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 
6 use crate::runone::FileUpdate;
7 use crate::subtest::SubTest;
8 use anyhow::Context;
9 use cranelift_codegen::data_value::DataValue;
10 use cranelift_codegen::ir;
11 use cranelift_codegen::ir::{Function, LibCall};
12 use cranelift_codegen::isa::TargetIsa;
13 use cranelift_codegen::settings::Flags;
14 use cranelift_interpreter::environment::FunctionStore;
15 use cranelift_interpreter::interpreter::{Interpreter, InterpreterState, LibCallValues};
16 use cranelift_interpreter::step::ControlFlow;
17 use cranelift_reader::{Details, TestCommand, TestFile, parse_run_command};
18 use log::{info, trace};
19 use smallvec::smallvec;
20 use std::borrow::Cow;
21 
22 struct TestInterpret;
23 
subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>>24 pub 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 
32 impl SubTest for TestInterpret {
name(&self) -> &'static str33     fn name(&self) -> &'static str {
34         "interpret"
35     }
36 
is_mutating(&self) -> bool37     fn is_mutating(&self) -> bool {
38         false
39     }
40 
needs_isa(&self) -> bool41     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.
run_target<'a>( &self, testfile: &TestFile, _: &mut FileUpdate, _: &'a str, _: &'a Flags, _: Option<&'a dyn TargetIsa>, ) -> anyhow::Result<()>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 
run( &self, _func: Cow<ir::Function>, _context: &crate::subtest::Context, ) -> anyhow::Result<()>70     fn run(
71         &self,
72         _func: Cow<ir::Function>,
73         _context: &crate::subtest::Context,
74     ) -> anyhow::Result<()> {
75         unreachable!()
76     }
77 }
78 
run_test(func_store: &FunctionStore, func: &Function, details: &Details) -> anyhow::Result<()>79 fn 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 }
121