diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index 55884d8..e3884e2 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -1434,9 +1434,17 @@ premise was wrong.** over 28 stages: exactly **34 of 7563** sites change, **34 → 0** with an `end_coroutine` LHS, and the two counts being equal proves the leak was confined to them — the other 7529 were never affected. They now print an - explicit unknown. 🟡 Recoverable but not recovered: the RHS of each is still - exact, and seeding the tracker at coroutine ENTRIES (`start_coroutine`'s target - is staged slot 0) would resolve the LHS. ▶️ **Still open:** the 35 unnamed built-ins (`builtin16` 132 + explicit unknown. + 🔴 **(2026-08-27) THAT FIX WAS TOO NARROW — I fixed the instance, not the class.** + `op12 jmp` is unconditional, so the next instruction is never reached by + fall-through either, and the tracker walked through it exactly as it had walked + through `end_coroutine`. Exposed by another impossible output: a six-way switch + on `builtin80`, which returns only 1 or 0. A/B over 28 stages: **889 of 7563 + sites (11.75 %) change**, and unresolved goes **34 (0.45 %) → 756 (10.00 %)**. + The earlier "0.0 % unresolved" was a MISSING CHECK, not a strong result. + 🟡 Recovering the 756 needs a real dataflow **join over each block's actual + predecessors** (a CFG fixpoint) — the branch targets are all known, the analysis + is not written. ▶️ **Still open:** the 35 unnamed built-ins (`builtin16` 132 sites, `builtin105` 117, `builtin103` 115 — each now a vtable-slot lookup); ✅ **(2026-08-27) partial — [structures/isl-unit-args](structures/isl-unit-args.md):** reading the implementations shows **55** built-ins take a unit at `local[4]`, @@ -1448,7 +1456,13 @@ premise was wrong.** `slot0 == 1` in 100.0 % / 100.0 % / **2.5 %**. Artefacts: calls + phase-ends byte-identical, conditions changes on 28 sites (raw number → unit name). Still unnamed: all 24. `builtin103` is a predicate over `[phase+10152]` and - `[phase+10156]` (no unit arg); `builtin105` tests a unit record's `+16` == 4. + `[phase+10156]` (no unit arg); `builtin105` tests a unit record's `+16` == 4 + (`isl-builtins.md` documents `rec+16` as the unit STATE, 2 = active). + 🔴 **(2026-08-27) `builtin80` is a COMMAND, not a predicate** (`0x82268460`): + it allocates a 20-byte object, stamps vtable `0x820A8CB0` + magic `0xAB0311BA` + + 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); 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 diff --git a/docs/re/data/isl-stage02-conditions.txt b/docs/re/data/isl-stage02-conditions.txt index 812e8c9..26bfeab 100644 --- a/docs/re/data/isl-stage02-conditions.txt +++ b/docs/re/data/isl-stage02-conditions.txt @@ -16,12 +16,9 @@ the right-hand side is evaluated (its result lands in `special[0]`), unit_relation 52 builtin103 39 builtin105 38 - builtin80 28 - request_script_message 26 read_freg 10 unit_hp_pct 9 builtin7 9 - builtin83 2 builtin99 1 ## phase 1 — 435 sites @@ -29,17 +26,17 @@ the right-hand side is evaluated (its result lands in `special[0]`), 0x0032F4 if unit_alive(TCN105) != 1 -> 0x3388 0x0044A4 if unit_alive(TCN105) != 1 -> 0x4538 0x004D70 if unit_hp_pct(TCN001, Character_Player_Test) != 0 -> 0x51FC - 0x005110 if request_script_message(MSG_VOICE_D_257, 2, 1, 9, 1, -1) == 0 -> 0x4E68 - 0x005124 if request_script_message(MSG_VOICE_D_257, 2, 1, 9, 1, -1) == 1 -> 0x4F10 - 0x005138 if request_script_message(MSG_VOICE_D_257, 2, 1, 9, 1, -1) == 2 -> 0x4FB8 - 0x00514C if request_script_message(MSG_VOICE_D_257, 2, 1, 9, 1, -1) == 3 -> 0x5060 + 0x005110 if == 0 -> 0x4E68 + 0x005124 if == 1 -> 0x4F10 + 0x005138 if == 2 -> 0x4FB8 + 0x00514C if == 3 -> 0x5060 0x00523C if hp_pct_test(TCN004, 0) != 1 -> 0x5840 - 0x00572C if request_script_message(MSG_VOICE_D_261, 2, 1, 9, 1, -1) == 0 -> 0x5334 - 0x005740 if request_script_message(MSG_VOICE_D_261, 2, 1, 9, 1, -1) == 1 -> 0x53DC - 0x005754 if request_script_message(MSG_VOICE_D_261, 2, 1, 9, 1, -1) == 2 -> 0x5484 - 0x005768 if request_script_message(MSG_VOICE_D_261, 2, 1, 9, 1, -1) == 3 -> 0x552C - 0x00577C if request_script_message(MSG_VOICE_D_261, 2, 1, 9, 1, -1) == 4 -> 0x55D4 - 0x005790 if request_script_message(MSG_VOICE_D_261, 2, 1, 9, 1, -1) == 5 -> 0x567C + 0x00572C if == 0 -> 0x5334 + 0x005740 if == 1 -> 0x53DC + 0x005754 if == 2 -> 0x5484 + 0x005768 if == 3 -> 0x552C + 0x00577C if == 4 -> 0x55D4 + 0x005790 if == 5 -> 0x567C 0x005888 if read_freg(0) <= 210 -> 0x6028 0x0058DC if hp_pct_test(ADT102, 0) != 1 -> 0x6028 0x005930 if hp_pct_test(ADT107, 0) != 1 -> 0x6028 @@ -177,7 +174,7 @@ the right-hand side is evaluated (its result lands in `special[0]`), 0x00C668 if unit_state(ADN111) != 1 -> 0xC6E8 0x00C72C if unit_state(ADN112) != 1 -> 0xC7AC 0x00C7F0 if unit_state(ADS151) != 1 -> 0xC870 - 0x00C930 if hp_pct_test(TCN004, 50, 26) != 1 -> 0xEC70 + 0x00C930 if hp_pct_test(TCN004, 50) != 1 -> 0xEC70 0x00C944 if global[20] > 25 -> 0xEC70 0x00C99C if unit_state(ADN101) != 1 -> 0xCA08 0x00C9EC if unit_relation(ADN101, TCN004) != 1 -> 0xCA08 @@ -273,21 +270,21 @@ the right-hand side is evaluated (its result lands in `special[0]`), 0x0103D0 if unit_state(ADT113) == 1 -> 0x10438 0x010484 if unit_state(ADN114) == 1 -> 0x104EC 0x010538 if unit_state(ADN115) == 1 -> 0x105A0 - 0x0105C4 if == 28 -> 0xFB1C - 0x0105D8 if == 29 -> 0xFBD0 - 0x0105EC if == 30 -> 0xFC84 - 0x010600 if == 31 -> 0xFD38 - 0x010614 if == 32 -> 0xFDEC - 0x010628 if == 33 -> 0xFEA0 - 0x01063C if == 34 -> 0xFF54 - 0x010650 if == 35 -> 0x10008 - 0x010664 if == 36 -> 0x100BC - 0x010678 if == 37 -> 0x10170 - 0x01068C if == 38 -> 0x10224 - 0x0106A0 if == 39 -> 0x102D8 - 0x0106B4 if == 40 -> 0x1038C - 0x0106C8 if == 41 -> 0x10440 - 0x0106DC if == 42 -> 0x104F4 + 0x0105C4 if == 28 -> 0xFB1C + 0x0105D8 if == 29 -> 0xFBD0 + 0x0105EC if == 30 -> 0xFC84 + 0x010600 if == 31 -> 0xFD38 + 0x010614 if == 32 -> 0xFDEC + 0x010628 if == 33 -> 0xFEA0 + 0x01063C if == 34 -> 0xFF54 + 0x010650 if == 35 -> 0x10008 + 0x010664 if == 36 -> 0x100BC + 0x010678 if == 37 -> 0x10170 + 0x01068C if == 38 -> 0x10224 + 0x0106A0 if == 39 -> 0x102D8 + 0x0106B4 if == 40 -> 0x1038C + 0x0106C8 if == 41 -> 0x10440 + 0x0106DC if == 42 -> 0x104F4 0x010728 if builtin103() != 0 -> 0x10748 0x010788 if hp_pct_test(TCN004, 30) != 1 -> 0x108D4 0x010814 if dist_lt(TCN004, TCN001, 10000) == 1 -> 0x108D4 @@ -469,10 +466,10 @@ the right-hand side is evaluated (its result lands in `special[0]`), 0x018FFC if hp_pct_test(ADN208, 0) != 1 -> 0x19090 0x019124 if builtin103() != 1 -> 0x1915C 0x0191CC if unit_hp_pct(TCN001, Character_Player_Test) != 0 -> 0x19658 - 0x01956C if request_script_message(MSG_VOICE_D_257, 2, 1, 9, 1, -1) == 0 -> 0x192C4 - 0x019580 if request_script_message(MSG_VOICE_D_257, 2, 1, 9, 1, -1) == 1 -> 0x1936C - 0x019594 if request_script_message(MSG_VOICE_D_257, 2, 1, 9, 1, -1) == 2 -> 0x19414 - 0x0195A8 if request_script_message(MSG_VOICE_D_257, 2, 1, 9, 1, -1) == 3 -> 0x194BC + 0x01956C if == 0 -> 0x192C4 + 0x019580 if == 1 -> 0x1936C + 0x019594 if == 2 -> 0x19414 + 0x0195A8 if == 3 -> 0x194BC 0x019698 if hp_pct_test(TCT206, 0) != 1 -> 0x1994C 0x0199C8 if builtin7(TCT206, 1, Route_TCT206_p2S, 4294967295, 500) == 1 -> 0x19A38 0x019A24 if read_freg(0) <= 600 -> 0x19A44 @@ -531,21 +528,21 @@ the right-hand side is evaluated (its result lands in `special[0]`), 0x01B0D4 if dist_lt(TCT206, ADN232, 15000) != 1 -> 0x1B138 0x01B180 if hp_pct_test(ADN201, 0) == 1 -> 0x1B220 0x01B20C if dist_lt(TCT206, ADN201, 20000) == 1 -> 0x1B250 - 0x01B298 if hp_pct_test(ADN202, 0, 0) == 1 -> 0x1B338 + 0x01B298 if hp_pct_test(ADN202, 0) == 1 -> 0x1B338 0x01B324 if dist_lt(TCT206, ADN202, 20000) == 1 -> 0x1B368 - 0x01B3B0 if hp_pct_test(ADN207, 0, 0) == 1 -> 0x1B450 + 0x01B3B0 if hp_pct_test(ADN207, 0) == 1 -> 0x1B450 0x01B43C if dist_lt(TCT206, ADN207, 20000) == 1 -> 0x1B480 - 0x01B4C8 if hp_pct_test(ADN231, 0, 0) == 1 -> 0x1B568 + 0x01B4C8 if hp_pct_test(ADN231, 0) == 1 -> 0x1B568 0x01B554 if dist_lt(TCT206, ADN231, 20000) == 1 -> 0x1B598 - 0x01B5E0 if hp_pct_test(ADN232, 0, 0) == 1 -> 0x1B680 + 0x01B5E0 if hp_pct_test(ADN232, 0) == 1 -> 0x1B680 0x01B66C if dist_lt(TCT206, ADN232, 20000) == 1 -> 0x1B6B0 - 0x01B6C0 if builtin80(TCT206) == 0 -> 0x1AC74 - 0x01B6D4 if builtin80(TCT206) == 1 -> 0x1B140 - 0x01B6E8 if builtin80(TCT206) == 2 -> 0x1B258 - 0x01B6FC if builtin80(TCT206) == 3 -> 0x1B370 - 0x01B710 if builtin80(TCT206) == 4 -> 0x1B488 - 0x01B724 if builtin80(TCT206) == 5 -> 0x1B5A0 - 0x01B77C if hp_pct_test(TCN207, 0, 0) != 1 -> 0x1B79C + 0x01B6C0 if == 0 -> 0x1AC74 + 0x01B6D4 if == 1 -> 0x1B140 + 0x01B6E8 if == 2 -> 0x1B258 + 0x01B6FC if == 3 -> 0x1B370 + 0x01B710 if == 4 -> 0x1B488 + 0x01B724 if == 5 -> 0x1B5A0 + 0x01B77C if hp_pct_test(TCN207, 0) != 1 -> 0x1B79C 0x01B7D4 if unit_alive(ADN201) != 1 -> 0x1B8A4 0x01B840 if dist_lt(TCN207, ADN201, 15000) != 1 -> 0x1B8A4 0x01B8C8 if unit_alive(ADN202) != 1 -> 0x1B998 @@ -558,21 +555,21 @@ the right-hand side is evaluated (its result lands in `special[0]`), 0x01BC10 if dist_lt(TCN207, ADN232, 15000) != 1 -> 0x1BC74 0x01BCBC if hp_pct_test(ADN201, 0) == 1 -> 0x1BD5C 0x01BD48 if dist_lt(TCN207, ADN201, 20000) == 1 -> 0x1BD8C - 0x01BDD4 if hp_pct_test(ADN202, 0, 0) == 1 -> 0x1BE74 + 0x01BDD4 if hp_pct_test(ADN202, 0) == 1 -> 0x1BE74 0x01BE60 if dist_lt(TCN207, ADN202, 20000) == 1 -> 0x1BEA4 - 0x01BEEC if hp_pct_test(ADN207, 0, 0) == 1 -> 0x1BF8C + 0x01BEEC if hp_pct_test(ADN207, 0) == 1 -> 0x1BF8C 0x01BF78 if dist_lt(TCN207, ADN207, 20000) == 1 -> 0x1BFBC - 0x01C004 if hp_pct_test(ADN231, 0, 0) == 1 -> 0x1C0A4 + 0x01C004 if hp_pct_test(ADN231, 0) == 1 -> 0x1C0A4 0x01C090 if dist_lt(TCN207, ADN231, 20000) == 1 -> 0x1C0D4 - 0x01C11C if hp_pct_test(ADN232, 0, 0) == 1 -> 0x1C1BC + 0x01C11C if hp_pct_test(ADN232, 0) == 1 -> 0x1C1BC 0x01C1A8 if dist_lt(TCN207, ADN232, 20000) == 1 -> 0x1C1EC - 0x01C1FC if builtin80(TCN207) == 0 -> 0x1B7B0 - 0x01C210 if builtin80(TCN207) == 1 -> 0x1BC7C - 0x01C224 if builtin80(TCN207) == 2 -> 0x1BD94 - 0x01C238 if builtin80(TCN207) == 3 -> 0x1BEAC - 0x01C24C if builtin80(TCN207) == 4 -> 0x1BFC4 - 0x01C260 if builtin80(TCN207) == 5 -> 0x1C0DC - 0x01C2B8 if hp_pct_test(TCN208, 0, 0) != 1 -> 0x1C2D8 + 0x01C1FC if == 0 -> 0x1B7B0 + 0x01C210 if == 1 -> 0x1BC7C + 0x01C224 if == 2 -> 0x1BD94 + 0x01C238 if == 3 -> 0x1BEAC + 0x01C24C if == 4 -> 0x1BFC4 + 0x01C260 if == 5 -> 0x1C0DC + 0x01C2B8 if hp_pct_test(TCN208, 0) != 1 -> 0x1C2D8 0x01C310 if unit_alive(ADN201) != 1 -> 0x1C3E0 0x01C37C if dist_lt(TCN208, ADN201, 15000) != 1 -> 0x1C3E0 0x01C404 if unit_alive(ADN202) != 1 -> 0x1C4D4 @@ -585,20 +582,20 @@ the right-hand side is evaluated (its result lands in `special[0]`), 0x01C74C if dist_lt(TCN208, ADN232, 15000) != 1 -> 0x1C7B0 0x01C7F8 if hp_pct_test(ADN201, 0) == 1 -> 0x1C898 0x01C884 if dist_lt(TCN208, ADN201, 20000) == 1 -> 0x1C8C8 - 0x01C910 if hp_pct_test(ADN202, 0, 0) == 1 -> 0x1C9B0 + 0x01C910 if hp_pct_test(ADN202, 0) == 1 -> 0x1C9B0 0x01C99C if dist_lt(TCN208, ADN202, 20000) == 1 -> 0x1C9E0 - 0x01CA28 if hp_pct_test(ADN207, 0, 0) == 1 -> 0x1CAC8 + 0x01CA28 if hp_pct_test(ADN207, 0) == 1 -> 0x1CAC8 0x01CAB4 if dist_lt(TCN208, ADN207, 20000) == 1 -> 0x1CAF8 - 0x01CB40 if hp_pct_test(ADN231, 0, 0) == 1 -> 0x1CBE0 + 0x01CB40 if hp_pct_test(ADN231, 0) == 1 -> 0x1CBE0 0x01CBCC if dist_lt(TCN208, ADN231, 20000) == 1 -> 0x1CC10 - 0x01CC58 if hp_pct_test(ADN232, 0, 0) == 1 -> 0x1CCF8 + 0x01CC58 if hp_pct_test(ADN232, 0) == 1 -> 0x1CCF8 0x01CCE4 if dist_lt(TCN208, ADN232, 20000) == 1 -> 0x1CD28 - 0x01CD38 if builtin80(TCN208) == 0 -> 0x1C2EC - 0x01CD4C if builtin80(TCN208) == 1 -> 0x1C7B8 - 0x01CD60 if builtin80(TCN208) == 2 -> 0x1C8D0 - 0x01CD74 if builtin80(TCN208) == 3 -> 0x1C9E8 - 0x01CD88 if builtin80(TCN208) == 4 -> 0x1CB00 - 0x01CD9C if builtin80(TCN208) == 5 -> 0x1CC18 + 0x01CD38 if == 0 -> 0x1C2EC + 0x01CD4C if == 1 -> 0x1C7B8 + 0x01CD60 if == 2 -> 0x1C8D0 + 0x01CD74 if == 3 -> 0x1C9E8 + 0x01CD88 if == 4 -> 0x1CB00 + 0x01CD9C if == 5 -> 0x1CC18 0x01D200 if hp_pct_test(ADN201, 0) != 1 -> 0x1D244 0x01D27C if unit_alive(TCN207) != 1 -> 0x1D34C 0x01D2E8 if dist_lt(ADN201, TCN207, 15000) != 1 -> 0x1D34C @@ -609,17 +606,17 @@ the right-hand side is evaluated (its result lands in `special[0]`), 0x01D658 if builtin7(ADN201, 1, Route_ADN201_p2S, 4294967295, 500) != 1 -> 0x1D6F8 0x01D738 if hp_pct_test(TCN207, 0) == 1 -> 0x1D7D8 0x01D7C4 if dist_lt(ADN201, TCN207, 20000) == 1 -> 0x1D808 - 0x01D880 if builtin7(ADN201, 1, Route_ADN201_p2S, 4294967295, 500, 0) != 1 -> 0x1D920 + 0x01D880 if builtin7(ADN201, 1, Route_ADN201_p2S, 4294967295, 500) != 1 -> 0x1D920 0x01D960 if hp_pct_test(TCN208, 0) == 1 -> 0x1DA00 0x01D9EC if dist_lt(ADN201, TCN208, 20000) == 1 -> 0x1DA30 - 0x01DAA8 if builtin7(ADN201, 1, Route_ADN201_p2S, 4294967295, 500, 0) != 1 -> 0x1DB48 + 0x01DAA8 if builtin7(ADN201, 1, Route_ADN201_p2S, 4294967295, 500) != 1 -> 0x1DB48 0x01DBC0 if dist_lt(ADN201, TCT206, 20000) == 1 -> 0x1DC04 - 0x01DC1C if builtin80(ADN201) == 0 -> 0x1D258 - 0x01DC30 if builtin80(ADN201) == 1 -> 0x1D5E8 - 0x01DC44 if builtin80(ADN201) == 2 -> 0x1D810 - 0x01DC58 if builtin80(ADN201) == 3 -> 0x1DA38 - 0x01DC6C if builtin80(ADN201) == 4 -> 0x1DC0C - 0x01DCC4 if hp_pct_test(ADN202, 0, 0) != 1 -> 0x1DD08 + 0x01DC1C if == 0 -> 0x1D258 + 0x01DC30 if == 1 -> 0x1D5E8 + 0x01DC44 if == 2 -> 0x1D810 + 0x01DC58 if == 3 -> 0x1DA38 + 0x01DC6C if == 4 -> 0x1DC0C + 0x01DCC4 if hp_pct_test(ADN202, 0) != 1 -> 0x1DD08 0x01DD4C if hp_pct_test(ADN207, 0) != 1 -> 0x1DD6C 0x01DDA4 if unit_alive(TCN207) != 1 -> 0x1DE74 0x01DE10 if dist_lt(ADN207, TCN207, 15000) != 1 -> 0x1DE74 @@ -632,18 +629,18 @@ the right-hand side is evaluated (its result lands in `special[0]`), 0x01E27C if unit_alive(TCN208) != 1 -> 0x1E31C 0x01E3C4 if hp_pct_test(TCN207, 0) == 1 -> 0x1E464 0x01E450 if dist_lt(ADN207, TCN207, 20000) == 1 -> 0x1E494 - 0x01E50C if builtin7(ADN207, 1, Route_ADN207_p2S, 3, 55, 0) != 1 -> 0x1E64C + 0x01E50C if builtin7(ADN207, 1, Route_ADN207_p2S, 3, 55) != 1 -> 0x1E64C 0x01E544 if unit_alive(TCN208) != 1 -> 0x1E5E4 0x01E68C if hp_pct_test(TCN208, 0) == 1 -> 0x1E72C 0x01E718 if dist_lt(ADN207, TCN208, 20000) == 1 -> 0x1E75C - 0x01E7D4 if builtin7(ADN207, 1, Route_ADN207_p2S, 3, 55, 0) != 1 -> 0x1E914 + 0x01E7D4 if builtin7(ADN207, 1, Route_ADN207_p2S, 3, 55) != 1 -> 0x1E914 0x01E80C if unit_alive(TCN208) != 1 -> 0x1E8AC 0x01E98C if dist_lt(ADN207, TCT206, 20000) == 1 -> 0x1E9D0 - 0x01E9E8 if builtin80(ADN207) == 0 -> 0x1DD80 - 0x01E9FC if builtin80(ADN207) == 1 -> 0x1E1D4 - 0x01EA10 if builtin80(ADN207) == 2 -> 0x1E49C - 0x01EA24 if builtin80(ADN207) == 3 -> 0x1E764 - 0x01EA38 if builtin80(ADN207) == 4 -> 0x1E9D8 + 0x01E9E8 if == 0 -> 0x1DD80 + 0x01E9FC if == 1 -> 0x1E1D4 + 0x01EA10 if == 2 -> 0x1E49C + 0x01EA24 if == 3 -> 0x1E764 + 0x01EA38 if == 4 -> 0x1E9D8 0x01F78C if unit_state(ADN201) != 1 -> 0x1F7F8 0x01F7E4 if unit_state(TCT206) == 1 -> 0x1F804 0x01F85C if dist_lt(ADN201, TCT206, 10000) != 1 -> 0x1FA7C @@ -773,17 +770,17 @@ the right-hand side is evaluated (its result lands in `special[0]`), 0x02B0D8 if unit_alive(TCN002) != 1 -> 0x2B20C 0x02B230 if unit_alive(TCN003) != 1 -> 0x2B310 0x02B4F8 if unit_hp_pct(TCN001, Character_Player_Test) != 0 -> 0x2B984 - 0x02B898 if request_script_message(MSG_VOICE_D_257, 2, 1, 9, 1, -1) == 0 -> 0x2B5F0 - 0x02B8AC if request_script_message(MSG_VOICE_D_257, 2, 1, 9, 1, -1) == 1 -> 0x2B698 - 0x02B8C0 if request_script_message(MSG_VOICE_D_257, 2, 1, 9, 1, -1) == 2 -> 0x2B740 - 0x02B8D4 if request_script_message(MSG_VOICE_D_257, 2, 1, 9, 1, -1) == 3 -> 0x2B7E8 + 0x02B898 if == 0 -> 0x2B5F0 + 0x02B8AC if == 1 -> 0x2B698 + 0x02B8C0 if == 2 -> 0x2B740 + 0x02B8D4 if == 3 -> 0x2B7E8 0x02B9C4 if hp_pct_test(TCN004, 0) != 1 -> 0x2BFC8 - 0x02BEB4 if request_script_message(MSG_VOICE_D_261, 2, 1, 9, 1, -1) == 0 -> 0x2BABC - 0x02BEC8 if request_script_message(MSG_VOICE_D_261, 2, 1, 9, 1, -1) == 1 -> 0x2BB64 - 0x02BEDC if request_script_message(MSG_VOICE_D_261, 2, 1, 9, 1, -1) == 2 -> 0x2BC0C - 0x02BEF0 if request_script_message(MSG_VOICE_D_261, 2, 1, 9, 1, -1) == 3 -> 0x2BCB4 - 0x02BF04 if request_script_message(MSG_VOICE_D_261, 2, 1, 9, 1, -1) == 4 -> 0x2BD5C - 0x02BF18 if request_script_message(MSG_VOICE_D_261, 2, 1, 9, 1, -1) == 5 -> 0x2BE04 + 0x02BEB4 if == 0 -> 0x2BABC + 0x02BEC8 if == 1 -> 0x2BB64 + 0x02BEDC if == 2 -> 0x2BC0C + 0x02BEF0 if == 3 -> 0x2BCB4 + 0x02BF04 if == 4 -> 0x2BD5C + 0x02BF18 if == 5 -> 0x2BE04 0x02BFC8 if global[112] < 4 -> 0x2C1F8 0x02C240 if read_freg(0) <= 300 -> 0x2CF8C 0x02C294 if hp_pct_test(ADT301, 0) != 1 -> 0x2CF8C @@ -846,8 +843,8 @@ the right-hand side is evaluated (its result lands in `special[0]`), 0x02F6B8 if global[120] != 0 -> 0x2F780 0x02F6CC if global[124] != 1 -> 0x2F780 0x02F830 if global[108] != 1 -> 0x2F9FC - 0x02F9C8 if request_script_message(MSG_VOICE_A_223, 2, 0, 8, 1, -1) == 0 -> 0x2F870 - 0x02F9DC if request_script_message(MSG_VOICE_A_223, 2, 0, 8, 1, -1) == 1 -> 0x2F918 + 0x02F9C8 if == 0 -> 0x2F870 + 0x02F9DC if == 1 -> 0x2F918 0x02FA44 if 8 < 1 -> 0x2FBC8 0x02FC10 if 8 < 3 -> 0x2FCF4 0x02FD3C if 8 < 4 -> 0x2FE20 @@ -913,7 +910,7 @@ the right-hand side is evaluated (its result lands in `special[0]`), 0x032AF8 if unit_state(ADT307) != 1 -> 0x32B78 0x032BBC if unit_state(ADT308) != 1 -> 0x32C3C 0x032C80 if unit_state(ADS351) != 1 -> 0x32D00 - 0x032DDC if dist_lt(TCN001, TCN099, 8000, 8) != 1 -> 0x32E9C + 0x032DDC if dist_lt(TCN001, TCN099, 8000) != 1 -> 0x32E9C 0x032F00 if unit_hp_pct(TCN001, Character_Player_Test) <= 50 -> 0x32FD4 0x03301C if unit_state(ADS351) == 1 -> 0x3303C 0x033094 if dist_lt(TCN001, ADS351, 5000) != 1 -> 0x33154 @@ -992,5 +989,5 @@ the right-hand side is evaluated (its result lands in `special[0]`), 0x034968 if unit_state(ADT307) == 1 -> 0x34988 0x034988 if global[20] != 7 -> 0x34A00 0x0349E0 if unit_state(ADT308) == 1 -> 0x34A00 - 0x034A90 if builtin83(209) == 1 -> 0x34A30 - 0x034AA4 if builtin83(209) == 2 -> 0x34A5C + 0x034A90 if == 1 -> 0x34A30 + 0x034AA4 if == 2 -> 0x34A5C diff --git a/docs/re/structures/isl-conditions.md b/docs/re/structures/isl-conditions.md index 937f69a..aafd4b2 100644 --- a/docs/re/structures/isl-conditions.md +++ b/docs/re/structures/isl-conditions.md @@ -47,10 +47,13 @@ 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 % | +| **LHS honestly unresolved** | **756 — 10.0 %** | | RHS is a plain number | 7 544 — 99.7 % | +🔴 **The first version of this table claimed 0.0 % unresolved. That was wrong** — +see *The correction* below. ~11.75 % of the operands it reported were derived +from state that leaked across an unconditional jump. + **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. @@ -128,3 +131,55 @@ done. 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. + + +## 🔴 The correction — I fixed the instance, not the class + +The `end_coroutine` fix above was **too narrow**, and the giveaway was again an +impossible output: the listing showed + +``` +if builtin80(TCT206) == 0 … == 1 … == 2 … == 3 … == 4 … == 5 +``` + +a six-way switch on a built-in that returns only **1 or 0**. Reading +`builtin80`'s body (`0x82268460`) settles that it is not a predicate at all — it +allocates a 20-byte object, stamps a vtable `0x820A8CB0`, a magic `0xAB0311BA` +and the unit's live object into it, pushes it onto a queue via the same helper +`push.i` uses, and returns 1, or 0 when the unit is absent. **It is a command.** + +Disassembling the site explains it: + +``` +01B698 call builtin80(TCT206) +01B6A4 set.i global[76] = 0 +01B6B0 jmp -> 0x1B738 +01B6B8 jmp -> 0x1B738 +01B6C0 cmp.i … <- reached ONLY by a branch from elsewhere +``` + +Two unconditional jumps sit between the call and the compare. **`op12` is +unconditional, so the next instruction is never reached by fall-through** — and +the tracker walked straight through it, exactly as it had walked through +`end_coroutine`. One is a thread boundary and the other a block boundary, but +they are the same defect: a linear walk cannot carry state across a point where +control does not flow. + +| A/B over all 28 stages, 7563 sites | | +|---|---| +| sites whose operands change once `jmp` also resets | **889 — 11.75 %** | +| LHS unresolved, before → after | 34 (0.45 %) → **756 (10.00 %)** | + +So the earlier "0.0 % unresolved" was not a strong result, it was a **missing +check**: the walk always had *some* value to report, and reporting it was the +bug. 10 % is the honest figure, and the remaining 90 % is now trustworthy for a +reason — the state reaching those sites really does flow there. + +🟡 **Recovering the 756 needs real dataflow.** Each is a block entered only by a +branch, so its state is the *join* over its actual predecessors — a fixpoint over +the CFG, not a linear pass. The branch targets are all known (`[phase+232] + +word@+4`), so the CFG is available; the analysis is not written. + +**Method note worth keeping:** when the same defect appears twice, fix the class. +Patching `end_coroutine` alone left 22× more bad sites in place than it removed, +and only another impossible-looking output exposed them. \ No newline at end of file diff --git a/docs/re/structures/isl-unit-args.md b/docs/re/structures/isl-unit-args.md index 93fd865..356b6b6 100644 --- a/docs/re/structures/isl-unit-args.md +++ b/docs/re/structures/isl-unit-args.md @@ -76,9 +76,16 @@ A 40× separation, and the additions sit exactly on the positive class. raw number becoming a unit name: ``` -- 0x01B6C0 if builtin80(0x8B) == 0 -> + 0x01B6C0 if builtin80(TCT206) == 0 +- 0x01B6C0 if builtin80(1, 117) == 0 -> + 0x01B6C0 if builtin80(TCT206) == 0 ``` +⚠️ **That example was itself a bogus site** — see +[isl-conditions](isl-conditions.md#-the-correction--i-fixed-the-instance-not-the-class). +`0x1B6C0` is reached only by a branch, so attributing its comparison to +`builtin80` was wrong, and `builtin80` is a command rather than a predicate. The +UNIT_ARG result is unaffected — it was derived by reading implementations, not +from this listing — but the illustration was picked from bad output. + ## 🟡 Not settled * **The 24 are still unnamed.** Knowing an argument is a unit is not knowing what diff --git a/tools/re-capture/isl.py b/tools/re-capture/isl.py index fe6a88b..be8b0aa 100755 --- a/tools/re-capture/isl.py +++ b/tools/re-capture/isl.py @@ -383,10 +383,15 @@ def conditions(b, sym1=None, sym2=None): push and pop balance at 1877 each with ZERO underflows, and in Stage 02 all 319 `pop.i` sites are immediately followed by `cmp.i`. - The tracker resets at `end_coroutine`, which destroys the thread. A/B over - all 28 stages: exactly 34 of 7563 sites change, and all 34 are the ones that - previously reported the impossible `end_coroutine` as a left-hand side. They - become an explicit unknown rather than a wrong answer. + The tracker resets at every CONTROL-FLOW BOUNDARY: `end_coroutine` (returns 3, + destroying the thread) and `jmp` (unconditional, so the next instruction is + not reached by fall-through). A linear walk cannot know a block's state when + that block is only ever entered by a branch, so those sites report an explicit + unknown instead of a stale value. + + MEASURED over all 28 stages: resetting at `jmp` changes 889 of 7563 sites + (11.75%) and leaves 756 (10.00%) honestly unresolved. Recovering those needs + a real dataflow join over each block's actual predecessors, not a linear walk. """ bases = phase_bases(b) sp, loc, stack = {}, {}, [] @@ -439,6 +444,13 @@ def conditions(b, sym1=None, sym2=None): # `end_coroutine` itself as the left-hand side of a comparison, # which is impossible: it returns no value a script can test. sp, loc, stack, pend = {}, {}, [], None + elif op == 12: + # An UNCONDITIONAL jump: execution never reaches the following + # instruction by fall-through, so whatever this walk is carrying is + # not that block's state. Same bug class as `end_coroutine` below, + # and it is much bigger: 889 of 7563 sites (11.75%) had operands + # derived from state that leaked across a `jmp`. + sp, loc, stack, pend = {}, {}, [], None elif op == 21: stack.append(sp.get(1)) elif op == 22: @@ -452,7 +464,7 @@ def conditions(b, sym1=None, sym2=None): tgt = bases[ph - 1] + words[0] if words else None lhs = pend[1] if lhs.startswith('special['): - lhs = '' + lhs = '' out.append({'off': pend[0], 'phase': ph, 'lhs': lhs, 'rel': REL[op], 'rhs': pend[2], 'branch': off, 'target': tgt})