feat(riscv): add RISC-V RV64A, FP and CSR instruction support

Assisted-by: Kimi K3
This commit is contained in:
2026-08-02 11:45:00 +02:00
parent 801fb963c9
commit eee7a6d4a4
6 changed files with 455 additions and 0 deletions
+187
View File
@@ -90,6 +90,66 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
}
// FP conversion / move instructions use a separate table (rs2 encodes
// the conversion type, not a register). Handle them before the main
// table lookup.
if cvtEnc, ok := riscvCvtTable[mnem]; ok {
if len(ops) != 2 {
return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops))
}
rs1 := regFromOperand(ops[0])
rd := regFromOperand(ops[1])
if rd < 0 || rs1 < 0 {
return nil, fmt.Errorf("invalid operand in %s", mnem)
}
word := riscvCvtType(cvtEnc, rd, rs1)
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
}
// R4-type fused multiply-add: INSTR rs1, rs2, rs3, rd (destination last).
if fmaEnc, ok := riscvFmaTable[mnem]; ok {
if len(ops) != 4 {
return nil, fmt.Errorf("%s expects 4 operands, got %d", mnem, len(ops))
}
rs1 := regFromOperand(ops[0])
rs2 := regFromOperand(ops[1])
rs3 := regFromOperand(ops[2])
rd := regFromOperand(ops[3])
if rd < 0 || rs1 < 0 || rs2 < 0 || rs3 < 0 {
return nil, fmt.Errorf("invalid FP register in %s", mnem)
}
word := riscvFmaType(fmaEnc, rd, rs1, rs2, rs3)
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
}
// CSR instructions: INSTR csr, rs1|uimm, rd (destination last).
if csrEnc, ok := riscvCsrTable[mnem]; ok {
if len(ops) != 3 {
return nil, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops))
}
csr := immFromOperand(ops[0]) // CSR address (12-bit)
rd := regFromOperand(ops[2]) // destination register
if rd < 0 {
return nil, fmt.Errorf("invalid destination register in %s", mnem)
}
var src int
if csrEnc.imm {
// Immediate variant: ops[1] is a 5-bit unsigned immediate.
src = int(immFromOperand(ops[1]))
if src < 0 || src > 31 {
return nil, fmt.Errorf("%s: uimm out of range 0-31", mnem)
}
} else {
// Register variant: ops[1] is a register.
src = regFromOperand(ops[1])
if src < 0 {
return nil, fmt.Errorf("invalid source register in %s", mnem)
}
}
word := riscvCsrType(csrEnc, rd, src, csr)
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
}
enc, ok := riscvInstrTable[mnem]
if !ok {
return nil, fmt.Errorf("unsupported RISC-V instruction %q", mnem)
@@ -106,6 +166,82 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
}
word = riscvRType(enc, rd, rs1, rs2)
// AMO atomics: Plan 9 order is INSTR src, (addr), dst.
case len(ops) == 3 && isAMOInstr(mnem):
rs2 := regFromOperand(ops[0]) // source value
rs1, _ := memFromOperandWithFrame(ops[1], fi) // memory address
rd := regFromOperand(ops[2]) // destination (old value)
if rd < 0 || rs1 < 0 || rs2 < 0 {
return nil, fmt.Errorf("invalid operand in %s", mnem)
}
word = riscvAMOType(enc, rd, rs1, rs2)
// FP arithmetic: Plan 9 order is INSTR src1, src2, dst.
case len(ops) == 3 && isFPArithInstr(mnem):
rs1 := regFromOperand(ops[0])
rs2 := regFromOperand(ops[1])
rd := regFromOperand(ops[2])
if rd < 0 || rs1 < 0 || rs2 < 0 {
return nil, fmt.Errorf("invalid FP register in %s", mnem)
}
word = riscvRType(enc, rd, rs1, rs2)
// FP arithmetic (2-operand): FSQRT src, dst.
case len(ops) == 2 && isFPArithInstr(mnem):
rs1 := regFromOperand(ops[0])
rd := regFromOperand(ops[1])
if rd < 0 || rs1 < 0 {
return nil, fmt.Errorf("invalid FP register in %s", mnem)
}
word = riscvRType(enc, rd, rs1, 0)
// FP loads: INSTR addr, freg (Plan 9: source first).
case len(ops) == 2 && isFPLoadInstr(mnem):
rd := regFromOperand(ops[1])
rs1, imm := memFromOperandWithFrame(ops[0], fi)
if rd < 0 || rs1 < 0 {
return nil, fmt.Errorf("invalid operand in %s", mnem)
}
word = riscvIType(enc, rd, rs1, imm)
// FP stores: INSTR freg, addr (Plan 9: source first).
case len(ops) == 2 && isFPStoreInstr(mnem):
rs2 := regFromOperand(ops[0])
rs1, imm := memFromOperandWithFrame(ops[1], fi)
if rs2 < 0 || rs1 < 0 {
return nil, fmt.Errorf("invalid operand in %s", mnem)
}
word = riscvSType(enc, rs1, rs2, imm)
// LR (load-reserved): INSTR (addr), dst — 2 operands.
case len(ops) == 2 && isLRInstr(mnem):
rs1, _ := memFromOperandWithFrame(ops[0], fi)
rd := regFromOperand(ops[1])
if rd < 0 || rs1 < 0 {
return nil, fmt.Errorf("invalid operand in %s", mnem)
}
word = riscvAMOType(enc, rd, rs1, 0) // rs2=0 for LR
// SC (store-conditional): INSTR src, (addr), dst — 3 operands.
case len(ops) == 3 && isSCInstr(mnem):
rs2 := regFromOperand(ops[0])
rs1, _ := memFromOperandWithFrame(ops[1], fi)
rd := regFromOperand(ops[2])
if rd < 0 || rs1 < 0 || rs2 < 0 {
return nil, fmt.Errorf("invalid operand in %s", mnem)
}
word = riscvAMOType(enc, rd, rs1, rs2)
// FP compare: INSTR src1, src2, dst(int) — result in integer register.
case len(ops) == 3 && isFPCmpInstr(mnem):
rs1 := regFromOperand(ops[0])
rs2 := regFromOperand(ops[1])
rd := regFromOperand(ops[2])
if rd < 0 || rs1 < 0 || rs2 < 0 {
return nil, fmt.Errorf("invalid operand in %s", mnem)
}
word = riscvRType(enc, rd, rs1, rs2)
// I-type with immediate: Plan 9 order is INSTR src, imm, dst.
case len(ops) == 3 && isITypeInstr(mnem):
rs1 := regFromOperand(ops[0]) // source register
@@ -216,6 +352,57 @@ func isUTypeInstr(m string) bool {
return m == "LUI" || m == "AUIPC"
}
func isAMOInstr(m string) bool {
switch m {
case "AMOSWAPW", "AMOSWAPD", "AMOADDW", "AMOADDD",
"AMOANDW", "AMOANDD", "AMOORW", "AMOORD",
"AMOXORW", "AMOXORD", "AMOMAXW", "AMOMAXD",
"AMOMINW", "AMOMIND", "AMOMAXUW", "AMOMAXUD",
"AMOMINUW", "AMOMINUD":
return true
}
return false
}
func isFPArithInstr(m string) bool {
switch m {
case "FADDS", "FSUBS", "FMULS", "FDIVS",
"FADDD", "FSUBD", "FMULD", "FDIVD",
"FSQRTS", "FSQRTD", "FMINS", "FMAXS", "FMIND", "FMAXD":
return true
}
return false
}
func isFPLoadInstr(m string) bool {
return m == "FLW" || m == "FLD"
}
func isFPStoreInstr(m string) bool {
return m == "FSW" || m == "FSD"
}
func isLRInstr(m string) bool {
return m == "LRW" || m == "LRD"
}
func isSCInstr(m string) bool {
return m == "SCW" || m == "SCD"
}
func isFPCmpInstr(m string) bool {
switch m {
case "FEQS", "FLTS", "FLES", "FEQD", "FLTD", "FLED":
return true
}
return false
}
func isFPCvtInstr(m string) bool {
_, ok := riscvCvtTable[m]
return ok
}
// Operand helpers.
func regFromOperand(op *ast.Operand) int {
// Register is in Addr.Base (from (base) syntax) or Addr.Sym.Name (bare ident).