phase11: merge 24 + cookbook 104 + workflow protocol — 542 bodies / 551 regions

Worker A's 0x80033DC8 (360 B) and 0x8006A98C (132 B), plus two gp symbol rows
(D_80122724, D_80122728) that unblock 0x800A4CA8.

PROCESS DEFECT FOUND AND FIXED. Worker C and worker D both matched 0x800320D8
independently and D overwrote C's source file. Nothing corrupted -- both spellings match
and the region still reports 276/0/MATCH -- but one worker's effort was duplicated. The
partitions are genuinely disjoint (273/279/278/271, union 1101 = sum), so there was NO
assignment error: the gap was that no worker could know another had started a row, since
the registry only knows about MERGED claims and both started before either merged. The
root cause is the adjacency rule (cookbook 99, 4-for-4) crossing partition boundaries --
the best dispatch heuristic found so far invalidated the assumption the assignment rested on.

Fix: .run/p11/inflight.tsv (write-ahead log alongside the merge registry's commit log),
with the protocol written up in docs/ORCHESTRATOR_WORKFLOW.md so the next orchestrator
inherits it.
This commit is contained in:
Christopher Williams
2026-09-24 10:07:56 -04:00
parent 197a070f2f
commit 6b7239d83d
7 changed files with 328 additions and 83 deletions
+2
View File
@@ -145,6 +145,7 @@
0x80031FC4 0x800320D8 src/func_80031FC4.c
0x800320D8 0x800321EC src/func_800320D8.c
0x800321EC 0x800321F8 src/func_800321EC.c
0x80033DC8 0x80033F30 src/func_80033DC8.c
0x80034A80 0x80034ABC src/func_80034A80.c
0x800354F8 0x80035548 src/func_800354F8.c
0x80036134 0x8003622C src/func_80036134.c
@@ -254,6 +255,7 @@
0x80069580 0x800695D8 src/func_80069580.c
0x800697A4 0x800697C4 src/func_800697A4.c
0x800697C4 0x800697FC src/func_800697C4.c
0x8006A98C 0x8006AA10 src/func_8006A98C.c
0x8006AA10 0x8006AA88 src/func_8006AA10.c
0x8006ADB0 0x8006AE04 src/func_8006ADB0.c
0x8006B184 0x8006B1CC src/func_8006B184.c
1 # Code-region registry: one C region per matched function.
145 0x80031FC4
146 0x800320D8
147 0x800321EC
148 0x80033DC8
149 0x80034A80
150 0x800354F8
151 0x80036134
255 0x80069580
256 0x800697A4
257 0x800697C4
258 0x8006A98C
259 0x8006AA10
260 0x8006ADB0
261 0x8006B184
+2
View File
@@ -381,6 +381,8 @@ D_80122718 0x80122718 gp
D_8012271A 0x8012271A gp
D_8012271C 0x8012271C gp
D_80122720 0x80122720 gp
D_80122724 0x80122724 gp
D_80122728 0x80122728 gp
D_8012272C 0x8012272C gp
D_80122734 0x80122734 gp
D_80122738 0x80122738 gp
1 # Symbol registry: absolute addresses for cross-references used by C regions.
381 D_8012271A
382 D_8012271C
383 D_80122720
384 D_80122724
385 D_80122728
386 D_8012272C
387 D_80122734
388 D_80122738
+58 -1
View File
@@ -1556,7 +1556,39 @@ block as the `then`** — `if (a0 != D1) { if (a0 == D2) k = 1; else k = -1; } e
`x = a1[0] - a0[0]` matches; `x = -x` emits `negu` and **costs 8 bytes**. Also on that row:
`y = ...` must be **assigned after** the x branch — an initialiser gives 124 B instead of 136.
### 102. Vector copies: FIVE independent confirmations
### 102. Vector copies: FIVE independent confirmations — but the rule STOPS before 128 bytes
Struct-assignment vector copies (4 loads then 4 stores) are now confirmed on five independent rows:
`0x8005584C`, `0x8009F798`, `0x80036DA4`, `0x80036B14`, `0x800556E8`.
**But worker B found the ceiling.** `0x800689DC` is a **fully unrolled 8x16-byte copy** (128 bytes,
`a0+64` -> `a0+0`, returns 1). This compiler will **not** produce that shape:
| spelling | bytes |
|---|---|
| 128-byte struct assignment | 64-72 (a 4-word **loop**) |
| 8-iteration loop | 72 |
| 8 explicit statements | 264 (overshoots) |
**So a 128-byte block move is LOOPED by this compiler — the original's unrolled form is not a
struct assignment.** The struct-assignment rule holds at 16/32 bytes but **not at 128**. If a
128-byte copy in the corpus has a matched source, its shape would settle the question.
Companions, re-confirmed: **OR-chain `||` codegen** — every `bnez` jumps to the SAME forward target
and the last term inverts to skip the body; `t.v[0]=t.v[1]=t.v[2]=*a3` reads one address three times
with no CSE; and **byte offsets, not `int *` arithmetic** (`244(v0)` vs `p+244` = +976).
### 103. Finding 42's "written order decides" has a COUNTEREXAMPLE (worker B)
Worker B's `0x800321F8` (2 bytes): the operand order of two `addu`s is `addu v0,a0,v0` in the
original and `addu v0,v0,a0` from cc1. **Re-spelling the sum as `i*60 + a0` (writing `a0` second)
did NOT flip the encoding.**
**Qualification for finding 42: when the base is a POINTER PARAMETER combined with an
already-scaled index, the operand order is decided elsewhere — by pseudo creation order — not by
the source.** Finding 42's rule applies to the arithmetic-operand case it was derived from, not
universally.
Struct-assignment vector copies (4 loads then 4 stores) are now confirmed on five independent rows:
`0x8005584C`, `0x8009F798`, `0x80036DA4`, `0x80036B14`, `0x800556E8`. Treat it as a rule.
@@ -1564,3 +1596,28 @@ Struct-assignment vector copies (4 loads then 4 stores) are now confirmed on fiv
Companions, re-confirmed: **OR-chain `||` codegen** — every `bnez` jumps to the SAME forward target
and the last term inverts to skip the body; `t.v[0]=t.v[1]=t.v[2]=*a3` reads one address three times
with no CSE; and **byte offsets, not `int *` arithmetic** (`244(v0)` vs `p+244` = +976).
### 104. The adjacency rule CROSSES partitions — the in-flight ledger (process)
Finding 99 (take the row adjacent to one you just matched) is 4-for-4 across two workers and
beats every ranker, so it is now the primary dispatch rule. **But it has a structural cost:**
the worker partitions are disjoint, while adjacency sends a worker to a neighbour that is
frequently in **another** worker's partition.
On `0x800320D8` **worker C and worker D both matched the same region independently**, and D
overwrote C's source file. Nothing was corrupted — both spellings are valid and the region
still reports `276 bytes / 0 differing / MATCH` — but a worker's whole effort was duplicated.
**Root cause was mine, not the workers':** the partitions were verified disjoint (273/279/278/271,
union 1101 = sum), so no assignment error existed. The gap was that **no worker had any way to
know another had started a row.** The registry only knows about *merged* claims, and both
workers started before either was merged.
**Fix: `.run/p11/inflight.tsv`, an append-only in-flight ledger.** Before starting a row, check
`config/regions.tsv` **and** the ledger; append your address before you begin. This is the
distributed-systems answer — the merge registry is the commit log, and this is the write-ahead
log.
**Generalised lesson for the next orchestrator: a heuristic that makes workers more effective
can invalidate the assumption your work assignment rests on.** Adjacency optimised *which* row
to take without anyone revisiting *who owns it*.
+28
View File
@@ -383,3 +383,31 @@ Each of these was earned by a real incident. Quote them in charters; they are no
- Do not commit ROM-derived material, build outputs, or session archives.
- Do not close a herdr pane it did not create.
- Do not estimate context, and do not accept an estimated context figure from a worker.
## In-flight claim ledger (Phase 11 addition)
**The problem.** The worker partitions are disjoint, but the **adjacency rule** — take the row
next to one you just matched — is the strongest dispatch heuristic found (4-for-4 across two
workers, beating the redundancy ranker and the size ranker). Adjacency **crosses partition
boundaries**, so a worker regularly reaches a row that belongs to another worker's partition.
On `0x800320D8` worker C and worker D both matched the same region independently, and D
overwrote C's source file. Nothing was corrupted — both spellings match — but a worker's whole
effort was duplicated. **The partitions were verified disjoint** (273/279/278/271, union 1101 =
sum), so there was no assignment error. The gap was that **the registry only knows about MERGED
claims**, and both workers started before either was merged.
**The protocol.**
1. **Before starting a row**, check `config/regions.tsv` **and** `.run/p11/inflight.tsv`.
If the address is in either, pick a different row.
2. **Append your address to `.run/p11/inflight.tsv` before you begin** — `address<TAB>worker<TAB>size`.
Append only; never edit or remove a row.
The registry is the **commit log**; the ledger is the **write-ahead log**. `.run/` is gitignored,
so the ledger is session-scoped by design — it coordinates live workers, not history.
**The generalisable lesson: a heuristic that makes workers more effective can invalidate the
assumption the work assignment rests on.** Adjacency optimised *which* row to take without anyone
revisiting *who owns it*. When you adopt a worker-discovered heuristic, re-check what it assumes
about your own process.
+61 -82
View File
@@ -1,97 +1,76 @@
/*
* func_800320D8 — 276 bytes at 0x800320D8..0x800321EC
*
* Phase 11, band ABOVE the old 244-byte ceiling. First-attempt match, taken
* directly from its sibling func_80031FC4 (same body, same local layout, same
* callee shape — only the object offsets and the callee address differ).
* Goal B, Phase 11. **Found by the ADJACENCY rule, not by the size ranker**: it sits 1048 bytes from
* this worker's 0x80031BBC match, and it is the same author's shape. Three spellings.
*
* Fills one 4-word row from a helper, then fills two more rows from two
* optional 4-word blocks of an object — each block either copied whole or as
* its first word only, gated by a separate flag — and finally writes three
* component differences plus one raw component.
* What the body is: a 3-vector difference with two conditional 4-word loads.
*
* The observed instructions are:
* addiu sp,sp,-80 / sw s0,64(sp) / move s0,a0 / sw s1,68(sp) / move s1,a1
* sw s2,72(sp) / move s2,a2 / sw ra,76(sp)
* jal func_80031F78 / addiu a1,sp,16
* lw a2,164(s0)
* lw v0,976(a2) / nop / bnez v0,full1 / nop
* lw v0,1032(a2) / j join1 / sw v0,32(sp)
* full1:
* lw v0,1032(a2) / lw v1,1036(a2) / lw a0,1040(a2) / lw a1,1044(a2)
* sw v0,32(sp) / sw v1,36(sp) / sw a0,40(sp) / sw a1,44(sp)
* join1:
* lw a2,164(s0)
* lw v0,2272(a2) / nop / bnez v0,full2 / nop
* lw v0,2328(a2) / j join2 / sw v0,48(sp)
* full2:
* lw v0,2328(a2) / lw v1,2332(a2) / lw a0,2336(a2) / lw a1,2340(a2)
* sw v0,48(sp) / sw v1,52(sp) / sw a0,56(sp) / sw a1,60(sp)
* join2:
* lw v0,32(sp) / lw v1,16(sp) / nop / subu v0,v0,v1 / sw v0,0(s1)
* lw v0,36(sp) / lw v1,20(sp) / nop / subu v0,v0,v1 / sw v0,4(s1)
* lw v0,40(sp) / lw v1,24(sp) / nop / subu v0,v0,v1 / sw v0,8(s1)
* lw v0,56(sp) / nop / sw v0,0(s2)
* lw ra,76(sp) / lw s2,72(sp) / lw s1,68(sp) / lw s0,64(sp)
* addiu sp,sp,80 / jr ra / nop
* func_80031F78(a0, t); <- t is a 4-int local at sp+0x10
* obj = *(int **)(a0 + 164);
* if (*(int *)((char *)obj + 976) == 0)
* v.a = *(int *)((char *)obj + 1032);
* else
* v = *(V4 *)((char *)obj + 1032); <- a 16-byte struct ASSIGNMENT
* obj = *(int **)(a0 + 164); <- re-read, per block
* if (*(int *)((char *)obj + 2272) == 0)
* w.a = *(int *)((char *)obj + 2328);
* else
* w = *(V4 *)((char *)obj + 2328);
* a1[0] = v.a - t[0];
* a1[1] = v.b - t[1];
* a1[2] = v.c - t[2];
* a2[0] = w.c;
*
* The four byte-required facts are the same four as its sibling func_80031FC4,
* and each was established there:
* 1. `a0->f164` is RE-READ for the second block (a local pointer is one
* instruction short) because the stores into the local matrix can alias it.
* 2. The condition is INVERTED with the arms swapped, so the branch is `bnez`
* TO the four-word block with the single store inline (mirrored-layout
* lever, per guard).
* 3. The four-word copy is a STRUCT assignment, which batches the loads; four
* element stores serialise them behind four maspsx load-delay nops.
* 4. The matrix is 3x4 with a 16-byte row stride; the fourth column is never
* read.
* THE LEVER — a struct ASSIGNMENT, not four element stores. Four indexed stores
* (`v[0] = ...; v[1] = ...; v[2] = ...; v[3] = ...`) compile to **load/store PAIRS interleaved with
* load-delay nops**; the original batches **four loads then four stores**. Writing
* `v = *(V4 *)((char *)obj + 1032);` reproduces the batch exactly. This is cookbook 28's
* "struct assignment, not element stores, is what puts a fixed base in a register" family, applied
* to a *copy out of* an object rather than into one — and the tell is the batch shape.
*
* THE FAMILY LEVER: this row is the sibling of func_80031FC4 and shares its
* callee family (func_80031F78 vs func_80031F2C). Only the object offsets
* changed — the two flags stay at +976 and +2272, while the two 4-word blocks
* move to +1032 and +2328 (48 bytes later than the sibling's +984/+2280) — so
* the body, the local matrix, the branch polarities and the epilogue are
* identical and the match came on the first spelling.
* The object pointer is re-read once per block (not once per statement, and not once per function):
* a macro-style spelling re-reads it for EVERY statement and comes out **80 bytes long**, and a
* single read for the whole function does not re-read at all. **One assignment per block is the
* shape.** The two `if` blocks leave `v.b`/`v.c` uninitialised on the single-word path, and the
* original then subtracts them — an uninitialised read that is a real property of the original, not a
* reconstruction error.
*
* LIMITS: offsets and widths are read off the disassembly; everything between
* the named fields is padding reconstructed only to fix the offsets, and the
* real structures are certainly larger. `struct S`'s `+164` is a pointer. The
* callee's signature is inferred from its single call site.
* LIMITS: the function name, the callee, the meaning of the two field blocks and of the 164/976/1032/
* 2272/2328 offsets are hypotheses reconstructed from the disassembly; only the compiled bytes are
* evidence. `V4` is a 16-byte struct with only `.a`/`.b`/`.c` read; the 4-word copy is byte-required,
* the member names are not. The block offsets are recovered values.
*/
struct V4 { int v[4]; };
extern void func_80031F78(int a0, int *a1);
struct T {
char pad0[976];
int f976;
char pad_a[1032 - 980];
struct V4 v1;
char pad1[2272 - 1048];
int f2272;
char pad_b[2328 - 2276];
struct V4 v2;
};
typedef struct { int a, b, c, d; } V4;
struct S { char pad[164]; struct T *f164; };
extern void func_80031F78(struct S *a, int *b);
void func_800320D8(struct S *a0, int *a1, int *a2)
void func_800320D8(int a0, int *a1, int *a2)
{
int m[3][4];
int t[4];
V4 v;
V4 w;
int *obj;
func_80031F78(a0, m[0]);
if (a0->f164->f976 == 0)
m[1][0] = a0->f164->v1.v[0];
else
*(struct V4 *)m[1] = a0->f164->v1;
if (a0->f164->f2272 == 0)
m[2][0] = a0->f164->v2.v[0];
else
*(struct V4 *)m[2] = a0->f164->v2;
a1[0] = m[1][0] - m[0][0];
a1[1] = m[1][1] - m[0][1];
a1[2] = m[1][2] - m[0][2];
a2[0] = m[2][2];
func_80031F78(a0, t);
obj = *(int **)(a0 + 164);
if (*(int *)((char *)obj + 976) == 0) {
v.a = *(int *)((char *)obj + 1032);
} else {
v = *(V4 *)((char *)obj + 1032);
}
obj = *(int **)(a0 + 164);
if (*(int *)((char *)obj + 2272) == 0) {
w.a = *(int *)((char *)obj + 2328);
} else {
w = *(V4 *)((char *)obj + 2328);
}
a1[0] = v.a - t[0];
a1[1] = v.b - t[1];
a1[2] = v.c - t[2];
a2[0] = w.c;
}
+100
View File
@@ -0,0 +1,100 @@
/*
* func_80033DC8 — 360 bytes at 0x80033DC8..0x80033F30
*
* Hypothesis, not a claim about meaning: builds two 3-component positions, offsetting each
* by the object's translation, and stores their difference into the caller's vector. The
* second position comes either from a helper call or from a fixed sub-object, depending on
* three flags. Completely repetitive (six identical add-into-frame groups), which is why it
* matched on the SECOND spelling.
*
* Shape (a3 = a0, s0 = a1):
* 27BDFFC8 addiu sp,sp,-56
* 00803821 move a3,a0
* AFBF0034 sw ra,52(sp)
* AFB00030 sw s0,48(sp)
* 8CE0009C lw v0,156(a3) ; p = *(int **)(a0 + 156)
* 00A08021 move s0,a1
* 8C430090 lw v1,144(v0) ; v = *(V *)(p + 144) -- 4 loads then 4 stores
* 8C440094 lw a0,148(v0)
* 8C450098 lw a1,152(v0)
* 8C46009C lw a2,156(v0)
* AFA30010 sw v1,16(sp)
* ... AFA6001C sw a2,28(sp)
* 8FA20010 lw v0,16(sp) ; v[0] += a0[1191]
* 8CE1129C lw v1,4764(a3)
* 00431021 addu v0,v0,v1
* AFA20010 sw v0,16(sp)
* ... ; v[1] += a0[1192], v[2] += a0[1193]
* 9080129A lbu v0,4762(a3) ; if (*(u8 *)(a0+4762)) -> the else
* 1440002E bnez v0,0x80033E7C
* 24020006 li v0,6
* 8CE10134 lw v1,308(a3) ; a0[77]
* 14220027 bne v1,v0,0x80033E6C ; if (a0[77] != 6) -> THE CALL
* 00E02021 move a0,a3 ; (delay)
* 90800138 lbu v0,312(a3) ; if (*(u8 *)(a0+312)) -> the else
* 1440002B bnez v0,0x80033E7C
* 00000000 nop
* 0C00C7DE jal 0x80031F78 ; 0x80033E6C: func_80031F78(a0, w)
* 27A50020 addiu a1,sp,32 ; (delay)
* 0800CFB8 j 0x80033EE0
* 00000000 nop
* 8CE0009C lw v0,156(a3) ; 0x80033E7C: w = *(V *)(p + 4)
* ... AFA6002C sw a2,44(sp)
* ... ; w[0..2] += a0[1191..1193]
* 8FA20010 lw v0,16(sp) ; a1[0] = v[0] - w[0]
* 8FA10020 lw v1,32(sp)
* 00431023 subu v0,v0,v1
* AE020000 sw v0,0(s0)
* ... ; a1[1], a1[2]
* 8FBF0034 lw ra,52(sp) ; (END)
*
* **THE CONDITION IS `x == 0 && (y != 6 || z == 0)`, NOT `x == 0 && y == 6 && z == 0`.**
* The natural AND-of-three spelling gives the CORRECT LENGTH (360) and **7 differing
* bytes** — one branch displacement and the branch-delay placement — because it sends the
* `y == 6` failure to the else block, whereas the original sends it to the CALL. Reading
* the original's control flow literally: `bnez x, else; bne y,6, CALL; bnez z, else; CALL`.
* So the call runs when `x == 0 && (y != 6 || z == 0)`. **A correct-length candidate whose
* residual is a branch direction and a delay-slot instruction means the boolean STRUCTURE
* is wrong — check for a `||` hiding inside an `&&` (or vice versa) before touching
* anything else.**
*
* Also byte-required: both 4-word copies are STRUCT ASSIGNMENTS (seventh independent
* confirmation); the six add-into-frame groups are three separate `+=` statements per
* vector (each reloading both operands), not a loop.
*
* LIMITS: the function name, the callee, the object layout (the pointer at +156, the source
* vectors at +144 and +4, the translation at 4764/4768/4772, the flags at 4762/312 and the
* word at 308) and the whole "two positions and a difference" reading are hypotheses taken
* from the instruction shape; only the bytes are evidence. The `V` struct exists purely to
* obtain the 4-word block moves. func_80031F78 is not registered and is referenced by its
* address-named spelling.
*/
struct V { int v[4]; };
void func_80031F78(int *, int *);
void func_80033DC8(int *a0, int *a1)
{
struct V v;
struct V w;
v = *(struct V *)(*(char **)((char *)a0 + 156) + 144);
v.v[0] += a0[1191];
v.v[1] += a0[1192];
v.v[2] += a0[1193];
if (*(unsigned char *)((char *)a0 + 4762) == 0 &&
(a0[77] != 6 || *(unsigned char *)((char *)a0 + 312) == 0)) {
func_80031F78(a0, (int *)&w);
} else {
w = *(struct V *)(*(char **)((char *)a0 + 156) + 4);
w.v[0] += a0[1191];
w.v[1] += a0[1192];
w.v[2] += a0[1193];
}
a1[0] = v.v[0] - w.v[0];
a1[1] = v.v[1] - w.v[1];
a1[2] = v.v[2] - w.v[2];
}
+77
View File
@@ -0,0 +1,77 @@
/*
* func_8006A98C — 132 bytes at 0x8006A98C..0x8006AA10
*
* Hypothesis, not a claim about meaning: adds one 3-component vector into another, twice,
* each guarded by a flag byte. A leaf with a completely repetitive body — which is why it
* matched on the FIRST spelling.
*
* Original words (int-index form; byte offsets are 4x):
* 8C83000C lw a3,12(a0) ; p = *(int **)(a0 + 12)
* 30A100FF andi a1,a1,0xff
* 10A0000E beqz a1,0x8006A9D0 ; if ((a1 & 0xff) == 0) skip
* 304200FF andi v0,a2,0xff ; (delay) the SECOND guard's test, hoisted
* 8C620050 lw v0,80(a3) ; p[20] += p[24]
* 8C630060 lw v1,96(a3)
* 8C640064 lw a0,100(a3)
* 8C650068 lw a1,104(a3)
* 00431021 addu v0,v0,v1
* AC620050 sw v0,80(a3)
* 8C620054 lw v0,84(a3) ; p[21] += p[25]
* 8C630058 lw v1,88(a3)
* 00861021 addu v0,v0,a0
* 00650821 addu v1,v1,a1
* AC620054 sw v0,84(a3)
* AC630058 sw v1,88(a3) ; p[22] += p[26]
* 304200FF andi v0,a2,0xff ; 0x8006A9D0:
* 1040000B beqz v0,0x8006AA08 ; if ((a2 & 0xff) == 0) done
* 00000000 nop
* 8C6200D0 lw v0,208(a3) ; p[52] += p[56]
* 8C6300E0 lw v1,224(a3)
* 8C6400E4 lw a0,228(a3)
* 8C6500E8 lw a1,232(a3)
* 00431021 addu v0,v0,v1
* AC6200D0 sw v0,208(a3)
* 8C6200D4 lw v0,212(a3) ; p[53] += p[57]
* 8C6300D8 lw v1,216(a3)
* 00861021 addu v0,v0,a0
* 00650821 addu v1,v1,a1
* AC6200D4 sw v0,212(a3)
* AC6300D8 sw v1,216(a3) ; p[54] += p[58]
* 03E00008 jr ra
* 00000000 nop
*
* BYTE-REQUIRED SHAPES:
*
* 1. **The guards are the raw `a1 & 0xff` / `a2 & 0xff` expressions**, not `(x & 0xff) != 0`
* — cc1 emits `andi` + `beqz` either way, but the first guard's DELAY SLOT holds the
* second guard's `andi v0,a2,0xff`, which only appears if the source re-evaluates the
* mask in the second `if`.
* 2. **The two vectors are 16 bytes apart in the stride sense** — the addends are at
* +80/+84/+88 and +96/+100/+104, i.e. one 4-int vector at int offset 20 and another at
* 24. That is the project's 4-int vector layout again (the same shape as 0x80068910 and
* the `struct V` rows).
* 3. **Three separate `+=` statements per vector**, each reloading both operands. The
* middle pair shares its loads (`addu v0,v0,a0` / `addu v1,v1,a1` before two stores),
* which is exactly what three independent statements produce.
*
* LIMITS: the function name, the object layout (the pointer at +12, the vectors at int
* offsets 20/24 and 52/56) and the meaning of the two flag bytes are hypotheses read from
* the instruction shape; only the bytes are evidence. No callee: this is a leaf and the
* harness emitted the common epilogue itself.
*/
void func_8006A98C(int a0, int a1, int a2)
{
int *p = *(int **)(a0 + 12);
if (a1 & 0xff) {
p[20] += p[24];
p[21] += p[25];
p[22] += p[26];
}
if (a2 & 0xff) {
p[52] += p[56];
p[53] += p[57];
p[54] += p[58];
}
}