// 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 }