mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 17:06:04 +00:00
Implement IR-based code generator (--use-ir)
New src/codegen/ir_codegen.rs walks IrProgram and emits 6502 instructions. This enables optimizer passes to actually affect the output ROM. Design: - Each IR temp gets a zero-page slot at $80 + temp_index - Functions reset the temp counter at entry (temps don't outlive functions) - Globals map by name to their analyzer-assigned zero-page addresses - Operands are loaded into A, computed, stored back to the dest slot Handles all IrOp variants: - LoadImm, LoadVar, StoreVar (basic loads/stores) - Add/Sub (CLC+ADC / SEC+SBC) - Mul (JSR __multiply runtime routine) - And/Or/Xor (zero-page operands) - ShiftLeft/ShiftRight (repeated ASL/LSR) - Negate/Complement (EOR #$FF + optional two's complement) - CmpEq/Ne/Lt/Gt/LtEq/GtEq (CMP + conditional branch + 0/1) - ArrayLoad/ArrayStore (TAX + ZeroPageX/AbsoluteX) - Call (ZP param passing + JSR) - DrawSprite (OAM slot 0 write, uses sprite_tiles map) - ReadInput (LDA $01, P1 input) - WaitFrame (poll frame flag at $00) All terminators: - Jump (JMP to block label) - Branch (LDA temp + BNE true / JMP false) - Return (optional value in A + RTS) - Unreachable (BRK) IR lowering fixes: - ReadInput now has a destination IrTemp (was a side-effect-only op) - ButtonRead uses the proper input temp instead of uninitialized register - Logical AND/OR use new emit_move helper (OR with zero) instead of bogus raw VarId for path merging CLI: - New --use-ir flag on `build` subcommand to opt in to IR codegen - Default remains AST codegen (for now); IR codegen is experimental All 7 examples compile through the IR pipeline and produce valid iNES ROMs. Tests: 266 total (7 new ir_codegen unit + 2 new integration). https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
parent
5512567349
commit
1ede169f1e
8 changed files with 650 additions and 17 deletions
|
|
@ -482,3 +482,69 @@ fn program_with_mmc1() {
|
|||
let info = rom::validate_ines(&rom_data).expect("should be valid iNES");
|
||||
assert_eq!(info.mapper, 1, "should be MMC1 (mapper 1)");
|
||||
}
|
||||
|
||||
// ── IR Codegen Tests ──
|
||||
|
||||
/// Compile a program using the IR-based codegen path instead of the
|
||||
/// AST-based codegen. Validates the full IR pipeline produces a valid ROM.
|
||||
fn compile_with_ir_codegen(source: &str) -> Vec<u8> {
|
||||
use nescript::codegen::IrCodeGen;
|
||||
|
||||
let (program, diags) = nescript::parser::parse(source);
|
||||
assert!(
|
||||
diags.is_empty(),
|
||||
"unexpected parse errors: {diags:?}\nsource:\n{source}"
|
||||
);
|
||||
let program = program.expect("parse should succeed");
|
||||
|
||||
let analysis = analyzer::analyze(&program);
|
||||
assert!(
|
||||
analysis.diagnostics.iter().all(|d| !d.is_error()),
|
||||
"unexpected analysis errors: {:?}",
|
||||
analysis.diagnostics
|
||||
);
|
||||
|
||||
// Lower to IR and run the optimizer
|
||||
let mut ir_program = ir::lower(&program, &analysis);
|
||||
optimizer::optimize(&mut ir_program);
|
||||
|
||||
// IR-based codegen
|
||||
let codegen = IrCodeGen::new(&analysis.var_allocations, &ir_program);
|
||||
let instructions = codegen.generate(&ir_program);
|
||||
|
||||
// Link into a ROM
|
||||
let linker = Linker::new(program.game.mirroring);
|
||||
linker.link(&instructions)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ir_codegen_minimal_rom() {
|
||||
let source = r#"
|
||||
game "IR Test" { mapper: NROM }
|
||||
var x: u8 = 42
|
||||
on frame { wait_frame }
|
||||
start Main
|
||||
"#;
|
||||
let rom_data = compile_with_ir_codegen(source);
|
||||
let info = rom::validate_ines(&rom_data).expect("should be valid iNES");
|
||||
assert_eq!(info.mapper, 0);
|
||||
assert_eq!(rom_data.len(), 16 + 16384 + 8192);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ir_codegen_full_pipeline() {
|
||||
let source = r#"
|
||||
game "IR Full" { mapper: NROM }
|
||||
var x: u8 = 0
|
||||
var y: u8 = 0
|
||||
on frame {
|
||||
if button.right { x += 1 }
|
||||
if button.left { x -= 1 }
|
||||
if x > 100 { x = 0 }
|
||||
draw Smiley at: (x, y)
|
||||
}
|
||||
start Main
|
||||
"#;
|
||||
let rom_data = compile_with_ir_codegen(source);
|
||||
rom::validate_ines(&rom_data).expect("should be valid iNES");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue