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

tooling: add --no-opt CLI flag and criterion compile benchmarks

Adds two items from the "Code quality / tooling" section of
docs/future-work.md. Both make it easier to chase regressions
without touching codegen.

- `nescript build --no-opt` skips the IR optimizer pass so
  optimizer-introduced miscompiles can be bisected against the
  unoptimized output. Threaded through CompileOptions and gated
  at the single optimizer call site in src/main.rs. Covered by a
  new integration test that compiles the same program twice
  (opt on / opt off) and asserts both outputs are valid iNES
  ROMs with matching headers and reset vectors.

- A criterion-based `benches/compile.rs` harness that times the
  full parse -> analyze -> lower -> optimize -> codegen -> link
  pipeline on every examples/*.ne file. Sources are pre-read
  into memory so file I/O stays off the hot loop, and each
  example gets its own Criterion group for easy regression
  spotting.

Committed ROM bytes under examples/*.nes are unchanged; the
emulator goldens under tests/emulator/goldens/ are untouched.
This commit is contained in:
Claude 2026-04-14 01:43:51 +00:00
parent d2cfce595b
commit 65a63f9a68
No known key found for this signature in database
5 changed files with 740 additions and 2 deletions

View file

@ -42,6 +42,13 @@ enum Cli {
/// Dump a call graph showing which functions call which.
#[arg(long)]
call_graph: bool,
/// Skip the IR optimizer pass. Useful for bisecting
/// optimizer-introduced miscompiles: if a program misbehaves
/// with the optimizer on but works with `--no-opt`, the bug
/// lives in `src/optimizer/`.
#[arg(long)]
no_opt: bool,
},
/// Type-check a source file without building
Check {
@ -62,6 +69,7 @@ fn main() {
dump_ir,
memory_map,
call_graph,
no_opt,
} => {
let output = output.unwrap_or_else(|| input.with_extension("nes"));
match compile(
@ -72,6 +80,7 @@ fn main() {
dump_ir,
memory_map,
call_graph,
no_opt,
},
) {
Ok(rom) => {
@ -220,6 +229,7 @@ struct CompileOptions {
dump_ir: bool,
memory_map: bool,
call_graph: bool,
no_opt: bool,
}
fn compile(input: &PathBuf, opts: &CompileOptions) -> Result<Vec<u8>, ()> {
@ -228,6 +238,7 @@ fn compile(input: &PathBuf, opts: &CompileOptions) -> Result<Vec<u8>, ()> {
let dump_ir = opts.dump_ir;
let memory_map = opts.memory_map;
let call_graph = opts.call_graph;
let no_opt = opts.no_opt;
let raw_source = std::fs::read_to_string(input).map_err(|e| {
eprintln!("error: failed to read {}: {e}", input.display());
})?;
@ -265,9 +276,13 @@ fn compile(input: &PathBuf, opts: &CompileOptions) -> Result<Vec<u8>, ()> {
return Err(());
}
// IR lowering and optimization
// IR lowering and (optionally) optimization. `--no-opt` skips
// the IR optimizer pass entirely so optimizer-introduced
// miscompiles can be bisected against the unoptimized output.
let mut ir_program = ir::lower(&program, &analysis);
optimizer::optimize(&mut ir_program);
if !no_opt {
optimizer::optimize(&mut ir_program);
}
if dump_ir {
print!("{}", ir_program.pretty());