diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index 8f48420d..6cbbc4c4 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -20,6 +20,24 @@ unknown, what evidence exists, and what the first step would be. Move an item in what starts the 389 unreachable routines is **still open**. 🟡 A trigger node's layout and its consumer are unread; built-in 25 stays unnamed. +* ✅ **(2026-08-27) EVERY PHASE EXIT IS NOW REACHABLE — [isl-stream-entry-points](isl-stream-entry-points.md).** + `data/isl-phase-guards-all.txt`: **5 of 177 unreachable exits → 0.** The cheap + first step FAILED usefully: an unreached routine's entry offset does **not** + appear as a word in the file in any encoding (phase-relative 6.6 % vs an 11.5 % + control; absolute 1.6 % vs 3.3 %; /4 forms 0–1.6 % vs 6.6–8.2 %) — all at or + below chance, ruling out "some operand points at them". Nor is it dead code: the + 3069 unreached instructions in Stage 02 hold **485 calls** (`start_coroutine` ×75, + `squadron_attack` ×59, `objective_marker` ×13). ✅ The answer is the mission-level + stream: each `0x1883` record is `0x1883, base, size, 0, entry_a, entry_b` with the + entries **phase-relative** — **82 of 88 land on a valid instruction (93.2 %) vs a + 38.6 % control** — and `entry_b` is the phase's FORCE_END_PHASE handler (Stage 02: + `0x1482C`, `0x249F0`, `0x34A10`). Those exits now show **0 necessary conditions**, + which is what an engine-entered abort handler should look like. + ⚠️ First seeding attempt moved NOTHING (85.0 %→85.0 %, 5→5) because + `dominating_conditions()` builds its own entry set — fix-the-instance again. + 🟡 Still open: reach only 85.0 %→**85.2 %**, so what starts the other ~15 % is + unknown; `entry_a` is unidentified; 6 of 88 values do not land on an instruction. + ## ✅✅ SOLVED — the mission freeze was a modal sign-in dialog (2026-08-26) `XamShowSigninUI` opens a modal dialog and `xeXamDispatchDialog` blocks the diff --git a/docs/re/data/isl-phase-guards-all.txt b/docs/re/data/isl-phase-guards-all.txt index e15ef987..84249560 100644 --- a/docs/re/data/isl-phase-guards-all.txt +++ b/docs/re/data/isl-phase-guards-all.txt @@ -32,9 +32,7 @@ fired by time passing, which is not a property of the graph. precond 0x002BC4 hp_pct_test(ADT101, 0) != 1 precond 0x0031D8 read_freg(0) < 600 -## phase 1 — FORCE_END_PHASE at 0x00E0B4: 2 necessary condition(s) - precond 0x00DFD4 builtin103() == 1 - TRIGGER 0x00E034 hp_pct_test(ADS129, 0) != 1 +## phase 1 — FORCE_END_PHASE at 0x00E0B4: 0 necessary condition(s) ## phase 2 — END_PHASE at 0x011B10: 2 necessary condition(s) precond 0x011814 unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -57,11 +55,7 @@ fired by time passing, which is not a property of the graph. precond 0x011B70 read_freg(0) < 70 precond 0x012774 read_freg(0) < 600 -## phase 2 — FORCE_END_PHASE at 0x01B828: 4 necessary condition(s) - precond 0x01B4FC builtin103() == 1 - precond 0x01B55C hp_pct_test(ADT205, 0) != 1 - precond 0x01B5A0 unit_alive(ADT205) != 1 - TRIGGER 0x01B6F8 unit_alive(ADT205) != 1 +## phase 2 — FORCE_END_PHASE at 0x01B828: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -74,7 +68,7 @@ entry to that `END_PHASE` passes through it, so it is NECESSARY for the phase to end that way. Reachability alone is the wrong query here — in a poll loop both successors reach the exit. -CFG reached 15670 of 18739 instructions (83.6%). +CFG reached 15821 of 18739 instructions (84.4%). `precond` = necessary: the exit cannot happen unless it holds. `TRIGGER` = also SUFFICIENT: once it holds the exit is unavoidable. @@ -105,8 +99,7 @@ fired by time passing, which is not a property of the graph. precond 0x005888 read_freg(0) <= 210 precond 0x006070 read_freg(0) < 1200 -## phase 1 — FORCE_END_PHASE at 0x01482C: UNREACHABLE from any static entry - (started from the trigger queue at `phase+272`, by data not code) +## phase 1 — FORCE_END_PHASE at 0x01482C: 0 necessary condition(s) ## phase 2 — END_PHASE at 0x019640: 2 necessary condition(s) precond 0x0191CC unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -127,9 +120,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x01AA38 global[104] != 0 TRIGGER 0x01AAF8 global[4] == 0 -## phase 2 — FORCE_END_PHASE at 0x0249F0: 2 necessary condition(s) - precond 0x0247E4 hp_pct_test(ADN202, 0) != 1 - TRIGGER 0x024848 unit_state(TCT206) != 1 +## phase 2 — FORCE_END_PHASE at 0x0249F0: 0 necessary condition(s) ## phase 3 — END_PHASE at 0x02B96C: 2 necessary condition(s) precond 0x02B4F8 unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -170,8 +161,7 @@ fired by time passing, which is not a property of the graph. precond 0x02C240 read_freg(0) <= 300 precond 0x02CFD4 read_freg(0) < 1200 -## phase 3 — FORCE_END_PHASE at 0x034A10: UNREACHABLE from any static entry - (started from the trigger queue at `phase+272`, by data not code) +## phase 3 — FORCE_END_PHASE at 0x034A10: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -184,7 +174,7 @@ entry to that `END_PHASE` passes through it, so it is NECESSARY for the phase to end that way. Reachability alone is the wrong query here — in a poll loop both successors reach the exit. -CFG reached 8382 of 11105 instructions (75.5%). +CFG reached 8482 of 11105 instructions (76.4%). `precond` = necessary: the exit cannot happen unless it holds. `TRIGGER` = also SUFFICIENT: once it holds the exit is unavoidable. @@ -220,9 +210,7 @@ fired by time passing, which is not a property of the graph. precond 0x003FC8 read_freg(0) < 210 precond 0x0046E8 read_freg(0) < 600 -## phase 1 — FORCE_END_PHASE at 0x009FF4: 2 necessary condition(s) - precond 0x009E60 unit_state(ADT101) == 1 - TRIGGER 0x009EC4 unit_state(ADT101) != 1 +## phase 1 — FORCE_END_PHASE at 0x009FF4: 0 necessary condition(s) ## phase 2 — END_PHASE at 0x00E1B8: 2 necessary condition(s) precond 0x00DD44 unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -249,8 +237,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x00F1E4 != 1 TRIGGER 0x00F450 global[60] != 1 -## phase 2 — FORCE_END_PHASE at 0x014CF8: UNREACHABLE from any static entry - (started from the trigger queue at `phase+272`, by data not code) +## phase 2 — FORCE_END_PHASE at 0x014CF8: 0 necessary condition(s) ## phase 3 — END_PHASE at 0x019638: 2 necessary condition(s) precond 0x0191C4 unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -286,8 +273,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x01A994 global[44] != 0 TRIGGER 0x01AB10 global[8] == 0 -## phase 3 — FORCE_END_PHASE at 0x01F510: 1 necessary condition(s) - TRIGGER 0x01F200 hp_pct_test(ADN301, 0) != 1 +## phase 3 — FORCE_END_PHASE at 0x01F510: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -332,8 +318,7 @@ fired by time passing, which is not a property of the graph. precond 0x003208 hp_pct_test(ADT101, 0) != 1 precond 0x0037FC read_freg(0) < 600 -## phase 1 — FORCE_END_PHASE at 0x0052A4: 1 necessary condition(s) - TRIGGER 0x0051E0 hp_pct_test(ADT106, 0) != 1 +## phase 1 — FORCE_END_PHASE at 0x0052A4: 0 necessary condition(s) ## phase 2 — END_PHASE at 0x008918: 2 necessary condition(s) precond 0x008474 unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -360,8 +345,7 @@ fired by time passing, which is not a property of the graph. precond 0x008FA4 global[16] != 1 precond 0x0099A4 read_freg(0) < 600 -## phase 2 — FORCE_END_PHASE at 0x00C9CC: 1 necessary condition(s) - TRIGGER 0x00C6BC hp_pct_test(ADN201, 0) != 1 +## phase 2 — FORCE_END_PHASE at 0x00C9CC: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -404,8 +388,7 @@ fired by time passing, which is not a property of the graph. precond 0x003B20 hp_pct_test(ADT110, 0) != 1 precond 0x003B74 hp_pct_test(ADT111, 0) != 1 -## phase 1 — FORCE_END_PHASE at 0x007538: 1 necessary condition(s) - TRIGGER 0x0071DC dist_lt(TCN004, TCN001, 20000) == 1 +## phase 1 — FORCE_END_PHASE at 0x007538: 0 necessary condition(s) ## phase 2 — END_PHASE at 0x00A088: 4 necessary condition(s) TRIGGER 0x00964C unit_hp_pct(TCN001, Character_Player_Test) == 0 @@ -423,9 +406,7 @@ fired by time passing, which is not a property of the graph. precond 0x00A19C hp_pct_test(ADT207, 0) != 1 precond 0x00A1F0 hp_pct_test(ADT208, 0) != 1 -## phase 2 — FORCE_END_PHASE at 0x00EEF4: 2 necessary condition(s) - precond 0x00ED64 hp_pct_test(ADS251a, 0) != 1 - TRIGGER 0x00EDC4 hp_pct_test(TCN004, 40) != 1 +## phase 2 — FORCE_END_PHASE at 0x00EEF4: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -466,9 +447,7 @@ fired by time passing, which is not a property of the graph. precond 0x00409C global[0] < 9 TRIGGER 0x00422C global[4] != 1 -## phase 1 — FORCE_END_PHASE at 0x008EFC: 2 necessary condition(s) - precond 0x008D6C hp_pct_test(ADS151b, 0) != 1 - TRIGGER 0x008DCC hp_pct_test(TCN004, 40) != 1 +## phase 1 — FORCE_END_PHASE at 0x008EFC: 0 necessary condition(s) ## phase 2 — END_PHASE at 0x00C5D4: 2 necessary condition(s) precond 0x00C130 unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -502,8 +481,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x00D618 squad_survival_pct(TCN205, 99.9) == 1 TRIGGER 0x00D72C global[8] == 0 -## phase 2 — FORCE_END_PHASE at 0x010E18: 1 necessary condition(s) - TRIGGER 0x010D54 hp_pct_test(TCN215, 0) != 1 +## phase 2 — FORCE_END_PHASE at 0x010E18: 0 necessary condition(s) ## phase 3 — END_PHASE at 0x013FCC: 2 necessary condition(s) precond 0x013B28 unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -529,10 +507,7 @@ fired by time passing, which is not a property of the graph. precond 0x014698 hp_pct_test(ADT301, 0) != 1 precond 0x014F2C read_freg(0) < 1200 -## phase 3 — FORCE_END_PHASE at 0x01B31C: 3 necessary condition(s) - precond 0x01B0E8 unit_state(ADN321) != 1 - precond 0x01B140 unit_state(TCN001) == 1 - TRIGGER 0x01B1B8 dist_lt(ADN321, TCN001, 10000) != 1 +## phase 3 — FORCE_END_PHASE at 0x01B31C: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -581,9 +556,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x0037FC read_freg(0, 0) >= 300 TRIGGER 0x0039B8 global[8] != 1 -## phase 1 — FORCE_END_PHASE at 0x00976C: 2 necessary condition(s) - precond 0x0095D4 hp_pct_test(ADS151a, 0) != 1 - TRIGGER 0x00963C hp_pct_test(TCN012, 40) != 1 +## phase 1 — FORCE_END_PHASE at 0x00976C: 0 necessary condition(s) ## phase 2 — END_PHASE at 0x00D168: 2 necessary condition(s) precond 0x00CD80 unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -635,12 +608,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x00EF18 squad_survival_pct(TCN004, 99.9) != 0 TRIGGER 0x00F344 global[16] != 1 -## phase 2 — FORCE_END_PHASE at 0x017104: 5 necessary condition(s) - precond 0x016A98 hp_pct_test(ADN223, 0) != 1 - precond 0x016C00 unit_state(ADN223) != 1 - precond 0x016D00 unit_state(ADN232) != 1 - precond 0x016E00 unit_state(ADN233) != 1 - TRIGGER 0x016F00 unit_state(ADN234) != 1 +## phase 2 — FORCE_END_PHASE at 0x017104: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -678,8 +646,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x00457C hp_pct_test(ADN102, 0) != 1 TRIGGER 0x004C78 global[4] != 1 -## phase 1 — FORCE_END_PHASE at 0x00C980: 1 necessary condition(s) - TRIGGER 0x00C8BC global[0] != 5 +## phase 1 — FORCE_END_PHASE at 0x00C980: 0 necessary condition(s) ## phase 2 — END_PHASE at 0x00FB10: 2 necessary condition(s) TRIGGER 0x00EEB8 unit_hp_pct(TCN001, Character_Player_Test) == 0 @@ -704,8 +671,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x010C38 hp_pct_test(TCN012, 99.9) != 0 TRIGGER 0x010D28 global[28] != 1 -## phase 2 — FORCE_END_PHASE at 0x015A80: 1 necessary condition(s) - TRIGGER 0x0159BC hp_pct_test(ADN210, 0) != 1 +## phase 2 — FORCE_END_PHASE at 0x015A80: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -755,8 +721,7 @@ fired by time passing, which is not a property of the graph. precond 0x004234 global[0] != 11 precond 0x004A0C read_freg(0) < 600 -## phase 1 — FORCE_END_PHASE at 0x007BF0: 1 necessary condition(s) - TRIGGER 0x007B2C hp_pct_test(TCN109, 0) != 1 +## phase 1 — FORCE_END_PHASE at 0x007BF0: 0 necessary condition(s) ## phase 2 — END_PHASE at 0x00AA50: 2 necessary condition(s) precond 0x00A698 unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -781,8 +746,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x00B454 unit_state(TCN006) != 1 TRIGGER 0x00B650 global[4] == 0 -## phase 2 — FORCE_END_PHASE at 0x00F018: 1 necessary condition(s) - TRIGGER 0x00EF54 hp_pct_test(TCN009, 0) != 1 +## phase 2 — FORCE_END_PHASE at 0x00F018: 0 necessary condition(s) ## phase 3 — END_PHASE at 0x012204: 2 necessary condition(s) precond 0x011E4C unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -826,9 +790,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x013290 unit_state(TCN006) != 1 TRIGGER 0x013534 global[4] == 0 -## phase 3 — FORCE_END_PHASE at 0x01CA54: 2 necessary condition(s) - precond 0x01C940 unit_relation(TCN001, ADN320) == 1 - TRIGGER 0x01C990 unit_relation(ADN320, TCN001) != 1 +## phase 3 — FORCE_END_PHASE at 0x01CA54: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -864,8 +826,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x001918 hp_pct_test(TCN001, 99.9) != 0 TRIGGER 0x001A2C global[4] == 0 -## phase 1 — FORCE_END_PHASE at 0x007CF4: 1 necessary condition(s) - TRIGGER 0x007C0C global[0] > 2 +## phase 1 — FORCE_END_PHASE at 0x007CF4: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -905,8 +866,7 @@ fired by time passing, which is not a property of the graph. precond 0x0025A0 read_freg(0) < 600 precond 0x0027E0 global[0] < 10 -## phase 1 — FORCE_END_PHASE at 0x00A684: 1 necessary condition(s) - TRIGGER 0x00A588 hp_pct_test(ADS151a, 0) != 1 +## phase 1 — FORCE_END_PHASE at 0x00A684: 0 necessary condition(s) ## phase 2 — END_PHASE at 0x00C488: 2 necessary condition(s) precond 0x00C0A0 unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -930,14 +890,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x00CFA8 hp_pct_test(TCN004, 99.9) != 0 TRIGGER 0x00D098 global[4] != 1 -## phase 2 — FORCE_END_PHASE at 0x014728: 7 necessary condition(s) - precond 0x014044 unit_state(ADS254) != 2 - precond 0x0141C4 global[28] != 1 - precond 0x014228 unit_state(ADS254) != 1 - precond 0x014314 unit_state(ADS255) != 1 - precond 0x014400 unit_state(ADS256) != 1 - precond 0x0144EC unit_state(ADS257) != 1 - TRIGGER 0x0145D8 unit_state(ADS258) != 1 +## phase 2 — FORCE_END_PHASE at 0x014728: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -973,9 +926,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x00435C hp_pct_test(ADN001, 0) != 1 TRIGGER 0x005214 global[12] != 1 -## phase 1 — FORCE_END_PHASE at 0x012824: 2 necessary condition(s) - precond 0x012434 hp_pct_test(ADN045, 0) != 1 - TRIGGER 0x0124A0 unit_state(ADN045) != 1 +## phase 1 — FORCE_END_PHASE at 0x012824: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -1016,9 +967,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x00640C global[4] != 0 TRIGGER 0x00681C global[8] != 1 -## phase 1 — FORCE_END_PHASE at 0x00EAA0: 2 necessary condition(s) - precond 0x00E910 hp_pct_test(ADS051a, 0) != 1 - TRIGGER 0x00E970 hp_pct_test(TCN004, 40) != 1 +## phase 1 — FORCE_END_PHASE at 0x00EAA0: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -1058,14 +1007,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x002E90 read_freg(0) > 300 TRIGGER 0x003034 global[8] != 1 -## phase 1 — FORCE_END_PHASE at 0x0061D4: 7 necessary condition(s) - precond 0x005DE0 hp_pct_test(ADT102, 0) != 1 - precond 0x005E58 hp_pct_test(ADT103, 0) != 1 - precond 0x005ED0 hp_pct_test(ADT104, 0) != 1 - precond 0x005F48 hp_pct_test(ADT105, 0) != 1 - precond 0x005FC0 hp_pct_test(ADT108, 0) != 1 - precond 0x006038 hp_pct_test(ADT114, 0) != 1 - TRIGGER 0x006070 global[0] > 1 +## phase 1 — FORCE_END_PHASE at 0x0061D4: 0 necessary condition(s) ## phase 2 — END_PHASE at 0x008324: 1 necessary condition(s) precond 0x007FC4 unit_hp_pct(TCN201, Character_Player_Test) != 0 @@ -1082,8 +1024,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x008974 hp_pct_test(TCN201, 99.9) != 0 TRIGGER 0x008AA8 global[8] != 1 -## phase 2 — FORCE_END_PHASE at 0x00AA94: 1 necessary condition(s) - TRIGGER 0x00A9D0 hp_pct_test(ADT201, 20) != 1 +## phase 2 — FORCE_END_PHASE at 0x00AA94: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -1115,8 +1056,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x00571C hp_pct_test(ADN101, 0) != 1 TRIGGER 0x00632C global[0] != 1 -## phase 1 — FORCE_END_PHASE at 0x00C730: 1 necessary condition(s) - TRIGGER 0x00C618 hp_pct_test(ADN101, 0) != 1 +## phase 1 — FORCE_END_PHASE at 0x00C730: 0 necessary condition(s) ## phase 2 — END_PHASE at 0x010FE0: 2 necessary condition(s) TRIGGER 0x010388 unit_hp_pct(TCN001, Character_Player_Test) == 0 @@ -1136,9 +1076,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x0124D4 squad_survival_pct(TCN207, 99.9) != 0 TRIGGER 0x0125C4 global[8] != 1 -## phase 2 — FORCE_END_PHASE at 0x017304: 2 necessary condition(s) - precond 0x017164 hp_pct_test(ADT201, 0) != 1 - TRIGGER 0x017240 global[0] != 1 +## phase 2 — FORCE_END_PHASE at 0x017304: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -1172,10 +1110,7 @@ fired by time passing, which is not a property of the graph. precond 0x000CF8 player_gauge0_test(0) != 1 TRIGGER 0x000D34 player_gauge1_test(0) != 1 -## phase 1 — FORCE_END_PHASE at 0x001584: 3 necessary condition(s) - TRIGGER 0x001404 builtin141(TCN001, 1, 2, 0, 0, 0, 1000, 5, 100, 100) != 1 - TRIGGER 0x001524 builtin141(TCN001, 1, 2, 0, -4000, 0, 1000, 5, 100, 100) != 1 - TRIGGER 0x001544 global[0] != 1 +## phase 1 — FORCE_END_PHASE at 0x001584: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -1358,10 +1293,7 @@ fired by time passing, which is not a property of the graph. precond 0x0076D8 hp_pct_test(TCN004, 0) != 1 precond 0x007D84 read_freg(0) < 1800 -## phase 1 — FORCE_END_PHASE at 0x00C114: 3 necessary condition(s) - precond 0x00BB08 unit_state(ADT101) != 1 - precond 0x00BDE8 unit_state(ADT108) != 1 - TRIGGER 0x00C090 global[80] != 1 +## phase 1 — FORCE_END_PHASE at 0x00C114: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -1374,7 +1306,7 @@ entry to that `END_PHASE` passes through it, so it is NECESSARY for the phase to end that way. Reachability alone is the wrong query here — in a poll loop both successors reach the exit. -CFG reached 9522 of 9936 instructions (95.8%). +CFG reached 9577 of 9936 instructions (96.4%). `precond` = necessary: the exit cannot happen unless it holds. `TRIGGER` = also SUFFICIENT: once it holds the exit is unavoidable. @@ -1396,8 +1328,7 @@ fired by time passing, which is not a property of the graph. precond 0x017AB0 read_freg(0) < 1200 TRIGGER 0x017BAC global[0] >= 100 -## phase 1 — FORCE_END_PHASE at 0x01C28C: UNREACHABLE from any static entry - (started from the trigger queue at `phase+272`, by data not code) +## phase 1 — FORCE_END_PHASE at 0x01C28C: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -1438,8 +1369,7 @@ fired by time passing, which is not a property of the graph. precond 0x00462C read_freg(0) < 900 TRIGGER 0x004828 global[0] != 0 -## phase 1 — FORCE_END_PHASE at 0x00FF5C: 1 necessary condition(s) - TRIGGER 0x00FE2C hp_pct_test(TCT019, 50) != 1 +## phase 1 — FORCE_END_PHASE at 0x00FF5C: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -1497,8 +1427,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x003514 unit_state(TCN005) != 1 TRIGGER 0x003710 hp_pct_test(TCN004, 80) != 1 -## phase 1 — FORCE_END_PHASE at 0x0088A0: 1 necessary condition(s) - TRIGGER 0x0087DC hp_pct_test(ADN119, 50) != 1 +## phase 1 — FORCE_END_PHASE at 0x0088A0: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -1534,8 +1463,7 @@ fired by time passing, which is not a property of the graph. precond 0x002920 read_freg(0) < 1200 TRIGGER 0x002A4C unit_hp_pct(TCN001, ELLEN) != 0 -## phase 1 — FORCE_END_PHASE at 0x006F24: 1 necessary condition(s) - TRIGGER 0x006E60 hp_pct_test(ADT112, 0) != 1 +## phase 1 — FORCE_END_PHASE at 0x006F24: 0 necessary condition(s) ------------------------------------------------------------------------ @@ -1548,7 +1476,7 @@ entry to that `END_PHASE` passes through it, so it is NECESSARY for the phase to end that way. Reachability alone is the wrong query here — in a poll loop both successors reach the exit. -CFG reached 14755 of 19779 instructions (74.6%). +CFG reached 14840 of 19779 instructions (75.0%). `precond` = necessary: the exit cannot happen unless it holds. `TRIGGER` = also SUFFICIENT: once it holds the exit is unavoidable. @@ -1588,5 +1516,4 @@ fired by time passing, which is not a property of the graph. precond 0x011534 global[52] < 14 precond 0x011554 global[60] != 1 -## phase 1 — FORCE_END_PHASE at 0x038960: UNREACHABLE from any static entry - (started from the trigger queue at `phase+272`, by data not code) +## phase 1 — FORCE_END_PHASE at 0x038960: 0 necessary condition(s) diff --git a/docs/re/data/isl-stage02-conditions.txt b/docs/re/data/isl-stage02-conditions.txt index ea642240..34df3441 100644 --- a/docs/re/data/isl-stage02-conditions.txt +++ b/docs/re/data/isl-stage02-conditions.txt @@ -993,5 +993,5 @@ block over its ACTUAL predecessors — not from a linear walk. 0x034968 if == 1 -> 0x34988 0x034988 if global[20] != 7 -> 0x34A00 0x0349E0 if == 1 -> 0x34A00 - 0x034A90 if == -> 0x34A30 - 0x034AA4 if == -> 0x34A5C + 0x034A90 if global[116] == 1 -> 0x34A30 + 0x034AA4 if global[116] == 2 -> 0x34A5C diff --git a/docs/re/data/isl-stage02-phase-guards.txt b/docs/re/data/isl-stage02-phase-guards.txt index b1c19ae7..aacfab7c 100644 --- a/docs/re/data/isl-stage02-phase-guards.txt +++ b/docs/re/data/isl-stage02-phase-guards.txt @@ -7,7 +7,7 @@ entry to that `END_PHASE` passes through it, so it is NECESSARY for the phase to end that way. Reachability alone is the wrong query here — in a poll loop both successors reach the exit. -CFG reached 15670 of 18739 instructions (83.6%). +CFG reached 15821 of 18739 instructions (84.4%). `precond` = necessary: the exit cannot happen unless it holds. `TRIGGER` = also SUFFICIENT: once it holds the exit is unavoidable. @@ -38,8 +38,7 @@ fired by time passing, which is not a property of the graph. precond 0x005888 read_freg(0) <= 210 precond 0x006070 read_freg(0) < 1200 -## phase 1 — FORCE_END_PHASE at 0x01482C: UNREACHABLE from any static entry - (started from the trigger queue at `phase+272`, by data not code) +## phase 1 — FORCE_END_PHASE at 0x01482C: 0 necessary condition(s) ## phase 2 — END_PHASE at 0x019640: 2 necessary condition(s) precond 0x0191CC unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -60,9 +59,7 @@ fired by time passing, which is not a property of the graph. TRIGGER 0x01AA38 global[104] != 0 TRIGGER 0x01AAF8 global[4] == 0 -## phase 2 — FORCE_END_PHASE at 0x0249F0: 2 necessary condition(s) - precond 0x0247E4 hp_pct_test(ADN202, 0) != 1 - TRIGGER 0x024848 unit_state(TCT206) != 1 +## phase 2 — FORCE_END_PHASE at 0x0249F0: 0 necessary condition(s) ## phase 3 — END_PHASE at 0x02B96C: 2 necessary condition(s) precond 0x02B4F8 unit_hp_pct(TCN001, Character_Player_Test) != 0 @@ -103,5 +100,4 @@ fired by time passing, which is not a property of the graph. precond 0x02C240 read_freg(0) <= 300 precond 0x02CFD4 read_freg(0) < 1200 -## phase 3 — FORCE_END_PHASE at 0x034A10: UNREACHABLE from any static entry - (started from the trigger queue at `phase+272`, by data not code) +## phase 3 — FORCE_END_PHASE at 0x034A10: 0 necessary condition(s) diff --git a/docs/re/isl-stream-entry-points.md b/docs/re/isl-stream-entry-points.md new file mode 100644 index 00000000..756cf660 --- /dev/null +++ b/docs/re/isl-stream-entry-points.md @@ -0,0 +1,74 @@ +# ✅ The mission-level `0x1883` record carries two ENTRY POINTS — and they were the missing exits + +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 for a random 4-aligned +offset.** + +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. diff --git a/tools/re-capture/isl_cfg.py b/tools/re-capture/isl_cfg.py index dfbc7ea4..abe49a68 100644 --- a/tools/re-capture/isl_cfg.py +++ b/tools/re-capture/isl_cfg.py @@ -132,7 +132,8 @@ def dominating_conditions(b): import collections as _c E, nxt = edges(b, spawn=True) offs = isl.linear_offsets(b) - entries = {e for e in set(isl.phase_bases(b)) | set(coroutine_entries(b)) if e in nxt} + entries = {e for e in set(isl.phase_bases(b)) | set(coroutine_entries(b)) + | set(stream_entries(b)) if e in nxt} preds = _c.defaultdict(list) for a, ss in E.items(): for s in ss: preds[s].append(a) @@ -174,6 +175,31 @@ def dominating_conditions(b): return out, len(R), len(offs) +def stream_entries(b): + """The pair of entry points each mission-level `0x1883` record carries. + + Layout, per phase: `0x1883, base_delta, size, 0, entry_a, entry_b` + with both entries PHASE-RELATIVE. Measured over all 28 stages: 82 of 88 + land on a valid instruction (93.2 %) against a 38.6 % chance rate for a + random 4-aligned offset. + + These are why some routines have no static predecessor at all: nothing in + the bytecode names them. In Stage 02 the SECOND entry of phases 1 and 3 is + exactly the `FORCE_END_PHASE` site that the CFG could not otherwise reach. + """ + code = struct.unpack_from('>I', b, 0x08)[0] + end = struct.unpack_from('>I', b, 0x0C)[0] + out = [] + for o in range(0x24, 0x400, 4): + if o + 24 > len(b): break + if struct.unpack_from('>I', b, o)[0] == 0x1883: + base = code + struct.unpack_from('>I', b, o + 4)[0] + for k in (16, 20): + t = base + struct.unpack_from('>I', b, o + k)[0] + if 0 <= t < end: out.append(t) + return out + + def coroutine_entries(b): """Every `start_coroutine` target, found by a linear pre-pass.""" offs = isl.linear_offsets(b) @@ -212,7 +238,7 @@ def conditions(b, s1=None, s2=None): # have no static predecessor. Seed every `start_coroutine` target as well -- # a linear pre-pass finds them because the target is staged into local[0] # immediately before the call, which no control-flow boundary intervenes in. - entries = list(bases) + coroutine_entries(b) + entries = list(bases) + coroutine_entries(b) + stream_entries(b) for e in entries: if e in nxt or e == offs[-1]: IN[e] = EMPTY; work.append(e)