diff --git a/verify/goobj_link_test.go b/verify/goobj_link_test.go index 82bce8e..83e7d5c 100644 --- a/verify/goobj_link_test.go +++ b/verify/goobj_link_test.go @@ -34,6 +34,20 @@ import ( // repack, so a build-layout change that silently skipped the substitution // fails the test instead of passing it vacuously. +// buildLinkParityGasm builds the gasm binary from the tree under test, one +// level above this package, so every link-parity test exercises the shipped +// CLI path rather than the library. +func buildLinkParityGasm(t *testing.T, goBin string) string { + t.Helper() + gasmBin := filepath.Join(t.TempDir(), "gasm") + build := exec.Command(goBin, "build", "-o", gasmBin, "./cmd/gasm") + build.Dir = ".." + if out, err := build.CombinedOutput(); err != nil { + t.Fatalf("build gasm: %v\n%s", err, out) + } + return gasmBin +} + // goobjLinkModule is the Go side of the regression module. The assembly // declares the five functions; main verifies their semantics and prints a // deterministic line the linked binaries must agree on. @@ -130,28 +144,29 @@ func TestGOOBJLinkRegression(t *testing.T) { // One gasm binary for every architecture, built from the tree under // test: the regression exercises the shipped CLI path, not the library. - // The build runs from the module root, one level above this package. - gasmBin := filepath.Join(t.TempDir(), "gasm") - gasmBuild := exec.Command(goBin, "build", "-o", gasmBin, "./cmd/gasm") - gasmBuild.Dir = ".." - if out, err := gasmBuild.CombinedOutput(); err != nil { - t.Fatalf("build gasm: %v\n%s", err, out) - } + gasmBin := buildLinkParityGasm(t, goBin) for _, goarch := range []string{"amd64", "arm64", "riscv64", "loong64"} { t.Run(goarch, func(t *testing.T) { - testGOOBJLinkArch(t, goBin, gasmBin, goarch) + goobjLinkKernel(t, goBin, gasmBin, goarch, "linkreg_"+goarch+".s", goobjLinkMainSrc) }) } } -func testGOOBJLinkArch(t *testing.T, goBin, gasmBin, goarch string) { +// goobjLinkKernel runs the whole regression for one architecture against +// one kernel: the kernel is assembled by the toolchain for the baseline, +// re-assembled by the gasm CLI, substituted into the real build's package +// archive and the program relinked. The Go side (mainSrc) carries the +// semantics and prints the deterministic line the two binaries must share. +// The standing linkreg kernels and the newer family kernels all go through +// this one path, so a change here moves every link-parity claim at once. +func goobjLinkKernel(t *testing.T, goBin, gasmBin, goarch, kernelFile, mainSrc string) { t.Helper() dir := t.TempDir() // The module: main.go, go.mod and the kernel as main_.s, the // name the build system selects the assembly by. - kernel, err := os.ReadFile(filepath.Join("testdata", "linkreg_"+goarch+".s")) + kernel, err := os.ReadFile(filepath.Join("testdata", kernelFile)) if err != nil { t.Fatalf("read kernel: %v", err) } @@ -159,7 +174,7 @@ func testGOOBJLinkArch(t *testing.T, goBin, gasmBin, goarch string) { if err := os.WriteFile(asmPath, kernel, 0o644); err != nil { t.Fatal(err) } - if err := os.WriteFile(filepath.Join(dir, "main.go"), []byte(goobjLinkMainSrc), 0o644); err != nil { + if err := os.WriteFile(filepath.Join(dir, "main.go"), []byte(mainSrc), 0o644); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module linkreg\n\ngo 1.27\n"), 0o644); err != nil {