cranelift_filetests/
test_interpret.rs

1//! Test command for interpreting CLIF files and verifying their results
2//!
3//! The `interpret` test command interprets each function on the host machine
4//! using [RunCommand](cranelift_reader::RunCommand)s.
5
6use crate::runone::FileUpdate;
7use crate::subtest::SubTest;
8use anyhow::Context;
9use cranelift_codegen::data_value::DataValue;
10use cranelift_codegen::ir;
11use cranelift_codegen::ir::{Function, LibCall};
12use cranelift_codegen::isa::TargetIsa;
13use cranelift_codegen::settings::Flags;
14use cranelift_interpreter::environment::FunctionStore;
15use cranelift_interpreter::interpreter::{Interpreter, InterpreterState, LibCallValues};
16use cranelift_interpreter::step::ControlFlow;
17use cranelift_reader::{parse_run_command, Details, TestCommand, TestFile};
18use log::{info, trace};
19use smallvec::smallvec;
20use std::borrow::Cow;
21
22struct TestInterpret;
23
24pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
25    assert_eq!(parsed.command, "interpret");
26    if !parsed.options.is_empty() {
27        anyhow::bail!("No options allowed on {}", parsed);
28    }
29    Ok(Box::new(TestInterpret))
30}
31
32impl SubTest for TestInterpret {
33    fn name(&self) -> &'static str {
34        "interpret"
35    }
36
37    fn is_mutating(&self) -> bool {
38        false
39    }
40
41    fn needs_isa(&self) -> bool {
42        false
43    }
44
45    /// Runs the entire subtest for a given target, invokes [Self::run] for running
46    /// individual tests.
47    fn run_target<'a>(
48        &self,
49        testfile: &TestFile,
50        _: &mut FileUpdate,
51        _: &'a str,
52        _: &'a Flags,
53        _: Option<&'a dyn TargetIsa>,
54    ) -> anyhow::Result<()> {
55        // We can build the FunctionStore once and reuse it
56        let mut func_store = FunctionStore::default();
57        for (func, _) in &testfile.functions {
58            func_store.add(func.name.to_string(), &func);
59        }
60
61        for (func, details) in &testfile.functions {
62            info!("Test: {}({}) interpreter", self.name(), func.name);
63
64            run_test(&func_store, func, details).context(self.name())?;
65        }
66
67        Ok(())
68    }
69
70    fn run(
71        &self,
72        _func: Cow<ir::Function>,
73        _context: &crate::subtest::Context,
74    ) -> anyhow::Result<()> {
75        unreachable!()
76    }
77}
78
79fn run_test(func_store: &FunctionStore, func: &Function, details: &Details) -> anyhow::Result<()> {
80    for comment in details.comments.iter() {
81        if let Some(command) = parse_run_command(comment.text, &func.signature)? {
82            trace!("Parsed run command: {}", command);
83
84            command
85                .run(|func_name, run_args| {
86                    // Rebuild the interpreter state on every run to ensure that we don't accidentally depend on
87                    // some leftover state
88                    let state = InterpreterState::default()
89                        .with_function_store(func_store.clone())
90                        .with_libcall_handler(|libcall: LibCall, args: LibCallValues| {
91                            use LibCall::*;
92                            Ok(smallvec![match (libcall, &args[..]) {
93                                (CeilF32, [DataValue::F32(a)]) => DataValue::F32(a.ceil()),
94                                (CeilF64, [DataValue::F64(a)]) => DataValue::F64(a.ceil()),
95                                (FloorF32, [DataValue::F32(a)]) => DataValue::F32(a.floor()),
96                                (FloorF64, [DataValue::F64(a)]) => DataValue::F64(a.floor()),
97                                (TruncF32, [DataValue::F32(a)]) => DataValue::F32(a.trunc()),
98                                (TruncF64, [DataValue::F64(a)]) => DataValue::F64(a.trunc()),
99                                _ => unreachable!(),
100                            }])
101                        });
102
103                    let mut args = Vec::with_capacity(run_args.len());
104                    args.extend_from_slice(run_args);
105
106                    // Because we have stored function names with a leading %, we need to re-add it.
107                    let func_name = &format!("%{func_name}");
108                    match Interpreter::new(state).call_by_name(func_name, &args) {
109                        Ok(ControlFlow::Return(results)) => Ok(results.to_vec()),
110                        Ok(e) => {
111                            panic!("Unexpected returned control flow: {e:?}")
112                        }
113                        Err(t) => Err(format!("unexpected trap: {t:?}")),
114                    }
115                })
116                .map_err(|e| anyhow::anyhow!("{}", e))?;
117        }
118    }
119    Ok(())
120}