1 //! CLI tool to read Cranelift IR files and compile them into native code.
2
3 use crate::disasm::print_all;
4 use crate::utils::read_to_string;
5 use anyhow::{Context as _, Result};
6 use clap::Parser;
7 use cranelift_codegen::Context;
8 use cranelift_codegen::print_errors::pretty_error;
9 use cranelift_codegen::settings::FlagsOrIsa;
10 use cranelift_codegen::timing;
11 use cranelift_reader::OwnedFlagsOrIsa;
12 use cranelift_reader::{ParseOptions, parse_sets_and_triple, parse_test};
13 use std::path::Path;
14 use std::path::PathBuf;
15
16 /// Compiles Cranelift IR into target language
17 #[derive(Parser)]
18 pub struct Options {
19 /// Print the resulting Cranelift IR
20 #[arg(short)]
21 print: bool,
22
23 /// Print pass timing report
24 #[arg(short = 'T')]
25 report_times: bool,
26
27 /// Print machine code disassembly
28 #[arg(short = 'D', long)]
29 disasm: bool,
30
31 /// Configure Cranelift settings
32 #[arg(long = "set")]
33 settings: Vec<String>,
34
35 /// Specify the Cranelift target
36 #[arg(long = "target")]
37 target: String,
38
39 /// Specify an input file to be used. Use '-' for stdin.
40 files: Vec<PathBuf>,
41
42 /// Output object file
43 #[arg(short = 'o', long = "output")]
44 output: Option<PathBuf>,
45 }
46
run(options: &Options) -> Result<()>47 pub 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
handle_module( options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa, module: Option<&mut impl cranelift_module::Module>, ) -> Result<()>78 fn 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 // If we have an isa from the command-line, use that. Otherwise if the
90 // file contains a unique isa, use that.
91 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 // Compile and encode the result to machine code.
103 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 }
146