From e89365295bd0e507f55df453e9b333aec77d29fb Mon Sep 17 00:00:00 2001 From: Christopher Williams Date: Thu, 24 Sep 2026 07:43:34 -0400 Subject: [PATCH] =?UTF-8?q?phase10:=20merge=2015=20+=20size-first=20re-ran?= =?UTF-8?q?king=20=E2=80=94=20451=20distinct=20bodies=20/=20460=20regions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +1 body (0x800C1424, worker C claim 18, closed with two register-allocation levers: guard-on-the-expression so CSE keeps one load whose destination is the guard's operand, and a counter initialisation as a statement so the counter takes a2). FORECAST FINDING (the important part of this commit): measured the matched corpus against the remaining pool and re-ranked the lever files. matched: 459 regions, median 48 bytes, 454 of 459 at <=200 bytes remaining levered rows: median 456 bytes Size is therefore the strongest predictor left, so .run/p10/lever*.tsv now ranks size band FIRST and lever second: P1 = <=200B and levered (47 rows across partitions) P2 = <=200B, no lever (412) <- the unexploited band that actually matches P3 = levered but >200B (503) P4 = rest (303) Worker C had proposed continuing on fresh P1 rows (old meaning: known-callee), which under the new ranking are mostly P3 -- the 200-800B band where the allocator/optimiser tie-breaks live. Redirected to P2 from the top. Worker C's measured re-flag accepted (last 12 attempts produced 1 match, 8 of 9 sub-8-byte negatives being cc1 scheduling/allocation with no spelling lever) and answered with a band change rather than a stop, since C is at 47% context. --- config/match_worklist.tsv | 4 +- config/regions.tsv | 1 + src/func_800C1424.c | 108 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 src/func_800C1424.c diff --git a/config/match_worklist.tsv b/config/match_worklist.tsv index a6d4c36..79a3ecb 100644 --- a/config/match_worklist.tsv +++ b/config/match_worklist.tsv @@ -1272,7 +1272,7 @@ 1265 0x801007E0 0x80100808 40 fallthrough 3 frame 1 - 1 # # listed=1265 -# excluded_already_registered=459 +# excluded_already_registered=460 # excluded_bad_extent_start=8 # excluded_degenerate_body=252 # excluded_delay_slot_start=5 @@ -1280,5 +1280,5 @@ # excluded_low_confidence_grade=90 # excluded_named_exclusion=9 # excluded_no_extent=0 -# excluded_recorded_negative=142 +# excluded_recorded_negative=141 # excluded_trapping_arith=54 diff --git a/config/regions.tsv b/config/regions.tsv index 3975897..f1f3df8 100644 --- a/config/regions.tsv +++ b/config/regions.tsv @@ -359,6 +359,7 @@ 0x800BFEC0 0x800BFEE0 src/func_800BFEC0.c 0x800BFEE0 0x800BFF00 src/func_800BFEE0.c 0x800BFF00 0x800BFF20 src/func_800BFF00.c +0x800C1424 0x800C14BC src/func_800C1424.c 0x800C1EA8 0x800C1F04 src/func_800C1EA8.c 0x800C5C84 0x800C5CBC src/func_800C5C84.c 0x800F2F6C 0x800F2FB8 src/func_800F2F6C.c maspsx=off diff --git a/src/func_800C1424.c b/src/func_800C1424.c new file mode 100644 index 0000000..46ce016 --- /dev/null +++ b/src/func_800C1424.c @@ -0,0 +1,108 @@ +/* + * func_800C1424 — 152 bytes at 0x800C1424..0x800C14BC + * + * Byte-identical reconstruction of a framed table search and release: a + * pointer field on the object is the base of 30 twelve-byte records, the record + * whose first word matches a key is found, and that record is released through + * two calls with the found pointer cleared. + * + * The observed instructions are: + * addiu sp,sp,-32 + * sw ra,24(sp) + * sw s1,20(sp) + * sw s0,16(sp) + * lw a0,468(a0) base = *(int **)(p + 468) + * nop (load delay) + * beqz a0,0x800C14A4 if (base == 0) return + * nop + * beqz a1,0x800C14A4 if (key == 0) return + * move s0,zero (delay slot) found = 0 + * move a2,zero i = 0 + * move s1,a0 base (copied for the calls) + * addiu v1,s1,2356 e = (char *)base + 2356 + * lw v0,0(v1) + * nop + * bne v0,a1,0x800C1470 if (*e == key) { found = e; break; } + * nop + * j 0x800C1480 + * move s0,v1 (delay slot) + * addiu a2,a2,1 i++ + * slti v0,a2,30 + * bnez v0,0x800C1458 + * addiu v1,v1,12 (delay slot) e += 12 + * beqz s0,0x800C14A4 if (found == 0) return + * move a0,s1 (delay slot) + * move a1,zero + * jal 0x800263A8 + * move a2,s0 (delay slot) + * sw zero,0(s0) *found = 0 + * move a0,s1 + * jal 0x80026560 + * move a1,v0 (delay slot) + * lw ra,24(sp) + * lw s1,20(sp) + * lw s0,16(sp) + * addiu sp,sp,32 + * jr ra + * nop + * + * The frame is 32 bytes: the 16-byte o32 outgoing argument area, `s0` at + * 16(sp), `s1` at 20(sp) and `ra` at 24(sp). TWO source-shape facts are + * load-bearing, and both are about register allocation rather than semantics: + * + * 1. The guard must be written against the **expression**, not the local: + * `if (*(int **)(p + 468) == 0) return;` and only then + * `base = *(int **)(p + 468);`. cc1's CSE then keeps ONE load whose + * destination is the guard's operand (`a0`) and emits the `move s1,a0` copy + * for the longer live range. Writing the guard against the local instead + * makes cc1 load straight into `s1` and the copy disappears (148 bytes). + * 2. `i = 0;` must be a **statement before** the base assignment, not the + * `for`-initialiser: scheduled early, `a0` still holds base, so the loop + * counter takes `a2` (the original) rather than `a0` (152 vs 12 differing + * bytes with the counter in a0). + * + * The search is a counted loop with a byte-stride induction pointer (`v1 += 12`) + * and a separate counter, and the "found" path sits behind a `j`, so the loop is + * written as a `break`. `*found = 0` is a separate statement between the two + * calls, which is why the store follows the first `jal`. + * + * LIMITS: the function name, the callees' arities and parameter types, the + * record stride 12, the count 30, the base offset 2356 and the key's meaning + * are hypotheses reconstructed from the disassembly. Only the compiled bytes + * are evidence. + */ + +int func_800263A8(int *, int, int *); +void func_80026560(int *, int); + +void func_800C1424(char *p, int key) { + int *e; + int *found; + int i; + int *base; + int r; + + if (*(int **)(p + 468) == 0) + return; + if (key == 0) + return; + + found = 0; + i = 0; + base = *(int **)(p + 468); + e = (int *)((char *)base + 2356); + for (; i < 30; i++) { + if (*e == key) { + found = e; + break; + } + e = (int *)((char *)e + 12); + } + + if (found == 0) + return; + + r = func_800263A8(base, 0, found); + *found = 0; + func_80026560(base, r); +}