4.4 KiB
§32 — The region-a CAMERA-GIANT idiom set: struct-base hoisting + 4 sibling levers (Phase 24 T7, Fable5-cracked on func_80129CF8 191 ins, match_one MATCH; transferable to the 6 sibling giants)
The region-a (ov_SC01_077_a.c) giants Ghidra flattens into per-global lui/%lo — but the target holds global base ARRAYS in callee-saved regs across the whole function. Fable5 read the gcc-2.7.2 source and cracked func_80129CF8 in 3 edits (202→143→10→0), no pins, no permuter. The idiom set (each transferable — .run/t7/func_80129CF8.c is the worked example, .run/t7/GIANTS_SURVEY.md the target list):
- Hoisted
$sNbase = an explicit POINTER LOCAL, assigned AFTER the first call, in the target'sluiorder (p2 = D_800AF630; p1 = D_80126DB8;right after the firstjal). gcc-2.7.2 has no cross-bb CSE, so a base living in a callee-saved reg across calls/branches can ONLY come from a source local — you cannot get it from a bare global access. Noregister __asm__pins needed: allocation order = density priorityfloor_log2(n_refs)*n_refs/live_length(global.c:594 allocno_compare) then first-fit regno (mips.h has no REG_ALLOC_ORDER) → most-referenced ptr→$s0, next→$s1, next→$s2. The init placement survives because sched1's ascending-LUID tie-break (sched.c:2414 rank_for_schedule) keeps zero-dep sets in order. A single-set/single-use base local is SAFE from the RC-7 init-sink (its hi/lo SET_SRC is a LO_SUM → failsrtx_equal_patlocal-alloc.c:1169-1172). - Branch polarity is READ OFF THE TARGET OPCODE (not Ghidra's
if): targetbeqz $v0,.Lcopywith the other arm as fall-through ⇒ writeif (sel != 0) {fallthrough-arm} else {copy}. On func_80129CF8 this single inversion fixed 133 of 143 mismatches. lw/lw/nop/addu/swper-element ladders = the S12 reused-s32-temp fence —a = p1[i]; b = cam[j]; cam[k] = a+b;reusing ONE(a,b)pair across all elements. But leave the LAST element in Ghidra's fresh-temp shape (t = p1[last] + cam[..]; cam[..] = ..; cam[..] = t;) — its unfenced load is what the scheduler hoists into the previous element's load-delay slot, and a following call-arg&cam[..]fills the gap before it, both automatically.- A grouped 3-loads/3-stores copy fed by
la $reg,SRC(split lui/addiu) = a 32-byte STRUCT ASSIGNMENT, never scalar copies:*(RView*)(p2 + 6) = *(RView*)D_800AE688;(RView = the GsRVIEW2-shaped 32-byte struct, now inengine_types.h).expand_block_move(mips.c:2361) emits onemovstrsi_internalfor ≤2*MAX_MOVE_BYTES(32) with 4 scratches;output_block_moveburns the last scratch onla a1,SRC→ 3 data regs → the 3+3 grouping. The dest must go through the pointer local (p2+6, folded to0x18($s2)); a bare global dest is CONSTANT_P and degrades to 2 regs. Scalara=src[i]; dst[i]=a;copies schedule as lw/lw/sw/sw pairs — WRONG shape. - Frame bigger than args(16)+saves by a round chunk = a DEAD LOCAL AGGREGATE: add an unused
RView view;(32 B) to reserve the missing 0x20 — gcc-2.7.2 assigns stack slots to local structs/arrays at expand time regardless of use, and -O2 never deletes them. Suspect this whenever a sibling's frame is 0x20/0x28 over-accountable. - This class needs NO pins, NO asm fences, NO permuter — the interp-loop software-pipelining (a2/sll/lh-next/sra/sw + a3-in-jal-slot) is deterministic sched2+dbr output once the bases sit in $s0/$s1/$s2.
BANKING (Phase-24 T7b — RESOLVED, see §33): a freshly-matched giant MATCHes standalone (match_one) but its loose data decls (struct BigCopy / s32 / s32[] / u8[] / s8-vs-u8) collide in-TU with engine_core.h when you try to bank it. The fix is a decl-reconcile — declare each symbol its canonical type and cast byte-neutrally at the access site (cam = (s32*)D_80126948;, p1 = (s32*)&D_80126DB8; for a struct BigCopy base, D_801151D4 = (s32)cam;, *(u8*)&D_801150D6 to force lbu under s8) — done by HAND for func_80129CF8, now automated by tools/reconcile_decls.py (§33). R14 correction: the T7 note that "banking ×134 hits the wall" was a MISDIAGNOSIS — once the ×1 bank is reconciled, dedup_propagate --recover propagates it to all 134 overlays byte-identical for free (proven: func_80129CF8 ×134, clean fleet 136/136). The wall was only ever the ×1 reconcile of the loose draft, not the propagation.