re: the per-phase clear conditions, by dominance over the ISL CFG
Closes the backlog's "which condition guards each END_PHASE". With the CFG from the previous commit this is a graph query, not new machinery. The obvious query is WRONG for this language, and I implemented it first: "one successor reaches END_PHASE and the other does not" finds 1/62/1 guards across Stage 02's three phases, and the 1s are both the same read_freg(0) < 1200 timeout -- every objective test missed. The cause is the dominant idiom: a POLL LOOP's loop-back branch also reaches the exit, one iteration later, so neither successor discriminates. The asymmetric 1/62/1 is what exposed it; a uniform number would have read as plausible. Dominance has no such blind spot: a condition dominates an exit when every path from an entry passes through it, so it is NECESSARY for the phase to end that way, and a poll loop's test dominates its own exit by construction. Iterative dominators converge in 3 passes over 15670/18739 instructions (83.6%). Result for Stage 02 -- every exit in all three phases is dominated by unit_hp_pct(TCN001, Character_Player_Test) != 0, the player's ship being alive, which falls out rather than being assumed. Beyond that, phase 1's objective exit requires hp_pct_test on ADT102, ADT107 and ADT113; phase 3's requires ADT301 and ADT302; read_freg(0) gates at 210 / 300 and times out at 1200; random(3) and random(5) dominate only the exits that pick one of several closing lines. Two of the 15 exits are reachable from NO static entry, both FORCE_END_PHASE. That agrees with the independently measured 389 unreachable routines: they are started from the trigger queue at phase+272, by data rather than code. Practical note recorded: the first dominator run was OOM-killed -- 6743 nodes each holding a Python set of up to 6743 elements. Integer bitmasks run in seconds. Not settled, and said so: dominance gives necessary, not sufficient, conditions; only Stage 02's artefact is committed; one listed condition is still an unresolved <unknown>; read_freg's units are inferred from the gate values, not read. calls, phase-ends and conditions all regenerate byte-identical.
This commit is contained in:
@@ -1472,7 +1472,20 @@ premise was wrong.**
|
||||
+ the unit's live object, pushes it on a queue via the `push.i` helper, and
|
||||
returns 1 (or 0 if the unit is absent). Finding that **exposed a much bigger
|
||||
bug in my own condition tracker** — see the entry below.
|
||||
which condition guards each `END_PHASE` (needs the control flow between them);
|
||||
~~which condition guards each `END_PHASE` (needs the control flow between them);~~
|
||||
✅ **(2026-08-27) DONE — [structures/isl-phase-guards](structures/isl-phase-guards.md)**
|
||||
(`isl_report.py phase-guards`, `data/isl-stage02-phase-guards.txt`). Uses
|
||||
DOMINANCE over the CFG, not reachability. 🔴 The obvious query — "one branch
|
||||
reaches `END_PHASE`, the other doesn't" — is **wrong for this language** and was
|
||||
tried first: poll loops have BOTH successors reaching the exit, so it found
|
||||
1/62/1 guards in Stage 02's three phases, the 1s being a `read_freg(0) < 1200`
|
||||
timeout, missing every objective test. Dominators converge in 3 passes over
|
||||
15670/18739 instructions. Result: **every exit in all 3 phases requires
|
||||
`unit_hp_pct(TCN001, Character_Player_Test) != 0`** (the player alive); phase 1's
|
||||
objective exit additionally requires `hp_pct_test(ADT102/ADT107/ADT113, 0) != 1`,
|
||||
phase 3's requires `ADT301`/`ADT302`, and `read_freg(0)` gates at 210/300/1200.
|
||||
🟡 Dominance gives NECESSARY not sufficient conditions. 🟡 2 of 15 exits are
|
||||
reachable from NO static entry — consistent with the trigger queue at `phase+272`.
|
||||
the vtable's length. The condition lives in the `op10`/`op13` poll loop upstream of
|
||||
the outro — e.g. phase 3 polls `unit_state(ADT308)` and branches back to
|
||||
`0xFEB4` until it passes. Artefact: `data/isl-stage02-phase-ends.txt`.
|
||||
|
||||
Reference in New Issue
Block a user