From ece0d3f127f8457d1a1be00af212c23e881eb80d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Wed, 5 Aug 2026 20:20:47 +0200 Subject: [PATCH] feat(cli): add --map flag to diff for comparing differently-named functions --- CHANGELOG.md | 6 ++++++ cmd/gasm/main.go | 41 +++++++++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e93aa75..706dd10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Conventional Commits](https://www.conventionalcommi 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 RISC-V GOOBJ emission, YMM vector register display, named buffer allocation diff --git a/cmd/gasm/main.go b/cmd/gasm/main.go index 3caf76d..06ace7a 100644 --- a/cmd/gasm/main.go +++ b/cmd/gasm/main.go @@ -542,7 +542,11 @@ Compare the machine code produced by assembling two files. Shows which functions differ and the byte-level differences. Useful for verifying that two implementations produce identical code, 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) if fs.NArg() != 2 { fmt.Fprintln(os.Stderr, "usage: gasm diff ") @@ -550,6 +554,19 @@ or for tracking encoding changes between Go assembler versions. } 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. img1, err := assembleFile(path1) if err != nil { @@ -562,7 +579,7 @@ or for tracking encoding changes between Go assembler versions. return 1 } - // Compare functions by name. + // Compare functions by name, honouring the --map overrides. funcs1 := make(map[string][]byte) for _, fn := range img1.Funcs { 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] } - // 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 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 { fmt.Printf("%s: only in %s\n", name, path1) diffs++ continue } + matched2[target] = true + label := name + if target != name { + label = name + " → " + target + } 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) diffs++ } 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 { - if _, ok := funcs1[name]; !ok { + if !matched2[name] { fmt.Printf("%s: only in %s\n", name, path2) diffs++ }