phase-36: S104 s104_e5 — func_8018AD74 banked at 0 through the whole-object gate + propagated — launder → a marked empty do-while (cse1 block end, cse.c:8054; sched.c:2428) — Drew ruling (a)

This commit is contained in:
Drew T
2026-09-11 01:54:49 -06:00
parent a1a0a80e29
commit 72d805da34
10 changed files with 694 additions and 181 deletions
@@ -0,0 +1,94 @@
void func_80187DD0(s32 param_1)
{
s32 t1 = *(s32 *)(param_1 + 0x64);
s32 pad[2];
if (*(s16 *)(t1 + 0x36) != *(s16 *)(param_1 + 0x10A)) {
func_8012C218((void *)param_1);
return;
}
{
if ((*(u32 *)(t1 + 0xE0) & 4) != 0) {
func_8012E8A8((u8 *)param_1);
return;
}
{
s32 a2 = *(s32 *)(*(s32 *)(t1 + 0x20) + 0x20);
s32 a3 = param_1 + 0xFC;
if (a2 != 0) {
if ((a2 & 0x1000000) != 0) {
s32 a1 = a2 & 0xFEFFFFFF;
u32 v0;
u32 a0;
u32 v1;
*(u16 *)(param_1 + 0x102) = *(u16 *)(a1 + 0x66);
v0 = *(u32 *)(a1 + 0x60);
a0 = *(u8 *)(a1 + 0x61) | ((v0 & 0xF) << 8);
v1 = (((s32)v0 >> 16) & 0xFF) | ((v0 & 0xF0) << 4);
do { // !FAKE: do-while — flow counts the stores' refs at loop depth 2 (flow.c:1440-1449, :2711 reg_n_refs += loop_depth), which lifts a0's quantity over a1's in qty_compare_1 (local-alloc.c:1598); its LOOP notes also keep sched1 from moving the 0x104 store above v1's or (sched.c:2053-2074), so w's quantity ties the (w & 0xF) one at 13333 and wins on qty number (P36 S104 e5 minimum-lever)
*(u16 *)(param_1 + 0x104) = a0;
*(u16 *)(param_1 + 0x106) = v1;
} while (0);
*(s16 *)(param_1 + 0xFC) = (s8)*(u8 *)(a1 + 0x63);
*(s16 *)(param_1 + 0xFE) = (s8)*(u8 *)(a1 + 0x64);
*(s16 *)(param_1 + 0x100) = (s8)*(u8 *)(a1 + 0x65);
} else {
*(UAlign12 *)(param_1 + 0xFC) = *(UAlign12 *)(a2 + 0x90);
}
*(s32 *)(*(s32 *)(param_1 + 0x20) + 0x20) = a3;
}
if ((*(u32 *)(t1 + 0xE0) & 0x80) != 0) {
if (*(u16 *)(param_1 + 0x34) == 0) {
s16 sVar2 = *(u16 *)(param_1 + 0x108) + 0x40;
*(s16 *)(param_1 + 0x108) = sVar2;
if (sVar2 > 0x200) {
*(u16 *)(param_1 + 0x108) = 0x200;
*(s16 *)(param_1 + 0x34) = *(s16 *)(param_1 + 0x34) + 1;
}
} else {
u16 uVar3 = *(u16 *)(param_1 + 0x108) - 0x80;
*(u16 *)(param_1 + 0x108) = uVar3;
if ((s16)uVar3 < 0) {
*(u16 *)(param_1 + 0x108) = 0;
*(u16 *)(param_1 + 0x34) = 0;
*(u32 *)(t1 + 0xE0) = *(u32 *)(t1 + 0xE0) & ~0x80;
}
}
}
*(u16 *)(a3 + 6) = *(u16 *)(a3 + 6) + *(u16 *)(param_1 + 0x108);
*(u32 *)(param_1 + 4) = *(u32 *)(t1 + 4);
*(u32 *)(param_1 + 8) = *(u32 *)(t1 + 8);
*(u32 *)(param_1 + 0xC) = *(u32 *)(t1 + 0xC);
*(UAlign8 *)(param_1 + 0x50) = *(UAlign8 *)(t1 + 0x50);
{
s32 dst = *(s32 *)(param_1 + 0x20);
s32 src = *(s32 *)(t1 + 0x20);
*(UAlign8 *)(dst + 0x10) = *(UAlign8 *)(src + 0x10);
}
{
s32 dst = *(s32 *)(param_1 + 0x20);
s32 src = *(s32 *)(t1 + 0x20);
*(UAlign8 *)(dst + 0x18) = *(UAlign8 *)(src + 0x18);
}
{
s32 src = *(s32 *)(t1 + 0x20);
s32 dst = *(s32 *)(param_1 + 0x20);
*(u32 *)(dst + 4) = *(u32 *)(src + 4);
}
{
s32 src = *(s32 *)(t1 + 0x20);
s32 dst = *(s32 *)(param_1 + 0x20);
*(u16 *)(dst + 0x2C) = *(u16 *)(src + 0x2C);
}
}
}
}
@@ -0,0 +1,94 @@
void func_80187DD0(s32 param_1)
{
s32 t1 = *(s32 *)(param_1 + 0x64);
s32 pad[2];
if (*(s16 *)(t1 + 0x36) != *(s16 *)(param_1 + 0x10A)) {
func_8012C218((void *)param_1);
return;
}
{
if ((*(u32 *)(t1 + 0xE0) & 4) != 0) {
func_8012E8A8((u8 *)param_1);
return;
}
{
s32 a2 = *(s32 *)(*(s32 *)(t1 + 0x20) + 0x20);
s32 a3 = param_1 + 0xFC;
if (a2 != 0) {
if ((a2 & 0x1000000) != 0) {
s32 a1 = a2 & 0xFEFFFFFF;
u32 v0;
u32 a0;
u32 v1b;
u32 v1;
*(u16 *)(param_1 + 0x102) = *(u16 *)(a1 + 0x66);
v0 = *(u32 *)(a1 + 0x60);
a0 = *(u8 *)(a1 + 0x61) | ((v0 & 0xF) << 8);
v1b = ((s32)v0 >> 16) & 0xFF;
v1 = ((v0 & 0xF0) << 4) | v1b;
*(u16 *)(param_1 + 0x104) = (u16)a0;
*(u16 *)(param_1 + 0x106) = (u16)v1;
*(s16 *)(param_1 + 0xFC) = (s8)*(u8 *)(a1 + 0x63);
*(s16 *)(param_1 + 0xFE) = (s8)*(u8 *)(a1 + 0x64);
*(s16 *)(param_1 + 0x100) = (s8)*(u8 *)(a1 + 0x65);
} else {
*(UAlign12 *)(param_1 + 0xFC) = *(UAlign12 *)(a2 + 0x90);
}
*(s32 *)(*(s32 *)(param_1 + 0x20) + 0x20) = a3;
}
if ((*(u32 *)(t1 + 0xE0) & 0x80) != 0) {
if (*(u16 *)(param_1 + 0x34) == 0) {
s16 sVar2 = *(u16 *)(param_1 + 0x108) + 0x40;
*(s16 *)(param_1 + 0x108) = sVar2;
if (sVar2 > 0x200) {
*(u16 *)(param_1 + 0x108) = 0x200;
*(s16 *)(param_1 + 0x34) = *(s16 *)(param_1 + 0x34) + 1;
}
} else {
u16 uVar3 = *(u16 *)(param_1 + 0x108) - 0x80;
*(u16 *)(param_1 + 0x108) = uVar3;
if ((s16)uVar3 < 0) {
*(u16 *)(param_1 + 0x108) = 0;
*(u16 *)(param_1 + 0x34) = 0;
*(u32 *)(t1 + 0xE0) = *(u32 *)(t1 + 0xE0) & ~0x80;
}
}
}
*(u16 *)(a3 + 6) = *(u16 *)(a3 + 6) + *(u16 *)(param_1 + 0x108);
*(u32 *)(param_1 + 4) = *(u32 *)(t1 + 4);
*(u32 *)(param_1 + 8) = *(u32 *)(t1 + 8);
*(u32 *)(param_1 + 0xC) = *(u32 *)(t1 + 0xC);
*(UAlign8 *)(param_1 + 0x50) = *(UAlign8 *)(t1 + 0x50);
{
s32 dst = *(s32 *)(param_1 + 0x20);
s32 src = *(s32 *)(t1 + 0x20);
*(UAlign8 *)(dst + 0x10) = *(UAlign8 *)(src + 0x10);
}
{
s32 dst = *(s32 *)(param_1 + 0x20);
s32 src = *(s32 *)(t1 + 0x20);
*(UAlign8 *)(dst + 0x18) = *(UAlign8 *)(src + 0x18);
}
{
s32 src = *(s32 *)(t1 + 0x20);
s32 dst = *(s32 *)(param_1 + 0x20);
*(u32 *)(dst + 4) = *(u32 *)(src + 4);
}
{
s32 src = *(s32 *)(t1 + 0x20);
s32 dst = *(s32 *)(param_1 + 0x20);
*(u16 *)(dst + 0x2C) = *(u16 *)(src + 0x2C);
}
}
}
}
@@ -0,0 +1,89 @@
# func_80187DD0 (src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c), e5, P36 T7 S104: MINIMUM-LEVER at 0
**Final: score 0 (174/174 ins, byte-identical).** Lever count 1 → 1, but the kind changes: the `register u32 v0 __asm__("$2")`
pin becomes ONE marked `do { … } while (0);` around the two halfword stores, which is ordinary C (step 8). No pin, no asm,
no volatile. The best lever-free text scores **2** (below). The nibble-packing block now reads:
v0 = *(u32 *)(a1 + 0x60);
a0 = *(u8 *)(a1 + 0x61) | ((v0 & 0xF) << 8);
v1 = (((s32)v0 >> 16) & 0xFF) | ((v0 & 0xF0) << 4);
do { // !FAKE: do-while — …
*(u16 *)(param_1 + 0x104) = a0;
*(u16 *)(param_1 + 0x106) = v1;
} while (0);
**The same block closes all five other copies of the class, each `--try` at 0** (`scratch/copy_<fn>.c`, built by
`scratch/port.py`, which swaps only this block into the tree's text): func_80181F40 (ov_SC06_020), func_80185E64 (ov_SC06_022),
func_8018833C (ov_SC06_024), func_80182B74 (ov_SC06_032), func_80186858 (ov_SC06_033). That is 6 pins → 6 marked do-whiles.
## (a) The residual
Same count, a register permutation in one block (local-alloc). The target has the loaded word `w` in v0 and the `(w&0xF)<<8`
temp in v1. The lever-free text swaps them (and the or's result with them).
## (b) The pass and the decision (local-alloc, proved with `lsim3.py` on every candidate's `.lreg`, 0 mismatches)
Quantities in the block (numbers are local-alloc's birth/death indices, 2 per insn). The target needs this assignment:
q2 = {w, w&0xF0, <<4} → v0, q3 = {w&0xF, <<8} → v1, q5 = {>>16, &0xFF, the second or} → v1, q4 = {lbu 0x61, the first
or} → a0, q0 = {mask, a1} → a1. `qty_compare_1` (local-alloc.c:1598) ranks by `floor_log2(refs)·refs/(death−birth)`,
with ties going to the lower qty number:
- q2 (8 refs) against q3 (4 refs): **24/L2 ≥ 8/L3**. That holds only when sched1's order is `lw, andi 0xf, lbu, sll 8, or,
<group 2>, sh, sh`: L2 = 18, L3 = 6, a 13333 tie that q2 wins on number. If the 0x104 store is scheduled inside q2's range
(the lever-free default: sched1 puts it right after the first or because the group-2 insns are birthing-boosted,
sched.c:2507-2545), then L2 = 20, q3 wins, and w loses v0. That is the residual.
- q4 (4 refs, L4 = 16 when the store sits after group 2) against q0 (9 refs, L0 = 50): 0.50 < 0.54, so q0 takes a0 and
the lbu gets a1. Fixing the first condition alone turns the v0/v1 residual into an a0/a1 one (score 11,
`e3/u16u16_aa.c`: u16 result variables make the ors SUBREG-destination sets that fail `birthing_insn_p`, which moves
the store late). L4 cannot drop to 14 without a barrier (the lbu→or load latency queues the lbu behind the `sll`,
sched.c:2583-2600), so q4 needs a fifth ref.
- **The do-while supplies both.** flow counts a ref at `loop_depth` (2 inside the loop; propagate_block tracks the LOOP notes, flow.c:1440-1449, and mark_used_regs adds `reg_n_refs += loop_depth`, flow.c:2711), so the store's use of a0 makes
q4 5 refs: 10/16 = 0.625 > 0.54. The LOOP notes are a full sched barrier (sched.c:2053-2074), so the stores cannot
be hoisted into q2's range: L2 = 18. `lsim3` on `scratch/dumps_b9/`: q2 13333, q3 13333, q5 11666, q4 **6250**, q0
5400. That is exactly the target assignment. In sched2 the barrier only keeps the stores where the target has them.
## (c) The move
Inline the two packed values into one expression each (`a0 = lbu | ((w & 0xF) << 8)`, so the lbu's destination is set
once and stays birthing-boosted, which puts it between `andi` and `sll`) and wrap the two stores in `do { } while (0)`.
Each piece is needed. Body_free + the do-while alone scores 10, because `a0` is set twice there, the lbu is not
boosted and it is scheduled first.
## (d) GENERATOR PROPOSAL
For a local-alloc permutation where `lsim3` shows the loser short by one ref or by 2 in live length, try (1) a
`do { <the store that is the losing quantity's last use> } while (0)`, which doubles that ref (flow loop_depth) and pins
the store after everything above it. Also try (2) inlining a two-statement `x = load; x |= y;` into one expression so the load's
destination is set once (it becomes birthing-boosted). Score the pair, and each alone. Mark (1) as `!FAKE: do-while`.
## (e) What did not work (all `--try`, ~1,100 candidates in `scratch/e1`..`e9`)
| family | best | why |
|---|---|---|
| inline / operand order / statement order of the 7 temps (`e1`, 160) | **2** (`u32_a1101_s01_o0.c`, in `body_plain_best.c`) | the second or's result ties to `(w&0xF0)<<4` (q2 13636 > q3), so w gets v0, but the or result lands in v0, not v1 |
| variable reuse / compound assignment (`e2`, 256) | 3 | the same tie structures |
| u16/s16 result variables (`e3`, 180) | 11 | v0/v1 right, a0/a1 swapped (q4 0.50 < q0 0.54, above) |
| extra copies to add refs (`e4`/`e5`, 330) | 3 | cse folds the copies before flow counts the refs |
| `const` reads to relax the store→load dependences (`e6`/`e7`) | 2 | RTX_UNCHANGING_P reorders everything (up to 93 worse) |
| barrier between `sll` and `lbu` (`e8`) | 9 | the allocation is exactly right, but the barrier survives into sched2, the lbu cannot fill the lw delay slot, +2 nops |
## (f) Where the method fell short
- `lsim3.py` was decisive: every hypothesis was two numbers on its table. It needs a documented home (it lives in
another agent's scratch). It should also print each qty's registers' refs, which is how the loop-depth ref showed up.
- Nothing in the method says flow's `reg_n_refs` is **weighted by loop depth**, so a `do { } while (0)` changes
allocation priorities as well as the schedule. That is a second, independent effect of the same marked construct (d4 used only the barrier).
- A two-sided allocation constraint (q2≥q3 AND q4>q0) where every plain-C single move fixes one side and breaks the
other is a case for an exhaustive search over the priority arithmetic, not for more spellings.
- Interpretation (not proven): two stores wrapped in `do { … } while (0)` is the shape of a C statement macro
(`#define SET_PAIR(p, a, b) do { … } while (0)`), so this may be the original text rather than a lever. The sibling
in 800.c (`local[1] = a1[3].b1 | ((w & 0xF) << 8); local[2] = …`) is lever-free because it sits inside a real
`while` loop, which again points to loop depth as the missing fact.
## (g) Structs answer
Unlikely to remove this lever. The deciding quantities are refs and live lengths in local-alloc, plus sched1's
placement of the 0x104 store. Struct field access would make the reads and stores `MEM_IN_STRUCT_P`, and they already
are (every `*(T *)(p + K)` is a PLUS_EXPR address, expr.c:4568-4577). `true_dependence` (sched.c:817-842) disambiguates
only against a fixed address, and both sides here are register-based, so the dependences cannot change. Tested the only
aliasing channel that could move the store, `const` (RTX_UNCHANGING_P): it makes things worse. What a struct *could*
supply is the original's shape of this block, e.g. a colour struct written through a pointer inside a loop over parts. If
the original sits in a real loop, the do-while stands in for real loop depth, and that information lives in the caller's
structure, not in a type.
Files: `body.c` (min-lever text), `body_plain_best.c` (best lever-free text, 2), `scratch/copy_<fn>.c` (5 class copies
at 0), `scratch/port.py`, `scratch/an.sh` (dump + lsim3 + sched order for one candidate), `scratch/dumps_b9/` (the close),
`scratch/dumps_u16aa/` (the a0/a1 case), `scratch/e1`..`e9` (candidate families with `eN_scores.txt`).
@@ -0,0 +1,28 @@
void func_8018AD74(s32 p1, s32 p2, s32 p3, s32 p4)
{
/* [T51] block-scoped decls (house idiom in this TU, cf. func_8018AF88): a file-scope
decl of these symbols would constrain every LATER function in the object. */
extern Blk20_8018AF88 D_800AE620;
extern s32 func_8012C658(s32, s32, s32);
extern void RotMatrixY(s32 a0, void *a1);
extern void func_800484EC(s32, s32, s32);
extern u8 D_801CD468[];
Blk20_8018AF88 local_30; /* sp+0x10 */
Blk20_8018AF88 *m;
s32 obj;
s32 out[3]; /* sp+0x30, only [0] and [2] used */
local_30 = D_800AE620;
obj = func_8012C658((s16)p2, (s16)p3, p1);
do { } while (0); // !FAKE: do-while — its NOTE_INSN_LOOP_END ends cse1's extended block (cse.c:8054), so m below is not folded onto the struct copy's address pseudo (cse.c:846-862) and is set AFTER obj's copy: sched1 then ranks it last by LUID (sched.c:2428) (P36 S104 e5 minimum-lever)
m = &local_30;
if (obj != 0) {
*(u16 *)(obj + 0xA) = *(u16 *)(obj + 0xA) - 0x80;
RotMatrixY((s16)p4, m);
func_800484EC((s32)m, (s32)&D_801CD468, (s32)out);
*(s32 *)(obj + 0x10) = out[0];
*(s32 *)(obj + 0x14) = 0xFFD80000;
*(s32 *)(obj + 0x18) = out[2];
}
}
@@ -0,0 +1,85 @@
# func_8018AD74 (src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c), e5, P36 T7 S104: MINIMUM-LEVER at 0
**Final: score 0 (60/60 ins, byte-identical).** Lever count 1 → 1, but the kind changes: the tree's `__asm__` launder
on `obj` becomes ONE marked empty `do { } while (0);`, which is ordinary C (step 8). No pin, no asm, no volatile. I found no
lever-free spelling (below). The move against `body_free.c`:
- obj = func_8012C658((s16)p2, (s16)p3, p1);
- if (obj != 0) {
- m = &local_30;
+ obj = func_8012C658((s16)p2, (s16)p3, p1);
+ do { } while (0); // !FAKE: do-while — …
+ m = &local_30;
+ if (obj != 0) {
The class's other copy closes with the same text: **func_80185F4C** (src/ov_SC06_032/ov_SC06_032_jr_80182890.c) scores
**0** (`scratch/copy_func_80185F4C.c`).
## (a) The residual
Same count, one instruction in the wrong place. The target is `jal; [move s1,a3]; move s0,v0; beqz s0; [addiu s2,sp,16]`.
The free text is `move s1,a3; jal; [addiu s2,sp,16]; move s0,v0; beqz; [sll a0,s1,16]`. The `m = &local_30` address
(`addiu s2,sp,16`) sits before the call instead of between the call-result copy and the branch.
## (b) The pass and the decision (proved on dumps, `scratch/dumps_free/`, `scratch/dumps_tree/`)
1. **cse1 hoists `m` into block 0.** `local_30 = D_800AE620` expands through `expand_block_move`, which copies the
destination address into a pseudo (`.rtl` insn 17, `reg 78 = fp+16`). `m = &local_30` in the if-body is the same
`(plus fp 16)`, so cse finds reg 78 in the table. m's own pseudo is not made the class head (it does not outlive the
extended block, `make_regs_eqv`, cse.c:846-862), so every use of m becomes reg 78 and m's set is deleted. The
struct copy's own address is folded back to `fp+16`, so reg 78 now exists only to be m. It is set at insn 17, before the call.
2. **sched1 ranks it before the call.** At T-2 (just before the branch) the ready list is {17 (reg 78), 35 (`obj = v0`)}.
Both are birthing insns, boosted to `max_priority` (`adjust_priority`, sched.c:2507-2545; `birthing_insn_p` needs
`reg_n_sets == 1`, :2469-2490). Both are class 3. The tie goes to the higher LUID (`rank_for_schedule`, sched.c:2428),
which is 35, so 35 is placed last and 17 falls before the `jal`. reorg then fills the `jal` slot with it.
3. **The tree's launder** makes obj's pseudo `reg_n_sets == 2`. 35 is then not a birthing insn (priority 1 against 17's
boost), and 17 lands between 35 and the branch (`.sched` of the tree: `T-2: 17 (7f000001) 35 (1), now 17`).
## (c) The move (minimum lever)
Assign `m = &local_30;` in block 0 after the call, behind an empty `do { } while (0);`. The loop's `NOTE_INSN_LOOP_END`
ends cse1's extended basic block (`cse_end_of_basic_block`, cse.c:8054). m's `(plus fp 16)` is then never looked up
against reg 78. Reg 78 goes dead once the movstr address is folded, and cse deletes it, so cse2 (which ignores the note)
has nothing to merge. m keeps its own pseudo, set after 35. At T-2 both are boosted and the LUID tie now picks m's set.
It lands between `move s0,v0` and `beqz`, and reorg puts it in the branch delay slot. The note is also a sched barrier
(sched.c:2053-2074), but between 35 and m's set that is harmless.
Placement matters (all `--try`): the empty loop **between the call and `m =`** scores **0**. `do { m = &local_30; } while (0)`
scores 5, because the LOOP_END comes after m and the fold still happens. Wrapping the call scores 7: its LOOP_BEG pins
`move s1,a3` before the argument setup and flips the s1/s2 allocation. Wrapping the struct copy scores 21.
## (d) GENERATOR PROPOSAL
When the only residual is an `addiu sK,sp,N` (a local's address) sitting in a `jal` delay slot where the target has it in
the following branch's slot, and the source takes that local's address inside the if-body after a struct copy into the
same local: hoist `p = &local;` to just before the `if`, behind an empty `do { } while (0);` (marked). The mechanical
test: `.cse` shows the address pseudo created by `expand_block_move` standing in for the pointer variable.
## (e) What did not work (all `--try`)
| spelling | score | why |
|---|---|---|
| body_free (start) | 5 | above |
| `&local_30` direct at both calls, no `m` | 5 | same fold onto reg 78 |
| `m = &local_30;` before / instead of the struct copy (`*m = D_800AE620`) | 5 / 5 | m is set first, LUID lowest |
| `m = &local_30;` between the call and `if`, no loop | 5 | cse1 folds it onto reg 78 again |
| `if (obj == 0) return;` form | 5 | the same extended block |
| `p1 = func(…, p1)` / `p2 = func(…)` to give obj a second set | 5 / 5 | expand puts the call value in a fresh temp; cse deletes the param copy, so the temp still has `n_sets == 1` |
| the sweep's R18/R7 bests | 2 (history) | never below 2 |
## (f) Where the method fell short
- A plausible origin for an EMPTY `do { } while (0)` in 1990s game code is a debug macro compiled out
(`#define DPRINTF(...) do { } while (0)`). That is an interpretation, not proven. It would make this text the original
rather than a lever, and it is why I prefer it to the asm. Three distinct effects of a do-while(0) are now on record:
a sched barrier (d4), a cse1 extended-block end (this one, cse.c:8054), and a loop-depth ref weight (e5's
func_80187DD0, flow.c:2711). A generator that inserts `do { } while (0);` at each statement boundary and scores all of
them would have found this in about 20 compiles.
- The method has the birthing boost (S104 d8) but no rule for **cse moving a variable's definition to another block**
by folding it onto an earlier pseudo that holds the same address. That fold is what put `m` in block 0 in the first place.
"Read `.cse` for a user variable that vanished" belongs in step 3.
- I looked for a plain-C second set of `obj` (a natural `reg_n_sets == 2`) and did not find one. Every reuse of a
parameter goes through a call-value temp.
## (g) Structs answer
Struct typing would not remove this lever. The deciding facts are cse's value equivalence of two `(plus fp 16)`
computations and sched1's LUID tie. Neither involves memory aliasing (`expr.c:4568-4577` → `sched.c` true_dependence
is not on the path: the two contenders are register copies). `local_30` is already a struct. What would matter is the
original's **statement placement** (where `&m` is formed) or a second assignment of `obj`, which is information a
struct definition does not supply.
Files: `body.c` (the min-lever text), `scratch/copy_func_80185F4C.c` (SC06_032 copy, 0), `scratch/d/` (do-while
placements), `scratch/dumps_free/`, `scratch/dumps_tree/`, `scratch/dumps_v2/`, `scratch/dumps_v7/`.
@@ -0,0 +1,35 @@
void func_8018B9D4(s32 param_1)
{
struct { u16 a; u16 b; s16 c; u16 d; } t1;
struct { u16 a; u16 b; s16 c; u16 d; } t2;
func_8018BCC4((void *)param_1, 0, (u16 *)(param_1 + 0xE8), 0x81818);
func_8018BCC4((void *)param_1, 1, (u16 *)(param_1 + 0xE8), 0x204040);
t1.b = 0;
t1.a = 0;
t1.c = -*(u16 *)(param_1 + 0xE8);
t2.b = 0;
t2.a = 0;
t2.c = -*(u16 *)(param_1 + 0xEA);
if (func_8012DEB8(param_1, &t1, &t2) != 0) {
D_80126B96 = 0x4018;
D_80126B98 = 0x96;
}
if (*(s16 *)(param_1 + 0xEA) < *(s16 *)(param_1 + 0xFE)) {
*(s16 *)(param_1 + 0xEA) += 0x40;
if (*(s16 *)(param_1 + 0xEA) >= *(s16 *)(param_1 + 0xFE)) {
*(s16 *)(param_1 + 0xEA) = *(s16 *)(param_1 + 0xFE);
func_8018BC40(param_1);
}
} else {
*(s16 *)(param_1 + 0xE8) += 0x40;
if (*(s16 *)(param_1 + 0xE8) < *(s16 *)(param_1 + 0xFE)) {
return;
}
if (*(s32 *)(param_1 + 0xCC) != 0) {
func_80016714((void *)*(s32 *)(param_1 + 0xCC), 0x38);
}
*(s32 *)(*(s32 *)(param_1 + 0x64) + 0xD0) = 0;
func_8012C218((void *)param_1);
}
}
@@ -0,0 +1,87 @@
# func_8018B9D4 (src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c), e5, P36 T7 S104: CLOSED at 0, zero levers
**Final: score 0 (81/81 ins, byte-identical, frame included), plain C.** Lever count 1 → 0 (the `$0` pin), and the
`s32 pad[2]` and `r1..r4` keepalives are gone as well. The whole change from `body_free.c`: the `var_ea/var_fe/var_a0/var_v0`
temp chain is replaced by direct field arithmetic on memory:
if (*(s16 *)(param_1 + 0xEA) < *(s16 *)(param_1 + 0xFE)) {
*(s16 *)(param_1 + 0xEA) += 0x40;
if (*(s16 *)(param_1 + 0xEA) >= *(s16 *)(param_1 + 0xFE)) { ... }
} else {
*(s16 *)(param_1 + 0xE8) += 0x40;
if (*(s16 *)(param_1 + 0xE8) < *(s16 *)(param_1 + 0xFE)) return;
...
The class's other copy closes with the same text (callees renamed): **func_80186BAC**
(src/ov_SC06_032/ov_SC06_032_jr_80182890.c) scores **0** (`scratch/copy_func_80186BAC.c`).
## (a) The residual
Same count (81/81). The target has `lh v0,234(s1); lh v1,254(s1); move a0,v0; slt v0,v0,v1; beqz; addiu v0,a0,64`. The
lever-free text has `lh v1; lh v0; nop; slt v0,v1,v0; addiu v0,v1,64`. The target keeps a **copy** of the loaded 0xEA value
(`move a0,v0`) that lives past the compare. The free text has no copy, and the compare and the add read one register.
## (b) The pass and the decision (proved on dumps, `scratch/dumps_n2e/`, `dumps_c1/`, `dumps_c5/`)
The copy comes from **combine's narrow-load split**, `combine.c:1887-1940`: when a 3-insn combination builds
`(parallel [(set A (sign_extend (mem:HI))) (set P (mem:HI))])` because the narrow load's destination P is still
needed, combine splits it into `A = sign_extend(mem)` (= `lh`) plus `P:HI = (subreg:HI A)`, a register copy.
- P is still needed because `*(s16 *)(p+0xEA) += 0x40` re-reads the halfword, and cse1 forwards that read to the
HImode load pseudo P (`.cse` insn 104: `(plus:SI (subreg:SI (reg:HI 83)) 64)`). So P has two readers: the extension
for the compare, and the add.
- `.combine` insns 92/93: `(set (reg:SI 85) (sign_extend:SI (mem:HI …234)))` and `(set (reg:HI 83) (subreg:HI (reg:SI 85) 0))`.
- The copy's source is a **SUBREG**, so local-alloc's `optimize_reg_copy_1` does not touch it. That function rewrites a
later use of the source onto the copy so the two can be tied, and it is only called for `GET_CODE (SET_SRC) == REG`
(`local-alloc.c:1003-1007`). A85 dies at the `slt` and not at the copy, so `combine_regs` cannot tie them either →
two registers → `move a0,v0`.
Why each temp spelling fails (bytes + dumps):
- `var_a0 = var_ea` (s32): a REG-REG copy. cse canonicalises it onto var_ea and deletes it.
- `var_a0 = (s16)var_ea` (`c1`): cse keeps the re-extension, combine reduces it to `(set 79 77)` (sign-bit copies), and then
**local-alloc's `optimize_reg_copy_1` rewrites the `slt` to read 79** (`.lreg` insn 103) and ties them. Score 5.
- `s16 var_a0; var_a0 = var_ea` (`c5`): a SUBREG copy, but cse `fold_rtx` SUBREG case (paradoxical subreg of a reg
equivalent to a lowpart subreg → the inner reg) folds the add's `(subreg:SI (reg:HI 79))` back to 77. The copy dies. Score 5.
- So the copy has to be created **after** cse by combine, and with a SUBREG source. That is the 1887 split, and only
a second READ of the same halfword creates it. A temp chain never does.
## (c) The move
Spell the field update as the original almost certainly wrote it, `*(s16 *)(p + 0xEA) += 0x40;`, and re-read the field
in the following test instead of reusing a temp: `if (*(s16 *)(p + 0xEA) >= …)`. The else arm takes the same spelling on
0xE8 (`+= 0x40` as `s16` or `u16`, both 0). The re-read after the store is forwarded by cse (store-to-load), and
the target's `sll/sra` of the stored value is exactly that forwarded extension. The frame also came out right with no
pad: `.frame vars=32` in both.
## (d) GENERATOR PROPOSAL
When the target keeps a `move rX,rY` right after an `lh rY` (or `lb`) and rX is used later only in an `addiu`/`sh` back
to the same field, rewrite the decompiler's temp chain `t = *(s16 *)(p+K); u = t; if (t < …) { v = u + C; *(s16 *)(p+K) = v;
if ((v << 16) >> 16 …` as `if (*(s16 *)(p+K) < …) { *(s16 *)(p+K) += C; if (*(s16 *)(p+K) …`. That is, **collapse the temps into
compound assignment on the memory lvalue and re-read the field at each test**. The tell is mechanical: a `move` whose
source was just loaded by `lh`/`lb` and which the text spells with a `+ zr` / `$0` pin or a launder.
## (e) What did not work (all `--try`)
| spelling | score |
|---|---|
| body_free (start) | 5 |
| all 81 width combinations of var_ea/var_fe/var_a0/var_v0 ∈ {s32,s16,u16} (`scratch/w/`) | best 4 (`u16 var_a0`: copy kept but an extra `andi 0xffff`) |
| `var_a0 = (s16)var_ea` | 5 (optimize_reg_copy_1 ties it, above) |
| `var_ea` kept, `var_v0 = var_ea + 0x40`, `(s16)var_v0` test | 5 |
| `+= 0x40` text but with `pad[2]` kept | 8 (the frame is 8 bytes too big, the body is identical) |
| `+= 0x40` text, pad and `r1..r4` deleted | **0** |
## (f) Where the method fell short
- Counting said "same count, one `nop` vs `move`": that is a copy the target keeps. Step 3's listed causes are all
about copies that **cse** deletes (R20/R21 width or distance). This one is deleted by **local-alloc
`optimize_reg_copy_1`** (`local-alloc.c:700`, caller `:1003`), a pass the method never names. Worth a line in step 3:
"a copy that survives cse and combine can still be folded by optimize_reg_copy_1 when its source is a REG that dies
later in the same block with no jump in between; a SUBREG-source copy (combine.c:1887 split) is exempt."
- The pad and the keepalives were part of the lever: the temp spelling needed a `pad[2]` to make the frame right. A
close that deletes the temps has to delete the pad too. Otherwise it scores 8 and looks worse than the start.
- No generator reaches "replace a temp chain with a compound assignment on the lvalue". R6 (inline) inlines one temp
at a time, and the first step alone does not create the second read.
## (g) Structs answer
A struct would help the READABILITY, not the decision: `e->ea += 0x40; if (e->ea >= e->fe)` gives the same RTL as the
cast spelling (the field reads are `mem:HI` either way). The deciding facts are the second read of the same `s16`
location, the combine split and the SUBREG copy, and none of them involve aliasing. The struct is the natural way to
write the re-read, and that is likely why the original has it. Not tested separately. The cast text already closes.
Files: `body.c` (the closed text), `scratch/copy_func_80186BAC.c` (the SC06_032 copy, 0), dumps in `scratch/dumps_n2e/`
(close), `scratch/dumps_c1/` and `scratch/dumps_c5/` (the two refuted copy spellings), `scratch/w/` (the width sweep).
+179 -179
View File
@@ -1,7 +1,7 @@
{
"head": "b591697b8",
"head": "a1a0a80e2",
"stamp": "15956e4a96c4",
"generated": "2026-09-11 01:50",
"generated": "2026-09-11 01:54",
"aliases": [
"main",
"ov_SC03_014",
@@ -21,206 +21,206 @@
"main": {
"objects": 85,
"identical": 85,
"seconds": 6.521999999999999,
"mean_s": 0.077
"seconds": 6.381999999999998,
"mean_s": 0.075
},
"ov_SC03_014": {
"objects": 32,
"identical": 32,
"seconds": 4.517,
"mean_s": 0.141
"seconds": 4.442,
"mean_s": 0.139
},
"ov_SC03_015": {
"objects": 32,
"identical": 32,
"seconds": 4.3549999999999995,
"mean_s": 0.136
"seconds": 4.295999999999999,
"mean_s": 0.134
},
"ov_SC04_011": {
"objects": 28,
"identical": 28,
"seconds": 3.813,
"mean_s": 0.136
"seconds": 3.727,
"mean_s": 0.133
}
},
"per_object_seconds": {
"build/src/800.o": 0.699,
"build/src/800_b.o": 0.071,
"build/src/800_b_2.o": 0.304,
"build/src/800_b_o0a.o": 0.081,
"build/src/800_c.o": 0.183,
"build/src/800b2.o": 0.073,
"build/src/apicard1.o": 0.068,
"build/src/apicard2.o": 0.064,
"build/src/apicard3.o": 0.063,
"build/src/apicard4.o": 0.088,
"build/src/apicard5.o": 0.062,
"build/src/apicard6.o": 0.087,
"build/src/apicard7.o": 0.062,
"build/src/boot.o": 0.104,
"build/src/gap.o": 0.074,
"build/src/libapi1.o": 0.081,
"build/src/libapi2.o": 0.057,
"build/src/libc2_1.o": 0.06,
"build/src/libc2_2.o": 0.058,
"build/src/libcd1.o": 0.064,
"build/src/libcd2.o": 0.058,
"build/src/libetc.o": 0.066,
"build/src/libgpu.o": 0.059,
"build/src/libgpu2.o": 0.077,
"build/src/libgs1.o": 0.048,
"build/src/libgs2.o": 0.061,
"build/src/libgs3.o": 0.059,
"build/src/libgs4.o": 0.061,
"build/src/libgs5.o": 0.061,
"build/src/libgs6.o": 0.068,
"build/src/800.o": 0.711,
"build/src/800_b.o": 0.078,
"build/src/800_b_2.o": 0.277,
"build/src/800_b_o0a.o": 0.064,
"build/src/800_c.o": 0.189,
"build/src/800b2.o": 0.075,
"build/src/apicard1.o": 0.061,
"build/src/apicard2.o": 0.072,
"build/src/apicard3.o": 0.065,
"build/src/apicard4.o": 0.08,
"build/src/apicard5.o": 0.065,
"build/src/apicard6.o": 0.081,
"build/src/apicard7.o": 0.063,
"build/src/boot.o": 0.08,
"build/src/gap.o": 0.067,
"build/src/libapi1.o": 0.068,
"build/src/libapi2.o": 0.055,
"build/src/libc2_1.o": 0.058,
"build/src/libc2_2.o": 0.055,
"build/src/libcd1.o": 0.066,
"build/src/libcd2.o": 0.057,
"build/src/libetc.o": 0.061,
"build/src/libgpu.o": 0.057,
"build/src/libgpu2.o": 0.064,
"build/src/libgs1.o": 0.058,
"build/src/libgs2.o": 0.057,
"build/src/libgs3.o": 0.064,
"build/src/libgs4.o": 0.069,
"build/src/libgs5.o": 0.053,
"build/src/libgs6.o": 0.075,
"build/src/libgs7.o": 0.061,
"build/src/libgs8.o": 0.061,
"build/src/libgte1.o": 0.063,
"build/src/libgte10.o": 0.063,
"build/src/libgs8.o": 0.057,
"build/src/libgte1.o": 0.065,
"build/src/libgte10.o": 0.062,
"build/src/libgte11.o": 0.061,
"build/src/libgte12.o": 0.058,
"build/src/libgte12.o": 0.062,
"build/src/libgte13.o": 0.064,
"build/src/libgte14.o": 0.069,
"build/src/libgte15.o": 0.05,
"build/src/libgte16.o": 0.057,
"build/src/libgte17.o": 0.07,
"build/src/libgte18.o": 0.061,
"build/src/libgte19.o": 0.059,
"build/src/libgte2.o": 0.064,
"build/src/libgte20.o": 0.063,
"build/src/libgte21.o": 0.061,
"build/src/libgte22.o": 0.053,
"build/src/libgte23.o": 0.057,
"build/src/libgte24.o": 0.064,
"build/src/libgte25.o": 0.059,
"build/src/libgte26.o": 0.063,
"build/src/libgte27.o": 0.067,
"build/src/libgte28.o": 0.053,
"build/src/libgte29.o": 0.06,
"build/src/libgte3.o": 0.059,
"build/src/libgte30.o": 0.06,
"build/src/libgte4.o": 0.061,
"build/src/libgte5.o": 0.063,
"build/src/libgte6.o": 0.062,
"build/src/libgte7.o": 0.069,
"build/src/libgte8.o": 0.061,
"build/src/libgte9.o": 0.069,
"build/src/libmcrd1.o": 0.071,
"build/src/libmcrd2.o": 0.064,
"build/src/libpad1.o": 0.06,
"build/src/libpad2.o": 0.075,
"build/src/sgap.o": 0.067,
"build/src/sgap_2.o": 0.062,
"build/src/libgte14.o": 0.063,
"build/src/libgte15.o": 0.062,
"build/src/libgte16.o": 0.054,
"build/src/libgte17.o": 0.061,
"build/src/libgte18.o": 0.063,
"build/src/libgte19.o": 0.058,
"build/src/libgte2.o": 0.059,
"build/src/libgte20.o": 0.068,
"build/src/libgte21.o": 0.058,
"build/src/libgte22.o": 0.063,
"build/src/libgte23.o": 0.054,
"build/src/libgte24.o": 0.077,
"build/src/libgte25.o": 0.079,
"build/src/libgte26.o": 0.055,
"build/src/libgte27.o": 0.061,
"build/src/libgte28.o": 0.052,
"build/src/libgte29.o": 0.049,
"build/src/libgte3.o": 0.063,
"build/src/libgte30.o": 0.069,
"build/src/libgte4.o": 0.055,
"build/src/libgte5.o": 0.059,
"build/src/libgte6.o": 0.064,
"build/src/libgte7.o": 0.063,
"build/src/libgte8.o": 0.06,
"build/src/libgte9.o": 0.059,
"build/src/libmcrd1.o": 0.073,
"build/src/libmcrd2.o": 0.061,
"build/src/libpad1.o": 0.065,
"build/src/libpad2.o": 0.056,
"build/src/sgap.o": 0.061,
"build/src/sgap_2.o": 0.064,
"build/src/sgap_3.o": 0.064,
"build/src/sgap_4.o": 0.073,
"build/src/sgap_5.o": 0.064,
"build/src/sgap_6.o": 0.07,
"build/src/sgap_8.o": 0.072,
"build/src/snd1.o": 0.072,
"build/src/snd10.o": 0.061,
"build/src/snd11.o": 0.055,
"build/src/snd12.o": 0.067,
"build/src/snd2.o": 0.071,
"build/src/snd3.o": 0.064,
"build/src/snd4.o": 0.061,
"build/src/snd5.o": 0.072,
"build/src/snd6.o": 0.071,
"build/src/snd7.o": 0.066,
"build/src/snd8.o": 0.065,
"build/src/snd9.o": 0.069,
"build/src/ov_SC03_014/ov_SC03_014.o": 0.125,
"build/src/ov_SC03_014/ov_SC03_014_after.o": 0.5,
"build/src/ov_SC03_014/ov_SC03_014_jr_8012ACE0.o": 0.388,
"build/src/ov_SC03_014/ov_SC03_014_jr_80135888.o": 0.065,
"build/src/ov_SC03_014/ov_SC03_014_jr_80135A4C.o": 0.057,
"build/src/ov_SC03_014/ov_SC03_014_jr_80135D20.o": 0.117,
"build/src/ov_SC03_014/ov_SC03_014_jr_801380E0.o": 0.148,
"build/src/ov_SC03_014/ov_SC03_014_jr_8013C98C.o": 0.129,
"build/src/ov_SC03_014/ov_SC03_014_jr_8013F350.o": 0.075,
"build/src/ov_SC03_014/ov_SC03_014_jr_8013FFD8.o": 0.066,
"build/src/ov_SC03_014/ov_SC03_014_jr_80140608.o": 0.174,
"build/src/ov_SC03_014/ov_SC03_014_jr_8015444C.o": 0.07,
"build/src/ov_SC03_014/ov_SC03_014_jr_80154C24.o": 0.188,
"build/src/ov_SC03_014/ov_SC03_014_jr_801588CC.o": 0.097,
"build/src/ov_SC03_014/ov_SC03_014_jr_80159C84.o": 0.061,
"build/src/ov_SC03_014/ov_SC03_014_jr_8015A3C8.o": 0.075,
"build/src/ov_SC03_014/ov_SC03_014_jr_8015AE2C.o": 0.089,
"build/src/ov_SC03_014/ov_SC03_014_jr_8015C32C.o": 0.441,
"build/src/ov_SC03_014/ov_SC03_014_jr_8016AB6C.o": 0.261,
"build/src/ov_SC03_014/ov_SC03_014_jr_80171B4C.o": 0.094,
"build/src/ov_SC03_014/ov_SC03_014_jr_801734BC.o": 0.182,
"build/src/ov_SC03_014/ov_SC03_014_jr_801789AC.o": 0.054,
"build/src/sgap_4.o": 0.082,
"build/src/sgap_5.o": 0.062,
"build/src/sgap_6.o": 0.062,
"build/src/sgap_8.o": 0.07,
"build/src/snd1.o": 0.064,
"build/src/snd10.o": 0.063,
"build/src/snd11.o": 0.069,
"build/src/snd12.o": 0.059,
"build/src/snd2.o": 0.065,
"build/src/snd3.o": 0.059,
"build/src/snd4.o": 0.059,
"build/src/snd5.o": 0.068,
"build/src/snd6.o": 0.068,
"build/src/snd7.o": 0.063,
"build/src/snd8.o": 0.06,
"build/src/snd9.o": 0.06,
"build/src/ov_SC03_014/ov_SC03_014.o": 0.128,
"build/src/ov_SC03_014/ov_SC03_014_after.o": 0.499,
"build/src/ov_SC03_014/ov_SC03_014_jr_8012ACE0.o": 0.38,
"build/src/ov_SC03_014/ov_SC03_014_jr_80135888.o": 0.072,
"build/src/ov_SC03_014/ov_SC03_014_jr_80135A4C.o": 0.058,
"build/src/ov_SC03_014/ov_SC03_014_jr_80135D20.o": 0.116,
"build/src/ov_SC03_014/ov_SC03_014_jr_801380E0.o": 0.15,
"build/src/ov_SC03_014/ov_SC03_014_jr_8013C98C.o": 0.112,
"build/src/ov_SC03_014/ov_SC03_014_jr_8013F350.o": 0.077,
"build/src/ov_SC03_014/ov_SC03_014_jr_8013FFD8.o": 0.067,
"build/src/ov_SC03_014/ov_SC03_014_jr_80140608.o": 0.196,
"build/src/ov_SC03_014/ov_SC03_014_jr_8015444C.o": 0.072,
"build/src/ov_SC03_014/ov_SC03_014_jr_80154C24.o": 0.169,
"build/src/ov_SC03_014/ov_SC03_014_jr_801588CC.o": 0.093,
"build/src/ov_SC03_014/ov_SC03_014_jr_80159C84.o": 0.064,
"build/src/ov_SC03_014/ov_SC03_014_jr_8015A3C8.o": 0.073,
"build/src/ov_SC03_014/ov_SC03_014_jr_8015AE2C.o": 0.091,
"build/src/ov_SC03_014/ov_SC03_014_jr_8015C32C.o": 0.427,
"build/src/ov_SC03_014/ov_SC03_014_jr_8016AB6C.o": 0.25,
"build/src/ov_SC03_014/ov_SC03_014_jr_80171B4C.o": 0.091,
"build/src/ov_SC03_014/ov_SC03_014_jr_801734BC.o": 0.183,
"build/src/ov_SC03_014/ov_SC03_014_jr_801789AC.o": 0.053,
"build/src/ov_SC03_014/ov_SC03_014_jr_80178D40.o": 0.095,
"build/src/ov_SC03_014/ov_SC03_014_jr_8017A4AC.o": 0.065,
"build/src/ov_SC03_014/ov_SC03_014_jr_8017AE2C.o": 0.153,
"build/src/ov_SC03_014/ov_SC03_014_jr_8017EB7C.o": 0.19,
"build/src/ov_SC03_014/ov_SC03_014_jr_80184440.o": 0.048,
"build/src/ov_SC03_014/ov_SC03_014_jr_801848E4.o": 0.276,
"build/src/ov_SC03_014/ov_SC03_014_o0b.o": 0.056,
"build/src/ov_SC03_014/ov_SC03_014_jr_8017A4AC.o": 0.061,
"build/src/ov_SC03_014/ov_SC03_014_jr_8017AE2C.o": 0.15,
"build/src/ov_SC03_014/ov_SC03_014_jr_8017EB7C.o": 0.184,
"build/src/ov_SC03_014/ov_SC03_014_jr_80184440.o": 0.044,
"build/src/ov_SC03_014/ov_SC03_014_jr_801848E4.o": 0.266,
"build/src/ov_SC03_014/ov_SC03_014_o0b.o": 0.05,
"build/src/ov_SC03_014/ov_SC03_014_o0c.o": 0.057,
"build/src/ov_SC03_014/ov_SC03_014_o0d.o": 0.057,
"build/src/ov_SC03_014/ov_SC03_014_o0e.o": 0.064,
"build/src/ov_SC03_015/ov_SC03_015.o": 0.118,
"build/src/ov_SC03_015/ov_SC03_015_after.o": 0.518,
"build/src/ov_SC03_015/ov_SC03_015_jr_8012ACE0.o": 0.369,
"build/src/ov_SC03_015/ov_SC03_015_jr_80135888.o": 0.051,
"build/src/ov_SC03_015/ov_SC03_015_jr_80135A4C.o": 0.051,
"build/src/ov_SC03_015/ov_SC03_015_jr_80135D20.o": 0.122,
"build/src/ov_SC03_015/ov_SC03_015_jr_801380E0.o": 0.143,
"build/src/ov_SC03_015/ov_SC03_015_jr_8013C98C.o": 0.112,
"build/src/ov_SC03_015/ov_SC03_015_jr_8013F350.o": 0.076,
"build/src/ov_SC03_015/ov_SC03_015_jr_8013FFD8.o": 0.063,
"build/src/ov_SC03_015/ov_SC03_015_jr_80140608.o": 0.166,
"build/src/ov_SC03_015/ov_SC03_015_jr_8015444C.o": 0.068,
"build/src/ov_SC03_015/ov_SC03_015_jr_80154C24.o": 0.163,
"build/src/ov_SC03_015/ov_SC03_015_jr_801588CC.o": 0.084,
"build/src/ov_SC03_015/ov_SC03_015_jr_80159C84.o": 0.067,
"build/src/ov_SC03_015/ov_SC03_015_jr_8015A3C8.o": 0.07,
"build/src/ov_SC03_015/ov_SC03_015_jr_8015AE2C.o": 0.094,
"build/src/ov_SC03_015/ov_SC03_015_jr_8015C32C.o": 0.421,
"build/src/ov_SC03_015/ov_SC03_015_jr_8016AB6C.o": 0.254,
"build/src/ov_SC03_015/ov_SC03_015_jr_80171B4C.o": 0.094,
"build/src/ov_SC03_015/ov_SC03_015_jr_801734BC.o": 0.181,
"build/src/ov_SC03_015/ov_SC03_015_jr_801789AC.o": 0.056,
"build/src/ov_SC03_015/ov_SC03_015_jr_80178D40.o": 0.099,
"build/src/ov_SC03_015/ov_SC03_015_jr_8017A4AC.o": 0.056,
"build/src/ov_SC03_015/ov_SC03_015_jr_8017AE2C.o": 0.156,
"build/src/ov_SC03_015/ov_SC03_015_jr_8017EB7C.o": 0.165,
"build/src/ov_SC03_015/ov_SC03_015_jr_80184440.o": 0.05,
"build/src/ov_SC03_015/ov_SC03_015_jr_801848E4.o": 0.257,
"build/src/ov_SC03_015/ov_SC03_015_o0b.o": 0.05,
"build/src/ov_SC03_015/ov_SC03_015_o0c.o": 0.059,
"build/src/ov_SC03_015/ov_SC03_015_o0d.o": 0.055,
"build/src/ov_SC03_015/ov_SC03_015_o0e.o": 0.067,
"build/src/ov_SC04_011/ov_SC04_011.o": 0.123,
"build/src/ov_SC04_011/ov_SC04_011_after.o": 0.415,
"build/src/ov_SC04_011/ov_SC04_011_jr_8012ACE0.o": 0.332,
"build/src/ov_SC04_011/ov_SC04_011_jr_80135888.o": 0.055,
"build/src/ov_SC04_011/ov_SC04_011_jr_80135A4C.o": 0.048,
"build/src/ov_SC04_011/ov_SC04_011_jr_80135D20.o": 0.105,
"build/src/ov_SC04_011/ov_SC04_011_jr_801380E0.o": 0.153,
"build/src/ov_SC04_011/ov_SC04_011_jr_8013C98C.o": 0.104,
"build/src/ov_SC04_011/ov_SC04_011_jr_8013F350.o": 0.072,
"build/src/ov_SC04_011/ov_SC04_011_jr_8013FFD8.o": 0.059,
"build/src/ov_SC04_011/ov_SC04_011_jr_80140608.o": 0.168,
"build/src/ov_SC04_011/ov_SC04_011_jr_8015444C.o": 0.074,
"build/src/ov_SC03_014/ov_SC03_014_o0d.o": 0.048,
"build/src/ov_SC03_014/ov_SC03_014_o0e.o": 0.066,
"build/src/ov_SC03_015/ov_SC03_015.o": 0.119,
"build/src/ov_SC03_015/ov_SC03_015_after.o": 0.472,
"build/src/ov_SC03_015/ov_SC03_015_jr_8012ACE0.o": 0.345,
"build/src/ov_SC03_015/ov_SC03_015_jr_80135888.o": 0.045,
"build/src/ov_SC03_015/ov_SC03_015_jr_80135A4C.o": 0.054,
"build/src/ov_SC03_015/ov_SC03_015_jr_80135D20.o": 0.121,
"build/src/ov_SC03_015/ov_SC03_015_jr_801380E0.o": 0.134,
"build/src/ov_SC03_015/ov_SC03_015_jr_8013C98C.o": 0.103,
"build/src/ov_SC03_015/ov_SC03_015_jr_8013F350.o": 0.063,
"build/src/ov_SC03_015/ov_SC03_015_jr_8013FFD8.o": 0.059,
"build/src/ov_SC03_015/ov_SC03_015_jr_80140608.o": 0.151,
"build/src/ov_SC03_015/ov_SC03_015_jr_8015444C.o": 0.063,
"build/src/ov_SC03_015/ov_SC03_015_jr_80154C24.o": 0.165,
"build/src/ov_SC03_015/ov_SC03_015_jr_801588CC.o": 0.088,
"build/src/ov_SC03_015/ov_SC03_015_jr_80159C84.o": 0.063,
"build/src/ov_SC03_015/ov_SC03_015_jr_8015A3C8.o": 0.062,
"build/src/ov_SC03_015/ov_SC03_015_jr_8015AE2C.o": 0.09,
"build/src/ov_SC03_015/ov_SC03_015_jr_8015C32C.o": 0.435,
"build/src/ov_SC03_015/ov_SC03_015_jr_8016AB6C.o": 0.253,
"build/src/ov_SC03_015/ov_SC03_015_jr_80171B4C.o": 0.1,
"build/src/ov_SC03_015/ov_SC03_015_jr_801734BC.o": 0.191,
"build/src/ov_SC03_015/ov_SC03_015_jr_801789AC.o": 0.052,
"build/src/ov_SC03_015/ov_SC03_015_jr_80178D40.o": 0.097,
"build/src/ov_SC03_015/ov_SC03_015_jr_8017A4AC.o": 0.062,
"build/src/ov_SC03_015/ov_SC03_015_jr_8017AE2C.o": 0.157,
"build/src/ov_SC03_015/ov_SC03_015_jr_8017EB7C.o": 0.191,
"build/src/ov_SC03_015/ov_SC03_015_jr_80184440.o": 0.052,
"build/src/ov_SC03_015/ov_SC03_015_jr_801848E4.o": 0.268,
"build/src/ov_SC03_015/ov_SC03_015_o0b.o": 0.06,
"build/src/ov_SC03_015/ov_SC03_015_o0c.o": 0.053,
"build/src/ov_SC03_015/ov_SC03_015_o0d.o": 0.057,
"build/src/ov_SC03_015/ov_SC03_015_o0e.o": 0.071,
"build/src/ov_SC04_011/ov_SC04_011.o": 0.127,
"build/src/ov_SC04_011/ov_SC04_011_after.o": 0.411,
"build/src/ov_SC04_011/ov_SC04_011_jr_8012ACE0.o": 0.326,
"build/src/ov_SC04_011/ov_SC04_011_jr_80135888.o": 0.046,
"build/src/ov_SC04_011/ov_SC04_011_jr_80135A4C.o": 0.053,
"build/src/ov_SC04_011/ov_SC04_011_jr_80135D20.o": 0.102,
"build/src/ov_SC04_011/ov_SC04_011_jr_801380E0.o": 0.148,
"build/src/ov_SC04_011/ov_SC04_011_jr_8013C98C.o": 0.106,
"build/src/ov_SC04_011/ov_SC04_011_jr_8013F350.o": 0.069,
"build/src/ov_SC04_011/ov_SC04_011_jr_8013FFD8.o": 0.051,
"build/src/ov_SC04_011/ov_SC04_011_jr_80140608.o": 0.165,
"build/src/ov_SC04_011/ov_SC04_011_jr_8015444C.o": 0.063,
"build/src/ov_SC04_011/ov_SC04_011_jr_80154C24.o": 0.152,
"build/src/ov_SC04_011/ov_SC04_011_jr_801588CC.o": 0.087,
"build/src/ov_SC04_011/ov_SC04_011_jr_80159C84.o": 0.064,
"build/src/ov_SC04_011/ov_SC04_011_jr_8015A3C8.o": 0.061,
"build/src/ov_SC04_011/ov_SC04_011_jr_8015AE2C.o": 0.093,
"build/src/ov_SC04_011/ov_SC04_011_jr_8015C32C.o": 0.377,
"build/src/ov_SC04_011/ov_SC04_011_jr_8016AB6C.o": 0.215,
"build/src/ov_SC04_011/ov_SC04_011_jr_80171B4C.o": 0.098,
"build/src/ov_SC04_011/ov_SC04_011_jr_801734BC.o": 0.156,
"build/src/ov_SC04_011/ov_SC04_011_jr_801789AC.o": 0.056,
"build/src/ov_SC04_011/ov_SC04_011_jr_80178D40.o": 0.081,
"build/src/ov_SC04_011/ov_SC04_011_jr_8017A4AC.o": 0.063,
"build/src/ov_SC04_011/ov_SC04_011_jr_8017AE2C.o": 0.093,
"build/src/ov_SC04_011/ov_SC04_011_jr_8017D494.o": 0.406,
"build/src/ov_SC04_011/ov_SC04_011_o0b.o": 0.051,
"build/src/ov_SC04_011/ov_SC04_011_o0c.o": 0.052
"build/src/ov_SC04_011/ov_SC04_011_jr_80159C84.o": 0.065,
"build/src/ov_SC04_011/ov_SC04_011_jr_8015A3C8.o": 0.063,
"build/src/ov_SC04_011/ov_SC04_011_jr_8015AE2C.o": 0.095,
"build/src/ov_SC04_011/ov_SC04_011_jr_8015C32C.o": 0.335,
"build/src/ov_SC04_011/ov_SC04_011_jr_8016AB6C.o": 0.217,
"build/src/ov_SC04_011/ov_SC04_011_jr_80171B4C.o": 0.087,
"build/src/ov_SC04_011/ov_SC04_011_jr_801734BC.o": 0.155,
"build/src/ov_SC04_011/ov_SC04_011_jr_801789AC.o": 0.055,
"build/src/ov_SC04_011/ov_SC04_011_jr_80178D40.o": 0.086,
"build/src/ov_SC04_011/ov_SC04_011_jr_8017A4AC.o": 0.061,
"build/src/ov_SC04_011/ov_SC04_011_jr_8017AE2C.o": 0.105,
"build/src/ov_SC04_011/ov_SC04_011_jr_8017D494.o": 0.404,
"build/src/ov_SC04_011/ov_SC04_011_o0b.o": 0.044,
"build/src/ov_SC04_011/ov_SC04_011_o0c.o": 0.049
},
"ok": true,
"seconds": 2.2
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -5386,9 +5386,9 @@ void func_8018AD74(s32 p1, s32 p2, s32 p3, s32 p4)
local_30 = D_800AE620;
obj = func_8012C658((s16)p2, (s16)p3, p1);
do { } while (0); // !FAKE: do-while — its NOTE_INSN_LOOP_END ends cse1's extended block (cse.c:8054), so m below is not folded onto the struct copy's address pseudo (cse.c:846-862) and is set AFTER obj's copy: sched1 then ranks it last by LUID (sched.c:2428) (P36 S104 e5 minimum-lever)
m = &local_30;
if (obj != 0) {
__asm__("" : "=r"(obj) : "0"(obj)); /* §350 birthing-boost kill; 0 bytes */ // !FAKE: launder — NEEDED DIFFERS (P36 rung B tus9)
m = &local_30;
*(u16 *)(obj + 0xA) = *(u16 *)(obj + 0xA) - 0x80;
RotMatrixY((s16)p4, m);
func_800484EC((s32)m, (s32)&D_801CD468, (s32)out);