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
|
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]
|
enc, ok := riscvInstrTable[mnem]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unsupported RISC-V instruction %q", mnem)
|
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)
|
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.
|
// I-type with immediate: Plan 9 order is INSTR src, imm, dst.
|
||||||
case len(ops) == 3 && isITypeInstr(mnem):
|
case len(ops) == 3 && isITypeInstr(mnem):
|
||||||
rs1 := regFromOperand(ops[0]) // source register
|
rs1 := regFromOperand(ops[0]) // source register
|
||||||
@@ -216,6 +352,57 @@ func isUTypeInstr(m string) bool {
|
|||||||
return m == "LUI" || m == "AUIPC"
|
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.
|
// Operand helpers.
|
||||||
func regFromOperand(op *ast.Operand) int {
|
func regFromOperand(op *ast.Operand) int {
|
||||||
// Register is in Addr.Base (from (base) syntax) or Addr.Sym.Name (bare ident).
|
// Register is in Addr.Base (from (base) syntax) or Addr.Sym.Name (bare ident).
|
||||||
|
|||||||
@@ -221,6 +221,63 @@ var riscvInstrTable = map[string]riscvEnc{
|
|||||||
"ECALL": {0x73, 0x0, 0x00},
|
"ECALL": {0x73, 0x0, 0x00},
|
||||||
"EBREAK": {0x73, 0x0, 0x00},
|
"EBREAK": {0x73, 0x0, 0x00},
|
||||||
"FENCE": {0x0F, 0x0, 0x00},
|
"FENCE": {0x0F, 0x0, 0x00},
|
||||||
|
|
||||||
|
// RV64A — atomics (AMO opcode 0x2F).
|
||||||
|
// funct3: 0x2 = word, 0x3 = doubleword. funct5 in bits [31:27].
|
||||||
|
"AMOSWAPW": {0x2F, 0x2, 0x01 << 2},
|
||||||
|
"AMOSWAPD": {0x2F, 0x3, 0x01 << 2},
|
||||||
|
"AMOADDW": {0x2F, 0x2, 0x00 << 2},
|
||||||
|
"AMOADDD": {0x2F, 0x3, 0x00 << 2},
|
||||||
|
"AMOANDW": {0x2F, 0x2, 0x0C << 2},
|
||||||
|
"AMOANDD": {0x2F, 0x3, 0x0C << 2},
|
||||||
|
"AMOORW": {0x2F, 0x2, 0x06 << 2},
|
||||||
|
"AMOORD": {0x2F, 0x3, 0x06 << 2},
|
||||||
|
"AMOXORW": {0x2F, 0x2, 0x04 << 2},
|
||||||
|
"AMOXORD": {0x2F, 0x3, 0x04 << 2},
|
||||||
|
"AMOMAXW": {0x2F, 0x2, 0x14 << 2},
|
||||||
|
"AMOMAXD": {0x2F, 0x3, 0x14 << 2},
|
||||||
|
"AMOMINW": {0x2F, 0x2, 0x10 << 2},
|
||||||
|
"AMOMIND": {0x2F, 0x3, 0x10 << 2},
|
||||||
|
"AMOMAXUW": {0x2F, 0x2, 0x1C << 2},
|
||||||
|
"AMOMAXUD": {0x2F, 0x3, 0x1C << 2},
|
||||||
|
"AMOMINUW": {0x2F, 0x2, 0x18 << 2},
|
||||||
|
"AMOMINUD": {0x2F, 0x3, 0x18 << 2},
|
||||||
|
|
||||||
|
// 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},
|
||||||
|
"FSW": {0x27, 0x2, 0x00},
|
||||||
|
"FSD": {0x27, 0x3, 0x00},
|
||||||
|
// FP min/max.
|
||||||
|
"FMINS": {0x53, 0x0, 0x14},
|
||||||
|
"FMAXS": {0x53, 0x1, 0x14},
|
||||||
|
"FMIND": {0x53, 0x0, 0x15},
|
||||||
|
"FMAXD": {0x53, 0x1, 0x15},
|
||||||
|
|
||||||
|
// RV64A — load-reserved / store-conditional (funct5 0x02 / 0x03).
|
||||||
|
"LRW": {0x2F, 0x2, 0x02 << 2},
|
||||||
|
"LRD": {0x2F, 0x3, 0x02 << 2},
|
||||||
|
"SCW": {0x2F, 0x2, 0x03 << 2},
|
||||||
|
"SCD": {0x2F, 0x3, 0x03 << 2},
|
||||||
|
|
||||||
|
// FP compare — result in integer register (funct7 0x50/0x51).
|
||||||
|
"FEQS": {0x53, 0x2, 0x50},
|
||||||
|
"FLTS": {0x53, 0x1, 0x50},
|
||||||
|
"FLES": {0x53, 0x0, 0x50},
|
||||||
|
"FEQD": {0x53, 0x2, 0x51},
|
||||||
|
"FLTD": {0x53, 0x1, 0x51},
|
||||||
|
"FLED": {0x53, 0x0, 0x51},
|
||||||
}
|
}
|
||||||
|
|
||||||
// riscvRType encodes an R-type instruction: funct7 | rs2 | rs1 | funct3 | rd | opcode.
|
// riscvRType encodes an R-type instruction: funct7 | rs2 | rs1 | funct3 | rd | opcode.
|
||||||
@@ -229,6 +286,109 @@ func riscvRType(enc riscvEnc, rd, rs1, rs2 int) uint32 {
|
|||||||
(enc.funct3 << 12) | (uint32(rd) << 7) | enc.opcode
|
(enc.funct3 << 12) | (uint32(rd) << 7) | enc.opcode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// riscvAMOType encodes an atomic (AMO) instruction.
|
||||||
|
// Layout: funct5 | aq | rl | rs2 | rs1 | funct3 | rd | opcode.
|
||||||
|
// The funct5 is stored in the upper bits of enc.funct7 (shifted left by 2).
|
||||||
|
func riscvAMOType(enc riscvEnc, rd, rs1, rs2 int) uint32 {
|
||||||
|
funct5 := enc.funct7 >> 2 // extract funct5 from the stored value
|
||||||
|
return (funct5 << 27) | (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]
|
||||||
|
opcode uint32 // always 0x53 (OP-FP)
|
||||||
|
}
|
||||||
|
|
||||||
|
var riscvCvtTable = map[string]riscvCvtEnc{
|
||||||
|
// float → int (rs2 selects the integer width/sign).
|
||||||
|
"FCVTWS": {0x60, 0x0, 0x53}, // float32 → int32
|
||||||
|
"FCVTWUS": {0x60, 0x1, 0x53}, // float32 → uint32
|
||||||
|
"FCVTLS": {0x60, 0x2, 0x53}, // float32 → int64
|
||||||
|
"FCVTLUS": {0x60, 0x3, 0x53}, // float32 → uint64
|
||||||
|
"FCVTWD": {0x61, 0x0, 0x53}, // float64 → int32
|
||||||
|
"FCVTWUD": {0x61, 0x1, 0x53}, // float64 → uint32
|
||||||
|
"FCVTLD": {0x61, 0x2, 0x53}, // float64 → int64
|
||||||
|
"FCVTLUD": {0x61, 0x3, 0x53}, // float64 → uint64
|
||||||
|
// int → float (rs2 selects the integer width/sign).
|
||||||
|
"FCVTSW": {0x68, 0x0, 0x53}, // int32 → float32
|
||||||
|
"FCVTSWU": {0x68, 0x1, 0x53}, // uint32 → float32
|
||||||
|
"FCVTSL": {0x68, 0x2, 0x53}, // int64 → float32
|
||||||
|
"FCVTSLU": {0x68, 0x3, 0x53}, // uint64 → float32
|
||||||
|
"FCVTDW": {0x69, 0x0, 0x53}, // int32 → float64
|
||||||
|
"FCVTDWU": {0x69, 0x1, 0x53}, // uint32 → float64
|
||||||
|
"FCVTDL": {0x69, 0x2, 0x53}, // int64 → float64
|
||||||
|
"FCVTDLU": {0x69, 0x3, 0x53}, // uint64 → float64
|
||||||
|
// float → float width conversion.
|
||||||
|
"FCVTSD": {0x20, 0x1, 0x53}, // float64 → float32
|
||||||
|
"FCVTDS": {0x21, 0x0, 0x53}, // float32 → float64
|
||||||
|
// Bit moves between integer and FP registers (no conversion).
|
||||||
|
"FMVXD": {0x71, 0x0, 0x53}, // float64 → int64 (bit move)
|
||||||
|
"FMVDX": {0x79, 0x0, 0x53}, // int64 → float64 (bit move)
|
||||||
|
"FMVXW": {0x70, 0x0, 0x53}, // float32 → int32 (bit move)
|
||||||
|
"FMVWX": {0x78, 0x0, 0x53}, // int32 → float32 (bit move)
|
||||||
|
}
|
||||||
|
|
||||||
|
// riscvCvtType encodes an FP conversion instruction.
|
||||||
|
// Layout: funct7 | rs2(convtype) | rs1 | funct3(0) | rd | opcode.
|
||||||
|
func riscvCvtType(enc riscvCvtEnc, rd, rs1 int) uint32 {
|
||||||
|
return (enc.funct7 << 25) | (enc.rs2 << 20) | (uint32(rs1) << 15) |
|
||||||
|
(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},
|
||||||
|
"FMSUBS": {0x0, 0x47}, // rd = rs1*rs2 - rs3
|
||||||
|
"FMSUBD": {0x1, 0x47},
|
||||||
|
"FNMSUBS": {0x0, 0x4B}, // rd = -(rs1*rs2) + rs3
|
||||||
|
"FNMSUBD": {0x1, 0x4B},
|
||||||
|
"FNMADDS": {0x0, 0x4F}, // rd = -(rs1*rs2) - rs3
|
||||||
|
"FNMADDD": {0x1, 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=dynamic */ | (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.
|
// riscvIType encodes an I-type instruction: imm[11:0] | rs1 | funct3 | rd | opcode.
|
||||||
func riscvIType(enc riscvEnc, rd, rs1 int, imm int32) uint32 {
|
func riscvIType(enc riscvEnc, rd, rs1 int, imm int32) uint32 {
|
||||||
return (uint32(imm&0xFFF) << 20) | (uint32(rs1) << 15) |
|
return (uint32(imm&0xFFF) << 20) | (uint32(rs1) << 15) |
|
||||||
|
|||||||
Vendored
+20
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func atomicAdd(ptr *int64, val int64) int64
|
||||||
|
TEXT ·atomicAdd(SB), NOSPLIT, $0-24
|
||||||
|
LD a+0(FP), A0
|
||||||
|
LD b+8(FP), A1
|
||||||
|
AMOADDD A1, (A0), A2
|
||||||
|
SD A2, ret+16(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func fpAdd(a, b float64) float64
|
||||||
|
TEXT ·fpAdd(SB), NOSPLIT, $0-24
|
||||||
|
FLD a+0(FP), FA0
|
||||||
|
FLD b+8(FP), FA1
|
||||||
|
FADDD FA0, FA1, FA2
|
||||||
|
FSD FA2, ret+16(FP)
|
||||||
|
RET
|
||||||
Vendored
+27
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func readCSR(csr int64) int64
|
||||||
|
// Reads a CSR into the return value.
|
||||||
|
TEXT ·readCSR(SB), NOSPLIT, $0-16
|
||||||
|
LD a+0(FP), A0
|
||||||
|
CSRRS $0x300, X0, A1
|
||||||
|
SD A1, ret+8(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func setCSRBit(csr, bit int64) int64
|
||||||
|
TEXT ·setCSRBit(SB), NOSPLIT, $0-24
|
||||||
|
LD a+0(FP), A0
|
||||||
|
LD b+8(FP), A1
|
||||||
|
CSRRS $0x304, A1, A2
|
||||||
|
SD A2, ret+16(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func writeCSR(val int64) int64
|
||||||
|
TEXT ·writeCSR(SB), NOSPLIT, $0-16
|
||||||
|
LD a+0(FP), A0
|
||||||
|
CSRRW $0x305, A0, A1
|
||||||
|
SD A1, ret+8(FP)
|
||||||
|
RET
|
||||||
Vendored
+24
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func fma(a, b, c float64) float64
|
||||||
|
// Computes a*b + c using fused multiply-add.
|
||||||
|
TEXT ·fma(SB), NOSPLIT, $0-32
|
||||||
|
FLD a+0(FP), FA0
|
||||||
|
FLD b+8(FP), FA1
|
||||||
|
FLD c+16(FP), FA2
|
||||||
|
FMADDD FA0, FA1, FA2, FA3
|
||||||
|
FSD FA3, ret+24(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func fms(a, b, c float64) float64
|
||||||
|
// Computes a*b - c using fused multiply-subtract.
|
||||||
|
TEXT ·fms(SB), NOSPLIT, $0-32
|
||||||
|
FLD a+0(FP), FA0
|
||||||
|
FLD b+8(FP), FA1
|
||||||
|
FLD c+16(FP), FA2
|
||||||
|
FMSUBD FA0, FA1, FA2, FA3
|
||||||
|
FSD FA3, ret+24(FP)
|
||||||
|
RET
|
||||||
Vendored
+37
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func casLoop(ptr *int64, old, new int64) bool
|
||||||
|
// Compare-and-swap using LR/SC.
|
||||||
|
TEXT ·casLoop(SB), NOSPLIT, $0-32
|
||||||
|
cas_retry:
|
||||||
|
LD a+0(FP), A0
|
||||||
|
LRD (A0), A1
|
||||||
|
LD b+8(FP), A2
|
||||||
|
BNE A1, A2, cas_fail
|
||||||
|
LD c+16(FP), A3
|
||||||
|
SCD A3, (A0), A4
|
||||||
|
BNE A4, X0, cas_retry
|
||||||
|
ADDI X0, $1, A5
|
||||||
|
SD A5, ret+24(FP)
|
||||||
|
RET
|
||||||
|
cas_fail:
|
||||||
|
SD X0, ret+24(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func intToFloat(x int64) float64
|
||||||
|
TEXT ·intToFloat(SB), NOSPLIT, $0-16
|
||||||
|
LD a+0(FP), A0
|
||||||
|
FCVTDL A0, FA0
|
||||||
|
FSD FA0, ret+8(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func compare(a, b float64) bool
|
||||||
|
TEXT ·compare(SB), NOSPLIT, $0-24
|
||||||
|
FLD a+0(FP), FA0
|
||||||
|
FLD b+8(FP), FA1
|
||||||
|
FLTD FA0, FA1, A0
|
||||||
|
SD A0, ret+16(FP)
|
||||||
|
RET
|
||||||
Reference in New Issue
Block a user