Two files (isl_report.py's docstring and structures/isl-builtins.md) recorded the same blocker on a faithful per-phase condition listing: that it needs the coroutine entry points from start_coroutine's operand. Measured against isl.call_sites(), which enumerates by scanning the encoding rather than by decoding and so is an independent denominator: linear + jumps, stopping at ret (what the tool did) 133 / 2846 = 4.7% linear + jumps, continuing past ret 2275 / 2846 = 79.9% ... + following start_coroutine (the recorded fix) 2355 / 2846 = 82.7% plain linear decode, no control flow at all 2846 / 2846 = 100.0% Following the coroutine entries buys 2.8 points. Disc-wide, a plain linear decode from the first phase base reaches 25705/25705 call sites over all 28 stages, and 28/28 decode clean to code_end with no desync. The real bug was isl.dis ending on `if op == 20: break`. Op 20 is `ret`, but this is a coroutine VM -- the thread suspends and resumes at the FOLLOWING instruction, so code continues past it. dis() now takes stop_at_ret (default True, preserving the old output: data/isl-stage02.txt regenerates byte-identical) and isl.linear_offsets() is the correct walk. By-product, kept with its control: start_coroutine's target is staged slot 0 -- 73/83 phase-1 sites land on a valid instruction, against a 38.7% chance rate for an arbitrary 4-aligned offset. New artefact data/isl-stage02-phase-ends.txt with a committed generator (isl_report.py phase-ends). It shows END_PHASE's call site is the WRONG place to read a clear condition: all 12 Stage-02 sites sit in one stereotyped outro. Not settled, and stated as such: op10/op13/op14/op21/op23 are unread handlers, so the condition in the poll loop upstream cannot be named yet.
4.4 KiB
✅ The ISL instruction stream is FLAT — the recorded blocker was my own decoder
Two files recorded the same blocker on producing a faithful per-phase condition
listing — tools/re-capture/isl_report.py's docstring and
structures/isl-builtins.md:
"neither
isl.resync… nor a naive linear decode from the phase base reaches every call site, so producing that listing faithfully needs the coroutine entry points, whichstart_coroutine's operand carries and this tool does not yet follow."
Refuted. The coroutine entry points were never the problem.
The measurement
isl.call_sites() enumerates calls by scanning the encoding, not by
decoding, so it is an independent denominator — exactly the control this needed.
Against it, for Stage02.ssb (2 846 call sites):
| decode strategy | call sites reached |
|---|---|
linear + jumps, stopping at ret — what the tool did |
133 — 4.7 % |
linear + jumps, continuing past ret |
2 275 — 79.9 % |
… + following start_coroutine's target — the recorded fix |
2 355 — 82.7 % |
| plain linear decode, no control flow at all | 2 846 — 100.0 % |
Following the coroutine entries — the thing both files said was required — buys 2.8 points. Ignoring control flow entirely gets everything.
And it holds across the disc
Decoding linearly from the first phase base to code_end:
28 files, 25 705 / 25 705 call sites = 100.00 %, and 28 / 28 decode clean to the end with no desync. Not one stage has a gap or a bad length.
So the stream is a flat, self-consistent instruction sequence. Reaching a call site needs no control-flow reconstruction — only a correct length walk.
The actual bug: ret is a YIELD
isl.dis ended its loop on if op == 20: break. Op 20 is ret — but this is
a coroutine VM: the handler "sets r29=1 and takes the suspend path", the
thread parks, and it later resumes at the following instruction. Code
continues after a ret, so stopping there abandons the file at the first one.
For Stage 02 that is offset 0x1F0 of a 0x34C80 code region — 271 of 18 739
instructions.
This also silently truncated my own reading: the listing for the phase-3
FORCE_END_PHASE at 0x34A10 stopped four instructions short of the site,
at the ret at 0x34A0C.
dis now takes stop_at_ret (default True, preserving the old output — the
committed data/isl-stage02.txt regenerates byte-identical), and
isl.linear_offsets() is the correct walk.
✅ By-product: start_coroutine's target is staged slot 0
Worth keeping even though it was not the blocker. Of 83 start_coroutine sites
in phase 1, the only staged slot is 0, and phase_base + slot0 lands on a
valid instruction in 73/83 = 88 %.
Control: an arbitrary 4-aligned offset in the code region looks like a valid instruction 38.7 % of the time (20 887 / 53 990). So 88 % is discriminating, not the null result — the operand is a code offset relative to the phase base.
🟡 Where a phase actually ends — and where the condition is NOT
New artefact data/isl-stage02-phase-ends.txt
(generator: isl_report.py phase-ends). Stage 02 has 15 phase-ending calls:
12 END_PHASE (6) and 3 FORCE_END_PHASE (62).
Every one of the 12 END_PHASE sites sits in the same stereotyped outro:
call wait_cmds_drained
call fade_sound(3)
call builtin85(3)
call wait_s(3)
call END_PHASE
call end_coroutine
That is the phase teardown, not its condition — so END_PHASE's call site
is the wrong place to look for the clear condition. The decision is upstream,
in the op10/op13 pair that precedes the outro, which
structures/isl-builtins.md already flags as an
unread compare/branch (handlers 0x82271598 and 0x82271830).
The phase-3 FORCE_END_PHASE region shows the shape a real condition takes —
a poll loop, not a straight line:
op10 global[20], 7
op14 -> 0xFEB4
call unit_state(ADT308)
op23
op10 special, 1
op13 -> 0xFEB4 ; branch back — poll until the test passes
call end_coroutine
🟡 Not settled: op10/op13/op14/op21/op23 are still unread
handlers, so which way each branch goes is a guess, and I am not naming them
from a pattern — the corpus has paid for that twice already. Reading those five
handlers is what turns this listing into the per-phase clear condition.