1use crate::disasm::print_all;
4use crate::utils::read_to_string;
5use anyhow::{Context as _, Result};
6use clap::Parser;
7use cranelift_codegen::print_errors::pretty_error;
8use cranelift_codegen::settings::FlagsOrIsa;
9use cranelift_codegen::timing;
10use cranelift_codegen::Context;
11use cranelift_reader::OwnedFlagsOrIsa;
12use cranelift_reader::{parse_sets_and_triple, parse_test, ParseOptions};
13use std::path::Path;
14use std::path::PathBuf;
15
16#[derive(Parser)]
18pub struct Options {
19 #[arg(short)]
21 print: bool,
22
23 #[arg(short = 'T')]
25 report_times: bool,
26
27 #[arg(short = 'D', long)]
29 disasm: bool,
30
31 #[arg(long = "set")]
33 settings: Vec<String>,
34
35 #[arg(long = "target")]
37 target: String,
38
39 files: Vec<PathBuf>,
41
42 #[arg(short = 'o', long = "output")]
44 output: Option<PathBuf>,
45}
46
47pub fn run(options: &Options) -> Result<()> {
48 let parsed = parse_sets_and_triple(&options.settings, &options.target)?;
49
50 let mut module = match (&options.output, &parsed) {
51 (Some(output), OwnedFlagsOrIsa::Isa(isa)) => {
52 let builder = cranelift_object::ObjectBuilder::new(
53 isa.clone(),
54 output
55 .file_name()
56 .and_then(|s| s.to_str())
57 .unwrap_or("a.out"),
58 cranelift_module::default_libcall_names(),
59 )?;
60 Some(cranelift_object::ObjectModule::new(builder))
61 }
62 _ => None,
63 };
64
65 for path in &options.files {
66 let name = String::from(path.as_os_str().to_string_lossy());
67 handle_module(options, path, &name, parsed.as_fisa(), module.as_mut())?;
68 }
69
70 if let (Some(module), Some(output)) = (module, &options.output) {
71 let bytes = module.finish().emit()?;
72 std::fs::write(output, bytes)?;
73 }
74
75 Ok(())
76}
77
78fn handle_module(
79 options: &Options,
80 path: &Path,
81 name: &str,
82 fisa: FlagsOrIsa,
83 module: Option<&mut impl cranelift_module::Module>,
84) -> Result<()> {
85 let buffer = read_to_string(&path)?;
86 let test_file = parse_test(&buffer, ParseOptions::default())
87 .with_context(|| format!("failed to parse {name}"))?;
88
89 let isa = fisa.isa.or(test_file.isa_spec.unique_isa());
92
93 let isa = match isa {
94 None => anyhow::bail!("compilation requires a target isa"),
95 Some(isa) => isa,
96 };
97
98 for (func, _) in test_file.functions {
99 let mut context = Context::new();
100 context.func = func;
101
102 let compiled_code = context
104 .compile(isa, &mut Default::default())
105 .map_err(|err| anyhow::anyhow!("{}", pretty_error(&err.func, err.inner)))?;
106 let code_info = compiled_code.code_info();
107
108 if let Some(&mut ref mut module) = module {
109 let name = context.func.name.to_string();
110 let fid = module.declare_function(
111 &name,
112 cranelift_module::Linkage::Export,
113 &context.func.signature,
114 )?;
115 module.define_function_with_control_plane(
116 fid,
117 &mut context,
118 &mut Default::default(),
119 )?;
120 }
121
122 if options.print {
123 println!("{}", context.func.display());
124 }
125
126 if options.disasm {
127 let result = context.compiled_code().unwrap();
128 print_all(
129 isa,
130 &context.func,
131 context.compiled_code().unwrap().code_buffer(),
132 code_info.total_size,
133 options.print,
134 result.buffer.relocs(),
135 result.buffer.traps(),
136 )?;
137 }
138 }
139
140 if options.report_times {
141 print!("{}", timing::take_current());
142 }
143
144 Ok(())
145}