feat(riscv): add RISC-V RV64A, FP and CSR instruction support
Assisted-by: Kimi K3
This commit is contained in:
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user