test(asm): seed the riscv64 and loong64 assemble fuzz targets

Assisted-by: GLM 5.3
This commit is contained in:
petrbalvin committed 2026-10-07 13:51:02 +02:00
1 parent b9dfb48d79
commit 965b33e5b0
11 files changed
+1288

No files matched your search

+99
View File
@@ -37,6 +37,22 @@ func corpusSeeds(f *testing.F) {
}
}
// archCorpusSeeds seeds a target with every assembly file of its architecture
// seed directory, testdata/seeds/<dir>. The files hold the target-specific
// corpus: the instruction families GOROOT's own assembler corpus exercises for
// the architecture, minimalised, plus the boundary shapes the encoder's range
// gates live on. They are committed assembly, so `gasm fmt` and `gasm lint`
// gate them the way they gate every other .s file. A plain `go test` run
// replays each one as a regression case.
func archCorpusSeeds(f *testing.F, dir string) {
files, _ := filepath.Glob(filepath.Join("testdata", "seeds", dir, "*.s"))
for _, path := range files {
if b, err := os.ReadFile(path); err == nil {
f.Add(string(b))
}
}
}
// fuzzAssemble is the whole fuzz body, shared by every target and one line
// apart between them: parse with macro and include expansion, assemble
// through the target's file-level entry, and hold the invariants. The
@@ -179,3 +195,86 @@ func FuzzAssembleARM64(f *testing.F) {
fuzzAssemble(t, "fuzz_arm64.s", src, AssembleFileARM64)
})
}
// FuzzAssembleRISCV64 hammers the same pipeline for the fixed riscv64 target.
// The seed corpus lives in testdata/seeds/riscv64: the instruction families
// GOROOT's riscv64 assembler corpus exercises (the immediate-range ladder of
// the I-type arithmetic, the load/store and branch offsets, the atomics, the
// FP conversions and the fused multiply-adds), the RVV configuration and
// arithmetic classes with their mask forms, the RVC-compressible shapes, the
// CSR instructions and the Zbb/Zba/Zbs bit-manipulation set, plus one file per
// rejection shape so each diagnostic path replays on its own.
func FuzzAssembleRISCV64(f *testing.F) {
corpusSeeds(f)
archCorpusSeeds(f, "riscv64")
// Minimal seeds for the shared file-level surface, spelled the riscv64
// way: the guard classes, the macro and include expansion, and the data
// section with its symbol-valued fields.
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tRET\n")
f.Add("TEXT ·f(SB), $16-8\n\tMOV x+0(FP), X10\n\tMOV X10, ret+8(FP)\n\tRET\n")
f.Add("TEXT ·f(SB), $256-0\n\tCALL ·helper(SB)\n\tRET\nTEXT ·helper(SB), NOSPLIT, $0\n\tRET\n")
f.Add("#define L(n) ADDI $n, X10, X10\nTEXT ·f(SB), NOSPLIT, $0\n\tL(7)\n\tRET\n")
f.Add("#include \"textflag.h\"\nTEXT ·f(SB), NOSPLIT, $0\n\tRET\n")
f.Add("#include \"fuzzdefs.h\"\nTEXT ·f(SB), $16-8\n\tMOV KONST, X10\n\tMOV ARG(x), X11\n\tRET\n")
f.Add("DATA d<>+0(SB)/8, $0xf4f8fcff\nDATA d<>+4(SB)/4, $1\nGLOBL d<>(SB), RODATA, $8\n" +
"TEXT ·f(SB), NOSPLIT, $0\n\tMOV d<>(SB), X10\n\tRET\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tPCALIGN $16\n\tADD X11, X10, X10\n\tPCALIGN $2048\n\tRET\n")
f.Add("TEXT ·f(SB), $0\n\tPCDATA $0, $1\n\tFUNCDATA $0, ·meta(SB)\n\tRET\n")
// Shapes that must be rejected: each pins a diagnostic path the accepted
// seeds never reach.
f.Add("TEXT ·f(SB), $0\n\tBOGUSINSTR X10, X11\n\tRET\n")
f.Add("GLOBL d(SB), $-8\n")
f.Add("GLOBL d(SB), $0x4000001\n")
f.Add("DATA d+0(SB)/9, $1\nGLOBL d(SB), $8\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tADD X11, X32\n\tRET\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tADDI $2048, X5, X6\n\tADD X11, X40, X6\n\tRET\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tCSRRW $0x1000, X5, X6\n\tRET\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tSLLI $64, X5, X6\n\tRET\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tVLE8V (X10), V32\n\tRET\n")
f.Add("#define A A\nA\n")
f.Fuzz(func(t *testing.T, src string) {
fuzzAssemble(t, "fuzz_riscv64.s", src, AssembleFileRISCV)
})
}
// FuzzAssembleLOONG64 hammers the same pipeline for the fixed loong64 target.
// The seed corpus lives in testdata/seeds/loong64: the LSX and LASX register
// banks with their immediate forms and range gates (the si5 compares, the
// biased shifts, the VSHUF4I/VPERMI/VEXTRINS immediates), the ll/sc offset
// ladder with its three encoding spans, the pointer loads and stores, the
// atomics with their dbar forms, the branches, the bit-field instructions and
// the register-class moves, plus one file per rejection shape.
func FuzzAssembleLOONG64(f *testing.F) {
corpusSeeds(f)
archCorpusSeeds(f, "loong64")
// Minimal seeds for the shared file-level surface, spelled the loong64
// way.
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tRET\n")
f.Add("TEXT ·f(SB), $16-8\n\tMOVV x+0(FP), R4\n\tMOVV R4, ret+8(FP)\n\tRET\n")
f.Add("TEXT ·f(SB), $256-0\n\tCALL ·helper(SB)\n\tRET\nTEXT ·helper(SB), NOSPLIT, $0\n\tRET\n")
f.Add("#define L(n) ADDV $n, R4, R4\nTEXT ·f(SB), NOSPLIT, $0\n\tL(7)\n\tRET\n")
f.Add("#include \"textflag.h\"\nTEXT ·f(SB), NOSPLIT, $0\n\tRET\n")
f.Add("#include \"fuzzdefs.h\"\nTEXT ·f(SB), $16-8\n\tMOVV KONST, R4\n\tMOVV ARG(x), R5\n\tRET\n")
f.Add("DATA d<>+0(SB)/8, $0xf4f8fcff\nDATA d<>+4(SB)/4, $1\nGLOBL d<>(SB), RODATA, $8\n" +
"TEXT ·f(SB), NOSPLIT, $0\n\tMOVV d<>(SB), R4\n\tRET\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tPCALIGN $16\n\tADDV R5, R4, R4\n\tPCALIGN $2048\n\tRET\n")
f.Add("TEXT ·f(SB), $0\n\tPCDATA $0, $1\n\tFUNCDATA $0, ·meta(SB)\n\tRET\n")
// Shapes that must be rejected: each pins a diagnostic path the accepted
// seeds never reach.
f.Add("TEXT ·f(SB), $0\n\tBOGUSINSTR R4, R5\n\tRET\n")
f.Add("GLOBL d(SB), $-8\n")
f.Add("GLOBL d(SB), $0x4000001\n")
f.Add("DATA d+0(SB)/9, $1\nGLOBL d(SB), $8\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tADD R5, R32\n\tRET\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tBEQZ V0, L1\nL1:\n\tRET\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tVADDV V1, V2, X3\n\tRET\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tVSEQV $32, V2, V3\n\tRET\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tVSHUF4IV $16, V2, V1\n\tRET\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tBSTRPICKV $64, R4, $5, R6\n\tRET\n")
f.Add("#define A A\nA\n")
f.Fuzz(func(t *testing.T, src string) {
fuzzAssemble(t, "fuzz_loong64.s", src, AssembleFileLOONG64)
})
}