feat(cli): add --map flag to diff for comparing differently-named
functions
This commit is contained in:
@@ -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
@@ -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++
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user