phase10: merge 15 + size-first re-ranking — 451 distinct bodies / 460 regions

+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.
This commit is contained in:
Christopher Williams
2026-09-24 07:43:34 -04:00
parent 53365e0df9
commit e89365295b
3 changed files with 111 additions and 2 deletions
+2 -2
View File
@@ -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
1 # Syphon Filter 3 (USA) match worklist.
1272 1265
1273 #
1274 # listed=1265
1275 # excluded_already_registered=459 # excluded_already_registered=460
1276 # excluded_bad_extent_start=8
1277 # excluded_degenerate_body=252
1278 # excluded_delay_slot_start=5
1280 # excluded_low_confidence_grade=90
1281 # excluded_named_exclusion=9
1282 # excluded_no_extent=0
1283 # excluded_recorded_negative=142 # excluded_recorded_negative=141
1284 # excluded_trapping_arith=54
+1
View File
@@ -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
1 # Code-region registry: one C region per matched function.
359 0x800BFEC0
360 0x800BFEE0
361 0x800BFF00
362 0x800C1424
363 0x800C1EA8
364 0x800C5C84
365 0x800F2F6C
+108
View File
@@ -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);
}