From c3dea26a6dbfd1b03140ecc65704dc5fb5f2f8c2 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 7 Oct 2025 11:20:24 -0700 Subject: [PATCH 1/4] Enable `relaxed_simd_deterministic`. Have wizer enable `relaxed_simd_deterministic`, so that wizer's output doesn't depend on the relaxed SIMD lowerings on the host that runs the initialization code. Strictly speaking, the Wasm spec [requires] relaxed SIMD instructions to have the same behavior across all runs of a given program: > These choices are fixed, that is, parameters are constant during the execution of any given program. [requires]: https://webassembly.github.io/spec/core/exec/numerics.html#relaxed-operations Wizer doesn't have a way to ensure that this guarantee is upheld by the engine the resumes execution after the snapshot in general. However, by enabling deterministic lowerings for relaxed SIMD instructions, hosts that chose to can also enable deterministic lowerings and match it. --- src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 61d4d81..c7f1718 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -669,6 +669,13 @@ impl Wizer { config.wasm_tail_call(true); config.wasm_extended_const(true); + // The spec requires relaxed-simd instructions to be deterministic + // within a run. We don't have any way configure the nondeterminism + // of the code after a snapshot restore, however we can at least + // use deterministic lowerings so that if the subsequent engine + // also uses deterministic lowerings, it'll match. + config.relaxed_simd_deterministic(true); + // Proposals that we should add support for. config.wasm_threads(false); From cbed13d46b96f0928c433d41348087e727ea5665 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 7 Oct 2025 13:18:04 -0700 Subject: [PATCH 2/4] Make nondeterministic SIMD lowering an option and add a test. --- src/lib.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++------ tests/tests.rs | 30 ++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c7f1718..1394574 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,13 @@ 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. + wasm_relaxed_simd: Option, + /// Enable or disable the Wasm reference-types proposal. /// /// Currently does not implement snapshotting or the use of references, @@ -269,6 +289,7 @@ impl std::fmt::Debug for Wizer { init_func, func_renames, allow_wasi, + relaxed_simd_deterministic, preload, preload_bytes, make_linker: _, @@ -281,12 +302,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 +322,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 +376,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 +389,7 @@ impl Wizer { wasm_multi_value: None, wasm_bulk_memory: None, wasm_simd: None, + wasm_relaxed_simd: None, wasm_reference_types: None, } } @@ -405,6 +431,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 +622,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 +706,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,12 +718,8 @@ impl Wizer { config.wasm_tail_call(true); config.wasm_extended_const(true); - // The spec requires relaxed-simd instructions to be deterministic - // within a run. We don't have any way configure the nondeterminism - // of the code after a snapshot restore, however we can at least - // use deterministic lowerings so that if the subsequent engine - // also uses deterministic lowerings, it'll match. - config.relaxed_simd_deterministic(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); @@ -710,6 +755,7 @@ impl Wizer { features.set(WasmFeatures::TAIL_CALL, true); features.set(WasmFeatures::EXTENDED_CONST, true); + features.set(WasmFeatures::RELAXED_SIMD, true); 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) +} From 3d7953766ada01bd9d4da4cff89355ba185eef05 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 9 Oct 2025 13:48:58 -0700 Subject: [PATCH 3/4] Update src/lib.rs Co-authored-by: Nick Fitzgerald --- src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1394574..26e399c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -755,7 +755,10 @@ impl Wizer { features.set(WasmFeatures::TAIL_CALL, true); features.set(WasmFeatures::EXTENDED_CONST, true); - features.set(WasmFeatures::RELAXED_SIMD, true); + features.set( + WasmFeatures::RELAXED_SIMD, + self.wasm_relaxed_simd.unwrap_or(DEFAULT_WASM_RELAXED_SIMD), + ); return features; } From cffb388503aeabaa3996b513d217c3f240b110c7 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 9 Oct 2025 13:55:21 -0700 Subject: [PATCH 4/4] Fix the cli help message for `wasm_relaxed_simd`. --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 26e399c..6f65a03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -270,6 +270,7 @@ pub struct Wizer { /// 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.