feat(cli): add --map flag to diff for comparing differently-named

functions
This commit is contained in:
2026-08-05 20:20:47 +02:00
parent ad6e3360df
commit ece0d3f127
2 changed files with 41 additions and 6 deletions
+6
View File
@@ -9,6 +9,12 @@ and this project adheres to [Conventional Commits](https://www.conventionalcommi
Unreleased changes on the `development` branch. Unreleased changes on the `development` branch.
### Added
- **`gasm diff --map`** — compare functions whose names differ between files
(e.g. `--map wideCopyAVX2=wideCopyAVX512` pairs AVX2 and AVX-512 variants
regardless of suffix). Unmapped functions fall back to the original name match.
## [0.29.0] — 2026-08-05 ## [0.29.0] — 2026-08-05
RISC-V GOOBJ emission, YMM vector register display, named buffer allocation RISC-V GOOBJ emission, YMM vector register display, named buffer allocation
+35 -6
View File
@@ -542,7 +542,11 @@ Compare the machine code produced by assembling two files.
Shows which functions differ and the byte-level differences. Shows which functions differ and the byte-level differences.
Useful for verifying that two implementations produce identical code, Useful for verifying that two implementations produce identical code,
or for tracking encoding changes between Go assembler versions. or for tracking encoding changes between Go assembler versions.
Use --map to compare functions whose names differ between the files,
e.g. --map wideCopyAVX2=wideCopyAVX512 pairs the two regardless of suffix.
`) `)
mapSpec := fs.String("map", "", "comma-separated old=new pairs to match functions with different names")
fs.Parse(args) fs.Parse(args)
if fs.NArg() != 2 { if fs.NArg() != 2 {
fmt.Fprintln(os.Stderr, "usage: gasm diff <file1.s> <file2.s>") fmt.Fprintln(os.Stderr, "usage: gasm diff <file1.s> <file2.s>")
@@ -550,6 +554,19 @@ or for tracking encoding changes between Go assembler versions.
} }
path1, path2 := fs.Arg(0), fs.Arg(1) path1, path2 := fs.Arg(0), fs.Arg(1)
// Parse the name mapping (file1 name → file2 name).
nameMap := make(map[string]string)
if *mapSpec != "" {
for _, pair := range strings.Split(*mapSpec, ",") {
old, new, ok := strings.Cut(pair, "=")
if !ok || old == "" || new == "" {
fmt.Fprintf(os.Stderr, "gasm diff: invalid --map pair %q (expected old=new)\n", pair)
return 2
}
nameMap[old] = new
}
}
// Assemble both files. // Assemble both files.
img1, err := assembleFile(path1) img1, err := assembleFile(path1)
if err != nil { if err != nil {
@@ -562,7 +579,7 @@ or for tracking encoding changes between Go assembler versions.
return 1 return 1
} }
// Compare functions by name. // Compare functions by name, honouring the --map overrides.
funcs1 := make(map[string][]byte) funcs1 := make(map[string][]byte)
for _, fn := range img1.Funcs { for _, fn := range img1.Funcs {
funcs1[fn.Name] = img1.Code[fn.Offset : fn.Offset+fn.Size] funcs1[fn.Name] = img1.Code[fn.Offset : fn.Offset+fn.Size]
@@ -572,25 +589,37 @@ or for tracking encoding changes between Go assembler versions.
funcs2[fn.Name] = img2.Code[fn.Offset : fn.Offset+fn.Size] funcs2[fn.Name] = img2.Code[fn.Offset : fn.Offset+fn.Size]
} }
// Find common functions and compare. // Track which file2 functions were consumed (by direct match or via --map)
// so the "only in file2" pass skips them.
matched2 := make(map[string]bool)
diffs := 0 diffs := 0
for name, code1 := range funcs1 { for name, code1 := range funcs1 {
code2, ok := funcs2[name] target := name
if mapped, ok := nameMap[name]; ok {
target = mapped
}
code2, ok := funcs2[target]
if !ok { if !ok {
fmt.Printf("%s: only in %s\n", name, path1) fmt.Printf("%s: only in %s\n", name, path1)
diffs++ diffs++
continue continue
} }
matched2[target] = true
label := name
if target != name {
label = name + " → " + target
}
if !bytes.Equal(code1, code2) { if !bytes.Equal(code1, code2) {
fmt.Printf("%s: DIFFERS (%d vs %d bytes)\n", name, len(code1), len(code2)) fmt.Printf("%s: DIFFERS (%d vs %d bytes)\n", label, len(code1), len(code2))
printByteDiff(code1, code2) printByteDiff(code1, code2)
diffs++ diffs++
} else { } else {
fmt.Printf("%s: identical (%d bytes)\n", name, len(code1)) fmt.Printf("%s: identical (%d bytes)\n", label, len(code1))
} }
} }
for name := range funcs2 { for name := range funcs2 {
if _, ok := funcs1[name]; !ok { if !matched2[name] {
fmt.Printf("%s: only in %s\n", name, path2) fmt.Printf("%s: only in %s\n", name, path2)
diffs++ diffs++
} }