Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/src/run.rs
1690 views
1
//! CLI tool to compile Cranelift IR files to native code in memory and execute them.
2
3
use crate::utils::{iterate_files, read_to_string};
4
use anyhow::Result;
5
use clap::Parser;
6
use cranelift_codegen::isa::{CallConv, OwnedTargetIsa};
7
use cranelift_filetests::TestFileCompiler;
8
use cranelift_native::builder as host_isa_builder;
9
use cranelift_reader::{Details, IsaSpec, ParseOptions, parse_run_command, parse_test};
10
use std::path::{Path, PathBuf};
11
use target_lexicon::{HOST, Triple};
12
13
/// Execute clif code and verify with test expressions
14
#[derive(Parser)]
15
pub struct Options {
16
/// Specify an input file to be used. Use '-' for stdin.
17
#[arg(required = true)]
18
files: Vec<PathBuf>,
19
20
/// Be more verbose
21
#[arg(short, long)]
22
verbose: bool,
23
}
24
25
pub fn run(options: &Options) -> Result<()> {
26
let stdin_exist = options
27
.files
28
.iter()
29
.find(|file| *file == Path::new("-"))
30
.is_some();
31
let filtered_files = options
32
.files
33
.iter()
34
.cloned()
35
.filter(|file| *file != Path::new("-"))
36
.collect::<Vec<_>>();
37
let mut total = 0;
38
let mut errors = 0;
39
let mut special_files: Vec<PathBuf> = vec![];
40
if stdin_exist {
41
special_files.push("-".into());
42
}
43
for file in iterate_files(&filtered_files).chain(special_files) {
44
total += 1;
45
match run_single_file(&file) {
46
Ok(_) => {
47
if options.verbose {
48
println!("{}", file.to_string_lossy());
49
}
50
}
51
Err(e) => {
52
if options.verbose {
53
println!("{}: {}", file.to_string_lossy(), e);
54
}
55
errors += 1;
56
}
57
}
58
}
59
60
if options.verbose {
61
match total {
62
0 => println!("0 files"),
63
1 => println!("1 file"),
64
n => println!("{n} files"),
65
}
66
}
67
68
match errors {
69
0 => Ok(()),
70
1 => anyhow::bail!("1 failure"),
71
n => anyhow::bail!("{} failures", n),
72
}
73
}
74
75
/// Run all functions in a file that are succeeded by "run:" comments
76
fn run_single_file(path: &PathBuf) -> Result<()> {
77
let file_contents = read_to_string(&path)?;
78
run_file_contents(file_contents)
79
}
80
81
/// Main body of `run_single_file` separated for testing
82
fn run_file_contents(file_contents: String) -> Result<()> {
83
let options = ParseOptions {
84
default_calling_convention: CallConv::triple_default(&Triple::host()), // use the host's default calling convention
85
..ParseOptions::default()
86
};
87
let test_file = parse_test(&file_contents, options)?;
88
let isa = create_target_isa(&test_file.isa_spec)?;
89
let mut tfc = TestFileCompiler::new(isa);
90
tfc.add_testfile(&test_file)?;
91
let compiled = tfc.compile()?;
92
93
for (func, Details { comments, .. }) in test_file.functions {
94
for comment in comments {
95
if let Some(command) = parse_run_command(comment.text, &func.signature)? {
96
let trampoline = compiled.get_trampoline(&func).unwrap();
97
98
command
99
.run(|_, args| Ok(trampoline.call(&compiled, args)))
100
.map_err(|s| anyhow::anyhow!("{}", s))?;
101
}
102
}
103
}
104
Ok(())
105
}
106
107
/// Build an ISA based on the current machine running this code (the host)
108
fn create_target_isa(isa_spec: &IsaSpec) -> Result<OwnedTargetIsa> {
109
let builder = host_isa_builder().map_err(|s| anyhow::anyhow!("{}", s))?;
110
match *isa_spec {
111
IsaSpec::None(ref flags) => {
112
// build an ISA for the current machine
113
Ok(builder.finish(flags.clone())?)
114
}
115
IsaSpec::Some(ref isas) => {
116
for isa in isas {
117
if isa.triple().architecture == HOST.architecture {
118
return Ok(builder.finish(isa.flags().clone())?);
119
}
120
}
121
anyhow::bail!(
122
"The target ISA specified in the file is not compatible with the host ISA"
123
)
124
}
125
}
126
}
127
128
#[cfg(test)]
129
mod test {
130
use super::*;
131
132
#[test]
133
fn nop() {
134
if cranelift_native::builder().is_err() {
135
return;
136
}
137
let code = String::from(
138
"
139
function %test() -> i8 {
140
block0:
141
nop
142
v1 = iconst.i8 -1
143
return v1
144
}
145
; run
146
",
147
);
148
run_file_contents(code).unwrap()
149
}
150
}
151
152