use clap::ValueHint;
use solar_codegen::backend::evm::{
EVM_IR_PASSES, EvmIrModule, EvmIrPass, parse_evm_ir_module, verify_evm_ir_module,
};
use solar_interface::{Ident, Session, Symbol};
use std::{path::Path, process::ExitCode};
#[derive(clap::Args)]
#[command(after_help = after_help(), arg_required_else_help = true)]
pub(crate) struct EvmOptArgs {
#[arg(
long = "passes",
visible_alias = "pass",
value_name = "NAMES",
value_delimiter = ',',
value_parser = parse_pass,
default_value = "none"
)]
passes: Vec<EvmIrPass>,
#[arg(long)]
print_after_each: bool,
#[arg(value_hint = ValueHint::FilePath)]
input: String,
}
fn parse_pass(name: &str) -> Result<EvmIrPass, String> {
EvmIrPass::by_name(name).ok_or_else(|| format!("unknown EVM IR pass: {name}"))
}
fn after_help() -> String {
format!(
"Passes:\n {}\n\nInput formats:\n *.evmir EVM IR",
EVM_IR_PASSES.iter().map(|pass| pass.name()).collect::<Vec<_>>().join("\n ")
)
}
fn selected_pass_list_label(passes: &[EvmIrPass], separator: &str) -> String {
passes.iter().map(|pass| pass.name()).collect::<Vec<_>>().join(separator)
}
fn print_module(module: &EvmIrModule, name: &str, after: &str) {
println!("// === {name} (after {after}) ===");
print!("{}", module.to_text());
}
fn run_pipeline(module: &mut EvmIrModule, name: &str, args: &EvmOptArgs) -> Result<(), String> {
if args.print_after_each {
for &pass in &args.passes {
pass.run(module);
verify_evm_ir_module(module)
.map_err(|e| format!("EVM IR pass `{}` produced invalid IR: {e}", pass.name()))?;
print_module(module, name, pass.name());
}
} else {
for &pass in &args.passes {
pass.run(module);
}
verify_evm_ir_module(module)
.map_err(|e| format!("EVM IR pipeline produced invalid IR: {e}"))?;
let label = selected_pass_list_label(&args.passes, ",");
print_module(module, name, &label);
}
Ok(())
}
fn process_evmir(args: &EvmOptArgs) -> Result<(), String> {
let sess = Session::builder().with_stderr_emitter().build();
let source = sess
.source_map()
.load_file(Path::new(&args.input))
.map_err(|e| format!("failed to read {}: {e}", args.input))?;
let text = source.src.as_str();
let mut result: Result<(), String> = Ok(());
sess.enter(|| {
let mut module = match parse_evm_ir_module(text) {
Ok(m) => m,
Err(e) => {
result = Err(format!("{e}"));
return;
}
};
if let Err(e) = verify_evm_ir_module(&module) {
result = Err(format!("{e}"));
return;
}
let name = Ident::with_dummy_span(Symbol::intern(&args.input)).to_string();
result = run_pipeline(&mut module, &name, args);
});
result
}
pub(crate) fn run(args: EvmOptArgs) -> ExitCode {
let ext = Path::new(&args.input).extension().and_then(|s| s.to_str()).unwrap_or("");
let result = match ext {
"evmir" => process_evmir(&args),
_ => Err(format!("unsupported input file extension `.{ext}` (expected .evmir)")),
};
match result {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("error: {e}");
ExitCode::FAILURE
}
}
}