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 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 { 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 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