118 lines
3.2 KiB
Go
118 lines
3.2 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package verify
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"sourcedock.dev/petrbalvin/gasm-devkit/asm"
|
|
"sourcedock.dev/petrbalvin/gasm-devkit/parser"
|
|
)
|
|
|
|
// TestGroundTruthLOONG64 assembles the loong64 test kernels with gasm and
|
|
// compares them byte-for-byte against `go tool asm` (GOARCH=loong64). The
|
|
// relocation fields of static-symbol references are masked before the
|
|
// comparison, since the toolchain leaves them zero for the linker.
|
|
func TestGroundTruthLOONG64(t *testing.T) {
|
|
for _, path := range []string{
|
|
"../testdata/verify/basic_loong64.s",
|
|
"../testdata/verify/fp_loong64.s",
|
|
"../testdata/verify/bigframe_loong64.s",
|
|
"../testdata/verify/guard_loong64.s",
|
|
"../testdata/verify/indirect_loong64.s",
|
|
"../testdata/verify/movwfp_loong64.s",
|
|
"../testdata/verify/branchu_loong64.s",
|
|
"../testdata/verify/atomics_loong64.s",
|
|
"../testdata/verify/vector_loong64.s",
|
|
"../testdata/verify/pcalign_loong64.s",
|
|
"../testdata/verify/l64forms_loong64.s",
|
|
"trampoline_loong64.s",
|
|
} {
|
|
t.Run(path, func(t *testing.T) {
|
|
src, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("read: %v", err)
|
|
}
|
|
f, errs := parser.Parse(path, string(src))
|
|
if len(errs) > 0 {
|
|
t.Fatalf("parse: %v", errs)
|
|
}
|
|
img, err := asm.AssembleFileLOONG64(f)
|
|
if err != nil {
|
|
t.Fatalf("AssembleFileLOONG64: %v", err)
|
|
}
|
|
gt, err := GroundTruthLOONG64(path)
|
|
if err != nil {
|
|
t.Fatalf("GroundTruthLOONG64: %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++
|
|
t.Logf("%s: MATCH (%d bytes)", fn.Name, fn.Size)
|
|
}
|
|
if matched == 0 {
|
|
t.Fatal("no functions matched")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// maskRelocs zeroes the 4-byte immediate fields of the relocation sites.
|
|
func maskRelocs(code []byte, relocs []asm.Reloc) []byte {
|
|
for _, r := range relocs {
|
|
for j := r.Off; j < r.Off+4 && j < len(code); j++ {
|
|
code[j] = 0
|
|
}
|
|
}
|
|
return code
|
|
}
|
|
|
|
func diffHex(a, b []byte) string {
|
|
var out bytes.Buffer
|
|
n := max(len(b), len(a))
|
|
for i := 0; i < n; i += 4 {
|
|
ab, bb := "??", "??"
|
|
if i+4 <= len(a) {
|
|
ab = fmt.Sprintf("%02x%02x%02x%02x", a[i], a[i+1], a[i+2], a[i+3])
|
|
} else if i < len(a) {
|
|
var sb strings.Builder
|
|
for j := i; j < len(a); j++ {
|
|
fmt.Fprintf(&sb, "%02x", a[j])
|
|
}
|
|
ab = sb.String()
|
|
}
|
|
if i+4 <= len(b) {
|
|
bb = fmt.Sprintf("%02x%02x%02x%02x", b[i], b[i+1], b[i+2], b[i+3])
|
|
} else if i < len(b) {
|
|
var sb strings.Builder
|
|
for j := i; j < len(b); j++ {
|
|
fmt.Fprintf(&sb, "%02x", b[j])
|
|
}
|
|
bb = sb.String()
|
|
}
|
|
mark := " "
|
|
if ab != bb {
|
|
mark = "!"
|
|
}
|
|
fmt.Fprintf(&out, "%04x: %s %s %s\n", i, ab, bb, mark)
|
|
}
|
|
return out.String()
|
|
}
|