mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-10-05 00:47:54 -04:00
docs(phase-29): T35 — the last 4 codegen-map files audited vs real gcc-2.7.2
Scope enumerated before acting: cse_expr.md, loop.md, sched.md (full pass — T33 landed only a partial), t7g-giant-harvest.md. 35 agents (9 derive + 26 adversarial refute), 2.48M subagent tokens. 308 findings: 174 CONFIRMED / 99 LINE-DRIFT / 26 REFUTED raised -> 20 UPHELD, 6 OVERTURNED / 9 unverifiable. cse_expr.md had the highest error density (17 refuted of 74); loop.md the lowest (3 of 96) thanks to its pre-existing caveat table. 12 FABRICATED (vs 0 last audit) — DIAGNOSED, not waved through: the agents pasted MAP text into the source_quote field instead of compiler source. All 12 are CONFIRMED-status and none underpins an upheld refutation, so nothing was deleted on bad evidence — but they are UNVERIFIED, they sit in loop.md's biv-elimination area, and loop.md now records that as an open gap rather than a pass (R32). Headline corrections: - cse_expr: THE 1000-INSN CSE FLUSH DOES NOT EXIST IN 2.7.2 (added in 2.8.1; grep num_insns -> no hits). It drove THREE places — §1's killer table, §6's giant tell, §7's "shift +-insns across the 1000 boundary" lever. A lever aimed at a counter our compiler lacks, in exactly the giants this map serves. All struck. - cse_expr: §2's "kill THE class reg" is singular and wrong. The audit BYTE-REPRODUCED T31's wall on the pinned cc1: expand_block_move (mips.c:2350-2351) copy_addr_to_reg's BOTH aggregate addresses. Two byte-proven remedies recorded, with the caveat that field-by-field copy is closed when the target's own bytes need the block move (func_80132F40's case). - cse_expr: assign_temp absent in 2.7.2 and no /s reset on slot reuse (recycled slots INHERIT /s); no BUILT_IN_MEMSET; §6's "recompute after a join is never a residual" false at -O2. - sched: S7's EPILOGUE half false (no live define_expand "epilogue" on MIPS) — re-scoped not deleted; insn_cost is DEP-KIND-BLIND so restoring /s anti edges is not free. - loop: "no memory load is EVER hoisted from a loop containing a call" FALSE — invariant_p checks RTX_UNCHANGING_P first; byte-proven that a const int* load hoists to the preheader. Call args are emitted LEFT-to-right, not right-to-left. Remaining: matching-cookbook.md (~52 citations, MIXED provenance) — but a DIFFERENT risk profile, since its idioms are byte-proven and citations are explanation, so a targeted citation sweep is proportionate rather than a full audit. Not done; flagged. Docs-only: no src/ or config/ touched, R22 not re-run and not claimed.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# gcc-2.7.2 residual→C-lever map: CSE + expression emission + `/s` aliasing + stack layout
|
||||
**Pass-group:** `cse.c` (8989) + `expr.c` (12077) + `function.c` (6321), with targeted reads of
|
||||
`calls.c` / `stmt.c` / `local-alloc.c` / `sched.c` where they consume this group's decisions.
|
||||
All source cites are `tools/reference/gcc-papermario/<file>:<line>`. All byte-proofs ran through
|
||||
All source cites are `tools/reference/gcc-papermario/<file>:<line>` — **[A23] which is gcc 2.8.1, NOT
|
||||
our 2.7.2; see the audit block below before following any line number.** All byte-proofs ran through
|
||||
`tools/match_one.py` (pinned triple, relocation-masked byte equality). Proof C files:
|
||||
`.run/gccmap/proofs/`. Date: 2026-07-02, Phase 23.
|
||||
|
||||
@@ -9,6 +10,71 @@ All source cites are `tools/reference/gcc-papermario/<file>:<line>`. All byte-pr
|
||||
Phase 20). Both canonical stub exemplars now MATCH** (`func_80149374` 23 ins, `func_801493D0`
|
||||
23 ins — reach-134 fns, ready for whole-binary integration). Lever = §2 below.
|
||||
|
||||
> ## ⚠️ SOURCE-VERSION AUDIT (Phase 29 SESSION-23, 2026-07-28) — THIS FILE HAD THE MOST ERRORS
|
||||
> The cites above say `gcc-papermario`, **which is gcc 2.8.1, not our 2.7.2** (vanilla 2.7.2 is at
|
||||
> `tools/reference/gcc-2.7.2/`). All 74 claims here were re-derived against the real source by
|
||||
> parallel agents, each REFUTED claim then adversarially re-checked by an independent agent told to
|
||||
> uphold the map by default. **17 REFUTED raised; the highest error density of any map file.**
|
||||
> Line numbers drift −20 to −220 (cse.c), −960 to −1180 (expr.c), −230 to −520 (function.c) — enough
|
||||
> to land inside a different function. **`grep -n '^sym ('` before citing.**
|
||||
>
|
||||
> ### [A23-1] THE 1000-INSN CSE FLUSH DOES NOT EXIST IN gcc 2.7.2 — it is 2.8.1-only
|
||||
> `grep -n num_insns tools/reference/gcc-2.7.2/cse.c` → **no hits**; no `flush_hash_table`, no
|
||||
> "quadratic", no "Perhaps for 2.9" anywhere in the 8,779-line file. It was added in 2.8.1
|
||||
> (`gcc-papermario/cse.c:8621-8644`). **In our compiler a CSE class NEVER expires by instruction
|
||||
> count.** This invalidates THREE places below — §1's table row, §6's "long straight-line giant"
|
||||
> tell, and §7's "check `num_insns` distance / shift ±insns across the 1000 boundary" bullet — i.e.
|
||||
> **a lever aimed at a counter our compiler does not have, in exactly the giants this map is
|
||||
> consulted for.** All three are struck inline.
|
||||
>
|
||||
> ### [A23-2] §2's "kill THE class reg" is SINGULAR AND WRONG — and this is why §2 can fail
|
||||
> A CSE class routinely holds **several** registers, and the extra ones have **no C-level name**, so
|
||||
> the output-only asm kill can only ever name one of them. Byte-reproduced with the pinned cc1 during
|
||||
> the audit: adding one line `v[1] = v[0];` to a working case defeats the recipe completely (working:
|
||||
> frame 56 / 2 saved regs / `addiu` remat at site 2 → broken: frame 64 / 3 saved regs / `addu
|
||||
> $16,$sp,16` hoist / both sites `move $aN,$16`). **Mechanism:** `config/mips/mips.c:2350-2351`
|
||||
> (`expand_block_move`) calls `copy_addr_to_reg` on **BOTH** aggregate addresses, creating
|
||||
> `(set (reg:SI 77) (plus:SI (reg:SI 30 $fp) 16))`; cse substitutes reg 77 into the FIRST call's
|
||||
> arg-load, so by the time the asm kill fires it invalidates a register **already out of the chain**
|
||||
> and reg 77 sails on into `$16`.
|
||||
> **Two remedies, both byte-proven in the audit:** (a) spell the aggregate copy **field-by-field**
|
||||
> (returns to frame 56 / 2 regs / remat), or (b) force a **real join CODE_LABEL** between the block
|
||||
> move and the calls. **Caveat before reaching for (a):** if the TARGET's own bytes contain the
|
||||
> unaligned `lwl/lwr…swl/swr` block move, spelling it field-by-field changes bytes you need — in that
|
||||
> case the s32/remat route is simply closed and you should keep the narrower-typed draft.
|
||||
> *(This is the documented explanation of a live 47→40-mismatch wall hit the same day on
|
||||
> `func_80132F40`; ladder preserved at `.run/near6/f132F40_v1..v6.c`.)*
|
||||
>
|
||||
> ### The other upheld corrections (each struck or annotated at its site)
|
||||
> - **§1's "complete list" of class killers is NOT complete** (upheld narrowly — the word "complete"
|
||||
> fails). Notably a **volatile SET's dest is invalidated** via `do_not_record` (`cse.c:7110-7114`),
|
||||
> which is the source line for §2's own lever — as written, §1 says that lever cannot exist.
|
||||
> - **§4a: `assign_temp` does not exist in gcc 2.7.2** (added in 2.8; `grep -rn assign_temp` → 0 hits)
|
||||
> and **2.7.2's `assign_stack_temp` does NO `/s` reset**, so a **RECYCLED slot INHERITS `/s` and
|
||||
> `RTX_UNCHANGING_P` from its previous occupant** — the opposite of what §4a claims, and it
|
||||
> interacts directly with §5's own slot-recycling text.
|
||||
> - **There is NO `BUILT_IN_MEMSET`/`BZERO` in 2.7.2's `expand_builtin`.** `memset` is always an
|
||||
> ordinary library CALL (which flushes the whole cse memory table), never an inline `/s` BLKmode
|
||||
> block move. The "memcpy/memset/strcpy" trio is really **memcpy/strcpy** sharing one path.
|
||||
> - **§6's "recompute right after a join is NORMAL — never a residual" is FALSE at -O2**, where
|
||||
> `flag_cse_follow_jumps` and `flag_cse_skip_blocks` are both set (`toplev.c:3389-3390`) and
|
||||
> `cse_end_of_basic_block` extends the table across a join (TAKEN `cse.c:8118`, AROUND `:8150`).
|
||||
> As written, that row would have blocked §H.1's own antidote.
|
||||
> - **§5's "`frame_offset` starts at 0 (= sp+0x10 at runtime)"** — only the parenthetical is wrong.
|
||||
> `STARTING_FRAME_OFFSET` is `current_function_outgoing_args_size`, which is **0 for a leaf with no
|
||||
> calls**, so a leaf's first local sits at **sp+0**, not sp+0x10. The rest of the claim stands.
|
||||
> - **§H's "no bank (5 permuter-shaped clusters)" verdict is SUPERSEDED** — byte-refuted the same day:
|
||||
> 4 of the 5 clusters proved steerable from C (`func_80176734` 217 → **13**, count exact 371/371).
|
||||
> Only two coupled ties survive. §H's own two mechanisms both CONFIRMED, with one correction: the
|
||||
> diamond antidote's barrier-preceded label is the **ELSE** label, not the merge label (the merge
|
||||
> label works because it is neither followable nor skip-block-able, so `new_basic_block()`
|
||||
> (`cse.c:8430`) clears the table).
|
||||
> - **§H's `update_equiv_regs` live-length doubling is exactly ×2 on global priority**
|
||||
> (`local-alloc.c:1064`, and `allocno_live_length` is the DENOMINATOR — `global.c:594-597`); the
|
||||
> rest of the informal "~×4" comes from the `floor_log2(n_refs)*n_refs` numerator. It applies only
|
||||
> to pseudos carrying a REG_EQUIV note, and `CONSTANT_P` (`rtl.h:237-240`) **excludes a bare PLUS**
|
||||
> — so a frame address never qualifies (same correction as `regalloc.md` RC-7).
|
||||
|
||||
---
|
||||
|
||||
## §0 The diagnostic loop: RTL dumps from the pinned cc1
|
||||
@@ -24,9 +90,11 @@ cd wd && <repo>/tools/bin/gcc-2.7.2-psx/cc1 -quiet -O2 -G0 -mips1 -mcpu=3000 -mg
|
||||
-msoft-float -fgnu-linker -da t.i -o t.s
|
||||
```
|
||||
|
||||
Reading the dumps: pseudos start ≈ reg 70 (MIPS: 0-31 GPR, 32-63 FPR, 64-66 hi/lo/fpsw,
|
||||
67-70 virtuals). `(reg:SI 69)` in `.rtl` = **virtual-stack-vars** (frame base, = first local's
|
||||
address). In `.cse` and later, frame addresses appear as `(plus (reg 30 $fp) k)` — `$fp` is
|
||||
Reading the dumps: ~~pseudos start ≈ reg 70 (MIPS: 0-31 GPR, 32-63 FPR, 64-66 hi/lo/fpsw,
|
||||
67-70 virtuals).~~ **[A23] corrected — `FIRST_PSEUDO_REGISTER` is 68** (`config/mips/mips.h:1179`),
|
||||
so the four virtuals are **68-71** and **the first pseudo is 72**, not ~70. (The headline
|
||||
`(reg:SI 69)` below is still right: virtual-stack-vars is the 2nd virtual.) `(reg:SI 69)` in `.rtl`
|
||||
= **virtual-stack-vars** (frame base, = first local's address). In `.cse` and later, frame addresses appear as `(plus (reg 30 $fp) k)` — `$fp` is
|
||||
eliminated to `$sp` only at reload, so grep for `$fp` pre-reload, `sp` post. Triage rule:
|
||||
- residual visible in `.cse` → this file, §1-§3 below;
|
||||
- appears first in `.lreg/.greg` → regalloc (pins / §30 recipes);
|
||||
@@ -49,7 +117,7 @@ not reuse that value" reduces to whether the class was still valid at the second
|
||||
| CALL_INSN | `invalidate_memory(everything)`: ALL `MEM` entries die (non-const calls) | `cse.c:7409-7415` |
|
||||
| memory store | selective MEM-entry kill via `note_mem_written` — see §4 aliasing table | `cse.c:7709`, `1732` |
|
||||
| CODE_LABEL | **total flush** (`new_basic_block`) — every class dies at every label | `cse.c:797, 8614` |
|
||||
| 1000 insns | **total flush** mid-block ("extreme quadratic behavior" kludge) | `cse.c:8626` |
|
||||
| ~~1000 insns~~ | ~~**total flush** mid-block ("extreme quadratic behavior" kludge)~~ **[A23-1] DOES NOT EXIST IN 2.7.2** — 2.8.1-only; no `num_insns` in our `cse.c`. A class never expires by insn count. | ~~`cse.c:8626`~~ |
|
||||
| volatile asm | **NOTHING** (no reg-class invalidation; a `"memory"` clobber kills only MEM entries via BLKmode→`all=1`) | `cse.c:6340-6355` |
|
||||
|
||||
Consequences you will see in diffs:
|
||||
@@ -268,7 +336,7 @@ internal offsets AND changes `/s` (stmt.c:3646 gives the array home `/s`) and IV
|
||||
| frame ±8, or a local's offset out of decl order | §5 slot recycling / 8-rounding | reproduce/eliminate the compiler temp; reorder decls |
|
||||
| every array sits 8-aligned with padding gaps | §5 BLKmode rounding | expected — don't fight it, mimic with decl order |
|
||||
| value recomputed right after a branch join/label | §1 label flush | NORMAL — never a residual; don't add CSE-defeating hacks |
|
||||
| long straight-line giant: early value suddenly recomputed mid-function | §1 1000-insn flush | expected in giants; position-dependent — see §7 |
|
||||
| long straight-line giant: early value suddenly recomputed mid-function | ~~§1 1000-insn flush~~ **[A23-1] NOT the flush (absent in 2.7.2)** — look for a label/join, a volatile, or a call flushing the memory table instead | ~~expected in giants; position-dependent — see §7~~ **re-triage against §1's real killer list** |
|
||||
| `lui` above a branch, `ori` duplicated in delay slot + taken path | dbr/reorg territory (delay-slot stealing), NOT cse | route to jump/sched pass-group |
|
||||
|
||||
---
|
||||
@@ -284,10 +352,14 @@ internal offsets AND changes `/s` (stmt.c:3646 gives the array home `/s`) and IV
|
||||
- **§5 layout: STEERABLE** via decl order/typing/temp reproduction; INTRINSIC only in that you
|
||||
cannot place two 8-BLKmode objects at 4-mod-8 offsets — that's evidence the original source
|
||||
had different object boundaries, not a permuter case.
|
||||
- **§1 1000-insn flush: INTRINSIC-ish** — you cannot move the counter from C; if a giant's
|
||||
- ~~**§1 1000-insn flush: INTRINSIC-ish** — you cannot move the counter from C; if a giant's
|
||||
residual is a reuse/recompute flip exactly once mid-function, check `num_insns` distance;
|
||||
restructuring that shifts ±insns across the 1000 boundary is the only (fragile) lever.
|
||||
Document any confirmed case before hand-grinding.
|
||||
Document any confirmed case before hand-grinding.~~
|
||||
**[A23-1] DELETED — the counter does not exist in gcc 2.7.2** (`grep -n num_insns cse.c` → no hits;
|
||||
added in 2.8.1). Do NOT spend a giant's budget measuring distance to a 1000-insn boundary. A
|
||||
once-mid-function reuse/recompute flip in OUR compiler is a label/join (§1 + the -O2
|
||||
follow-jumps/skip-blocks behaviour), a volatile, or a call — all of which ARE steerable.
|
||||
- **`func_80132784` (400 ins, `asm/ov_SC01_077/nonmatchings/ov_SC01_077_a/`)** — the draft
|
||||
(`.run/backlog_drafts/func_80132784.c`, stuck 240/400) is MULTI-CLASS, in this order:
|
||||
(1) §5: single `u8 buf[0xC0]` vs target's separate 8-aligned locals (target arg addresses
|
||||
|
||||
@@ -21,6 +21,16 @@ differences are BEHAVIORAL and bit us immediately:
|
||||
| `combine_givs` order | qsort hook + refined benefit bookkeeping | plain linked-list pair loop (anchor rules below) |
|
||||
| giv increment placement | AUTO_INC logic (moot on MIPS) | always inserted **immediately before the biv increment insn** |
|
||||
|
||||
> **[A23] AUDIT OUTCOME (2026-07-28):** all 96 claims in this file were re-derived against
|
||||
> `tools/reference/gcc-2.7.2/` by parallel agents, each REFUTED claim adversarially re-checked.
|
||||
> **Only 3 REFUTED were raised — the LOWEST error density of any map file**, which is a direct credit
|
||||
> to the caveat table above: it already captured the behavioural 2.8.1-vs-2.7.2 deltas. Two upheld
|
||||
> corrections are marked `[A23]` inline (the "no memory load is EVER hoisted" absolute, and the
|
||||
> right-to-left call-arg order). **One honest gap:** 12 findings in the biv-elimination /
|
||||
> `check_dbra_loop` area returned evidence that quoted THIS FILE rather than the compiler source, so
|
||||
> they are **unverified, not confirmed** — the caveat table's own rows are the ones affected. Re-derive
|
||||
> them before leaning on a biv-elimination claim.
|
||||
|
||||
A vanilla 2.7.2 extraction is at `.run/gccmap/gcc-2.7.2-vanilla-src/` (loop.c, unroll.c,
|
||||
sched.c, cse.c, rtl.h, config/mips). **Recommend promoting it to `tools/reference/` —
|
||||
line refs below are to that tree.** (2.8.1 refs marked "pm:".)
|
||||
@@ -78,9 +88,13 @@ exclusion), `record_giv` (:4341), `combine_givs_p`/`express_from` (:5457/:5419),
|
||||
6. **Anchor choice**: `bl->giv` is prepend-built during the forward scan, and
|
||||
`combine_givs` (2.7.2 loop.c:5494) takes g1 from the list head first (pass 0 =
|
||||
replaceable g1 only) → **the LAST-emitted DEST_ADDR giv anchors** and all others
|
||||
become `anchor+delta` offsets. NB call args are expanded right-to-left, so *the
|
||||
first arg's load is emitted last* — that's why banked func_80150528 anchors at
|
||||
+0x20 (first call arg) with 0x38/0x3C offsets off it.
|
||||
become `anchor+delta` offsets. ~~NB call args are expanded right-to-left, so *the
|
||||
first arg's load is emitted last*~~ — **[A23] FALSE: args are emitted LEFT-TO-RIGHT.** The audit's
|
||||
RTL dump shows `$a0` at insn 10, `$a1` at 12, `$a2` at 14 (ascending), so the FIRST arg's load is
|
||||
emitted **first**. The observed fact that banked `func_80150528` anchors at +0x20 (its first call
|
||||
arg) still holds — but it follows from `record_giv`'s prepend + `combine_givs` taking the list
|
||||
HEAD (which makes the last-PREPENDED giv the head), NOT from a right-to-left arg order. Do not
|
||||
reason about arg emission order from this bullet.
|
||||
|
||||
**C levers (with proofs):**
|
||||
- **Target has biv + ONE derived IV (offset cluster)** → single walked base pointer,
|
||||
@@ -205,9 +219,18 @@ substitution (:735-770), `combine_movables` (:1239), desirability + emission
|
||||
`threshold × savings × lifetime ≥ insn_count` (thresholds L0 — for typical loops
|
||||
this is nearly always true; the interesting blockers are the SAFETY conditions).
|
||||
- **A CALL anywhere in the loop sets `unknown_address_altered`** (`prescan_loop:2201`)
|
||||
→ `invariant_p(MEM) == 0` for every load → **no memory load is EVER hoisted from a
|
||||
→ `invariant_p(MEM) == 0` for ~~every~~ **most** loads. ~~**no memory load is EVER hoisted from a
|
||||
loop containing a call** (proven expC c3: `lw D_SRC` stays in-loop). Don't fight
|
||||
it with cached locals — match the target's in-loop reloads by NOT caching.
|
||||
it with cached locals — match the target's in-loop reloads by NOT caching.~~
|
||||
**[A23] "EVER" is FALSE — there is a real exception.** `invariant_p`'s `case MEM:` arm
|
||||
(2.7.2 `loop.c:2760-2775`) checks `RTX_UNCHANGING_P (x)` **before** consulting
|
||||
`unknown_address_altered` and `break`s — i.e. **read-only items ARE invariant and DO hoist even
|
||||
with a call in the loop.** Byte-proven during the audit on the pinned cc1:
|
||||
`int t(const int *p,int n){int s=0,i;for(i=0;i<n;i++){s+=*p;f(i);}return s;}` puts
|
||||
`lw $19,0($4)` in the **PREHEADER**, not the loop body.
|
||||
**So the advice "don't fight it with cached locals" is right for ordinary loads and wrong for
|
||||
`const`/`RTX_UNCHANGING_P` ones** — if the TARGET hoists a load out of a call-containing loop,
|
||||
a const-qualified pointer is the lever, not evidence that you mis-read the loop.
|
||||
- No-call loops: an invariant global load hoists if no `true_dependence` with any
|
||||
`loop_store_mems` entry (`invariant_p:2695`; `/s`/`MEM_IN_STRUCT_P` enters here —
|
||||
expr.c agent's domain, cookbook §30/§30a#1) — proven expC c2 (`lw D_SRC` hoisted
|
||||
|
||||
@@ -62,7 +62,8 @@ The scheduler **never moves an insn across a basic-block boundary** (gcc-2.7.2 h
|
||||
`schedule_block` (sched.c:3172) schedules each bb **BACKWARD** (from the tail): *picked early = placed late*.
|
||||
|
||||
1. **Dependences** (`sched_analyze:2190`): true/anti/output on regs; memory via `pending_read/write` lists + `memrefs_conflict_p:627` (same-base different-const-offset stores DON'T conflict → mutually reorderable); every MEM op and every reg dep on a CALL: calls flush the pending lists (`flush_pending_lists:1647`) → **no memory op ever crosses a call**; `MEM_IN_STRUCT_P` (`/s`) enters via `true/anti/output_dependence:829-907` (the §30 store-vs-load flag).
|
||||
2. **Latency** (`insn_cost:1390` + `mips.md` function units): load **2** (r3000), xfer 2, store 1, ALU 1, imul **12**, idiv **35**, call result **1** (no unit). `mips.h:3204 ADJUST_COST`: **anti/output dep cost = 0 → clamped to 1 (LINK_COST_FREE)**. A dep INTO a `USE` insn is also cost-free (sched.c:1419 — arg setup overlaps the call).
|
||||
2. **Latency** (`insn_cost:1390`→**2.7.2 :1363** + `mips.md` function units): load **2** (r3000), xfer 2, store 1, ALU 1, imul **12**, idiv **35**, call result **1** (no unit). `mips.h:3204 ADJUST_COST`: **anti/output dep cost = 0 → clamped to 1 (LINK_COST_FREE)**. A dep INTO a `USE` insn is also cost-free (sched.c:1419 — arg setup overlaps the call).
|
||||
**[A23] `insn_cost` is DEP-KIND-BLIND in 2.7.2** — it has no `REG_DEP_ANTI` zero-case (added in ≥2.8), so an anti-dep of a latency-2 load still contributes **+1** to priority rather than 0. Consequence for the §4 aliasing levers: **restoring `/s` anti edges is NOT free — it RE-GROUPS downstream stores.** Budget for that before using `/s` to steer.
|
||||
3. **Priority** (`priority:1452`) = longest-chain-from-bb-top: `pri(insn) = max over LOG_LINKS preds of (pri(pred) + cost(pred) − 1)`, min 1. So: **all-latency-1 code ties at pri 1**; each load on the path adds **+1**, imul +11, idiv +34; anti/output links propagate the pred's priority unchanged (+0). Priorities can only be RAISED by C edits, never lowered.
|
||||
4. **Ready list** = insns whose successors are all scheduled. Sort (`rank_for_schedule:2414`), pick `ready[0]`:
|
||||
1. **highest INSN_PRIORITY**;
|
||||
@@ -72,7 +73,7 @@ The scheduler **never moves an insn across a basic-block boundary** (gcc-2.7.2 h
|
||||
6. **Launch/queue** (`schedule_insn:2587`): when a pred's last successor is scheduled, it becomes ready — but if the link cost >1 (load feeding the just-scheduled consumer) it is **queued `cost` cycles**: one independent insn gets wedged between a load and its consumer whenever one is ready; if none, they stay adjacent.
|
||||
7. **`adjust_priority:2534` (2.7.2: **2507**) — THE BIRTHING BOOST (pre-reload ONLY, `reload_completed==0`)**: on becoming ready, an insn whose pattern is `SET(REG, …)` — **any REG, pseudo OR hard; CORRECTED 2026-07-28, there is no `>= FIRST_PSEUDO_REGISTER` test in the function** — with the dest live and **`REG_N_SETS(dest)==1`** (`birthing_insn_p:2498`; 2.7.2: **2469**, the `reg_n_sets` test at **2490**) has its priority raised to `max_priority` (≈ the launching insn's) → it wins every tie → **single-set defs sink to just before their first consumer**. Dump tell: `(7f000001)` priorities in the ready list. NB: REG_N_SETS is counted **after cse/flow** — a source-level 2nd assignment that cse copy-propagates or flow dead-store-eliminates does NOT kill the boost (proof: `exp/t5.c`, `exp/t6.c` — both still boosted).
|
||||
8. Special pins: **bb0 head-skip** (sched.c:3218-3244): the leading run of `pseudo = hard-arg-reg` param copies is excluded from scheduling (stays first, in arg order). **Tail pin** (3313-3360): trailing JUMP/CALL/USE insns stay at bb end (TAIL_PRIORITY). `SCHED_GROUP_P`: a call + its immediately-preceding `USE argreg` insns move as one unit.
|
||||
9. **sched2 differences**: no boost, no head-skip; hard-reg anti/output webs (scratch reuse) now pin most of sched1's order in place; **nop-moves are deleted** (sched.c:4926); RTL prologue/epilogue saves are now in the pool (see S7). sched2's LUID = sched1's output order → **pre-reload placement persists**.
|
||||
9. **sched2 differences**: no boost, no head-skip; hard-reg anti/output webs (scratch reuse) now pin most of sched1's order in place; **nop-moves are deleted** (sched.c:4926); RTL prologue ~~/epilogue~~ saves are now in the pool (see S7) — **[A23] PROLOGUE ONLY; the epilogue expander is dead on MIPS in 2.7.2, so epilogue restores never enter sched2's pool.** sched2's LUID = sched1's output order → **pre-reload placement persists**.
|
||||
|
||||
---
|
||||
|
||||
@@ -116,7 +117,13 @@ The scheduler **never moves an insn across a basic-block boundary** (gcc-2.7.2 h
|
||||
### S6 — "Independent insn separates address-gen from use" / copy placed between ⇒ mechanics of the class rule
|
||||
- **Decision point:** `rank_for_schedule:2428-2452` class 3 > class 1. On MIPS anti/output are ALWAYS class 3 (ADJUST_COST) — only true-data-deps through latency>1 (loads, mul/div) are demoted. Explains the recurring "unrelated move sits between `addiu $x` and `lw …($x)`" target shapes. Steer via which independents are available (statement order).
|
||||
|
||||
### S7 — Prologue/epilogue save/restore interleave ⇒ sched2 artifact, body-side STEERABLE
|
||||
### S7 — Prologue~~/epilogue~~ save~~/restore~~ interleave ⇒ sched2 artifact, body-side STEERABLE
|
||||
> **[A23] RE-SCOPED, not deleted (audit 2026-07-28).** The **prologue** half is CONFIRMED: the MIPS
|
||||
> prologue really is RTL, so its saves are in sched2's pool. The **epilogue** half is FALSE for our
|
||||
> build — `grep -n 'define_expand "epilogue"' config/mips/mips.md` finds only a DEAD entry, and
|
||||
> `thread_prologue_and_epilogue_insns` (`function.c:5515`) is split by two independent guards
|
||||
> (`HAVE_prologue` / `HAVE_epilogue`), so the epilogue restores are NOT scheduled RTL here.
|
||||
> **Do not look for epilogue-restore interleave as a sched2 artifact — it cannot occur.**
|
||||
- MIPS prologue is **RTL**: saves emitted `$ra` down to `$s0` (**descending regno**, `save_restore_insns:5077`), sp-adjust first. sched2 weaves body insns among them under the same rank rules (anti-deps: `sw $sN` must precede the first body write of `$sN`).
|
||||
- Target tell: `sw $s1` far from `sw $ra/$s5/$s4` (pulled by an early body overwrite of `$s1`); a callee-save `sw` in a branch/call delay slot (dbr backward-fill, exp/t2 f2: `bne…; sw $31,20($sp)`).
|
||||
- The saves' RELATIVE order is fixed (descending regno) — if the target shows otherwise it's sched2 weaving, steered by the body insns' priorities/LUIDs, not by any prologue-side lever.
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# T7 §G — levers harvested this session (formalize into cookbook §36/§31 at batch close, R30)
|
||||
|
||||
> **[A23] SOURCE-VERSION AUDIT (2026-07-28).** 30 claims re-derived against `tools/reference/gcc-2.7.2/`;
|
||||
> 2 REFUTED raised. **Provenance here is MIXED, not uniformly contaminated** — some cites are already
|
||||
> 2.7.2-correct (e.g. `sched.c:2469` = the true `birthing_insn_p` line) while others came from the
|
||||
> 2.8.1 `gcc-papermario` tree. So check each citation individually rather than assuming a uniform
|
||||
> offset. This is a session harvest log, not a primary reference: where it disagrees with
|
||||
> `sched.md` / `regalloc.md` / `cse_expr.md` (all audited the same day), those files win.
|
||||
|
||||
## TOOL FIX (flywheel) — permuter comment-strip (tools/p16_permute.py make_base_c)
|
||||
A draft's header comment (long, non-ASCII —/§, prose that trips a prep regex) lost its closing
|
||||
`*/` in base.c → `cpp -P -nostdinc` died "unterminated comment" → decomp-permuter no-op'd SILENTLY
|
||||
|
||||
@@ -6842,3 +6842,66 @@ Struck-through text is preserved, never deleted (H5).
|
||||
2. `func_80176734` at 13/371 — permuter, then the `reg_renumber` gdb oracle. 51,198 ins.
|
||||
**§H's oracle recipe is now audited**, so it is safe to run.
|
||||
3. **`loop.md` is the last un-audited map file** (`scan_loop` drifts +74). Same two-stage recipe.
|
||||
|
||||
## ✅ T35 — the remaining 4 map files audited: 20 corrections, 6 false alarms caught, 12 flagged unverified
|
||||
|
||||
Drew: "do loop.md too. are there more? do them all if we need to." Enumerated the real scope first
|
||||
rather than guessing, then ran one wave over everything left: **`cse_expr.md`, `loop.md`,
|
||||
`sched.md` (full pass — T33 had only landed a partial), `t7g-giant-harvest.md`.**
|
||||
**35 agents (9 derive + 26 adversarial refute), 2.48M subagent tokens, 25 min.**
|
||||
|
||||
**308 findings: 174 CONFIRMED · 99 LINE-DRIFT · 26 REFUTED raised → 20 upheld / 6 overturned · 9 unverifiable.**
|
||||
|
||||
| file | findings | REFUTED raised | note |
|
||||
|---|---|---|---|
|
||||
| `cse_expr.md` | 74 | **17** | highest error density of any map file |
|
||||
| `sched.md` | 108 | 4 | full claim-by-claim pass (T33 was partial) |
|
||||
| `loop.md` | 96 | 3 | LOWEST density — credit to its pre-existing caveat table |
|
||||
| `t7g-giant-harvest.md` | 30 | 2 | MIXED provenance, not uniformly contaminated |
|
||||
|
||||
### ⚠️ 12 FABRICATED — diagnosed, and the diagnosis matters
|
||||
`tools/verify_map_findings.py` flagged 12 quotes that appear nowhere near their cited line (vs **0**
|
||||
in the T34 audit). **Cause is NOT invention: the agents pasted MAP text into the `source_quote`
|
||||
field instead of compiler source** — "single fall-through exit." is in `loop.md`, not `loop.c`.
|
||||
All 12 are **CONFIRMED**-status and **none underpins an upheld refutation**, so nothing was deleted
|
||||
on bad evidence. But they are **unverified, not confirmed**, they all sit in loop.md's
|
||||
biv-elimination / `check_dbra_loop` area, and that is recorded in `loop.md` as an open gap rather
|
||||
than quietly counted as a pass (R32 — a silent skip is a defect).
|
||||
|
||||
### The headline corrections
|
||||
- **[cse_expr] THE 1000-INSN CSE FLUSH DOES NOT EXIST IN 2.7.2** — `grep -n num_insns cse.c` → no
|
||||
hits; added in 2.8.1. It appeared in **three** places: §1's killer table, §6's giant tell, and §7's
|
||||
"shift ±insns across the 1000 boundary" lever. **A lever aimed at a counter our compiler does not
|
||||
have, in exactly the giants this map is consulted for.** All three struck.
|
||||
- **[cse_expr] §2's "kill THE class reg" is singular and wrong** — a CSE class holds SEVERAL regs and
|
||||
the extras have no C-level name. **The audit byte-reproduced my own T31 wall with the pinned cc1**:
|
||||
`expand_block_move` (`mips.c:2350-2351`) does `copy_addr_to_reg` on BOTH aggregate addresses, and
|
||||
cse substitutes that pseudo into the first call's arg-load, so the kill invalidates a register
|
||||
already out of the chain. Two byte-proven remedies recorded (field-by-field copy; a real join
|
||||
CODE_LABEL) **with the caveat that (a) is closed when the target's own bytes need the block move**
|
||||
— which is precisely `func_80132F40`'s case. An independent audit and a live byte-test converged
|
||||
on the same wrong sentence from opposite directions.
|
||||
- **[cse_expr] `assign_temp` does not exist in 2.7.2**, and `assign_stack_temp` does NO `/s` reset —
|
||||
a **recycled slot INHERITS `/s`**, the opposite of §4a. **No `BUILT_IN_MEMSET` either** — memset is
|
||||
always a library call, so the "memcpy/memset/strcpy" trio is really memcpy/strcpy.
|
||||
- **[cse_expr] §6's "recompute after a join is NORMAL — never a residual" is false at -O2** (both
|
||||
`flag_cse_follow_jumps` and `flag_cse_skip_blocks` are set) — as written it would have blocked
|
||||
§H's own antidote.
|
||||
- **[sched] S7's EPILOGUE half is false** — MIPS has no live `define_expand "epilogue"`, so epilogue
|
||||
restores never enter sched2's pool. Re-scoped, not deleted (the prologue half is correct).
|
||||
- **[sched] `insn_cost` is DEP-KIND-BLIND in 2.7.2** (no `REG_DEP_ANTI` zero-case) → restoring `/s`
|
||||
anti edges is NOT free; it re-groups downstream stores.
|
||||
- **[loop] "no memory load is EVER hoisted from a loop containing a call" is FALSE** —
|
||||
`invariant_p`'s `case MEM:` checks `RTX_UNCHANGING_P` FIRST and breaks. Byte-proven on the pinned
|
||||
cc1: a `const int *` load lands in the **preheader**. So a const-qualified pointer is a real lever.
|
||||
- **[loop] call args are emitted LEFT-to-right**, not right-to-left (RTL dump: `$a0` insn 10, `$a1`
|
||||
12, `$a2` 14). The `func_80150528` anchor observation still holds but for a different reason.
|
||||
|
||||
### Scope answer for Drew: one more exists, and it is a DIFFERENT risk profile
|
||||
`matching-cookbook.md` carries ~52 source citations and they ARE behavioural. But it is **mixed
|
||||
provenance** (`loop.c:5556` is exact in 2.7.2; `expr.c:5535` is wrong — the real `MEM_IN_STRUCT_P`
|
||||
sites are 4577/4904) and — the key difference — **its idioms are BYTE-PROVEN, with citations attached
|
||||
as explanation.** A drifted cookbook citation corrupts the *explanation* while the *lever still
|
||||
works*. Recommended: a **targeted citation sweep**, not a claim-by-claim audit. NOT yet done.
|
||||
|
||||
**Docs-only — no `src/`/`config/` touched, R22 not re-run and not claimed.**
|
||||
|
||||
Reference in New Issue
Block a user