numcodecs_wasm_host_reproducible/transform/
nan.rs

1use std::collections::VecDeque;
2
3use anyhow::{Error, anyhow};
4use wasm_encoder::reencode::{self, Reencode};
5
6/// Adapted from cranelift's NaN canonicalisation codegen pass
7/// <https://github.com/bytecodealliance/wasmtime/blob/ead6c7cc5dbb876437acbdf429a9190f25b96755/cranelift/codegen/src/nan_canonicalization.rs>
8/// Released under the Apache-2.0 WITH LLVM-exception License
9///
10/// Implementation written referencing:
11/// - WebAssembly Core Specification v2 <https://www.w3.org/TR/2024/WD-wasm-core-2-20240219>
12/// - The "WebAssembly 128-bit packed SIMD Extension" Specification: <https://github.com/WebAssembly/spec/blob/f8114686035f6ffc358771c822ede3c96bf54cd9/proposals/simd/SIMD.md>
13/// - The "Non-trapping Float-to-int Conversions" Extension Specification: <https://github.com/WebAssembly/spec/blob/f8114686035f6ffc358771c822ede3c96bf54cd9/proposals/nontrapping-float-to-int-conversion/Overview.md>
14pub enum NaNCanonicaliser {}
15
16impl NaNCanonicaliser {
17    pub fn apply_to_module(
18        wasm: &[u8],
19        features: wasmparser::WasmFeatures,
20    ) -> Result<Vec<u8>, anyhow::Error> {
21        let mut parser = wasmparser::Parser::new(0);
22        parser.set_features(features);
23
24        let mut module = wasm_encoder::Module::new();
25
26        let mut reencoder = NaNCanonicaliserReencoder {
27            func_type_num_params: Vec::new(),
28            function_type_indices: VecDeque::new(),
29        };
30        reencoder
31            .parse_core_module(&mut module, parser, wasm)
32            .map_err(|err| anyhow::format_err!("{}", err))?;
33
34        let wasm = module.finish();
35        wasmparser::Validator::new_with_features(features).validate_all(&wasm)?;
36        Ok(wasm)
37    }
38}
39
40struct NaNCanonicaliserReencoder {
41    func_type_num_params: Vec<u32>,
42    function_type_indices: VecDeque<usize>,
43}
44
45impl wasm_encoder::reencode::Reencode for NaNCanonicaliserReencoder {
46    type Error = Error;
47
48    fn parse_type_section(
49        &mut self,
50        types: &mut wasm_encoder::TypeSection,
51        section: wasmparser::TypeSectionReader<'_>,
52    ) -> Result<(), reencode::Error<Self::Error>> {
53        for func_ty in section.into_iter_err_on_gc_types() {
54            let func_ty = self.func_type(func_ty?)?;
55            #[expect(clippy::cast_possible_truncation)] // guaranteed by wasm spec
56            let num_params = func_ty.params().len() as u32;
57            self.func_type_num_params.push(num_params);
58            types.ty().func_type(&func_ty);
59        }
60        Ok(())
61    }
62
63    fn parse_function_section(
64        &mut self,
65        functions: &mut wasm_encoder::FunctionSection,
66        section: wasmparser::FunctionSectionReader<'_>,
67    ) -> Result<(), reencode::Error<Self::Error>> {
68        for function in section {
69            let function = self.type_index(function?)?;
70            self.function_type_indices.push_back(function as usize);
71            functions.function(function);
72        }
73        Ok(())
74    }
75
76    fn parse_function_body(
77        &mut self,
78        code: &mut wasm_encoder::CodeSection,
79        func: wasmparser::FunctionBody<'_>,
80    ) -> Result<(), reencode::Error<Self::Error>> {
81        let Some(function_ty) = self.function_type_indices.pop_front() else {
82            return Err(reencode::Error::UserError(anyhow!(
83                "wasm function body without declaration",
84            )));
85        };
86        let Some(num_params) = self.func_type_num_params.get(function_ty).copied() else {
87            return Err(reencode::Error::UserError(anyhow!(
88                "invalid type index for wasm function",
89            )));
90        };
91
92        let locals = func.get_locals_reader()?;
93        let locals = locals.into_iter().collect::<Result<Vec<_>, _>>()?;
94        let mut locals = locals
95            .into_iter()
96            .map(|(count, ty)| self.val_type(ty).map(|ty| (count, ty)))
97            .collect::<Result<Vec<_>, _>>()?;
98        let mut num_locals = locals.iter().map(|(count, _ty)| *count).sum::<u32>();
99
100        let mut stash_f32 = None;
101        let mut stash_f64 = None;
102        let mut stash_v128 = None;
103
104        let instructions = func.get_operators_reader()?;
105        for instruction in instructions {
106            if let Some(kind) = Self::may_produce_non_deterministic_nan(&instruction?)
107                .map_err(|err| reencode::Error::UserError(anyhow!(err)))?
108            {
109                match kind {
110                    MaybeNaNKind::F32 => stash_f32.get_or_insert_with(|| {
111                        let stash_f32 = num_params + num_locals;
112                        locals.push((1, wasm_encoder::ValType::F32));
113                        num_locals += 1;
114                        stash_f32
115                    }),
116                    MaybeNaNKind::F64 => stash_f64.get_or_insert_with(|| {
117                        let stash_f64 = num_params + num_locals;
118                        locals.push((1, wasm_encoder::ValType::F64));
119                        num_locals += 1;
120                        stash_f64
121                    }),
122                    MaybeNaNKind::F32x4 | MaybeNaNKind::F64x2 => {
123                        stash_v128.get_or_insert_with(|| {
124                            let stash_v128 = num_params + num_locals;
125                            locals.push((1, wasm_encoder::ValType::V128));
126                            num_locals += 1;
127                            stash_v128
128                        })
129                    }
130                };
131            }
132        }
133
134        let mut function = wasm_encoder::Function::new(locals);
135
136        for instruction in func.get_operators_reader()? {
137            let instruction = instruction?;
138
139            let kind = Self::may_produce_non_deterministic_nan(&instruction)
140                .map_err(|err| reencode::Error::UserError(anyhow!(err)))?;
141
142            function.instruction(&self.instruction(instruction)?);
143
144            if let Some(kind) = kind {
145                let Some(stash) = (match kind {
146                    MaybeNaNKind::F32 => stash_f32,
147                    MaybeNaNKind::F64 => stash_f64,
148                    MaybeNaNKind::F32x4 | MaybeNaNKind::F64x2 => stash_v128,
149                }) else {
150                    return Err(reencode::Error::UserError(anyhow!(
151                        "wasm float operation without matching canonicalisation stash",
152                    )));
153                };
154
155                for canon_instruction in
156                    Self::generate_nan_canonicalisation_instructions(kind, stash)
157                {
158                    function.instruction(&canon_instruction);
159                }
160            }
161        }
162
163        code.function(&function);
164
165        Ok(())
166    }
167}
168
169impl NaNCanonicaliserReencoder {
170    // Canonical 32-bit and 64-bit NaN values
171    const CANON_NAN_B32: u32 = 0x7FC0_0000;
172    const CANON_NAN_B32X4: u128 = 0x7FC0_0000_7FC0_0000_7FC0_0000_7FC0_0000;
173    const CANON_NAN_B64: u64 = 0x7FF8_0000_0000_0000;
174    const CANON_NAN_B64X2: u128 = 0x7FF8_0000_0000_0000_7FF8_0000_0000_0000;
175
176    fn generate_nan_canonicalisation_instructions(
177        kind: MaybeNaNKind,
178        stash: u32,
179    ) -> impl IntoIterator<Item = wasm_encoder::Instruction<'static>> {
180        match kind {
181            MaybeNaNKind::F32 => [
182                // stack: [x, ...]; stash: ??
183                wasm_encoder::Instruction::LocalSet(stash),
184                // stack: [...]; stash: x
185                // canonical NaN
186                wasm_encoder::Instruction::F32Const(wasm_encoder::Ieee32::from(f32::from_bits(
187                    Self::CANON_NAN_B32,
188                ))),
189                // stack: [canon(NaN), ...]; stash: x
190                wasm_encoder::Instruction::LocalGet(stash),
191                // stack: [x, canon(NaN), ...]; stash: x
192                wasm_encoder::Instruction::LocalGet(stash),
193                // stack: [x, x, canon(NaN), ...]; stash: x
194                wasm_encoder::Instruction::LocalGet(stash),
195                // stack: [x, x, x, canon(NaN), ...]; stash: x
196                // isNaN: x != x
197                wasm_encoder::Instruction::F32Ne,
198                // stack: [isNaN, x, canon(NaN), ...]; stash: x
199                // select expects the stack [c: isNaN, val2: x, val1: canon(NaN), ...]
200                // select returns if c == 0 { val2 } else { val1 }
201                // here if isNaN  then c = 1 and val1 = canon(NaN) is returned
202                //      if !isNaN then c = 0 and val2 = x is returned
203                wasm_encoder::Instruction::Select,
204                // stack: [canon(x), ...]; stash: x
205            ],
206            MaybeNaNKind::F64 => [
207                // stack: [x, ...]; stash: ??
208                wasm_encoder::Instruction::LocalSet(stash),
209                // stack: [...]; stash: x
210                // canonical NaN
211                wasm_encoder::Instruction::F64Const(wasm_encoder::Ieee64::from(f64::from_bits(
212                    Self::CANON_NAN_B64,
213                ))),
214                // stack: [canon(NaN), ...]; stash: x
215                wasm_encoder::Instruction::LocalGet(stash),
216                // stack: [x, canon(NaN), ...]; stash: x
217                wasm_encoder::Instruction::LocalGet(stash),
218                // stack: [x, x, canon(NaN), ...]; stash: x
219                wasm_encoder::Instruction::LocalGet(stash),
220                // stack: [x, x, x, canon(NaN), ...]; stash: x
221                // isNaN: x != x
222                wasm_encoder::Instruction::F64Ne,
223                // stack: [isNaN, x, canon(NaN), ...]; stash: x
224                // select expects the stack [c: isNaN, val2: x, val1: canon(NaN), ...]
225                // select returns if c == 0 { val2 } else { val1 }
226                // here if isNaN  then c = 1 and val1 = canon(NaN) is returned
227                //      if !isNaN then c = 0 and val2 = x is returned
228                wasm_encoder::Instruction::Select,
229                // stack: [canon(x), ...]; stash: x
230            ],
231            MaybeNaNKind::F32x4 => [
232                // stack: [x, ...]; stash: ??
233                wasm_encoder::Instruction::LocalSet(stash),
234                // stack: [...]; stash: x
235                // canonical NaN
236                wasm_encoder::Instruction::V128Const(i128::from_le_bytes(
237                    Self::CANON_NAN_B32X4.to_le_bytes(),
238                )),
239                // stack: [canon(NaN), ...]; stash: x
240                wasm_encoder::Instruction::LocalGet(stash),
241                // stack: [x, canon(NaN), ...]; stash: x
242                wasm_encoder::Instruction::LocalGet(stash),
243                // stack: [x, x, canon(NaN), ...]; stash: x
244                wasm_encoder::Instruction::LocalGet(stash),
245                // stack: [x, x, x, canon(NaN), ...]; stash: x
246                // isNaN: x != x
247                wasm_encoder::Instruction::F32x4Ne,
248                // stack: [isNaN, x, canon(NaN), ...]; stash: x
249                // bitselect expects the stack [c: isNaN, val2: x, val1: canon(NaN), ...]
250                // bitselect returns if c[i] == 0 { val2[i] } else { val1[i] }
251                // here if per-lane isNaN  then c = 1 and val1 = canon(NaN) is returned
252                //      if per-lane !isNaN then c = 0 and val2 = x is returned
253                wasm_encoder::Instruction::V128Bitselect,
254                // stack: [canon(x), ...]; stash: x
255            ],
256            MaybeNaNKind::F64x2 => [
257                // stack: [x, ...]; stash: ??
258                wasm_encoder::Instruction::LocalSet(stash),
259                // stack: [...]; stash: x
260                // canonical NaN
261                wasm_encoder::Instruction::V128Const(i128::from_le_bytes(
262                    Self::CANON_NAN_B64X2.to_le_bytes(),
263                )),
264                // stack: [canon(NaN), ...]; stash: x
265                wasm_encoder::Instruction::LocalGet(stash),
266                // stack: [x, canon(NaN), ...]; stash: x
267                wasm_encoder::Instruction::LocalGet(stash),
268                // stack: [x, x, canon(NaN), ...]; stash: x
269                wasm_encoder::Instruction::LocalGet(stash),
270                // stack: [x, x, x, canon(NaN), ...]; stash: x
271                // isNaN: x != x
272                wasm_encoder::Instruction::F64x2Ne,
273                // stack: [isNaN, x, canon(NaN), ...]; stash: x
274                // bitselect expects the stack [c: isNaN, val2: x, val1: canon(NaN), ...]
275                // bitselect returns if c[i] == 0 { val2[i] } else { val1[i] }
276                // here if per-lane isNaN  then c = 1 and val1 = canon(NaN) is returned
277                //      if per-lane !isNaN then c = 0 and val2 = x is returned
278                wasm_encoder::Instruction::V128Bitselect,
279                // stack: [canon(x), ...]; stash: x
280            ],
281        }
282    }
283
284    #[expect(clippy::too_many_lines)]
285    fn may_produce_non_deterministic_nan(
286        instr: &wasmparser::Operator,
287    ) -> Result<Option<MaybeNaNKind>, NonDeterministicWasmFeature> {
288        match instr {
289            // === MVP ===
290            // non-float operation
291            wasmparser::Operator::Unreachable
292            | wasmparser::Operator::Nop
293            | wasmparser::Operator::Block { .. }
294            | wasmparser::Operator::Loop { .. }
295            | wasmparser::Operator::If { .. }
296            | wasmparser::Operator::Else => Ok(None),
297            // === Exception handling ===
298            // non-float operation
299            wasmparser::Operator::TryTable { .. }
300            | wasmparser::Operator::Throw { .. }
301            | wasmparser::Operator::ThrowRef => Ok(None),
302            // === Legacy exception handling (deprecated) ===
303            // non-float operation
304            wasmparser::Operator::Try { .. }
305            | wasmparser::Operator::Catch { .. }
306            | wasmparser::Operator::Rethrow { .. }
307            | wasmparser::Operator::Delegate { .. }
308            | wasmparser::Operator::CatchAll => Ok(None),
309            // === MVP ===
310            // non-float operation
311            wasmparser::Operator::End
312            | wasmparser::Operator::Br { .. }
313            | wasmparser::Operator::BrIf { .. }
314            | wasmparser::Operator::BrTable { .. }
315            | wasmparser::Operator::Return
316            | wasmparser::Operator::Call { .. }
317            | wasmparser::Operator::CallIndirect { .. } => Ok(None),
318            // === Tail calls ===
319            // non-float operation
320            wasmparser::Operator::ReturnCall { .. }
321            | wasmparser::Operator::ReturnCallIndirect { .. } => Ok(None),
322            // === MVP ===
323            // non-float operation
324            wasmparser::Operator::Drop | wasmparser::Operator::Select => Ok(None),
325            // === Reference types ===
326            // non-float operation
327            wasmparser::Operator::TypedSelect { .. }
328            | wasmparser::Operator::TypedSelectMulti { .. } => Ok(None),
329            // locals may contain floats, but get/set/tee are deterministic
330            wasmparser::Operator::LocalGet { .. }
331            | wasmparser::Operator::LocalSet { .. }
332            | wasmparser::Operator::LocalTee { .. } => Ok(None),
333            // non-float operation
334            wasmparser::Operator::GlobalGet { .. }
335            | wasmparser::Operator::GlobalSet { .. }
336            | wasmparser::Operator::I32Load { .. }
337            | wasmparser::Operator::I64Load { .. } => Ok(None),
338            // loading a float from memory is deterministic, even when a
339            //  non-canonical NaN value is loaded
340            wasmparser::Operator::F32Load { .. } | wasmparser::Operator::F64Load { .. } => Ok(None),
341            // non-float operation
342            wasmparser::Operator::I32Load8S { .. }
343            | wasmparser::Operator::I32Load8U { .. }
344            | wasmparser::Operator::I32Load16S { .. }
345            | wasmparser::Operator::I32Load16U { .. }
346            | wasmparser::Operator::I64Load8S { .. }
347            | wasmparser::Operator::I64Load8U { .. }
348            | wasmparser::Operator::I64Load16S { .. }
349            | wasmparser::Operator::I64Load16U { .. }
350            | wasmparser::Operator::I64Load32S { .. }
351            | wasmparser::Operator::I64Load32U { .. }
352            | wasmparser::Operator::I32Store { .. }
353            | wasmparser::Operator::I64Store { .. } => Ok(None),
354            // storing a float to memory is deterministic, even when a
355            //  non-canonical NaN value is stored
356            wasmparser::Operator::F32Store { .. } | wasmparser::Operator::F64Store { .. } => {
357                Ok(None)
358            }
359            // non-float operation
360            wasmparser::Operator::I32Store8 { .. }
361            | wasmparser::Operator::I32Store16 { .. }
362            | wasmparser::Operator::I64Store8 { .. }
363            | wasmparser::Operator::I64Store16 { .. }
364            | wasmparser::Operator::I64Store32 { .. }
365            | wasmparser::Operator::MemorySize { .. }
366            | wasmparser::Operator::MemoryGrow { .. }
367            | wasmparser::Operator::I32Const { .. }
368            | wasmparser::Operator::I64Const { .. } => Ok(None),
369            // constant float values are deterministic, even when they represent
370            //  non-canonical NaN values
371            wasmparser::Operator::F32Const { .. } | wasmparser::Operator::F64Const { .. } => {
372                Ok(None)
373            }
374            // === Reference types ===
375            // non-float operation
376            wasmparser::Operator::RefNull { .. }
377            | wasmparser::Operator::RefIsNull
378            | wasmparser::Operator::RefFunc { .. } => Ok(None),
379            // === Garbage collection ===
380            // non-float operation
381            wasmparser::Operator::RefEq => Ok(None),
382            // === MVP ===
383            // non-float operation
384            wasmparser::Operator::I32Eqz
385            | wasmparser::Operator::I32Eq
386            | wasmparser::Operator::I32Ne
387            | wasmparser::Operator::I32LtS
388            | wasmparser::Operator::I32LtU
389            | wasmparser::Operator::I32GtS
390            | wasmparser::Operator::I32GtU
391            | wasmparser::Operator::I32LeS
392            | wasmparser::Operator::I32LeU
393            | wasmparser::Operator::I32GeS
394            | wasmparser::Operator::I32GeU
395            | wasmparser::Operator::I64Eqz
396            | wasmparser::Operator::I64Eq
397            | wasmparser::Operator::I64Ne
398            | wasmparser::Operator::I64LtS
399            | wasmparser::Operator::I64LtU
400            | wasmparser::Operator::I64GtS
401            | wasmparser::Operator::I64GtU
402            | wasmparser::Operator::I64LeS
403            | wasmparser::Operator::I64LeU
404            | wasmparser::Operator::I64GeS
405            | wasmparser::Operator::I64GeU => Ok(None),
406            // deterministic float operation
407            wasmparser::Operator::F32Eq
408            | wasmparser::Operator::F32Ne
409            | wasmparser::Operator::F32Lt
410            | wasmparser::Operator::F32Gt
411            | wasmparser::Operator::F32Le
412            | wasmparser::Operator::F32Ge
413            | wasmparser::Operator::F64Eq
414            | wasmparser::Operator::F64Ne
415            | wasmparser::Operator::F64Lt
416            | wasmparser::Operator::F64Gt
417            | wasmparser::Operator::F64Le
418            | wasmparser::Operator::F64Ge => Ok(None),
419            // non-float operation
420            wasmparser::Operator::I32Clz
421            | wasmparser::Operator::I32Ctz
422            | wasmparser::Operator::I32Popcnt
423            | wasmparser::Operator::I32Add
424            | wasmparser::Operator::I32Sub
425            | wasmparser::Operator::I32Mul
426            | wasmparser::Operator::I32DivS
427            | wasmparser::Operator::I32DivU
428            | wasmparser::Operator::I32RemS
429            | wasmparser::Operator::I32RemU
430            | wasmparser::Operator::I32And
431            | wasmparser::Operator::I32Or
432            | wasmparser::Operator::I32Xor
433            | wasmparser::Operator::I32Shl
434            | wasmparser::Operator::I32ShrS
435            | wasmparser::Operator::I32ShrU
436            | wasmparser::Operator::I32Rotl
437            | wasmparser::Operator::I32Rotr
438            | wasmparser::Operator::I64Clz
439            | wasmparser::Operator::I64Ctz
440            | wasmparser::Operator::I64Popcnt
441            | wasmparser::Operator::I64Add
442            | wasmparser::Operator::I64Sub
443            | wasmparser::Operator::I64Mul
444            | wasmparser::Operator::I64DivS
445            | wasmparser::Operator::I64DivU
446            | wasmparser::Operator::I64RemS
447            | wasmparser::Operator::I64RemU
448            | wasmparser::Operator::I64And
449            | wasmparser::Operator::I64Or
450            | wasmparser::Operator::I64Xor
451            | wasmparser::Operator::I64Shl
452            | wasmparser::Operator::I64ShrS
453            | wasmparser::Operator::I64ShrU
454            | wasmparser::Operator::I64Rotl
455            | wasmparser::Operator::I64Rotr => Ok(None),
456            // deterministic float operation
457            wasmparser::Operator::F32Abs | wasmparser::Operator::F32Neg => Ok(None),
458            // non-deterministic float operation that may produce any NaN
459            wasmparser::Operator::F32Ceil
460            | wasmparser::Operator::F32Floor
461            | wasmparser::Operator::F32Trunc
462            | wasmparser::Operator::F32Nearest
463            | wasmparser::Operator::F32Sqrt
464            | wasmparser::Operator::F32Add
465            | wasmparser::Operator::F32Sub
466            | wasmparser::Operator::F32Mul
467            | wasmparser::Operator::F32Div
468            | wasmparser::Operator::F32Min
469            | wasmparser::Operator::F32Max => Ok(Some(MaybeNaNKind::F32)),
470            // deterministic float operation
471            wasmparser::Operator::F32Copysign
472            | wasmparser::Operator::F64Abs
473            | wasmparser::Operator::F64Neg => Ok(None),
474            // non-deterministic float operation that may produce any NaN
475            wasmparser::Operator::F64Ceil
476            | wasmparser::Operator::F64Floor
477            | wasmparser::Operator::F64Trunc
478            | wasmparser::Operator::F64Nearest
479            | wasmparser::Operator::F64Sqrt
480            | wasmparser::Operator::F64Add
481            | wasmparser::Operator::F64Sub
482            | wasmparser::Operator::F64Mul
483            | wasmparser::Operator::F64Div
484            | wasmparser::Operator::F64Min
485            | wasmparser::Operator::F64Max => Ok(Some(MaybeNaNKind::F64)),
486            // deterministic float operation
487            wasmparser::Operator::F64Copysign => Ok(None),
488            // non-float operation
489            wasmparser::Operator::I32WrapI64 => Ok(None),
490            // truncate float to int, which traps to be deterministic
491            wasmparser::Operator::I32TruncF32S
492            | wasmparser::Operator::I32TruncF32U
493            | wasmparser::Operator::I32TruncF64S
494            | wasmparser::Operator::I32TruncF64U => Ok(None),
495            // non-float operation
496            wasmparser::Operator::I64ExtendI32S | wasmparser::Operator::I64ExtendI32U => Ok(None),
497            // truncate float to int, which traps to be deterministic
498            wasmparser::Operator::I64TruncF32S
499            | wasmparser::Operator::I64TruncF32U
500            | wasmparser::Operator::I64TruncF64S
501            | wasmparser::Operator::I64TruncF64U => Ok(None),
502            // convert int to float, deterministic
503            wasmparser::Operator::F32ConvertI32S
504            | wasmparser::Operator::F32ConvertI32U
505            | wasmparser::Operator::F32ConvertI64S
506            | wasmparser::Operator::F32ConvertI64U => Ok(None),
507            // non-deterministic float operation that may produce any NaN
508            wasmparser::Operator::F32DemoteF64 => Ok(Some(MaybeNaNKind::F32)),
509            // convert int to float, deterministic
510            wasmparser::Operator::F64ConvertI32S
511            | wasmparser::Operator::F64ConvertI32U
512            | wasmparser::Operator::F64ConvertI64S
513            | wasmparser::Operator::F64ConvertI64U => Ok(None),
514            // non-deterministic float operation that may produce any NaN
515            wasmparser::Operator::F64PromoteF32 => Ok(Some(MaybeNaNKind::F64)),
516            // float <-> int bit cast, deterministic
517            wasmparser::Operator::I32ReinterpretF32
518            | wasmparser::Operator::I64ReinterpretF64
519            | wasmparser::Operator::F32ReinterpretI32
520            | wasmparser::Operator::F64ReinterpretI64 => Ok(None),
521            // === Sign extension ===
522            // non-float operation
523            wasmparser::Operator::I32Extend8S
524            | wasmparser::Operator::I32Extend16S
525            | wasmparser::Operator::I64Extend8S
526            | wasmparser::Operator::I64Extend16S
527            | wasmparser::Operator::I64Extend32S => Ok(None),
528            // === Garbage collection ===
529            // non-float operation
530            wasmparser::Operator::StructNew { .. }
531            | wasmparser::Operator::StructNewDefault { .. }
532            | wasmparser::Operator::StructGet { .. }
533            | wasmparser::Operator::StructGetS { .. }
534            | wasmparser::Operator::StructGetU { .. }
535            | wasmparser::Operator::StructSet { .. }
536            | wasmparser::Operator::ArrayNew { .. }
537            | wasmparser::Operator::ArrayNewDefault { .. }
538            | wasmparser::Operator::ArrayNewFixed { .. }
539            | wasmparser::Operator::ArrayNewData { .. }
540            | wasmparser::Operator::ArrayNewElem { .. }
541            | wasmparser::Operator::ArrayGet { .. }
542            | wasmparser::Operator::ArrayGetS { .. }
543            | wasmparser::Operator::ArrayGetU { .. }
544            | wasmparser::Operator::ArraySet { .. }
545            | wasmparser::Operator::ArrayLen
546            | wasmparser::Operator::ArrayFill { .. }
547            | wasmparser::Operator::ArrayCopy { .. }
548            | wasmparser::Operator::ArrayInitData { .. }
549            | wasmparser::Operator::ArrayInitElem { .. }
550            | wasmparser::Operator::RefTestNonNull { .. }
551            | wasmparser::Operator::RefTestNullable { .. }
552            | wasmparser::Operator::RefCastNonNull { .. }
553            | wasmparser::Operator::RefCastNullable { .. }
554            | wasmparser::Operator::BrOnCast { .. }
555            | wasmparser::Operator::BrOnCastFail { .. }
556            | wasmparser::Operator::AnyConvertExtern
557            | wasmparser::Operator::ExternConvertAny
558            | wasmparser::Operator::RefI31
559            | wasmparser::Operator::I31GetS
560            | wasmparser::Operator::I31GetU => Ok(None),
561            // === Non-trapping float-to-int conversions ===
562            // truncate float to int, which saturates to be deterministic
563            wasmparser::Operator::I32TruncSatF32S
564            | wasmparser::Operator::I32TruncSatF32U
565            | wasmparser::Operator::I32TruncSatF64S
566            | wasmparser::Operator::I32TruncSatF64U
567            | wasmparser::Operator::I64TruncSatF32S
568            | wasmparser::Operator::I64TruncSatF32U
569            | wasmparser::Operator::I64TruncSatF64S
570            | wasmparser::Operator::I64TruncSatF64U => Ok(None),
571            // === Bulk memory ===
572            // non-float operation
573            wasmparser::Operator::MemoryInit { .. }
574            | wasmparser::Operator::DataDrop { .. }
575            | wasmparser::Operator::MemoryCopy { .. }
576            | wasmparser::Operator::MemoryFill { .. }
577            | wasmparser::Operator::TableInit { .. }
578            | wasmparser::Operator::ElemDrop { .. }
579            | wasmparser::Operator::TableCopy { .. } => Ok(None),
580            // === Reference types ===
581            // non-float operation
582            wasmparser::Operator::TableFill { .. }
583            | wasmparser::Operator::TableGet { .. }
584            | wasmparser::Operator::TableSet { .. }
585            | wasmparser::Operator::TableGrow { .. }
586            | wasmparser::Operator::TableSize { .. } => Ok(None),
587            // === Memory control ===
588            // non-float operation
589            wasmparser::Operator::MemoryDiscard { .. } => Ok(None),
590            // === Threads ===
591            // non-deterministic with potential data races
592            wasmparser::Operator::MemoryAtomicNotify { .. }
593            | wasmparser::Operator::MemoryAtomicWait32 { .. }
594            | wasmparser::Operator::MemoryAtomicWait64 { .. }
595            | wasmparser::Operator::AtomicFence
596            | wasmparser::Operator::I32AtomicLoad { .. }
597            | wasmparser::Operator::I64AtomicLoad { .. }
598            | wasmparser::Operator::I32AtomicLoad8U { .. }
599            | wasmparser::Operator::I32AtomicLoad16U { .. }
600            | wasmparser::Operator::I64AtomicLoad8U { .. }
601            | wasmparser::Operator::I64AtomicLoad16U { .. }
602            | wasmparser::Operator::I64AtomicLoad32U { .. }
603            | wasmparser::Operator::I32AtomicStore { .. }
604            | wasmparser::Operator::I64AtomicStore { .. }
605            | wasmparser::Operator::I32AtomicStore8 { .. }
606            | wasmparser::Operator::I32AtomicStore16 { .. }
607            | wasmparser::Operator::I64AtomicStore8 { .. }
608            | wasmparser::Operator::I64AtomicStore16 { .. }
609            | wasmparser::Operator::I64AtomicStore32 { .. }
610            | wasmparser::Operator::I32AtomicRmwAdd { .. }
611            | wasmparser::Operator::I64AtomicRmwAdd { .. }
612            | wasmparser::Operator::I32AtomicRmw8AddU { .. }
613            | wasmparser::Operator::I32AtomicRmw16AddU { .. }
614            | wasmparser::Operator::I64AtomicRmw8AddU { .. }
615            | wasmparser::Operator::I64AtomicRmw16AddU { .. }
616            | wasmparser::Operator::I64AtomicRmw32AddU { .. }
617            | wasmparser::Operator::I32AtomicRmwSub { .. }
618            | wasmparser::Operator::I64AtomicRmwSub { .. }
619            | wasmparser::Operator::I32AtomicRmw8SubU { .. }
620            | wasmparser::Operator::I32AtomicRmw16SubU { .. }
621            | wasmparser::Operator::I64AtomicRmw8SubU { .. }
622            | wasmparser::Operator::I64AtomicRmw16SubU { .. }
623            | wasmparser::Operator::I64AtomicRmw32SubU { .. }
624            | wasmparser::Operator::I32AtomicRmwAnd { .. }
625            | wasmparser::Operator::I64AtomicRmwAnd { .. }
626            | wasmparser::Operator::I32AtomicRmw8AndU { .. }
627            | wasmparser::Operator::I32AtomicRmw16AndU { .. }
628            | wasmparser::Operator::I64AtomicRmw8AndU { .. }
629            | wasmparser::Operator::I64AtomicRmw16AndU { .. }
630            | wasmparser::Operator::I64AtomicRmw32AndU { .. }
631            | wasmparser::Operator::I32AtomicRmwOr { .. }
632            | wasmparser::Operator::I64AtomicRmwOr { .. }
633            | wasmparser::Operator::I32AtomicRmw8OrU { .. }
634            | wasmparser::Operator::I32AtomicRmw16OrU { .. }
635            | wasmparser::Operator::I64AtomicRmw8OrU { .. }
636            | wasmparser::Operator::I64AtomicRmw16OrU { .. }
637            | wasmparser::Operator::I64AtomicRmw32OrU { .. }
638            | wasmparser::Operator::I32AtomicRmwXor { .. }
639            | wasmparser::Operator::I64AtomicRmwXor { .. }
640            | wasmparser::Operator::I32AtomicRmw8XorU { .. }
641            | wasmparser::Operator::I32AtomicRmw16XorU { .. }
642            | wasmparser::Operator::I64AtomicRmw8XorU { .. }
643            | wasmparser::Operator::I64AtomicRmw16XorU { .. }
644            | wasmparser::Operator::I64AtomicRmw32XorU { .. }
645            | wasmparser::Operator::I32AtomicRmwXchg { .. }
646            | wasmparser::Operator::I64AtomicRmwXchg { .. }
647            | wasmparser::Operator::I32AtomicRmw8XchgU { .. }
648            | wasmparser::Operator::I32AtomicRmw16XchgU { .. }
649            | wasmparser::Operator::I64AtomicRmw8XchgU { .. }
650            | wasmparser::Operator::I64AtomicRmw16XchgU { .. }
651            | wasmparser::Operator::I64AtomicRmw32XchgU { .. }
652            | wasmparser::Operator::I32AtomicRmwCmpxchg { .. }
653            | wasmparser::Operator::I64AtomicRmwCmpxchg { .. }
654            | wasmparser::Operator::I32AtomicRmw8CmpxchgU { .. }
655            | wasmparser::Operator::I32AtomicRmw16CmpxchgU { .. }
656            | wasmparser::Operator::I64AtomicRmw8CmpxchgU { .. }
657            | wasmparser::Operator::I64AtomicRmw16CmpxchgU { .. }
658            | wasmparser::Operator::I64AtomicRmw32CmpxchgU { .. } => {
659                Err(NonDeterministicWasmFeature::Threads)
660            }
661            // === Shared-everything threads ===
662            // non-deterministic with potential data races
663            wasmparser::Operator::GlobalAtomicGet { .. }
664            | wasmparser::Operator::GlobalAtomicSet { .. }
665            | wasmparser::Operator::GlobalAtomicRmwAdd { .. }
666            | wasmparser::Operator::GlobalAtomicRmwSub { .. }
667            | wasmparser::Operator::GlobalAtomicRmwAnd { .. }
668            | wasmparser::Operator::GlobalAtomicRmwOr { .. }
669            | wasmparser::Operator::GlobalAtomicRmwXor { .. }
670            | wasmparser::Operator::GlobalAtomicRmwXchg { .. }
671            | wasmparser::Operator::GlobalAtomicRmwCmpxchg { .. }
672            | wasmparser::Operator::TableAtomicGet { .. }
673            | wasmparser::Operator::TableAtomicSet { .. }
674            | wasmparser::Operator::TableAtomicRmwXchg { .. }
675            | wasmparser::Operator::TableAtomicRmwCmpxchg { .. }
676            | wasmparser::Operator::StructAtomicGet { .. }
677            | wasmparser::Operator::StructAtomicGetS { .. }
678            | wasmparser::Operator::StructAtomicGetU { .. }
679            | wasmparser::Operator::StructAtomicSet { .. }
680            | wasmparser::Operator::StructAtomicRmwAdd { .. }
681            | wasmparser::Operator::StructAtomicRmwSub { .. }
682            | wasmparser::Operator::StructAtomicRmwAnd { .. }
683            | wasmparser::Operator::StructAtomicRmwOr { .. }
684            | wasmparser::Operator::StructAtomicRmwXor { .. }
685            | wasmparser::Operator::StructAtomicRmwXchg { .. }
686            | wasmparser::Operator::StructAtomicRmwCmpxchg { .. }
687            | wasmparser::Operator::ArrayAtomicGet { .. }
688            | wasmparser::Operator::ArrayAtomicGetS { .. }
689            | wasmparser::Operator::ArrayAtomicGetU { .. }
690            | wasmparser::Operator::ArrayAtomicSet { .. }
691            | wasmparser::Operator::ArrayAtomicRmwAdd { .. }
692            | wasmparser::Operator::ArrayAtomicRmwSub { .. }
693            | wasmparser::Operator::ArrayAtomicRmwAnd { .. }
694            | wasmparser::Operator::ArrayAtomicRmwOr { .. }
695            | wasmparser::Operator::ArrayAtomicRmwXor { .. }
696            | wasmparser::Operator::ArrayAtomicRmwXchg { .. }
697            | wasmparser::Operator::ArrayAtomicRmwCmpxchg { .. }
698            | wasmparser::Operator::RefI31Shared => {
699                Err(NonDeterministicWasmFeature::SharedEverythingThreads)
700            }
701            // === SIMD ===
702            // non-float operation, memory loads/stores are deterministic
703            wasmparser::Operator::V128Load { .. }
704            | wasmparser::Operator::V128Load8x8S { .. }
705            | wasmparser::Operator::V128Load8x8U { .. }
706            | wasmparser::Operator::V128Load16x4S { .. }
707            | wasmparser::Operator::V128Load16x4U { .. }
708            | wasmparser::Operator::V128Load32x2S { .. }
709            | wasmparser::Operator::V128Load32x2U { .. }
710            | wasmparser::Operator::V128Load8Splat { .. }
711            | wasmparser::Operator::V128Load16Splat { .. }
712            | wasmparser::Operator::V128Load32Splat { .. }
713            | wasmparser::Operator::V128Load64Splat { .. }
714            | wasmparser::Operator::V128Load32Zero { .. }
715            | wasmparser::Operator::V128Load64Zero { .. }
716            | wasmparser::Operator::V128Store { .. }
717            | wasmparser::Operator::V128Load8Lane { .. }
718            | wasmparser::Operator::V128Load16Lane { .. }
719            | wasmparser::Operator::V128Load32Lane { .. }
720            | wasmparser::Operator::V128Load64Lane { .. }
721            | wasmparser::Operator::V128Store8Lane { .. }
722            | wasmparser::Operator::V128Store16Lane { .. }
723            | wasmparser::Operator::V128Store32Lane { .. }
724            | wasmparser::Operator::V128Store64Lane { .. } => Ok(None),
725            // constant values are deterministic, even when they represent
726            //  non-canonical NaN values
727            wasmparser::Operator::V128Const { .. } => Ok(None),
728            // non-float operation
729            wasmparser::Operator::I8x16Shuffle { .. }
730            | wasmparser::Operator::I8x16ExtractLaneS { .. }
731            | wasmparser::Operator::I8x16ExtractLaneU { .. }
732            | wasmparser::Operator::I8x16ReplaceLane { .. }
733            | wasmparser::Operator::I16x8ExtractLaneS { .. }
734            | wasmparser::Operator::I16x8ExtractLaneU { .. }
735            | wasmparser::Operator::I16x8ReplaceLane { .. }
736            | wasmparser::Operator::I32x4ExtractLane { .. }
737            | wasmparser::Operator::I32x4ReplaceLane { .. }
738            | wasmparser::Operator::I64x2ExtractLane { .. }
739            | wasmparser::Operator::I64x2ReplaceLane { .. } => Ok(None),
740            // extracting or replacing lanes is deterministic
741            wasmparser::Operator::F32x4ExtractLane { .. }
742            | wasmparser::Operator::F32x4ReplaceLane { .. }
743            | wasmparser::Operator::F64x2ExtractLane { .. }
744            | wasmparser::Operator::F64x2ReplaceLane { .. } => Ok(None),
745            // non-float operation
746            wasmparser::Operator::I8x16Swizzle
747            | wasmparser::Operator::I8x16Splat
748            | wasmparser::Operator::I16x8Splat
749            | wasmparser::Operator::I32x4Splat
750            | wasmparser::Operator::I64x2Splat => Ok(None),
751            // splatting is deterministic, even when a non-canonical NaN value
752            //  is splatted
753            wasmparser::Operator::F32x4Splat | wasmparser::Operator::F64x2Splat => Ok(None),
754            // non-float operation
755            wasmparser::Operator::I8x16Eq
756            | wasmparser::Operator::I8x16Ne
757            | wasmparser::Operator::I8x16LtS
758            | wasmparser::Operator::I8x16LtU
759            | wasmparser::Operator::I8x16GtS
760            | wasmparser::Operator::I8x16GtU
761            | wasmparser::Operator::I8x16LeS
762            | wasmparser::Operator::I8x16LeU
763            | wasmparser::Operator::I8x16GeS
764            | wasmparser::Operator::I8x16GeU
765            | wasmparser::Operator::I16x8Eq
766            | wasmparser::Operator::I16x8Ne
767            | wasmparser::Operator::I16x8LtS
768            | wasmparser::Operator::I16x8LtU
769            | wasmparser::Operator::I16x8GtS
770            | wasmparser::Operator::I16x8GtU
771            | wasmparser::Operator::I16x8LeS
772            | wasmparser::Operator::I16x8LeU
773            | wasmparser::Operator::I16x8GeS
774            | wasmparser::Operator::I16x8GeU
775            | wasmparser::Operator::I32x4Eq
776            | wasmparser::Operator::I32x4Ne
777            | wasmparser::Operator::I32x4LtS
778            | wasmparser::Operator::I32x4LtU
779            | wasmparser::Operator::I32x4GtS
780            | wasmparser::Operator::I32x4GtU
781            | wasmparser::Operator::I32x4LeS
782            | wasmparser::Operator::I32x4LeU
783            | wasmparser::Operator::I32x4GeS
784            | wasmparser::Operator::I32x4GeU
785            | wasmparser::Operator::I64x2Eq
786            | wasmparser::Operator::I64x2Ne
787            | wasmparser::Operator::I64x2LtS
788            | wasmparser::Operator::I64x2GtS
789            | wasmparser::Operator::I64x2LeS
790            | wasmparser::Operator::I64x2GeS => Ok(None),
791            // deterministic float operation
792            wasmparser::Operator::F32x4Eq
793            | wasmparser::Operator::F32x4Ne
794            | wasmparser::Operator::F32x4Lt
795            | wasmparser::Operator::F32x4Gt
796            | wasmparser::Operator::F32x4Le
797            | wasmparser::Operator::F32x4Ge
798            | wasmparser::Operator::F64x2Eq
799            | wasmparser::Operator::F64x2Ne
800            | wasmparser::Operator::F64x2Lt
801            | wasmparser::Operator::F64x2Gt
802            | wasmparser::Operator::F64x2Le
803            | wasmparser::Operator::F64x2Ge => Ok(None),
804            // non-float operation
805            wasmparser::Operator::V128Not
806            | wasmparser::Operator::V128And
807            | wasmparser::Operator::V128AndNot
808            | wasmparser::Operator::V128Or
809            | wasmparser::Operator::V128Xor
810            | wasmparser::Operator::V128Bitselect
811            | wasmparser::Operator::V128AnyTrue
812            | wasmparser::Operator::I8x16Abs
813            | wasmparser::Operator::I8x16Neg
814            | wasmparser::Operator::I8x16Popcnt
815            | wasmparser::Operator::I8x16AllTrue
816            | wasmparser::Operator::I8x16Bitmask
817            | wasmparser::Operator::I8x16NarrowI16x8S
818            | wasmparser::Operator::I8x16NarrowI16x8U
819            | wasmparser::Operator::I8x16Shl
820            | wasmparser::Operator::I8x16ShrS
821            | wasmparser::Operator::I8x16ShrU
822            | wasmparser::Operator::I8x16Add
823            | wasmparser::Operator::I8x16AddSatS
824            | wasmparser::Operator::I8x16AddSatU
825            | wasmparser::Operator::I8x16Sub
826            | wasmparser::Operator::I8x16SubSatS
827            | wasmparser::Operator::I8x16SubSatU
828            | wasmparser::Operator::I8x16MinS
829            | wasmparser::Operator::I8x16MinU
830            | wasmparser::Operator::I8x16MaxS
831            | wasmparser::Operator::I8x16MaxU
832            | wasmparser::Operator::I8x16AvgrU
833            | wasmparser::Operator::I16x8ExtAddPairwiseI8x16S
834            | wasmparser::Operator::I16x8ExtAddPairwiseI8x16U
835            | wasmparser::Operator::I16x8Abs
836            | wasmparser::Operator::I16x8Neg
837            | wasmparser::Operator::I16x8Q15MulrSatS
838            | wasmparser::Operator::I16x8AllTrue
839            | wasmparser::Operator::I16x8Bitmask
840            | wasmparser::Operator::I16x8NarrowI32x4S
841            | wasmparser::Operator::I16x8NarrowI32x4U
842            | wasmparser::Operator::I16x8ExtendLowI8x16S
843            | wasmparser::Operator::I16x8ExtendHighI8x16S
844            | wasmparser::Operator::I16x8ExtendLowI8x16U
845            | wasmparser::Operator::I16x8ExtendHighI8x16U
846            | wasmparser::Operator::I16x8Shl
847            | wasmparser::Operator::I16x8ShrS
848            | wasmparser::Operator::I16x8ShrU
849            | wasmparser::Operator::I16x8Add
850            | wasmparser::Operator::I16x8AddSatS
851            | wasmparser::Operator::I16x8AddSatU
852            | wasmparser::Operator::I16x8Sub
853            | wasmparser::Operator::I16x8SubSatS
854            | wasmparser::Operator::I16x8SubSatU
855            | wasmparser::Operator::I16x8Mul
856            | wasmparser::Operator::I16x8MinS
857            | wasmparser::Operator::I16x8MinU
858            | wasmparser::Operator::I16x8MaxS
859            | wasmparser::Operator::I16x8MaxU
860            | wasmparser::Operator::I16x8AvgrU
861            | wasmparser::Operator::I16x8ExtMulLowI8x16S
862            | wasmparser::Operator::I16x8ExtMulHighI8x16S
863            | wasmparser::Operator::I16x8ExtMulLowI8x16U
864            | wasmparser::Operator::I16x8ExtMulHighI8x16U
865            | wasmparser::Operator::I32x4ExtAddPairwiseI16x8S
866            | wasmparser::Operator::I32x4ExtAddPairwiseI16x8U
867            | wasmparser::Operator::I32x4Abs
868            | wasmparser::Operator::I32x4Neg
869            | wasmparser::Operator::I32x4AllTrue
870            | wasmparser::Operator::I32x4Bitmask
871            | wasmparser::Operator::I32x4ExtendLowI16x8S
872            | wasmparser::Operator::I32x4ExtendHighI16x8S
873            | wasmparser::Operator::I32x4ExtendLowI16x8U
874            | wasmparser::Operator::I32x4ExtendHighI16x8U
875            | wasmparser::Operator::I32x4Shl
876            | wasmparser::Operator::I32x4ShrS
877            | wasmparser::Operator::I32x4ShrU
878            | wasmparser::Operator::I32x4Add
879            | wasmparser::Operator::I32x4Sub
880            | wasmparser::Operator::I32x4Mul
881            | wasmparser::Operator::I32x4MinS
882            | wasmparser::Operator::I32x4MinU
883            | wasmparser::Operator::I32x4MaxS
884            | wasmparser::Operator::I32x4MaxU
885            | wasmparser::Operator::I32x4DotI16x8S
886            | wasmparser::Operator::I32x4ExtMulLowI16x8S
887            | wasmparser::Operator::I32x4ExtMulHighI16x8S
888            | wasmparser::Operator::I32x4ExtMulLowI16x8U
889            | wasmparser::Operator::I32x4ExtMulHighI16x8U
890            | wasmparser::Operator::I64x2Abs
891            | wasmparser::Operator::I64x2Neg
892            | wasmparser::Operator::I64x2AllTrue
893            | wasmparser::Operator::I64x2Bitmask
894            | wasmparser::Operator::I64x2ExtendLowI32x4S
895            | wasmparser::Operator::I64x2ExtendHighI32x4S
896            | wasmparser::Operator::I64x2ExtendLowI32x4U
897            | wasmparser::Operator::I64x2ExtendHighI32x4U
898            | wasmparser::Operator::I64x2Shl
899            | wasmparser::Operator::I64x2ShrS
900            | wasmparser::Operator::I64x2ShrU
901            | wasmparser::Operator::I64x2Add
902            | wasmparser::Operator::I64x2Sub
903            | wasmparser::Operator::I64x2Mul
904            | wasmparser::Operator::I64x2ExtMulLowI32x4S
905            | wasmparser::Operator::I64x2ExtMulHighI32x4S
906            | wasmparser::Operator::I64x2ExtMulLowI32x4U
907            | wasmparser::Operator::I64x2ExtMulHighI32x4U => Ok(None),
908            // non-deterministic float operation that may produce any NaN
909            wasmparser::Operator::F32x4Ceil
910            | wasmparser::Operator::F32x4Floor
911            | wasmparser::Operator::F32x4Trunc
912            | wasmparser::Operator::F32x4Nearest => Ok(Some(MaybeNaNKind::F32x4)),
913            // deterministic float operation
914            wasmparser::Operator::F32x4Abs | wasmparser::Operator::F32x4Neg => Ok(None),
915            // non-deterministic float operation that may produce any NaN
916            wasmparser::Operator::F32x4Sqrt
917            | wasmparser::Operator::F32x4Add
918            | wasmparser::Operator::F32x4Sub
919            | wasmparser::Operator::F32x4Mul
920            | wasmparser::Operator::F32x4Div
921            | wasmparser::Operator::F32x4Min
922            | wasmparser::Operator::F32x4Max => Ok(Some(MaybeNaNKind::F32x4)),
923            // deterministic float operation
924            wasmparser::Operator::F32x4PMin | wasmparser::Operator::F32x4PMax => Ok(None),
925            // non-deterministic float operation that may produce any NaN
926            wasmparser::Operator::F64x2Ceil
927            | wasmparser::Operator::F64x2Floor
928            | wasmparser::Operator::F64x2Trunc
929            | wasmparser::Operator::F64x2Nearest => Ok(Some(MaybeNaNKind::F64x2)),
930            // deterministic float operation
931            wasmparser::Operator::F64x2Abs | wasmparser::Operator::F64x2Neg => Ok(None),
932            // non-deterministic float operation that may produce any NaN
933            wasmparser::Operator::F64x2Sqrt
934            | wasmparser::Operator::F64x2Add
935            | wasmparser::Operator::F64x2Sub
936            | wasmparser::Operator::F64x2Mul
937            | wasmparser::Operator::F64x2Div
938            | wasmparser::Operator::F64x2Min
939            | wasmparser::Operator::F64x2Max => Ok(Some(MaybeNaNKind::F64x2)),
940            // deterministic float operation
941            wasmparser::Operator::F64x2PMin | wasmparser::Operator::F64x2PMax => Ok(None),
942            // truncate float to int, which saturates to be deterministic
943            wasmparser::Operator::I32x4TruncSatF32x4S
944            | wasmparser::Operator::I32x4TruncSatF32x4U => Ok(None),
945            // convert int to float, deterministic
946            wasmparser::Operator::F32x4ConvertI32x4S | wasmparser::Operator::F32x4ConvertI32x4U => {
947                Ok(None)
948            }
949            // truncate float to int, which saturates to be deterministic
950            wasmparser::Operator::I32x4TruncSatF64x2SZero
951            | wasmparser::Operator::I32x4TruncSatF64x2UZero => Ok(None),
952            // convert int to float, deterministic
953            wasmparser::Operator::F64x2ConvertLowI32x4S
954            | wasmparser::Operator::F64x2ConvertLowI32x4U => Ok(None),
955            // non-deterministic float operation that may produce any NaN
956            wasmparser::Operator::F32x4DemoteF64x2Zero => Ok(Some(MaybeNaNKind::F32x4)),
957            wasmparser::Operator::F64x2PromoteLowF32x4 => Ok(Some(MaybeNaNKind::F64x2)),
958            // === Relaxed SIMD ===
959            // non-deterministic, result may be platform-dependent
960            wasmparser::Operator::I8x16RelaxedSwizzle
961            | wasmparser::Operator::I32x4RelaxedTruncF32x4S
962            | wasmparser::Operator::I32x4RelaxedTruncF32x4U
963            | wasmparser::Operator::I32x4RelaxedTruncF64x2SZero
964            | wasmparser::Operator::I32x4RelaxedTruncF64x2UZero
965            | wasmparser::Operator::F32x4RelaxedMadd
966            | wasmparser::Operator::F32x4RelaxedNmadd
967            | wasmparser::Operator::F64x2RelaxedMadd
968            | wasmparser::Operator::F64x2RelaxedNmadd
969            | wasmparser::Operator::I8x16RelaxedLaneselect
970            | wasmparser::Operator::I16x8RelaxedLaneselect
971            | wasmparser::Operator::I32x4RelaxedLaneselect
972            | wasmparser::Operator::I64x2RelaxedLaneselect
973            | wasmparser::Operator::F32x4RelaxedMin
974            | wasmparser::Operator::F32x4RelaxedMax
975            | wasmparser::Operator::F64x2RelaxedMin
976            | wasmparser::Operator::F64x2RelaxedMax
977            | wasmparser::Operator::I16x8RelaxedQ15mulrS
978            | wasmparser::Operator::I16x8RelaxedDotI8x16I7x16S
979            | wasmparser::Operator::I32x4RelaxedDotI8x16I7x16AddS => {
980                Err(NonDeterministicWasmFeature::RelaxedSimd)
981            }
982            // === Typed function references ===
983            // non-float operation
984            wasmparser::Operator::CallRef { .. }
985            | wasmparser::Operator::ReturnCallRef { .. }
986            | wasmparser::Operator::RefAsNonNull
987            | wasmparser::Operator::BrOnNull { .. }
988            | wasmparser::Operator::BrOnNonNull { .. } => Ok(None),
989            // === Stack switching ===
990            // non-float operation
991            wasmparser::Operator::ContNew { .. }
992            | wasmparser::Operator::ContBind { .. }
993            | wasmparser::Operator::Suspend { .. }
994            | wasmparser::Operator::Resume { .. }
995            | wasmparser::Operator::ResumeThrow { .. }
996            | wasmparser::Operator::Switch { .. } => Ok(None),
997            // === Wide Arithmetic ===
998            // non-float operation
999            wasmparser::Operator::I64Add128
1000            | wasmparser::Operator::I64Sub128
1001            | wasmparser::Operator::I64MulWideS
1002            | wasmparser::Operator::I64MulWideU => Ok(None),
1003            // === FIXME ===
1004            #[cfg(not(test))]
1005            #[expect(clippy::panic)]
1006            _ => panic!("unsupported instruction"),
1007            #[cfg(test)]
1008            #[expect(unsafe_code)]
1009            _ => {
1010                unsafe extern "C" {
1011                    fn nan_canonicaliser_unhandled_operator() -> !;
1012                }
1013                unsafe { nan_canonicaliser_unhandled_operator() }
1014            }
1015        }
1016    }
1017}
1018
1019#[derive(Copy, Clone)]
1020enum MaybeNaNKind {
1021    F32,
1022    F64,
1023    F32x4,
1024    F64x2,
1025}
1026
1027#[derive(Debug, thiserror::Error)]
1028enum NonDeterministicWasmFeature {
1029    #[error(
1030        "WASM uses the non-deterministic relaxed-simd feature, which may produce \
1031         platform-dependent results"
1032    )]
1033    RelaxedSimd,
1034    #[error("WASM uses the non-deterministic threads feature, which may produce data races")]
1035    Threads,
1036    #[error(
1037        "WASM uses the non-deterministic shared-everything threads feature, which may produce \
1038         data races"
1039    )]
1040    SharedEverythingThreads,
1041}