Files
gasm-sdk/asm/riscv_encode.go
T

1269 lines
50 KiB
Go
Raw Normal View History

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
package asm
import "strings"
// RISC-V register encoding: maps register names to their 5-bit numbers.
// The Go assembler uses the standard RISC-V ABI naming.
// riscvRegNum returns the 5-bit register number for a RISC-V register name.
// Returns -1 if the register is not recognized.
func riscvRegNum(name string) int {
n, _ := riscvBankedRegNum(name)
return n
}
// riscvRegBank classifies a RISC-V register name by its bank: the integer
// file (X0-X31 with the ABI aliases), the floating-point file (F0-F31 with
// the FP aliases) and the vector file (V0-V31). The toolchain's validate
// stage checks every operand against its instruction's bank and rejects the
// wrong one; the bank is what makes "F1" and "X1" different operands even
// though both encode as the number 1.
type riscvRegBank uint8
const (
riscvBankNone riscvRegBank = iota // not a register name
riscvBankInt // X0-X31, ZERO, RA, SP, ...
riscvBankFloat // F0-F31, FT0-FT11, FS0-FS11, FA0-FA7
riscvBankVec // V0-V31
)
// String names the bank the way the bank-mismatch diagnostics spell it.
func (b riscvRegBank) String() string {
switch b {
case riscvBankInt:
return "integer"
case riscvBankFloat:
return "float"
case riscvBankVec:
return "vector"
}
return "none"
}
// riscvBankedRegNum returns the 5-bit register number for a name together
// with the bank the name belongs to. riscvBankNone with a negative number
// names no register at all.
func riscvBankedRegNum(name string) (int, riscvRegBank) {
switch name {
// Floating-point registers (F0-F31).
case "F0", "FT0":
return 0, riscvBankFloat
case "F1", "FT1":
return 1, riscvBankFloat
case "F2", "FT2":
return 2, riscvBankFloat
case "F3", "FT3":
return 3, riscvBankFloat
case "F4", "FT4":
return 4, riscvBankFloat
case "F5", "FT5":
return 5, riscvBankFloat
case "F6", "FT6":
return 6, riscvBankFloat
case "F7", "FT7":
return 7, riscvBankFloat
case "F8", "FS0":
return 8, riscvBankFloat
case "F9", "FS1":
return 9, riscvBankFloat
case "F10", "FA0":
return 10, riscvBankFloat
case "F11", "FA1":
return 11, riscvBankFloat
case "F12", "FA2":
return 12, riscvBankFloat
case "F13", "FA3":
return 13, riscvBankFloat
case "F14", "FA4":
return 14, riscvBankFloat
case "F15", "FA5":
return 15, riscvBankFloat
case "F16", "FA6":
return 16, riscvBankFloat
case "F17", "FA7":
return 17, riscvBankFloat
case "F18", "FS2":
return 18, riscvBankFloat
case "F19", "FS3":
return 19, riscvBankFloat
case "F20", "FS4":
return 20, riscvBankFloat
case "F21", "FS5":
return 21, riscvBankFloat
case "F22", "FS6":
return 22, riscvBankFloat
case "F23", "FS7":
return 23, riscvBankFloat
case "F24", "FS8":
return 24, riscvBankFloat
case "F25", "FS9":
return 25, riscvBankFloat
case "F26", "FS10":
return 26, riscvBankFloat
case "F27", "FS11":
return 27, riscvBankFloat
case "F28", "FT8":
return 28, riscvBankFloat
case "F29", "FT9":
return 29, riscvBankFloat
case "F30", "FT10":
return 30, riscvBankFloat
case "F31", "FT11":
return 31, riscvBankFloat
}
// Vector registers V0-V31 (the "V" extension). They share the
// register numbering with the integer file: a bare number 0-31.
if len(name) >= 2 && name[0] == 'V' {
if n, ok := parseRegDigits(name[1:], 31); ok {
return n, riscvBankVec
}
}
switch name {
// Numbered integer registers.
case "X0", "ZERO":
return 0, riscvBankInt
case "X1", "RA", "LR":
return 1, riscvBankInt
case "X2", "SP":
return 2, riscvBankInt
case "X3", "GP":
return 3, riscvBankInt
case "X4", "TP":
return 4, riscvBankInt
case "X5", "T0":
return 5, riscvBankInt
case "X6", "T1":
return 6, riscvBankInt
case "X7", "T2":
return 7, riscvBankInt
case "X8", "S0", "FP":
return 8, riscvBankInt
case "X9", "S1":
return 9, riscvBankInt
case "X10", "A0":
return 10, riscvBankInt
case "X11", "A1":
return 11, riscvBankInt
case "X12", "A2":
return 12, riscvBankInt
case "X13", "A3":
return 13, riscvBankInt
case "X14", "A4":
return 14, riscvBankInt
case "X15", "A5":
return 15, riscvBankInt
case "X16", "A6":
return 16, riscvBankInt
case "X17", "A7":
return 17, riscvBankInt
case "X18", "S2":
return 18, riscvBankInt
case "X19", "S3":
return 19, riscvBankInt
case "X20", "S4":
return 20, riscvBankInt
case "X21", "S5":
return 21, riscvBankInt
case "X22", "S6":
return 22, riscvBankInt
case "X23", "S7":
return 23, riscvBankInt
case "X24", "S8":
return 24, riscvBankInt
case "X25", "S9":
return 25, riscvBankInt
case "X26", "S10", "CTXT":
return 26, riscvBankInt
case "X27", "S11", "g":
return 27, riscvBankInt
case "X28", "T3":
return 28, riscvBankInt
case "X29", "T4":
return 29, riscvBankInt
case "X30", "T5":
return 30, riscvBankInt
case "X31", "T6", "TMP":
return 31, riscvBankInt
}
return -1, riscvBankNone
}
// parseRegDigits parses a decimal register suffix and reports whether it is
// within [0, max].
func parseRegDigits(digits string, max int) (int, bool) {
if digits == "" {
return 0, false
}
n := 0
for i := 0; i < len(digits); i++ {
if digits[i] < '0' || digits[i] > '9' {
return 0, false
}
n = n*10 + int(digits[i]-'0')
if n > max {
return 0, false
}
}
return n, true
}
// RISC-V instruction encoding parameters.
type riscvEnc struct {
opcode uint32 // bits [6:0]
funct3 uint32 // bits [14:12]
funct7 uint32 // bits [31:25]
}
// riscvInstrTable maps RISC-V mnemonics to their encoding.
var riscvInstrTable = map[string]riscvEnc{
// RV64I, R-type arithmetic/logic.
"ADD": {0x33, 0x0, 0x00},
"SUB": {0x33, 0x0, 0x20},
"SLL": {0x33, 0x1, 0x00},
"SLT": {0x33, 0x2, 0x00},
"SLTU": {0x33, 0x3, 0x00},
"XOR": {0x33, 0x4, 0x00},
"SRL": {0x33, 0x5, 0x00},
"SRA": {0x33, 0x5, 0x20},
"OR": {0x33, 0x6, 0x00},
"AND": {0x33, 0x7, 0x00},
// RV64I, 32-bit variants (W suffix).
"ADDW": {0x3B, 0x0, 0x00},
"SUBW": {0x3B, 0x0, 0x20},
"SLLW": {0x3B, 0x1, 0x00},
"SRLW": {0x3B, 0x5, 0x00},
"SRAW": {0x3B, 0x5, 0x20},
// RV64I, I-type shift-immediate (shamt in rs2 field).
"SLLI": {0x13, 0x1, 0x00},
"SRLI": {0x13, 0x5, 0x00},
"SRAI": {0x13, 0x5, 0x20},
"SLLIW": {0x1B, 0x1, 0x00},
"SRLIW": {0x1B, 0x5, 0x00},
"SRAIW": {0x1B, 0x5, 0x20},
// Zba/Zbs shift-immediate forms: the shamt spans bits [25:20], so the
// funct7 field carries the operation's funct6 and bit 25 comes from the
// amount. SLLIUW (Zba) zeroes the upper 32 bits before the shift.
"BCLRI": {0x13, 0x1, 0x24},
"BEXTI": {0x13, 0x5, 0x24},
"BINVI": {0x13, 0x1, 0x34},
"BSETI": {0x13, 0x1, 0x14},
"SLLIUW": {0x1B, 0x1, 0x04},
// Zbb unary bit operations: one source register, the rs2 field fixed
// (the count or the position the operation works on).
"CLZ": {0x13, 0x1, 0x30},
"CLZW": {0x1B, 0x1, 0x30},
"CPOP": {0x13, 0x1, 0x30},
"CPOPW": {0x1B, 0x1, 0x30},
"CTZ": {0x13, 0x1, 0x30},
"CTZW": {0x1B, 0x1, 0x30},
"SEXTB": {0x13, 0x1, 0x30},
"SEXTH": {0x13, 0x1, 0x30},
"ORCB": {0x13, 0x5, 0x14},
"REV8": {0x13, 0x5, 0x35},
"ZEXTH": {0x3B, 0x4, 0x04},
// RV64M, multiply/divide.
"MUL": {0x33, 0x0, 0x01},
"MULH": {0x33, 0x1, 0x01},
"MULHSU": {0x33, 0x2, 0x01},
"MULHU": {0x33, 0x3, 0x01},
"DIV": {0x33, 0x4, 0x01},
"DIVU": {0x33, 0x5, 0x01},
"REM": {0x33, 0x6, 0x01},
"REMU": {0x33, 0x7, 0x01},
// RV64M, 32-bit variants.
"MULW": {0x3B, 0x0, 0x01},
"DIVW": {0x3B, 0x4, 0x01},
"DIVUW": {0x3B, 0x5, 0x01},
"REMW": {0x3B, 0x6, 0x01},
"REMUW": {0x3B, 0x7, 0x01},
// Zicond conditional zeroing.
"CZEROEQZ": {0x33, 0x5, 0x07},
"CZERONEZ": {0x33, 0x7, 0x07},
// Zba address generation and Zbc carry-less multiplication.
"ADDUW": {0x3B, 0x0, 0x04},
"SH1ADD": {0x33, 0x2, 0x10},
"SH1ADDUW": {0x3B, 0x2, 0x10},
"SH2ADD": {0x33, 0x4, 0x10},
"SH2ADDUW": {0x3B, 0x4, 0x10},
"SH3ADD": {0x33, 0x6, 0x10},
"SH3ADDUW": {0x3B, 0x6, 0x10},
"CLMUL": {0x33, 0x1, 0x05},
"CLMULH": {0x33, 0x3, 0x05},
"CLMULR": {0x33, 0x2, 0x05},
// Zbs single-bit: the register forms; the immediate spellings lower to
// the shift-immediate entries below (BCLR $n is BCLRI $n).
"BCLR": {0x33, 0x1, 0x24},
"BEXT": {0x33, 0x5, 0x24},
"BINV": {0x33, 0x1, 0x34},
"BSET": {0x33, 0x1, 0x14},
// RV64I, I-type arithmetic.
"ADDI": {0x13, 0x0, 0x00},
"ADDIW": {0x1B, 0x0, 0x00},
"SLTI": {0x13, 0x2, 0x00},
"SLTIU": {0x13, 0x3, 0x00},
"XORI": {0x13, 0x4, 0x00},
"ORI": {0x13, 0x6, 0x00},
"ANDI": {0x13, 0x7, 0x00},
// Loads (I-type).
"LB": {0x03, 0x0, 0x00},
"LH": {0x03, 0x1, 0x00},
"LW": {0x03, 0x2, 0x00},
"LD": {0x03, 0x3, 0x00},
"LBU": {0x03, 0x4, 0x00},
"LHU": {0x03, 0x5, 0x00},
"LWU": {0x03, 0x6, 0x00},
// Stores (S-type).
"SB": {0x23, 0x0, 0x00},
"SH": {0x23, 0x1, 0x00},
"SW": {0x23, 0x2, 0x00},
"SD": {0x23, 0x3, 0x00},
// Branches (B-type).
"BEQ": {0x63, 0x0, 0x00},
"BNE": {0x63, 0x1, 0x00},
"BLT": {0x63, 0x4, 0x00},
"BGE": {0x63, 0x5, 0x00},
"BLTU": {0x63, 0x6, 0x00},
"BGEU": {0x63, 0x7, 0x00},
// The swapped-spelling comparison forms: encoded as BLT/BGE/BLTU/BGEU
// with the register operands swapped.
"BGT": {0x63, 0x4, 0x00},
"BLE": {0x63, 0x5, 0x00},
"BGTU": {0x63, 0x6, 0x00},
"BLEU": {0x63, 0x7, 0x00},
// U-type.
"LUI": {0x37, 0x0, 0x00},
"AUIPC": {0x17, 0x0, 0x00},
// System.
"ECALL": {0x73, 0x0, 0x00},
"EBREAK": {0x73, 0x0, 0x00},
"FENCE": {0x0F, 0x0, 0x00},
"FENCE.TSO": {0x0F, 0x0, 0x00},
"PAUSE": {0x0F, 0x0, 0x00},
// JALR, indirect jump/call (I-type).
"JALR": {0x67, 0x0, 0x00},
// RV64A, atomics (AMO opcode 0x2F).
// funct3: 0x2 = word, 0x3 = doubleword. The stored funct7 is the full
// 7-bit field: funct5 in the upper five bits and the aq/rl ordering bits in
// the lower two, exactly as the toolchain writes them: every AMO sets both
// aq and rl (funct7 |= 3).
"AMOSWAPW": {0x2F, 0x2, 0x01<<2 | 0x3},
"AMOSWAPD": {0x2F, 0x3, 0x01<<2 | 0x3},
"AMOADDW": {0x2F, 0x2, 0x00<<2 | 0x3},
"AMOADDD": {0x2F, 0x3, 0x00<<2 | 0x3},
"AMOANDW": {0x2F, 0x2, 0x0C<<2 | 0x3},
"AMOANDD": {0x2F, 0x3, 0x0C<<2 | 0x3},
"AMOORW": {0x2F, 0x2, 0x08<<2 | 0x3},
"AMOORD": {0x2F, 0x3, 0x08<<2 | 0x3},
"AMOXORW": {0x2F, 0x2, 0x04<<2 | 0x3},
"AMOXORD": {0x2F, 0x3, 0x04<<2 | 0x3},
"AMOMAXW": {0x2F, 0x2, 0x14<<2 | 0x3},
"AMOMAXD": {0x2F, 0x3, 0x14<<2 | 0x3},
"AMOMINW": {0x2F, 0x2, 0x10<<2 | 0x3},
"AMOMIND": {0x2F, 0x3, 0x10<<2 | 0x3},
"AMOMAXUW": {0x2F, 0x2, 0x1C<<2 | 0x3},
"AMOMAXUD": {0x2F, 0x3, 0x1C<<2 | 0x3},
"AMOMINUW": {0x2F, 0x2, 0x18<<2 | 0x3},
"AMOMINUD": {0x2F, 0x3, 0x18<<2 | 0x3},
// RV64F/D, floating-point arithmetic.
"FADDS": {0x53, 0x0, 0x00},
"FSUBS": {0x53, 0x0, 0x04},
"FMULS": {0x53, 0x0, 0x08},
"FDIVS": {0x53, 0x0, 0x0C},
"FADDD": {0x53, 0x0, 0x01},
"FSUBD": {0x53, 0x0, 0x05},
"FMULD": {0x53, 0x0, 0x09},
"FDIVD": {0x53, 0x0, 0x0D},
"FSQRTS": {0x53, 0x0, 0x2C},
"FSQRTD": {0x53, 0x0, 0x2D},
// FP loads/stores.
"FLW": {0x07, 0x2, 0x00},
"FLD": {0x07, 0x3, 0x00},
"FLQ": {0x07, 0x4, 0x00},
"FSW": {0x27, 0x2, 0x00},
"FSD": {0x27, 0x3, 0x00},
"FSQ": {0x27, 0x4, 0x00},
// FP min/max.
"FMINS": {0x53, 0x0, 0x14},
"FMAXS": {0x53, 0x1, 0x14},
"FMIND": {0x53, 0x0, 0x15},
"FMAXD": {0x53, 0x1, 0x15},
// FP compare: the integer destination rides in rd (the 3-op R-type
// path writes it there).
"FEQS": {0x53, 0x2, 0x50},
"FLTS": {0x53, 0x1, 0x50},
"FLES": {0x53, 0x0, 0x50},
"FEQD": {0x53, 0x2, 0x51},
"FLTD": {0x53, 0x1, 0x51},
"FLED": {0x53, 0x0, 0x51},
"FEQQ": {0x53, 0x2, 0x53},
"FLTQ": {0x53, 0x1, 0x53},
"FLEQ": {0x53, 0x0, 0x53},
// FP sign injection: the sign source rides in rs2; the XOR form's
// funct3 is 2 in every width.
"FSGNJD": {0x53, 0x0, 0x11},
"FSGNJS": {0x53, 0x0, 0x10},
"FSGNJXD": {0x53, 0x2, 0x11},
"FSGNJXS": {0x53, 0x2, 0x10},
"FSGNJND": {0x53, 0x1, 0x11},
"FSGNJNS": {0x53, 0x1, 0x10},
"FSGNJQ": {0x53, 0x0, 0x13},
"FSGNJNQ": {0x53, 0x1, 0x13},
"FSGNJXQ": {0x53, 0x2, 0x13},
// RV64Q, quad-precision arithmetic (the fmt field rides in funct7's
// low bits: 11 for quad).
"FADDQ": {0x53, 0x0, 0x03},
"FSUBQ": {0x53, 0x0, 0x07},
"FMULQ": {0x53, 0x0, 0x0B},
"FDIVQ": {0x53, 0x0, 0x0F},
"FSQRTQ": {0x53, 0x0, 0x2F},
"FMINQ": {0x53, 0x0, 0x17},
"FMAXQ": {0x53, 0x1, 0x17},
// RV64A, load-reserved / store-conditional (funct5 0x02 / 0x03).
// The toolchain gives LR acquire ordering (aq = 1) and SC release
// ordering (rl = 1).
"LRW": {0x2F, 0x2, 0x02<<2 | 0x2},
"LRD": {0x2F, 0x3, 0x02<<2 | 0x2},
"SCW": {0x2F, 0x2, 0x03<<2 | 0x1},
"SCD": {0x2F, 0x3, 0x03<<2 | 0x1},
}
// riscvRType encodes an R-type instruction: funct7 | rs2 | rs1 | funct3 | rd | opcode.
func riscvRType(enc riscvEnc, rd, rs1, rs2 int) uint32 {
return (enc.funct7 << 25) | (uint32(rs2) << 20) | (uint32(rs1) << 15) |
(enc.funct3 << 12) | (uint32(rd) << 7) | enc.opcode
}
// riscvAMOType encodes an atomic (AMO) instruction.
// Layout: funct7 | rs2 | rs1 | funct3 | rd | opcode, where funct7 carries the
// funct5 in its upper five bits and the aq/rl ordering bits in the lower two
// (the table stores the full field, so the word needs no reassembly).
func riscvAMOType(enc riscvEnc, rd, rs1, rs2 int) uint32 {
return (enc.funct7 << 25) | (uint32(rs2) << 20) | (uint32(rs1) << 15) |
(enc.funct3 << 12) | (uint32(rd) << 7) | enc.opcode
}
// FP conversion instructions (FCVT, FMV). These use the rs2 field to
// encode the conversion type rather than a register, so they are handled
// separately from the general instruction table.
type riscvCvtEnc struct {
funct7 uint32 // bits [31:25]
rs2 uint32 // conversion-type code in bits [24:20]
funct3 uint32 // the rounding mode or the fclass marker, bits [14:12]
opcode uint32 // always 0x53 (OP-FP)
}
var riscvCvtTable = map[string]riscvCvtEnc{
// float → int (rs2 selects the integer width/sign). The bare forms
// carry the specification's default rounding mode RTZ in funct3; the
// suffixed spellings override it.
"FCVTWS": {0x60, 0x0, 0x1, 0x53}, // float32 → int32
"FCVTWUS": {0x60, 0x1, 0x1, 0x53}, // float32 → uint32
"FCVTLS": {0x60, 0x2, 0x1, 0x53}, // float32 → int64
"FCVTLUS": {0x60, 0x3, 0x1, 0x53}, // float32 → uint64
"FCVTWD": {0x61, 0x0, 0x1, 0x53}, // float64 → int32
"FCVTWUD": {0x61, 0x1, 0x1, 0x53}, // float64 → uint32
"FCVTLD": {0x61, 0x2, 0x1, 0x53}, // float64 → int64
"FCVTLUD": {0x61, 0x3, 0x1, 0x53}, // float64 → uint64
// int → float (rs2 selects the integer width/sign): the default
// rounding mode RNE keeps funct3 0.
"FCVTSW": {0x68, 0x0, 0x0, 0x53}, // int32 → float32
"FCVTSWU": {0x68, 0x1, 0x0, 0x53}, // uint32 → float32
"FCVTSL": {0x68, 0x2, 0x0, 0x53}, // int64 → float32
"FCVTSLU": {0x68, 0x3, 0x0, 0x53}, // uint64 → float32
"FCLASSS": {0x70, 0x0, 0x1, 0x53}, // classify float32 → GPR mask
"FCLASSD": {0x71, 0x0, 0x1, 0x53}, // classify float64 → GPR mask
"FCVTDW": {0x69, 0x0, 0x0, 0x53}, // int32 → float64
"FCVTDWU": {0x69, 0x1, 0x0, 0x53}, // uint32 → float64
"FCVTDL": {0x69, 0x2, 0x0, 0x53}, // int64 → float64
"FCVTDLU": {0x69, 0x3, 0x0, 0x53}, // uint64 → float64
// float → float width conversion.
"FCVTSD": {0x20, 0x1, 0x0, 0x53}, // float64 → float32
"FCVTDS": {0x21, 0x0, 0x0, 0x53}, // float32 → float64
// Quad-precision conversions: the fmt field rides in funct7's low
// bits (11 for quad), the other width in rs2 where one is needed.
"FCVTSQ": {0x20, 0x3, 0x0, 0x53}, // quad → float32
"FCVTDQ": {0x21, 0x3, 0x0, 0x53}, // quad → float64
"FCVTQS": {0x23, 0x0, 0x0, 0x53}, // float32 → quad
"FCVTQD": {0x23, 0x1, 0x0, 0x53}, // float64 → quad
"FCVTWQ": {0x63, 0x0, 0x1, 0x53}, // quad → int32
"FCVTWUQ": {0x63, 0x1, 0x1, 0x53}, // quad → uint32
"FCVTLQ": {0x63, 0x2, 0x1, 0x53}, // quad → int64
"FCVTLUQ": {0x63, 0x3, 0x1, 0x53}, // quad → uint64
"FCVTQW": {0x6B, 0x0, 0x0, 0x53}, // int32 → quad
"FCVTQWU": {0x6B, 0x1, 0x0, 0x53}, // uint32 → quad
"FCVTQL": {0x6B, 0x2, 0x0, 0x53}, // int64 → quad
"FCVTQLU": {0x6B, 0x3, 0x0, 0x53}, // uint64 → quad
"FCLASSQ": {0x73, 0x0, 0x1, 0x53}, // classify quad → GPR mask
// Bit moves between integer and FP registers (no conversion).
"FMVXD": {0x71, 0x0, 0x0, 0x53}, // float64 → int64 (bit move)
"FMVDX": {0x79, 0x0, 0x0, 0x53}, // int64 → float64 (bit move)
"FMVXW": {0x70, 0x0, 0x0, 0x53}, // float32 → int32 (bit move)
"FMVWX": {0x78, 0x0, 0x0, 0x53}, // int32 → float32 (bit move)
// The toolchain's W/D suffix spellings of the same moves.
"FMVXS": {0x70, 0x0, 0x0, 0x53},
"FMVFS": {0x78, 0x0, 0x0, 0x53},
"FMVSX": {0x78, 0x0, 0x0, 0x53},
}
// riscvCvtBanks answers which register bank each operand of an FP
// conversion or move carries, read from the mnemonic's direction: FCVTWS
// moves float32 into an integer register (rd integer, rs1 float), FCVTSW
// the reverse, FCLASS reads a float into an integer mask and the FMV
// spellings follow their X position. The rd bank comes first.
func riscvCvtBanks(mnem string) (riscvRegBank, riscvRegBank) {
// The classify and bit-move forms.
switch {
case strings.HasPrefix(mnem, "FCLASS"):
return riscvBankInt, riscvBankFloat
case strings.HasPrefix(mnem, "FMV"):
if len(mnem) > 3 && mnem[3] == 'X' {
return riscvBankInt, riscvBankFloat // FMVXD, FMVXW, FMVXS
}
return riscvBankFloat, riscvBankInt // FMVDX, FMVWX, FMVFS, FMVSX
}
if !strings.HasPrefix(mnem, "FCVT") || len(mnem) < 6 {
return riscvBankNone, riscvBankNone
}
rest := mnem[4:]
// intWU/LU + floatSDQ: float → integer.
switch rest[:2] {
case "WU", "LU":
if isRiscvWidthLetter(rest[2]) {
return riscvBankInt, riscvBankFloat
}
}
switch rest[0] {
case 'W', 'L':
if isRiscvWidthLetter(rest[1]) {
return riscvBankInt, riscvBankFloat
}
}
// floatSDQ + intWU/LU: integer → float.
if isRiscvWidthLetter(rest[0]) {
switch rest[1:] {
case "W", "WU", "L", "LU":
return riscvBankFloat, riscvBankInt
}
// float → float width conversion.
return riscvBankFloat, riscvBankFloat
}
return riscvBankNone, riscvBankNone
}
// isRiscvWidthLetter reports whether c names a float width (S, D or Q).
func isRiscvWidthLetter(c byte) bool {
return c == 'S' || c == 'D' || c == 'Q'
}
// riscvCvtType encodes an FP conversion instruction.
// Layout: funct7 | rs2(convtype) | rs1 | funct3(rm) | rd | opcode.
func riscvCvtType(enc riscvCvtEnc, rd, rs1 int) uint32 {
return (enc.funct7 << 25) | (enc.rs2 << 20) | (uint32(rs1) << 15) |
(enc.funct3 << 12) | (uint32(rd) << 7) | enc.opcode
}
// R4-type fused multiply-add instructions (FMADD/FMSUB/FNMSUB/FNMADD).
// These take 4 register operands: rs1, rs2, rs3, rd.
// Layout: rs3 | fmt | rs2 | rs1 | rm | rd | opcode.
type riscvFmaEnc struct {
fmt uint32 // bits [26:25]: 0x0 = single, 0x1 = double
opcode uint32 // bits [6:0]
}
var riscvFmaTable = map[string]riscvFmaEnc{
"FMADDS": {0x0, 0x43}, // rd = rs1*rs2 + rs3
"FMADDD": {0x1, 0x43},
"FMADDQ": {0x3, 0x43},
"FMSUBS": {0x0, 0x47}, // rd = rs1*rs2 - rs3
"FMSUBD": {0x1, 0x47},
"FMSUBQ": {0x3, 0x47},
"FNMSUBS": {0x0, 0x4B}, // rd = -(rs1*rs2) + rs3
"FNMSUBD": {0x1, 0x4B},
"FNMSUBQ": {0x3, 0x4B},
"FNMADDS": {0x0, 0x4F}, // rd = -(rs1*rs2) - rs3
"FNMADDD": {0x1, 0x4F},
"FNMADDQ": {0x3, 0x4F},
}
// riscvFmaType encodes an R4-type fused multiply-add instruction.
func riscvFmaType(enc riscvFmaEnc, rd, rs1, rs2, rs3 int) uint32 {
return (uint32(rs3) << 27) | (enc.fmt << 25) | (uint32(rs2) << 20) |
(uint32(rs1) << 15) | (0x0 << 12) /* rm=RNE */ | (uint32(rd) << 7) | enc.opcode
}
// CSR (Control and Status Register) instructions.
// Format: csr[11:0] | rs1/zimm | funct3 | rd | opcode (0x73).
type riscvCsrEnc struct {
funct3 uint32 // bits [14:12]
imm bool // true for CSRRWI/CSRRSI/CSRRCI (5-bit uimm variant)
}
var riscvCsrTable = map[string]riscvCsrEnc{
"CSRRW": {0x1, false}, // rd=CSR, CSR=rs1
"CSRRS": {0x2, false}, // rd=CSR, CSR |= rs1
"CSRRC": {0x3, false}, // rd=CSR, CSR &= ~rs1
"CSRRWI": {0x5, true}, // rd=CSR, CSR=uimm
"CSRRSI": {0x6, true}, // rd=CSR, CSR |= uimm
"CSRRCI": {0x7, true}, // rd=CSR, CSR &= ~uimm
}
// riscvCsrType encodes a CSR instruction.
// csr is the 12-bit CSR address; src is either a register number or a 5-bit
// unsigned immediate (depending on enc.imm).
func riscvCsrType(enc riscvCsrEnc, rd, src int, csr int32) uint32 {
return (uint32(csr&0xFFF) << 20) | (uint32(src&0x1F) << 15) |
(enc.funct3 << 12) | (uint32(rd) << 7) | 0x73
}
// riscvIType encodes an I-type instruction: imm[11:0] | rs1 | funct3 | rd | opcode.
func riscvIType(enc riscvEnc, rd, rs1 int, imm int32) uint32 {
return (uint32(imm&0xFFF) << 20) | (uint32(rs1) << 15) |
(enc.funct3 << 12) | (uint32(rd) << 7) | enc.opcode
}
// riscvSType encodes an S-type instruction: imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode.
func riscvSType(enc riscvEnc, rs1, rs2 int, imm int32) uint32 {
immU := uint32(imm) & 0xFFF
return ((immU >> 5) << 25) | (uint32(rs2) << 20) | (uint32(rs1) << 15) |
(enc.funct3 << 12) | ((immU & 0x1F) << 7) | enc.opcode
}
// riscvBType encodes a B-type instruction (branches).
func riscvBType(enc riscvEnc, rs1, rs2 int, offset int32) uint32 {
imm := uint32(offset) & 0x1FFE // bits [12:1], bit 0 is always 0
return (((imm >> 12) & 1) << 31) | // imm[12]
(((imm >> 5) & 0x3F) << 25) | // imm[10:5]
(uint32(rs2) << 20) | (uint32(rs1) << 15) |
(enc.funct3 << 12) |
(((imm >> 1) & 0xF) << 8) | // imm[4:1]
(((imm >> 11) & 1) << 7) | // imm[11]
enc.opcode
}
// riscvUType encodes a U-type instruction: imm[31:12] | rd | opcode.
func riscvUType(enc riscvEnc, rd int, imm int32) uint32 {
return (uint32(imm) & 0xFFFFF000) | (uint32(rd) << 7) | enc.opcode
}
// riscvJType encodes a J-type instruction (JAL).
func riscvJType(rd int, offset int32) uint32 {
imm := uint32(offset) & 0x1FFFFE // bits [20:1]
return (((imm >> 20) & 1) << 31) | // imm[20]
(((imm >> 1) & 0x3FF) << 21) | // imm[10:1]
(((imm >> 11) & 1) << 20) | // imm[11]
(((imm >> 12) & 0xFF) << 12) | // imm[19:12]
(uint32(rd) << 7) |
0x6F // JAL opcode
}
// ---- RVV ("V" extension) encoding helpers ----
// The OP-V major opcode and its funct3 subclasses.
const (
riscvOpV = 0x57 // the vector operation opcode (also OPcfg for vset*)
// funct3 values: 0 OPIVV, 1 OPFVV, 2 OPMVV, 3 OPIVI, 4 OPIVX,
// 5 OPFVF, 6 OPMVX, 7 vsetvli. The subclass a mnemonic carries lives
// in its table entry's funct3.
riscvVf3Cfg = 0x7 // vsetvli
)
// riscvVType composes the vsetvli/vsetivli vtype immediate: the register
// group multiplier in [2:0], the selected element width in [5:3] and the
// tail-agnostic and mask-agnostic policies in bits 6 and 7.
func riscvVType(vsew, vlmul, vta, vma int) int {
return vlmul | vsew<<3 | vta<<6 | vma<<7
}
// riscvVSetEnc encodes VSETVLI and VSETIVLI: imm[31:20] = vtype, rs1 = the
// avl register or 5-bit uimm, rd = the destination. Both carry funct3 7; a
// vsetivli is distinguished by bits [31:30] set in the immediate (the 0xC00
// the toolchain writes above its 10-bit vtype).
func riscvVSetEnc(vsetivli bool, avl, vtype, rd int) uint32 {
imm := vtype & 0x3FF
if vsetivli {
imm |= 0xC00
}
return uint32(imm)<<20 | uint32(avl&0x1F)<<15 | uint32(riscvVf3Cfg)<<12 |
uint32(rd)<<7 | riscvOpV
}
// riscvVLSType encodes a vector load or store: the full 32-bit word with the
// segment count in bits [31:29], the addressing mode in bits [28:26], the
// unmasked bit at 25 and the width in funct3. width follows the load
// convention (0 = 8-bit, 5 = 16-bit, 6 = 32-bit, 7 = 64-bit).
func riscvVLSType(op uint32, nf, mop, width int, rs2 int32, rs1, rd int) uint32 {
return uint32(nf&0x7)<<29 | uint32(mop&0x7)<<26 | 1<<25 |
uint32(rs2)<<20 | uint32(rs1)<<15 | uint32(width&0x7)<<12 |
uint32(rd)<<7 | op
}
// riscvVecWord composes one OP-V arithmetic word: funct7 carries funct6 << 1
// with the vm bit in its LSB, exactly as the table stores it; rs1Field takes
// the source register, the scalar or the immediate; vs2 and vd name vector
// registers.
func riscvVecWord(funct7 uint32, rs1Field int32, vs2 int, funct3 uint32, vd int) uint32 {
return funct7<<25 | uint32(vs2&0x1F)<<20 | uint32(rs1Field&0x1F)<<15 |
funct3&0x7<<12 | uint32(vd&0x1F)<<7 | riscvOpV
}
// riscvSegNF maps a segment count to the 3-bit nf field (count - 1).
func riscvSegNF(n int) int32 { return int32(n - 1) }
// The table below mirrors the Go toolchain's own instruction table
// (cmd/internal/obj/riscv): funct7 carries funct6 << 1 with the fixed
// vm bit in its LSB, exactly as the toolchain stores it.
// riscvVecOpClass names the operand shape a vector arithmetic mnemonic
// takes: which operands land in the rs1 and vs2 fields, whether a
// V0 mask may sit between them, and whether the class rewrites its
// operands or its mnemonic before encoding.
type riscvVecOpClass uint8
const (
// vs1, vs2 [, v0], vd: the plain vector-vector form
vecVV riscvVecOpClass = iota
// vs2, vs1 [, v0], vd: the multiply-accumulate order
vecMACC
// vs2 [, v0], vd: one vector operand, fixed rs1
vecUNARY
// vs2 [, v0], xrd: mask bookkeeping into an integer register
vecM2I
// vs2, vd: vmv.v.v and vmv.v.x spellings
vecVMVV
// $imm, vd: vmv.v.i
vecVMVI
// fs1, vd: vfmv.v.f
vecVFMVVF
// vs2, vd: two-operand forms with a fixed rs1 field
vecTWO
// xs1|fs1, vd: two-operand forms with a fixed vs2 field
vecTWOX
// vs1, vs2, v0, vd: the carry forms, mask mandatory
vecADC
// vs1, vs2, v0, vd: the merge forms, mask mandatory
vecMERGE
// vs1, vs2, vd: the carry-producing forms
vecVMADC
// vs2 [, v0], vd with rs1 = x0: VNEGV and friends
vecNEG
// vs2 [, v0], vd: XOR with -1
vecVNOT
// vs2, vs1 [, v0], vd: the swapped comparisons
vecSWAPVV
// $imm, vs2 [, v0], vd: swapped, imm-1
vecSWAPVI
// vs2 [, v0], vd with rs1 = vs2: VFABSV and VFNEGV
vecVFABS
// vs1, vs2, vd: the mask-mask forms, no mask operand
vecMM
// vd: the whole-mask clears and sets
vecVMCLR
// [v0,] vd: the element index
vecVID
)
// riscvVecOp is one vector arithmetic instruction's fixed fields: the
// funct3 subclass, the fixed rs1 field (the unary constants), the fixed
// funct7 (funct6 << 1 with the fixed vm bit), the operand class and the
// immediate form the first operand takes.
type riscvVecOp struct {
funct3 uint32
rs1 uint32
funct7 uint32
class riscvVecOpClass
imm bool // the first operand is an immediate in the rs1 field
immU bool // that immediate is unsigned [0, 31] (shifts and slides)
}
// riscvVecOps maps every vector arithmetic mnemonic the toolchain knows
// onto its fields. The loads and stores and the configuration setters
// have their own paths.
var riscvVecOps = map[string]riscvVecOp{
"VAADDUVV": {0x2, 0x0, 0x10, 0, false, false},
"VAADDUVX": {0x6, 0x0, 0x10, 0, false, false},
"VAADDVV": {0x2, 0x0, 0x12, 0, false, false},
"VAADDVX": {0x6, 0x0, 0x12, 0, false, false},
"VADCVIM": {0x3, 0x0, 0x20, 9, true, false},
"VADCVVM": {0x0, 0x0, 0x20, 9, false, false},
"VADCVXM": {0x4, 0x0, 0x20, 9, false, false},
"VADDVI": {0x3, 0x0, 0x00, 0, true, false},
"VADDVV": {0x0, 0x0, 0x00, 0, false, false},
"VADDVX": {0x4, 0x0, 0x00, 0, false, false},
"VANDNVV": {0x0, 0x0, 0x02, 0, false, false},
"VANDNVX": {0x4, 0x0, 0x02, 0, false, false},
"VANDVI": {0x3, 0x0, 0x12, 0, true, false},
"VANDVV": {0x0, 0x0, 0x12, 0, false, false},
"VANDVX": {0x4, 0x0, 0x12, 0, false, false},
"VASUBUVV": {0x2, 0x0, 0x14, 0, false, false},
"VASUBUVX": {0x6, 0x0, 0x14, 0, false, false},
"VASUBVV": {0x2, 0x0, 0x16, 0, false, false},
"VASUBVX": {0x6, 0x0, 0x16, 0, false, false},
"VBREV8V": {0x2, 0x8, 0x24, 2, false, false},
"VBREVV": {0x2, 0xa, 0x24, 2, false, false},
"VCLMULHVV": {0x2, 0x0, 0x1a, 0, false, false},
"VCLMULHVX": {0x6, 0x0, 0x1a, 0, false, false},
"VCLMULVV": {0x2, 0x0, 0x18, 0, false, false},
"VCLMULVX": {0x6, 0x0, 0x18, 0, false, false},
"VCLZV": {0x2, 0xc, 0x24, 2, false, false},
"VCOMPRESSVM": {0x2, 0x0, 0x2f, 17, false, false},
"VCPOPM": {0x2, 0x10, 0x20, 3, false, false},
"VCPOPV": {0x2, 0xe, 0x24, 2, false, false},
"VCTZV": {0x2, 0xd, 0x24, 2, false, false},
"VDIVUVV": {0x2, 0x0, 0x40, 0, false, false},
"VDIVUVX": {0x6, 0x0, 0x40, 0, false, false},
"VDIVVV": {0x2, 0x0, 0x42, 0, false, false},
"VDIVVX": {0x6, 0x0, 0x42, 0, false, false},
"VFABSV": {0x1, 0x0, 0x14, 16, false, false},
"VFADDVF": {0x5, 0x0, 0x00, 0, false, false},
"VFADDVV": {0x1, 0x0, 0x00, 0, false, false},
"VFCLASSV": {0x1, 0x10, 0x26, 2, false, false},
"VFCVTFXUV": {0x1, 0x2, 0x24, 2, false, false},
"VFCVTFXV": {0x1, 0x3, 0x24, 2, false, false},
"VFCVTRTZXFV": {0x1, 0x7, 0x24, 2, false, false},
"VFCVTRTZXUFV": {0x1, 0x6, 0x24, 2, false, false},
"VFCVTXFV": {0x1, 0x1, 0x24, 2, false, false},
"VFCVTXUFV": {0x1, 0x0, 0x24, 2, false, false},
"VFDIVVF": {0x5, 0x0, 0x40, 0, false, false},
"VFDIVVV": {0x1, 0x0, 0x40, 0, false, false},
"VFIRSTM": {0x2, 0x11, 0x20, 3, false, false},
"VFMACCVF": {0x5, 0x0, 0x58, 1, false, false},
"VFMACCVV": {0x1, 0x0, 0x58, 1, false, false},
"VFMADDVF": {0x5, 0x0, 0x50, 1, false, false},
"VFMADDVV": {0x1, 0x0, 0x50, 1, false, false},
"VFMAXVF": {0x5, 0x0, 0x0c, 0, false, false},
"VFMAXVV": {0x1, 0x0, 0x0c, 0, false, false},
"VFMERGEVFM": {0x5, 0x0, 0x2e, 10, false, false},
"VFMINVF": {0x5, 0x0, 0x08, 0, false, false},
"VFMINVV": {0x1, 0x0, 0x08, 0, false, false},
"VFMSACVF": {0x5, 0x0, 0x5c, 1, false, false},
"VFMSACVV": {0x1, 0x0, 0x5c, 1, false, false},
"VFMSUBVF": {0x5, 0x0, 0x54, 1, false, false},
"VFMSUBVV": {0x1, 0x0, 0x54, 1, false, false},
"VFMULVF": {0x5, 0x0, 0x48, 0, false, false},
"VFMULVV": {0x1, 0x0, 0x48, 0, false, false},
"VFMVFS": {0x1, 0x0, 0x21, 7, false, false},
"VFMVSF": {0x5, 0x0, 0x21, 8, false, false},
"VFMVVF": {0x5, 0x0, 0x2f, 6, false, false},
"VFNCVTFFW": {0x1, 0x14, 0x24, 2, false, false},
"VFNCVTFXUW": {0x1, 0x12, 0x24, 2, false, false},
"VFNCVTFXW": {0x1, 0x13, 0x24, 2, false, false},
"VFNCVTRODFFW": {0x1, 0x15, 0x24, 2, false, false},
"VFNCVTRTZXFW": {0x1, 0x17, 0x24, 2, false, false},
"VFNCVTRTZXUFW": {0x1, 0x16, 0x24, 2, false, false},
"VFNCVTXFW": {0x1, 0x11, 0x24, 2, false, false},
"VFNCVTXUFW": {0x1, 0x10, 0x24, 2, false, false},
"VFNEGV": {0x1, 0x0, 0x12, 16, false, false},
"VFNMACCVF": {0x5, 0x0, 0x5a, 1, false, false},
"VFNMACCVV": {0x1, 0x0, 0x5a, 1, false, false},
"VFNMADDVF": {0x5, 0x0, 0x52, 1, false, false},
"VFNMADDVV": {0x1, 0x0, 0x52, 1, false, false},
"VFNMSACVF": {0x5, 0x0, 0x5e, 1, false, false},
"VFNMSACVV": {0x1, 0x0, 0x5e, 1, false, false},
"VFNMSUBVF": {0x5, 0x0, 0x56, 1, false, false},
"VFNMSUBVV": {0x1, 0x0, 0x56, 1, false, false},
"VFRDIVVF": {0x5, 0x0, 0x42, 0, false, false},
"VFREC7V": {0x1, 0x5, 0x26, 2, false, false},
"VFREDMAXVS": {0x1, 0x0, 0x0e, 0, false, false},
"VFREDMINVS": {0x1, 0x0, 0x0a, 0, false, false},
"VFREDOSUMVS": {0x1, 0x0, 0x06, 0, false, false},
"VFREDUSUMVS": {0x1, 0x0, 0x02, 0, false, false},
"VFRSQRT7V": {0x1, 0x4, 0x26, 2, false, false},
"VFRSUBVF": {0x5, 0x0, 0x4e, 0, false, false},
"VFSGNJNVF": {0x5, 0x0, 0x12, 0, false, false},
"VFSGNJNVV": {0x1, 0x0, 0x12, 0, false, false},
"VFSGNJVF": {0x5, 0x0, 0x10, 0, false, false},
"VFSGNJVV": {0x1, 0x0, 0x10, 0, false, false},
"VFSGNJXVF": {0x5, 0x0, 0x14, 0, false, false},
"VFSGNJXVV": {0x1, 0x0, 0x14, 0, false, false},
"VFSLIDE1DOWNVF": {0x5, 0x0, 0x1e, 0, false, false},
"VFSLIDE1UPVF": {0x5, 0x0, 0x1c, 0, false, false},
"VFSQRTV": {0x1, 0x0, 0x26, 2, false, false},
"VFSUBVF": {0x5, 0x0, 0x04, 0, false, false},
"VFSUBVV": {0x1, 0x0, 0x04, 0, false, false},
"VFWADDVF": {0x5, 0x0, 0x60, 0, false, false},
"VFWADDVV": {0x1, 0x0, 0x60, 0, false, false},
"VFWADDWF": {0x5, 0x0, 0x68, 0, false, false},
"VFWADDWV": {0x1, 0x0, 0x68, 0, false, false},
"VFWCVTFFV": {0x1, 0xc, 0x24, 2, false, false},
"VFWCVTFXUV": {0x1, 0xa, 0x24, 2, false, false},
"VFWCVTFXV": {0x1, 0xb, 0x24, 2, false, false},
"VFWCVTRTZXFV": {0x1, 0xf, 0x24, 2, false, false},
"VFWCVTRTZXUFV": {0x1, 0xe, 0x24, 2, false, false},
"VFWCVTXFV": {0x1, 0x9, 0x24, 2, false, false},
"VFWCVTXUFV": {0x1, 0x8, 0x24, 2, false, false},
"VFWMACCVF": {0x5, 0x0, 0x78, 1, false, false},
"VFWMACCVV": {0x1, 0x0, 0x78, 1, false, false},
"VFWMSACVF": {0x5, 0x0, 0x7c, 1, false, false},
"VFWMSACVV": {0x1, 0x0, 0x7c, 1, false, false},
"VFWMULVF": {0x5, 0x0, 0x70, 0, false, false},
"VFWMULVV": {0x1, 0x0, 0x70, 0, false, false},
"VFWNMACCVF": {0x5, 0x0, 0x7a, 1, false, false},
"VFWNMACCVV": {0x1, 0x0, 0x7a, 1, false, false},
"VFWNMSACVF": {0x5, 0x0, 0x7e, 1, false, false},
"VFWNMSACVV": {0x1, 0x0, 0x7e, 1, false, false},
"VFWREDOSUMVS": {0x1, 0x0, 0x66, 0, false, false},
"VFWREDUSUMVS": {0x1, 0x0, 0x62, 0, false, false},
"VFWSUBVF": {0x5, 0x0, 0x64, 0, false, false},
"VFWSUBVV": {0x1, 0x0, 0x64, 0, false, false},
"VFWSUBWF": {0x5, 0x0, 0x6c, 0, false, false},
"VFWSUBWV": {0x1, 0x0, 0x6c, 0, false, false},
"VIDV": {0x2, 0x11, 0x28, 19, false, false},
"VIOTAM": {0x2, 0x10, 0x28, 3, false, false},
"VMACCVV": {0x2, 0x0, 0x5a, 1, false, false},
"VMACCVX": {0x6, 0x0, 0x5a, 1, false, false},
"VMADCVI": {0x3, 0x0, 0x23, 11, true, false},
"VMADCVIM": {0x3, 0x0, 0x22, 10, true, false},
"VMADCVV": {0x0, 0x0, 0x23, 11, false, false},
"VMADCVVM": {0x0, 0x0, 0x22, 10, false, false},
"VMADCVX": {0x4, 0x0, 0x23, 11, false, false},
"VMADCVXM": {0x4, 0x0, 0x22, 10, false, false},
"VMADDVV": {0x2, 0x0, 0x52, 1, false, false},
"VMADDVX": {0x6, 0x0, 0x52, 1, false, false},
"VMANDMM": {0x2, 0x0, 0x33, 17, false, false},
"VMANDNMM": {0x2, 0x0, 0x31, 17, false, false},
"VMAXUVV": {0x0, 0x0, 0x0c, 0, false, false},
"VMAXUVX": {0x4, 0x0, 0x0c, 0, false, false},
"VMAXVV": {0x0, 0x0, 0x0e, 0, false, false},
"VMAXVX": {0x4, 0x0, 0x0e, 0, false, false},
"VMCLRM": {0x2, 0x0, 0x37, 18, false, false},
"VMERGEVIM": {0x3, 0x0, 0x2e, 10, true, false},
"VMERGEVVM": {0x0, 0x0, 0x2e, 10, false, false},
"VMERGEVXM": {0x4, 0x0, 0x2e, 10, false, false},
"VMFEQVF": {0x5, 0x0, 0x30, 0, false, false},
"VMFEQVV": {0x1, 0x0, 0x30, 0, false, false},
"VMFGEVF": {0x5, 0x0, 0x3e, 0, false, false},
"VMFGEVV": {0x1, 0x0, 0x32, 14, false, false},
"VMFGTVF": {0x5, 0x0, 0x3a, 0, false, false},
"VMFGTVV": {0x1, 0x0, 0x36, 14, false, false},
"VMFLEVF": {0x5, 0x0, 0x32, 0, false, false},
"VMFLEVV": {0x1, 0x0, 0x32, 0, false, false},
"VMFLTVF": {0x5, 0x0, 0x36, 0, false, false},
"VMFLTVV": {0x1, 0x0, 0x36, 0, false, false},
"VMFNEVF": {0x5, 0x0, 0x38, 0, false, false},
"VMFNEVV": {0x1, 0x0, 0x38, 0, false, false},
"VMINUVV": {0x0, 0x0, 0x08, 0, false, false},
"VMINUVX": {0x4, 0x0, 0x08, 0, false, false},
"VMINVV": {0x0, 0x0, 0x0a, 0, false, false},
"VMINVX": {0x4, 0x0, 0x0a, 0, false, false},
"VMMVM": {0x2, 0x0, 0x33, 17, false, false},
"VMNANDMM": {0x2, 0x0, 0x3b, 17, false, false},
"VMNORMM": {0x2, 0x0, 0x3d, 17, false, false},
"VMNOTM": {0x2, 0x0, 0x3b, 17, false, false},
"VMORMM": {0x2, 0x0, 0x35, 17, false, false},
"VMORNMM": {0x2, 0x0, 0x39, 17, false, false},
"VMSBCVV": {0x0, 0x0, 0x27, 11, false, false},
"VMSBCVVM": {0x0, 0x0, 0x26, 10, false, false},
"VMSBCVX": {0x4, 0x0, 0x27, 11, false, false},
"VMSBCVXM": {0x4, 0x0, 0x26, 10, false, false},
"VMSBFM": {0x2, 0x1, 0x28, 3, false, false},
"VMSEQVI": {0x3, 0x0, 0x30, 0, true, false},
"VMSEQVV": {0x0, 0x0, 0x30, 0, false, false},
"VMSEQVX": {0x4, 0x0, 0x30, 0, false, false},
"VMSETM": {0x2, 0x0, 0x3f, 18, false, false},
"VMSGEUVI": {0x3, 0x0, 0x3c, 15, true, false},
"VMSGEUVV": {0x0, 0x0, 0x38, 14, false, false},
"VMSGEVI": {0x3, 0x0, 0x3e, 15, true, false},
"VMSGEVV": {0x0, 0x0, 0x3a, 14, false, false},
"VMSGTUVI": {0x3, 0x0, 0x3c, 0, true, false},
"VMSGTUVV": {0x0, 0x0, 0x34, 14, false, false},
"VMSGTUVX": {0x4, 0x0, 0x3c, 0, false, false},
"VMSGTVI": {0x3, 0x0, 0x3e, 0, true, false},
"VMSGTVV": {0x0, 0x0, 0x36, 14, false, false},
"VMSGTVX": {0x4, 0x0, 0x3e, 0, false, false},
"VMSIFM": {0x2, 0x3, 0x28, 3, false, false},
"VMSLEUVI": {0x3, 0x0, 0x38, 0, true, false},
"VMSLEUVV": {0x0, 0x0, 0x38, 0, false, false},
"VMSLEUVX": {0x4, 0x0, 0x38, 0, false, false},
"VMSLEVI": {0x3, 0x0, 0x3a, 0, true, false},
"VMSLEVV": {0x0, 0x0, 0x3a, 0, false, false},
"VMSLEVX": {0x4, 0x0, 0x3a, 0, false, false},
"VMSLTUVI": {0x3, 0x0, 0x38, 15, true, false},
"VMSLTUVV": {0x0, 0x0, 0x34, 0, false, false},
"VMSLTUVX": {0x4, 0x0, 0x34, 0, false, false},
"VMSLTVI": {0x3, 0x0, 0x3a, 15, true, false},
"VMSLTVV": {0x0, 0x0, 0x36, 0, false, false},
"VMSLTVX": {0x4, 0x0, 0x36, 0, false, false},
"VMSNEVI": {0x3, 0x0, 0x32, 0, true, false},
"VMSNEVV": {0x0, 0x0, 0x32, 0, false, false},
"VMSNEVX": {0x4, 0x0, 0x32, 0, false, false},
"VMSOFM": {0x2, 0x2, 0x28, 3, false, false},
"VMULHSUVV": {0x2, 0x0, 0x4c, 0, false, false},
"VMULHSUVX": {0x6, 0x0, 0x4c, 0, false, false},
"VMULHUVV": {0x2, 0x0, 0x48, 0, false, false},
"VMULHUVX": {0x6, 0x0, 0x48, 0, false, false},
"VMULHVV": {0x2, 0x0, 0x4e, 0, false, false},
"VMULHVX": {0x6, 0x0, 0x4e, 0, false, false},
"VMULVV": {0x2, 0x0, 0x4a, 0, false, false},
"VMULVX": {0x6, 0x0, 0x4a, 0, false, false},
"VMV1RV": {0x3, 0x0, 0x4f, 7, false, false},
"VMV2RV": {0x3, 0x1, 0x4f, 7, false, false},
"VMV4RV": {0x3, 0x3, 0x4f, 7, false, false},
"VMV8RV": {0x3, 0x7, 0x4f, 7, false, false},
"VMVSX": {0x6, 0x0, 0x21, 8, false, false},
"VMVVI": {0x3, 0x0, 0x2f, 5, true, false},
"VMVVV": {0x0, 0x0, 0x2f, 4, false, false},
"VMVVX": {0x4, 0x0, 0x2f, 4, false, false},
"VMVXS": {0x2, 0x0, 0x21, 7, false, false},
"VMXNORMM": {0x2, 0x0, 0x3f, 17, false, false},
"VMXORMM": {0x2, 0x0, 0x37, 17, false, false},
"VNCLIPUWI": {0x3, 0x0, 0x5c, 0, true, true},
"VNCLIPUWV": {0x0, 0x0, 0x5c, 0, false, false},
"VNCLIPUWX": {0x4, 0x0, 0x5c, 0, false, false},
"VNCLIPWI": {0x3, 0x0, 0x5e, 0, true, true},
"VNCLIPWV": {0x0, 0x0, 0x5e, 0, false, false},
"VNCLIPWX": {0x4, 0x0, 0x5e, 0, false, false},
"VNCVTXXW": {0x4, 0x0, 0x58, 12, false, false},
"VNEGV": {0x4, 0x0, 0x06, 12, false, false},
"VNMSACVV": {0x2, 0x0, 0x5e, 1, false, false},
"VNMSACVX": {0x6, 0x0, 0x5e, 1, false, false},
"VNMSUBVV": {0x2, 0x0, 0x56, 1, false, false},
"VNMSUBVX": {0x6, 0x0, 0x56, 1, false, false},
"VNOTV": {0x3, 0x0, 0x16, 13, true, false},
"VNSRAWI": {0x3, 0x0, 0x5a, 0, true, true},
"VNSRAWV": {0x0, 0x0, 0x5a, 0, false, false},
"VNSRAWX": {0x4, 0x0, 0x5a, 0, false, false},
"VNSRLWI": {0x3, 0x0, 0x58, 0, true, true},
"VNSRLWV": {0x0, 0x0, 0x58, 0, false, false},
"VNSRLWX": {0x4, 0x0, 0x58, 0, false, false},
"VORVI": {0x3, 0x0, 0x14, 0, true, false},
"VORVV": {0x0, 0x0, 0x14, 0, false, false},
"VORVX": {0x4, 0x0, 0x14, 0, false, false},
"VREDANDVS": {0x2, 0x0, 0x02, 0, false, false},
"VREDMAXUVS": {0x2, 0x0, 0x0c, 0, false, false},
"VREDMAXVS": {0x2, 0x0, 0x0e, 0, false, false},
"VREDMINUVS": {0x2, 0x0, 0x08, 0, false, false},
"VREDMINVS": {0x2, 0x0, 0x0a, 0, false, false},
"VREDORVS": {0x2, 0x0, 0x04, 0, false, false},
"VREDSUMVS": {0x2, 0x0, 0x00, 0, false, false},
"VREDXORVS": {0x2, 0x0, 0x06, 0, false, false},
"VREMUVV": {0x2, 0x0, 0x44, 0, false, false},
"VREMUVX": {0x6, 0x0, 0x44, 0, false, false},
"VREMVV": {0x2, 0x0, 0x46, 0, false, false},
"VREMVX": {0x6, 0x0, 0x46, 0, false, false},
"VREV8V": {0x2, 0x9, 0x24, 2, false, false},
"VRGATHEREI16VV": {0x0, 0x0, 0x1c, 0, false, false},
"VRGATHERVI": {0x3, 0x0, 0x18, 0, true, true},
"VRGATHERVV": {0x0, 0x0, 0x18, 0, false, false},
"VRGATHERVX": {0x4, 0x0, 0x18, 0, false, false},
"VROLVV": {0x0, 0x0, 0x2a, 0, false, false},
"VROLVX": {0x4, 0x0, 0x2a, 0, false, false},
"VRORVI": {0x3, 0x0, 0x28, 0, true, true},
"VRORVV": {0x0, 0x0, 0x28, 0, false, false},
"VRORVX": {0x4, 0x0, 0x28, 0, false, false},
"VRSUBVI": {0x3, 0x0, 0x06, 0, true, false},
"VRSUBVX": {0x4, 0x0, 0x06, 0, false, false},
"VSADDUVI": {0x3, 0x0, 0x40, 0, true, false},
"VSADDUVV": {0x0, 0x0, 0x40, 0, false, false},
"VSADDUVX": {0x4, 0x0, 0x40, 0, false, false},
"VSADDVI": {0x3, 0x0, 0x42, 0, true, false},
"VSADDVV": {0x0, 0x0, 0x42, 0, false, false},
"VSADDVX": {0x4, 0x0, 0x42, 0, false, false},
"VSBCVVM": {0x0, 0x0, 0x24, 9, false, false},
"VSBCVXM": {0x4, 0x0, 0x24, 9, false, false},
"VSEXTVF2": {0x2, 0x7, 0x24, 2, false, false},
"VSEXTVF4": {0x2, 0x5, 0x24, 2, false, false},
"VSEXTVF8": {0x2, 0x3, 0x24, 2, false, false},
"VSLIDE1DOWNVX": {0x6, 0x0, 0x1e, 0, false, false},
"VSLIDE1UPVX": {0x6, 0x0, 0x1c, 0, false, false},
"VSLIDEDOWNVI": {0x3, 0x0, 0x1e, 0, true, true},
"VSLIDEDOWNVX": {0x4, 0x0, 0x1e, 0, false, false},
"VSLIDEUPVI": {0x3, 0x0, 0x1c, 0, true, true},
"VSLIDEUPVX": {0x4, 0x0, 0x1c, 0, false, false},
"VSLLVI": {0x3, 0x0, 0x4a, 0, true, true},
"VSLLVV": {0x0, 0x0, 0x4a, 0, false, false},
"VSLLVX": {0x4, 0x0, 0x4a, 0, false, false},
"VSMULVV": {0x0, 0x0, 0x4e, 0, false, false},
"VSMULVX": {0x4, 0x0, 0x4e, 0, false, false},
"VSRAVI": {0x3, 0x0, 0x52, 0, true, true},
"VSRAVV": {0x0, 0x0, 0x52, 0, false, false},
"VSRAVX": {0x4, 0x0, 0x52, 0, false, false},
"VSRLVI": {0x3, 0x0, 0x50, 0, true, true},
"VSRLVV": {0x0, 0x0, 0x50, 0, false, false},
"VSRLVX": {0x4, 0x0, 0x50, 0, false, false},
"VSSRAVI": {0x3, 0x0, 0x56, 0, true, true},
"VSSRAVV": {0x0, 0x0, 0x56, 0, false, false},
"VSSRAVX": {0x4, 0x0, 0x56, 0, false, false},
"VSSRLVI": {0x3, 0x0, 0x54, 0, true, true},
"VSSRLVV": {0x0, 0x0, 0x54, 0, false, false},
"VSSRLVX": {0x4, 0x0, 0x54, 0, false, false},
"VSSUBUVV": {0x0, 0x0, 0x44, 0, false, false},
"VSSUBUVX": {0x4, 0x0, 0x44, 0, false, false},
"VSSUBVV": {0x0, 0x0, 0x46, 0, false, false},
"VSSUBVX": {0x4, 0x0, 0x46, 0, false, false},
"VSUBVV": {0x0, 0x0, 0x04, 0, false, false},
"VSUBVX": {0x4, 0x0, 0x04, 0, false, false},
"VWADDUVV": {0x2, 0x0, 0x60, 0, false, false},
"VWADDUVX": {0x6, 0x0, 0x60, 0, false, false},
"VWADDUWV": {0x2, 0x0, 0x68, 0, false, false},
"VWADDUWX": {0x6, 0x0, 0x68, 0, false, false},
"VWADDVV": {0x2, 0x0, 0x62, 0, false, false},
"VWADDVX": {0x6, 0x0, 0x62, 0, false, false},
"VWADDWV": {0x2, 0x0, 0x6a, 0, false, false},
"VWADDWX": {0x6, 0x0, 0x6a, 0, false, false},
"VWCVTUXXV": {0x6, 0x0, 0x60, 12, false, false},
"VWCVTXXV": {0x6, 0x0, 0x62, 12, false, false},
"VWMACCSUVV": {0x2, 0x0, 0x7e, 1, false, false},
"VWMACCSUVX": {0x6, 0x0, 0x7e, 1, false, false},
"VWMACCUSVX": {0x6, 0x0, 0x7c, 1, false, false},
"VWMACCUVV": {0x2, 0x0, 0x78, 1, false, false},
"VWMACCUVX": {0x6, 0x0, 0x78, 1, false, false},
"VWMACCVV": {0x2, 0x0, 0x7a, 1, false, false},
"VWMACCVX": {0x6, 0x0, 0x7a, 1, false, false},
"VWMULSUVV": {0x2, 0x0, 0x74, 0, false, false},
"VWMULSUVX": {0x6, 0x0, 0x74, 0, false, false},
"VWMULUVV": {0x2, 0x0, 0x70, 0, false, false},
"VWMULUVX": {0x6, 0x0, 0x70, 0, false, false},
"VWMULVV": {0x2, 0x0, 0x76, 0, false, false},
"VWMULVX": {0x6, 0x0, 0x76, 0, false, false},
"VWREDSUMUVS": {0x0, 0x0, 0x60, 0, false, false},
"VWREDSUMVS": {0x0, 0x0, 0x62, 0, false, false},
"VWSLLVI": {0x3, 0x0, 0x6a, 0, true, true},
"VWSLLVV": {0x0, 0x0, 0x6a, 0, false, false},
"VWSLLVX": {0x4, 0x0, 0x6a, 0, false, false},
"VWSUBUVV": {0x2, 0x0, 0x64, 0, false, false},
"VWSUBUVX": {0x6, 0x0, 0x64, 0, false, false},
"VWSUBUWV": {0x2, 0x0, 0x6c, 0, false, false},
"VWSUBUWX": {0x6, 0x0, 0x6c, 0, false, false},
"VWSUBVV": {0x2, 0x0, 0x66, 0, false, false},
"VWSUBVX": {0x6, 0x0, 0x66, 0, false, false},
"VWSUBWV": {0x2, 0x0, 0x6e, 0, false, false},
"VWSUBWX": {0x6, 0x0, 0x6e, 0, false, false},
"VXORVI": {0x3, 0x0, 0x16, 0, true, false},
"VXORVV": {0x0, 0x0, 0x16, 0, false, false},
"VXORVX": {0x4, 0x0, 0x16, 0, false, false},
"VZEXTVF2": {0x2, 0x6, 0x24, 2, false, false},
"VZEXTVF4": {0x2, 0x4, 0x24, 2, false, false},
"VZEXTVF8": {0x2, 0x2, 0x24, 2, false, false},
}
// ---- RVC (compressed) encoding helpers ----
// isRVCIntReg reports whether a register number can be encoded in the 3-bit
// prime register field used by compressed instructions (x8-x15).
func isRVCIntReg(r int) bool { return r >= 8 && r <= 15 }
// rvcReg3 returns the 3-bit encoding for registers x8-x15 (0-7).
func rvcReg3(r int) uint32 { return uint32(r - 8) }
// rvcCR encodes a CR-type (register) compressed instruction.
// Format: funct4 | rd/rs1 | rs2 | op=2.
func rvcCR(funct4, rd, rs2 uint32) uint16 {
return uint16((funct4 << 12) | (rd << 7) | (rs2 << 2) | 0x2)
}
// rvcCI encodes a CI-type (immediate) compressed instruction.
// Used for C.ADDI, C.LI, C.LUI, C.ADDIW, linear 6-bit immediate.
func rvcCI(funct3, rd uint32, imm uint32) uint16 {
return uint16((funct3 << 13) | ((imm>>5)&1)<<12 | (rd << 7) | (imm&0x1F)<<2 | 0x1)
}
// rvcSLLI encodes C.SLLI, which shares funct3=0 with C.ADDI but lives in the
// op=10 quadrant (unlike C.ADDI's op=01).
func rvcSLLI(rd, shamt uint32) uint16 {
return uint16(((shamt>>5)&1)<<12 | (rd << 7) | (shamt&0x1F)<<2 | 0x2)
}
// encodeRVCPattern extracts the bits listed in pattern (MSB first) from imm
// into a packed value, matching cmd/internal/obj/riscv's encodeBitPattern.
func encodeRVCPattern(imm uint32, pattern []int) uint32 {
packed := uint32(0)
for _, bit := range pattern {
packed = packed<<1 | (imm>>bit)&1
}
return packed
}
// rvcLSP encodes a stack-relative compressed load (op=10 quadrant): C.LWSP
// (funct3=2, 4-byte scale), C.LDSP (funct3=3) or C.FLDSP (funct3=1, 8-byte
// scale). offset is the full byte offset.
func rvcLSP(funct3, rd uint32, offset uint32) uint16 {
pattern := []int{5, 4, 3, 8, 7, 6}
if funct3 == 0x2 {
pattern = []int{5, 4, 3, 2, 7, 6}
}
packed := uint32(0)
for i, b := range pattern {
packed |= ((offset >> b) & 1) << (5 - i)
}
return uint16((funct3 << 13) | ((packed>>5)&1)<<12 | (rd << 7) | (packed&0x1F)<<2 | 0x2)
}
// rvcSSP encodes a stack-relative compressed store (op=10 quadrant): C.SWSP
// (funct3=6, 4-byte scale), C.SDSP (funct3=7) or C.FSDSP (funct3=5, 8-byte
// scale). offset is the full byte offset.
func rvcSSP(funct3, rs2 uint32, offset uint32) uint16 {
pattern := []int{5, 4, 3, 8, 7, 6}
if funct3 == 0x6 {
pattern = []int{5, 4, 3, 2, 7, 6}
}
packed := uint32(0)
for i, b := range pattern {
packed |= ((offset >> b) & 1) << (5 - i)
}
return uint16((funct3 << 13) | (packed << 7) | (rs2 << 2) | 0x2)
}
// rvcCL encodes a register-relative compressed load (op=00 quadrant): C.LW
// (funct3=2), C.LD (funct3=3) or C.FLD (funct3=1). imm is the full byte
// offset; the immediate bits are extracted per the RISC-V CL format.
func rvcCL(funct3, rd, rs1 uint32, imm uint32) uint16 {
pattern := []int{5, 4, 3, 7, 6}
if funct3 == 0x2 {
pattern = []int{5, 4, 3, 2, 6}
}
packed := encodeRVCPattern(imm, pattern)
return uint16((funct3 << 13) | ((packed>>2)&0x7)<<10 | (rs1 << 7) | ((packed & 0x3) << 5) | (rd << 2))
}
// rvcCS encodes a register-relative compressed store (op=00 quadrant): C.SW
// (funct3=6), C.SD (funct3=7) or C.FSD (funct3=5). imm is the full byte
// offset; the immediate bits are extracted per the RISC-V CS format, with the
// same five-bit patterns as the load side ({5,4,3,7,6} and {5,4,3,2,6},
// matching the toolchain's encodeCS).
func rvcCS(funct3, rs2, rs1 uint32, imm uint32) uint16 {
pattern := []int{5, 4, 3, 7, 6}
if funct3 == 0x6 {
pattern = []int{5, 4, 3, 2, 6}
}
packed := encodeRVCPattern(imm, pattern)
return uint16((funct3 << 13) | ((packed>>2)&0x7)<<10 | (rs1 << 7) | ((packed & 0x3) << 5) | (rs2 << 2))
}
// rvcCIW encodes a CIW-type compressed immediate wide instruction: C.ADDI4SPN
// (funct3=0). imm is the raw byte offset.
func rvcCIW(funct3, rd uint32, imm uint32) uint16 {
packed := encodeRVCPattern(imm, []int{5, 4, 9, 8, 7, 6, 2, 3})
return uint16((funct3 << 13) | (packed << 5) | (rd << 2))
}
// rvcCA encodes a CA-type (arithmetic) compressed instruction.
// Format: funct6[15:10] | rd'/rs1'[9:7] | funct2[6:5] | rs2'[4:2] | op=01.
func rvcCA(funct6, funct2, rd, rs2 uint32) uint16 {
return uint16((funct6 << 10) | (rd << 7) | (funct2 << 5) | (rs2 << 2) | 0x1)
}
// rvcCBShift encodes a CB-type shift/immediate compressed instruction
// (C.SRLI, C.SRAI, C.ANDI). rd is the 3-bit prime-register index; imm is
// the 6-bit shamt/immediate; funct2 selects the operation (0=SRLI, 1=SRAI,
// 2=ANDI).
func rvcCBShift(funct2, rd, imm uint32) uint16 {
return uint16((0x4 << 13) | ((imm>>5)&1)<<12 | (funct2 << 10) | (rd << 7) | (imm&0x1F)<<2 | 0x1)
}
// rvcADDI16SP encodes C.ADDI16SP: ADDI rd, imm, rd for the stack pointer
// with a 10-bit signed, 16-byte-scaled immediate. imm is the raw byte
// offset; the immediate bits are extracted in the order [9|4|6|8:7|5].
func rvcADDI16SP(rd uint32, imm int32) uint16 {
u := uint32(imm)
packed := uint32(0)
for _, bit := range []uint{9, 4, 6, 8, 7, 5} {
packed = packed<<1 | (u>>bit)&1
}
return uint16((0x3 << 13) | ((packed>>5)&1)<<12 | (rd << 7) | (packed&0x1F)<<2 | 0x1)
}