feat(verify): extend differential tests to go-flac and AVX-512, add --abi/--profile CLI flags

Assisted-by: Qwen 3.8 Max Preview
This commit is contained in:
2026-08-02 23:11:30 +02:00
parent d9f6167a4d
commit 9262990ce5
4 changed files with 387 additions and 4 deletions
+34 -3
View File
@@ -29,7 +29,7 @@ import (
// version is the release version, stamped at build time via
// -ldflags "-X main.version=…" (defaulting to the current release).
var version = "0.20.0"
var version = "0.21.0"
func main() {
if len(os.Args) < 2 {
@@ -472,7 +472,7 @@ requires -p, the package path, and the installed Go toolchain).
}
func cmdVerify(args []string) int {
fs := newCommand("verify", "gasm verify <file.s>", `
fs := newCommand("verify", "gasm verify [-smoke] [-abi] [-profile] <file.s>", `
Assemble FILE (amd64), map it into executable memory and report the available
functions. This confirms the assembled image is self-consistent (no
unresolved external symbols) and executable — the prerequisite for dynamic
@@ -481,11 +481,18 @@ testing.
With -smoke, each NOSPLIT function is called with a zeroed argument block to
confirm the JIT trampoline works end-to-end. This is safe only for functions
that tolerate nil pointers and zero lengths in their arguments.
With -abi, each function is called with sentinel values in the callee-saved
registers (BP, R14) and a red-zone canary below SP; violations are reported.
With -profile, the static basic-block structure is listed for each function.
`)
smoke := fs.Bool("smoke", false, "call each NOSPLIT function with zeroed args")
abi := fs.Bool("abi", false, "run ABI-checking calls (sentinel registers + red zone)")
profile := fs.Bool("profile", false, "list basic-block structure per function")
fs.Parse(args)
if fs.NArg() != 1 {
fmt.Fprintln(os.Stderr, "usage: gasm verify [-smoke] <file.s>")
fmt.Fprintln(os.Stderr, "usage: gasm verify [-smoke] [-abi] [-profile] <file.s>")
return 2
}
path := fs.Arg(0)
@@ -511,6 +518,16 @@ that tolerate nil pointers and zero lengths in their arguments.
flags = " NOSPLIT"
}
fmt.Printf(" %s: %d bytes, args=%d, frame=%d%s\n", name, fl.Size, fl.Args, fl.Frame, flags)
if *profile {
blocks, err := k.Blocks(name)
if err != nil {
fmt.Printf(" profile: %v\n", err)
} else {
fmt.Printf(" blocks: %d\n", len(blocks))
}
}
if *smoke && fl.NoSplit {
args := make([]byte, fl.Args)
_, err := k.CallFunc(name, args)
@@ -521,6 +538,20 @@ that tolerate nil pointers and zero lengths in their arguments.
fmt.Printf(" smoke: OK\n")
}
}
if *abi && fl.NoSplit {
args := make([]byte, fl.Args)
_, report, err := k.CallFuncChecked(name, args)
if err != nil {
fmt.Printf(" abi: FAIL — %v\n", err)
rc = 1
} else if !report.OK() {
fmt.Printf(" abi: %s\n", report)
rc = 1
} else {
fmt.Printf(" abi: clean\n")
}
}
}
return rc
}