mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 13:33:34 -04:00
docs(accelerators): #20 — set TU boundaries at the rodata island's jtbl spans, at segmentation time
Found P31 S72; COULD have been found 2026-06-15 (Phase 7, commit:0025), which wrote the island's contents by hand and named loadDestPtrTable as the divider. The signal needs no matching progress — it is a property of the retail image. The number that matters is the cost curve: at Phase 6 src/800.c had 13 externs and 0 typedefs and the split was a yaml edit; at S72 it had 2,378 externs and 175 typedefs and cost 57 crossing declarations, a shared header and 4 stale consumers. Plus a session of wrong conclusions (11 'PROVEN gate-rejects', 10 of which banked once the carve existed). PREREQ recorded honestly: the binding constraint came from the OVERLAY work two phases later, so this is knowledge that never got carried back to main — not carelessness. General principle: segmentation is the exception to probe-before-investing. When a decision is evidenced at t=0, cheap now, and strictly more expensive later, make it early even though its payoff is unproven.
This commit is contained in:
@@ -523,3 +523,63 @@ in the same change. The rule generalises past decomp: **if your agents produce a
|
||||
next agent on that item must be handed it.** An archive of your own verified outcomes is training data
|
||||
for your own tooling (see also `docs/decision-log.md` on the banked-corpus pivot) — and the specific
|
||||
trap here is that the write side felt complete on its own, because the notes were being *saved*.
|
||||
|
||||
---
|
||||
|
||||
## #20 — SET YOUR TU BOUNDARIES AT THE RODATA ISLAND'S JUMP-TABLE SPANS, AT SEGMENTATION TIME (P31 S72)
|
||||
|
||||
**What it is.** A compiled object contributes exactly **one contiguous `.rodata` run**. A binary whose
|
||||
switch jump tables sit in an island of several *separated* spans can therefore carve only one span
|
||||
per code object — so every switch function outside that one span **cannot ever bank**: gcc emits its
|
||||
table while the raw copy is still emitted from the data segment, the image grows, and every symbol
|
||||
above the insertion point shifts. The fix is to make the code subsegment boundaries line up with the
|
||||
spans, because a contiguous run of tables IS one translation unit's rodata (tables pack tight within
|
||||
a TU, separated by other data across TUs).
|
||||
|
||||
**When we found it.** P31 S72, 2026-09-02.
|
||||
|
||||
**When it COULD have been found.** **2026-06-15, Phase 7** — commit `commit:0025` wrote the island's
|
||||
contents into `config/splat.us.exe.yaml` by hand, naming the game jtbls, `loadDestPtrTable @0x80072C70`
|
||||
as the divider, and the library tables at `0x800737CC+`. Everything needed to compute the spans and
|
||||
their owner address ranges was in that comment, 2.5 months before it was used. The signal needs **no
|
||||
matching progress at all** — it is a property of the retail image, readable the day the binary is
|
||||
first split.
|
||||
|
||||
**What it would have saved, and the number that matters is the COST CURVE, not the delay.** The price
|
||||
of a TU split is the declarations that cross the new boundary, and that grows monotonically with how
|
||||
much of the file you have matched:
|
||||
|
||||
| moment | `src/800.c` | externs | typedefs | what the split costs |
|
||||
|---|---|---|---|---|
|
||||
| Phase 6 (file created) | 4,277 lines, 1,998 stubs | 13 | 0 | a yaml edit |
|
||||
| Phase 7 (island documented) | 2,712 lines | 101 | 0 | a yaml edit |
|
||||
| **P31 S72 (actually done)** | **27,126 lines, 94% matched** | **2,378** | **175** | 57 crossing decls, 19 typedefs moved to a new shared header, a compile-error loop, and 4 consumers left stale |
|
||||
|
||||
It also cost a full session of wrong conclusions: 11 functions were recorded as *"PROVEN gate-rejects,
|
||||
§376 in its purest form, do not re-slate"* when 10 of them banked byte-identical the moment the carve
|
||||
existed.
|
||||
|
||||
**PREREQ — and this is the honest part.** At Phase 7 you could have *made* the split but not *known
|
||||
why*. The binding constraint (one object, one contiguous `.rodata` run) and the carve machinery came
|
||||
out of the OVERLAY work in Phase 26 §8 / Phase 29 §8e. So this is not "we were careless in Phase 7";
|
||||
it is knowledge that arrived from a different population two phases later and was never carried back
|
||||
to `main`. **The transferable advice is therefore for segmentation time on the NEXT project, where
|
||||
you can carry it in from day one:**
|
||||
|
||||
> Before writing the first subsegment list, dump the target's `.rodata`/data island, mark every jump
|
||||
> table, group them into contiguous spans, and map each span to the address range of the functions
|
||||
> that reference it. **Put your initial code-subsegment boundaries at those ranges.** At 0% matched
|
||||
> this is free — there are no declarations to reconcile because there is no C yet — and it removes a
|
||||
> class of wall you will otherwise hit at 90% completion, on your largest and most valuable functions.
|
||||
|
||||
**The general principle, which is worth more than the specific recipe.** Most of this project's
|
||||
discipline is *probe before investing* — do not build tooling on speculation. **Segmentation is the
|
||||
exception.** Structural decisions get monotonically more expensive as matched work accumulates, while
|
||||
the evidence for them is available at t=0 and never improves. For that class, the cheap moment is the
|
||||
earliest moment, and deferring is what costs. When a decision is (a) evidenced from raw data, (b)
|
||||
cheap now, and (c) strictly more expensive later, make it early even though its payoff is unproven —
|
||||
that is the opposite of the default instinct, and the reason to write it down.
|
||||
|
||||
**Companion:** the *method* for doing the split late, if you inherit a project that did not do it
|
||||
early, is cookbook §431 (cut verbatim, let the compiler enumerate what crosses, MOVE typedefs to a
|
||||
shared header, and check every consumer that hardcoded the old filename).
|
||||
|
||||
Reference in New Issue
Block a user