From 4eb9100def72c56ba16fcdb6a7c1a5b0a541c1ab Mon Sep 17 00:00:00 2001 From: petrbalvin Date: Wed, 7 Oct 2026 21:38:44 +0200 Subject: [PATCH] test(asm): walk the loong64 audit backlog under every operand shape Assisted-by: GLM 5.3 Flash --- asm/loong64_errorparity_test.go | 41 ++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/asm/loong64_errorparity_test.go b/asm/loong64_errorparity_test.go index 7ded6be..ed0530b 100644 --- a/asm/loong64_errorparity_test.go +++ b/asm/loong64_errorparity_test.go @@ -276,7 +276,8 @@ func TestLoong64PairErrorParity(t *testing.T) { // encode must be refused by gasm as well, and the one name the audit // misses because its probe battery lacks the shape (SCQ, whose only form // is the three-operand store-conditional) must encode to the toolchain's -// own bytes. +// own bytes. The refusal claim is walked under the full operand-shape +// battery below, against the live toolchain and against gasm alike. func TestLoong64BacklogNameParity(t *testing.T) { if testing.Short() { t.Skip("live toolchain backlog: skipped in -short mode") @@ -286,6 +287,32 @@ func TestLoong64BacklogNameParity(t *testing.T) { t.Errorf("gasm encodes %s, which the toolchain refuses under every shape", name) } } + // The shapes the plain audit probes, walked here so the backlog's + // claim holds under any operand shape, not only the bare mnemonic: + // the toolchain must refuse every combination, and gasm must refuse + // it too. + goroot := loong64Goroot(t) + for _, name := range loong64RejectedNames { + for _, shape := range loong64BacklogShapes { + body := strings.TrimSpace(name + " " + shape) + if loong64ProbeAssembles(body) { + t.Errorf("gasm encodes %s, which the toolchain refuses under every shape", body) + } + // The toolchain side of the same row: the file holds the one + // instruction, and the live assembler must report it. A + // spelling the toolchain accepts would open the name for + // encoding and close nothing here but the parity. + src := "#include \"textflag.h\"\n\nTEXT ·f(SB), NOSPLIT, $0-0\np2:\n\t" + body + "\n\tRET\n" + dir := t.TempDir() + path := filepath.Join(dir, "backlogshape.s") + if err := os.WriteFile(path, []byte(src), 0o644); err != nil { + t.Fatalf("write: %v", err) + } + if lines := loong64ToolchainRejects(t, goroot, path); len(lines) == 0 { + t.Errorf("the toolchain assembles %q, against the backlog's claim", body) + } + } + } // sc.q: loong64enc1.s pins the encoding as c4145738 // (SCQ R4, R5, (R6): sc.q rd, rk, (rj)). src := "#include \"textflag.h\"\n\nTEXT ·f(SB), NOSPLIT, $0-0\n\tSCQ R4, R5, (R6)\n\tRET\n" @@ -303,6 +330,18 @@ func TestLoong64BacklogNameParity(t *testing.T) { } } +// loong64BacklogShapes is the operand-shape battery the backlog walk runs +// every rejected name through: the register, immediate, memory, vector and +// label positions the loong64 grammar has, mirroring the plain audit's +// probe battery (cmd/gasm's probeShapes) so the two answer the same +// question. +var loong64BacklogShapes = []string{ + "", "R4", "R4, R5", "R4, R5, R6", "$1, R4", "R4, (R5)", "(R4), R5", + "F0, F1", "F0, F1, F2", "V1, V2", "X1, X2", "V1, FCC0", "$65536, R4", + "R4, R5, p2", "p2(SB)", "R5, (R4), R6", "$1, $0", "R1, R5, 0", + "0(R7), $5, $0", "V1, V2, V3, V4", "R4, R5, (R6)", "$16", "(R4)", +} + // loong64ProbeAssembles answers whether the single corpus line, wrapped in // its own function, parses and assembles on loong64. func loong64ProbeAssembles(body string) bool {