| // Copyright 2024 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package riscv64asm |
| |
| import ( |
| "encoding/binary" |
| "errors" |
| ) |
| |
| type argTypeList [6]argType |
| |
| // An instFormat describes the format of an instruction encoding. |
| type instFormat struct { |
| mask uint32 |
| value uint32 |
| op Op |
| // args describe how to decode the instruction arguments. |
| // args is stored as a fixed-size array. |
| // if there are fewer than len(args) arguments, args[i] == 0 marks |
| // the end of the argument list. |
| args argTypeList |
| } |
| |
| var ( |
| errShort = errors.New("truncated instruction") |
| errUnknown = errors.New("unknown instruction") |
| ) |
| |
| var decoderCover []bool |
| |
| func init() { |
| decoderCover = make([]bool, len(instFormats)) |
| } |
| |
| // Decode decodes the 4 bytes in src as a single instruction. |
| func Decode(src []byte) (Inst, error) { |
| length := len(src) |
| if length < 2 { |
| return Inst{}, errShort |
| } |
| |
| var x uint32 |
| // Non-RVC instructions always starts with 0x11 |
| // So check whether src[0] & 3 == 3 |
| if src[0]&3 == 3 { |
| if length < 4 { |
| return Inst{}, errShort |
| } |
| length = 4 |
| x = binary.LittleEndian.Uint32(src) |
| } else { |
| length = 2 |
| x = uint32(binary.LittleEndian.Uint16(src)) |
| } |
| |
| Search: |
| for i, f := range instFormats { |
| if (x & f.mask) != f.value { |
| continue |
| } |
| |
| // Decode args. |
| var args Args |
| for j, aop := range f.args { |
| if aop == 0 { |
| break |
| } |
| arg := decodeArg(aop, x, i) |
| if arg == nil && f.op != C_NOP { |
| // Cannot decode argument. |
| continue Search |
| } |
| args[j] = arg |
| } |
| |
| if length == 2 { |
| args = convertCompressedIns(&f, args) |
| } |
| |
| decoderCover[i] = true |
| inst := Inst{ |
| Op: f.op, |
| Args: args, |
| Enc: x, |
| Len: length, |
| } |
| return inst, nil |
| } |
| return Inst{}, errUnknown |
| } |
| |
| // decodeArg decodes the arg described by aop from the instruction bits x. |
| // It returns nil if x cannot be decoded according to aop. |
| func decodeArg(aop argType, x uint32, index int) Arg { |
| switch aop { |
| case arg_rd: |
| return X0 + Reg((x>>7)&((1<<5)-1)) |
| |
| case arg_rs1: |
| return X0 + Reg((x>>15)&((1<<5)-1)) |
| |
| case arg_rs2: |
| return X0 + Reg((x>>20)&((1<<5)-1)) |
| |
| case arg_rs3: |
| return X0 + Reg((x>>27)&((1<<5)-1)) |
| |
| case arg_fd: |
| return F0 + Reg((x>>7)&((1<<5)-1)) |
| |
| case arg_fs1: |
| return F0 + Reg((x>>15)&((1<<5)-1)) |
| |
| case arg_fs2: |
| return F0 + Reg((x>>20)&((1<<5)-1)) |
| |
| case arg_fs3: |
| return F0 + Reg((x>>27)&((1<<5)-1)) |
| |
| case arg_rs1_amo: |
| return AmoReg{X0 + Reg((x>>15)&((1<<5)-1))} |
| |
| case arg_rs1_mem: |
| imm := x >> 20 |
| // Sign-extend |
| if imm>>uint32(12-1) == 1 { |
| imm |= 0xfffff << 12 |
| } |
| return RegOffset{X0 + Reg((x>>15)&((1<<5)-1)), Simm{int32(imm), true, 12}} |
| |
| case arg_rs1_store: |
| imm := (x<<20)>>27 | (x>>25)<<5 |
| // Sign-extend |
| if imm>>uint32(12-1) == 1 { |
| imm |= 0xfffff << 12 |
| } |
| return RegOffset{X0 + Reg((x>>15)&((1<<5)-1)), Simm{int32(imm), true, 12}} |
| |
| case arg_pred: |
| imm := x << 4 >> 28 |
| return MemOrder(uint8(imm)) |
| |
| case arg_succ: |
| imm := x << 8 >> 28 |
| return MemOrder(uint8(imm)) |
| |
| case arg_csr: |
| imm := x >> 20 |
| return CSR(imm) |
| |
| case arg_zimm: |
| imm := x << 12 >> 27 |
| return Uimm{imm, true} |
| |
| case arg_shamt5: |
| imm := x << 7 >> 27 |
| return Uimm{imm, false} |
| |
| case arg_shamt6: |
| imm := x << 6 >> 26 |
| return Uimm{imm, false} |
| |
| case arg_imm12: |
| imm := x >> 20 |
| // Sign-extend |
| if imm>>uint32(12-1) == 1 { |
| imm |= 0xfffff << 12 |
| } |
| return Simm{int32(imm), true, 12} |
| |
| case arg_imm20: |
| imm := x >> 12 |
| return Uimm{imm, false} |
| |
| case arg_jimm20: |
| imm := (x>>31)<<20 | (x<<1)>>22<<1 | (x<<11)>>31<<11 | (x<<12)>>24<<12 |
| // Sign-extend |
| if imm>>uint32(21-1) == 1 { |
| imm |= 0x7ff << 21 |
| } |
| return Simm{int32(imm), true, 21} |
| |
| case arg_simm12: |
| imm := (x<<20)>>27 | (x>>25)<<5 |
| // Sign-extend |
| if imm>>uint32(12-1) == 1 { |
| imm |= 0xfffff << 12 |
| } |
| return Simm{int32(imm), true, 12} |
| |
| case arg_bimm12: |
| imm := (x<<20)>>28<<1 | (x<<1)>>26<<5 | (x<<24)>>31<<11 | (x>>31)<<12 |
| // Sign-extend |
| if imm>>uint32(13-1) == 1 { |
| imm |= 0x7ffff << 13 |
| } |
| return Simm{int32(imm), true, 13} |
| |
| case arg_rd_p, arg_rs2_p: |
| return X8 + Reg((x>>2)&((1<<3)-1)) |
| |
| case arg_fd_p, arg_fs2_p: |
| return F8 + Reg((x>>2)&((1<<3)-1)) |
| |
| case arg_rs1_p, arg_rd_rs1_p: |
| return X8 + Reg((x>>7)&((1<<3)-1)) |
| |
| case arg_rd_n0, arg_rs1_n0, arg_rd_rs1_n0, arg_c_rs1_n0: |
| if X0+Reg((x>>7)&((1<<5)-1)) == X0 { |
| return nil |
| } |
| return X0 + Reg((x>>7)&((1<<5)-1)) |
| |
| case arg_c_rs2_n0: |
| if X0+Reg((x>>2)&((1<<5)-1)) == X0 { |
| return nil |
| } |
| return X0 + Reg((x>>2)&((1<<5)-1)) |
| |
| case arg_c_fs2: |
| return F0 + Reg((x>>2)&((1<<5)-1)) |
| |
| case arg_c_rs2: |
| return X0 + Reg((x>>2)&((1<<5)-1)) |
| |
| case arg_rd_n2: |
| if X0+Reg((x>>7)&((1<<5)-1)) == X0 || X0+Reg((x>>7)&((1<<5)-1)) == X2 { |
| return nil |
| } |
| return X0 + Reg((x>>7)&((1<<5)-1)) |
| |
| case arg_c_imm6: |
| imm := (x<<25)>>27 | (x<<19)>>31<<5 |
| // Sign-extend |
| if imm>>uint32(6-1) == 1 { |
| imm |= 0x3ffffff << 6 |
| } |
| return Simm{int32(imm), true, 6} |
| |
| case arg_c_nzimm6: |
| imm := (x<<25)>>27 | (x<<19)>>31<<5 |
| // Sign-extend |
| if imm>>uint32(6-1) == 1 { |
| imm |= 0x3ffffff << 6 |
| } |
| if int32(imm) == 0 { |
| return nil |
| } |
| return Simm{int32(imm), true, 6} |
| |
| case arg_c_nzuimm6: |
| imm := (x<<25)>>27 | (x<<19)>>31<<5 |
| if int32(imm) == 0 { |
| return nil |
| } |
| return Uimm{imm, false} |
| |
| case arg_c_uimm7: |
| imm := (x<<26)>>31<<6 | (x<<25)>>31<<2 | (x<<19)>>29<<3 |
| return Uimm{imm, false} |
| |
| case arg_c_uimm8: |
| imm := (x<<25)>>30<<6 | (x<<19)>>29<<3 |
| return Uimm{imm, false} |
| |
| case arg_c_uimm8sp_s: |
| imm := (x<<23)>>30<<6 | (x<<19)>>28<<2 |
| return Uimm{imm, false} |
| |
| case arg_c_uimm8sp: |
| imm := (x<<25)>>29<<2 | (x<<19)>>31<<5 | (x<<28)>>30<<6 |
| return Uimm{imm, false} |
| |
| case arg_c_uimm9sp_s: |
| imm := (x<<22)>>29<<6 | (x<<19)>>29<<3 |
| return Uimm{imm, false} |
| |
| case arg_c_uimm9sp: |
| imm := (x<<25)>>30<<3 | (x<<19)>>31<<5 | (x<<27)>>29<<6 |
| return Uimm{imm, false} |
| |
| case arg_c_bimm9: |
| imm := (x<<29)>>31<<5 | (x<<27)>>30<<1 | (x<<25)>>30<<6 | (x<<19)>>31<<8 | (x<<20)>>30<<3 |
| // Sign-extend |
| if imm>>uint32(9-1) == 1 { |
| imm |= 0x7fffff << 9 |
| } |
| return Simm{int32(imm), true, 9} |
| |
| case arg_c_nzimm10: |
| imm := (x<<29)>>31<<5 | (x<<27)>>30<<7 | (x<<26)>>31<<6 | (x<<25)>>31<<4 | (x<<19)>>31<<9 |
| // Sign-extend |
| if imm>>uint32(10-1) == 1 { |
| imm |= 0x3fffff << 10 |
| } |
| if int32(imm) == 0 { |
| return nil |
| } |
| return Simm{int32(imm), true, 10} |
| |
| case arg_c_nzuimm10: |
| imm := (x<<26)>>31<<3 | (x<<25)>>31<<2 | (x<<21)>>28<<6 | (x<<19)>>30<<4 |
| if int32(imm) == 0 { |
| return nil |
| } |
| return Uimm{imm, false} |
| |
| case arg_c_imm12: |
| imm := (x<<29)>>31<<5 | (x<<26)>>28<<1 | (x<<25)>>31<<7 | (x<<24)>>31<<6 | (x<<23)>>31<<10 | (x<<21)>>30<<8 | (x<<20)>>31<<4 | (x<<19)>>31<<11 |
| // Sign-extend |
| if imm>>uint32(12-1) == 1 { |
| imm |= 0xfffff << 12 |
| } |
| return Simm{int32(imm), true, 12} |
| |
| case arg_c_nzimm18: |
| imm := (x<<25)>>27<<12 | (x<<19)>>31<<17 |
| // Sign-extend |
| if imm>>uint32(18-1) == 1 { |
| imm |= 0x3fff << 18 |
| } |
| if int32(imm) == 0 { |
| return nil |
| } |
| return Simm{int32(imm), true, 18} |
| |
| default: |
| return nil |
| } |
| } |
| |
| // convertCompressedIns rewrites the RVC Instruction to regular Instructions |
| func convertCompressedIns(f *instFormat, args Args) Args { |
| var newargs Args |
| switch f.op { |
| case C_ADDI4SPN: |
| f.op = ADDI |
| newargs[0] = args[0] |
| newargs[1] = Reg(X2) |
| newargs[2] = Simm{int32(args[1].(Uimm).Imm), true, 12} |
| |
| case C_LW: |
| f.op = LW |
| newargs[0] = args[0] |
| newargs[1] = RegOffset{args[1].(Reg), Simm{int32(args[2].(Uimm).Imm), true, 12}} |
| |
| case C_SW: |
| f.op = SW |
| newargs[0] = args[1] |
| newargs[1] = RegOffset{args[0].(Reg), Simm{int32(args[2].(Uimm).Imm), true, 12}} |
| |
| case C_NOP: |
| f.op = ADDI |
| newargs[0] = X0 |
| newargs[1] = X0 |
| newargs[2] = Simm{0, true, 12} |
| |
| case C_ADDI: |
| f.op = ADDI |
| newargs[0] = args[0] |
| newargs[1] = args[0] |
| newargs[2] = Simm{args[1].(Simm).Imm, true, 12} |
| |
| case C_LI: |
| f.op = ADDI |
| newargs[0] = args[0] |
| newargs[1] = Reg(X0) |
| newargs[2] = Simm{args[1].(Simm).Imm, true, 12} |
| |
| case C_ADDI16SP: |
| f.op = ADDI |
| newargs[0] = Reg(X2) |
| newargs[1] = Reg(X2) |
| newargs[2] = Simm{args[0].(Simm).Imm, true, 12} |
| |
| case C_LUI: |
| f.op = LUI |
| newargs[0] = args[0] |
| newargs[1] = Uimm{uint32(args[1].(Simm).Imm >> 12), false} |
| |
| case C_ANDI: |
| f.op = ANDI |
| newargs[0] = args[0] |
| newargs[1] = args[0] |
| newargs[2] = Simm{args[1].(Simm).Imm, true, 12} |
| |
| case C_SUB: |
| f.op = SUB |
| newargs[0] = args[0] |
| newargs[1] = args[0] |
| newargs[2] = args[1] |
| |
| case C_XOR: |
| f.op = XOR |
| newargs[0] = args[0] |
| newargs[1] = args[0] |
| newargs[2] = args[1] |
| |
| case C_OR: |
| f.op = OR |
| newargs[0] = args[0] |
| newargs[1] = args[0] |
| newargs[2] = args[1] |
| |
| case C_AND: |
| f.op = AND |
| newargs[0] = args[0] |
| newargs[1] = args[0] |
| newargs[2] = args[1] |
| |
| case C_J: |
| f.op = JAL |
| newargs[0] = Reg(X0) |
| newargs[1] = Simm{args[0].(Simm).Imm, true, 21} |
| |
| case C_BEQZ: |
| f.op = BEQ |
| newargs[0] = args[0] |
| newargs[1] = Reg(X0) |
| newargs[2] = Simm{args[1].(Simm).Imm, true, 13} |
| |
| case C_BNEZ: |
| f.op = BNE |
| newargs[0] = args[0] |
| newargs[1] = Reg(X0) |
| newargs[2] = Simm{args[1].(Simm).Imm, true, 13} |
| |
| case C_LWSP: |
| f.op = LW |
| newargs[0] = args[0] |
| newargs[1] = RegOffset{Reg(X2), Simm{int32(args[1].(Uimm).Imm), true, 12}} |
| |
| case C_JR: |
| f.op = JALR |
| newargs[0] = Reg(X0) |
| newargs[1] = RegOffset{args[0].(Reg), Simm{0, true, 12}} |
| |
| case C_MV: |
| f.op = ADD |
| newargs[0] = args[0] |
| newargs[1] = Reg(X0) |
| newargs[2] = args[1] |
| |
| case C_EBREAK: |
| f.op = EBREAK |
| |
| case C_JALR: |
| f.op = JALR |
| newargs[0] = Reg(X1) |
| newargs[1] = RegOffset{args[0].(Reg), Simm{0, true, 12}} |
| |
| case C_ADD: |
| f.op = ADD |
| newargs[0] = args[0] |
| newargs[1] = args[0] |
| newargs[2] = args[1] |
| |
| case C_SWSP: |
| f.op = SW |
| newargs[0] = args[0] |
| newargs[1] = RegOffset{Reg(X2), Simm{int32(args[1].(Uimm).Imm), true, 12}} |
| |
| // riscv64 compressed instructions |
| case C_LD: |
| f.op = LD |
| newargs[0] = args[0] |
| newargs[1] = RegOffset{args[1].(Reg), Simm{int32(args[2].(Uimm).Imm), true, 12}} |
| |
| case C_SD: |
| f.op = SD |
| newargs[0] = args[1] |
| newargs[1] = RegOffset{args[0].(Reg), Simm{int32(args[2].(Uimm).Imm), true, 12}} |
| |
| case C_ADDIW: |
| f.op = ADDIW |
| newargs[0] = args[0] |
| newargs[1] = args[0] |
| newargs[2] = Simm{args[1].(Simm).Imm, true, 12} |
| |
| case C_SRLI: |
| f.op = SRLI |
| newargs[0] = args[0] |
| newargs[1] = args[0] |
| newargs[2] = args[1] |
| |
| case C_SRAI: |
| f.op = SRAI |
| newargs[0] = args[0] |
| newargs[1] = args[0] |
| newargs[2] = args[1] |
| |
| case C_SUBW: |
| f.op = SUBW |
| newargs[0] = args[0] |
| newargs[1] = args[0] |
| newargs[2] = args[1] |
| |
| case C_ADDW: |
| f.op = ADDW |
| newargs[0] = args[0] |
| newargs[1] = args[0] |
| newargs[2] = args[1] |
| |
| case C_SLLI: |
| f.op = SLLI |
| newargs[0] = args[0] |
| newargs[1] = args[0] |
| newargs[2] = args[1] |
| |
| case C_LDSP: |
| f.op = LD |
| newargs[0] = args[0] |
| newargs[1] = RegOffset{Reg(X2), Simm{int32(args[1].(Uimm).Imm), true, 12}} |
| |
| case C_SDSP: |
| f.op = SD |
| newargs[0] = args[0] |
| newargs[1] = RegOffset{Reg(X2), Simm{int32(args[1].(Uimm).Imm), true, 12}} |
| |
| // riscv double precision floating point compressed instructions |
| case C_FLD: |
| f.op = FLD |
| newargs[0] = args[0] |
| newargs[1] = RegOffset{args[1].(Reg), Simm{int32(args[2].(Uimm).Imm), true, 12}} |
| |
| case C_FSD: |
| f.op = FSD |
| newargs[0] = args[1] |
| newargs[1] = RegOffset{args[0].(Reg), Simm{int32(args[2].(Uimm).Imm), true, 12}} |
| |
| case C_FLDSP: |
| f.op = FLD |
| newargs[0] = args[0] |
| newargs[1] = RegOffset{Reg(X2), Simm{int32(args[1].(Uimm).Imm), true, 12}} |
| |
| case C_FSDSP: |
| f.op = FSD |
| newargs[0] = args[0] |
| newargs[1] = RegOffset{Reg(X2), Simm{int32(args[1].(Uimm).Imm), true, 12}} |
| |
| case C_UNIMP: |
| f.op = CSRRW |
| newargs[0] = Reg(X0) |
| newargs[1] = CSR(CYCLE) |
| newargs[2] = Reg(X0) |
| } |
| return newargs |
| } |