xref: /wasmtime-44.0.1/cranelift/src/compile.rs (revision 90ac295e)
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