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

Codegen: extend A-tracking and copy-propagation to absolute addresses

Both peephole passes now track \`Absolute\` addresses in addition to
zero-page. This lets function-local variables (which live in RAM at
\$0300+) benefit from the same redundant-load elimination and
operand-rewriting that globals at ZP addresses already enjoyed.

Before:
    LDA \$04         ; load param
    STA \$0300       ; store to t
    LDA \$0300       ; reload t
    STA \$81         ; store to temp
    LDA \$0300       ; reload t again
    STA \$82         ; store to temp
    LDA \$81
    CLC
    ADC \$82
    STA \$0300
    LDA \$0300
    RTS

After:
    LDA \$04
    STA \$0300
    CLC
    ADC \$0300
    STA \$0300
    RTS

Renames the A-tracking enum's \`Addr\` variant to \`Zp\` alongside a
new \`Abs\` variant, matching the \`Source\` type used by copy prop.

https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
Claude 2026-04-12 17:04:59 +00:00
parent 34b312537d
commit 2e5933ccca
No known key found for this signature in database

View file

@ -253,27 +253,20 @@ fn copy_propagate_temps(instructions: &mut [Instruction]) {
a = Some(Source::Imm(v));
}
(Opcode::LDA, AddressingMode::ZeroPage(addr)) => {
// If addr is a temp whose source is tracked and the
// source still holds the intended value, rewrite the
// load to use the source directly.
// If addr is a temp whose source is tracked, rewrite
// the load to use the source directly.
if is_temp_slot_addr(addr) {
if let Some(src) = temp_source.get(&addr).copied() {
match src {
Source::Imm(v) => {
inst.mode = AddressingMode::Immediate(v);
a = Some(Source::Imm(v));
continue;
}
Source::Zp(orig) => {
inst.mode = AddressingMode::ZeroPage(orig);
a = Some(Source::Zp(orig));
continue;
}
}
inst.mode = source_to_mode(src);
a = Some(src);
continue;
}
}
a = Some(Source::Zp(addr));
}
(Opcode::LDA, AddressingMode::Absolute(addr)) => {
a = Some(Source::Abs(addr));
}
// Arithmetic / logical ops that read a temp slot: rewrite
// the operand to the temp's tracked source. Clobbers A.
(
@ -281,10 +274,7 @@ fn copy_propagate_temps(instructions: &mut [Instruction]) {
AddressingMode::ZeroPage(addr),
) if is_temp_slot_addr(addr) => {
if let Some(src) = temp_source.get(&addr).copied() {
inst.mode = match src {
Source::Imm(v) => AddressingMode::Immediate(v),
Source::Zp(orig) => AddressingMode::ZeroPage(orig),
};
inst.mode = source_to_mode(src);
}
a = None;
}
@ -303,6 +293,10 @@ fn copy_propagate_temps(instructions: &mut [Instruction]) {
temp_source.retain(|_, src| !matches!(*src, Source::Zp(a) if a == addr));
}
}
(Opcode::STA, AddressingMode::Absolute(addr)) => {
// Invalidate any temp tracking this absolute source.
temp_source.retain(|_, src| !matches!(*src, Source::Abs(a) if a == addr));
}
// Any op that reads a non-ZP location or clobbers A: just
// invalidate A; temps are unaffected.
_ => {
@ -314,10 +308,19 @@ fn copy_propagate_temps(instructions: &mut [Instruction]) {
}
}
fn source_to_mode(src: Source) -> AddressingMode {
match src {
Source::Imm(v) => AddressingMode::Immediate(v),
Source::Zp(addr) => AddressingMode::ZeroPage(addr),
Source::Abs(addr) => AddressingMode::Absolute(addr),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Source {
Imm(u8),
Zp(u8),
Abs(u16),
}
fn is_temp_slot_addr(addr: u8) -> bool {
@ -376,29 +379,34 @@ fn remove_redundant_loads(instructions: &mut Vec<Instruction>) {
a = Imm(*v);
}
(Opcode::LDA, AddressingMode::ZeroPage(addr)) => {
if let Addr(existing) = a {
if let Zp(existing) = a {
if existing == *addr {
keep[i] = false;
continue;
}
}
a = Addr(*addr);
a = Zp(*addr);
}
(Opcode::LDA, AddressingMode::Absolute(addr)) => {
// We could track absolute addresses too, but don't
// try to unify them with ZP. Just record the value.
// Not going to eliminate against prior state.
let _ = addr;
a = None;
if let Abs(existing) = a {
if existing == *addr {
keep[i] = false;
continue;
}
}
a = Abs(*addr);
}
(Opcode::STA, AddressingMode::ZeroPage(addr)) => {
// After STA, A is unchanged. Additionally, `addr` now
// holds A's value. Remember that equivalence: a later
// `LDA addr` is redundant.
a = Addr(*addr);
a = Zp(*addr);
}
(Opcode::STA, AddressingMode::Absolute(addr)) => {
a = Abs(*addr);
}
(Opcode::STA, _) => {
// A unchanged, but we don't track non-ZP addresses.
// A unchanged, but we don't track exotic addressing modes.
}
// Ops that clobber A — clear tracker.
(
@ -436,7 +444,8 @@ fn remove_redundant_loads(instructions: &mut Vec<Instruction>) {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AKnown {
None,
Addr(u8),
Zp(u8),
Abs(u16),
Imm(u8),
}