asmdiff 0.1.1__tar.gz → 0.1.3__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.1.3/.claude/settings.local.json +9 -0
- asmdiff-0.1.3/.gitignore +2 -0
- asmdiff-0.1.3/CLAUDE.md +28 -0
- {asmdiff-0.1.1 → asmdiff-0.1.3}/PKG-INFO +113 -11
- {asmdiff-0.1.1 → asmdiff-0.1.3}/README.md +112 -10
- asmdiff-0.1.3/asmdiff.example.toml +51 -0
- {asmdiff-0.1.1 → asmdiff-0.1.3}/asmdiff.py +375 -28
- {asmdiff-0.1.1 → asmdiff-0.1.3}/pyproject.toml +2 -2
- asmdiff-0.1.3/test_asmdiff.py +912 -0
- asmdiff-0.1.1/.claude/settings.local.json +0 -7
- asmdiff-0.1.1/asmdiff.example.toml +0 -36
- asmdiff-0.1.1/test_asmdiff.py +0 -484
- {asmdiff-0.1.1 → asmdiff-0.1.3}/LICENSE +0 -0
- {asmdiff-0.1.1 → asmdiff-0.1.3}/asmdiff_example.c +0 -0
asmdiff-0.1.3/.gitignore
ADDED
asmdiff-0.1.3/CLAUDE.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# asmdiff
|
|
2
|
+
|
|
3
|
+
Single-file, stdlib-only Python CLI that compares per-function assembly
|
|
4
|
+
between paired C implementations across a compiler matrix. Published to
|
|
5
|
+
PyPI as `asmdiff` (hatchling build, see pyproject.toml).
|
|
6
|
+
|
|
7
|
+
## Toolchain
|
|
8
|
+
|
|
9
|
+
- Runtime: Python >= 3.8; config files (asmdiff.toml) need >= 3.11 (tomllib).
|
|
10
|
+
- No third-party dependencies, in source or tests. Keep it that way.
|
|
11
|
+
- Compilers invoked at runtime: whatever the user's matrix names
|
|
12
|
+
(host gcc/clang, xtensa/riscv ESP toolchains via glob patterns).
|
|
13
|
+
|
|
14
|
+
## Commands
|
|
15
|
+
|
|
16
|
+
- Run tests: `python3 test_asmdiff.py` (unittest; add `-v` for detail).
|
|
17
|
+
- Run the tool: `python3 asmdiff.py asmdiff_example.c`.
|
|
18
|
+
- Everything is safe/reversible: the tool only compiles to `-S` in a
|
|
19
|
+
temp dir and prints. No flashing, no shared infra.
|
|
20
|
+
|
|
21
|
+
## Layout
|
|
22
|
+
|
|
23
|
+
- `asmdiff.py` — the entire tool.
|
|
24
|
+
- `test_asmdiff.py` — unit tests, one class per feature area.
|
|
25
|
+
- `asmdiff.example.toml` — documented starter config; keep it generic
|
|
26
|
+
(portable gcc/clang defaults, ESP targets as commented examples).
|
|
27
|
+
- README.md documents every user-visible behavior; changes to flags or
|
|
28
|
+
config keys update README, example toml, and the module docstring.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: asmdiff
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Compare per-function assembly between paired C implementations
|
|
5
5
|
Project-URL: Homepage, https://github.com/rt-rtos/asmdiff
|
|
6
6
|
Project-URL: Repository, https://github.com/rt-rtos/asmdiff
|
|
@@ -16,7 +16,7 @@ Classifier: Topic :: Software Development :: Debuggers
|
|
|
16
16
|
Requires-Python: >=3.8
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
|
|
19
|
-
# asmdiff
|
|
19
|
+
# [asmdiff](https://pypi.org/project/asmdiff/)
|
|
20
20
|
## per-function assembly comparison for paired C implementations
|
|
21
21
|
|
|
22
22
|
> asmdiff is a stdlib only command-line tool for comparing the generated assembly of individual C functions across implementations, compiler flags, compiler versions, and source revisions. It is intended for investigating compiler code generation rather than benchmarking runtime performance.
|
|
@@ -94,7 +94,7 @@ $ asmdiff.py asmdiff_example.c
|
|
|
94
94
|
```
|
|
95
95
|
asmdiff.py SOURCE.c [SOURCE2.c] [--pair OLD:NEW]... [--across FUNC]...
|
|
96
96
|
[--cc 'CC FLAGS']... [--target NAME]... [--config PATH]
|
|
97
|
-
[-- EXTRA_FLAGS...]
|
|
97
|
+
[--compile-commands [PATH]] [-v] [-- EXTRA_FLAGS...]
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
| Option | Meaning |
|
|
@@ -105,6 +105,8 @@ asmdiff.py SOURCE.c [SOURCE2.c] [--pair OLD:NEW]... [--across FUNC]...
|
|
|
105
105
|
| `--cc 'CC FLAGS'` | One compiler invocation, command and flags in a single quoted string. Repeatable to build a matrix. |
|
|
106
106
|
| `--target NAME` | A named target from the config file, resolved to a `--cc` entry. Repeatable; appended to the matrix after `--cc` entries. |
|
|
107
107
|
| `--config PATH` | Config file to use. Default search: `asmdiff.toml` next to `SOURCE.c`, then in the current directory, then `~/.config/`. First hit wins. |
|
|
108
|
+
| `--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). |
|
|
109
|
+
| `-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. |
|
|
108
110
|
| `-- FLAGS...` | Everything after a bare `--` is appended to *every* compiler invocation. |
|
|
109
111
|
|
|
110
112
|
With no `--cc` and no `--target`, the config file's top-level
|
|
@@ -139,19 +141,19 @@ each compiler+flags combination once:
|
|
|
139
141
|
|
|
140
142
|
```toml
|
|
141
143
|
# asmdiff.toml — next to your harnesses, in CWD, or in ~/.config/
|
|
142
|
-
default = "
|
|
144
|
+
default = "esp32s3" # target(s) used when no --cc/--target is given
|
|
143
145
|
|
|
144
|
-
[
|
|
146
|
+
[esp32s3] # production-like ESP32-S3 codegen
|
|
145
147
|
cc = "$HOME/.espressif/tools/xtensa-esp-elf/esp-*/xtensa-esp-elf/bin/xtensa-esp32s3-elf-gcc"
|
|
146
148
|
flags = [
|
|
147
|
-
"-O2", "-
|
|
149
|
+
"-O2", "-DMY_FEATURE", "-DNDEBUG",
|
|
148
150
|
"-Wno-strict-aliasing", "-mlongcalls",
|
|
149
|
-
"-I$HOME/project/components/
|
|
151
|
+
"-I$HOME/project/components/dsp/include",
|
|
150
152
|
]
|
|
151
153
|
|
|
152
|
-
[host
|
|
154
|
+
[host] # same defines on host gcc
|
|
153
155
|
cc = "gcc"
|
|
154
|
-
flags = ["-O2", "-
|
|
156
|
+
flags = ["-O2", "-DMY_FEATURE", "-I$HOME/project/components/dsp/include"]
|
|
155
157
|
```
|
|
156
158
|
|
|
157
159
|
`cc` values expand `~` and `$VARS` and may be glob patterns, so a config
|
|
@@ -168,8 +170,8 @@ shapes:
|
|
|
168
170
|
|
|
169
171
|
```bash
|
|
170
172
|
asmdiff.py h.c # config default target(s)
|
|
171
|
-
asmdiff.py h.c --target
|
|
172
|
-
asmdiff.py h.c --across f --target
|
|
173
|
+
asmdiff.py h.c --target esp32s3 --target host # two-target matrix
|
|
174
|
+
asmdiff.py h.c --across f --target esp32s3 --cc 'gcc -O2' # mix freely
|
|
173
175
|
```
|
|
174
176
|
|
|
175
177
|
A config placed next to your harness files travels with them: any invocation
|
|
@@ -179,6 +181,100 @@ included `asmdiff.example.toml` is a starting point. If a flag or include
|
|
|
179
181
|
path must vary per machine, that's what per-machine config files are for —
|
|
180
182
|
nothing lives in the tool.
|
|
181
183
|
|
|
184
|
+
### Borrowing includes from `compile_commands.json`
|
|
185
|
+
|
|
186
|
+
A real project source rarely compiles with a handful of `-I` flags. An
|
|
187
|
+
ESP-IDF component pulls in `freertos/FreeRTOS.h`, `esp_*` headers, and a
|
|
188
|
+
*generated* `sdkconfig.h`, reachable only through the dozens of `-I`/`-D`
|
|
189
|
+
flags the build system computes — none of which live in the source file.
|
|
190
|
+
That is why `asmdiff.py component.c` fails with `freertos/FreeRTOS.h: No
|
|
191
|
+
such file or directory`: not a wrong compiler (the xtensa toolchain ships
|
|
192
|
+
no FreeRTOS either), just missing include paths. Transcribing them by hand
|
|
193
|
+
is miserable.
|
|
194
|
+
|
|
195
|
+
So don't. Any build that uses CMake or Ninja can emit a
|
|
196
|
+
[`compile_commands.json`](https://clang.llvm.org/docs/JSONCompilationDatabase.html)
|
|
197
|
+
recording the exact flags for every source it builds (ESP-IDF writes one to
|
|
198
|
+
`build/compile_commands.json` on every `idf.py build`). Point a target at it:
|
|
199
|
+
|
|
200
|
+
```toml
|
|
201
|
+
[esp32s3-idf]
|
|
202
|
+
cc = "xtensa-esp32s3-elf-gcc"
|
|
203
|
+
flags = ["-O2", "-mlongcalls"]
|
|
204
|
+
compile_commands = "$HOME/myproject/build/compile_commands.json"
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Now `asmdiff.py $HOME/myproject/components/dsp/biquad.c --target esp32s3-idf`
|
|
209
|
+
finds that file's entry in the database and adds the include/define flags
|
|
210
|
+
it recorded — `-I`, `-isystem`, `-iquote`, `-idirafter`, `-include`,
|
|
211
|
+
`-imacros`, `-D`, `-U`, plus the header-environment driver flags `-specs`
|
|
212
|
+
and `--sysroot` — to this target's command. Everything else the
|
|
213
|
+
database records (its own compiler, `-O`/`-std`/`-W` flags, `-c`, `-o`, the
|
|
214
|
+
source) is ignored: **you** own the compiler and optimisation flags via
|
|
215
|
+
`cc`/`flags`; only the header environment is borrowed. That split is the
|
|
216
|
+
point — you can now sweep *your* `-O`/`-m` variations over a source that
|
|
217
|
+
only ever compiled one way under the build system.
|
|
218
|
+
|
|
219
|
+
Details that make it robust:
|
|
220
|
+
|
|
221
|
+
- **Paths are made absolute** against each entry's `directory`, so a
|
|
222
|
+
database full of build-relative `-I../include` flags still resolves when
|
|
223
|
+
asmdiff runs from anywhere.
|
|
224
|
+
- **`@file` response files are expanded.** Build systems park flags in
|
|
225
|
+
them — ESP-IDF v6 hides `-specs=picolibc.specs` in
|
|
226
|
+
`build/toolchain/cflags`, and without it every libc header breaks —
|
|
227
|
+
so the flag scan reads them (nested ones too) instead of skipping the
|
|
228
|
+
token. A bare specs name is left for the compiler's own search
|
|
229
|
+
directories; a specs path resolves like any other recorded path.
|
|
230
|
+
- **Per source file.** The lookup keys on the source you pass (matched by
|
|
231
|
+
resolved absolute path), so two files in an `--across`/summary run each
|
|
232
|
+
get their own recorded flags.
|
|
233
|
+
- **Absent source is an error**, not a silent empty flag set — otherwise
|
|
234
|
+
you'd just hit the missing-header failure this feature exists to prevent.
|
|
235
|
+
The message flags a same-basename entry recorded under a different path.
|
|
236
|
+
- **`compile_commands` expands `~` and `$VARS`.** The `==` header always
|
|
237
|
+
prints the resolved compiler command; run with `-- -v` if you want to see
|
|
238
|
+
every include path the compiler actually received.
|
|
239
|
+
|
|
240
|
+
### Auto-discovering the database
|
|
241
|
+
|
|
242
|
+
When you run asmdiff from inside the project anyway, the path is
|
|
243
|
+
redundant. Two opt-ins skip it:
|
|
244
|
+
|
|
245
|
+
```toml
|
|
246
|
+
[esp32s3-idf]
|
|
247
|
+
cc = "xtensa-esp32s3-elf-gcc"
|
|
248
|
+
flags = ["-O2", "-mlongcalls"]
|
|
249
|
+
compile_commands = true # search instead of naming a path
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
asmdiff.py biquad.c --compile-commands # same, for any matrix
|
|
254
|
+
asmdiff.py biquad.c --compile-commands path/to/compile_commands.json
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Both walk up from the current directory, checking each level for
|
|
258
|
+
`compile_commands.json` and then `build/compile_commands.json` (where
|
|
259
|
+
CMake and idf.py leave it) — first hit wins. The walk stops at the
|
|
260
|
+
repository root (the first directory with a `.git`), so running from any
|
|
261
|
+
depth of component directory finds the project database, but an unrelated
|
|
262
|
+
one further up the filesystem is never picked up. Finding nothing is an
|
|
263
|
+
error: discovery is opt-in, so if you asked for it, silence would be a
|
|
264
|
+
lie. It is never on by default — a target without `compile_commands` and
|
|
265
|
+
no `--compile-commands` flag borrows nothing.
|
|
266
|
+
|
|
267
|
+
The precedence is what you'd hope: a target that names its own
|
|
268
|
+
`compile_commands` path always keeps it; `--compile-commands` (with or
|
|
269
|
+
without a path) fills in every other matrix entry, including `--cc`
|
|
270
|
+
strings and the built-in gcc/clang fallback.
|
|
271
|
+
|
|
272
|
+
One behavioral difference: with a *discovered* database, a source that has
|
|
273
|
+
no entry is compiled without borrowed flags after a one-line stderr note,
|
|
274
|
+
instead of being an error. That keeps standalone harness files working
|
|
275
|
+
when a `build/` directory happens to sit nearby; an explicitly named
|
|
276
|
+
database still treats an absent source as the error it is.
|
|
277
|
+
|
|
182
278
|
## Whole-file summary
|
|
183
279
|
|
|
184
280
|
With no `--pair`, no `--across`, and no `old_*`/`new_*` functions to
|
|
@@ -207,6 +303,12 @@ weight, did a call appear that shouldn't have? It sums parsed function
|
|
|
207
303
|
bodies only (no literal pools, data, or alignment), so it is not a size
|
|
208
304
|
measurement, and per-function rows are where the real information is.
|
|
209
305
|
|
|
306
|
+
Only labels the assembler types as functions are listed — global data
|
|
307
|
+
(string constants, state structs, lookup tables) gets column-0 labels too
|
|
308
|
+
but is not code. A `calls` list longer than 8 symbols is truncated to
|
|
309
|
+
`..., +N more`; real firmware dispatch functions call dozens of distinct
|
|
310
|
+
symbols and would otherwise make rows thousands of characters wide.
|
|
311
|
+
|
|
210
312
|
## Comparing the same function across two builds (`--across`)
|
|
211
313
|
|
|
212
314
|
`--pair` needs both variants to coexist in one compilation. Real changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# asmdiff
|
|
1
|
+
# [asmdiff](https://pypi.org/project/asmdiff/)
|
|
2
2
|
## per-function assembly comparison for paired C implementations
|
|
3
3
|
|
|
4
4
|
> asmdiff is a stdlib only command-line tool for comparing the generated assembly of individual C functions across implementations, compiler flags, compiler versions, and source revisions. It is intended for investigating compiler code generation rather than benchmarking runtime performance.
|
|
@@ -76,7 +76,7 @@ $ asmdiff.py asmdiff_example.c
|
|
|
76
76
|
```
|
|
77
77
|
asmdiff.py SOURCE.c [SOURCE2.c] [--pair OLD:NEW]... [--across FUNC]...
|
|
78
78
|
[--cc 'CC FLAGS']... [--target NAME]... [--config PATH]
|
|
79
|
-
[-- EXTRA_FLAGS...]
|
|
79
|
+
[--compile-commands [PATH]] [-v] [-- EXTRA_FLAGS...]
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
| Option | Meaning |
|
|
@@ -87,6 +87,8 @@ asmdiff.py SOURCE.c [SOURCE2.c] [--pair OLD:NEW]... [--across FUNC]...
|
|
|
87
87
|
| `--cc 'CC FLAGS'` | One compiler invocation, command and flags in a single quoted string. Repeatable to build a matrix. |
|
|
88
88
|
| `--target NAME` | A named target from the config file, resolved to a `--cc` entry. Repeatable; appended to the matrix after `--cc` entries. |
|
|
89
89
|
| `--config PATH` | Config file to use. Default search: `asmdiff.toml` next to `SOURCE.c`, then in the current directory, then `~/.config/`. First hit wins. |
|
|
90
|
+
| `--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). |
|
|
91
|
+
| `-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. |
|
|
90
92
|
| `-- FLAGS...` | Everything after a bare `--` is appended to *every* compiler invocation. |
|
|
91
93
|
|
|
92
94
|
With no `--cc` and no `--target`, the config file's top-level
|
|
@@ -121,19 +123,19 @@ each compiler+flags combination once:
|
|
|
121
123
|
|
|
122
124
|
```toml
|
|
123
125
|
# asmdiff.toml — next to your harnesses, in CWD, or in ~/.config/
|
|
124
|
-
default = "
|
|
126
|
+
default = "esp32s3" # target(s) used when no --cc/--target is given
|
|
125
127
|
|
|
126
|
-
[
|
|
128
|
+
[esp32s3] # production-like ESP32-S3 codegen
|
|
127
129
|
cc = "$HOME/.espressif/tools/xtensa-esp-elf/esp-*/xtensa-esp-elf/bin/xtensa-esp32s3-elf-gcc"
|
|
128
130
|
flags = [
|
|
129
|
-
"-O2", "-
|
|
131
|
+
"-O2", "-DMY_FEATURE", "-DNDEBUG",
|
|
130
132
|
"-Wno-strict-aliasing", "-mlongcalls",
|
|
131
|
-
"-I$HOME/project/components/
|
|
133
|
+
"-I$HOME/project/components/dsp/include",
|
|
132
134
|
]
|
|
133
135
|
|
|
134
|
-
[host
|
|
136
|
+
[host] # same defines on host gcc
|
|
135
137
|
cc = "gcc"
|
|
136
|
-
flags = ["-O2", "-
|
|
138
|
+
flags = ["-O2", "-DMY_FEATURE", "-I$HOME/project/components/dsp/include"]
|
|
137
139
|
```
|
|
138
140
|
|
|
139
141
|
`cc` values expand `~` and `$VARS` and may be glob patterns, so a config
|
|
@@ -150,8 +152,8 @@ shapes:
|
|
|
150
152
|
|
|
151
153
|
```bash
|
|
152
154
|
asmdiff.py h.c # config default target(s)
|
|
153
|
-
asmdiff.py h.c --target
|
|
154
|
-
asmdiff.py h.c --across f --target
|
|
155
|
+
asmdiff.py h.c --target esp32s3 --target host # two-target matrix
|
|
156
|
+
asmdiff.py h.c --across f --target esp32s3 --cc 'gcc -O2' # mix freely
|
|
155
157
|
```
|
|
156
158
|
|
|
157
159
|
A config placed next to your harness files travels with them: any invocation
|
|
@@ -161,6 +163,100 @@ included `asmdiff.example.toml` is a starting point. If a flag or include
|
|
|
161
163
|
path must vary per machine, that's what per-machine config files are for —
|
|
162
164
|
nothing lives in the tool.
|
|
163
165
|
|
|
166
|
+
### Borrowing includes from `compile_commands.json`
|
|
167
|
+
|
|
168
|
+
A real project source rarely compiles with a handful of `-I` flags. An
|
|
169
|
+
ESP-IDF component pulls in `freertos/FreeRTOS.h`, `esp_*` headers, and a
|
|
170
|
+
*generated* `sdkconfig.h`, reachable only through the dozens of `-I`/`-D`
|
|
171
|
+
flags the build system computes — none of which live in the source file.
|
|
172
|
+
That is why `asmdiff.py component.c` fails with `freertos/FreeRTOS.h: No
|
|
173
|
+
such file or directory`: not a wrong compiler (the xtensa toolchain ships
|
|
174
|
+
no FreeRTOS either), just missing include paths. Transcribing them by hand
|
|
175
|
+
is miserable.
|
|
176
|
+
|
|
177
|
+
So don't. Any build that uses CMake or Ninja can emit a
|
|
178
|
+
[`compile_commands.json`](https://clang.llvm.org/docs/JSONCompilationDatabase.html)
|
|
179
|
+
recording the exact flags for every source it builds (ESP-IDF writes one to
|
|
180
|
+
`build/compile_commands.json` on every `idf.py build`). Point a target at it:
|
|
181
|
+
|
|
182
|
+
```toml
|
|
183
|
+
[esp32s3-idf]
|
|
184
|
+
cc = "xtensa-esp32s3-elf-gcc"
|
|
185
|
+
flags = ["-O2", "-mlongcalls"]
|
|
186
|
+
compile_commands = "$HOME/myproject/build/compile_commands.json"
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Now `asmdiff.py $HOME/myproject/components/dsp/biquad.c --target esp32s3-idf`
|
|
191
|
+
finds that file's entry in the database and adds the include/define flags
|
|
192
|
+
it recorded — `-I`, `-isystem`, `-iquote`, `-idirafter`, `-include`,
|
|
193
|
+
`-imacros`, `-D`, `-U`, plus the header-environment driver flags `-specs`
|
|
194
|
+
and `--sysroot` — to this target's command. Everything else the
|
|
195
|
+
database records (its own compiler, `-O`/`-std`/`-W` flags, `-c`, `-o`, the
|
|
196
|
+
source) is ignored: **you** own the compiler and optimisation flags via
|
|
197
|
+
`cc`/`flags`; only the header environment is borrowed. That split is the
|
|
198
|
+
point — you can now sweep *your* `-O`/`-m` variations over a source that
|
|
199
|
+
only ever compiled one way under the build system.
|
|
200
|
+
|
|
201
|
+
Details that make it robust:
|
|
202
|
+
|
|
203
|
+
- **Paths are made absolute** against each entry's `directory`, so a
|
|
204
|
+
database full of build-relative `-I../include` flags still resolves when
|
|
205
|
+
asmdiff runs from anywhere.
|
|
206
|
+
- **`@file` response files are expanded.** Build systems park flags in
|
|
207
|
+
them — ESP-IDF v6 hides `-specs=picolibc.specs` in
|
|
208
|
+
`build/toolchain/cflags`, and without it every libc header breaks —
|
|
209
|
+
so the flag scan reads them (nested ones too) instead of skipping the
|
|
210
|
+
token. A bare specs name is left for the compiler's own search
|
|
211
|
+
directories; a specs path resolves like any other recorded path.
|
|
212
|
+
- **Per source file.** The lookup keys on the source you pass (matched by
|
|
213
|
+
resolved absolute path), so two files in an `--across`/summary run each
|
|
214
|
+
get their own recorded flags.
|
|
215
|
+
- **Absent source is an error**, not a silent empty flag set — otherwise
|
|
216
|
+
you'd just hit the missing-header failure this feature exists to prevent.
|
|
217
|
+
The message flags a same-basename entry recorded under a different path.
|
|
218
|
+
- **`compile_commands` expands `~` and `$VARS`.** The `==` header always
|
|
219
|
+
prints the resolved compiler command; run with `-- -v` if you want to see
|
|
220
|
+
every include path the compiler actually received.
|
|
221
|
+
|
|
222
|
+
### Auto-discovering the database
|
|
223
|
+
|
|
224
|
+
When you run asmdiff from inside the project anyway, the path is
|
|
225
|
+
redundant. Two opt-ins skip it:
|
|
226
|
+
|
|
227
|
+
```toml
|
|
228
|
+
[esp32s3-idf]
|
|
229
|
+
cc = "xtensa-esp32s3-elf-gcc"
|
|
230
|
+
flags = ["-O2", "-mlongcalls"]
|
|
231
|
+
compile_commands = true # search instead of naming a path
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
asmdiff.py biquad.c --compile-commands # same, for any matrix
|
|
236
|
+
asmdiff.py biquad.c --compile-commands path/to/compile_commands.json
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Both walk up from the current directory, checking each level for
|
|
240
|
+
`compile_commands.json` and then `build/compile_commands.json` (where
|
|
241
|
+
CMake and idf.py leave it) — first hit wins. The walk stops at the
|
|
242
|
+
repository root (the first directory with a `.git`), so running from any
|
|
243
|
+
depth of component directory finds the project database, but an unrelated
|
|
244
|
+
one further up the filesystem is never picked up. Finding nothing is an
|
|
245
|
+
error: discovery is opt-in, so if you asked for it, silence would be a
|
|
246
|
+
lie. It is never on by default — a target without `compile_commands` and
|
|
247
|
+
no `--compile-commands` flag borrows nothing.
|
|
248
|
+
|
|
249
|
+
The precedence is what you'd hope: a target that names its own
|
|
250
|
+
`compile_commands` path always keeps it; `--compile-commands` (with or
|
|
251
|
+
without a path) fills in every other matrix entry, including `--cc`
|
|
252
|
+
strings and the built-in gcc/clang fallback.
|
|
253
|
+
|
|
254
|
+
One behavioral difference: with a *discovered* database, a source that has
|
|
255
|
+
no entry is compiled without borrowed flags after a one-line stderr note,
|
|
256
|
+
instead of being an error. That keeps standalone harness files working
|
|
257
|
+
when a `build/` directory happens to sit nearby; an explicitly named
|
|
258
|
+
database still treats an absent source as the error it is.
|
|
259
|
+
|
|
164
260
|
## Whole-file summary
|
|
165
261
|
|
|
166
262
|
With no `--pair`, no `--across`, and no `old_*`/`new_*` functions to
|
|
@@ -189,6 +285,12 @@ weight, did a call appear that shouldn't have? It sums parsed function
|
|
|
189
285
|
bodies only (no literal pools, data, or alignment), so it is not a size
|
|
190
286
|
measurement, and per-function rows are where the real information is.
|
|
191
287
|
|
|
288
|
+
Only labels the assembler types as functions are listed — global data
|
|
289
|
+
(string constants, state structs, lookup tables) gets column-0 labels too
|
|
290
|
+
but is not code. A `calls` list longer than 8 symbols is truncated to
|
|
291
|
+
`..., +N more`; real firmware dispatch functions call dozens of distinct
|
|
292
|
+
symbols and would otherwise make rows thousands of characters wide.
|
|
293
|
+
|
|
192
294
|
## Comparing the same function across two builds (`--across`)
|
|
193
295
|
|
|
194
296
|
`--pair` needs both variants to coexist in one compilation. Real changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# asmdiff config — named compiler+flags targets.
|
|
2
|
+
#
|
|
3
|
+
# Copy to one of the locations asmdiff searches (first hit wins):
|
|
4
|
+
# 1. the path given with --config
|
|
5
|
+
# 2. asmdiff.toml next to the SOURCE.c being compiled
|
|
6
|
+
# 3. asmdiff.toml in the current directory
|
|
7
|
+
# 4. ~/.config/asmdiff.toml
|
|
8
|
+
#
|
|
9
|
+
# Each [table] is a target usable as `--target NAME`; the optional
|
|
10
|
+
# top-level `default` names the target(s) used when no --cc/--target
|
|
11
|
+
# is given (a list runs several: default = ["gcc", "clang"]).
|
|
12
|
+
# Compile at the flags your project ships with — that is the whole
|
|
13
|
+
# point of the tool.
|
|
14
|
+
|
|
15
|
+
default = "gcc"
|
|
16
|
+
|
|
17
|
+
[gcc]
|
|
18
|
+
cc = "gcc"
|
|
19
|
+
flags = [
|
|
20
|
+
"-O3", "-Wall", "-Wextra",
|
|
21
|
+
# Project-specific include paths and defines go here:
|
|
22
|
+
# "-I/path/to/project/src", "-DMY_FEATURE",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[clang]
|
|
26
|
+
cc = "clang"
|
|
27
|
+
flags = ["-O3", "-Wall", "-Wextra"]
|
|
28
|
+
|
|
29
|
+
# cc values may use ~, $VARS, and glob patterns. A glob that matches
|
|
30
|
+
# several installed toolchains resolves to the highest version-sorted
|
|
31
|
+
# one (announced on stderr); pin the exact esp-NN directory instead if
|
|
32
|
+
# reproducibility across sessions matters more than convenience.
|
|
33
|
+
[esp32s3]
|
|
34
|
+
cc = "$HOME/.espressif/tools/xtensa-esp-elf/esp-*/xtensa-esp-elf/bin/xtensa-esp32s3-elf-gcc"
|
|
35
|
+
flags = ["-O2", "-mlongcalls", "-DNDEBUG"]
|
|
36
|
+
|
|
37
|
+
# A project source that pulls in framework headers (ESP-IDF's freertos/*,
|
|
38
|
+
# esp_*, a generated sdkconfig.h, ...) needs the dozens of -I/-D flags its
|
|
39
|
+
# build system passes. Point `compile_commands` at the database that build
|
|
40
|
+
# emits (idf.py leaves build/compile_commands.json), and asmdiff copies the
|
|
41
|
+
# include/define flags recorded there for the file being compiled — so you
|
|
42
|
+
# never transcribe include paths by hand. The compiler and -O/-m flags
|
|
43
|
+
# still come from cc/flags; only the header environment is borrowed.
|
|
44
|
+
# A path expands ~ and $VARS; `compile_commands = true` instead searches
|
|
45
|
+
# at run time: each directory from the CWD up to the repository root,
|
|
46
|
+
# and its build/ subdirectory (a source missing from a database found
|
|
47
|
+
# that way is compiled without borrowed flags, with a note).
|
|
48
|
+
[esp32s3-idf]
|
|
49
|
+
cc = "$HOME/.espressif/tools/xtensa-esp-elf/esp-*/xtensa-esp-elf/bin/xtensa-esp32s3-elf-gcc"
|
|
50
|
+
flags = ["-O2", "-mlongcalls"]
|
|
51
|
+
compile_commands = true
|