cranelift_filetests/
test_run.rs1use crate::function_runner::{CompiledTestFile, TestFileCompiler};
6use crate::runone::FileUpdate;
7use crate::subtest::{Context, SubTest};
8use anyhow::Context as _;
9use cranelift_codegen::data_value::DataValue;
10use cranelift_codegen::ir::Type;
11use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa};
12use cranelift_codegen::settings::{Configurable, Flags};
13use cranelift_codegen::{ir, settings};
14use cranelift_reader::TestCommand;
15use cranelift_reader::{parse_run_command, TestFile};
16use log::{info, trace};
17use std::borrow::Cow;
18use target_lexicon::Architecture;
19
20struct TestRun;
21
22pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
23 assert_eq!(parsed.command, "run");
24 if !parsed.options.is_empty() {
25 anyhow::bail!("No options allowed on {}", parsed);
26 }
27 Ok(Box::new(TestRun))
28}
29
30fn build_host_isa(
34 infer_native_flags: bool,
35 flags: settings::Flags,
36 isa_flags: Vec<settings::Value>,
37) -> anyhow::Result<OwnedTargetIsa> {
38 let mut builder = cranelift_native::builder_with_options(infer_native_flags)
39 .map_err(|e| anyhow::Error::msg(e))?;
40
41 for value in isa_flags {
43 builder.set(value.name, &value.value_string())?;
44 }
45
46 let isa = builder.finish(flags)?;
47 Ok(isa)
48}
49
50fn is_isa_compatible(
52 file_path: &str,
53 host: Option<&dyn TargetIsa>,
54 requested: &dyn TargetIsa,
55) -> Result<(), String> {
56 let host_triple = match host {
57 Some(host) => host.triple().clone(),
58 None => target_lexicon::Triple::host(),
59 };
60 let host_arch = host_triple.architecture;
64 let requested_arch = requested.triple().architecture;
65
66 match (host_arch, requested_arch) {
67 (host, requested) if host == requested => {}
69
70 (Architecture::Riscv64(_), Architecture::Riscv64(_)) => {}
72
73 (
76 _,
77 Architecture::Pulley32
78 | Architecture::Pulley64
79 | Architecture::Pulley32be
80 | Architecture::Pulley64be,
81 ) if host_triple.pointer_width() == requested.triple().pointer_width()
82 && host_triple.endianness() == requested.triple().endianness() => {}
83
84 _ => {
85 return Err(format!(
86 "skipped {file_path}: host can't run {requested_arch:?} programs"
87 ))
88 }
89 }
90
91 let requested_flags = requested.isa_flags();
94 for req_value in requested_flags {
95 if req_value.name == "pointer_width" {
97 continue;
98 }
99 let requested = match req_value.as_bool() {
100 Some(requested) => requested,
101 None => unimplemented!("ISA flag {} of kind {:?}", req_value.name, req_value.kind()),
102 };
103 let host_isa_flags = match host {
104 Some(host) => host.isa_flags(),
105 None => {
106 return Err(format!(
107 "host not available on this platform for isa-specific flag"
108 ))
109 }
110 };
111 let available_in_host = host_isa_flags
112 .iter()
113 .find(|val| val.name == req_value.name)
114 .and_then(|val| val.as_bool())
115 .unwrap_or(false);
116
117 if !requested || available_in_host {
118 continue;
119 }
120
121 if req_value.name == "sign_return_address" && matches!(host_arch, Architecture::Aarch64(_))
132 {
133 continue;
134 }
135
136 return Err(format!(
137 "skipped {}: host does not support ISA flag {}",
138 file_path, req_value.name
139 ));
140 }
141
142 Ok(())
143}
144
145fn compile_testfile(
146 testfile: &TestFile,
147 flags: &Flags,
148 isa: &dyn TargetIsa,
149) -> anyhow::Result<CompiledTestFile> {
150 let isa = match isa.triple().architecture {
151 Architecture::Pulley32
154 | Architecture::Pulley64
155 | Architecture::Pulley32be
156 | Architecture::Pulley64be => {
157 let mut builder = cranelift_codegen::isa::lookup(isa.triple().clone())?;
158 for value in isa.isa_flags() {
159 builder.set(value.name, &value.value_string()).unwrap();
160 }
161 builder.finish(flags.clone())?
162 }
163
164 _ => build_host_isa(false, flags.clone(), isa.isa_flags()).unwrap(),
169 };
170
171 let mut tfc = TestFileCompiler::new(isa);
172 tfc.add_testfile(testfile)?;
173 Ok(tfc.compile()?)
174}
175
176fn run_test(
177 testfile: &CompiledTestFile,
178 func: &ir::Function,
179 context: &Context,
180) -> anyhow::Result<()> {
181 for comment in context.details.comments.iter() {
182 if let Some(command) = parse_run_command(comment.text, &func.signature)? {
183 trace!("Parsed run command: {}", command);
184
185 command
186 .run(|_, run_args| {
187 let (_ctx_struct, _vmctx_ptr) =
188 build_vmctx_struct(context.isa.unwrap().pointer_type());
189
190 let mut args = Vec::with_capacity(run_args.len());
191 args.extend_from_slice(run_args);
192
193 let trampoline = testfile.get_trampoline(func).unwrap();
194 Ok(trampoline.call(&args))
195 })
196 .map_err(|s| anyhow::anyhow!("{}", s))?;
197 }
198 }
199 Ok(())
200}
201
202impl SubTest for TestRun {
203 fn name(&self) -> &'static str {
204 "run"
205 }
206
207 fn is_mutating(&self) -> bool {
208 false
209 }
210
211 fn needs_isa(&self) -> bool {
212 true
213 }
214
215 fn run_target<'a>(
218 &self,
219 testfile: &TestFile,
220 file_update: &mut FileUpdate,
221 file_path: &'a str,
222 flags: &'a Flags,
223 isa: Option<&'a dyn TargetIsa>,
224 ) -> anyhow::Result<()> {
225 if flags.enable_pinned_reg() {
228 return Err(anyhow::anyhow!([
229 "Cannot run runtests with pinned_reg enabled.",
230 "See https://github.com/bytecodealliance/wasmtime/issues/4376 for more info"
231 ]
232 .join("\n")));
233 }
234
235 let host_isa = build_host_isa(true, flags.clone(), vec![]).ok();
237 if let Err(e) = is_isa_compatible(file_path, host_isa.as_deref(), isa.unwrap()) {
238 log::info!("{}", e);
239 return Ok(());
240 }
241
242 let compiled_testfile = compile_testfile(&testfile, flags, isa.unwrap())?;
243
244 for (func, details) in &testfile.functions {
245 info!(
246 "Test: {}({}) {}",
247 self.name(),
248 func.name,
249 isa.map_or("-", TargetIsa::name)
250 );
251
252 let context = Context {
253 preamble_comments: &testfile.preamble_comments,
254 details,
255 flags,
256 isa,
257 file_path: file_path.as_ref(),
258 file_update,
259 };
260
261 run_test(&compiled_testfile, &func, &context).context(self.name())?;
262 }
263
264 Ok(())
265 }
266
267 fn run(&self, _func: Cow<ir::Function>, _context: &Context) -> anyhow::Result<()> {
268 unreachable!()
269 }
270}
271
272pub fn build_vmctx_struct(ptr_ty: Type) -> (Vec<u64>, DataValue) {
274 let context_struct: Vec<u64> = Vec::new();
275
276 let ptr = context_struct.as_ptr() as usize as i128;
277 let ptr_dv =
278 DataValue::from_integer(ptr, ptr_ty).expect("Failed to cast pointer to native target size");
279
280 (context_struct, ptr_dv)
282}