Path: blob/main/cranelift/filetests/src/test_interpret.rs
1691 views
//! Test command for interpreting CLIF files and verifying their results1//!2//! The `interpret` test command interprets each function on the host machine3//! using [RunCommand](cranelift_reader::RunCommand)s.45use crate::runone::FileUpdate;6use crate::subtest::SubTest;7use anyhow::Context;8use cranelift_codegen::data_value::DataValue;9use cranelift_codegen::ir;10use cranelift_codegen::ir::{Function, LibCall};11use cranelift_codegen::isa::TargetIsa;12use cranelift_codegen::settings::Flags;13use cranelift_interpreter::environment::FunctionStore;14use cranelift_interpreter::interpreter::{Interpreter, InterpreterState, LibCallValues};15use cranelift_interpreter::step::ControlFlow;16use cranelift_reader::{Details, TestCommand, TestFile, parse_run_command};17use log::{info, trace};18use smallvec::smallvec;19use std::borrow::Cow;2021struct TestInterpret;2223pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {24assert_eq!(parsed.command, "interpret");25if !parsed.options.is_empty() {26anyhow::bail!("No options allowed on {}", parsed);27}28Ok(Box::new(TestInterpret))29}3031impl SubTest for TestInterpret {32fn name(&self) -> &'static str {33"interpret"34}3536fn is_mutating(&self) -> bool {37false38}3940fn needs_isa(&self) -> bool {41false42}4344/// Runs the entire subtest for a given target, invokes [Self::run] for running45/// individual tests.46fn run_target<'a>(47&self,48testfile: &TestFile,49_: &mut FileUpdate,50_: &'a str,51_: &'a Flags,52_: Option<&'a dyn TargetIsa>,53) -> anyhow::Result<()> {54// We can build the FunctionStore once and reuse it55let mut func_store = FunctionStore::default();56for (func, _) in &testfile.functions {57func_store.add(func.name.to_string(), &func);58}5960for (func, details) in &testfile.functions {61info!("Test: {}({}) interpreter", self.name(), func.name);6263run_test(&func_store, func, details).context(self.name())?;64}6566Ok(())67}6869fn run(70&self,71_func: Cow<ir::Function>,72_context: &crate::subtest::Context,73) -> anyhow::Result<()> {74unreachable!()75}76}7778fn run_test(func_store: &FunctionStore, func: &Function, details: &Details) -> anyhow::Result<()> {79for comment in details.comments.iter() {80if let Some(command) = parse_run_command(comment.text, &func.signature)? {81trace!("Parsed run command: {command}");8283command84.run(|func_name, run_args| {85// Rebuild the interpreter state on every run to ensure that we don't accidentally depend on86// some leftover state87let state = InterpreterState::default()88.with_function_store(func_store.clone())89.with_libcall_handler(|libcall: LibCall, args: LibCallValues| {90use LibCall::*;91Ok(smallvec![match (libcall, &args[..]) {92(CeilF32, [DataValue::F32(a)]) => DataValue::F32(a.ceil()),93(CeilF64, [DataValue::F64(a)]) => DataValue::F64(a.ceil()),94(FloorF32, [DataValue::F32(a)]) => DataValue::F32(a.floor()),95(FloorF64, [DataValue::F64(a)]) => DataValue::F64(a.floor()),96(TruncF32, [DataValue::F32(a)]) => DataValue::F32(a.trunc()),97(TruncF64, [DataValue::F64(a)]) => DataValue::F64(a.trunc()),98_ => unreachable!(),99}])100});101102let mut args = Vec::with_capacity(run_args.len());103args.extend_from_slice(run_args);104105// Because we have stored function names with a leading %, we need to re-add it.106let func_name = &format!("%{func_name}");107match Interpreter::new(state).call_by_name(func_name, &args) {108Ok(ControlFlow::Return(results)) => Ok(results.to_vec()),109Ok(e) => {110panic!("Unexpected returned control flow: {e:?}")111}112Err(t) => Err(format!("unexpected trap: {t:?}")),113}114})115.map_err(|e| anyhow::anyhow!("{}", e))?;116}117}118Ok(())119}120121122