feat(asm): add AArch64 arm64 encoder with ground-truth verification

Assisted-by: MiMo V2.5 Pro
This commit is contained in:
2026-08-20 13:33:39 +02:00
parent 01dcc3b86e
commit 4221ec5741
14 changed files with 2640 additions and 22 deletions
+96
View File
@@ -498,6 +498,8 @@ requires -p, the package path, and the installed Go toolchain).
obj, err = img.ELFRISCVObject()
case arch.LOONG64:
obj, err = img.ELFLOONG64Object()
case arch.ARM64:
obj, err = img.ELFAARCH64Object()
default:
obj, err = img.ELFObject()
}
@@ -508,6 +510,8 @@ requires -p, the package path, and the installed Go toolchain).
obj, err = img.GOObjectRISCV(*pkg, path)
case arch.LOONG64:
obj, err = img.GOObjectLOONG64(*pkg, path)
case arch.ARM64:
obj, err = img.GOObjectAARCH64(*pkg, path)
default:
obj, err = img.GOObject(*pkg, path)
}
@@ -921,6 +925,95 @@ func cmdVerifyLOONG64(path string, groundTruth, profile bool) int {
return 0
}
func cmdVerifyARM64(path string, groundTruth, profile bool) int {
src, err := readSource(path)
if err != nil {
fmt.Fprintf(os.Stderr, "gasm verify: %v\n", err)
return 1
}
f, errs := parser.Parse(path, src)
for _, e := range errs {
fmt.Fprintf(os.Stderr, "%s: %v\n", path, e)
}
if len(errs) > 0 {
return 1
}
img, err := asm.AssembleFileARM64(f)
if err != nil {
fmt.Fprintf(os.Stderr, "gasm verify: %v\n", err)
return 1
}
if groundTruth {
gt, err := verify.GroundTruthARM64(path)
if err != nil {
fmt.Fprintf(os.Stderr, "gasm verify: ground truth: %v\n", err)
return 1
}
matched, total := 0, 0
for _, fn := range img.Funcs {
gasmCode := img.Code[fn.Offset : fn.Offset+fn.Size]
goCode, ok := gt[fn.Name]
if !ok {
fmt.Printf(" %s: SKIP (not in go tool asm output)\n", fn.Name)
continue
}
total++
gasmCmp := make([]byte, len(gasmCode))
goCmp := make([]byte, len(goCode))
copy(gasmCmp, gasmCode)
copy(goCmp, goCode)
for _, r := range fn.Relocs {
for j := r.Off; j < r.Off+4 && j < len(gasmCmp); j++ {
gasmCmp[j] = 0
}
for j := r.Off; j < r.Off+4 && j < len(goCmp); j++ {
goCmp[j] = 0
}
}
if bytes.Equal(gasmCmp, goCmp) {
matched++
if len(fn.Relocs) > 0 {
fmt.Printf(" %s: MATCH (%d bytes, %d relocs masked)\n", fn.Name, fn.Size, len(fn.Relocs))
} else {
fmt.Printf(" %s: MATCH (%d bytes)\n", fn.Name, fn.Size)
}
} else {
fmt.Printf(" %s: MISMATCH (%d vs %d bytes)\n", fn.Name, fn.Size, len(goCode))
for i := 0; i < len(gasmCode) || i < len(goCode); i += 16 {
var gb, gs string
for j := i; j < i+16 && j < len(gasmCode); j++ {
gb += fmt.Sprintf(" %02x", gasmCode[j])
}
for j := i; j < i+16 && j < len(goCode); j++ {
gs += fmt.Sprintf(" %02x", goCode[j])
}
fmt.Printf(" %04x: gasm:%s\n", i, gb)
fmt.Printf(" %04x: gt: %s\n", i, gs)
}
}
}
fmt.Printf("%s: %d/%d matched\n", path, matched, total)
if matched < total {
return 1
}
return 0
}
if profile {
for _, fn := range img.Funcs {
fmt.Printf("%s: %d bytes, labels: %v\n", fn.Name, fn.Size, fn.Labels)
}
return 0
}
fmt.Printf("%s: %d functions assembled\n", path, len(img.Funcs))
for _, fn := range img.Funcs {
fmt.Printf(" %s: %d bytes\n", fn.Name, fn.Size)
}
return 0
}
func cmdVerify(args []string) int {
fs := newCommand("verify", "gasm verify [-smoke] [-abi] [-fuzz] [-ground-truth] [-profile] [-call] <file.s>", `
Assemble FILE (amd64), map it into executable memory and report the available
@@ -975,6 +1068,9 @@ decoders) that crash on random input but should succeed on valid data.
case arch.LOONG64:
// LoongArch: ground-truth only (no JIT on non-LoongArch hosts).
return cmdVerifyLOONG64(path, *groundTruth, *profile)
case arch.ARM64:
// AArch64: ground-truth only (no JIT on non-ARM64 hosts).
return cmdVerifyARM64(path, *groundTruth, *profile)
default:
fmt.Fprintln(os.Stderr, "gasm verify: only amd64, riscv64 and loong64 are supported")
return 1