asmdiff 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- asmdiff-0.3.0/2026-07-10-reverb-session-feedback-handoff.md +102 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/PKG-INFO +86 -5
- {asmdiff-0.2.0 → asmdiff-0.3.0}/README.md +85 -4
- {asmdiff-0.2.0 → asmdiff-0.3.0}/asmdiff.example.toml +6 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/asmdiff.py +429 -54
- asmdiff-0.3.0/demo/delay_macro.c +414 -0
- asmdiff-0.3.0/demo/delay_orig.c +355 -0
- asmdiff-0.3.0/demo/delay_restrict.c +361 -0
- asmdiff-0.3.0/demo/delay_struct.c +415 -0
- asmdiff-0.3.0/demo/esp32-matrix.png +0 -0
- asmdiff-0.3.0/demo/quickstart.png +0 -0
- asmdiff-0.3.0/hackaday-tip-draft.md +101 -0
- asmdiff-0.3.0/hn-show-draft.md +57 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/pyproject.toml +4 -1
- {asmdiff-0.2.0 → asmdiff-0.3.0}/test_asmdiff.py +490 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/.claude/settings.local.json +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/.gitignore +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/2026-07-06-inspect-mode.md +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/2026-07-07-codegen-hotspots-handoff.md:Zone.Identifier +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/2026-07-07-elf-mode-handoff.md +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/2026-07-07-table-layout-handoff.md +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/CLAUDE.md +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/FIELD-TEST-REPORT.md +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/LICENSE +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/Screenshot 2026-07-07 225145.jpg +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/amy-hotspot-analysis.md +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/asmdiff_example.c +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/demo/esp32-matrix.gif +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/demo/quickstart.gif +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/dist_1/.gitignore +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/dist_1/asmdiff-0.1.1-py3-none-any.whl +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/dist_1/asmdiff-0.1.1.tar.gz +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/dist_1/asmdiff-0.1.2-py3-none-any.whl +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/dist_1/asmdiff-0.1.2.tar.gz +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/dist_1/asmdiff-0.1.3-py3-none-any.whl +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/dist_1/asmdiff-0.1.3.tar.gz +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/dist_1/asmdiff-0.1.4-py3-none-any.whl +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/dist_1/asmdiff-0.1.4.tar.gz +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/docs/superpowers/plans/2026-07-08-asmdiff-demo-gifs.md +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/docs/superpowers/specs/2026-07-07-edit-config-design.md +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/docs/superpowers/specs/2026-07-08-asmdiff-demo-gifs-design.md +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/docs/superpowers/specs/2026-07-08-asmdiff-demo-gifs-pipeline.mmd +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/gif-plan.md +0 -0
- {asmdiff-0.2.0 → asmdiff-0.3.0}/how-could-i-create-velvety-hollerith.md:Zone.Identifier +0 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Field feedback: S3-Amysynth reverb codegen session — handoff 2026-07-10
|
|
2
|
+
|
|
3
|
+
Feedback from a real investigation, not a scripted test matrix. Workload:
|
|
4
|
+
`stereo_reverb` in AMY's `delay.c` (ESP32-S3 project at `~/esp-idf/S3-Amysynth`,
|
|
5
|
+
xtensa-esp-elf-gcc 15.2.0, ship `-O2` flags via `-db`), comparing four source
|
|
6
|
+
variants: pre-optimization original (extracted from git history), the shipped
|
|
7
|
+
token-paste locals-cache, a struct/`static inline` respelling, and a
|
|
8
|
+
`restrict`-annotated original. Modes exercised: two-file `-a FUNC` compare,
|
|
9
|
+
`--target` matrix, `-db` + `--flags-like`, host-compiler contrast runs.
|
|
10
|
+
|
|
11
|
+
## State
|
|
12
|
+
|
|
13
|
+
The tool answered every question the session asked, correctly. All findings
|
|
14
|
+
below are UX/ergonomics, not correctness: in each case asmdiff computed the
|
|
15
|
+
right thing and then made the caller dig for it. Ranked by time actually lost.
|
|
16
|
+
|
|
17
|
+
## Findings
|
|
18
|
+
|
|
19
|
+
### 1. Summary tables print last; no summaries-only mode
|
|
20
|
+
Two-file compare with the default target set produced a 102 KB dump (three full
|
|
21
|
+
side-by-side listings, esp32/s2/s3) with the `function/role/insns/loop
|
|
22
|
+
spans/calls` table at the *end* of each target section. Every subsequent
|
|
23
|
+
invocation was pre-piped through `grep -A4 "role.*insns"` to skip the listings.
|
|
24
|
+
|
|
25
|
+
Repro: `asmdiff delay_macro.c delay_struct.c -a stereo_reverb -db --flags-like
|
|
26
|
+
components/amy/src/delay.c` (no `--target`).
|
|
27
|
+
|
|
28
|
+
Proposal: `--summary-only` (or print summaries before listings). For
|
|
29
|
+
agent/scripted callers the table is the decision input; the listing is only
|
|
30
|
+
pulled when a delta needs explaining. A `--json` summary emitter would serve
|
|
31
|
+
the same callers.
|
|
32
|
+
|
|
33
|
+
### 2. Doc drift: `host` target promised, absent from shipped config
|
|
34
|
+
The skill/README text says ESP profiles "+ host" are pre-configured;
|
|
35
|
+
`~/.config/asmdiff.toml` actually ships `gcc` and `clang`. `--target host`
|
|
36
|
+
errors out. The error's target list is what rescued the session — good — but
|
|
37
|
+
the drift costs one failed call per session that trusts the docs.
|
|
38
|
+
|
|
39
|
+
Fix: add a `[host]` alias target to the example/default toml, or correct the
|
|
40
|
+
README and the `~/.claude/skills/asmdiff` skill text (both say "host").
|
|
41
|
+
|
|
42
|
+
### 3. `-db` on foreign-arch targets drops project include paths with the flags
|
|
43
|
+
For host targets, `-db` correctly refuses to borrow xtensa flags ("compiling
|
|
44
|
+
without its include/define flags") — but that also drops `-I`, so the compile
|
|
45
|
+
dies on `fatal error: amy.h: No such file or directory`. Workaround was manual:
|
|
46
|
+
`-- -I~/esp-idf/S3-Amysynth/components/amy/src`.
|
|
47
|
+
|
|
48
|
+
Proposal: the project's own `-I` paths are target-portable even when its `-D`s
|
|
49
|
+
are not. For foreign-arch targets, borrow `-I` only (skip `-D`), or offer
|
|
50
|
+
`--db-includes`. Keep the current loud note either way — the refusal itself is
|
|
51
|
+
right (see FIELD-TEST-REPORT Trap 1 for why silently borrowing would be worse).
|
|
52
|
+
|
|
53
|
+
### 4. No per-span instruction mix (`--span-stats`)
|
|
54
|
+
The decisive number of the session — loop body 96 loads / 31 stores vs 85 / 26,
|
|
55
|
+
multiplies identical at 14 — required two rounds of hand-written awk over the
|
|
56
|
+
listing, and the first attempt silently counted past the loop end into the
|
|
57
|
+
epilogue. The tool's own doctrine is "weigh the span, not the function"; it
|
|
58
|
+
should be able to weigh it. The 2026-07-07 ELF round hit the same wall (ad-hoc
|
|
59
|
+
awk over objdump for loop tabulation), so this is a recurring gap.
|
|
60
|
+
|
|
61
|
+
Proposal: `--span-stats` — per-span opcode histogram (loads / stores / mul /
|
|
62
|
+
branch / other), per function, alongside the existing `span:count` column.
|
|
63
|
+
|
|
64
|
+
### 5. Side-by-side pair listings don't collapse identical runs
|
|
65
|
+
Two ~500-insn functions differing by ~5 instructions print ~1000 listing lines
|
|
66
|
+
of which ~990 are identical pairs. Context-diff-style collapsing (keep N lines
|
|
67
|
+
around changed hunks, elide the rest with a count) would turn the 102 KB dump
|
|
68
|
+
from finding 1 into a few readable hunks. Interacts with finding 1: summary
|
|
69
|
+
first, collapsed hunks second, full listing on request.
|
|
70
|
+
|
|
71
|
+
### 6. Minor: missing-toolchain error could name the compiler
|
|
72
|
+
`--target rp2350` with no ARM toolchain installed → "error: no usable compiler
|
|
73
|
+
in the matrix". Accurate, but naming the miss ("arm-none-eabi-gcc: not found
|
|
74
|
+
on PATH") makes the fix obvious.
|
|
75
|
+
|
|
76
|
+
## Verified working this session (regression notes)
|
|
77
|
+
|
|
78
|
+
- `--flags-like <db-file>` on out-of-tree copies: exact borrowed-flag parity
|
|
79
|
+
both sides — this closes FIELD-TEST-REPORT Trap 2 ("before/after on a project
|
|
80
|
+
source has no supported path") and its Trap 1 hazard for the copy workflow.
|
|
81
|
+
- Two-file `-a FUNC` compare against a `git show <rev>:src/file.c` extraction:
|
|
82
|
+
frictionless; this is the natural "what did that upstream commit buy"
|
|
83
|
+
workflow.
|
|
84
|
+
- Default target resolution + multi-target matrix from config: zero toolchain
|
|
85
|
+
path digging; glob `cc` patterns resolved and announced.
|
|
86
|
+
- `span:count` + `calls` columns answered ZOL and libcall questions at a
|
|
87
|
+
glance.
|
|
88
|
+
|
|
89
|
+
Session results usable as a future test corpus (esp32s3, ship -O2, AMY at
|
|
90
|
+
v1.2.31): `stereo_reverb` original 437 insns / span 327; +`restrict` best
|
|
91
|
+
effort 473 / 306; shipped locals-cache 509 / 290; struct respelling 504 / 289.
|
|
92
|
+
Host: gcc -O3 467 = 467, clang -O3 443 = 443 (macro vs struct byte-count
|
|
93
|
+
identical). Variant sources preserved in the session job dir
|
|
94
|
+
(`~/.claude/jobs/b3de0cac/tmp/delay_{orig,restrict,macro,struct}.c`) — copy
|
|
95
|
+
them into `demo/` or the test suite if wanted before that dir is cleaned.
|
|
96
|
+
|
|
97
|
+
## Next steps
|
|
98
|
+
|
|
99
|
+
1. Finding 1 (`--summary-only`) — smallest change, largest per-call saving.
|
|
100
|
+
2. Finding 4 (`--span-stats`) — recurring need across two sessions.
|
|
101
|
+
3. Finding 5 (collapse identical hunks) — pairs with 1.
|
|
102
|
+
4. Findings 2, 3, 6 — config/doc/message fixes, each ~minutes.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: asmdiff
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Per-function assembly inspection and comparison, across a compiler matrix or from a shipped ELF
|
|
5
5
|
Project-URL: Homepage, https://github.com/rt-rtos/asmdiff
|
|
6
6
|
Project-URL: Repository, https://github.com/rt-rtos/asmdiff
|
|
@@ -358,9 +358,11 @@ zero-overhead loops", the empirical rules (verified on esp-gcc 15.2,
|
|
|
358
358
|
```
|
|
359
359
|
asmdiff SOURCE.c [SOURCE2.c | FUNC...] [--pair OLD:NEW]... [--across FUNC]...
|
|
360
360
|
[--cc 'CC FLAGS']... [--target NAME]... [--config PATH]
|
|
361
|
-
[--compile-commands [PATH]] [--flags-like PATH]
|
|
361
|
+
[--compile-commands [PATH]] [--flags-like PATH] [--db-includes]
|
|
362
|
+
[--summary-only] [--collapse] [--span-stats]
|
|
362
363
|
[--layout list|side-by-side] [-v] [-- EXTRA_FLAGS...]
|
|
363
364
|
asmdiff FIRMWARE.elf [FUNC...] [--filter REGEX] [--objdump PATH]
|
|
365
|
+
[--summary-only] [--span-stats]
|
|
364
366
|
```
|
|
365
367
|
|
|
366
368
|
| Option | Meaning |
|
|
@@ -376,6 +378,12 @@ asmdiff FIRMWARE.elf [FUNC...] [--filter REGEX] [--objdump PATH]
|
|
|
376
378
|
| `--config PATH` | Config file to use. Default search: `asmdiff.toml` next to `SOURCE.c`, then in the current directory, then `~/.config/`. First hit wins. |
|
|
377
379
|
| `-db, --compile-commands [PATH]` | Borrow each source's include/define flags from a `compile_commands.json`; with no `PATH`, walk up from the CWD checking each directory and its `build/` until the repository root. See [below](#borrowing-includes-from-compile_commandsjson). |
|
|
378
380
|
| `--flags-like PATH` | A source with no `compile_commands` entry borrows the flags recorded for `PATH` — the way to compare a modified copy of a project source under its original's header environment. |
|
|
381
|
+
| `--db-includes` | Borrow only the header-search paths from the database, dropping its defines, forced includes, and `-specs`/`--sysroot`; kept paths are re-emitted as `-idirafter` so they cannot shadow the host's own system headers. This is how a host target compiles a cross project's source. |
|
|
382
|
+
| `-s`, `--summary-only` | Print only the summary/stats tables, suppressing every assembly listing (see [Shaping the output](#shaping-the-output-for-reading-vs-deciding)). |
|
|
383
|
+
| `--json` | Emit the summary as JSON on stdout instead of tables — one record per function per compiler. Implies `--summary-only`; errors stay plain text on stderr. |
|
|
384
|
+
| `--collapse` | In side-by-side listings, elide runs of identical line pairs, keeping 3 lines of context around each difference. |
|
|
385
|
+
| `--span-stats` | Follow each stats table with a per-loop-span instruction mix: load/store/mul/branch/other counts per span. |
|
|
386
|
+
| `--version` | Print the version and exit. |
|
|
379
387
|
| `-v`, `--verbose` | On compile failure, print the full compiler command and complete error output. Default shows only the compiler, the source, and the first error lines. |
|
|
380
388
|
| `-- FLAGS...` | Everything after a bare `--` is appended to *every* compiler invocation. |
|
|
381
389
|
|
|
@@ -399,9 +407,66 @@ asmdiff h.c -- -fno-math-errno
|
|
|
399
407
|
```
|
|
400
408
|
|
|
401
409
|
Compilers missing from `PATH` are skipped with a warning; the run fails only
|
|
402
|
-
if none are usable
|
|
403
|
-
(
|
|
404
|
-
|
|
410
|
+
if none are usable, and that error names each missed binary
|
|
411
|
+
(`no-such-gcc: not found on PATH`) so the fix is in the message, not in a
|
|
412
|
+
warning that scrolled away. Exit status is non-zero only for operational
|
|
413
|
+
failures (compile error — the compiler's stderr is shown — unknown `--pair`
|
|
414
|
+
name, no usable compiler). Differing assembly is the expected result, never
|
|
415
|
+
an error.
|
|
416
|
+
|
|
417
|
+
## Shaping the output for reading vs deciding
|
|
418
|
+
|
|
419
|
+
A two-file compare over a three-target matrix prints three full
|
|
420
|
+
side-by-side listings with the summary table at the *end* of each — the
|
|
421
|
+
right shape for reading an unfamiliar delta, and roughly 100 KB of the
|
|
422
|
+
wrong shape when the table is all the decision needs. Three flags cut the
|
|
423
|
+
output to purpose:
|
|
424
|
+
|
|
425
|
+
- **`--summary-only`** (`-s`) keeps only the summary/stats tables. This is
|
|
426
|
+
the scripted/agent view: the table is the decision input, and a listing
|
|
427
|
+
is pulled with a second, narrower run only when a delta needs
|
|
428
|
+
explaining.
|
|
429
|
+
- **`--collapse`** elides runs of identical line pairs in every
|
|
430
|
+
side-by-side listing, keeping 3 lines of context around each difference
|
|
431
|
+
plus a `... N identical lines ...` marker. Two ~500-instruction
|
|
432
|
+
functions differing by five instructions render as a few readable hunks
|
|
433
|
+
instead of ~1000 lines.
|
|
434
|
+
- **`--span-stats`** follows each stats table with a per-loop-span
|
|
435
|
+
instruction mix, one row per span:
|
|
436
|
+
|
|
437
|
+
```
|
|
438
|
+
function span insns load store mul branch other
|
|
439
|
+
stereo_reverb .L108 327 96 31 14 12 174
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
This weighs the span rather than the whole function — the number that
|
|
443
|
+
actually decides a hot-loop comparison (is the rewrite trading loads
|
|
444
|
+
for stores? did the multiplies move?) without hand-counting a listing
|
|
445
|
+
and accidentally tallying past the loop end into the epilogue.
|
|
446
|
+
Buckets are by mnemonic (Xtensa, RISC-V, ARM) with an AT&T
|
|
447
|
+
memory-operand heuristic for x86; *branch* includes calls (outbound
|
|
448
|
+
calls are already itemised in the `calls` column), and anything the
|
|
449
|
+
tables don't know lands in *other*.
|
|
450
|
+
|
|
451
|
+
All three combine with every compile mode; `--summary-only` and
|
|
452
|
+
`--span-stats` also apply to ELF input.
|
|
453
|
+
|
|
454
|
+
For scripted callers, `--json` replaces the tables entirely with one
|
|
455
|
+
JSON document on stdout: a flat `results` list holding one record per
|
|
456
|
+
function per compiler — `cc`, `tag` (source label in two-file runs),
|
|
457
|
+
`role` (`baseline`/`candidate` in paired runs), `insns`, `loop_spans`,
|
|
458
|
+
`calls`, and `span_stats` when `--span-stats` is given. Flat records
|
|
459
|
+
keep it one `jq` expression away from any question the tables answer:
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
asmdiff old.c new.c -a stereo_reverb --json --span-stats \
|
|
463
|
+
| jq '.results[] | {role, insns, spans: .loop_spans}'
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
The top level carries `asmdiff` (version) and `mode`
|
|
467
|
+
(`pairs`/`across`/`inspect`/`summary`/`elf`; ELF runs add the binary's
|
|
468
|
+
path as `elf`). Warnings and errors stay plain text on stderr, so a
|
|
469
|
+
failed run never emits half a document.
|
|
405
470
|
|
|
406
471
|
## Config file: named targets
|
|
407
472
|
|
|
@@ -573,6 +638,17 @@ Details that make it robust:
|
|
|
573
638
|
- **`compile_commands` expands `~` and `$VARS`.** The `==` header always
|
|
574
639
|
prints the resolved compiler command; run with `-- -v` if you want to see
|
|
575
640
|
every include path the compiler actually received.
|
|
641
|
+
- **`--db-includes` restricts the borrow to header-search paths**,
|
|
642
|
+
dropping `-D`/`-U`, forced includes, and `-specs`/`--sysroot`. Use it
|
|
643
|
+
when the *target* doesn't match the arch the database was built for —
|
|
644
|
+
host gcc given an ESP-IDF database would otherwise inherit
|
|
645
|
+
`-specs=picolibc.specs` and cross-only defines it cannot compile
|
|
646
|
+
with, yet still needs the project's include paths to find its
|
|
647
|
+
headers. The kept paths are re-emitted as `-idirafter`, searched
|
|
648
|
+
*after* the compiler's own system directories: ESP-IDF's include set
|
|
649
|
+
contains a libc-overlay directory (`esp_libc/platform_include`) whose
|
|
650
|
+
`stdio.h` would shadow the host's under plain `-I`; demoted, the
|
|
651
|
+
project's own headers still resolve and the host's libc wins.
|
|
576
652
|
|
|
577
653
|
### Auto-discovering the database
|
|
578
654
|
|
|
@@ -665,6 +741,11 @@ baseline (`../baseline/src/oscillators.c`), which is the same file under a
|
|
|
665
741
|
path the database has never heard of. The absent-source error and the
|
|
666
742
|
soft-miss note both point at this flag when a same-name entry exists.
|
|
667
743
|
|
|
744
|
+
To contrast the same pair on a host compiler (does gcc-on-x86 make the
|
|
745
|
+
same choice?), add `--db-includes`: the borrow then carries only the
|
|
746
|
+
project's include paths, which are target-portable, and none of the
|
|
747
|
+
cross-only defines or `-specs` a host gcc would choke on.
|
|
748
|
+
|
|
668
749
|
## Whole-file summary
|
|
669
750
|
|
|
670
751
|
With no `--pair`, no `--across`, and no `old_*`/`new_*` functions to
|
|
@@ -340,9 +340,11 @@ zero-overhead loops", the empirical rules (verified on esp-gcc 15.2,
|
|
|
340
340
|
```
|
|
341
341
|
asmdiff SOURCE.c [SOURCE2.c | FUNC...] [--pair OLD:NEW]... [--across FUNC]...
|
|
342
342
|
[--cc 'CC FLAGS']... [--target NAME]... [--config PATH]
|
|
343
|
-
[--compile-commands [PATH]] [--flags-like PATH]
|
|
343
|
+
[--compile-commands [PATH]] [--flags-like PATH] [--db-includes]
|
|
344
|
+
[--summary-only] [--collapse] [--span-stats]
|
|
344
345
|
[--layout list|side-by-side] [-v] [-- EXTRA_FLAGS...]
|
|
345
346
|
asmdiff FIRMWARE.elf [FUNC...] [--filter REGEX] [--objdump PATH]
|
|
347
|
+
[--summary-only] [--span-stats]
|
|
346
348
|
```
|
|
347
349
|
|
|
348
350
|
| Option | Meaning |
|
|
@@ -358,6 +360,12 @@ asmdiff FIRMWARE.elf [FUNC...] [--filter REGEX] [--objdump PATH]
|
|
|
358
360
|
| `--config PATH` | Config file to use. Default search: `asmdiff.toml` next to `SOURCE.c`, then in the current directory, then `~/.config/`. First hit wins. |
|
|
359
361
|
| `-db, --compile-commands [PATH]` | Borrow each source's include/define flags from a `compile_commands.json`; with no `PATH`, walk up from the CWD checking each directory and its `build/` until the repository root. See [below](#borrowing-includes-from-compile_commandsjson). |
|
|
360
362
|
| `--flags-like PATH` | A source with no `compile_commands` entry borrows the flags recorded for `PATH` — the way to compare a modified copy of a project source under its original's header environment. |
|
|
363
|
+
| `--db-includes` | Borrow only the header-search paths from the database, dropping its defines, forced includes, and `-specs`/`--sysroot`; kept paths are re-emitted as `-idirafter` so they cannot shadow the host's own system headers. This is how a host target compiles a cross project's source. |
|
|
364
|
+
| `-s`, `--summary-only` | Print only the summary/stats tables, suppressing every assembly listing (see [Shaping the output](#shaping-the-output-for-reading-vs-deciding)). |
|
|
365
|
+
| `--json` | Emit the summary as JSON on stdout instead of tables — one record per function per compiler. Implies `--summary-only`; errors stay plain text on stderr. |
|
|
366
|
+
| `--collapse` | In side-by-side listings, elide runs of identical line pairs, keeping 3 lines of context around each difference. |
|
|
367
|
+
| `--span-stats` | Follow each stats table with a per-loop-span instruction mix: load/store/mul/branch/other counts per span. |
|
|
368
|
+
| `--version` | Print the version and exit. |
|
|
361
369
|
| `-v`, `--verbose` | On compile failure, print the full compiler command and complete error output. Default shows only the compiler, the source, and the first error lines. |
|
|
362
370
|
| `-- FLAGS...` | Everything after a bare `--` is appended to *every* compiler invocation. |
|
|
363
371
|
|
|
@@ -381,9 +389,66 @@ asmdiff h.c -- -fno-math-errno
|
|
|
381
389
|
```
|
|
382
390
|
|
|
383
391
|
Compilers missing from `PATH` are skipped with a warning; the run fails only
|
|
384
|
-
if none are usable
|
|
385
|
-
(
|
|
386
|
-
|
|
392
|
+
if none are usable, and that error names each missed binary
|
|
393
|
+
(`no-such-gcc: not found on PATH`) so the fix is in the message, not in a
|
|
394
|
+
warning that scrolled away. Exit status is non-zero only for operational
|
|
395
|
+
failures (compile error — the compiler's stderr is shown — unknown `--pair`
|
|
396
|
+
name, no usable compiler). Differing assembly is the expected result, never
|
|
397
|
+
an error.
|
|
398
|
+
|
|
399
|
+
## Shaping the output for reading vs deciding
|
|
400
|
+
|
|
401
|
+
A two-file compare over a three-target matrix prints three full
|
|
402
|
+
side-by-side listings with the summary table at the *end* of each — the
|
|
403
|
+
right shape for reading an unfamiliar delta, and roughly 100 KB of the
|
|
404
|
+
wrong shape when the table is all the decision needs. Three flags cut the
|
|
405
|
+
output to purpose:
|
|
406
|
+
|
|
407
|
+
- **`--summary-only`** (`-s`) keeps only the summary/stats tables. This is
|
|
408
|
+
the scripted/agent view: the table is the decision input, and a listing
|
|
409
|
+
is pulled with a second, narrower run only when a delta needs
|
|
410
|
+
explaining.
|
|
411
|
+
- **`--collapse`** elides runs of identical line pairs in every
|
|
412
|
+
side-by-side listing, keeping 3 lines of context around each difference
|
|
413
|
+
plus a `... N identical lines ...` marker. Two ~500-instruction
|
|
414
|
+
functions differing by five instructions render as a few readable hunks
|
|
415
|
+
instead of ~1000 lines.
|
|
416
|
+
- **`--span-stats`** follows each stats table with a per-loop-span
|
|
417
|
+
instruction mix, one row per span:
|
|
418
|
+
|
|
419
|
+
```
|
|
420
|
+
function span insns load store mul branch other
|
|
421
|
+
stereo_reverb .L108 327 96 31 14 12 174
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
This weighs the span rather than the whole function — the number that
|
|
425
|
+
actually decides a hot-loop comparison (is the rewrite trading loads
|
|
426
|
+
for stores? did the multiplies move?) without hand-counting a listing
|
|
427
|
+
and accidentally tallying past the loop end into the epilogue.
|
|
428
|
+
Buckets are by mnemonic (Xtensa, RISC-V, ARM) with an AT&T
|
|
429
|
+
memory-operand heuristic for x86; *branch* includes calls (outbound
|
|
430
|
+
calls are already itemised in the `calls` column), and anything the
|
|
431
|
+
tables don't know lands in *other*.
|
|
432
|
+
|
|
433
|
+
All three combine with every compile mode; `--summary-only` and
|
|
434
|
+
`--span-stats` also apply to ELF input.
|
|
435
|
+
|
|
436
|
+
For scripted callers, `--json` replaces the tables entirely with one
|
|
437
|
+
JSON document on stdout: a flat `results` list holding one record per
|
|
438
|
+
function per compiler — `cc`, `tag` (source label in two-file runs),
|
|
439
|
+
`role` (`baseline`/`candidate` in paired runs), `insns`, `loop_spans`,
|
|
440
|
+
`calls`, and `span_stats` when `--span-stats` is given. Flat records
|
|
441
|
+
keep it one `jq` expression away from any question the tables answer:
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
asmdiff old.c new.c -a stereo_reverb --json --span-stats \
|
|
445
|
+
| jq '.results[] | {role, insns, spans: .loop_spans}'
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
The top level carries `asmdiff` (version) and `mode`
|
|
449
|
+
(`pairs`/`across`/`inspect`/`summary`/`elf`; ELF runs add the binary's
|
|
450
|
+
path as `elf`). Warnings and errors stay plain text on stderr, so a
|
|
451
|
+
failed run never emits half a document.
|
|
387
452
|
|
|
388
453
|
## Config file: named targets
|
|
389
454
|
|
|
@@ -555,6 +620,17 @@ Details that make it robust:
|
|
|
555
620
|
- **`compile_commands` expands `~` and `$VARS`.** The `==` header always
|
|
556
621
|
prints the resolved compiler command; run with `-- -v` if you want to see
|
|
557
622
|
every include path the compiler actually received.
|
|
623
|
+
- **`--db-includes` restricts the borrow to header-search paths**,
|
|
624
|
+
dropping `-D`/`-U`, forced includes, and `-specs`/`--sysroot`. Use it
|
|
625
|
+
when the *target* doesn't match the arch the database was built for —
|
|
626
|
+
host gcc given an ESP-IDF database would otherwise inherit
|
|
627
|
+
`-specs=picolibc.specs` and cross-only defines it cannot compile
|
|
628
|
+
with, yet still needs the project's include paths to find its
|
|
629
|
+
headers. The kept paths are re-emitted as `-idirafter`, searched
|
|
630
|
+
*after* the compiler's own system directories: ESP-IDF's include set
|
|
631
|
+
contains a libc-overlay directory (`esp_libc/platform_include`) whose
|
|
632
|
+
`stdio.h` would shadow the host's under plain `-I`; demoted, the
|
|
633
|
+
project's own headers still resolve and the host's libc wins.
|
|
558
634
|
|
|
559
635
|
### Auto-discovering the database
|
|
560
636
|
|
|
@@ -647,6 +723,11 @@ baseline (`../baseline/src/oscillators.c`), which is the same file under a
|
|
|
647
723
|
path the database has never heard of. The absent-source error and the
|
|
648
724
|
soft-miss note both point at this flag when a same-name entry exists.
|
|
649
725
|
|
|
726
|
+
To contrast the same pair on a host compiler (does gcc-on-x86 make the
|
|
727
|
+
same choice?), add `--db-includes`: the borrow then carries only the
|
|
728
|
+
project's include paths, which are target-portable, and none of the
|
|
729
|
+
cross-only defines or `-specs` a host gcc would choke on.
|
|
730
|
+
|
|
650
731
|
## Whole-file summary
|
|
651
732
|
|
|
652
733
|
With no `--pair`, no `--across`, and no `old_*`/`new_*` functions to
|
|
@@ -26,6 +26,12 @@ flags = [
|
|
|
26
26
|
cc = "clang"
|
|
27
27
|
flags = ["-O3", "-Wall", "-Wextra"]
|
|
28
28
|
|
|
29
|
+
# The name the docs use for "your native compiler" (--target host): an
|
|
30
|
+
# alias of [gcc] kept as its own table so the examples work verbatim.
|
|
31
|
+
[host]
|
|
32
|
+
cc = "gcc"
|
|
33
|
+
flags = ["-O3", "-Wall", "-Wextra"]
|
|
34
|
+
|
|
29
35
|
# cc values may use ~, $VARS, and glob patterns. A glob that matches
|
|
30
36
|
# several installed toolchains resolves to the highest version-sorted
|
|
31
37
|
# one (announced on stderr); pin the exact esp-NN directory instead if
|