The gcc-2.7.2 codegen map — pass → residual → C-lever catalogue
What this is. A catalogue of the pinned compiler's behaviour, organised by optimisation pass: for each residual
pattern a near-miss diff can show, the mechanism in the compiler's own source that produces it, and the C-level lever
that moves it — each lever byte-proven on a named function with the diff before and after. It was written in
Phase 23 by four frontier-class agents reading the gcc 2.7.2 passes directly, after six phases in which the same
residuals had been called "unsteerable"; it is the artifact that lets a mid-tier agent, or a local model, apply
compiler-internal levers without reading 80,000 lines of source. The index and triage table live in the cookbook
(docs/matching-cookbook.md §31); the method is chapter 07 of the how-to
(docs/how-to-ai-decomp/07-compiler-source.md).
The files
| File | Pass group (source files) | Catalogue | Size |
|---|---|---|---|
sched.md |
scheduling: sched.c (sched1 pre-reload / sched2 post-reload) + reorg.c (delayed-branch) + the MIPS backend hooks (config/mips/mips.c, mips.h) |
S1–S13 (LUID = source order, the birthing boost in both directions, the load-gap filler, the bb0 head-skip, the reused-temp fence …), D1–D5 (delay-slot content, eligibility, eager steal, thread copies, merged exits); the S11/RC-6 verdict downgrade | 266 lines |
regalloc.md |
register allocation + reload: local-alloc.c, global.c, reload.c, reload1.c (with flow.c, function.c, caller-save.c) |
§A the eight knobs everything reduces to; §B RC-1…RC-15 (spill-slot shape = declaration order, wrong value spilled, callee-saved order, coalescing, pin side-effects, pressure lock, remat-vs-spill, reload artifacts, the preference cascade, the 3-qty sort bug, the $0-add opaque copy …); §C the tells; §D the triage order; §E the experiment record; §F–§H the later extensions incl. the reg_renumber-swap oracle |
286 lines |
loop.md |
the loop optimiser: loop.c (biv/giv detection, combine_givs, strength reduction, hoisting/preheader, biv elimination, reversal) |
L1–L8 (IV count/anchor, index-biv elimination, loop reversal, hoisting and the inline limit, final-value compensation, giv-add order, invalid loops, increment position) | 369 lines |
cse_expr.md |
common-subexpression elimination + expression emission: cse.c, expr.c, function.c (with calls.c, stmt.c) |
cross-call ADDRESS caching (hoist-vs-remat — the class that blocked the 400-instruction giants), cross-call VALUE CSE (the phantom callee-saved register), the /s (MEM_IN_STRUCT_P) aliasing model in all four arms and its store-side flush lever, the temp-slot and stack-layout rules |
389 lines |
t7g-giant-harvest.md |
a Phase-24 session harvest (levers found while cracking the giants; a permuter tool fix) | a log, not a primary reference: where it disagrees with the three audited files above, those win | 351 lines |
How to use it
- Read the tell off the near-miss diff (
match_one/rtu_matchoutput, or the RTL dumps): a temporary in a spill slot = a reload artifact; "every small edit moves 20+ instructions" = a pressure lock → the permuter; a load stuck below ax = 0store while offset loads float = the/sflag; a phantom seventh callee-saved register held across the function = cross-call CSE; a prologue initialisation in the wrong order = the birthing boost. Each catalogue carries its full tells table. - Look up the class's lever in the catalogue, apply it to the C, gate. The condensed triage table:
| Pass group | STEERABLE (a byte-proven lever exists) | INTRINSIC → the permuter |
|---|---|---|
| scheduling | S1 source order; S2 birthing boost (create or kill a sink); S4/5/6 load-gap filler, memory-unit spacing, hazard front-move; S7 prologue saves; D1–D3 delay-slot content / eligibility / eager steal | S3 load/mul chain-priority sink; S11 the LUID⊗allocation knife-edge — downgraded: try the S12 reused-temp fence and S13 head-skip escape first |
| regalloc / reload | RC-1 spill-slot shape (declaration order); RC-2 wrong value spilled (allocno density); RC-3 callee-saved order; RC-4 coalescing un-tie; RC-5 pin side-effects; RC-7 remat-vs-spill; RC-8 reload artifacts; RC-9 cross-block copy | RC-6 pressure lock (its true form); the local-vs-global allocation tie |
| loop | L1 IV count/anchor; L2 index-biv elimination; L3 reversal; L4 hoisting/preheader; L7 invalid loop; L8 increment position | L5 final-value compensation; L6 giv-add order |
| cse / expr | cross-call address caching (nested-block pointer + a post-call volatile re-set); cross-call value CSE; the /s model + store-side flush; stack layout |
the 1,000-instruction table flush (giants); QImode never gets /s |
- Before any register lever, read the allocation order (
tools/alloc_table.pyover the pass dumps) — everyregister … __asm__pin the last functions of the project carried came off byte-identical once the source shape was right; pins are symptoms (R73). Before any "wall" verdict, name the pass and quote the dump line (R65), and write the producer census against a NAMED list (R69) — two written proofs of unmatchability were each one producer short.
The byte-proof method
Every lever in these files was validated the same way: compile the exemplar with the pinned triple
(gcc-2.7.2-psx cc1 -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker → maspsx --aspsx-version=2.56 --expand-div → GNU as) and compare relocation-masked bytes to the original with tools/match_one.py
(or, in the real translation unit, rtu_match.py); the experiment C files and RTL dumps are kept under .run/gccmap/
(scratch). The instruments that grew around the map: tools/cc1_dumps_tu.sh (per-pass RTL dumps from the real
translation unit — the standalone compile is not faithful on the executable's units; -dL for the loop pass),
tools/alloc_table.py (the global allocator's priorities and hard registers per pseudo), tools/ghost_census.py
(frame and spill-slot census from the dumps), and the one-second reproducer battery: five-line functions that
isolate one mechanism, compiled with dumps, before any probe of the 500-instruction function.
Provenance — read this before following a line number
Two source trees, gitignored under tools/reference/ (SETUP §5.6):
gcc-2.7.2/— the vanilla GNU release, the real source of the pinnedcc1: a subset of 18.cfiles plusconfig/mips/(sched.c,reorg.c,local-alloc.c,global.c,reload.c,reload1.c,loop.c,cse.c,expr.c,function.c,calls.c,stmt.c,combine.c,toplev.c,flow.c,expmed.c,caller-save.c,integrate.c,optabs.c,varasm.c,recog.c, the headers), staged fromftp.gnu.org/old-gnu/gcc/gcc-2.7.2.tar.gz(sha2567cd8bce5c3aeec59a72ecc2d3d5123864a817b14cdbd0680b1a969c3bccc5da5; GPL-2.0). Re-fetch the tarball and copy the files you need; the whole tree is not required.gcc-papermario/—pmret/gcc-papermarioata6afc2afbcaf6682930141d526afdc95801fc2fd, source files at the repository root. It is gcc 2.8.1, not 2.7.2 — a fact established in Phase 23 after the map had been written against it. The difference is behavioural (2.8.1 disables biv-elimination paths that are live in the real 2.7.2cc1) and the line-number drift is large and non-uniform:combine_regs+103,allocate_reload_reg+377,choose_reload_regs+611 — enough to land inside a different function.
The Phase-29 audit (2026-07-28). All 184 citations and claims of the three primary files were re-derived against
the vanilla 2.7.2 source by five parallel agents; every refuted claim then went to an independent agent whose job was
to refute the refutation, defaulting to upholding the map. Result: 119 confirmed · 40 line-drift (mechanism intact,
number wrong) · 21 raised as refuted, of which 14 were overturned and 7 stand · 4 unverifiable. The model is
overwhelmingly sound — do not delete levers — but a line number may belong to either tree. Each file carries its own
audit banner with the specifics (cse_expr.md had the most errors; one sched.md claim about the birthing boost and
register-asm pins was byte-refuted and corrected).
Every citation is tagged with its tree. tools/gccmap_cites.py derives, for every file.c:NNN cite in these
files, which tree the number belongs to, and writes the tag after the cite:
| Tag | Meaning | Count |
|---|---|---|
[2.7.2] |
the cited construct sits at that line in the vanilla 2.7.2 subset — verified against the tree | 79 |
[2.8.1 pm] |
the cited construct sits at that line in gcc-papermario; the 2.7.2 line differs (re-derive with grep -n before citing) |
55 |
[repo] |
a project file (src/shared/engine_core.h), not gcc |
1 |
The derivation is from the trees, never typed: quoted source snippets and C identifiers in the citing sentence are
searched near the cited line in each tree (function extents for cites inside long bodies; the nearer tree wins, an
equal distance means the line is equally valid in 2.7.2); the author's own cues (papermario …, →2.7.2 :NNN)
break ties and fail loudly when they contradict the evidence; the 20 cites the evidence cannot settle (18 with no usable evidence near them, 2 where the two trees tie) carry a human
verdict with its reason in cite_overrides.tsv. Six known-true cases recorded in the map's own
text are the tool's controls (--controls, 6/6); --verify re-derives every tag; --check (textual, in
make tools-health and CI) refuses an untagged cite or a stale override. 135 cites across the five files.
Related
- Cookbook §31 (the index and triage), §17 (the register-pin toolkit), §30 (the
/sflag), §34 (the 3-qty sort bug), §501 (the twelve dump-read laws and the two overturned proofs). docs/accelerators.mdA4 — "read the compiler's source once, into a codegen map" (found Phase 23, available from Phase 6).docs/how-to-ai-decomp/07-compiler-source.md— the method for the next project.