feat(asm): resolve every CSR name the toolchain knows
The riscv64 assembler carried fourteen hand-picked CSR names where the toolchain resolves three hundred and twenty-nine: a CSRR/CSRW family instruction naming any privileged register beyond the few base ones came out as unknown CSR. The table now carries the RISC-V privileged specification's register set exactly as go tool asm spells it, and a differential test assembles every name through both assemblers and requires the words to agree byte for byte. Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
103864e8b2
commit
bffe408afa
2 files changed
+442
-17
No files matched your search
+332
-17
@@ -528,24 +528,339 @@ func riscvIsCondBranch(mnem string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// riscvCSRNames maps the standard CSR mnemonics the assembler accepts onto
|
||||
// their addresses.
|
||||
// riscvCSRNames maps every CSR mnemonic the assembler accepts onto its
|
||||
// address: the RISC-V privileged specification's register set as the Go
|
||||
// toolchain spells it, so a name `go tool asm` reads resolves here too.
|
||||
var riscvCSRNames = map[string]int32{
|
||||
"FFLAGS": 0x001,
|
||||
"FRM": 0x002,
|
||||
"FCSR": 0x003,
|
||||
"VSTART": 0x008,
|
||||
"VXSAT": 0x009,
|
||||
"VXRM": 0x00A,
|
||||
"VCSR": 0x00F,
|
||||
"CYCLE": 0xC00,
|
||||
"TIME": 0xC01,
|
||||
"INSTRET": 0xC02,
|
||||
"CYCLEH": 0xC80,
|
||||
"TIMEH": 0xC81,
|
||||
"INSTRETH": 0xC82,
|
||||
"VL": 0xC20,
|
||||
"VLENB": 0xC22,
|
||||
"FFLAGS": 0x001,
|
||||
"FRM": 0x002,
|
||||
"FCSR": 0x003,
|
||||
"UTVT": 0x007,
|
||||
"VSTART": 0x008,
|
||||
"VXSAT": 0x009,
|
||||
"VXRM": 0x00A,
|
||||
"VCSR": 0x00F,
|
||||
"SSP": 0x011,
|
||||
"SEED": 0x015,
|
||||
"JVT": 0x017,
|
||||
"UNXTI": 0x045,
|
||||
"UINTSTATUS": 0x046,
|
||||
"USCRATCHCSW": 0x048,
|
||||
"USCRATCHCSWL": 0x049,
|
||||
"SSTATUS": 0x100,
|
||||
"SIE": 0x104,
|
||||
"STVEC": 0x105,
|
||||
"SCOUNTEREN": 0x106,
|
||||
"STVT": 0x107,
|
||||
"SENVCFG": 0x10A,
|
||||
"SSTATEEN0": 0x10C,
|
||||
"SSTATEEN1": 0x10D,
|
||||
"SSTATEEN2": 0x10E,
|
||||
"SSTATEEN3": 0x10F,
|
||||
"SCOUNTINHIBIT": 0x120,
|
||||
"SSCRATCH": 0x140,
|
||||
"SEPC": 0x141,
|
||||
"SCAUSE": 0x142,
|
||||
"STVAL": 0x143,
|
||||
"SIP": 0x144,
|
||||
"SNXTI": 0x145,
|
||||
"SINTSTATUS": 0x146,
|
||||
"SSCRATCHCSW": 0x148,
|
||||
"SSCRATCHCSWL": 0x149,
|
||||
"STIMECMP": 0x14D,
|
||||
"SCTRCTL": 0x14E,
|
||||
"SCTRSTATUS": 0x14F,
|
||||
"SISELECT": 0x150,
|
||||
"SIREG": 0x151,
|
||||
"SIREG2": 0x152,
|
||||
"SIREG3": 0x153,
|
||||
"SIREG4": 0x155,
|
||||
"SIREG5": 0x156,
|
||||
"SIREG6": 0x157,
|
||||
"STOPEI": 0x15C,
|
||||
"SCTRDEPTH": 0x15F,
|
||||
"SATP": 0x180,
|
||||
"SRMCFG": 0x181,
|
||||
"SPMPEN": 0x183,
|
||||
"VSSTATUS": 0x200,
|
||||
"VSIE": 0x204,
|
||||
"VSTVEC": 0x205,
|
||||
"VSSCRATCH": 0x240,
|
||||
"VSEPC": 0x241,
|
||||
"VSCAUSE": 0x242,
|
||||
"VSTVAL": 0x243,
|
||||
"VSIP": 0x244,
|
||||
"VSTIMECMP": 0x24D,
|
||||
"VSCTRCTL": 0x24E,
|
||||
"VSISELECT": 0x250,
|
||||
"VSIREG": 0x251,
|
||||
"VSIREG2": 0x252,
|
||||
"VSIREG3": 0x253,
|
||||
"VSIREG4": 0x255,
|
||||
"VSIREG5": 0x256,
|
||||
"VSIREG6": 0x257,
|
||||
"VSTOPEI": 0x25C,
|
||||
"VSATP": 0x280,
|
||||
"MSTATUS": 0x300,
|
||||
"MISA": 0x301,
|
||||
"MEDELEG": 0x302,
|
||||
"MIDELEG": 0x303,
|
||||
"MIE": 0x304,
|
||||
"MTVEC": 0x305,
|
||||
"MCOUNTEREN": 0x306,
|
||||
"MTVT": 0x307,
|
||||
"MVIEN": 0x308,
|
||||
"MVIP": 0x309,
|
||||
"MENVCFG": 0x30A,
|
||||
"MSTATEEN0": 0x30C,
|
||||
"MSTATEEN1": 0x30D,
|
||||
"MSTATEEN2": 0x30E,
|
||||
"MSTATEEN3": 0x30F,
|
||||
"MPMPDELEG": 0x316,
|
||||
"MCOUNTINHIBIT": 0x320,
|
||||
"MCYCLECFG": 0x321,
|
||||
"MINSTRETCFG": 0x322,
|
||||
"MHPMEVENT3": 0x323,
|
||||
"MHPMEVENT4": 0x324,
|
||||
"MHPMEVENT5": 0x325,
|
||||
"MHPMEVENT6": 0x326,
|
||||
"MHPMEVENT7": 0x327,
|
||||
"MHPMEVENT8": 0x328,
|
||||
"MHPMEVENT9": 0x329,
|
||||
"MHPMEVENT10": 0x32A,
|
||||
"MHPMEVENT11": 0x32B,
|
||||
"MHPMEVENT12": 0x32C,
|
||||
"MHPMEVENT13": 0x32D,
|
||||
"MHPMEVENT14": 0x32E,
|
||||
"MHPMEVENT15": 0x32F,
|
||||
"MHPMEVENT16": 0x330,
|
||||
"MHPMEVENT17": 0x331,
|
||||
"MHPMEVENT18": 0x332,
|
||||
"MHPMEVENT19": 0x333,
|
||||
"MHPMEVENT20": 0x334,
|
||||
"MHPMEVENT21": 0x335,
|
||||
"MHPMEVENT22": 0x336,
|
||||
"MHPMEVENT23": 0x337,
|
||||
"MHPMEVENT24": 0x338,
|
||||
"MHPMEVENT25": 0x339,
|
||||
"MHPMEVENT26": 0x33A,
|
||||
"MHPMEVENT27": 0x33B,
|
||||
"MHPMEVENT28": 0x33C,
|
||||
"MHPMEVENT29": 0x33D,
|
||||
"MHPMEVENT30": 0x33E,
|
||||
"MHPMEVENT31": 0x33F,
|
||||
"MSCRATCH": 0x340,
|
||||
"MEPC": 0x341,
|
||||
"MCAUSE": 0x342,
|
||||
"MTVAL": 0x343,
|
||||
"MIP": 0x344,
|
||||
"MNXTI": 0x345,
|
||||
"MINTSTATUS": 0x346,
|
||||
"MSCRATCHCSW": 0x348,
|
||||
"MSCRATCHCSWL": 0x349,
|
||||
"MTINST": 0x34A,
|
||||
"MTVAL2": 0x34B,
|
||||
"MCTRCTL": 0x34E,
|
||||
"MISELECT": 0x350,
|
||||
"MIREG": 0x351,
|
||||
"MIREG2": 0x352,
|
||||
"MIREG3": 0x353,
|
||||
"MIREG4": 0x355,
|
||||
"MIREG5": 0x356,
|
||||
"MIREG6": 0x357,
|
||||
"MTOPEI": 0x35C,
|
||||
"PMPCFG0": 0x3A0,
|
||||
"PMPCFG1": 0x3A1,
|
||||
"PMPCFG2": 0x3A2,
|
||||
"PMPCFG3": 0x3A3,
|
||||
"PMPCFG4": 0x3A4,
|
||||
"PMPCFG5": 0x3A5,
|
||||
"PMPCFG6": 0x3A6,
|
||||
"PMPCFG7": 0x3A7,
|
||||
"PMPCFG8": 0x3A8,
|
||||
"PMPCFG9": 0x3A9,
|
||||
"PMPCFG10": 0x3AA,
|
||||
"PMPCFG11": 0x3AB,
|
||||
"PMPCFG12": 0x3AC,
|
||||
"PMPCFG13": 0x3AD,
|
||||
"PMPCFG14": 0x3AE,
|
||||
"PMPCFG15": 0x3AF,
|
||||
"PMPADDR0": 0x3B0,
|
||||
"PMPADDR1": 0x3B1,
|
||||
"PMPADDR2": 0x3B2,
|
||||
"PMPADDR3": 0x3B3,
|
||||
"PMPADDR4": 0x3B4,
|
||||
"PMPADDR5": 0x3B5,
|
||||
"PMPADDR6": 0x3B6,
|
||||
"PMPADDR7": 0x3B7,
|
||||
"PMPADDR8": 0x3B8,
|
||||
"PMPADDR9": 0x3B9,
|
||||
"PMPADDR10": 0x3BA,
|
||||
"PMPADDR11": 0x3BB,
|
||||
"PMPADDR12": 0x3BC,
|
||||
"PMPADDR13": 0x3BD,
|
||||
"PMPADDR14": 0x3BE,
|
||||
"PMPADDR15": 0x3BF,
|
||||
"PMPADDR16": 0x3C0,
|
||||
"PMPADDR17": 0x3C1,
|
||||
"PMPADDR18": 0x3C2,
|
||||
"PMPADDR19": 0x3C3,
|
||||
"PMPADDR20": 0x3C4,
|
||||
"PMPADDR21": 0x3C5,
|
||||
"PMPADDR22": 0x3C6,
|
||||
"PMPADDR23": 0x3C7,
|
||||
"PMPADDR24": 0x3C8,
|
||||
"PMPADDR25": 0x3C9,
|
||||
"PMPADDR26": 0x3CA,
|
||||
"PMPADDR27": 0x3CB,
|
||||
"PMPADDR28": 0x3CC,
|
||||
"PMPADDR29": 0x3CD,
|
||||
"PMPADDR30": 0x3CE,
|
||||
"PMPADDR31": 0x3CF,
|
||||
"PMPADDR32": 0x3D0,
|
||||
"PMPADDR33": 0x3D1,
|
||||
"PMPADDR34": 0x3D2,
|
||||
"PMPADDR35": 0x3D3,
|
||||
"PMPADDR36": 0x3D4,
|
||||
"PMPADDR37": 0x3D5,
|
||||
"PMPADDR38": 0x3D6,
|
||||
"PMPADDR39": 0x3D7,
|
||||
"PMPADDR40": 0x3D8,
|
||||
"PMPADDR41": 0x3D9,
|
||||
"PMPADDR42": 0x3DA,
|
||||
"PMPADDR43": 0x3DB,
|
||||
"PMPADDR44": 0x3DC,
|
||||
"PMPADDR45": 0x3DD,
|
||||
"PMPADDR46": 0x3DE,
|
||||
"PMPADDR47": 0x3DF,
|
||||
"PMPADDR48": 0x3E0,
|
||||
"PMPADDR49": 0x3E1,
|
||||
"PMPADDR50": 0x3E2,
|
||||
"PMPADDR51": 0x3E3,
|
||||
"PMPADDR52": 0x3E4,
|
||||
"PMPADDR53": 0x3E5,
|
||||
"PMPADDR54": 0x3E6,
|
||||
"PMPADDR55": 0x3E7,
|
||||
"PMPADDR56": 0x3E8,
|
||||
"PMPADDR57": 0x3E9,
|
||||
"PMPADDR58": 0x3EA,
|
||||
"PMPADDR59": 0x3EB,
|
||||
"PMPADDR60": 0x3EC,
|
||||
"PMPADDR61": 0x3ED,
|
||||
"PMPADDR62": 0x3EE,
|
||||
"PMPADDR63": 0x3EF,
|
||||
"SCONTEXT": 0x5A8,
|
||||
"HSTATUS": 0x600,
|
||||
"HEDELEG": 0x602,
|
||||
"HIDELEG": 0x603,
|
||||
"HIE": 0x604,
|
||||
"HTIMEDELTA": 0x605,
|
||||
"HCOUNTEREN": 0x606,
|
||||
"HGEIE": 0x607,
|
||||
"HVIEN": 0x608,
|
||||
"HVICTL": 0x609,
|
||||
"HENVCFG": 0x60A,
|
||||
"HSTATEEN0": 0x60C,
|
||||
"HSTATEEN1": 0x60D,
|
||||
"HSTATEEN2": 0x60E,
|
||||
"HSTATEEN3": 0x60F,
|
||||
"HTVAL": 0x643,
|
||||
"HIP": 0x644,
|
||||
"HVIP": 0x645,
|
||||
"HVIPRIO1": 0x646,
|
||||
"HVIPRIO2": 0x647,
|
||||
"HTINST": 0x64A,
|
||||
"HGATP": 0x680,
|
||||
"HCONTEXT": 0x6A8,
|
||||
"MSECCFG": 0x747,
|
||||
"TSELECT": 0x7A0,
|
||||
"TDATA1": 0x7A1,
|
||||
"TDATA2": 0x7A2,
|
||||
"TDATA3": 0x7A3,
|
||||
"TINFO": 0x7A4,
|
||||
"TCONTROL": 0x7A5,
|
||||
"MCONTEXT": 0x7A8,
|
||||
"MSCONTEXT": 0x7AA,
|
||||
"DCSR": 0x7B0,
|
||||
"DPC": 0x7B1,
|
||||
"DSCRATCH0": 0x7B2,
|
||||
"DSCRATCH1": 0x7B3,
|
||||
"MCYCLE": 0xB00,
|
||||
"MINSTRET": 0xB02,
|
||||
"MHPMCOUNTER3": 0xB03,
|
||||
"MHPMCOUNTER4": 0xB04,
|
||||
"MHPMCOUNTER5": 0xB05,
|
||||
"MHPMCOUNTER6": 0xB06,
|
||||
"MHPMCOUNTER7": 0xB07,
|
||||
"MHPMCOUNTER8": 0xB08,
|
||||
"MHPMCOUNTER9": 0xB09,
|
||||
"MHPMCOUNTER10": 0xB0A,
|
||||
"MHPMCOUNTER11": 0xB0B,
|
||||
"MHPMCOUNTER12": 0xB0C,
|
||||
"MHPMCOUNTER13": 0xB0D,
|
||||
"MHPMCOUNTER14": 0xB0E,
|
||||
"MHPMCOUNTER15": 0xB0F,
|
||||
"MHPMCOUNTER16": 0xB10,
|
||||
"MHPMCOUNTER17": 0xB11,
|
||||
"MHPMCOUNTER18": 0xB12,
|
||||
"MHPMCOUNTER19": 0xB13,
|
||||
"MHPMCOUNTER20": 0xB14,
|
||||
"MHPMCOUNTER21": 0xB15,
|
||||
"MHPMCOUNTER22": 0xB16,
|
||||
"MHPMCOUNTER23": 0xB17,
|
||||
"MHPMCOUNTER24": 0xB18,
|
||||
"MHPMCOUNTER25": 0xB19,
|
||||
"MHPMCOUNTER26": 0xB1A,
|
||||
"MHPMCOUNTER27": 0xB1B,
|
||||
"MHPMCOUNTER28": 0xB1C,
|
||||
"MHPMCOUNTER29": 0xB1D,
|
||||
"MHPMCOUNTER30": 0xB1E,
|
||||
"MHPMCOUNTER31": 0xB1F,
|
||||
"CYCLE": 0xC00,
|
||||
"TIME": 0xC01,
|
||||
"INSTRET": 0xC02,
|
||||
"HPMCOUNTER3": 0xC03,
|
||||
"HPMCOUNTER4": 0xC04,
|
||||
"HPMCOUNTER5": 0xC05,
|
||||
"HPMCOUNTER6": 0xC06,
|
||||
"HPMCOUNTER7": 0xC07,
|
||||
"HPMCOUNTER8": 0xC08,
|
||||
"HPMCOUNTER9": 0xC09,
|
||||
"HPMCOUNTER10": 0xC0A,
|
||||
"HPMCOUNTER11": 0xC0B,
|
||||
"HPMCOUNTER12": 0xC0C,
|
||||
"HPMCOUNTER13": 0xC0D,
|
||||
"HPMCOUNTER14": 0xC0E,
|
||||
"HPMCOUNTER15": 0xC0F,
|
||||
"HPMCOUNTER16": 0xC10,
|
||||
"HPMCOUNTER17": 0xC11,
|
||||
"HPMCOUNTER18": 0xC12,
|
||||
"HPMCOUNTER19": 0xC13,
|
||||
"HPMCOUNTER20": 0xC14,
|
||||
"HPMCOUNTER21": 0xC15,
|
||||
"HPMCOUNTER22": 0xC16,
|
||||
"HPMCOUNTER23": 0xC17,
|
||||
"HPMCOUNTER24": 0xC18,
|
||||
"HPMCOUNTER25": 0xC19,
|
||||
"HPMCOUNTER26": 0xC1A,
|
||||
"HPMCOUNTER27": 0xC1B,
|
||||
"HPMCOUNTER28": 0xC1C,
|
||||
"HPMCOUNTER29": 0xC1D,
|
||||
"HPMCOUNTER30": 0xC1E,
|
||||
"HPMCOUNTER31": 0xC1F,
|
||||
"VL": 0xC20,
|
||||
"VTYPE": 0xC21,
|
||||
"VLENB": 0xC22,
|
||||
"SCOUNTOVF": 0xDA0,
|
||||
"STOPI": 0xDB0,
|
||||
"HGEIP": 0xE12,
|
||||
"VSTOPI": 0xEB0,
|
||||
"MVENDORID": 0xF11,
|
||||
"MARCHID": 0xF12,
|
||||
"MIMPID": 0xF13,
|
||||
"MHARTID": 0xF14,
|
||||
"MCONFIGPTR": 0xF15,
|
||||
"MTOPI": 0xFB0,
|
||||
}
|
||||
|
||||
// riscvCSRAddress resolves a CSR operand: an integer immediate or one of the
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package asm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sourcedock.dev/petrbalvin/gasm-sdk/parser"
|
||||
)
|
||||
|
||||
// TestRISCVCSRDifferential proves the whole CSR name table against the
|
||||
// toolchain at once: one TEXT whose body reads every register the table
|
||||
// carries (CSRRW X0, NAME, X5), assembled by gasm and by go tool asm, must
|
||||
// agree byte for byte. A single wrong address names its register through
|
||||
// the first differing word: the CSR address occupies the instruction's top
|
||||
// twelve bits, so 329 names spread over distinct words identify themselves.
|
||||
func TestRISCVCSRDifferential(t *testing.T) {
|
||||
names := make([]string, 0, len(riscvCSRNames))
|
||||
for name := range riscvCSRNames {
|
||||
names = append(names, name)
|
||||
}
|
||||
slices.Sort(names)
|
||||
|
||||
var body strings.Builder
|
||||
for _, name := range names {
|
||||
body.WriteString(fmt.Sprintf("\tCSRRW X0, %s, X5\n", name))
|
||||
}
|
||||
src := "#include \"textflag.h\"\n\nTEXT ·csrs(SB), NOSPLIT, $0\n" + body.String() + "\tRET\n"
|
||||
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "csrs_riscv64.s")
|
||||
if err := os.WriteFile(path, []byte(src), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertRISCVDifferential(t, path, src, "csrs")
|
||||
}
|
||||
|
||||
// TestRISCVCSRPseudosDifferential pins the CSR pseudo spellings against the
|
||||
// oracle: the write-only forms (source first, CSR second), their immediate
|
||||
// variants and the CSRR read pseudo, one word each.
|
||||
func TestRISCVCSRPseudosDifferential(t *testing.T) {
|
||||
src := `#include "textflag.h"
|
||||
|
||||
TEXT ·csrps(SB), NOSPLIT, $0
|
||||
CSRS X5, TIME
|
||||
CSRC X5, CYCLE
|
||||
CSRW X5, INSTRET
|
||||
CSRSI $1, TIME
|
||||
CSRCI $2, CYCLE
|
||||
CSRWI $3, INSTRET
|
||||
CSRR VL, X10
|
||||
CSRRW X0, TIME, X11
|
||||
CSRRS X0, CYCLE, X12
|
||||
CSRRC X0, INSTRET, X13
|
||||
CSRRWI $4, TIME, X14
|
||||
CSRRSI $5, CYCLE, X15
|
||||
CSRRCI $6, INSTRET, X16
|
||||
RET
|
||||
`
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "csrps_riscv64.s")
|
||||
if err := os.WriteFile(path, []byte(src), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertRISCVDifferential(t, path, src, "csrps")
|
||||
}
|
||||
|
||||
// assertRISCVDifferential assembles the same source with gasm and with the
|
||||
// toolchain for riscv64 and requires the named function's code bytes to
|
||||
// agree. The live oracle is a deliberate-run comparison, so -short skips it
|
||||
// (the push pipeline's mode); the golden bytes of the individual encoders
|
||||
// are pinned separately in every mode.
|
||||
func assertRISCVDifferential(t *testing.T, path, src, fn string) {
|
||||
t.Helper()
|
||||
oracle := oracleFuncCode(t, toolAsmObject(t, path, "riscv64"))
|
||||
|
||||
// The oracle keys its functions by the qualified object name
|
||||
// (pkg.name); match on the local part.
|
||||
want := map[string][]byte{}
|
||||
for name, code := range oracle {
|
||||
if _, after, ok := strings.Cut(name, "."); ok {
|
||||
want[after] = code
|
||||
} else {
|
||||
want[name] = code
|
||||
}
|
||||
}
|
||||
if want[fn] == nil {
|
||||
t.Fatalf("the oracle object carries no function %q (has %v)", fn, keysOf(want))
|
||||
}
|
||||
|
||||
f, perrs := parser.Parse(path, src)
|
||||
if len(perrs) > 0 {
|
||||
t.Fatalf("parse: %v", perrs[0])
|
||||
}
|
||||
img, err := AssembleFileRISCV(f)
|
||||
if err != nil {
|
||||
t.Fatalf("AssembleFileRISCV: %v", err)
|
||||
}
|
||||
got := trimTrailingZeroWords(img.Code)
|
||||
wantB := trimTrailingZeroWords(want[fn])
|
||||
if !slices.Equal(got, wantB) {
|
||||
t.Errorf("%s: gasm and go tool asm disagree:\n gasm % x\n go % x", fn, got, wantB)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user