-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
compile.rs
147 lines (124 loc) · 4.28 KB
/
compile.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! CLI tool to read Cranelift IR files and compile them into native code.
use crate::disasm::print_all;
use crate::utils::read_to_string;
use anyhow::{Context as _, Result};
use clap::Parser;
use cranelift_codegen::print_errors::pretty_error;
use cranelift_codegen::settings::FlagsOrIsa;
use cranelift_codegen::timing;
use cranelift_codegen::Context;
use cranelift_reader::OwnedFlagsOrIsa;
use cranelift_reader::{parse_sets_and_triple, parse_test, ParseOptions};
use std::path::Path;
use std::path::PathBuf;
/// Compiles Cranelift IR into target language
#[derive(Parser)]
pub struct Options {
/// Print the resulting Cranelift IR
#[arg(short)]
print: bool,
/// Print pass timing report
#[arg(short = 'T')]
report_times: bool,
/// Print machine code disassembly
#[arg(short = 'D', long)]
disasm: bool,
/// Configure Cranelift settings
#[arg(long = "set")]
settings: Vec<String>,
/// Specify the Cranelift target
#[arg(long = "target")]
target: String,
/// Specify an input file to be used. Use '-' for stdin.
files: Vec<PathBuf>,
/// Output object file
#[arg(short = 'o', long = "output")]
output: Option<PathBuf>,
}
pub fn run(options: &Options) -> Result<()> {
let parsed = parse_sets_and_triple(&options.settings, &options.target)?;
let mut module = match (&options.output, &parsed) {
(Some(output), OwnedFlagsOrIsa::Isa(isa)) => {
let builder = cranelift_object::ObjectBuilder::new(
isa.clone(),
output
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("a.out"),
cranelift_module::default_libcall_names(),
)?;
Some(cranelift_object::ObjectModule::new(builder))
}
_ => None,
};
for path in &options.files {
let name = String::from(path.as_os_str().to_string_lossy());
handle_module(options, path, &name, parsed.as_fisa(), module.as_mut())?;
}
if let (Some(module), Some(output)) = (module, &options.output) {
let bytes = module.finish().emit()?;
std::fs::write(output, bytes)?;
}
Ok(())
}
fn handle_module(
options: &Options,
path: &Path,
name: &str,
fisa: FlagsOrIsa,
module: Option<&mut impl cranelift_module::Module>,
) -> Result<()> {
let buffer = read_to_string(&path)?;
let test_file = parse_test(&buffer, ParseOptions::default())
.with_context(|| format!("failed to parse {}", name))?;
// If we have an isa from the command-line, use that. Otherwise if the
// file contains a unique isa, use that.
let isa = fisa.isa.or(test_file.isa_spec.unique_isa());
let isa = match isa {
None => anyhow::bail!("compilation requires a target isa"),
Some(isa) => isa,
};
for (func, _) in test_file.functions {
let mut context = Context::new();
context.func = func;
let mut mem = vec![];
// Compile and encode the result to machine code.
let compiled_code = context
.compile_and_emit(isa, &mut mem, &mut Default::default())
.map_err(|err| anyhow::anyhow!("{}", pretty_error(&err.func, err.inner)))?;
let code_info = compiled_code.code_info();
if let Some(&mut ref mut module) = module {
let name = context.func.name.to_string();
let fid = module.declare_function(
&name,
cranelift_module::Linkage::Export,
&context.func.signature,
)?;
module.define_function_with_control_plane(
fid,
&mut context,
&mut Default::default(),
)?;
}
if options.print {
println!("{}", context.func.display());
}
if options.disasm {
let result = context.compiled_code().unwrap();
print_all(
isa,
&context.func.params,
&mem,
code_info.total_size,
options.print,
result.buffer.relocs(),
result.buffer.traps(),
result.buffer.stack_maps(),
)?;
}
}
if options.report_times {
print!("{}", timing::take_current());
}
Ok(())
}