makepad/libs/wasm_strip/examples/wasm_opt.rs
Admin 6a09a2fc8d wasm_strip: panics become traps, with a symbol file to read them back -- OptimizeOptions.panic_trap (off by default): every call LLVM follows with unreachable (a function that never returns: panic, panic_fmt, failed unwrap/expect, bounds and slice checks, allocation failure) becomes a plain unreachable, and the instructions computing its arguments go with it when nothing they do survives a trap (stack values, locals, loads, arithmetic); the panic functions, their formatting and messages are then dead for the passes after. A panic traps without printing. OptimizeOptions.symbols puts the symbol file in OptimizeReport.symbols: F <index> <name> for every function of the output and, with panic_trap, P <code offset> <index> <callee> | <message> | <file:line:col> per trap, the offset being the one a browser's wasm stack frame shows; the message and location come from the constant arguments of the site (a &str, a format string's first piece, a core::panic::Location). The sites carry a marker constant through the passes, taken out last; functions equal once it is out are merged then (merge::run_mapped says where each went), so the symbol file costs 1.2 KB of the 19.96 MB output. Names now go at the very end when only the symbol file needs them (pass reports leave them out). stitch: binary::body_offsets gives each instruction's offset in a function body entry. wasm_opt takes --panic-trap and --symbols <file>.
film.wasm (Stage player): code section 2.142 -> 2.111 MB brotli (-30.7 KB), 20.12 -> 19.96 MB raw, 12,665 sites, 9,531 with a location and 2,539 with a message; V8's WebAssembly.validate accepts it.

Checked: cargo test --release -p makepad-wasm-strip (42 unit incl. panics_become_traps_with_their_sites: the panic function and the import only it used go, the symbol line names message and location at the offset of the trap's unreachable, the function still answers in range and traps out of range under stitch; spec 2; fuzz 5), the same in a clean worktree at HEAD with only these files.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 12:53:29 +02:00

44 lines
1.7 KiB
Rust

//! Runs the optimiser on a wasm file:
//! `wasm_opt <in.wasm> [out.wasm] [--keep-names] [--panic-trap]
//! [--symbols <file>] [--passes strip,dce,...]`.
use makepad_wasm_strip::*;
fn main() {
let args: Vec<String> = std::env::args().collect();
let input = std::fs::read(&args[1]).expect("read input");
let mut opts = OptimizeOptions {
keep_names: args.iter().any(|arg| arg == "--keep-names"),
panic_trap: args.iter().any(|arg| arg == "--panic-trap"),
..OptimizeOptions::default()
};
let symbols = args.iter().position(|arg| arg == "--symbols").map(|at| args[at + 1].clone());
opts.symbols = symbols.is_some();
if let Some(at) = args.iter().position(|arg| arg == "--passes") {
let passes: Vec<&str> = args[at + 1].split(',').collect();
let on = |name: &str| passes.contains(&name);
opts.strip = on("strip");
opts.dce = on("dce");
opts.peephole = on("peephole");
opts.locals = on("locals");
opts.merge = on("merge");
opts.compact = on("compact");
opts.order = on("order");
}
let start = std::time::Instant::now();
match wasm_optimize_checked(&input, &opts) {
Ok((output, report)) => {
print!("{}", report.to_text());
println!("took {:?}", start.elapsed());
if let Some(path) = args.get(2).filter(|arg| !arg.starts_with("--")) {
std::fs::write(path, output).expect("write output");
}
if let (Some(path), Some(text)) = (&symbols, &report.symbols) {
std::fs::write(path, text).expect("write symbols");
}
}
Err(msg) => {
eprintln!("error: {msg}");
std::process::exit(1);
}
}
}