Skip to content

Commit e6540cf

Browse files
sunfishcodefitzgen
andauthored
Enable relaxed_simd_deterministic. (#139)
* 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. * Make nondeterministic SIMD lowering an option and add a test. * Update src/lib.rs Co-authored-by: Nick Fitzgerald <fitzgen@gmail.com> * Fix the cli help message for `wasm_relaxed_simd`. --------- Co-authored-by: Nick Fitzgerald <fitzgen@gmail.com>
1 parent 87e1401 commit e6540cf

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

src/lib.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const DEFAULT_WASM_MULTI_VALUE: bool = true;
4343
const DEFAULT_WASM_MULTI_MEMORY: bool = true;
4444
const DEFAULT_WASM_BULK_MEMORY: bool = true;
4545
const DEFAULT_WASM_SIMD: bool = true;
46+
const DEFAULT_WASM_RELAXED_SIMD: bool = false;
4647
const DEFAULT_WASM_REFERENCE_TYPES: bool = true;
4748

4849
/// The type of data that is stored in the `wasmtime::Store` during
@@ -140,6 +141,18 @@ pub struct Wizer {
140141
#[cfg_attr(feature = "structopt", structopt(long = "allow-wasi"))]
141142
allow_wasi: bool,
142143

144+
/// Use deterministic behavior for relaxed SIMD instructions.
145+
///
146+
/// The relaxed SIMD instructions in Wasm are instructions which are
147+
/// permitted to have different results when run on different host
148+
/// CPU architectures. This flag tells wizer to instead execute relaxed
149+
/// SIMD instructions according to the [deterministic profile], which
150+
/// ensures that they're deterministic and platform-independent.
151+
///
152+
/// [deterministic profile]: https://webassembly.github.io/spec/core/appendix/profiles.html#deterministic-profile-small-mathrm-det
153+
#[cfg_attr(feature = "structopt", structopt(long = "relaxed-simd-deterministic"))]
154+
relaxed_simd_deterministic: bool,
155+
143156
/// Provide an additional preloaded module that is available to the
144157
/// main module.
145158
///
@@ -252,6 +265,14 @@ pub struct Wizer {
252265
#[cfg_attr(feature = "structopt", structopt(long, value_name = "true|false"))]
253266
wasm_simd: Option<bool>,
254267

268+
/// Enable or disable the Wasm relaxed SIMD proposal.
269+
///
270+
/// Disabled by default. When enabled, by default relaxed SIMD instructions
271+
/// will produce different results on different platforms. For deterministic
272+
/// results, additionally enable the `--relaxed-simd-deterministic` flag.
273+
#[cfg_attr(feature = "structopt", structopt(long, value_name = "true|false"))]
274+
wasm_relaxed_simd: Option<bool>,
275+
255276
/// Enable or disable the Wasm reference-types proposal.
256277
///
257278
/// Currently does not implement snapshotting or the use of references,
@@ -269,6 +290,7 @@ impl std::fmt::Debug for Wizer {
269290
init_func,
270291
func_renames,
271292
allow_wasi,
293+
relaxed_simd_deterministic,
272294
preload,
273295
preload_bytes,
274296
make_linker: _,
@@ -281,12 +303,14 @@ impl std::fmt::Debug for Wizer {
281303
wasm_multi_value,
282304
wasm_bulk_memory,
283305
wasm_simd,
306+
wasm_relaxed_simd,
284307
wasm_reference_types,
285308
} = self;
286309
f.debug_struct("Wizer")
287310
.field("init_func", &init_func)
288311
.field("func_renames", &func_renames)
289312
.field("allow_wasi", &allow_wasi)
313+
.field("relaxed_simd_deterministic", &relaxed_simd_deterministic)
290314
.field("preload", &preload)
291315
.field("preload_bytes", &preload_bytes)
292316
.field("make_linker", &"..")
@@ -299,6 +323,7 @@ impl std::fmt::Debug for Wizer {
299323
.field("wasm_multi_value", &wasm_multi_value)
300324
.field("wasm_bulk_memory", &wasm_bulk_memory)
301325
.field("wasm_simd", &wasm_simd)
326+
.field("wasm_relaxed_simd", &wasm_relaxed_simd)
302327
.field("wasm_reference_types", &wasm_reference_types)
303328
.finish()
304329
}
@@ -352,6 +377,7 @@ impl Wizer {
352377
init_func: "wizer.initialize".into(),
353378
func_renames: vec![],
354379
allow_wasi: false,
380+
relaxed_simd_deterministic: false,
355381
preload: vec![],
356382
preload_bytes: vec![],
357383
make_linker: None,
@@ -364,6 +390,7 @@ impl Wizer {
364390
wasm_multi_value: None,
365391
wasm_bulk_memory: None,
366392
wasm_simd: None,
393+
wasm_relaxed_simd: None,
367394
wasm_reference_types: None,
368395
}
369396
}
@@ -405,6 +432,19 @@ impl Wizer {
405432
Ok(self)
406433
}
407434

435+
/// Use deterministic behavior for relaxed SIMD instructions.
436+
///
437+
/// The relaxed SIMD instructions in Wasm are instructions which are
438+
/// permitted to have different results when run on different host
439+
/// CPU architectures. This flag tells wizer to instead execute relaxed
440+
/// SIMD instructions according to the [deterministic profile], which
441+
/// ensures that they're deterministic and platform-independent.
442+
///
443+
/// [deterministic profile]: https://webassembly.github.io/spec/core/appendix/profiles.html#deterministic-profile-small-mathrm-det
444+
pub fn relaxed_simd_deterministic(&mut self, deterministic: bool) {
445+
self.relaxed_simd_deterministic = deterministic;
446+
}
447+
408448
/// Provide an additional preloaded module that is available to the
409449
/// main module.
410450
///
@@ -583,6 +623,15 @@ impl Wizer {
583623
self
584624
}
585625

626+
/// Enable or disable the Wasm relaxed SIMD proposal.
627+
///
628+
/// Defaults to `false`. When enabling, consdider whether to additionally
629+
/// use `relaxed_simd_deterministic`.
630+
pub fn wasm_relaxed_simd(&mut self, enable: bool) -> &mut Self {
631+
self.wasm_relaxed_simd = Some(enable);
632+
self
633+
}
634+
586635
/// Initialize the given Wasm, snapshot it, and return the serialized
587636
/// snapshot as a new, pre-initialized Wasm module.
588637
pub fn run(&self, wasm: &[u8]) -> anyhow::Result<Vec<u8>> {
@@ -658,6 +707,7 @@ impl Wizer {
658707
config.wasm_bulk_memory(self.wasm_bulk_memory.unwrap_or(DEFAULT_WASM_BULK_MEMORY));
659708

660709
config.wasm_simd(self.wasm_simd.unwrap_or(DEFAULT_WASM_SIMD));
710+
config.wasm_relaxed_simd(self.wasm_relaxed_simd.unwrap_or(DEFAULT_WASM_RELAXED_SIMD));
661711

662712
// Note that reference_types are not actually supported,
663713
// but many compilers now enable them by default
@@ -669,6 +719,9 @@ impl Wizer {
669719
config.wasm_tail_call(true);
670720
config.wasm_extended_const(true);
671721

722+
// Enable `relaxed_simd_deterministic` if requested.
723+
config.relaxed_simd_deterministic(self.relaxed_simd_deterministic);
724+
672725
// Proposals that we should add support for.
673726
config.wasm_threads(false);
674727

@@ -703,6 +756,10 @@ impl Wizer {
703756

704757
features.set(WasmFeatures::TAIL_CALL, true);
705758
features.set(WasmFeatures::EXTENDED_CONST, true);
759+
features.set(
760+
WasmFeatures::RELAXED_SIMD,
761+
self.wasm_relaxed_simd.unwrap_or(DEFAULT_WASM_RELAXED_SIMD),
762+
);
706763

707764
return features;
708765
}

tests/tests.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,3 +829,33 @@ fn accept_simd128() -> Result<()> {
829829
"#,
830830
)
831831
}
832+
833+
#[test]
834+
fn relaxed_simd_deterministic() -> Result<()> {
835+
let _ = env_logger::try_init();
836+
let wasm = wat_to_wasm(
837+
r#"
838+
(module
839+
(global $g (mut i32) i32.const 0)
840+
(func (export "wizer.initialize")
841+
(v128.const f32x4 2796203.5 0.0 0.0 0.0)
842+
(v128.const f32x4 3.0 0.0 0.0 0.0)
843+
(v128.const f32x4 8388611.0 0.0 0.0 0.0)
844+
f32x4.relaxed_madd
845+
f32x4.extract_lane 0
846+
i32.reinterpret_f32
847+
global.set $g)
848+
(func (export "run") (result i32)
849+
global.get $g
850+
)
851+
)
852+
"#,
853+
)?;
854+
let mut wizer = get_wizer();
855+
wizer.wasm_relaxed_simd(true);
856+
wizer.relaxed_simd_deterministic(true);
857+
858+
// We'll get 0x4b000003 if we have the deterministic `relaxed_madd`
859+
// semantics. We might get 0x4b000002 if we don't.
860+
wizen_and_run_wasm(&[], 0x4b800003, &wasm, wizer)
861+
}

0 commit comments

Comments
 (0)