mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-12 02:11:03 +00:00
Every lifted instruction read and wrote c->x[n] and c->sp, so each register passed from one guest instruction to the next went through a store and a load, and clang could not keep it in a host register across a guest store. recomp/regs.py now copies the six most used X registers and SP of a function into locals at its entry. A forward pass over the function's flow tracks which locals changed since the last sync, and only those are stored back before a call, an SVC, a trap, a return or a tail exit. After an SVC every local reloads; after a call only X0-X18 and X30, since AAPCS64 has the callee preserve X19-X29 and SP. TotK does not break that: main and subsdk0 import neither longjmp nor the unwinder, sdk's have no direct callers, and the runtime writes no register above X3 except X30. gen.py --no-callee-saved-trust reloads them too, --reg-locals sets the count. Functions with an unresolved computed br keep their registers in the Cpu, as they keep NZCV. All registers in locals was slower than none: clang spills them to the stack and the syncs stay. Over 300 hot functions, 4-6 locals gave the fewest instructions and memory operands. 1.4.3 .text 299 -> 266 MiB. Fight bench (bench-fight2-2026-10-27b, frames 5400-7500, uncapped): 11.30 and 11.92 ms vs 13.5-13.9 on main; frames 2400-5300 11.24 and 11.36 vs 13.25. Without the callee-saved trust 12.58 ms. tests/diff/func.py: 0 failing on 1.4.3 (889 functions, 583 with calls) and 1.2.1 (810); a plan with no spills fails at once. Probes kernel, vkexec, nvn-tex and shader pass in both versions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>