// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause package verify import ( "bytes" "os" "testing" "sourcedock.dev/petrbalvin/gasm-devkit/asm" "sourcedock.dev/petrbalvin/gasm-devkit/parser" ) func mustRead(t *testing.T, path string) string { t.Helper() b, err := os.ReadFile(path) if err != nil { t.Fatalf("read: %v", err) } return string(b) } func TestGroundTruthBasic(t *testing.T) { // Use the simple test kernel; it assembles with go tool asm. gt, err := GroundTruth("../testdata/verify/basic_amd64.s") if err != nil { t.Fatalf("GroundTruth: %v", err) } if len(gt) == 0 { t.Fatal("no functions extracted from ground truth") } // The "add" function should be present and non-empty. code, ok := gt["add"] if !ok { t.Fatalf("function 'add' not found in ground truth; got: %v", keys(gt)) } if len(code) == 0 { t.Fatal("add: zero-length code") } t.Logf("ground truth functions: %v", keys(gt)) } func TestGroundTruthComparison(t *testing.T) { // Assemble with gasm and compare against go tool asm. k, err := Load("../testdata/verify/basic_amd64.s") if err != nil { t.Fatalf("Load: %v", err) } defer k.Close() gt, err := GroundTruth("../testdata/verify/basic_amd64.s") if err != nil { t.Fatalf("GroundTruth: %v", err) } for _, name := range k.FuncNames() { fl, _ := k.Func(name) gasmCode := k.Image().Code[fl.Offset : fl.Offset+fl.Size] goCode, ok := gt[name] if !ok { t.Errorf("%s: not in ground truth", name) continue } if len(gasmCode) != len(goCode) { t.Errorf("%s: size mismatch: gasm=%d go=%d", name, len(gasmCode), len(goCode)) continue } for i := range gasmCode { if gasmCode[i] != goCode[i] { t.Errorf("%s: byte %d differs: gasm=%02x go=%02x", name, i, gasmCode[i], goCode[i]) break } } } } func TestGroundTruthBadFile(t *testing.T) { _, err := GroundTruth("/nonexistent/file_amd64.s") if err == nil { t.Fatal("expected error for nonexistent file") } } // TestGroundTruthCrossEnvGOARCH checks that a GOARCH exported in the // ambient environment does not override cross-assembly: the child // assembler must honour the requested GOARCH even when the environment // already carries a GOARCH entry (os.Getenv reads the first entry, so a // duplicate would win). func TestGroundTruthCrossEnvGOARCH(t *testing.T) { t.Setenv("GOARCH", "amd64") gt, err := GroundTruthARM64("../testdata/verify/basic_arm64.s") if err != nil { t.Fatalf("GroundTruthARM64 with ambient GOARCH=amd64: %v", err) } if len(gt) == 0 { t.Fatal("no functions extracted from ground truth") } } func keys(m map[string][]byte) []string { out := make([]string, 0, len(m)) for k := range m { out = append(out, k) } return out } // TestGroundTruthAMD64 runs the amd64 kernels through the same live // comparison the other arches use: gasm output versus go tool asm output, // with relocation fields masked on both sides. func TestGroundTruthAMD64(t *testing.T) { for _, path := range []string{ "../testdata/verify/basic_amd64.s", "../testdata/verify/bigframe_amd64.s", "../testdata/verify/guard_amd64.s", "../testdata/verify/indirect_amd64.s", "../testdata/verify/widen_amd64.s", "../testdata/verify/scalar_amd64.s", "../testdata/verify/atomics_amd64.s", "../testdata/verify/system_amd64.s", "../testdata/verify/crypto_amd64.s", "../testdata/verify/sse_amd64.s", "../testdata/verify/avx_amd64.s", "../testdata/verify/pfx_amd64.s", "../testdata/verify/vsib_amd64.s", "../testdata/verify/floatimm_amd64.s", "../testdata/verify/bookkeep_amd64.s", "../testdata/verify/quadreg_amd64.s", "../testdata/verify/rawdata_amd64.s", "../testdata/verify/avx512_amd64.s", "../testdata/verify/pfx_amd64.s", "../testdata/verify/vsib_amd64.s", "../testdata/verify/floatimm_amd64.s", "../testdata/verify/bookkeep_amd64.s", "../testdata/verify/quadreg_amd64.s", "../testdata/verify/rawdata_amd64.s", "../testdata/verify/avx512_amd64.s", "../testdata/verify/doubleshift_amd64.s", "../testdata/verify/ssestatic_amd64.s", } { t.Run(path, func(t *testing.T) { f, errs := parser.Parse(path, mustRead(t, path)) if len(errs) > 0 { t.Fatalf("parse: %v", errs) } img, err := asm.AssembleFile(f) if err != nil { t.Fatalf("AssembleFile: %v", err) } gt, err := GroundTruth(path) if err != nil { t.Fatalf("GroundTruth: %v", err) } matched := 0 for _, fn := range img.Funcs { gasmCode := maskRelocs(append([]byte(nil), img.Code[fn.Offset:fn.Offset+fn.Size]...), fn.Relocs) goCode, ok := gt[fn.Name] if !ok { t.Errorf("%s: not in ground truth (%d functions)", fn.Name, len(gt)) continue } goCode = maskRelocs(goCode, fn.Relocs) if !bytes.Equal(gasmCode, goCode) { t.Errorf("%s: MISMATCH gasm=%d go=%d bytes\n%s", fn.Name, len(gasmCode), len(goCode), diffHex(gasmCode, goCode)) continue } matched++ } if matched == 0 { t.Fatal("no functions matched") } }) } }