diff --git a/build.rs b/build.rs index 56cd2fb8a2..1debf375c0 100644 --- a/build.rs +++ b/build.rs @@ -14,12 +14,24 @@ fn main() { let mut out = File::create(out_dir.join("wast_testsuite_tests.rs")) .expect("error generating test source file"); - test_directory(&mut out, "misc_testsuite").expect("generating tests"); - test_directory(&mut out, "spec_testsuite").expect("generating tests"); - test_file(&mut out, "spec_testsuite/proposals/simd/simd_const.wast").expect("generating tests"); + for strategy in &["AlwaysCranelift", "AlwaysLightbeam"] { + writeln!(out, "#[allow(non_snake_case)]").expect("generating tests"); + writeln!(out, "mod {} {{", strategy).expect("generating tests"); + + test_directory(&mut out, "misc_testsuite", strategy).expect("generating tests"); + test_directory(&mut out, "spec_testsuite", strategy).expect("generating tests"); + test_file( + &mut out, + "spec_testsuite/proposals/simd/simd_const.wast", + strategy, + ) + .expect("generating tests"); + + writeln!(out, "}}").expect("generating tests"); + } } -fn test_directory(out: &mut File, testsuite: &str) -> io::Result<()> { +fn test_directory(out: &mut File, testsuite: &str, strategy: &str) -> io::Result<()> { let mut dir_entries: Vec<_> = read_dir(testsuite) .expect("reading testsuite directory") .map(|r| r.expect("reading testsuite directory entry")) @@ -44,76 +56,127 @@ fn test_directory(out: &mut File, testsuite: &str) -> io::Result<()> { dir_entries.sort_by_key(|dir| dir.path()); + start_test_module(out, testsuite)?; + for dir_entry in dir_entries { + write_testsuite_tests(out, &dir_entry.path(), testsuite, strategy)?; + } + finish_test_module(out) +} + +fn test_file(out: &mut File, testfile: &str, strategy: &str) -> io::Result<()> { + let testsuite = "single_file_spec_test"; + let path = Path::new(testfile); + start_test_module(out, testsuite)?; + write_testsuite_tests(out, path, testsuite, strategy)?; + finish_test_module(out) +} + +fn start_test_module(out: &mut File, testsuite: &str) -> io::Result<()> { writeln!( out, - "mod {} {{", + " mod {} {{", Path::new(testsuite) .file_stem() .expect("testsuite filename should have a stem") .to_str() .expect("testsuite filename should be representable as a string") - .replace("-", "_") + .replace("-", "_"), )?; writeln!( out, - " use super::{{native_isa, Path, WastContext, Compiler, Features}};" - )?; - for dir_entry in dir_entries { - write_testsuite_tests(out, &dir_entry.path(), testsuite)?; - } - writeln!(out, "}}")?; - Ok(()) + " use super::super::{{native_isa, Path, WastContext, Compiler, Features, CompilationStrategy}};" + ) } -fn test_file(out: &mut File, testfile: &str) -> io::Result<()> { - let path = Path::new(testfile); - write_testsuite_tests(out, path, "single_file_spec_test") +fn finish_test_module(out: &mut File) -> io::Result<()> { + writeln!(out, " }}") } -fn write_testsuite_tests(out: &mut File, path: &Path, testsuite: &str) -> io::Result<()> { +fn write_testsuite_tests( + out: &mut File, + path: &Path, + testsuite: &str, + strategy: &str, +) -> io::Result<()> { let stemstr = path .file_stem() .expect("file_stem") .to_str() .expect("to_str"); - writeln!(out, " #[test]")?; - if ignore(testsuite, stemstr) { - writeln!(out, " #[ignore]")?; + writeln!(out, " #[test]")?; + if ignore(testsuite, stemstr, strategy) { + writeln!(out, " #[ignore]")?; } - writeln!(out, " fn r#{}() {{", &stemstr.replace("-", "_"))?; - writeln!(out, " let isa = native_isa();")?; - writeln!(out, " let compiler = Compiler::new(isa);")?; + writeln!(out, " fn r#{}() {{", &stemstr.replace("-", "_"))?; + writeln!(out, " let isa = native_isa();")?; writeln!( out, - " let features = Features {{ simd: true, ..Default::default() }};" + " let compiler = Compiler::new(isa, CompilationStrategy::{});", + strategy )?; writeln!( out, - " let mut wast_context = WastContext::new(Box::new(compiler)).with_features(features);" + " let features = Features {{ simd: true, ..Default::default() }};" )?; - writeln!(out, " wast_context")?; - writeln!(out, " .register_spectest()")?; writeln!( out, - " .expect(\"instantiating \\\"spectest\\\"\");" + " let mut wast_context = WastContext::new(Box::new(compiler)).with_features(features);" )?; - writeln!(out, " wast_context")?; - write!(out, " .run_file(Path::new(\"")?; + writeln!(out, " wast_context")?; + writeln!(out, " .register_spectest()")?; + writeln!( + out, + " .expect(\"instantiating \\\"spectest\\\"\");" + )?; + writeln!(out, " wast_context")?; + write!(out, " .run_file(Path::new(\"")?; // Write out the string with escape_debug to prevent special characters such // as backslash from being reinterpreted. for c in path.display().to_string().chars() { write!(out, "{}", c.escape_debug())?; } writeln!(out, "\"))")?; - writeln!(out, " .expect(\"error running wast file\");",)?; - writeln!(out, " }}")?; + writeln!(out, " .expect(\"error running wast file\");",)?; + writeln!(out, " }}")?; writeln!(out)?; Ok(()) } /// Ignore tests that aren't supported yet. -fn ignore(testsuite: &str, name: &str) -> bool { +fn ignore(testsuite: &str, name: &str, strategy: &str) -> bool { + match strategy { + #[cfg(feature = "lightbeam")] + "AlwaysLightbeam" => match (testsuite, name) { + ("misc_testsuite", "memory_grow") + | ("misc_testsuite", "misc_traps") + | ("single_file_spec_test", "simd_const") + | ("spec_testsuite", "address") + | ("spec_testsuite", "align") + | ("spec_testsuite", "call") + | ("spec_testsuite", "call_indirect") + | ("spec_testsuite", "conversions") + | ("spec_testsuite", "elem") + | ("spec_testsuite", "func_ptrs") + | ("spec_testsuite", "globals") + | ("spec_testsuite", "i32") + | ("spec_testsuite", "i64") + | ("spec_testsuite", "if") + | ("spec_testsuite", "imports") + | ("spec_testsuite", "int_exprs") + | ("spec_testsuite", "linking") + | ("spec_testsuite", "memory_grow") + | ("spec_testsuite", "memory_trap") + | ("spec_testsuite", "select") + | ("spec_testsuite", "traps") + | ("spec_testsuite", "unreachable") + | ("spec_testsuite", "unwind") => return true, + _ => (), + }, + "AlwaysCranelift" => {} + _ => panic!("unrecognized strategy"), + } + if cfg!(windows) { return match (testsuite, name) { ("spec_testsuite", "address") => true, diff --git a/misc/wasmtime-py/src/lib.rs b/misc/wasmtime-py/src/lib.rs index f35fa7b1bb..ab745ff8da 100644 --- a/misc/wasmtime-py/src/lib.rs +++ b/misc/wasmtime-py/src/lib.rs @@ -69,7 +69,7 @@ pub fn instantiate( isa_builder.finish(cranelift_codegen::settings::Flags::new(flag_builder)) }; - let mut context = wasmtime_jit::Context::with_isa(isa); + let mut context = wasmtime_jit::Context::with_isa(isa, wasmtime_jit::CompilationStrategy::Auto); context.set_debug_info(generate_debug_info); let global_exports = context.get_global_exports(); diff --git a/src/bin/wast.rs b/src/bin/wast.rs index c144f1efb0..8d7866aeb4 100644 --- a/src/bin/wast.rs +++ b/src/bin/wast.rs @@ -34,14 +34,14 @@ use serde::Deserialize; use std::path::Path; use std::process; use wasmtime_environ::{cache_create_new_config, cache_init}; -use wasmtime_jit::{Compiler, Features}; +use wasmtime_jit::{CompilationStrategy, Compiler, Features}; use wasmtime_wast::WastContext; const USAGE: &str = " Wast test runner. Usage: - wast [-do] [--enable-simd] [--disable-cache | --cache-config=] ... + wast [-do] [--enable-simd] [--disable-cache | --cache-config=] [--always-lightmean | --always-cranelift] ... wast --create-cache-config [--cache-config=] wast --help | --version @@ -57,6 +57,8 @@ Options: creates default configuration and writes it to the disk, use with --cache-config to specify custom config file instead of default one + --always-lightbeam use Lightbeam for all compilation + --always-cranelift use Cranelift for all compilation -d, --debug enable debug output on stderr/stdout --enable-simd enable proposed SIMD instructions "; @@ -71,6 +73,8 @@ struct Args { flag_cache_config: Option, flag_create_cache_config: bool, flag_enable_simd: bool, + flag_always_lightbeam: bool, + flag_always_cranelift: bool, } fn main() { @@ -148,8 +152,18 @@ fn main() { features.simd = true; } + // Decide how to compile. + let strategy = match (args.flag_always_lightbeam, args.flag_always_cranelift) { + (true, false) => CompilationStrategy::AlwaysLightbeam, + (false, true) => CompilationStrategy::AlwaysCranelift, + (false, false) => CompilationStrategy::Auto, + (true, true) => { + panic!("Can't enable --always-cranelift and --always-lightbeam at the same time") + } + }; + let isa = isa_builder.finish(settings::Flags::new(flag_builder)); - let engine = Compiler::new(isa); + let engine = Compiler::new(isa, strategy); let mut wast_context = WastContext::new(Box::new(engine)).with_features(features); wast_context diff --git a/tests/instantiate.rs b/tests/instantiate.rs index adf4945688..00c29c09d1 100644 --- a/tests/instantiate.rs +++ b/tests/instantiate.rs @@ -8,7 +8,7 @@ use std::io::Read; use std::path::PathBuf; use std::rc::Rc; use wabt; -use wasmtime_jit::{instantiate, Compiler, NullResolver}; +use wasmtime_jit::{instantiate, CompilationStrategy, Compiler, NullResolver}; #[cfg(test)] const PATH_MODULE_RS2WASM_ADD_FUNC: &str = r"filetests/rs2wasm-add-func.wat"; @@ -40,7 +40,7 @@ fn test_environ_translate() { let isa = isa_builder.finish(settings::Flags::new(flag_builder)); let mut resolver = NullResolver {}; - let mut compiler = Compiler::new(isa); + let mut compiler = Compiler::new(isa, CompilationStrategy::Auto); let global_exports = Rc::new(RefCell::new(HashMap::new())); let instance = instantiate(&mut compiler, &data, &mut resolver, global_exports, false); assert!(instance.is_ok()); diff --git a/tests/wast_testsuites.rs b/tests/wast_testsuites.rs index 7fb0b017fc..d319332ae8 100644 --- a/tests/wast_testsuites.rs +++ b/tests/wast_testsuites.rs @@ -4,7 +4,7 @@ use cranelift_codegen::isa; use cranelift_codegen::settings; use cranelift_codegen::settings::Configurable; use std::path::Path; -use wasmtime_jit::{Compiler, Features}; +use wasmtime_jit::{CompilationStrategy, Compiler, Features}; use wasmtime_wast::WastContext; include!(concat!(env!("OUT_DIR"), "/wast_testsuite_tests.rs")); diff --git a/wasmtime-api/src/context.rs b/wasmtime-api/src/context.rs index 76085dd868..3eb40e7db4 100644 --- a/wasmtime-api/src/context.rs +++ b/wasmtime-api/src/context.rs @@ -2,7 +2,7 @@ use std::cell::{RefCell, RefMut}; use std::hash::{Hash, Hasher}; use std::rc::Rc; -use wasmtime_jit::{Compiler, Features}; +use wasmtime_jit::{CompilationStrategy, Compiler, Features}; use cranelift_codegen::settings; @@ -59,5 +59,5 @@ pub(crate) fn create_compiler(flags: settings::Flags) -> Compiler { isa_builder.finish(flags) }; - Compiler::new(isa) + Compiler::new(isa, CompilationStrategy::Auto) } diff --git a/wasmtime-jit/src/compiler.rs b/wasmtime-jit/src/compiler.rs index f1e87ce2d5..07aacf1b46 100644 --- a/wasmtime-jit/src/compiler.rs +++ b/wasmtime-jit/src/compiler.rs @@ -25,6 +25,20 @@ use wasmtime_runtime::{ VMFunctionBody, }; +/// Select which kind of compilation to use. +#[derive(Copy, Clone, Debug)] +pub enum CompilationStrategy { + /// Let Wasmtime pick the strategy. + Auto, + + /// Compile all functions with Cranelift. + AlwaysCranelift, + + /// Compile all functions with Lightbeam. + #[cfg(feature = "lightbeam")] + AlwaysLightbeam, +} + /// A WebAssembly code JIT compiler. /// /// A `Compiler` instance owns the executable memory that it allocates. @@ -40,6 +54,7 @@ pub struct Compiler { trap_registration_guards: Vec, trampoline_park: HashMap<*const VMFunctionBody, *const VMFunctionBody>, signatures: SignatureRegistry, + strategy: CompilationStrategy, /// The `FunctionBuilderContext`, shared between trampline function compilations. fn_builder_ctx: FunctionBuilderContext, @@ -47,7 +62,7 @@ pub struct Compiler { impl Compiler { /// Construct a new `Compiler`. - pub fn new(isa: Box) -> Self { + pub fn new(isa: Box, strategy: CompilationStrategy) -> Self { Self { isa, code_memory: CodeMemory::new(), @@ -55,6 +70,7 @@ impl Compiler { trampoline_park: HashMap::new(), signatures: SignatureRegistry::new(), fn_builder_ctx: FunctionBuilderContext::new(), + strategy, } } } @@ -73,11 +89,6 @@ impl Drop for Compiler { } } -#[cfg(feature = "lightbeam")] -type DefaultCompiler = wasmtime_environ::lightbeam::Lightbeam; -#[cfg(not(feature = "lightbeam"))] -type DefaultCompiler = wasmtime_environ::cranelift::Cranelift; - impl Compiler { /// Return the target's frontend configuration settings. pub fn frontend_config(&self) -> TargetFrontendConfig { @@ -105,12 +116,27 @@ impl Compiler { SetupError, > { let (compilation, relocations, address_transform, value_ranges, stack_slots, traps) = - DefaultCompiler::compile_module( - module, - function_body_inputs, - &*self.isa, - debug_data.is_some(), - ) + match self.strategy { + // For now, interpret `Auto` as `AlwaysCranelift` since that's the most stable + // implementation. + CompilationStrategy::Auto | CompilationStrategy::AlwaysCranelift => { + wasmtime_environ::cranelift::Cranelift::compile_module( + module, + function_body_inputs, + &*self.isa, + debug_data.is_some(), + ) + } + #[cfg(feature = "lightbeam")] + CompilationStrategy::AlwaysLightbeam => { + wasmtime_environ::lightbeam::Lightbeam::compile_module( + module, + function_body_inputs, + &*self.isa, + debug_data.is_some(), + ) + } + } .map_err(SetupError::Compile)?; let allocated_functions = diff --git a/wasmtime-jit/src/context.rs b/wasmtime-jit/src/context.rs index 16891bf229..22b2b94bae 100644 --- a/wasmtime-jit/src/context.rs +++ b/wasmtime-jit/src/context.rs @@ -1,7 +1,7 @@ use crate::action::{get, inspect_memory, invoke}; use crate::{ - instantiate, ActionError, ActionOutcome, Compiler, InstanceHandle, Namespace, RuntimeValue, - SetupError, + instantiate, ActionError, ActionOutcome, CompilationStrategy, Compiler, InstanceHandle, + Namespace, RuntimeValue, SetupError, }; use cranelift_codegen::isa::TargetIsa; use std::boxed::Box; @@ -103,8 +103,8 @@ impl Context { } /// Construct a new instance of `Context` with the given target. - pub fn with_isa(isa: Box) -> Self { - Self::new(Box::new(Compiler::new(isa))) + pub fn with_isa(isa: Box, strategy: CompilationStrategy) -> Self { + Self::new(Box::new(Compiler::new(isa, strategy))) } /// Retrieve the context features diff --git a/wasmtime-jit/src/lib.rs b/wasmtime-jit/src/lib.rs index 7c9ab11d84..bc16699429 100644 --- a/wasmtime-jit/src/lib.rs +++ b/wasmtime-jit/src/lib.rs @@ -49,7 +49,7 @@ mod resolver; mod target_tunables; pub use crate::action::{ActionError, ActionOutcome, RuntimeValue}; -pub use crate::compiler::Compiler; +pub use crate::compiler::{CompilationStrategy, Compiler}; pub use crate::context::{Context, ContextError, Features, UnknownInstance}; pub use crate::instantiate::{instantiate, CompiledModule, SetupError}; pub use crate::link::link_module;