docs(phase-26): cookbook §49 — the LUID DIAL (func_8017A4AC MATCH, 536 ins ×134, the biggest remaining fn)

The sched.c analogue of §47's live-length slider. A close=2 with IDENTICAL registers is not a regalloc
residual — it is sched2's rank_for_schedule falling through to its final tiebreak,
'return INSN_LUID(tmp) - INSN_LUID(tmp2)', i.e. position in the .greg stream. Root cause is upstream in
sched1: adjust_priority/birthing_insn_p gives every register-DEFINING insn LAUNCH_PRIORITY 0x7f000001
(sched.c:2574), which sinks the un-boosted insn past its rivals and inverts the LUID order.

THE DIAL: materialize a call argument's sign-extension into an explicit s32 temp, placed AFTER the
intervening statement (adjacent to the load, combine fuses lhu+sll+sra into one lh and you LOSE 3 insns;
the intervening store blocks the fusion). Widen the prototype to (s32,s32) so the call adds no conversion.
Same instructions emitted, earlier INSN_LUID -> the tie flips.

Zero-byte dial family is now three: §47 live-length (global.c), §48-A1/A4 sink-init/sink-call
(global.c/local-alloc), §49 LUID (sched.c). Method: -dS -dR dumps the ready lists + priorities; equal
priorities => you are on a LUID tiebreak => the fix is PLACEMENT, not registers.

11 of 12 wave cores now MATCH.
This commit is contained in:
Drew T
2026-07-14 00:53:27 -06:00
parent b813432b1b
commit a30500eecf
+49
View File
@@ -3369,3 +3369,52 @@ stay distinct, make them structurally distinct — separate registers, or separa
Every agent verified its `.rodata` table against the target jtbl and reported the evidence. Bake the
trap into the prompt, not into the post-mortem.
- The 3 near-misses are all pure allocation/emission-order residuals (close=2, 2, 21) — §47-slider class.
## §49 — The LUID DIAL: a zero-byte SCHEDULING dial (the sched.c analogue of §47) — `func_8017A4AC` (536 ins ×134), Phase 26 session 8
§47 splits a **global.c allocno-priority** tie by shifting a live-length. This splits a **sched.c
`rank_for_schedule`** tie by shifting an insn's position in the expand stream. Same philosophy: *when a tie is
broken by an accident of ordering, change the ordering — without changing a single emitted instruction.*
**The residual.** Two adjacent instructions transposed, **registers already identical** — a pure emission-order
residual. Not §47: `global.c` was innocent.
**The mechanism (two passes, and the proximate cause is not the root cause).**
- **sched2 (proximate).** gcc-2.7.2 schedules **BACKWARD** (`.sched2` prints `T-1` = the last insn). At the tie
point both candidates measured `priority = 2` and the same class vs `last_scheduled_insn`, so
`rank_for_schedule` fell through to its final tiebreak — `return INSN_LUID (tmp) - INSN_LUID (tmp2);` — i.e.
**the tie is decided purely by position in the `.greg` stream**.
- **sched1 (root).** `adjust_priority` → `birthing_insn_p` (`bb_live_regs[dest] && reg_n_sets[dest]==1`) hands
every register-DEFINING insn `LAUNCH_PRIORITY = 0x7f000001` (`max_priority`, sched.c:2574). That boost lets
the load chain seize the early backward cycles and **sinks** the un-boosted insn past its rivals — so the
`.greg` LUID order comes out inverted and sched2's tiebreak then picks the wrong one. *(A dead-end store —
`(set (mem) …)`, never "birthing", priority 2 — is starved and always floats to the front of its block.)*
**THE DIAL — materialize a call argument's sign-extension into an explicit `s32` temp, placed AFTER the
intervening statement.**
```c
case 19: {
s16 a, b; s32 ea, eb;
a = ring[i]; i = (i+1) & 0x1FF; ea = a; /* NOT next to the load — see below */
b = ring[i]; i = (i+1) & 0x1FF; eb = b;
f(ea, eb); /* prototype widened to (s32, s32) */
}
```
It moves the `sll/sra 16` pair EARLIER in the expand stream (lowering its `INSN_LUID`) while emitting **exactly
the same instructions**. Two placement rules are load-bearing:
- **The prototype must be `(s32, s32)`** so the call itself adds no conversion.
- **`ea = a;` must sit AFTER the store, not next to the load.** Adjacent to the load, `combine` fuses
`lhu`+`sll`+`sra` into a single `lh` and you LOSE 3 instructions. The intervening store blocks that fusion,
so the `sll/sra` pair survives — identical bytes, earlier LUID.
**The zero-byte dial family is now three (all emit nothing; all steer a tie):**
| dial | pass | what it shifts | § |
|---|---|---|---|
| live-length slider (`asm("")` between two volatile asms) | `global.c` allocno priority | live_length ±1 | §47 |
| sink-the-init / sink-the-consumer-call into the arms | `global.c` / `local-alloc` | refs + live-range, or deletes the allocno | §48-A1/A4 |
| **LUID dial (materialize a temp, placement-controlled)** | **`sched.c` rank_for_schedule** | **INSN_LUID (expand-stream position)** | **§49** |
**Method note (this is how it was measured, and it is reusable):** `-dS -dR` on cc1 emits the `.sched`/`.sched2`
traces — the ready lists, the computed priorities, and the chosen order. When a residual is "two instructions
swapped, same registers", dump the schedule and read the tie: if the priorities are equal, you are on a LUID
tiebreak and the fix is a *placement* change, not a register change. Harness: `.run/a4ac/dump2.sh`.