phase11: merge 55 + cookbook 123 SOLVED + 167-168 — 592 bodies / 601 regions

Worker E's 0x8002311C (160 B) CLOSES COOKBOOK 123'S OPEN QUESTION. Finding 123 recorded the
branchless MAX0 (x & -(x > 0)) as unreached -- 'no ternary and no bitwise spelling reached it'.
Worker E solved it: the lever is NAMING THE BOOLEAN.

  return s & -(s > 0);        -> BRANCHES
  return s > 0 ? s : 0;       -> branches
  flag = s > 0; return s & -flag;  -> EXACT (slt / negu / and)

Mechanism: naming the comparison forces cc1 to materialise it as a VALUE (slt) rather than a
test feeding a branch. That is finding 44's 'name the boolean' lever applied to the MAX half --
finding 44 previously had only the cond-into-&& direction for this family.

167: a 4-byte store cc1 DELETES means the object's address is never taken -- fold the word into
the array whose address IS taken by a call.

168: s = f(); s += f(); s += f(); loses one instruction vs three named results summed.
This commit is contained in:
Christopher Williams
2026-09-24 11:22:01 -04:00
parent db6022c9f7
commit c091483083
6 changed files with 964 additions and 736 deletions
+730 -731
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -81,6 +81,7 @@
0x80022FCC 0x80022FFC src/func_80022FCC.c
0x80023080 0x800230E4 src/func_80023080.c
0x800230E4 0x8002311C src/func_800230E4.c
0x8002311C 0x800231BC src/func_8002311C.c
0x80024C14 0x80024C34 src/func_80024C14.c
0x80025070 0x800250AC src/func_80025070.c
0x800259A0 0x800259DC src/func_800259A0.c
1 # Code-region registry: one C region per matched function.
81 0x80022FCC
82 0x80023080
83 0x800230E4
84 0x8002311C
85 0x80024C14
86 0x80025070
87 0x800259A0
+33 -5
View File
@@ -2010,11 +2010,21 @@ Worker B's `0x800B704C` (324 B, a bounding-box builder) split cleanly across thr
**So: a signed "clamp to zero below" written as a bitwise mask compiles branchlessly, while the same
thing written as a ternary compiles with a branch.** Record the MIN half as a positive lever.
**The MAX half remains unreached.** The target idiom is branchless
`slt v0,zero,v1; negu v0,v0; and v1,v1,v0` — by value `x & -(x > 0)` — and **no ternary and no
bitwise spelling reached it**: the ternary branches, the bitwise form branches, the reversed ternary
changes the length. Worker B's plausible source is an **SDK min/max macro** whose expansion is not
yet identified. **This is a named open question, not a failed attempt.**
**The MAX half WAS an open question and worker E SOLVED IT — the lever is NAMING THE BOOLEAN.**
| spelling | result |
|---|---|
| `return s & -(s > 0);` | **BRANCHES** — `move v0,s0` / `bgez v0,L` / `move v0,zero` |
| `return s > 0 ? s : 0;` | branches |
| **`flag = s > 0; return s & -flag;`** | **EXACT — `slt v0,zero,s0` / `negu v0,v0` / `and v0,s0,v0`** |
**Mechanism: naming the comparison forces cc1 to materialise it as a VALUE (`slt`) rather than a test
feeding a branch**, and the `negu`/`and` then follow branchlessly. **This is finding 44's "name the
boolean" lever applied to the MAX half** — finding 44 previously had only the *cond-into-`&&`*
direction recorded for this family.
> **The bitwise spelling branches because the comparison stays INSIDE the expression.** Name it and
> the branchless form falls out.
### 124. THE PARTITION DESIGN CONFOUNDED THE SIZE/REDUNDANCY MEASUREMENT (worker B)
@@ -2716,3 +2726,21 @@ cheapest possible place to learn which one a row wants.
> **When `tools/sf3_family` reports a ratio-1.000 sibling, diff the raw words FIRST.** A one-word diff
> means a copy and a field change, not a derivation.
### 167. A 4-byte store cc1 DELETES means the object's address is never taken (worker E)
Worker E's `0x8002311C`: a local must be `int v[5]` with the 5th word written at the vector's own
`base+16`. Writing it as a separate `int w[1]; w[0] = s;` makes cc1 **delete the store** — constant
index, address never taken ⇒ dead — the frame drops 56 → 48 and the region is **8 bytes short.**
> **Folding the word into the array whose address IS taken by the first call keeps it**, because the
> call could read `v[4]`. **A deleted 4-byte store is evidence that the address must be taken.**
### 168. `s = f(); s += f(); s += f();` loses one instruction (worker E)
The original keeps each result in a **named local**: `a = f(); b = f(); c = f(); s = a + b + c;` —
two `move`s (one in the 2nd call's delay slot, one in the 3rd's) then `addu s0,s0,s1` /
`addu s0,s0,v0`. **The `+=` form emits one move and two adds, and is 4 bytes SHORT.**
Same family as findings 156 and 157: **a chained accumulation is not the same source as three named
results summed.**
+75
View File
@@ -0,0 +1,75 @@
/*
* func_800182F4 — 144 bytes at 0x800182F4..0x80018384
*
* Two-way dispatch on two HIGH bits of a flag word: copy six sign-extended halfwords up into the
* record, or hand both objects to a helper. Returns 0. First spelling, default toolchain.
*
* addiu sp,sp,-0x18 / move v1,a0 / move a0,a1 / sw ra,16(sp)
* frame 24 with ra at 0x10. The two parameters are moved:
* the RECORD (a0) goes to v1 and the SOURCE ARRAY (a1) takes
* over a0 -- which is cc1's argument-register preference
* again, because the source array is the helper's FIRST
* argument below.
* lw a1,40(v1) flags = *(int *)(rec + 40)
* lui v0,0x400 / and v0,a1,v0
* beqz v0,ELSE / lui v0,0x100 if (flags & 0x4000000) { ... } else if (flags & 0x1000000)
* lh v0,10(a0) / sw v0,0(v1) p[0] = src[5]; (halfword load, WORD store)
* lh v0,12(a0) / sw v0,4(v1) p[1] = src[6];
* lh v0,14(a0) / sw v0,8(v1) p[2] = src[7];
* lh v0,16(a0) / sw v0,16(v1) p[4] = src[8]; <-- 12 IS SKIPPED
* lh v0,18(a0) / sw v0,20(v1) p[5] = src[9];
* lh v0,20(a0) / j END / sw v0,24(v1)
* p[6] = src[10]; the last store is in the `j` delay slot
* ELSE:
* and v0,a1,v0 / beqz v0,END
* jal 0x8001C0B0 / move a1,v1 func_8001C0B0(src, rec)
* END: lw ra / move v0,zero / jr ra / addiu sp,sp,24
* `return 0` (cookbook 13: the explicit value is what puts
* the zero in v0).
*
* **THE MASKS ARE THE TRAP ON THIS ROW, AND IT IS COOKBOOK 30's.** The original emits
* `lui v0,0x400` and `lui v0,0x100` -- and `lui` loads the HIGH half, so those are
* **0x04000000 and 0x01000000**, i.e. bits 26 and 24, **not** 1024 and 256. The disassembly
* mnemonic reads exactly like a small-mask test and is wrong by 16 bits; the raw words
* (`3c020400`, `3c020100`) are the evidence. Anyone reconstructing `flags & 0x400` here gets a
* candidate that is right in shape and wrong in every bit.
*
* The other byte-required detail is the STORE OFFSETS: 0, 4, 8, **16**, 20, 24 -- word index 3
* (byte 12) is skipped. Six stores, six loads, and the hole is real; a source that fills 12 as
* well shifts every later store by 4 and cannot match.
*
* **No `maspsx=epilogue` token.** This row is on the epilogue-shape list because the original's
* tail is `jr ra` with `addiu sp,sp,24` in the jump slot, but cc1 fills that slot ITSELF (there
* are three instructions -- `lw ra` / `move v0,zero` -- before the `jr`, so the release is
* reachable). The default toolchain matches at 144 bytes and `--fill-epilogue` is a NO-OP here
* (144/0 both ways). Third row where a tail-shape scan of the ORIGINAL over-included, after
* 0x80091674 and 0x8007D5FC.
*
* LIMITS: func_8001C0B0 is a cross-reference by address only and its purpose is not established.
* `rec` is treated as an `int *` with the flag word read at byte 40 and the stores at word indices
* 0/1/2/4/5/6; `src` is a `short *` read at halfword indices 5..10. The two objects' declared
* types, names and true layouts are not recovered -- only the access widths and offsets are
* evidence, and the halfword loads are the only reason to call src `short`. The flag bits are named
* by their values only.
*/
extern void func_8001C0B0(short *a0, int a1);
int func_800182F4(int rec, short *src)
{
int *p = (int *)rec;
int flags = *(int *)(rec + 40);
if (flags & 0x4000000) {
p[0] = src[5];
p[1] = src[6];
p[2] = src[7];
p[4] = src[8];
p[5] = src[9];
p[6] = src[10];
} else if (flags & 0x1000000) {
func_8001C0B0(src, rec);
}
return 0;
}
+85
View File
@@ -0,0 +1,85 @@
/*
* func_8002311C — 160 bytes at 0x8002311C..0x800231BC
*
* Copy a 4-word vector into a 5-word local, hand the local to a helper, dot it against three
* pairs of words from a1 through a second helper, sum the three results, publish the sum into
* the local's 5th word, and return the sum clamped at zero below. Third spelling.
*
* addiu sp,sp,-0x38 / sw s1,44 / move s1,a1 / sw ra,48 / sw s0,40
* frame 56 = 16 outgoing + 20 (the v[5] local at 0x10) +
* 8 scratch + s0/s1/ra. a1 is kept in s1 (used by the
* first call and at every dot site).
* lw v0,0(a0) .. lw a2,12(a0) four loads ...
* sw v0,16(sp) .. sw a2,28(sp) ... then four stores: one 16-byte STRUCT ASSIGNMENT
* (cookbook 28/102).
* addiu a0,sp,16 / jal 0x80010B14 / move a1,s1
* func_80010B14(v, a1) -- the local's address is TAKEN here,
* which is what keeps the v[4] store below alive.
* lw a0,16(sp) / lw a1,0(s1) ... three dot calls func_80010654(v[i], a1[i])
* slt v0,zero,s0 / negu v0,v0 / and v0,s0,v0
* the branchless clamp: v0 = s & -(s > 0)
*
* THREE THINGS ARE BYTE-REQUIRED, AND EACH WAS A DISTINCT ROOT CAUSE:
*
* 1. **THE LOCAL IS `int v[5]`, NOT A `V` PLUS A SECOND LOCAL.** The 5th word (`sw s0,32(sp)`)
* is at 0x20 = the vector's own base + 16, and the frame is 56 not 48. Writing it as a
* separate `int w[1]; ... w[0] = s;` makes cc1 **delete the store entirely** -- the frame
* drops to 48 and the region is 8 bytes short -- because `w`'s address is never taken, so a
* constant-index store to it is dead. Folding the word into the array whose address IS taken
* by the first call keeps it: the call could read v[4], so the store survives.
* **Diagnostic: a 4-byte store to a local that cc1 deletes is a store to an object whose
* address is never taken; make it part of the object whose address is.**
* 2. **EACH DOT RESULT IS A NAMED LOCAL.** `s = f(v[0],..); s += f(v[1],..); s += f(v[2],..);`
* emits ONE `move s0,v0` and two `addu`s; the original has TWO moves (`move s0,v0` in the
* second call's delay slot and `move s1,v0` in the third's) then `addu s0,s0,s1` /
* `addu s0,s0,v0`. So the source is three separate locals and one sum: `a = f(..); b = f(..);
* c = f(..); s = a + b + c;` -- the third result needs no move because it is consumed
* immediately by the add. One instruction, and it presents as a 4-byte LENGTH-MISMATCH.
* 3. **THE CLAMP IS REACHED BY NAMING THE BOOLEAN -- THIS CLOSES COOKBOOK 123's OPEN QUESTION.**
* Cookbook 123 recorded the branchless MAX0 (`slt v0,zero,s` / `negu v0,v0` / `and v0,s,v0`,
* i.e. by value `s & -(s > 0)`) as unreached: "no ternary and no bitwise spelling reached it",
* with the ternary branching and the bitwise form branching too. **Writing `s & -(s > 0)`
* branches** (measured here: `move v0,s0` / `bgez` / `move v0,zero`). **Writing
*
* flag = s > 0;
* return s & -flag;
*
* is exact**: naming the comparison forces cc1 to materialise it as a VALUE (`slt`) rather
* than a test feeding a branch, and the `negu`/`and` then follow branchlessly. This is
* cookbook 44's "name the boolean" lever applied to the MAX half; the naming is the whole
* difference between the branchy and branchless forms.
*
* The `int v[5]` element type is `int` because every access is a full word (`lw`/`sw`), and the
* copy is a 16-byte struct assignment rather than five element stores (which would interleave and
* cost 8 bytes). `v[3]` is copied by the struct assignment and never read -- the same
* fourth-word-unused shape as func_80028CE0/func_80028C34.
*
* LIMITS: both callees are cross-references by address only and their purposes are not
* established; the summary line is read off this body's shape. `a1` is indexed 0/1/2 as an
* `int *`; whether the original spelled those as fields is not recoverable. The clamp's reading as
* "at least zero" follows from the emitted `s & -(s > 0)` on signed values.
*/
struct V { int v[4]; };
extern void func_80010B14(int *a0, int *a1);
extern int func_80010654(int a0, int a1);
int func_8002311C(struct V *a0, int *a1)
{
int v[5];
int a, b, c, s, flag;
*(struct V *)v = *a0;
func_80010B14(v, a1);
a = func_80010654(v[0], a1[0]);
b = func_80010654(v[1], a1[1]);
c = func_80010654(v[2], a1[2]);
s = a + b + c;
v[4] = s;
flag = s > 0;
return s & -flag;
}
+40
View File
@@ -0,0 +1,40 @@
/*
* func_800FBE84 — 216 bytes at 0x800FBE84..0x800FBF5C
*
* Leaf routine that writes three packed-decimal bytes into the caller's buffer.
*
* The five unsigned magic-number divisions are the constant-division signature
* (`multu` + `mfhi` + `srl`, no `mflo`). Reading each shift off the code gives
* the divisors: 0x1B4E81B5 srl 3 = /75, 0x88888889 srl 5 = /60, and
* 0xCCCCCCCD srl 3 = /10 (used three times).
*
* Reconstructed value flow (n = arg + 150):
* a = n / 75; b = a / 60; t = n % 75;
* buf[2] = (t / 10) * 16 + t % 10;
* buf[1] = ((a % 60) / 10) * 16 + (a % 60) % 10;
* buf[0] = (b / 10) * 16 + b % 10;
* Each byte is a `(x / 10) << 4 | (x % 10)` packing (the `<< 4` is emitted as
* `sll`), and the second division of each pair is CSE'd with the first.
*
* `move v0,a1` sets the result to the buffer pointer before the stores; the
* frame is empty and the delay slot is the last `sb`.
*
* LIMITS: the name, the parameter types (the `multu`/`srl` forms fix the value
* arithmetic as unsigned) and the meaning of the three values are hypotheses
* reconstructed from the bytes; only the compiled bytes are evidence.
*/
unsigned char *func_800FBE84(unsigned int a0, unsigned char *a1)
{
unsigned int n = a0 + 150;
unsigned int a = n / 75;
unsigned int b = a / 60;
unsigned int t = n % 75;
unsigned int c;
a1[2] = (t / 10) * 16 + t % 10;
c = a % 60;
a1[1] = (c / 10) * 16 + c % 10;
a1[0] = (b / 10) * 16 + b % 10;
return a1;
}