1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-08 17:06:04 +00:00

codegen: fuse compare-then-branch to drop boolean materialization

Every NEScript condition (`if x < N`, `while i < end`, etc.)
lowers in two IR ops: `CmpX(d, a, b)` materializes a 0/1
boolean into temp `d`, and the block's terminator
`Branch(d, t, f)` reads `d` and branches on it. The codegen
faithfully emitted both halves — `LDA / CMP / branch-to-true /
LDA #0 / JMP done / true: LDA #1 / done:`, then later
`LDA d_slot / BNE branch_t / JMP branch_f` — about 14 cycles +
13 bytes per condition.

The 6502's natural pattern is one `CMP` + one branch on the
flags it just set: 8 cycles, no register-clobber, no temp slot.
Detect the canonical pattern in `gen_block` (last op is an 8-bit
`CmpX` whose dest temp is what the terminator branches on, with
no other uses) and emit the fused form directly via a new
`gen_cmp_branch` helper. The temp's allocation, store, load, and
the terminator's branch fall away.

Bookkeeping subtlety: the source temps `a`/`b` must be retired
*after* the fused emit, not before — the original `gen_op` order
is "emit body of op, then `retire_op_sources`". Decrementing
their use counts before the CMP would free their slots while
they were still live; `load_temp(a)` would then re-allocate `a`
to whatever stale slot the free list popped next. Got hit by
this on the first attempt — the SHA-256 example dutifully
returned all-zero hashes until the order was fixed.

Updated `ir_codegen_local_label_suffix_is_bank_namespaced`: the
test was relying on `if x == 0` to emit `__ir_cmp_*` labels for
its bank-namespacing check, which the fusion now collapses into
direct branches. Switched the test source to a shift-by-variable
pattern (`x = x << n`), which always emits `__ir_shift_loop_*`
labels regardless of future cmp/branch optimizations.

Cycle savings: ~6 cycles per condition. The SHA-256 rotate
loops alone account for ~9K cycles per block. Across all
examples the cycle drift shows up as audio-tick phase shifts
in five timing-sensitive ROMs (`audio_demo`, `friendly_assets`,
`noise_triangle_sfx`, `platformer`, `sfx_pitch_envelope`); the
goldens for those are refreshed in this commit, plus
`platformer.gif` (the only demo gif whose bytes actually moved).

Verified: cargo test/clippy/fmt clean on rustc 1.95.0;
emulator harness 34/34; reproducibility diff clean; SHA-256 of
"NES" still computes to AE9145DB…4E0D.

https://claude.ai/code/session_01FRmSBruVWCufm3LsUVMs8v
This commit is contained in:
Claude 2026-04-16 17:00:07 +00:00
parent 726faef538
commit 0600f5b872
No known key found for this signature in database
38 changed files with 184 additions and 20 deletions

View file

@ -1 +1 @@
95b6d6d4 132084
7b16a9db 132084

View file

@ -1 +1 @@
c49fe29d 132084
e73a6a73 132084

View file

@ -1 +1 @@
8fd9f7d2 132084
ea38044c 132084

View file

@ -1 +1 @@
f5c17e60 132084
dded3a6a 132084

View file

@ -1 +1 @@
9b06b3fc 132084
a7ce9d12 132084

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,010 B

After

Width:  |  Height:  |  Size: 1,012 B

Before After
Before After

View file

@ -1 +1 @@
61bd9e17 132084
eec31111 132084

View file

@ -1 +1 @@
62c8ca6b 132084
d6cf6f66 132084