diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 16072aa..b99cfe4 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -166,6 +166,41 @@ impl IrProgram { .map(|b| b.ops.len()) .sum() } + + /// Human-readable pretty-print of the entire program — used by + /// the `--dump-ir` CLI flag and by debugging sessions. + pub fn pretty(&self) -> String { + use std::fmt::Write; + let mut out = String::new(); + out.push_str("# IR Program\n"); + let _ = writeln!(out, "# start_state = {}", self.start_state); + let _ = writeln!(out, "# states = {:?}", self.states); + if !self.globals.is_empty() { + out.push_str("\n# Globals\n"); + for g in &self.globals { + let _ = writeln!( + out, + " {} {} (size={}) = {:?}", + g.var_id, g.name, g.size, g.init_value + ); + } + } + for func in &self.functions { + let _ = writeln!( + out, + "\nfn {}({} params, has_return={}):", + func.name, func.param_count, func.has_return + ); + for block in &func.blocks { + let _ = writeln!(out, " {}:", block.label); + for op in &block.ops { + let _ = writeln!(out, " {op:?}"); + } + let _ = writeln!(out, " -> {:?}", block.terminator); + } + } + out + } } impl IrFunction { diff --git a/src/main.rs b/src/main.rs index fd3049e..35856e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,6 +29,10 @@ enum Cli { #[arg(long)] asm_dump: bool, + /// Dump the lowered IR program to stdout (after optimization) + #[arg(long)] + dump_ir: bool, + /// Use the legacy AST-based codegen. The default is the IR-based /// codegen, which runs the optimizer passes before emitting 6502. #[arg(long)] @@ -50,10 +54,19 @@ fn main() { output, debug, asm_dump, + dump_ir, use_ast, } => { let output = output.unwrap_or_else(|| input.with_extension("nes")); - match compile(&input, debug, asm_dump, use_ast) { + match compile( + &input, + &CompileOptions { + debug, + asm_dump, + dump_ir, + use_ast, + }, + ) { Ok(rom) => { std::fs::write(&output, rom).unwrap_or_else(|e| { eprintln!("error: failed to write {}: {e}", output.display()); @@ -86,7 +99,19 @@ fn dump_asm(instructions: &[nescript::asm::Instruction]) { } } -fn compile(input: &PathBuf, debug: bool, asm_dump: bool, use_ast: bool) -> Result, ()> { +#[allow(clippy::struct_excessive_bools)] +struct CompileOptions { + debug: bool, + asm_dump: bool, + dump_ir: bool, + use_ast: bool, +} + +fn compile(input: &PathBuf, opts: &CompileOptions) -> Result, ()> { + let debug = opts.debug; + let asm_dump = opts.asm_dump; + let dump_ir = opts.dump_ir; + let use_ast = opts.use_ast; let raw_source = std::fs::read_to_string(input).map_err(|e| { eprintln!("error: failed to read {}: {e}", input.display()); })?; @@ -128,6 +153,10 @@ fn compile(input: &PathBuf, debug: bool, asm_dump: bool, use_ast: bool) -> Resul let mut ir_program = ir::lower(&program, &analysis); optimizer::optimize(&mut ir_program); + if dump_ir { + print!("{}", ir_program.pretty()); + } + // Resolve sprite assets (CHR data + tile indices) relative to the // source file's directory, so `@binary` / `@chr` paths work naturally. let source_dir = input.parent().unwrap_or_else(|| Path::new("."));