diff --git a/config/cflags.txt b/config/cflags.txt
index 1403889ed..5557921ac 100644
--- a/config/cflags.txt
+++ b/config/cflags.txt
@@ -3204,6 +3204,7 @@ src/levels/shared/L00_004224D0-main-g8.c -O2 -G8 -ffunction-sections -frac2-qcop
src/levels/shared/L00_003C6298-seeker.c -O2 -G0 -ffunction-sections -ffast-math -frac2-zero-ti-por -frac2-hazard-labels
src/levels/shared/L02_00410F68-g8.c -O2 -G8 -ffunction-sections -frac2-zero-ti-por -g
src/levels/shared/L03_002AB5C8-region.c -O2 -G0 -ffunction-sections -g
+src/levels/11_joba/L11_00451178-gu-g8.c -O2 -G8 -ffunction-sections -frac2-gcse-unsplit-edges
src/game/002E7338-g8.c -O2 -G8 -ffunction-sections
src/game/00337210.c -O2 -G0 -ffunction-sections
src/game/00316E08.c -O2 -G0 -ffunction-sections
diff --git a/docs/MATCHING-BLOCKERS.md b/docs/MATCHING-BLOCKERS.md
index 810dcbc41..260b7beac 100644
--- a/docs/MATCHING-BLOCKERS.md
+++ b/docs/MATCHING-BLOCKERS.md
@@ -641,6 +641,61 @@ loops give them: `for (i = 0; i < 16; i++) a[i] = 0;` has an address computation
into the same pointer countdown as `*dst++` over a reversed counter. Check loop spellings first when a near miss is
all stack offsets; they change nothing after loop.c.
+**Joba state machines, 5 November 2026 (USA v1.01, `-O2 -G8`, retained in `nonmatching/levels/`):**
+`func_L11_00453358` is now 4524/4524 bytes with every instruction equal; a scratch cc1 whose bucket
+count is forced from an environment variable (not committed) makes it `1 of 1` at S 539-543 and
+561-565, so only the gcse insn count is left (its source gives 551). Whole-file `-fno-force-mem`
+also lands it in the window (it removes the HI copy that `emit_cmp_insn`/`compare_from_rtx` force
+before each `short` compare), but changes four other places, among them a `u8` compare whose
+`andi 0xFF` retail keeps, so that flag is not retail's profile. Skipping `force_not_mem` only
+in `extendhisi2`, or only for HImode compares, moves `max_cuid` by just 2 and 4. Every spelling of
+an `if (*(s16 *)p)` test (`!= 0`, an `(s32)` cast, a struct field, an `s32` local) expands to the
+same three insns. Dead initialisers count one insn each and change no instruction. The case-9
+cross-jump in that function is closed only by the matched twin `func_L02_0040BE08`'s
+`height = 0.0f;`, which needs a ruling. (Under blocker 26 it is an artificial use, so `func_L11_00453358` is kept in `nonmatching/` exact but unlanded.) `func_L11_00451178`'s natural wording needs S 575-585 and
+then still differs in two orders, so it is not a count alone.
+Second pass: for 453358, loop spellings, `rot` placement and the byte-offset `qcopy` are
+count-neutral, and even all ten dead initialisers reach only S 555. A `pos` local lands in the
+window but takes a saved register, so the roughly 20-insn difference is still unidentified.
+**Later on 5 November 2026:** `func_L11_00453358` became exact (level 11 `OK`) but is kept in
+`nonmatching/` because of the case-9 dead store (blocker 26). The count came from two inlined helpers, not from
+spellings: `anim(self, a, b)` (`if (self[0x43] != a) func_002A7470(self, a, 0, b);`, eight
+sites) and `turn(self, rate, speed)` (`func_002B60B0` with the shared last two constants, four
+sites). Both are code-neutral and bring S from 551 to 543. So a void helper that wraps a
+repeated idiom is a count lever: F4's "inline helpers returning a value do nothing" does not
+extend to these. On the forced-bucket check, an `emit()` helper around the `func_002A8C28`
+calls is code-neutral too (S 541). For `func_L11_00451178` the same helpers bring the natural
+wording into its window (`turn` alone gives S 581), but its case-7 hoist order still differs.
+**Resolved later the same day:** `func_L11_00451178` matches too (`L11_00451178-gu-g8.c`). Three
+steps, each read from a dump:
+- **Slot order.** Retail's 0xCC slot for `tmpl` sits between gcse registers (0xC4/0xC8 for
+ `&work[1]`/`&work[4]`, 0xD0+ for case 12's addresses), which a user variable can only get if
+ it is *evicted*. Global-alloc gives `tmpl` and case 12's three reaching registers one saved
+ register (their lives do not overlap), reload takes it back, and the evicted pseudos get
+ slots in pseudo order after the ones spilled from the start. The variable has to win the
+ register first, so `tmpl = &particle;` goes just before the template is filled (a short
+ life), not earlier in the case. As a register argument it also drops gcse's copy from the
+ particle loop: 46 insns, so loop.c's first-pass threshold (61 - 3 per hoist) still covers
+ 0x70707F.
+- **PRE copy order.** PRE inserts its copies in expression-index order, which is the order of
+ first appearance. `spin = (float *)(state + 0x2B0);` before the loop (`func_L11_0044AFC8`'s
+ `rotation`) makes `state+0x2B0` appear before `object+0xF0`.
+- **Bucket count.** The `turn()` helper gives S 583.
+Reading `-dL` for "not desirable" and the loop's real insn count is the quickest check on a
+preheader order.
+
+**PR #419 contract review, 4 November 2026:** the sentry's `func_002B1C20` call must
+preserve its matched callee's unsigned 64-bit return. With that declaration and a
+64-bit local, the compound `!func_002A77E0(30) || (flags & 1)` condition adds a
+`dsll32`/`dsra32` normalization pair (4832/4824 bytes). The equivalent early exit
+`if (func_002A77E0(30) && (flags & 1UL) == 0) break;` preserves the original call
+order and low-bit tests and restores 4824 bytes, 1/1 USA v1.01 placements. Its
+canonical identity is absent from PAL and USA v2.00. The held drone also restores
+four ignored return contracts from their matched definitions: integer results for
+`func_002A8C28`, `func_002A8D70` and `func_L07_00321D68`, and a float result for
+`func_002AA548`. It remains 1/1 in both USA releases, but its case-9 dead store
+still prevents promotion.
+
**Update, 3 November 2026 (pending review):** now matched. `func_L12_00300410` is C in `src/levels/12_todano/L12_00300410-g8.c` (`-O2 -G8 -ffunction-sections`), placed at 1 US place (PAL and US2 catalogues have no function of this name). The measurements above are kept as history.
**Qualified field and call-lifetime remedies, 25 October 2026:**
@@ -3546,7 +3601,7 @@ attributed for provenance, not exclusive ownership.
| **Clear through a register address at a nonzero offset:** `func_002B5678` | Open | Retail has `addiu $v0,$s1,0x60 ; sq $zero,0($v0)`, the clear macro's register operand. After #258, C gives `por` + `sq $v0,0x60($s1)`. **Next:** a profile that materializes the address of a displaced const-0 TImode store. |
| **A `por` clear at a zero displacement:** `func_L02_003DB200`, `func_L02_00423520`, `func_L23_00422798`, and with the copy barrier `func_L02_003DBA70`, `func_L23_00429DC8` | Proposed: PR #281 | These functions clear a stack vector with `por $v0,$zero,$zero` + `sq $v0,0($sp)`. Since #258, the fold still makes that `sq $zero,0($sp)`, because the address is register-only.
A finer retail count (one place per catalogue row, boot game code from 0x26EA00, a store counted when every path into it starts from the `por`) finds 183 `por`-fed stores at a zero displacement, in 153 functions. Every one of them is to `0($sp)`; none goes through another register. Against that, 396 `sq $zero,0(reg)` stores in 263 functions, of which 82 (58 functions) are also to `0($sp)`.
PR #281 adds the opt-in `-frac2-zero-ti-por`: with it, the fold leaves a store to `0($sp)` alone, and stores through any other register still fold. It is neutral by default (806 of 806 units). Forced on everywhere, it changes exactly the 4 units whose matched functions clear `0($sp)` with `sq $zero`. The five functions above are exact with it. `func_L23_003C16D8` stays out: three of its clears go through a computed stack address. |
| **A cse block boundary that leaves no instruction:** `func_L02_003D26B0`; the five 632-byte twins `func_L11_00436D08`, `func_L11_00449C70`, `func_L11_0044C850`, `func_L11_004530E0`, `func_L11_004566C0`; `func_L02_003F1BA8`, `func_L02_0040BBD8`; `func_L07_00378898` | Open | Retail keeps a gcse (PRE) copy, for example `daddu $s5,$s4,$zero` of `e + 0x10`, that this cc1 makes only when cse1 and cse2 both see a label at that spot. Adding `\|\| x == 7 \|\| y == 9` to the test reproduces it, with extra code. An empty `do { } while (0)` reproduces it exactly but is rejected (#257, fc4a6c6). For `func_L07_00378898`, `-fno-cse-follow-jumps -fno-cse-skip-blocks` reproduces the block, as a probe only. **Next:** the original's construct at that spot, or a measured cse rule. |
-| **Meaningful nonempty one-pass groups:** `func_L02_003FB000`, `func_002CDA20`, `func_002CF2B8`, `func_002D53F0` | Strict linked matches; pending PR review | **4 November 2026:** current active RAC1 game C supplies the missing source-method precedent; see [source methods](COMPILER-NOTES.md#source-methods-shared-with-rac1). The actual last store gives Maktar 288 bytes, 1/1 places in each USA release (PAL identity absent). Actual formatter-call groups give the three text draws 140/136/140 bytes and 28/28 USA v1.01 places each; their canonical identities are absent in the other releases. The formatter style remains 64 bits; its return and the signed32 address getter follow their real definitions. Old narrower-style trials remain invalid. All 84 strict programs pass with zero C fallbacks, and all 92 placements of the five added functions are present in their carrying programs. This supersedes the earlier blanket hold on nonempty groups, not the ban on empty loops or fake dependencies. `func_0033A520` is still unresolved: its historical exact probe also assumes an unevidenced const global and fails both alternate releases. |
+| **Meaningful nonempty one-pass groups:** `func_L02_003FB000`, `func_002CDA20`, `func_002CF2B8`, `func_002D53F0` | Strict linked matches; pending PR review | **4 November 2026:** current active RAC1 game C supplies the missing source-method precedent; see [source methods](COMPILER-NOTES.md#source-methods-shared-with-rac1). The actual last store gives Maktar 288 bytes, 1/1 places in each USA release (PAL identity absent). Actual formatter-call groups give the three text draws 140/136/140 bytes and 28/28 USA v1.01 places each; their canonical identities are absent in the other releases. The formatter style remains 64 bits; its return and the signed32 address getter follow their real definitions. Old narrower-style trials remain invalid. All 84 strict programs pass with zero C fallbacks, and all 92 placements of the five added functions are present in their carrying programs. This supersedes the earlier blanket hold on nonempty groups, not the ban on empty loops or fake dependencies. `func_0033A520` is still unresolved: its historical exact probe also assumes an unevidenced const global and fails both alternate releases.
**5 November 2026:** `func_L02_003FE768` (shared boss bar) matches the same way, with each real conversion of its rounded edge in a group (`t = v * scale + 0.5f; do { top = t; } while (0);`): 2 of 2 USA places, 0 of 2 without the groups. A scratch cc1 that made every float-to-int conversion a sched2 barrier broke 95 of 228 matched functions, so the barrier belongs to these sources. In `func_L11_00444400` the same groups give retail's boundary before each box call (4692/4684 bytes); its register allocation is still open. |
| **Jump tables the build cannot place:** `func_L06_003FDA08`, `func_L13_00422CB8`, `func_L03_003C4578`, `func_L02_003F9AF0`, `func_L02_003F9B70`, `func_L02_003FE1B0` | Solved, PR pending | A computed goto through retail's own table, in the form of `func_00310A40`, matches all six. The three level 2 records' in-order switches were wrong: retail's tables map the values to the bodies in another order. Decode the table before trusting a record's `switch`. |
| **Labels behind hazard nops:** `func_L18_004308F0`, `func_L02_00411530` | Merged, #264 | Opt-in `-frac2-hazard-labels`. 198 retail branch targets after a compare or `mtc1` sit on the nop, and SN's `Ps2EeAs` agrees. Forced on everywhere it is neutral (778 of 779, one `.mdebug`). |
| **gcse critical-edge split, Cygnus block merge:** `func_002CD720`, `func_L02_003E79F8`, `func_L02_003FCCA0`, `func_L18_004283F0` | Merged, #261 | Opt-in `-frac2-gcse-unsplit-edges` and `-frac2-keep-block-order`. `func_L23_0041B9F0` reaches retail's size with the gcse switch and still stops at the copy order. |
diff --git a/nonmatching/README.md b/nonmatching/README.md
index 25b7393d4..4c0d9eab3 100644
--- a/nonmatching/README.md
+++ b/nonmatching/README.md
@@ -152,9 +152,8 @@ the placement rules are in `tools/fcheck.py`'s docstring; its tests are
| [`func_002A04B8`](game/func_002A04B8.c) | 48 | 28 October 2026 current-master check: ordinary-memory candidate is 44 bytes, 0/28 USA v1.01. A 29 October declaration-order probe also emits 44 bytes, 0/28, but moves the third output store to the `jr` delay slot while omitting retail's preceding nop; the loaded-word register and extraction order still differ. Isolated `-fno-schedule-insns` and `-fno-schedule-insns2` probes remain 44/48 and 0/28. An equivalent direct-byte-field probe is 28/48, 0/28 and emits three `lbu`s instead of the retail wide load and shifts. See retained source. |
| [`func_002B9030`](game/func_002B9030.c) | 8 | Retail is an 8-byte direct tail jump to `func_002B9038` with `$a1=0` in the delay slot. A natural one-argument C wrapper calling the target with a second zero argument compiles to a 28-byte frame/call/return sequence, 0/28 USA v1.01 placements; earlier default and `-mastra-sibcall` checks also missed 0/28 in US, PAL and US2. The compiler rejects `-foptimize-sibling-calls`; no assembly or function-level attribute workaround used. |
| [`func_L00_003FA858`](levels/func_L00_003FA858.c) | 5796 | 3 November 2026: Existing 144-byte quads, 64-byte matrix and vectors are complete. A genuine screen/vector phase view gives 5788/5796 bytes, 0/27 USA, and worsens the frame; the prior 5796-byte C is retained. Reopen on an actual flare-position/spill lifetime witness. |
-| [`func_L11_00444400`](levels/func_L11_00444400.c) | 4684 | 4 November 2026: 4640/4684 bytes; US 0/1, US2 absent, PAL absent; first size mismatch. Stored-address getters follow their matched signed32 return contracts, with explicit pointer casts at the actual uses. Physical EE widths and argument-bank order are preserved; this repair leaves the retained allocation/scheduling residual unchanged. The rounding-chain scheduling boundary remains; artificial wrappers/barriers are rejected. Historical index notes (superseded where inconsistent): 4640/4684 bytes. Current compiler and an additional -fno-force-mem control retain this result; the scheduling boundary after the rounding chains is unresolved. No empty wrapper, invented test or barrier is retained. US 0/1, US2 absent, PAL absent. |
+| [`func_L11_00444400`](levels/func_L11_00444400.c) | 4684 | 5 November 2026, third pass: the boundary is a block boundary that exists at sched2 and is gone by jump2/dbr (pass order flow2, sched2, jump2). An identical-arm mode test makes it but is not merged; an inline float rounding helper gives retail's chains and, with a mode test, its full save set. 5 November 2026: unchanged. An inline box helper does not make the scheduling boundary (sched2 interleaves the chains); a mid-block loop note is what the rejected do-while (0) supplied. Historical index notes (superseded where inconsistent): 4 November 2026: 4640/4684 bytes; US 0/1, US2 absent, PAL absent; first size mismatch. Stored-address getters follow their matched signed32 return contracts, with explicit pointer casts at the actual uses. Physical EE widths and argument-bank order are preserved; this repair leaves the retained allocation/scheduling residual unchanged. The rounding-chain scheduling boundary remains; artificial wrappers/barriers are rejected. Historical index notes (superseded where inconsistent): 4640/4684 bytes. Current compiler and an additional -fno-force-mem control retain this result; the scheduling boundary after the rounding chains is unresolved. No empty wrapper, invented test or barrier is retained. US 0/1, US2 absent, PAL absent. |
| [`func_L04_00414698`](levels/func_L04_00414698.c) | 6032 | 5976/6032 bytes. Restore the known pointer/integer helper returns and remove the empty floating comparison probe, which emitted nothing. The seven-FPR frame, vector lifetimes and dead retail comparison remain unresolved. US 0/2, US2 0/2, PAL absent. |
-| [`func_L11_00451178`](levels/func_L11_00451178.c) | 4824 | 4 November 2026: 4824/4824 bytes; US 0/1, US2 absent, PAL absent; first 0x1088. Five helpers keep their defined return contracts: the four level movement helpers return int and func_003280C8 returns a pointer. These ignored-result repairs leave the six case-12 PRE/spill words unchanged. Historical index notes (superseded where inconsistent): Finisher F3 (26 October 2026): 4824/4824 bytes, 1206/1206 instructions, everything equal except 7 words at case 12's head (the order of the spilled pointer sets, one store against a reload). Own unit `L11_00451178-gu-g8.c`, `-O2 -G8 -ffunction-sections -frac2-gcse-unsplit-edges`. The twin of func_L02_0040A4A8; see the source header. In the permuter farm. |
| [`func_L00_0038E880`](levels/func_L00_0038E880.c) | 1952 | 2 November 2026: 2 November 2026: retained 1944/1952 bytes, 0/27 US placements with the current default game compiler. Correct the ignored pointer returns of func_00308600 and func_002BF728, and the integer return of func_002E5DA0, from their current matched definitions. Evaluate the random lifetime before the second weapon-table read, as retail does; this fixes that call/read ordering without a See retained source for the full constraints. Not linked or counted. |
| [`func_L00_002EC970`](levels/func_L00_002EC970.c) | 1652 | M26: C draft, 1660/1652 B. The 0x700034A9 word is `por` (TImode zero) under -frac2-zero-ti-por, so the row is C-reachable; structure matches retail, register allocation and spill layout do not (see header) |
| [`func_L00_003D1D28`](levels/func_L00_003D1D28.c) | 3288 | 4 November 2026: 3288/3288 bytes; US 0/27, US2 0/27, PAL absent; first 0x7ac. Stored-address getters follow their matched signed32 return contracts, with explicit pointer casts at the actual uses. Physical EE widths and argument-bank order are preserved; this repair leaves the retained allocation/scheduling residual unchanged. PRE spill order remains; the complete hit request and real 48-byte matrices are preserved. Historical index notes (superseded where inconsistent): 3288/3288 bytes, not exact. The hit request now has its real aligned vector and the extra float at +0x24 read by func_002A8E20; tail alignment is implicit, not dummy bytes. Both matrix consumers were checked: they read/write exactly three quadwords, so the 48-byte matrices are intentional. The PRE spill ordering remains unresolved. US 0/27, US2 0/27, PAL absent. |
@@ -361,7 +360,6 @@ the placement rules are in `tools/fcheck.py`'s docstring; its tests are
| [`func_0032F3D0`](game/func_0032F3D0.c) | 444 | Decoded fully (see draft; D_001B0960 sda gate, two func_00300A00 calls, sprintf, func_0027F5B8). Needs -G8 -Wa,-G32 unit. Default flags: 448 vs 444 B: mine hoists 451.0f into a 6th callee-saved FP reg (retail keeps ... |
| [`func_L04_00416E38`](levels/func_L04_00416E38.c) | 492 | First-compile draft (472 B vs 492 B). Calls/args all identified (func_3092E8, func_32D030, func_30AAB8 with stack arg 3, func_30A9D8, func_3153B0, func_30A318). Open: retail recomputes (int)(D*263+.5) etc. after each ... |
| [`func_L01_003D6B00`](levels/func_L01_003D6B00.c) | 396 | 396 bytes; 0/2 in us, pal and us2. Right size; the loop body agrees substantially, but the count save/guard prologue and emitter argument schedule differ. A separate decrement-at-start loop grew to 408 bytes; disabling rerun-loop-opt gave 392 with an extra saved pointer and different constant motion; G8 did not fix it. Reopen with a genuine counter lifetime hypothesis. |
-| [`func_L02_003FE768`](shared/func_L02_003FE768.c) | 332 | 2 November 2026: Previously omitted retained source found in nonmatching/shared. Two fresh scheduling hypotheses: disabling sched1 keeps the 332-byte mode-hoist mismatch (+0xE4), disabling sched2 changes prologue/order from +0x4 at 332 bytes. Both 0/2 USA. Default marker/flags restored and original C retained with concise new evidence. Not linked or counted. |
| [`func_L18_0043FFF8`](shared/func_L18_0043FFF8.c) | 488 | 27 October 2026: 488/488 bytes, 0/3 places in each release at -G0 -g. Five initial flag constant/store words remain; the later vector, lifetime and return sequence agrees. |
| [`func_L13_0041AB30`](levels/func_L13_0041AB30.c) | 460 | Game -O2 -G8 -ffunction-sections -frac2-qcopy -g emits 464/460 bytes. US, PAL and US2 each fail 0/2 places; all canonical rows are present. Destination-base hoisting creates an extra saved register. Reopen with pointer-lifetime and allocator evidence. No strict build. |
| [`func_L08_0041E790`](levels/func_L08_0041E790.c) | 756 | 752/756 bytes; US and US2 0/2; PAL absent. The body agrees until the final rise division needs one incoming nop. Splitting the division gives two nops instead; G8 and hazard labels do not close it. The dependent boost transfer uses clobbering qcopy. No strict linked match. |
@@ -442,6 +440,6 @@ the placement rules are in `tools/fcheck.py`'s docstring; its tests are
| [`func_L10_00421790`](levels/func_L10_00421790.c) | 3620 | 4 November 2026: US 3616/3620 bytes, 0/1 after removing the placeholder text_row test/helper: every adjustment was zero, so the actual coordinate is the direct signed conversion. Real display-mode adjustments remain. Other releases not rechecked; HUD constant lifetimes and print/sprite argument scheduling remain. |
| [`func_L10_004239A0`](levels/func_L10_004239A0.c) | 1064 | 4 November 2026: US 1064/1064 bytes, 0/1, first +0xC after removing three do/while(0) wrappers and dead conditional float assignments. Real consumed display-mode adjustments remain. Other releases not rechecked; FP allocation, rounding and argument scheduling remain. |
| [`func_L11_00443D78`](levels/func_L11_00443D78.c) | 1332 | 4 November 2026: 1324/1332 bytes; US 0/1, US2 absent, PAL absent; first size mismatch. Stored-address getters follow their matched signed32 return contracts, with explicit pointer casts at the actual uses. Physical EE widths and argument-bank order are preserved; this repair leaves the retained allocation/scheduling residual unchanged. Coordinate conversions still combine into argument setup; allocation and branch scheduling remain. |
-| [`func_L11_00453358`](levels/func_L11_00453358.c) | 4524 | 4 November 2026: Current source is uncompiled: ten dead float stores used only to change instruction counts were removed, but inherited standalone declarations/types are incomplete. Recover those contracts and review the state machine before new trials; the old controlled four-spill result is historical. |
+| [`func_L11_00453358`](levels/func_L11_00453358.c) | 4524 | 4 November 2026 contract review: four ignored helper returns now follow their matched definitions; retained G8 C remains 1/1 in both USA releases (PAL identity absent). The contributor also reported a strict USA build before this return-only repair. Case 9 still contains the dead `height = 0.0f;` used to prevent cross-jumping; retained as a diagnostic, not promoted. See blocker 26. |
| [`func_L15_0042E1B0`](levels/func_L15_0042E1B0.c) | 3140 | 4 November 2026: US 3128/3140 bytes, 0/1; US2/PAL absent. Remove the invented pointer test, identical rounding arms, empty panel wrapper and unused helper. Natural text/panel rounding and FP argument lifetimes remain unresolved. |
| [`func_L10_004227A0`](levels/func_L10_004227A0.c) | 3420 | 4 November 2026: Final contract repair gives US 3260/3420 bytes, 0/1; US2/PAL canonical identities absent. Remove the overwritten scaled+1 control and unwitnessed volatile qualifiers. All eight calls now use the actual void helper with seven signed32 arguments, including color32 and text-address32. Under -O2 -G8, real HUD/helper lifetimes and rounding/scheduling remain unresolved; earlier controlled sizes are historical. |
diff --git a/nonmatching/functional.tsv b/nonmatching/functional.tsv
index 6acbde2ad..4e5b80267 100644
--- a/nonmatching/functional.tsv
+++ b/nonmatching/functional.tsv
@@ -122,7 +122,6 @@ func_L08_00308888 nonmatching/functional/func_L08_00308888.c
func_L10_00421790 nonmatching/functional/func_L10_00421790.c
func_L10_004227A0 nonmatching/functional/func_L10_004227A0.c
func_L10_00424CD0 nonmatching/functional/func_L10_00424CD0.c
-func_L11_00451178 nonmatching/functional/func_L11_00451178.c
func_L11_004580E8 nonmatching/functional/func_L11_004580E8.c
func_L13_0041AB30 nonmatching/functional/func_L13_0041AB30.c
func_L14_003DFA60 nonmatching/functional/func_L14_003DFA60.c
diff --git a/nonmatching/functional/func_L11_00451178.c b/nonmatching/functional/func_L11_00451178.c
deleted file mode 100644
index adf38ee40..000000000
--- a/nonmatching/functional/func_L11_00451178.c
+++ /dev/null
@@ -1,457 +0,0 @@
-/* functional: medium. Near-miss draft of retail size with the same frame: differences are allocation and scheduling, with some block order; paths the captured calls never take are unproven. */
-/* func_L11_00451178 (4,824 B, 1 place): finisher F3, 2026-10-26. The Joba twin of
- * func_L02_0040A4A8 (same particle loop: alpha = 127 after the 0x48 store, the template through
- * tmpl; see that unit's header). Profile -O2 -G8 -ffunction-sections -frac2-gcse-unsplit-edges.
- * 1206 of 1206 instructions, 4824 bytes; every instruction equal except 7 words in case 12.
- * What closed it:
- * - one function-scope scratch array work[6] at sp+0x10: retail reuses sp+0x10..0x5F for the
- * head's aim vector, case 7's bounds[2] + matrix[4], case 9's hand/shot/dir and case 12's
- * 5-quad buffer. Block-scoped vectors cannot share it here: an address-taken block local is
- * preserved to the enclosing level by every following expression statement (function.c
- * preserve_temp_slots), so the head's vector would stay live and push the frame to 0x180;
- * - pos = object + 0x10 set in each head arm, after the call in the state->0x2D8 arm (retail's
- * compute-then-copy);
- * - case 5 before case 6, each with its own movement-flag test (no fall-through);
- * - -G8 sda floats for the gp block D_L11_001B1D04..001B1D24, D_L11_001A8F20 as MACRO_ADDR
- * (one la insn at the call), D_L11_001C0D18 as an incomplete array, the boot pointers nosda;
- * - case 12's row copy as qcopy() in a counted loop: a TI copy lets loop.c reverse the loop;
- * - the spill slots: retail's sp+0xC4 &work[1], 0xC8 &work[4], 0xCC tmpl, 0xD0 state+0x2B0,
- * 0xD4 object+0xC0, 0xD8 object+0xF0 is pseudo-number order (none gets a hard register from
- * global; reload1.c gives slots in pseudo order). As gcse PRE copies the case-12 addresses
- * come in hash-bucket order (max_cuid 1175, 587 buckets) and always after tmpl, so they are
- * function-scope pointers declared out, scale, tmpl, spin, rows, axis.
- * Left (7 words, case 12 head): retail sets the spilled pointers in the order spin, out, axis,
- * scale, rows (stores to 0xD0, 0xC4, 0xD8, 0xC8, 0xD4); here out and scale are always adjacent
- * (0xD0, 0xC4, 0xC8, 0xD8, 0xD4 at best; all 120 orders of the five assignments give 12
- * sequences, none retail's; assigning them before the func_002B3868 call moves the frame ones
- * out of the block), and the state+0x1A8 clear is scheduled after the lw $s0 reload where
- * retail has it before.
- * v1932 (opus-1, 2026-10-31), recompiled on v2/c05: this body still gives 4824/4824 bytes, 0/1
- * places, 6 words differ (+0x1088..0x1094, +0x10D4/+0x10EC). Why single-set locals cannot fix
- * it: haifa-sched adjust_priority raises an insn that becomes ready when the func_002B3868 call
- * issues to the call's priority if birthing_insn_p (dest live, REG_N_SETS == 1). out and scale
- * are $1-based, so they depend on the call ($1 = $at is call-clobbered) and get boosted ahead of
- * axis. Retail's pointers are gcse PRE reaching registers (REG_N_SETS > 1, never boosted), as in
- * matched func_L11_0044AFC8. The natural wording (drop out/scale/tmpl/spin/rows/axis, write the
- * addresses directly, &particle for tmpl; the v1925 wip) gives retail's exact instruction order,
- * the 0x1A8 clear included. Only 15 spill-slot offsets then differ, because PRE numbers reaching
- * regs in bucket order, bucket = (14029 + regno + const) % S, S = (max_cuid/2)|1. That wording
- * has max_cuid 1176 (S 589): $1+176 wraps below $1+32. Retail's slots need S in 575..585, i.e.
- * max_cuid 1148..1171, 5 to 28 fewer insns after cse1 (or S 599 with state's pseudo >= 88, which
- * means 20+ more). Checked: deleting three object[0xBC] = 1 stores (max_cuid 1170) gives retail's
- * slots with no other change. Count-neutral: pointer locals inside one EBB, split head tests,
- * int vs float 0x1A8 clear, a byte-offset qcopy in the row loop. These change code: hoisted
- * &work[0]/&work[2] locals in case 9, a ?: argument for case 9's func_002A7750.
- * PERMUTE-FLAGS: -O2 -G8 -ffunction-sections -frac2-gcse-unsplit-edges
- *
- * closer D, 1 November 2026: the natural wording scores 19 words here. Merging the `object[0x60] & 2`
- * tails of cases 5, 6 and 10 with a shared label would lower max_cuid, but retail keeps those tails
- * apart (4,784 to 4,808 bytes), so the 5 to 28 fewer insns have to come from elsewhere.
- * Dropping the started, launch and goal locals scores 18 words; the first difference stays at +0xA24.
- *
- * closer D, later: MACRO_ADDR on D_00189E20 or D_L11_001C0D18 (one fewer insn each, same-register lui/addiu) moves
- * code, 29 to 57 words. They are not the missing 5 to 28 insns.
- */
-#include "common.h"
-
-typedef int TI128 __attribute__((mode(TI)));
-typedef union { TI128 quad; float f[4]; } ActorVector;
-typedef union { u8 b[0x2600]; s8 c[0x2600]; s16 h[0x1300]; u16 uh[0x1300]; s32 i[0x980]; u32 u[0x980]; f32 f[0x980]; void *ptr[0x980]; s64 d[0x4C0]; TI128 q[0x260]; } Hero;
-extern u8 D_00189E20[];
-typedef struct { u8 pad00[0x5C]; u8 *ids; } L11_Palette;
-extern L11_Palette D_L11_00200440;
-extern s32 D_L11_001A8FF4 __attribute__((sda));
-extern float D_L11_001B1D04 __attribute__((sda));
-extern float D_L11_001B1D08 __attribute__((sda));
-extern s32 D_L11_001B1D0C __attribute__((sda));
-extern u16 D_L11_001B1D10 __attribute__((sda));
-extern float D_L11_001B1D14 __attribute__((sda));
-extern float D_L11_001B1D18 __attribute__((sda));
-extern float D_L11_001B1D1C __attribute__((sda));
-extern float D_L11_001B1D20 __attribute__((sda));
-extern float D_L11_001B1D24 __attribute__((sda));
-extern u8 *D_L11_001C0D18[];
-extern u8 *D_0018C0B0 __attribute__((nosda));
-extern u8 *D_0018C2BC __attribute__((nosda));
-extern char D_L11_001A8F20[] MACRO_ADDR;
-
-extern void func_L11_00452450(u8 *);
-extern void func_002A7750(u8 *, s32, s32);
-extern s32 func_002A77E0(s32);
-extern void func_002A7470(u8 *, s32, s32, s32);
-extern float func_00283248(float, float);
-extern float func_00283C80(float, float);
-extern void func_00282CF0(void *, void *, void *);
-extern float func_002B04E0(u8 *, void *, u8 *);
-extern float func_00282E48(void *, void *);
-extern float func_00282E80(void *, void *);
-extern s32 func_002B3890(u8 *);
-extern s32 func_002B3868(u8 *);
-extern void func_002B60B0(u8 *, float, float, float, float);
-extern u8 *func_L11_004417E8(u8 *);
-extern void func_L11_00452F30(u8 *, u8 *);
-extern void func_L11_004530C0(u8 *);
-extern void func_L02_00333438(u8 *, void *);
-extern void func_L01_00321178(u8 *, void *);
-extern s32 func_L11_00441808(u8 *, u8 *);
-extern void func_002B5278(u8 *, float);
-extern s32 func_00282978(void *);
-extern s32 func_002761F8(void *, void *, s32, void *, void *);
-extern s32 func_L11_004530E0(u8 *);
-extern s32 func_002B4FF0(u8 *, float *, float);
-extern void func_L02_00333480(u8 *, void *, void *);
-extern s32 func_002B50E0(u8 *, void *, float);
-extern s32 func_00283DB8(void *, float);
-extern void func_0029FC20(u8 *, s32, void *);
-extern void func_0029FCC0(u8 *, s32, void *);
-extern void func_00282F20(void *, void *, float);
-extern void func_00282CC0(void *, void *, void *);
-extern void func_002ACB48(float *, float);
-extern float func_002A7878(float, float);
-extern float func_002A78D8(float, float);
-extern void *func_002C65A0(void *, void *, s32, u32, s32, s32, s32, float, float, float, s32);
-extern s32 func_002A8C28(void *, void *, float, u32, float, float, s32, s32, s32);
-extern void *func_002C16C0(void *, void *, s32, s32, u32, s32, float, float, float, float, float);
-extern s32 func_002A7820(s32, s32);
-extern void func_L11_00340500(u8 *, void *);
-extern float func_0029F530(u8 *);
-extern s32 func_002AEB60(u8 *, float);
-extern void func_00283098(void *, void *, void *);
-extern void func_003280C8(float, u8 *, void *, void *, s32, s32);
-extern s32 func_002B1C20(u8 *, void *);
-extern void func_L02_003FC248(u8 *, s32, s32);
-extern void func_L11_00452DF8(u8 *);
-extern void func_002AB6D8(void *, void *);
-extern void func_L07_00321D68(u8 *, void *);
-extern void func_00282D30(void *, void *, float);
-extern void func_002A3F10(u8 *, s32, s32 *, void *);
-extern void func_002B4090(float, float, float, u8 *, void *);
-extern void func_002833F0(void *, void *);
-extern void func_L11_00452810(u8 *);
-extern void func_L11_004528A0(u8 *);
-extern void func_L00_002E6EF8(void (*)(u8 *), void *);
-
-/* Update the Joba sentry's patrol, alert, attack and launch states. */
-void func_L11_00451178(u8 *object)
-{
- ActorVector target;
- ActorVector work[6];
- ActorVector effect;
- ActorVector near_point;
- ActorVector far_point;
- ActorVector velocity;
- ActorVector particle;
- s32 count;
- ActorVector *out;
- ActorVector *scale;
- ActorVector *tmpl;
- float *spin;
- ActorVector *rows;
- u8 *axis;
- u8 *state;
- u8 *pos;
- float heading;
- float angle;
- float range;
-
- state = *(u8 **)(object + 0x68);
- if (object[0x20] == 1 || (*(s16 *)(state + 0x2D8) && !D_L11_001A8FF4 && object[0x20] != 12 && object[0x20] != 11))
- return;
- func_L11_00452450(object);
- if (object[0x20] == 1 || (*(s16 *)(state + 0x2D8) && !D_L11_001A8FF4))
- return;
- if (*(s16 *)(state + 0x2D2) == 0 && object[0x95] == 3) {
- func_002A7750(object, 4, 2);
- if (func_002A77E0(2)) {
- if (object[0x43] != 2)
- func_002A7470(object, 2, 0, 10);
- } else if (object[0x43] != 3)
- func_002A7470(object, 3, 0, 10);
- }
- if (*(u8 **)(state + 0x2C4))
- target.quad = *(TI128 *)(*(u8 **)(state + 0x2C4) + 0x10);
- else
- target.quad = *(TI128 *)(D_00189E20 + 0x80);
- if (*(s16 *)(state + 0x2D8)) {
- func_00282CF0(&work[0], &target, object + 0x10);
- pos = object + 0x10;
- heading = 0.0f;
- angle = func_002B04E0(object, &work[0], state + 0x230);
- } else {
- pos = object + 0x10;
- heading = func_00283248(target.f[0] - *(float *)(object + 0x10), target.f[1] - *(float *)(object + 0x14));
- angle = func_00283C80(heading, *(float *)(object + 0xF8));
- }
- range = func_00282E48(pos, &target);
- if (object[0x95] == 2) {
- u8 *goal;
- if (func_002B3890(object)) {
- if (object[0x43] != 9)
- func_002A7470(object, 9, 0, 10);
- object[0xBC] = 0;
- func_002B60B0(object, 0.0044444446f, 0.13333334f, 0.0005817765f, 0.052359886f);
- func_L11_00452F30(object, func_L11_004417E8(*(u8 **)(object + 0xB8)));
- *(u16 *)(object + 0x96) = 10000;
- }
- if (object[0xBC] == 1 && *(u16 *)(object + 0x96) >= 301) {
- switch (func_002A77E0(5)) {
- case 0: if (object[0x43] != 2) func_002A7470(object, 2, 0, 10); break;
- case 1: if (object[0x43] != 3) func_002A7470(object, 3, 0, 10); break;
- case 2: if (object[0x43] != 5) func_002A7470(object, 5, 0, 10); break;
- case 3: if (object[0x43] != 6) func_002A7470(object, 6, 0, 10); break;
- }
- *(u16 *)(object + 0x96) = func_002A77E0(100);
- }
- if (*(s16 *)(state + 0x2D2)) {
- func_002A7750(object, 5, 3);
- if (func_002A77E0(2)) {
- if (object[0x43] != 1) func_002A7470(object, 1, 0, 10);
- } else if (object[0x43] != 4) func_002A7470(object, 4, 0, 10);
- }
- if (angle > 2.7925272f)
- func_L11_004530C0(object);
- goal = state + 0x2A0;
- if (!object[0xBC] && func_00282E80(pos, goal) < 0.5f)
- object[0xBC] = 1;
- else if (*(s16 *)(state + 0x2D8))
- func_L02_00333438(object, goal);
- else
- func_L01_00321178(object, goal);
- }
- switch (object[0x20]) {
- case 3:
- if (func_002B3868(object)) {
- object[0xBC] = 0;
- if (object[0x43] != 2) func_002A7470(object, 2, 0, 0);
- *(u16 *)(object + 0x96) = 0;
- }
- if (object[0xBC] == 0 && *(s16 *)(state + 0x2D4) < (s32)*(u16 *)(object + 0x96)) {
- if (object[0x43] != 9) func_002A7470(object, 9, 0, 10);
- object[0xBC] = 1;
- } else if (object[0xBC] == 1) {
- if (func_L11_00441808(*(u8 **)(object + 0xB8), pos))
- func_002A7750(object, 4, 2);
- func_L01_00321178(object, func_L11_004417E8(*(u8 **)(object + 0xB8)));
- }
- break;
- case 5:
- if (object[0x60] & 2)
- func_002A7750(object, 7, -1);
- break;
- case 6:
- if (func_002B3868(object)) {
- if (object[0x43] != 8) func_002A7470(object, 8, 0, 10);
- func_002B60B0(object, 0.0044444446f, 0.13333334f, 0.003490659f, 0.4188791f);
- }
- func_002B5278(object, heading);
- if (object[0x60] & 2)
- func_002A7750(object, 7, -1);
- break;
- case 7:
- if (func_002B3868(object)) {
- if (object[0x43] != 9) func_002A7470(object, 9, 0, 15);
- *(u16 *)(state + 0x2D6) = 0;
- }
- if (D_L11_001B1D24 < range)
- func_002B60B0(object, 0.0044444446f, 0.13333334f, 0.0005817765f, 0.052359886f);
- else if (range < D_L11_001B1D20)
- func_002B60B0(object, 0.0011111111f, 0.033333335f, 0.0005817765f, 0.052359886f);
- if (object[0x42] != 12 && range < D_L11_001B1D18 && angle < 1.5707965f) {
- if (object[0x43] != 12) func_002A7470(object, 12, 0, 10);
- } else if (object[0x42] == 12 && D_L11_001B1D1C < range) {
- if (object[0x43] != 9) func_002A7470(object, 9, 0, 15);
- } else if (object[0x42] != 12 && D_L11_001B1D04 < range && range < D_L11_001B1D08 && !func_002A77E0(D_L11_001B1D0C)) {
- if (func_00282978(state + 0x2D6) && object[0x31]) {
- u8 *other;
- s16 id;
- if (!func_002761F8(pos, &target, 1, object, 0) ||
- ((other = D_L11_001C0D18[0]) != 0 && (id = *(s16 *)(other + 0xAA)) != 0xD98 && id != 0xDCF &&
- id != 0xDF4 && id != 0xDF7 && id != 0xDF9 && id != 0xD37)) {
- func_002A7750(object, 9, -1);
- *(u16 *)(state + 0x2D6) = D_L11_001B1D10;
- }
- }
- } else if (range > 2.8f) {
- if (*(s16 *)(state + 0x2D8))
- func_L02_00333438(object, &target);
- else if (!func_L11_004530E0(object))
- func_002B4FF0(object, target.f, *(float *)(state + 0x2C8));
- } else if (*(s16 *)(state + 0x2D8)) {
- func_00282CF0(&work[0], &target, pos);
- func_L02_00333480(object, pos, &work[0]);
- } else
- func_002B50E0(object, pos, heading);
- if (angle > 2.7925272f)
- func_L11_004530C0(object);
- if (object[0x42] == 12) {
- work[1].quad = 0;
- work[1].f[0] = *(float *)(object + 0x10);
- work[1].f[1] = *(float *)(object + 0x14);
- work[1].f[2] = *(float *)(object + 0x18);
- work[1].f[3] = 2.0f;
- work[0].quad = work[1].quad;
- if (object[0x31] || func_00283DB8(work, 128.0f) > 0) {
- s32 i;
- s32 j;
- func_0029FC20(object, 4, &work[2]);
- qcopy(&work[1], &work[3]);
- func_0029FCC0(object, 4, &effect);
- func_00282F20(&near_point, work + 1, 0.85f);
- func_00282F20(&far_point, work + 1, 1.6f);
- func_00282F20(&velocity, work + 3, 0.25f);
- func_00282F20(work + 1, work + 1, 3.0f);
- func_00282CC0(&near_point, &near_point, &effect);
- func_00282CC0(&far_point, &far_point, &effect);
- near_point.f[2] -= 0.1f;
- tmpl = &particle;
- far_point.f[2] -= 0.1f;
- i = 2;
- do {
- float first;
- float second;
- float third;
- func_002ACB48(effect.f, 0.1f);
- first = func_002A7878(0.2f, 0.4f) * 210000.0f;
- second = func_002A7878(1.0f, 1.5f) * 210000.0f;
- third = func_002A78D8(0.0f, 0.01f);
- func_002C65A0(&effect, &velocity, 9, 0x808080, 30, 127, 32, first, second, third, 0);
- if (range < 3.0f) {
- func_002A8C28(object, &effect, 0.3f, 0x4000001, (float)state[0x34], 1.0f, 0, 1, 0);
- func_002A8C28(object, &near_point, 0.3f, 0x4000001, (float)state[0x34], 1.0f, 0, 1, 0);
- func_002A8C28(object, &far_point, 0.3f, 0x4000001, (float)state[0x34], 1.0f, 0, 1, 0);
- }
- } while (--i >= 0);
- particle.f[0] = 0.0f;
- particle.f[1] = 0.0f;
- particle.f[2] = 0.1f;
- particle.f[3] = 1.0f;
- for (j = 0; j < 2; j++) {
- u8 *part = func_002C16C0(&near_point, tmpl, 15, 60, 0x606060, 3,
- 105000.0f, 21000.0f, 1.0f, -0.0002f, 0.0f);
- if (part) {
- part[2] = *D_L11_00200440.ids;
- part[3] = 0x44;
- if (func_002A7820(0, 1)) {
- u8 *tail;
- u16 alpha;
- part[3] = 0x48;
- alpha = 127;
- *(u32 *)(part + 4) = 0x70707F;
- tail = part + 0x20;
- tail[0x11] = 0x70;
- tail[0x12] = 0x70;
- tail[0x13] = 0x70;
- *(u16 *)(tail + 0x1E) = alpha;
- }
- }
- }
- }
- }
- break;
- case 9:
- if (func_002B3868(object)) {
- object[0xBC] = 0;
- if (object[0x43] != 7) func_002A7470(object, 7, 0, 10);
- }
- if (!object[0xBC] && angle < D_L11_001B1D14 * 0.017453294f) {
- object[0xBC] = 1;
- if (object[0x43] != 17) func_002A7470(object, 17, 0, 10);
- } else if (*(s16 *)(state + 0x2D8))
- func_L11_00340500(object, &target);
- else
- func_002B5278(object, heading);
- if (object[0xBC] == 1) {
- float dist = func_0029F530(object);
- if (func_002AEB60(object, 17.0f)) {
- work[2].quad = 0;
- work[2].f[2] = 1.0f;
- func_0029FCC0(object, 4, &work[0]);
- if (*(u8 **)(state + 0x2C4))
- func_00283098(&work[2], &work[2], *(u8 **)(state + 0x2C4) + 0xC0);
- else
- func_00283098(&work[2], &work[2], D_0018C0B0 + 0xC0);
- func_00282CC0(&work[2], &work[2], &target);
- func_00282CF0(&work[1], &work[2], &work[0]);
- func_00282F20(&work[1], &work[1], 0.33333334f);
- func_003280C8((float)state[0x35], object, &work[1], &work[0], 0, 0);
- } else if (dist < 17.0f && object[0x43] == 17) {
- float first;
- float second;
- func_0029FCC0(object, 4, &work[0]);
- first = func_002A7878(1.0f, 1.5f) * 210000.0f;
- second = func_002A7878(0.2f, 0.4f) * 210000.0f;
- func_002C65A0(&work[0], D_L11_001A8F20, 4, 0x808080, 9, 0x4F, 0, first, second, func_002A78D8(0.0f, 0.1f), 0);
- }
- if (object[0x60] & 2) {
- if (D_0018C2BC == object || !func_002A77E0(6))
- func_002A7750(object, 10, -1);
- else
- func_002A7750(object, 7, -1);
- }
- }
- break;
- case 10:
- if (func_002B3868(object) && object[0x43] != 11)
- func_002A7470(object, 11, 0, 7);
- if (object[0x60] & 2)
- func_002A7750(object, 7, -1);
- break;
- case 11: {
- s32 flags = func_002B1C20(object, state + 0x50);
- if (object[0x42] == 15) {
- if (!func_002A77E0(30) || (flags & 1)) {
- if (func_0029F530(object) < 15.0f && *(s16 *)(state + 0x2D8) && !func_002A77E0(2))
- func_002A7750(object, 12, -1);
- else {
- func_L02_003FC248(object, 0, -1);
- func_L11_00452DF8(object);
- }
- }
- } else if (flags & 2)
- func_002A7750(object, 7, 3);
- break;
- }
- case 12: {
- u8 *launch = state + 0x1A0;
- s32 started = func_002B3868(object);
- spin = (float *)(state + 0x2B0);
- out = &work[1];
- axis = object + 0xF0;
- scale = &work[4];
- rows = (ActorVector *)(object + 0xC0);
- if (started) {
- s32 k;
- func_00282CF0(launch, func_L11_004417E8(*(u8 **)(object + 0xB8)), pos);
- *(float *)(state + 0x1A8) = 0.0f;
- func_00282F20(launch, launch, func_002A7878(3.0f, 5.0f) * 0.016666668f);
- *(s16 *)(state + 0x2D8) = 0;
- for (k = 0; k < 3; k++) {
- spin[k] = func_002A7878(240.0f, 720.0f) * 0.00029088825f;
- qcopy(&work[k], &rows[k]);
- }
- func_002AB6D8(work, axis);
- if (object[0x43] != 18) func_002A7470(object, 18, 0, 30);
- *(u16 *)(object + 0x34) &= 0xEFFF;
- }
- *(float *)(state + 0x1A8) -= *(float *)(state + 0x12C);
- func_L07_00321D68(object, launch);
- func_00282D30(spin, spin, 0.98f);
- {
- float f = *(float *)(object + 0x2C) * 0.0009765625f;
- count = 3;
- func_002A3F10(object, 1, &count, out);
- func_00282D30(&work[0], scale, f);
- }
- func_002B4090(*(float *)(state + 0x2B0), *(float *)(state + 0x2B4), *(float *)(state + 0x2B8), object, work);
- func_002833F0(rows, axis);
- if (*(float *)(object + 0x18) < ((float *)func_L11_004417E8(*(u8 **)(object + 0xB8)))[2] + 5.0f ||
- !func_002A77E0(450) || *(u16 *)(object + 0x96) >= 301) {
- func_L02_003FC248(object, 0, -1);
- func_L11_00452DF8(object);
- }
- break;
- }
- }
- func_L11_00452810(object);
- func_L00_002E6EF8(func_L11_004528A0, object);
-}
diff --git a/nonmatching/levels/func_L11_00444400.c b/nonmatching/levels/func_L11_00444400.c
index 911a8529d..3e4da74bf 100644
--- a/nonmatching/levels/func_L11_00444400.c
+++ b/nonmatching/levels/func_L11_00444400.c
@@ -1,3 +1,36 @@
+/* 5 November 2026, two-agent pass:
+ * - Boundary: with func_L02_003FE768's one-pass conversion groups (allowed by COMPILER-NOTES'
+ * source methods; that function now matches) on the box x/y/h and the label y, every call's
+ * argument setup follows its chains as in retail: 4692/4684 bytes, 458 instructions aligned.
+ * - Left, all register allocation: 0.5 in $f22 (the label -1/0 constants are block-local and
+ * local-alloc takes $f20/$f21 first; retail's are global, so a block boundary existed there
+ * during allocation); rect-address reaching registers keep $fp/$s6/$s7 where retail's reload
+ * evicts them (reload here never needs more than $v0/$v1); the constant 3 follows cse's
+ * 10-block path length. The only construct found that fixes 0.5 and the save set is the
+ * twin's dead `if (mode == 1) y = 1.0f;` arm (580 aligned), which blocker 26 rules out.
+ */
+/* 5 November 2026, third pass (retained body unchanged, 4640/4684 bytes). Measured:
+ * - This compiler's pass order after reload is flow2 -> sched2 -> jump2 (cross-jumping, jumps to
+ * the next insn) -> dbr. Retail's boundary before each call's argument setup is therefore a
+ * basic-block boundary that exists at sched2 and is gone by jump2 or dbr, without leaving a test.
+ * - `if (D_L11_001B3E00 == 1)` around the box call with identical arms (D_L11_001B3E00 being the
+ * word before the scale, as the mode word D_L02_001B3E00 is in level 2) does give the boundary:
+ * all three chains finish before the argument loads. But the arms are not merged, so the
+ * function grows to 5144 bytes.
+ * - An inline `hud_scaled(float v)` returning (int)(v * D_L11_001B3E04 + 0.5f) gives retail's
+ * chain shape (lwc1/cvt.s.w, value $f0, scale $f1); with the twin's dead `if (mode == 1)` arm
+ * it also gives retail's full save set ($s3, $s4, $s6, $s7 saved), as func_L02_003FF6E8's note
+ * explains, but no boundary (the test goes at flow2).
+ * - Negative: sched1 on (4644 bytes), no sched2 (retail does schedule: data and 0.5 move),
+ * volatile layout globals (lw/mtc1 loads).
+ */
+/* 5 November 2026: retained body unchanged (4640/4684 bytes). New negative: the boxes as a
+ * `static __inline__` helper taking the rounded coordinates as arguments (so they are
+ * evaluated before the inlined call) do not make retail's boundary; sched2 then interleaves
+ * all three chains. In haifa-sched a LOOP_BEG/END note in mid-block is a full barrier that
+ * does not end the block, which is why the rejected do-while (0) diagnostic worked; a natural
+ * construct with that effect is still not identified.
+ */
/* 4 November 2026 actual-contract review; retained near miss, not linked.
* USA v1.01 check.py: 4640/4684 bytes, 0/1 placements; first difference size mismatch.
* Profile: -O2 -G8 -ffunction-sections -fno-schedule-insns.
diff --git a/nonmatching/levels/func_L11_00453358.c b/nonmatching/levels/func_L11_00453358.c
index 2aa859dcb..f08c185ec 100644
--- a/nonmatching/levels/func_L11_00453358.c
+++ b/nonmatching/levels/func_L11_00453358.c
@@ -1,12 +1,95 @@
-/* 4 November 2026 source audit: removed the ten dead float stores used only to raise the instruction count.
- * The previous controlled result below is historical and cannot qualify this
- * revised draft. Production remains assembly; no linked or native credit.
- * Current cleaned body is uncompiled: inherited standalone declarations/types
- * are incomplete. Recover them and review the state machine before new trials.
+/* 4 November 2026 contract review: restore the integer returns of func_002A8C28,
+ * func_002A8D70 and func_L07_00321D68, and the float return of func_002AA548, from their
+ * matched definitions. The ignored results do not change the generated body: the retained
+ * -O2 -G8 -ffunction-sections candidate passes 1/1 in both USA releases. PAL has no canonical
+ * identity. It remains a held diagnostic because of the case-9 dead store below.
*/
-/*
- * func_L11_00453358: strict build rejected it: ten consecutive dead stores to dist with counting values (dist = 2.0f ... 11.0f), padding that only raises gcse's insn count; overseer ruling: not landable. Near miss without them: only four spill slots permuted
+/* func_L11_00453358 (4,524 B, 1 place), 5 November 2026: exact in tools/check.py (1 of 1) and in
+ * tools/build.py all --strict, but NOT landable under AGENTS.md / MATCHING-BLOCKERS blocker 26:
+ * case 9's `height = 0.0f;` is a dead store whose only job is to keep a label between case 9's
+ * func_002A7750(self, 11, -1) and its break until jump2, so jump2 does not cross-jump that tail
+ * into case 13's (14, -1) one (without it: 4516 bytes). The merged twin func_L02_0040BE08 uses
+ * the same store; a ruling on both, or a natural construct that leaves that label, is needed.
+ * Tried without success: case 9 with an early break, and three inline-helper shapes for the
+ * transition (an inlined body's return label is deleted too early).
+ * Everything else is ordinary C: own -G8 unit with the default -g8 profile; the anim() and
+ * turn() inline helpers give gcse retail's count (S 543), which orders the four spilled
+ * reaching registers (pos, self+0xC0, self+0xF0, pv+0x1B0) as retail does.
*/
+/* func_L11_00453358: the Joba drone's state machine (patrol path, chase, grab, release, fall).
+ * Own -G8 unit: the level gate D_L11_001A8FF4 and D_L11_001B1D40 are $gp-relative.
+ * The four spilled pointers (self+0x10, self+0xC0, self+0xF0, pv+0x1B0) are gcse reaching
+ * registers; their stack slots follow gcse's hash-bucket order, which depends on the insn
+ * count at gcse. The anim() and turn() helpers are inlined; their expansion gives retail's
+ * count (S 543) without changing any instruction.
+ * Case 9's `height = 0.0f;` is the same construct as case 9 of the matched twin
+ * func_L02_0040BE08: it keeps case 9's tail apart from case 13's in jump2's cross-jumping.
+ */
+#include "common.h"
+
+typedef int TI128 __attribute__((mode(TI)));
+typedef struct { TI128 q; } Vec;
+typedef union { u8 b[0x2600]; s16 h[0x1300]; s32 i[0x980]; f32 f[0x980]; u8 *ptr[0x980]; TI128 q[0x260]; } Hero;
+
+extern char D_00189E20[];
+extern s32 D_L11_001A8FF4 __attribute__((sda));
+extern f32 D_L11_001B1D40 __attribute__((sda));
+extern f32 D_L11_001B1D44;
+extern f32 D_L11_001B1D48;
+extern u8 *D_L11_001FF580[];
+
+extern int func_002761F8(void *, void *, int, void *, void *);
+extern s32 func_00277258(void *, s32, void *, float);
+extern float func_00282C48(float);
+extern void func_00282CC0(void *, void *, void *);
+extern void func_00282CF0(void *, void *, void *);
+extern void func_00282D30(void *, void *, float);
+extern float func_00282E48(void *, void *);
+extern float func_00282E80(void *, void *);
+extern void func_00282F20(void *, void *, float);
+extern float func_00283248(float, float);
+extern void func_002833F0(void *, void *);
+extern float func_00283B98(float, float);
+extern void func_002A677C(void *);
+extern void func_002A7470(void *, int, int, int);
+extern void func_002A7750(void *, int, int);
+extern int func_002A77E0(int);
+extern float func_002A7878(float, float);
+extern void func_002A7A00(void *, float, float);
+extern int func_002A8C28(void *, void *, int, float, float, float, int, int, int);
+extern int func_002A8D70(void *, int, int, float, float, float, int, int, int);
+extern float func_002AA150(float, float, float);
+extern float func_002AA548(void *, void *, float, float, float, float);
+extern void func_002AB6D8(void *, void *);
+extern int func_002B3868(u8 *);
+extern int func_002B3890(u8 *);
+extern void func_002B4090(void *, void *, float, float, float);
+extern int func_002B4FF0(void *, void *, float);
+extern void func_002B60B0(u8 *, float, float, float, float);
+extern char *func_002C2210(void *, float, float, int, unsigned int, int, int, void *, float, int);
+extern int func_L01_00321178(void *, void *);
+extern void func_L02_00333438(void *, void *);
+extern int func_L07_00321D68(void *, void *);
+extern u8 *func_L11_004417E8(u8 *);
+extern int func_L11_00441808(u8 *, void *);
+extern void func_L11_00441A38(u8 *, int, int);
+extern void func_L11_00454508(u8 *);
+extern void func_L11_00454AE0(u8 *);
+extern void func_L11_00454DD8(u8 *);
+
+/* Start animation a (blend b) unless it is already playing. */
+static __inline__ void anim(u8 *self, int a, int b)
+{
+ if (self[0x43] != a)
+ func_002A7470(self, a, 0, b);
+}
+
+/* Movement tuning with the shared turn limits. */
+static __inline__ void turn(u8 *self, float rate, float speed)
+{
+ func_002B60B0(self, rate, speed, 0.001163553f, 0.10471977f);
+}
+
void func_L11_00453358(u8 *self)
{
f32 v00[4];
@@ -17,6 +100,7 @@ void func_L11_00453358(u8 *self)
u8 *pv;
u8 *target;
f32 dist = 0.0f;
+ f32 height;
pv = *(u8 **)(self + 0x68);
if (self[0x20] == 1)
@@ -45,7 +129,7 @@ void func_L11_00453358(u8 *self)
switch (self[0x20]) {
case 3: {
if (func_002B3868(self) != 0) {
- func_002B60B0(self, 0.027777778f, 1.6666667f, 0.001163553f, 0.10471977f);
+ turn(self, 0.027777778f, 1.6666667f);
self[0xBC] = 0;
*(u16 *)(self + 0x96) = 0;
}
@@ -65,8 +149,7 @@ void func_L11_00453358(u8 *self)
*(u16 *)(self + 0x96) = 0;
if (*(u16 *)(self + 0x96) < 0x1A)
break;
- if (self[0x43] != 0)
- func_002A7470(self, 0, 0, 5);
+ anim(self, 0, 5);
func_002A7750(self, 8, 3);
break;
case 7: {
@@ -84,7 +167,7 @@ void func_L11_00453358(u8 *self)
if (func_002B3868(self) != 0) {
p = path + 0x10;
best = 0;
- func_002B60B0(self, 0.027777778f, 3.3333335f, 0.001163553f, 0.10471977f);
+ turn(self, 0.027777778f, 3.3333335f);
*(f32 *)(pv + 0x1C8) = func_002A7878(1.0f, 3.0f);
d = func_00282E48((self + 0x10), p);
for (i = 0; i < *(s32 *)path - 1; i++, p += 0x10) {
@@ -142,7 +225,7 @@ void func_L11_00453358(u8 *self)
case 8: {
s32 flag;
if (func_002B3868(self) != 0)
- func_002B60B0(self, 0.011111111f, 1.3333334f, 0.001163553f, 0.10471977f);
+ turn(self, 0.011111111f, 1.3333334f);
flag = 0;
if (*(s16 *)(pv + 0x1FC) != 0) {
if (4.0f < dist) {
@@ -155,10 +238,10 @@ void func_L11_00453358(u8 *self)
}
flag = dist < 4.0f;
} else {
- f32 z = *(f32 *)(target + 8) + 0.5f;
- func_002AA548(self + 0x18, pv + 0x108, z, 0.0055555557f, 0.027777778f, 0.16666667f);
+ height = *(f32 *)(target + 8) + 0.5f;
+ func_002AA548(self + 0x18, pv + 0x108, height, 0.0055555557f, 0.027777778f, 0.16666667f);
func_002B4FF0(self, v00, *(f32 *)(pv + 0x1D0));
- if (func_00282C48(*(f32 *)(self + 0x18) - z) < 0.1f &&
+ if (func_00282C48(*(f32 *)(self + 0x18) - height) < 0.1f &&
func_00282E80((self + 0x10), &((Hero *)D_00189E20)->q[0x80 / 16]) < 2.0f)
flag = 1;
}
@@ -170,19 +253,19 @@ void func_L11_00453358(u8 *self)
break;
}
case 9:
- if (func_002B3868(self) != 0 && self[0x43] != 2)
- func_002A7470(self, 2, 0, 3);
+ if (func_002B3868(self) != 0)
+ anim(self, 2, 3);
if (self[0x60] & 2) {
- if (self[0x43] != 3)
- func_002A7470(self, 3, 0, 5);
+ anim(self, 3, 5);
func_002A7750(self, 11, -1);
}
+ height = 0.0f;
break;
case 11: {
s32 k;
f32 lim = 0.0f;
if (func_002B3890(self) != 0) {
- func_002B60B0(self, 0.0022222223f, 0.06666667f, 0.001163553f, 0.10471977f);
+ turn(self, 0.0022222223f, 0.06666667f);
*(u16 *)(self + 0x96) = 0;
}
*(f32 *)(self + 0x48) = 1.75f;
@@ -207,13 +290,11 @@ void func_L11_00453358(u8 *self)
}
if (((Hero *)D_00189E20)->ptr[0x249C / 4] == self) {
func_002A7750(self, 12, -1);
- if (self[0x43] != 4)
- func_002A7470(self, 4, 0, 5);
+ anim(self, 4, 5);
*(f32 *)(self + 0x48) = 1.0f;
}
if (func_00282E48((self + 0x10), &((Hero *)D_00189E20)->q[0x80 / 16]) > 4.0f && *(u16 *)(self + 0x96) >= 0x79) {
- if (self[0x43] != 5)
- func_002A7470(self, 5, 0, 15);
+ anim(self, 5, 15);
func_002A7750(self, 4, -1);
*(f32 *)(self + 0x48) = 1.0f;
}
@@ -222,15 +303,13 @@ void func_L11_00453358(u8 *self)
case 12:
if (!(self[0x60] & 2))
break;
- if (self[0x43] != 0)
- func_002A7470(self, 0, 0, 15);
+ anim(self, 0, 15);
func_002A7750(self, 8, -1);
break;
case 10:
if (*(u16 *)(self + 0x96) >= 0xF) {
func_002A7750(self, 9, -1);
- if (self[0x43] != 2)
- func_002A7470(self, 2, 0, 2);
+ anim(self, 2, 2);
*(f32 *)(self + 0x48) = 1.0f;
}
qcopy(v10, (self + 0x10));
@@ -247,8 +326,7 @@ void func_L11_00453358(u8 *self)
func_002A8C28(self, (self + 0x10), 0x10000, 0.5f, (f32)pv[0x34], 1.0f, 0, 1, 0);
if (*(f32 *)(pv + 0x20) > 0.0f) {
func_002A7750(self, 8, 3);
- if (self[0x43] != 0)
- func_002A7470(self, 0, 0, 5);
+ anim(self, 0, 5);
} else if (*(s16 *)(pv + 0x1FC) != 0 && func_002A77E0(2) == 0) {
func_002A7750(self, 14, -1);
} else {
diff --git a/src/levels/11_joba/L11_004465F8.c b/src/levels/11_joba/L11_004465F8.c
index 06946ba0b..c252ecb48 100644
--- a/src/levels/11_joba/L11_004465F8.c
+++ b/src/levels/11_joba/L11_004465F8.c
@@ -601,7 +601,7 @@ void func_L11_00450748(char *p, float scale)
func_00282D30_2(p + 48, p + 48, scale);
}
-INCLUDE_ASM("asm/levels", func_L11_00451178);
+/* func_L11_00451178 is in L11_00451178-gu-g8.c. */
/* func_L11_00452450 is defined in L11_004465F8-g8.c. */
diff --git a/nonmatching/levels/func_L11_00451178.c b/src/levels/11_joba/L11_00451178-gu-g8.c
similarity index 72%
rename from nonmatching/levels/func_L11_00451178.c
rename to src/levels/11_joba/L11_00451178-gu-g8.c
index 795c84c2d..e05b078b3 100644
--- a/nonmatching/levels/func_L11_00451178.c
+++ b/src/levels/11_joba/L11_00451178-gu-g8.c
@@ -1,63 +1,19 @@
-/* 4 November 2026 actual-contract review; retained near miss, not linked.
- * USA v1.01 check.py: 4824/4824 bytes, 0/1 placements; first difference 0x1088.
- * Profile: -O2 -G8 -ffunction-sections -frac2-gcse-unsplit-edges.
- * Five helpers keep their defined return contracts: the four level movement helpers return int
- * and func_003280C8 returns a pointer. These ignored-result repairs leave the six case-12
- * PRE/spill words unchanged.
- * Earlier measurements below are historical where superseded by this note.
- */
-/* func_L11_00451178 (4,824 B, 1 place): finisher F3, 2026-10-26. The Joba twin of
- * func_L02_0040A4A8 (same particle loop: alpha = 127 after the 0x48 store, the template through
- * tmpl; see that unit's header). Profile -O2 -G8 -ffunction-sections -frac2-gcse-unsplit-edges.
- * 1206 of 1206 instructions, 4824 bytes; every instruction equal except 7 words in case 12.
- * What closed it:
- * - one function-scope scratch array work[6] at sp+0x10: retail reuses sp+0x10..0x5F for the
- * head's aim vector, case 7's bounds[2] + matrix[4], case 9's hand/shot/dir and case 12's
- * 5-quad buffer. Block-scoped vectors cannot share it here: an address-taken block local is
- * preserved to the enclosing level by every following expression statement (function.c
- * preserve_temp_slots), so the head's vector would stay live and push the frame to 0x180;
- * - pos = object + 0x10 set in each head arm, after the call in the state->0x2D8 arm (retail's
- * compute-then-copy);
- * - case 5 before case 6, each with its own movement-flag test (no fall-through);
- * - -G8 sda floats for the gp block D_L11_001B1D04..001B1D24, D_L11_001A8F20 as MACRO_ADDR
- * (one la insn at the call), D_L11_001C0D18 as an incomplete array, the boot pointers nosda;
- * - case 12's row copy as qcopy() in a counted loop: a TI copy lets loop.c reverse the loop;
- * - the spill slots: retail's sp+0xC4 &work[1], 0xC8 &work[4], 0xCC tmpl, 0xD0 state+0x2B0,
- * 0xD4 object+0xC0, 0xD8 object+0xF0 is pseudo-number order (none gets a hard register from
- * global; reload1.c gives slots in pseudo order). As gcse PRE copies the case-12 addresses
- * come in hash-bucket order (max_cuid 1175, 587 buckets) and always after tmpl, so they are
- * function-scope pointers declared out, scale, tmpl, spin, rows, axis.
- * Left (7 words, case 12 head): retail sets the spilled pointers in the order spin, out, axis,
- * scale, rows (stores to 0xD0, 0xC4, 0xD8, 0xC8, 0xD4); here out and scale are always adjacent
- * (0xD0, 0xC4, 0xC8, 0xD8, 0xD4 at best; all 120 orders of the five assignments give 12
- * sequences, none retail's; assigning them before the func_002B3868 call moves the frame ones
- * out of the block), and the state+0x1A8 clear is scheduled after the lw $s0 reload where
- * retail has it before.
- * v1932 (opus-1, 2026-10-31), recompiled on v2/c05: this body still gives 4824/4824 bytes, 0/1
- * places, 6 words differ (+0x1088..0x1094, +0x10D4/+0x10EC). Why single-set locals cannot fix
- * it: haifa-sched adjust_priority raises an insn that becomes ready when the func_002B3868 call
- * issues to the call's priority if birthing_insn_p (dest live, REG_N_SETS == 1). out and scale
- * are $1-based, so they depend on the call ($1 = $at is call-clobbered) and get boosted ahead of
- * axis. Retail's pointers are gcse PRE reaching registers (REG_N_SETS > 1, never boosted), as in
- * matched func_L11_0044AFC8. The natural wording (drop out/scale/tmpl/spin/rows/axis, write the
- * addresses directly, &particle for tmpl; the v1925 wip) gives retail's exact instruction order,
- * the 0x1A8 clear included. Only 15 spill-slot offsets then differ, because PRE numbers reaching
- * regs in bucket order, bucket = (14029 + regno + const) % S, S = (max_cuid/2)|1. That wording
- * has max_cuid 1176 (S 589): $1+176 wraps below $1+32. Retail's slots need S in 575..585, i.e.
- * max_cuid 1148..1171, 5 to 28 fewer insns after cse1 (or S 599 with state's pseudo >= 88, which
- * means 20+ more). Checked: deleting three object[0xBC] = 1 stores (max_cuid 1170) gives retail's
- * slots with no other change. Count-neutral: pointer locals inside one EBB, split head tests,
- * int vs float 0x1A8 clear, a byte-offset qcopy in the row loop. These change code: hoisted
- * &work[0]/&work[2] locals in case 9, a ?: argument for case 9's func_002A7750.
- * PERMUTE-FLAGS: -O2 -G8 -ffunction-sections -frac2-gcse-unsplit-edges
- *
- * closer D, 1 November 2026: the natural wording scores 19 words here. Merging the `object[0x60] & 2`
- * tails of cases 5, 6 and 10 with a shared label would lower max_cuid, but retail keeps those tails
- * apart (4,784 to 4,808 bytes), so the 5 to 28 fewer insns have to come from elsewhere.
- * Dropping the started, launch and goal locals scores 18 words; the first difference stays at +0xA24.
- *
- * closer D, later: MACRO_ADDR on D_00189E20 or D_L11_001C0D18 (one fewer insn each, same-register lui/addiu) moves
- * code, 29 to 57 words. They are not the missing 5 to 28 insns.
+/* func_L11_00451178: the Joba sentry's patrol, alert, attack and launch states; the twin of
+ * func_L02_0040A4A8 (L02_0040A4A8-gu.c). Own unit for -frac2-gcse-unsplit-edges.
+ * - work[6] is one function-scope scratch array: retail reuses sp+0x10..0x5F across the head
+ * and cases 7, 9 and 12.
+ * - Case 7 passes the particle template through tmpl, set just before the template is filled.
+ * Its short life wins it a saved register in global-alloc, which reload then takes back, so
+ * its slot (0xCC) is assigned with case 12's reaching registers (state+0x2B0, object+0xC0,
+ * object+0xF0), in pseudo order after &work[1] and &work[4] (0xC4, 0xC8). Through a register,
+ * the template also keeps the particle loop at 46 insns, so loop.c hoists all seven
+ * constants in its first pass (see the twin's header).
+ * - Case 12 sets spin = state + 0x2B0 before its loop, as func_L11_0044AFC8 does: the address
+ * then enters gcse's expression table before object+0xF0, which orders the PRE copies.
+ * - turn() is inlined; with it gcse sees retail's insn count (S 583), and the bucket order of
+ * the reaching registers gives retail's spill slots.
+ * - Case 11 preserves func_002B1C20's unsigned 64-bit return. Its early exit keeps the
+ * low-bit test in control flow; the compound OR spelling adds a 32-bit normalization.
*/
#include "common.h"
@@ -125,7 +81,7 @@ extern float func_0029F530(u8 *);
extern s32 func_002AEB60(u8 *, float);
extern void func_00283098(void *, void *, void *);
extern void *func_003280C8(float, u8 *, void *, void *, s32, s32);
-extern s32 func_002B1C20(u8 *, void *);
+extern unsigned long func_002B1C20(u8 *, void *);
extern void func_L02_003FC248(u8 *, s32, s32);
extern void func_L11_00452DF8(u8 *);
extern void func_002AB6D8(void *, void *);
@@ -138,6 +94,12 @@ extern void func_L11_00452810(u8 *);
extern void func_L11_004528A0(u8 *);
extern void func_L00_002E6EF8(void (*)(u8 *), void *);
+/* Movement tuning with the sentry's usual turn limits. */
+static __inline__ void turn(u8 *object, float rate, float speed)
+{
+ func_002B60B0(object, rate, speed, 0.0005817765f, 0.052359886f);
+}
+
/* Update the Joba sentry's patrol, alert, attack and launch states. */
void func_L11_00451178(u8 *object)
{
@@ -149,12 +111,7 @@ void func_L11_00451178(u8 *object)
ActorVector velocity;
ActorVector particle;
s32 count;
- ActorVector *out;
- ActorVector *scale;
ActorVector *tmpl;
- float *spin;
- ActorVector *rows;
- u8 *axis;
u8 *state;
u8 *pos;
float heading;
@@ -196,7 +153,7 @@ void func_L11_00451178(u8 *object)
if (object[0x43] != 9)
func_002A7470(object, 9, 0, 10);
object[0xBC] = 0;
- func_002B60B0(object, 0.0044444446f, 0.13333334f, 0.0005817765f, 0.052359886f);
+ turn(object, 0.0044444446f, 0.13333334f);
func_L11_00452F30(object, func_L11_004417E8(*(u8 **)(object + 0xB8)));
*(u16 *)(object + 0x96) = 10000;
}
@@ -260,9 +217,9 @@ void func_L11_00451178(u8 *object)
*(u16 *)(state + 0x2D6) = 0;
}
if (D_L11_001B1D24 < range)
- func_002B60B0(object, 0.0044444446f, 0.13333334f, 0.0005817765f, 0.052359886f);
+ turn(object, 0.0044444446f, 0.13333334f);
else if (range < D_L11_001B1D20)
- func_002B60B0(object, 0.0011111111f, 0.033333335f, 0.0005817765f, 0.052359886f);
+ turn(object, 0.0011111111f, 0.033333335f);
if (object[0x42] != 12 && range < D_L11_001B1D18 && angle < 1.5707965f) {
if (object[0x43] != 12) func_002A7470(object, 12, 0, 10);
} else if (object[0x42] == 12 && D_L11_001B1D1C < range) {
@@ -310,7 +267,6 @@ void func_L11_00451178(u8 *object)
func_00282CC0(&near_point, &near_point, &effect);
func_00282CC0(&far_point, &far_point, &effect);
near_point.f[2] -= 0.1f;
- tmpl = &particle;
far_point.f[2] -= 0.1f;
i = 2;
do {
@@ -328,6 +284,7 @@ void func_L11_00451178(u8 *object)
func_002A8C28(object, &far_point, 0.3f, 0x4000001, (float)state[0x34], 1.0f, 0, 1, 0);
}
} while (--i >= 0);
+ tmpl = &particle;
particle.f[0] = 0.0f;
particle.f[1] = 0.0f;
particle.f[2] = 0.1f;
@@ -404,53 +361,50 @@ void func_L11_00451178(u8 *object)
func_002A7750(object, 7, -1);
break;
case 11: {
- s32 flags = func_002B1C20(object, state + 0x50);
+ unsigned long flags = func_002B1C20(object, state + 0x50);
if (object[0x42] == 15) {
- if (!func_002A77E0(30) || (flags & 1)) {
- if (func_0029F530(object) < 15.0f && *(s16 *)(state + 0x2D8) && !func_002A77E0(2))
- func_002A7750(object, 12, -1);
- else {
- func_L02_003FC248(object, 0, -1);
- func_L11_00452DF8(object);
- }
+ if (func_002A77E0(30) && (flags & 1UL) == 0)
+ break;
+ if (func_0029F530(object) < 15.0f && *(s16 *)(state + 0x2D8) && !func_002A77E0(2))
+ func_002A7750(object, 12, -1);
+ else {
+ func_L02_003FC248(object, 0, -1);
+ func_L11_00452DF8(object);
}
- } else if (flags & 2)
+ } else if ((flags & 2UL) != 0)
func_002A7750(object, 7, 3);
break;
}
case 12: {
u8 *launch = state + 0x1A0;
s32 started = func_002B3868(object);
- spin = (float *)(state + 0x2B0);
- out = &work[1];
- axis = object + 0xF0;
- scale = &work[4];
- rows = (ActorVector *)(object + 0xC0);
if (started) {
s32 k;
+ float *spin;
func_00282CF0(launch, func_L11_004417E8(*(u8 **)(object + 0xB8)), pos);
*(float *)(state + 0x1A8) = 0.0f;
func_00282F20(launch, launch, func_002A7878(3.0f, 5.0f) * 0.016666668f);
*(s16 *)(state + 0x2D8) = 0;
+ spin = (float *)(state + 0x2B0);
for (k = 0; k < 3; k++) {
spin[k] = func_002A7878(240.0f, 720.0f) * 0.00029088825f;
- qcopy(&work[k], &rows[k]);
+ qcopy(&work[k], &((ActorVector *)(object + 0xC0))[k]);
}
- func_002AB6D8(work, axis);
+ func_002AB6D8(work, object + 0xF0);
if (object[0x43] != 18) func_002A7470(object, 18, 0, 30);
*(u16 *)(object + 0x34) &= 0xEFFF;
}
*(float *)(state + 0x1A8) -= *(float *)(state + 0x12C);
func_L07_00321D68(object, launch);
- func_00282D30(spin, spin, 0.98f);
+ func_00282D30(state + 0x2B0, state + 0x2B0, 0.98f);
{
float f = *(float *)(object + 0x2C) * 0.0009765625f;
count = 3;
- func_002A3F10(object, 1, &count, out);
- func_00282D30(&work[0], scale, f);
+ func_002A3F10(object, 1, &count, &work[1]);
+ func_00282D30(&work[0], &work[4], f);
}
func_002B4090(*(float *)(state + 0x2B0), *(float *)(state + 0x2B4), *(float *)(state + 0x2B8), object, work);
- func_002833F0(rows, axis);
+ func_002833F0(object + 0xC0, object + 0xF0);
if (*(float *)(object + 0x18) < ((float *)func_L11_004417E8(*(u8 **)(object + 0xB8)))[2] + 5.0f ||
!func_002A77E0(450) || *(u16 *)(object + 0x96) >= 301) {
func_L02_003FC248(object, 0, -1);
diff --git a/src/levels/shared/L02_003C43F0.c b/src/levels/shared/L02_003C43F0.c
index 5b3ff56f4..c3352d6cf 100644
--- a/src/levels/shared/L02_003C43F0.c
+++ b/src/levels/shared/L02_003C43F0.c
@@ -1113,7 +1113,7 @@ void func_L02_003FC248(void *a0, int unused_a1, int unused_a2) {
/* The two entry scalers live in the -g8 sibling: retail reads the table through $gp. */
-INCLUDE_ASM("asm/levels", func_L02_003FE768);
+/* func_L02_003FE768 is in L02_003FE768-g8.c. */
/* Allocates sixteen of sixty with no owner; on success attaches A0 as kind two. */
void func_L02_003FF698(void *a0) {
diff --git a/nonmatching/shared/func_L02_003FE768.c b/src/levels/shared/L02_003FE768-g8.c
similarity index 55%
rename from nonmatching/shared/func_L02_003FE768.c
rename to src/levels/shared/L02_003FE768-g8.c
index dae1889cd..ff7277d88 100644
--- a/nonmatching/shared/func_L02_003FE768.c
+++ b/src/levels/shared/L02_003FE768-g8.c
@@ -1,12 +1,8 @@
-/* 2 November 2026, bounded scheduling check on the pinned workspace compiler:
- * -fno-schedule-insns: 332/332 bytes, 0/2 USA places, same mode-load hoist
- * (checker first mismatch +0xE4). -fno-schedule-insns2: 332/332, 0/2, first
- * mismatch +0x4 and changed prologue/order. Neither scheduler-off profile
- * closes the load ordering; default -G8 retained. No alternate-release or
- * linked build was run for these failed trials.
- */
-/* Retained nonmatching reconstruction, 27 October 2026.
- * Profile: -O2 -G8 -ffunction-sections. Candidate and retail: 332 bytes. USA v1.01, USA v2.00 and PAL each reject both carrying places. The lui/lw of D_001A7B90 is hoisted across four FP conversion operations; all other words and registers agree after relocation. The 64-bit packet value and colour ABI were checked against current definitions. Computing the real right edge before the bottom conversion repaired the remaining tail. Reopen with evidence for the mode-load scheduling; preserve NaN guards and conversion order.
+/* func_L02_003FE768: the boss health bar (shared by Maktar and Joba).
+ * Own -G8 unit. Each screen edge is rounded into t and converted in a one-pass group
+ * (docs/COMPILER-NOTES.md, source methods shared with RAC1): the group's loop notes keep
+ * the D_001A7B90 mode load after the conversions, as retail schedules it. Without the
+ * groups the load is hoisted across them (0 of 2 places).
*/
#include "common.h"
extern void func_002FC560(unsigned int, unsigned long long);
@@ -23,6 +19,7 @@ extern int D_L02_001B134C[] __attribute__((sda));
extern int D_L02_001B1350[] __attribute__((sda));
extern float D_L02_001B3E04[] __attribute__((sda));
extern int D_001A7B90[] __attribute__((sda));
+
void func_L02_003FE768(float fraction)
{
int width;
@@ -31,6 +28,7 @@ void func_L02_003FE768(float fraction)
int bottom;
int left;
int right;
+ float t;
func_002FC560(0x42, 0x8000000044ULL);
if (fraction < 0.0f)
fraction = 0.0f;
@@ -39,12 +37,18 @@ void func_L02_003FE768(float fraction)
color = func_L00_0031C998(D_L02_001B134C[0], D_L02_001B1348, fraction + fraction - 1.0f);
else
color = func_L00_0031C998(D_L02_001B1350[0], D_L02_001B134C[0], fraction + fraction);
- top = (int)((float)D_L02_001B1334[0] * D_L02_001B3E04[0] + 0.5f);
+ t = (float)D_L02_001B1334[0] * D_L02_001B3E04[0] + 0.5f;
+ do {
+ top = t;
+ } while (0);
if (D_001A7B90[0])
top -= D_L02_001B1340;
left = D_L02_001B1330;
right = left + width;
- bottom = (int)((float)D_L02_001B133C[0] * D_L02_001B3E04[0] + 0.5f);
+ t = (float)D_L02_001B133C[0] * D_L02_001B3E04[0] + 0.5f;
+ do {
+ bottom = t;
+ } while (0);
if (D_001A7B90[0])
bottom -= D_L02_001B1340;
func_0028FC48(left, top, right, bottom, color, 0);