diff --git a/Makefile b/Makefile index 4acde792584..935a6a69218 100644 --- a/Makefile +++ b/Makefile @@ -192,6 +192,12 @@ check: check-bench # as default, and test a minimal set of features with only one backend # at a time. cargo check --manifest-path lib/runtime/Cargo.toml + # Check some of the cases where deterministic execution could matter + cargo check --manifest-path lib/runtime/Cargo.toml --features "deterministic-execution" + cargo check --manifest-path lib/runtime/Cargo.toml --no-default-features \ + --features=default-backend-singlepass,deterministic-execution + cargo check --manifest-path lib/runtime/Cargo.toml --no-default-features \ + --features=default-backend-llvm,deterministic-execution cargo check --release --manifest-path lib/runtime/Cargo.toml $(RUNTIME_CHECK) \ diff --git a/lib/llvm-backend-tests/tests/compile.rs b/lib/llvm-backend-tests/tests/compile.rs index 17071a44a04..1e32f6dd08d 100644 --- a/lib/llvm-backend-tests/tests/compile.rs +++ b/lib/llvm-backend-tests/tests/compile.rs @@ -1,40 +1,68 @@ +use wasmer_llvm_backend::{InkwellMemoryBuffer, InkwellModule, LLVMBackendConfig, LLVMCallbacks}; use wasmer_llvm_backend_tests::{get_compiler, wat2wasm}; -use wasmer_runtime::imports; -use wasmer_runtime_core::compile_with; +use wasmer_runtime::{imports, CompilerConfig}; +use wasmer_runtime_core::{backend::BackendCompilerConfig, compile_with, compile_with_config}; + +use std::cell::RefCell; +use std::rc::Rc; #[test] fn crash_return_with_float_on_stack() { const MODULE: &str = r#" (module - (type (;0;) (func)) - (type (;1;) (func (param f64) (result f64))) + (type (func)) + (type (func (param f64) (result f64))) (func $_start (type 0)) (func $fmod (type 1) (param f64) (result f64) local.get 0 - f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 f64.mul - return) -) + return)) "#; let wasm_binary = wat2wasm(MODULE.as_bytes()).expect("WAST not valid or malformed"); let module = compile_with(&wasm_binary, &get_compiler()).unwrap(); module.instantiate(&imports! {}).unwrap(); } +#[derive(Debug, Default)] +pub struct RecordPreOptIR { + preopt_ir: String, +} + +impl LLVMCallbacks for RecordPreOptIR { + fn preopt_ir_callback(&mut self, module: &InkwellModule) { + self.preopt_ir = module.print_to_string().to_string(); + } +} + #[test] fn crash_select_with_mismatched_pending() { - const MODULE: &str = r#" + const WAT: &str = r#" (module - (func (param f64) - f64.const 0x0p+0 (;=0;) + (func (param f64) (result f64) + f64.const 0x0p+0 local.get 0 f64.add - f64.const 0x0p+0 (;=0;) + f64.const 0x0p+0 i32.const 0 - select - drop)) + select)) "#; - let wasm_binary = wat2wasm(MODULE.as_bytes()).expect("WAST not valid or malformed"); - let module = compile_with(&wasm_binary, &get_compiler()).unwrap(); + let record_pre_opt_ir = Rc::new(RefCell::new(RecordPreOptIR::default())); + let compiler_config = CompilerConfig { + backend_specific_config: Some(BackendCompilerConfig(Box::new(LLVMBackendConfig { + callbacks: Some(record_pre_opt_ir.clone()), + }))), + ..Default::default() + }; + let wasm_binary = wat2wasm(WAT.as_bytes()).expect("WAST not valid or malformed"); + let module = compile_with_config(&wasm_binary, &get_compiler(), compiler_config).unwrap(); module.instantiate(&imports! {}).unwrap(); + const LLVM: &str = r#" + %s3 = fadd double 0.000000e+00, %s2 + %nan = fcmp uno double %s3, 0.000000e+00 + %2 = select i1 %nan, double 0x7FF8000000000000, double %s3 + %s5 = select i1 false, double %2, double 0.000000e+00 + br label %return +"#; + assert!(&record_pre_opt_ir.borrow().preopt_ir.contains(LLVM)); } diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs index 853cfac1195..a2db088a76a 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -37,9 +37,9 @@ pub type InkwellModule<'ctx> = inkwell::module::Module<'ctx>; pub type InkwellMemoryBuffer = inkwell::memory_buffer::MemoryBuffer; pub trait LLVMCallbacks: std::any::Any + 'static { - fn preopt_ir_callback(&mut self, module: &InkwellModule); - fn postopt_ir_callback(&mut self, module: &InkwellModule); - fn obj_memory_buffer_callback(&mut self, memory_buffer: &InkwellMemoryBuffer); + fn preopt_ir_callback(&mut self, _module: &InkwellModule) {} + fn postopt_ir_callback(&mut self, _module: &InkwellModule) {} + fn obj_memory_buffer_callback(&mut self, _memory_buffer: &InkwellMemoryBuffer) {} } pub struct LLVMBackendConfig { diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index 7c76d74ac45..1e28133c969 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -57,3 +57,4 @@ trace = ["debug"] "backend-singlepass" = [] "backend-llvm" = [] managed = [] +deterministic-execution = ["wasmparser/deterministic"] diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index 991e05ed8ad..a3ed432e846 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -186,6 +186,9 @@ pub fn validating_parser_config(features: &Features) -> wasmparser::ValidatingPa enable_simd: features.simd, enable_bulk_memory: false, enable_multi_value: false, + + #[cfg(feature = "deterministic-execution")] + deterministic_only: true, }, } } diff --git a/lib/runtime-core/src/import.rs b/lib/runtime-core/src/import.rs index 284ff1725a5..1c3bdbd9ad2 100644 --- a/lib/runtime-core/src/import.rs +++ b/lib/runtime-core/src/import.rs @@ -273,6 +273,54 @@ mod test { use crate::export::Export; use crate::global::Global; use crate::types::Value; + use std::ffi::c_void; + use std::sync::Arc; + + struct Data { + inner: *const u32, + } + + unsafe impl Send for Data {} + unsafe impl Sync for Data {} + + fn dummy_state_creator(data: Arc) -> (*mut c_void, fn(*mut c_void)) { + let data: *mut Data = Arc::into_raw(data) as _; + + (data as _, |_| {}) + } + + #[test] + fn state_creator_fn() { + let ptr = &0xAABBCCDDu32 as *const u32; + let data = Arc::new(Data { inner: ptr }); + + let imports = imports! { + move || dummy_state_creator(Arc::clone(&data)), + }; + + let (state, _dtor) = imports.call_state_creator().unwrap(); + let data: &mut Data = unsafe { &mut *(state as *mut Data) }; + + assert_eq!(ptr, data.inner); + } + + #[test] + fn state_creator_closure() { + let ptr = &0xAABBCCDDu32 as *const u32; + let data = Arc::new(Data { inner: ptr }); + + let imports = imports! { + move || { + let data: *mut Data = Arc::into_raw(Arc::clone(&data)) as _; + (data as _, |_| {}) + }, + }; + + let (state, _dtor) = imports.call_state_creator().unwrap(); + let data: &mut Data = unsafe { &mut *(state as *mut Data) }; + + assert_eq!(ptr, data.inner); + } #[test] fn extending_works() { diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index 56f98bc7163..5b46677a864 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -159,6 +159,9 @@ pub fn validate_and_report_errors_with_features( enable_multi_value: false, enable_reference_types: false, enable_threads: features.threads, + + #[cfg(feature = "deterministic-execution")] + deterministic_only: true, }, }; let mut parser = wasmparser::ValidatingParser::new(wasm, Some(config)); diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index e6fe8bb9b18..56430f051a4 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -44,6 +44,7 @@ singlepass = ["wasmer-singlepass-backend"] default-backend-singlepass = ["singlepass"] default-backend-llvm = ["llvm"] default-backend-cranelift = ["cranelift"] +deterministic-execution = ["wasmer-singlepass-backend/deterministic-execution", "wasmer-runtime-core/deterministic-execution"] [[bench]] name = "nginx" diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index 0c832bf3020..58046148290 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -71,7 +71,7 @@ //! let value = add_one.call(42)?; //! //! assert_eq!(value, 43); -//! +//! //! Ok(()) //! } //! ``` @@ -246,7 +246,7 @@ pub fn compiler_for_backend(backend: Backend) -> Option> { #[cfg(feature = "cranelift")] Backend::Cranelift => Some(Box::new(wasmer_clif_backend::CraneliftCompiler::new())), - #[cfg(feature = "singlepass")] + #[cfg(any(feature = "singlepass"))] Backend::Singlepass => Some(Box::new( wasmer_singlepass_backend::SinglePassCompiler::new(), )), @@ -254,6 +254,18 @@ pub fn compiler_for_backend(backend: Backend) -> Option> { #[cfg(feature = "llvm")] Backend::LLVM => Some(Box::new(wasmer_llvm_backend::LLVMCompiler::new())), + Backend::Auto => { + #[cfg(feature = "default-backend-singlepass")] + return Some(Box::new( + wasmer_singlepass_backend::SinglePassCompiler::new(), + )); + #[cfg(feature = "default-backend-cranelift")] + return Some(Box::new(wasmer_clif_backend::CraneliftCompiler::new())); + #[cfg(feature = "default-backend-llvm")] + return Some(Box::new(wasmer_llvm_backend::LLVMCompiler::new())); + } + + #[cfg(not(all(feature = "llvm", feature = "singlepass", feature = "cranelift")))] _ => None, } } diff --git a/lib/singlepass-backend/Cargo.toml b/lib/singlepass-backend/Cargo.toml index 6ed704837a9..7b9e424b501 100644 --- a/lib/singlepass-backend/Cargo.toml +++ b/lib/singlepass-backend/Cargo.toml @@ -22,3 +22,7 @@ smallvec = "0.6" serde = "1.0" serde_derive = "1.0" bincode = "1.2" + +[features] +default = [] +deterministic-execution = ["wasmer-runtime-core/deterministic-execution"]