The deque ops are an EXPRESSION STACK: push the left operand, evaluate the right (a built-in call, whose result lands in special[0]), pop the comparand back into special[1], compare. Tracking that through the linear decode is enough to recover what each site tests. Evidence the model is right, not just plausible: push vs pop across all 28 stages 1877 vs 1877 files that underflow or end unbalanced 0 of 28 Stage 02 pop.i sites followed by cmp.i 319 / 319 ops immediately before a pop.i call x313, cmp.a x6 isl.conditions() recovers 7563 condition sites disc-wide with 0.0% left as an unresolved special[N]; 83.2% have a built-in call as the LHS and 99.7% compare against a plain number. Most-tested: hp_pct_test 1955, unit_state 1257, unit_relation 796, dist_lt 450, unit_alive 413. They read as conditions now: if unit_alive(TCN105) != 1 if hp_pct_test(ADT308, 0) != 1 if dist_lt(ADT308, TCN000, 15000) != 1 (world unit = 1 m, so 15 km) if unit_state(ADT308) == 1 data/isl-stage02-conditions.txt was a stale artefact with NO generator -- the thing isl_report.py's docstring complained about. It has one now (isl_report.py conditions). The calls and phase-ends artefacts both regenerate byte-identical, so the change is additive. Recorded rather than glossed: 15 of Stage 02's 965 sites (1.6%) attribute the LHS to end_coroutine, which returns no value -- the tracker sets special[0] on EVERY call, so those show a stale value and are wrong, not imprecise. The fix is to set it only for built-ins that write [phase+164], which the vtable work makes checkable.
4.0 KiB
✅ Every condition site, with its comparand — the clear conditions are readable
This is what the whole ISL chain was for. data/isl-stage02-conditions.txt has
existed for a long time as a stale artefact with no generator —
isl_report.py's own docstring says so. It has one now, and the conditions are
resolved rather than printed as special[N].
✅ The deque ops are an expression stack
isl-bytecode.md names ops 21–24 push.i/push.f/pop.i/pop.f over deques
at phase+44 / phase+64. What they are used for is the missing piece:
set.i special[0] = 1
set.i special[1] = special[0]
push.i ; save the comparand
set.i local[0] = 1
set.i local[4] = 0x49
call unit_state(ADT308) ; result -> special[0] (clobbers it)
pop.i ; restore comparand -> special[1]
cmp.i special[0], special[1]
beq -> 0xFEB4
A textbook stack-machine lowering: push the left operand, evaluate the right, pop, compare. Tracking the stack through the decode is therefore enough to recover what every site actually tests.
The evidence it is a stack, not something else
| check | result |
|---|---|
push vs pop across all 28 stages |
1877 vs 1877 |
| files where the deque underflows or ends unbalanced | 0 of 28 |
Stage 02 pop.i sites immediately followed by cmp.i |
319 / 319 |
ops immediately preceding a pop.i |
call ×313, cmp.a ×6 |
Perfect balance with zero underflow across 28 independent files is not something
a wrong model produces, and pop.i → cmp.i at 319/319 makes pop.i a reliable
marker for a condition site.
✅ Result — disc-wide
isl.conditions() walks the linear stream tracking special[], local[] and
the stack, then reports each compare with its branch:
| all 28 stages | |
|---|---|
| condition sites | 7 563 |
either side left unresolved as special[N] |
0 — 0.0 % |
| LHS is a resolved built-in call | 6 292 — 83.2 % |
| RHS is a plain number | 7 544 — 99.7 % |
Most-tested predicates disc-wide: hp_pct_test 1955, unit_state 1257,
unit_relation 796, dist_lt 450, request_script_message 425,
unit_alive 413, read_freg 187.
And they read as conditions:
0x032F4 ph1 if unit_alive(TCN105) != 1 -> 0x3388
0x04D70 ph1 if unit_hp_pct(TCN001, Character_Player_Test) != 0 -> 0x51FC
0x2C4E0 ph3 if hp_pct_test(ADT308, 0) != 1 -> 0x2CF8C
0x30790 ph3 if dist_lt(ADT308, TCN000, 15000) != 1 -> 0x307B0
0x33FA0 ph3 if unit_state(ADT308) != 2 -> 0x34030
0x349E0 ph3 if unit_state(ADT308) == 1 -> 0x34A00
dist_lt(ADT308, TCN000, 15000) — with the world unit established as 1 metre,
that is a 15 km proximity test.
🟡 A caveat the numbers themselves expose
15 of Stage 02's 965 sites (1.6 %) attribute the left-hand side to
end_coroutine, which does not return a value. The tracker sets
special[0] on every call, so where the right-hand side of a comparison was
not actually a value-producing built-in, the LHS shown is a stale
special[0] from an earlier call. Those sites are wrong, not merely imprecise.
The fix is to set special[0] only for built-ins that write [phase+164] —
which isl-builtin-dispatch makes checkable, since
112 of 147 are vtable slots whose implementations can be tested for that store.
Not done; recorded so the 1.6 % is not read as accurate.
🟡 Not settled
- The 35 unnamed built-ins still print as
builtinN—builtin103(115 sites),builtin105(117) andbuiltin16(132) are the highest-traffic unknowns, and each is now a vtable-slot lookup away. - Which condition guards each
END_PHASE. Every site is readable, but linking a condition to the phase exit it eventually reaches needs the control flow between them, which this listing does not follow. - Only Stage 02's artefact is committed; the other 27 generate from the same command but are not in the tree.