Path: blob/main/cranelift/filetests/src/test_run.rs
1691 views
//! Test command for running CLIF files and verifying their results1//!2//! The `run` test command compiles each function on the host machine and executes it34use crate::function_runner::{CompiledTestFile, TestFileCompiler};5use crate::runone::FileUpdate;6use crate::subtest::{Context, SubTest};7use anyhow::Context as _;8use cranelift_codegen::data_value::DataValue;9use cranelift_codegen::ir::Type;10use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa};11use cranelift_codegen::settings::{Configurable, Flags};12use cranelift_codegen::{ir, settings};13use cranelift_reader::TestCommand;14use cranelift_reader::{TestFile, parse_run_command};15use log::{info, trace};16use std::borrow::Cow;17use target_lexicon::Architecture;1819struct TestRun;2021pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {22assert_eq!(parsed.command, "run");23if !parsed.options.is_empty() {24anyhow::bail!("No options allowed on {}", parsed);25}26Ok(Box::new(TestRun))27}2829/// Builds a [TargetIsa] for the current host.30///31/// ISA Flags can be overridden by passing [Value]'s via `isa_flags`.32fn build_host_isa(33infer_native_flags: bool,34flags: settings::Flags,35isa_flags: Vec<settings::Value>,36) -> anyhow::Result<OwnedTargetIsa> {37let mut builder = cranelift_native::builder_with_options(infer_native_flags)38.map_err(|e| anyhow::Error::msg(e))?;3940// Copy ISA Flags41for value in isa_flags {42builder.set(value.name, &value.value_string())?;43}4445let isa = builder.finish(flags)?;46Ok(isa)47}4849/// Checks if the host's ISA is compatible with the one requested by the test.50fn is_isa_compatible(51file_path: &str,52host: Option<&dyn TargetIsa>,53requested: &dyn TargetIsa,54) -> Result<(), String> {55let host_triple = match host {56Some(host) => host.triple().clone(),57None => target_lexicon::Triple::host(),58};59// If this test requests to run on a completely different60// architecture than the host platform then we skip it entirely,61// since we won't be able to natively execute machine code.62let host_arch = host_triple.architecture;63let requested_arch = requested.triple().architecture;6465match (host_arch, requested_arch) {66// If the host matches the requested target, then that's all good.67(host, requested) if host == requested => {}6869// Allow minor differences in risc-v targets.70(Architecture::Riscv64(_), Architecture::Riscv64(_)) => {}7172// Any host can run pulley so long as the pointer width and endianness73// match.74(75_,76Architecture::Pulley3277| Architecture::Pulley6478| Architecture::Pulley32be79| Architecture::Pulley64be,80) if host_triple.pointer_width() == requested.triple().pointer_width()81&& host_triple.endianness() == requested.triple().endianness() => {}8283_ => {84return Err(format!(85"skipped {file_path}: host can't run {requested_arch:?} programs"86));87}88}8990// We need to check that the requested ISA does not have any flags that91// we can't natively support on the host.92let requested_flags = requested.isa_flags();93for req_value in requested_flags {94// pointer_width for pulley already validated above95if req_value.name == "pointer_width" {96continue;97}98let requested = match req_value.as_bool() {99Some(requested) => requested,100None => unimplemented!("ISA flag {} of kind {:?}", req_value.name, req_value.kind()),101};102let host_isa_flags = match host {103Some(host) => host.isa_flags(),104None => {105return Err(format!(106"host not available on this platform for isa-specific flag"107));108}109};110let available_in_host = host_isa_flags111.iter()112.find(|val| val.name == req_value.name)113.and_then(|val| val.as_bool())114.unwrap_or(false);115116if !requested || available_in_host {117continue;118}119120// The AArch64 feature `sign_return_address` is supported on all AArch64121// hosts, regardless of whether `cranelift-native` infers it or not. The122// instructions emitted with this feature enabled are interpreted as123// "hint" noop instructions on CPUs which don't support address124// authentication.125//126// Note that at this time `cranelift-native` will only enable127// `sign_return_address` for macOS (notably not Linux) because of a128// historical bug in libunwind which causes pointer address signing,129// when run on hardware that supports it, so segfault during unwinding.130if req_value.name == "sign_return_address" && matches!(host_arch, Architecture::Aarch64(_))131{132continue;133}134135return Err(format!(136"skipped {}: host does not support ISA flag {}",137file_path, req_value.name138));139}140141Ok(())142}143144fn compile_testfile(145testfile: &TestFile,146flags: &Flags,147isa: &dyn TargetIsa,148) -> anyhow::Result<CompiledTestFile> {149let isa = match isa.triple().architecture {150// Convert `&dyn TargetIsa` to `OwnedTargetIsa` by re-making the ISA and151// applying pulley flags/etc.152Architecture::Pulley32153| Architecture::Pulley64154| Architecture::Pulley32be155| Architecture::Pulley64be => {156let mut builder = cranelift_codegen::isa::lookup(isa.triple().clone())?;157for value in isa.isa_flags() {158builder.set(value.name, &value.value_string()).unwrap();159}160builder.finish(flags.clone())?161}162163// We can't use the requested ISA directly since it does not contain info164// about the operating system / calling convention / etc..165//166// Copy the requested ISA flags into the host ISA and use that.167_ => build_host_isa(false, flags.clone(), isa.isa_flags()).unwrap(),168};169170let mut tfc = TestFileCompiler::new(isa);171tfc.add_testfile(testfile)?;172Ok(tfc.compile()?)173}174175fn run_test(176testfile: &CompiledTestFile,177func: &ir::Function,178context: &Context,179) -> anyhow::Result<()> {180for comment in context.details.comments.iter() {181if let Some(command) = parse_run_command(comment.text, &func.signature)? {182trace!("Parsed run command: {command}");183184command185.run(|_, run_args| {186let (_ctx_struct, _vmctx_ptr) =187build_vmctx_struct(context.isa.unwrap().pointer_type());188189let mut args = Vec::with_capacity(run_args.len());190args.extend_from_slice(run_args);191192let trampoline = testfile.get_trampoline(func).unwrap();193Ok(trampoline.call(&testfile, &args))194})195.map_err(|s| anyhow::anyhow!("{}", s))?;196}197}198Ok(())199}200201impl SubTest for TestRun {202fn name(&self) -> &'static str {203"run"204}205206fn is_mutating(&self) -> bool {207false208}209210fn needs_isa(&self) -> bool {211true212}213214/// Runs the entire subtest for a given target, invokes [Self::run] for running215/// individual tests.216fn run_target<'a>(217&self,218testfile: &TestFile,219file_update: &mut FileUpdate,220file_path: &'a str,221flags: &'a Flags,222isa: Option<&'a dyn TargetIsa>,223) -> anyhow::Result<()> {224// Disable runtests with pinned reg enabled.225// We've had some abi issues that the trampoline isn't quite ready for.226if flags.enable_pinned_reg() {227return Err(anyhow::anyhow!(228[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}235236// Check that the host machine can run this test case (i.e. has all extensions)237let host_isa = build_host_isa(true, flags.clone(), vec![]).ok();238if let Err(e) = is_isa_compatible(file_path, host_isa.as_deref(), isa.unwrap()) {239log::info!("{e}");240return Ok(());241}242243let compiled_testfile = compile_testfile(&testfile, flags, isa.unwrap())?;244245for (func, details) in &testfile.functions {246info!(247"Test: {}({}) {}",248self.name(),249func.name,250isa.map_or("-", TargetIsa::name)251);252253let context = Context {254preamble_comments: &testfile.preamble_comments,255details,256flags,257isa,258file_path: file_path.as_ref(),259file_update,260};261262run_test(&compiled_testfile, &func, &context).context(self.name())?;263}264265Ok(())266}267268fn run(&self, _func: Cow<ir::Function>, _context: &Context) -> anyhow::Result<()> {269unreachable!()270}271}272273/// Build a VMContext struct with the layout described in docs/testing.md.274pub fn build_vmctx_struct(ptr_ty: Type) -> (Vec<u64>, DataValue) {275let context_struct: Vec<u64> = Vec::new();276277let ptr = context_struct.as_ptr() as usize as i128;278let ptr_dv =279DataValue::from_integer(ptr, ptr_ty).expect("Failed to cast pointer to native target size");280281// Return all these to make sure we don't deallocate the heaps too early282(context_struct, ptr_dv)283}284285286