Asking who calls the function the timeline calls enumerates every way an ISL routine
can begin. sub_822737C8(phase, base, offset) computes base + offset early on, and has
seven real call sites: the phase initialiser sub_82270DF8, the built-in stub region
(start_coroutine), the timeline walker sub_822748D0, TWICE inside sub_8226D740 -- the
per-frame engine->script drain -- and two unread, sub_82273910 and sub_82264058.
CORRECTION to my own write-up: the 0x1883 record's third word is the phase's MAIN
ENTRY, not a "size". The initialiser hands it straight to the spawner:
8227101C or r5, r22, r22 ; the record's third word
82271020 or r4, r26, r26 ; the code base
82271030 bl 0x822737C8
44 of 44 records land on the instruction stream (100%) against a 25.0% control, and
all three Stage-02 targets open with the identical prologue
`special[0]=0 ; local[0]=0 ; call builtin116(0)` -- a routine entry, not a length.
So the record is 0x1883, base, MAIN_ENTRY, 0, code_end, force_end_handler.
Seeding the main entries moves no coverage number: every one was already among the
CFG's entry points by another route. This corrects a field's meaning, not the graph.
Lead recorded rather than claimed: both of the drain's spawns take their offset from
[node+112], the first field of a drained node to be located, and the best remaining
angle on the ~15% of code nothing appears to start. It is NOT shown that those nodes
come from the trigger queue at phase+272 -- that is precisely the over-reach
isl-builtins.md already made and withdrew, so it is not asserted here.
All artefacts regenerate byte-identical; this is documentation only.
143 lines
6.3 KiB
Markdown
143 lines
6.3 KiB
Markdown
# ✅ The mission-level `0x1883` record: one entry point and one code/data boundary
|
||
|
||
Every phase exit in all 28 stages is now reachable. `data/isl-phase-guards-all.txt`
|
||
went from **5 of 177 exits unreachable** to **0**.
|
||
|
||
## The hunt, including the part that failed
|
||
|
||
The first step was the cheap one: take each unreached routine's entry offset and
|
||
look for it as a word anywhere in the file. **It is not there**, in any encoding:
|
||
|
||
| entry encoded as | appears as a word | control (reached offsets) |
|
||
|---|---|---|
|
||
| phase-relative | 6.6 % | **11.5 %** |
|
||
| absolute | 1.6 % | 3.3 % |
|
||
| phase-relative / 4 | 1.6 % | 6.6 % |
|
||
| absolute / 4 | 0.0 % | 8.2 % |
|
||
|
||
Every variant sits **at or below** the control, so nothing in the bytecode names
|
||
these routines. That is a real negative, and it ruled out the whole family of
|
||
"some instruction operand points at them".
|
||
|
||
It also ruled out *dead code*: the 3 069 unreached instructions in Stage 02
|
||
contain **485 calls**, including `start_coroutine` ×75, `squadron_attack` ×59,
|
||
`set_group_speed` ×42 and `objective_marker` ×13. That is live mission logic.
|
||
|
||
## ✅ The answer is in the mission-level stream
|
||
|
||
`isl-bytecode.md` records that the stream at `+0x24` holds three `0x1883` records,
|
||
one per phase, whose operand is the phase's code base, and notes they each end
|
||
"in a pair of plausible ISL entry offsets". They are exactly that. The record is
|
||
|
||
```
|
||
0x1883, base_delta, size, 0, entry_a, entry_b ; entries PHASE-RELATIVE
|
||
```
|
||
|
||
~~**Measured over all 28 stages: 82 of 88 of those values land on a valid
|
||
instruction — 93.2 %, against a 38.6 % chance rate.**~~
|
||
⚠️ **That test was too weak** — see *What `entry_a` really is* below. A trailing
|
||
data record has length 8 and so passes "nonzero, even length"; the 93.2 % counted
|
||
data as code. The `entry_b` result stands on a different footing: those targets
|
||
were matched against `isl.call_sites()`, an independent enumeration.
|
||
|
||
And for Stage 02 the second entry of each record is the phase's force-end
|
||
handler:
|
||
|
||
```
|
||
base 0x0000E4 -> 0x014848, 0x01482C <- 0x1482C was an unreachable exit
|
||
base 0x014AA8 -> 0x024A0C, 0x0249F0
|
||
base 0x024B4C -> 0x034AC8, 0x034A10 <- 0x34A10 was an unreachable exit
|
||
```
|
||
|
||
Both of Stage 02's unreachable exits are named here, and phase 2's `0x249F0` —
|
||
already reachable — is the same slot, which is the consistency check.
|
||
|
||
Seeding these as CFG entries: **exits unreachable 5 → 0** across the disc.
|
||
|
||
🔑 It also *reads*: those exits now show **0 necessary conditions**, because they
|
||
are entry points rather than code reached through tests. A handler the engine can
|
||
enter directly, with no preconditions, is what `FORCE_END_PHASE` should look like.
|
||
|
||
## ⚠️ Seeded in one place, and the count did not move
|
||
|
||
The first attempt added the entries to `conditions()` and reported reach
|
||
**85.0 % → 85.0 %**, exits **5 → 5**. Nothing moved, which is the signal.
|
||
`dominating_conditions()` builds its **own** entry set and did not use the one I
|
||
had patched — the same fix-the-instance-not-the-class mistake that cost 22× its
|
||
own size earlier in this corpus. With both patched: exits **5 → 0**.
|
||
|
||
## 🟡 Not settled
|
||
|
||
* **Reach only went 85.0 % → 85.2 %.** These records name a handful of routines,
|
||
not the ~15 % of code still unreached. What starts *the rest* is still open —
|
||
and the negative above says it is not an operand in the file.
|
||
* **`entry_a` is unidentified.** `entry_b` is the force-end handler in the three
|
||
Stage-02 records; what the first entry of each pair is has not been checked.
|
||
* **6 of 88 values do not land on an instruction.** Not investigated — they may
|
||
be absent phases, a different record variant, or a decode edge.
|
||
|
||
|
||
## 🔴 What `entry_a` really is — NOT an entry point
|
||
|
||
Disassembling the three Stage-02 `entry_a` targets shows **opcodes 0x19 and
|
||
0x1A**, and the dispatcher's table has 25 entries (`cmplwi 0x18`). Those are not
|
||
instructions at all:
|
||
|
||
```
|
||
014848: 00000819 tag 19 00000019 int 25
|
||
014850: 00000819 tag 19 00002B20 int
|
||
014858: 0000081A tag 1A 00000000 float 0.0
|
||
014860: 00000819 tag 19 00000005 int 5
|
||
014868: 00000819 tag 19 00002B88 int
|
||
014870: 0000081A tag 1A 3F000000 float 0.5
|
||
```
|
||
|
||
Each phase region **ends with a trailing DATA table** of 8-byte typed records —
|
||
tag `0x19` = int, tag `0x1A` = IEEE float — and `entry_a` is where it starts.
|
||
|
||
**Confirmed across the disc: in 44 of 44 phases, the first offset whose opcode
|
||
exceeds 0x18 is exactly that phase's `entry_a`. Zero exceptions.** Only two tags
|
||
ever appear: 1 394 × `0x19` and 675 × `0x1A`.
|
||
|
||
So the record is
|
||
|
||
```
|
||
0x1883, base, MAIN_ENTRY, 0, code_end, force_end_handler
|
||
```
|
||
|
||
🔴 **The third word is NOT a size** — that was my label and it was wrong. The
|
||
phase initialiser `sub_82270DF8` passes it straight to the coroutine spawner:
|
||
|
||
```
|
||
8227101C or r5, r22, r22 ; r22 = the record's third word
|
||
82271020 or r4, r26, r26 ; r26 = the code base
|
||
82271030 bl 0x822737C8 ; spawn(phase, base, word3)
|
||
```
|
||
|
||
Measured: **44 of 44 records land on the instruction stream (100 %) against a
|
||
25.0 % control**, and all three of Stage 02's targets open with the identical
|
||
prologue `special[0]=0 ; local[0]=0 ; call builtin116(0)` — a routine entry, not
|
||
a length.
|
||
|
||
🟡 The table's contents are **not decoded**. Its int values do land on the
|
||
instruction stream more often than chance (46/51 vs 29.5 %), but **0 of them are
|
||
unreached run-starts** — so this is not what starts the unreachable code either.
|
||
|
||
## ✅ And the decoder was reading that data as code
|
||
|
||
`isl.linear_offsets` was decoding all **2 069** of those records as instructions,
|
||
1.23 % of the stream. Fixed by stopping each phase's walk at its boundary:
|
||
|
||
| | before | after |
|
||
|---|---|---|
|
||
| decoded instructions | 168 251 | **166 182** = 168 251 − 2 069 |
|
||
| records with opcode > 0x18 | 2 069 | **0** |
|
||
| **call sites covered** | 25 705 / 25 705 | **25 705 / 25 705** |
|
||
| exits unreachable | 0 | 0 |
|
||
| conditions with unknown LHS | 400 | 400 |
|
||
|
||
⚠️ **The first attempt at that fix destroyed 36 % of the stream** — 168 251 →
|
||
107 596, exits 0 → 74 — because `linear_offsets` is one global walk from the
|
||
first phase base, so stopping at phase 1's table lost every later phase. It has
|
||
to *skip* the region and resume at the next base. A count moving hard in the
|
||
wrong direction is the same signal as one that will not move. |