feat(asm): encode the riscv64 privileged instructions

Assisted-by: GLM 5.3 Flash
This commit is contained in:
petrbalvin committed 2026-10-07 00:47:27 +02:00
1 parent 778c297214
commit 82e8919208
2 files changed
+83 -1

No files matched your search

+37 -1
View File
@@ -1273,6 +1273,34 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
}
word = riscvIType(enc, 0, 0, imm)
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
case "SRET", "MRET", "WFI", "DRET":
// The privileged traps and the wait instruction: fixed funct7 and
// rs2 fields packed into the I-type immediate. The toolchain's
// object table carries the first three (its assembler accepts no
// mnemonic for them); DRET is the debug specification's own, so the
// golden vector pins it: SRET 0x10200073, MRET 0x30200073,
// WFI 0x10500073, DRET 0x7b200073.
imm := map[string]int32{"SRET": 0x102, "MRET": 0x302, "WFI": 0x105, "DRET": 0x7B2}[mnem]
word = riscvIType(riscvEnc{0x73, 0x0, 0x00}, 0, 0, imm)
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
case "SFENCEVMA":
// INSTR rs1, rs2: the memory-management fence, funct7 0x09 and an
// all-zero rd. The toolchain's object table carries the encoding
// (ASFENCEVMA, funct7 9) but its assembler accepts no mnemonic for
// it, so the privileged specification's form pins it:
// SFENCEVMA X10, X11 is 0x12b50073.
if len(ops) != 2 {
return nil, fmt.Errorf("SFENCEVMA expects 2 operands, got %d", len(ops))
}
rs1 := regFromOperand(ops[0])
rs2 := regFromOperand(ops[1])
if rs1 < 0 || rs2 < 0 {
return nil, fmt.Errorf("SFENCEVMA: invalid register operand")
}
word = riscvRType(riscvEnc{0x73, 0x0, 0x09}, 0, rs1, rs2)
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
}
// FP conversion / move instructions use a separate table (rs2 encodes
@@ -1990,11 +2018,19 @@ func splitRISCV32Imm(imm int32) (low, high int32) {
// riscvNormalisePseudo rewrites the toolchain's UNDEF spelling onto EBREAK:
// the assembler accepts UNDEF where the hardware wants the trap instruction
// and emits ebreak (compressed to C.EBREAK under RVC), so every pass sees the
// canonical name.
// canonical name. The privileged aliases fold the same way: SCALL and
// SBREAK are the supervisor spellings of ECALL and EBREAK and encode
// identically.
func riscvNormalisePseudo(mnem string) string {
if strings.EqualFold(mnem, "UNDEF") {
return "EBREAK"
}
switch mnem {
case "SCALL":
return "ECALL"
case "SBREAK":
return "EBREAK"
}
return mnem
}