diff --git a/internal/optimizer/optimizer_test.go b/internal/optimizer/optimizer_test.go index 164524b..c62e542 100644 --- a/internal/optimizer/optimizer_test.go +++ b/internal/optimizer/optimizer_test.go @@ -1069,3 +1069,46 @@ func TestPassRegDead_ReadModifyWrite(t *testing.T) { }) } } + +func TestPassRegDead_JmpDoesNotKillStore(t *testing.T) { + // sta regVar; ...(then body)...; jmp _END; _ELSE:; ...; _END:; ldy regVar + // The jmp in the THEN body should NOT kill the store because + // the jump target _END reaches code that reads regVar. + input := []string{ + "\tldy #0", + "\tlda (zp),y", + "\tsta if_val", + "\tlda test_i", + "\tcmp #$06", + "\tbne _I1", + "\tlda (zp),y", + "\tsta other", + "\tjmp _I2", + "_I1", + "\tldy #1", + "\tlda (zp),y", + "\tsta other", + "_I2", + "\tldy if_val", + "\tlda (zp),y", + "\trts", + } + + cfg := &Config{ + EnableRegisterVars: true, + RegisterVars: map[string]bool{ + "if_val": true, + }, + } + + output, dissolved := Optimize(input, cfg) + + if dissolved["if_val"] { + t.Error("if_val should NOT be dissolved — ldy if_val reads its value") + } + + joined := strings.Join(output, "\n") + if !strings.Contains(joined, "sta if_val") { + t.Errorf("expected sta if_val to be kept (jmp in THEN should not kill it), got:\n%s", joined) + } +} diff --git a/internal/optimizer/pass_regdead.go b/internal/optimizer/pass_regdead.go index 1c0d423..683c31d 100644 --- a/internal/optimizer/pass_regdead.go +++ b/internal/optimizer/pass_regdead.go @@ -60,7 +60,7 @@ func isRegStoreDead(lines []asmLine, start int, operand string) bool { } if l.isCode && blocksFlow(l) { - return true // rts/jmp/brk/rti — execution ends here + return true // rts/brk/rti — execution ends here } if l.isCode && readsFrom(l.opcode, l.operand, operand) { @@ -93,7 +93,7 @@ func readsFrom(opcode, operand, varName string) bool { // execution path: rts, jmp, brk, rti func blocksFlow(line asmLine) bool { switch line.opcode { - case "rts", "jmp", "brk", "rti": + case "rts", "brk", "rti": return true } return false