diff --git a/src/lib.rs b/src/lib.rs index 61d4d81..6f65a03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,7 @@ const DEFAULT_WASM_MULTI_VALUE: bool = true; const DEFAULT_WASM_MULTI_MEMORY: bool = true; const DEFAULT_WASM_BULK_MEMORY: bool = true; const DEFAULT_WASM_SIMD: bool = true; +const DEFAULT_WASM_RELAXED_SIMD: bool = false; const DEFAULT_WASM_REFERENCE_TYPES: bool = true; /// The type of data that is stored in the `wasmtime::Store` during @@ -140,6 +141,18 @@ pub struct Wizer { #[cfg_attr(feature = "structopt", structopt(long = "allow-wasi"))] allow_wasi: bool, + /// Use deterministic behavior for relaxed SIMD instructions. + /// + /// The relaxed SIMD instructions in Wasm are instructions which are + /// permitted to have different results when run on different host + /// CPU architectures. This flag tells wizer to instead execute relaxed + /// SIMD instructions according to the [deterministic profile], which + /// ensures that they're deterministic and platform-independent. + /// + /// [deterministic profile]: https://webassembly.github.io/spec/core/appendix/profiles.html#deterministic-profile-small-mathrm-det + #[cfg_attr(feature = "structopt", structopt(long = "relaxed-simd-deterministic"))] + relaxed_simd_deterministic: bool, + /// Provide an additional preloaded module that is available to the /// main module. /// @@ -252,6 +265,14 @@ pub struct Wizer { #[cfg_attr(feature = "structopt", structopt(long, value_name = "true|false"))] wasm_simd: Option, + /// Enable or disable the Wasm relaxed SIMD proposal. + /// + /// Disabled by default. When enabled, by default relaxed SIMD instructions + /// will produce different results on different platforms. For deterministic + /// results, additionally enable the `--relaxed-simd-deterministic` flag. + #[cfg_attr(feature = "structopt", structopt(long, value_name = "true|false"))] + wasm_relaxed_simd: Option, + /// Enable or disable the Wasm reference-types proposal. /// /// Currently does not implement snapshotting or the use of references, @@ -269,6 +290,7 @@ impl std::fmt::Debug for Wizer { init_func, func_renames, allow_wasi, + relaxed_simd_deterministic, preload, preload_bytes, make_linker: _, @@ -281,12 +303,14 @@ impl std::fmt::Debug for Wizer { wasm_multi_value, wasm_bulk_memory, wasm_simd, + wasm_relaxed_simd, wasm_reference_types, } = self; f.debug_struct("Wizer") .field("init_func", &init_func) .field("func_renames", &func_renames) .field("allow_wasi", &allow_wasi) + .field("relaxed_simd_deterministic", &relaxed_simd_deterministic) .field("preload", &preload) .field("preload_bytes", &preload_bytes) .field("make_linker", &"..") @@ -299,6 +323,7 @@ impl std::fmt::Debug for Wizer { .field("wasm_multi_value", &wasm_multi_value) .field("wasm_bulk_memory", &wasm_bulk_memory) .field("wasm_simd", &wasm_simd) + .field("wasm_relaxed_simd", &wasm_relaxed_simd) .field("wasm_reference_types", &wasm_reference_types) .finish() } @@ -352,6 +377,7 @@ impl Wizer { init_func: "wizer.initialize".into(), func_renames: vec![], allow_wasi: false, + relaxed_simd_deterministic: false, preload: vec![], preload_bytes: vec![], make_linker: None, @@ -364,6 +390,7 @@ impl Wizer { wasm_multi_value: None, wasm_bulk_memory: None, wasm_simd: None, + wasm_relaxed_simd: None, wasm_reference_types: None, } } @@ -405,6 +432,19 @@ impl Wizer { Ok(self) } + /// Use deterministic behavior for relaxed SIMD instructions. + /// + /// The relaxed SIMD instructions in Wasm are instructions which are + /// permitted to have different results when run on different host + /// CPU architectures. This flag tells wizer to instead execute relaxed + /// SIMD instructions according to the [deterministic profile], which + /// ensures that they're deterministic and platform-independent. + /// + /// [deterministic profile]: https://webassembly.github.io/spec/core/appendix/profiles.html#deterministic-profile-small-mathrm-det + pub fn relaxed_simd_deterministic(&mut self, deterministic: bool) { + self.relaxed_simd_deterministic = deterministic; + } + /// Provide an additional preloaded module that is available to the /// main module. /// @@ -583,6 +623,15 @@ impl Wizer { self } + /// Enable or disable the Wasm relaxed SIMD proposal. + /// + /// Defaults to `false`. When enabling, consdider whether to additionally + /// use `relaxed_simd_deterministic`. + pub fn wasm_relaxed_simd(&mut self, enable: bool) -> &mut Self { + self.wasm_relaxed_simd = Some(enable); + self + } + /// Initialize the given Wasm, snapshot it, and return the serialized /// snapshot as a new, pre-initialized Wasm module. pub fn run(&self, wasm: &[u8]) -> anyhow::Result> { @@ -658,6 +707,7 @@ impl Wizer { config.wasm_bulk_memory(self.wasm_bulk_memory.unwrap_or(DEFAULT_WASM_BULK_MEMORY)); config.wasm_simd(self.wasm_simd.unwrap_or(DEFAULT_WASM_SIMD)); + config.wasm_relaxed_simd(self.wasm_relaxed_simd.unwrap_or(DEFAULT_WASM_RELAXED_SIMD)); // Note that reference_types are not actually supported, // but many compilers now enable them by default @@ -669,6 +719,9 @@ impl Wizer { config.wasm_tail_call(true); config.wasm_extended_const(true); + // Enable `relaxed_simd_deterministic` if requested. + config.relaxed_simd_deterministic(self.relaxed_simd_deterministic); + // Proposals that we should add support for. config.wasm_threads(false); @@ -703,6 +756,10 @@ impl Wizer { features.set(WasmFeatures::TAIL_CALL, true); features.set(WasmFeatures::EXTENDED_CONST, true); + features.set( + WasmFeatures::RELAXED_SIMD, + self.wasm_relaxed_simd.unwrap_or(DEFAULT_WASM_RELAXED_SIMD), + ); return features; } diff --git a/tests/tests.rs b/tests/tests.rs index a6ea92e..0f5b302 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -829,3 +829,33 @@ fn accept_simd128() -> Result<()> { "#, ) } + +#[test] +fn relaxed_simd_deterministic() -> Result<()> { + let _ = env_logger::try_init(); + let wasm = wat_to_wasm( + r#" +(module + (global $g (mut i32) i32.const 0) + (func (export "wizer.initialize") + (v128.const f32x4 2796203.5 0.0 0.0 0.0) + (v128.const f32x4 3.0 0.0 0.0 0.0) + (v128.const f32x4 8388611.0 0.0 0.0 0.0) + f32x4.relaxed_madd + f32x4.extract_lane 0 + i32.reinterpret_f32 + global.set $g) + (func (export "run") (result i32) + global.get $g + ) +) + "#, + )?; + let mut wizer = get_wizer(); + wizer.wasm_relaxed_simd(true); + wizer.relaxed_simd_deterministic(true); + + // We'll get 0x4b000003 if we have the deterministic `relaxed_madd` + // semantics. We might get 0x4b000002 if we don't. + wizen_and_run_wasm(&[], 0x4b800003, &wasm, wizer) +}