mirror of
https://github.com/imjasonh/nescript
synced 2026-07-20 04:48:28 +00:00
CLI: --dump-ir flag + IrProgram::pretty
Adds \`IrProgram::pretty()\` for a readable IR dump and wires it to a new \`--dump-ir\` build flag. Useful for debugging IR-level issues (optimizer, lowering). Refactored \`compile()\` to take a \`CompileOptions\` struct to keep clippy's \`fn-params-excessive-bools\` lint happy. https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
parent
8b35ba9e13
commit
ae051f5909
2 changed files with 66 additions and 2 deletions
|
|
@ -166,6 +166,41 @@ impl IrProgram {
|
||||||
.map(|b| b.ops.len())
|
.map(|b| b.ops.len())
|
||||||
.sum()
|
.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 {
|
impl IrFunction {
|
||||||
|
|
|
||||||
33
src/main.rs
33
src/main.rs
|
|
@ -29,6 +29,10 @@ enum Cli {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
asm_dump: bool,
|
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
|
/// Use the legacy AST-based codegen. The default is the IR-based
|
||||||
/// codegen, which runs the optimizer passes before emitting 6502.
|
/// codegen, which runs the optimizer passes before emitting 6502.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
|
@ -50,10 +54,19 @@ fn main() {
|
||||||
output,
|
output,
|
||||||
debug,
|
debug,
|
||||||
asm_dump,
|
asm_dump,
|
||||||
|
dump_ir,
|
||||||
use_ast,
|
use_ast,
|
||||||
} => {
|
} => {
|
||||||
let output = output.unwrap_or_else(|| input.with_extension("nes"));
|
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) => {
|
Ok(rom) => {
|
||||||
std::fs::write(&output, rom).unwrap_or_else(|e| {
|
std::fs::write(&output, rom).unwrap_or_else(|e| {
|
||||||
eprintln!("error: failed to write {}: {e}", output.display());
|
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<Vec<u8>, ()> {
|
#[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<Vec<u8>, ()> {
|
||||||
|
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| {
|
let raw_source = std::fs::read_to_string(input).map_err(|e| {
|
||||||
eprintln!("error: failed to read {}: {e}", input.display());
|
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);
|
let mut ir_program = ir::lower(&program, &analysis);
|
||||||
optimizer::optimize(&mut ir_program);
|
optimizer::optimize(&mut ir_program);
|
||||||
|
|
||||||
|
if dump_ir {
|
||||||
|
print!("{}", ir_program.pretty());
|
||||||
|
}
|
||||||
|
|
||||||
// Resolve sprite assets (CHR data + tile indices) relative to the
|
// Resolve sprite assets (CHR data + tile indices) relative to the
|
||||||
// source file's directory, so `@binary` / `@chr` paths work naturally.
|
// source file's directory, so `@binary` / `@chr` paths work naturally.
|
||||||
let source_dir = input.parent().unwrap_or_else(|| Path::new("."));
|
let source_dir = input.parent().unwrap_or_else(|| Path::new("."));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue