numcodecs_wasm_host_reproducible/transform/
nan.rs1use std::collections::VecDeque;
2
3use anyhow::{anyhow, Error};
4use wasm_encoder::reencode::{self, Reencode};
5
6pub 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)] 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 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 wasm_encoder::Instruction::LocalSet(stash),
184 wasm_encoder::Instruction::F32Const(f32::from_bits(Self::CANON_NAN_B32)),
187 wasm_encoder::Instruction::LocalGet(stash),
189 wasm_encoder::Instruction::LocalGet(stash),
191 wasm_encoder::Instruction::LocalGet(stash),
193 wasm_encoder::Instruction::F32Ne,
196 wasm_encoder::Instruction::Select,
202 ],
204 MaybeNaNKind::F64 => [
205 wasm_encoder::Instruction::LocalSet(stash),
207 wasm_encoder::Instruction::F64Const(f64::from_bits(Self::CANON_NAN_B64)),
210 wasm_encoder::Instruction::LocalGet(stash),
212 wasm_encoder::Instruction::LocalGet(stash),
214 wasm_encoder::Instruction::LocalGet(stash),
216 wasm_encoder::Instruction::F64Ne,
219 wasm_encoder::Instruction::Select,
225 ],
227 MaybeNaNKind::F32x4 => [
228 wasm_encoder::Instruction::LocalSet(stash),
230 wasm_encoder::Instruction::V128Const(i128::from_le_bytes(
233 Self::CANON_NAN_B32X4.to_le_bytes(),
234 )),
235 wasm_encoder::Instruction::LocalGet(stash),
237 wasm_encoder::Instruction::LocalGet(stash),
239 wasm_encoder::Instruction::LocalGet(stash),
241 wasm_encoder::Instruction::F32x4Ne,
244 wasm_encoder::Instruction::V128Bitselect,
250 ],
252 MaybeNaNKind::F64x2 => [
253 wasm_encoder::Instruction::LocalSet(stash),
255 wasm_encoder::Instruction::V128Const(i128::from_le_bytes(
258 Self::CANON_NAN_B64X2.to_le_bytes(),
259 )),
260 wasm_encoder::Instruction::LocalGet(stash),
262 wasm_encoder::Instruction::LocalGet(stash),
264 wasm_encoder::Instruction::LocalGet(stash),
266 wasm_encoder::Instruction::F64x2Ne,
269 wasm_encoder::Instruction::V128Bitselect,
275 ],
277 }
278 }
279
280 #[expect(clippy::too_many_lines)]
281 fn may_produce_non_deterministic_nan(
282 instr: &wasmparser::Operator,
283 ) -> Result<Option<MaybeNaNKind>, NonDeterministicWasmFeature> {
284 match instr {
285 wasmparser::Operator::Unreachable
288 | wasmparser::Operator::Nop
289 | wasmparser::Operator::Block { .. }
290 | wasmparser::Operator::Loop { .. }
291 | wasmparser::Operator::If { .. }
292 | wasmparser::Operator::Else => Ok(None),
293 wasmparser::Operator::TryTable { .. }
296 | wasmparser::Operator::Throw { .. }
297 | wasmparser::Operator::ThrowRef => Ok(None),
298 wasmparser::Operator::Try { .. }
301 | wasmparser::Operator::Catch { .. }
302 | wasmparser::Operator::Rethrow { .. }
303 | wasmparser::Operator::Delegate { .. }
304 | wasmparser::Operator::CatchAll => Ok(None),
305 wasmparser::Operator::End
308 | wasmparser::Operator::Br { .. }
309 | wasmparser::Operator::BrIf { .. }
310 | wasmparser::Operator::BrTable { .. }
311 | wasmparser::Operator::Return
312 | wasmparser::Operator::Call { .. }
313 | wasmparser::Operator::CallIndirect { .. } => Ok(None),
314 wasmparser::Operator::ReturnCall { .. }
317 | wasmparser::Operator::ReturnCallIndirect { .. } => Ok(None),
318 wasmparser::Operator::Drop | wasmparser::Operator::Select => Ok(None),
321 wasmparser::Operator::TypedSelect { .. } => Ok(None),
324 wasmparser::Operator::LocalGet { .. }
326 | wasmparser::Operator::LocalSet { .. }
327 | wasmparser::Operator::LocalTee { .. } => Ok(None),
328 wasmparser::Operator::GlobalGet { .. }
330 | wasmparser::Operator::GlobalSet { .. }
331 | wasmparser::Operator::I32Load { .. }
332 | wasmparser::Operator::I64Load { .. } => Ok(None),
333 wasmparser::Operator::F32Load { .. } | wasmparser::Operator::F64Load { .. } => Ok(None),
336 wasmparser::Operator::I32Load8S { .. }
338 | wasmparser::Operator::I32Load8U { .. }
339 | wasmparser::Operator::I32Load16S { .. }
340 | wasmparser::Operator::I32Load16U { .. }
341 | wasmparser::Operator::I64Load8S { .. }
342 | wasmparser::Operator::I64Load8U { .. }
343 | wasmparser::Operator::I64Load16S { .. }
344 | wasmparser::Operator::I64Load16U { .. }
345 | wasmparser::Operator::I64Load32S { .. }
346 | wasmparser::Operator::I64Load32U { .. }
347 | wasmparser::Operator::I32Store { .. }
348 | wasmparser::Operator::I64Store { .. } => Ok(None),
349 wasmparser::Operator::F32Store { .. } | wasmparser::Operator::F64Store { .. } => {
352 Ok(None)
353 }
354 wasmparser::Operator::I32Store8 { .. }
356 | wasmparser::Operator::I32Store16 { .. }
357 | wasmparser::Operator::I64Store8 { .. }
358 | wasmparser::Operator::I64Store16 { .. }
359 | wasmparser::Operator::I64Store32 { .. }
360 | wasmparser::Operator::MemorySize { .. }
361 | wasmparser::Operator::MemoryGrow { .. }
362 | wasmparser::Operator::I32Const { .. }
363 | wasmparser::Operator::I64Const { .. } => Ok(None),
364 wasmparser::Operator::F32Const { .. } | wasmparser::Operator::F64Const { .. } => {
367 Ok(None)
368 }
369 wasmparser::Operator::RefNull { .. }
372 | wasmparser::Operator::RefIsNull
373 | wasmparser::Operator::RefFunc { .. } => Ok(None),
374 wasmparser::Operator::RefEq => Ok(None),
377 wasmparser::Operator::I32Eqz
380 | wasmparser::Operator::I32Eq
381 | wasmparser::Operator::I32Ne
382 | wasmparser::Operator::I32LtS
383 | wasmparser::Operator::I32LtU
384 | wasmparser::Operator::I32GtS
385 | wasmparser::Operator::I32GtU
386 | wasmparser::Operator::I32LeS
387 | wasmparser::Operator::I32LeU
388 | wasmparser::Operator::I32GeS
389 | wasmparser::Operator::I32GeU
390 | wasmparser::Operator::I64Eqz
391 | wasmparser::Operator::I64Eq
392 | wasmparser::Operator::I64Ne
393 | wasmparser::Operator::I64LtS
394 | wasmparser::Operator::I64LtU
395 | wasmparser::Operator::I64GtS
396 | wasmparser::Operator::I64GtU
397 | wasmparser::Operator::I64LeS
398 | wasmparser::Operator::I64LeU
399 | wasmparser::Operator::I64GeS
400 | wasmparser::Operator::I64GeU => Ok(None),
401 wasmparser::Operator::F32Eq
403 | wasmparser::Operator::F32Ne
404 | wasmparser::Operator::F32Lt
405 | wasmparser::Operator::F32Gt
406 | wasmparser::Operator::F32Le
407 | wasmparser::Operator::F32Ge
408 | wasmparser::Operator::F64Eq
409 | wasmparser::Operator::F64Ne
410 | wasmparser::Operator::F64Lt
411 | wasmparser::Operator::F64Gt
412 | wasmparser::Operator::F64Le
413 | wasmparser::Operator::F64Ge => Ok(None),
414 wasmparser::Operator::I32Clz
416 | wasmparser::Operator::I32Ctz
417 | wasmparser::Operator::I32Popcnt
418 | wasmparser::Operator::I32Add
419 | wasmparser::Operator::I32Sub
420 | wasmparser::Operator::I32Mul
421 | wasmparser::Operator::I32DivS
422 | wasmparser::Operator::I32DivU
423 | wasmparser::Operator::I32RemS
424 | wasmparser::Operator::I32RemU
425 | wasmparser::Operator::I32And
426 | wasmparser::Operator::I32Or
427 | wasmparser::Operator::I32Xor
428 | wasmparser::Operator::I32Shl
429 | wasmparser::Operator::I32ShrS
430 | wasmparser::Operator::I32ShrU
431 | wasmparser::Operator::I32Rotl
432 | wasmparser::Operator::I32Rotr
433 | wasmparser::Operator::I64Clz
434 | wasmparser::Operator::I64Ctz
435 | wasmparser::Operator::I64Popcnt
436 | wasmparser::Operator::I64Add
437 | wasmparser::Operator::I64Sub
438 | wasmparser::Operator::I64Mul
439 | wasmparser::Operator::I64DivS
440 | wasmparser::Operator::I64DivU
441 | wasmparser::Operator::I64RemS
442 | wasmparser::Operator::I64RemU
443 | wasmparser::Operator::I64And
444 | wasmparser::Operator::I64Or
445 | wasmparser::Operator::I64Xor
446 | wasmparser::Operator::I64Shl
447 | wasmparser::Operator::I64ShrS
448 | wasmparser::Operator::I64ShrU
449 | wasmparser::Operator::I64Rotl
450 | wasmparser::Operator::I64Rotr => Ok(None),
451 wasmparser::Operator::F32Abs | wasmparser::Operator::F32Neg => Ok(None),
453 wasmparser::Operator::F32Ceil
455 | wasmparser::Operator::F32Floor
456 | wasmparser::Operator::F32Trunc
457 | wasmparser::Operator::F32Nearest
458 | wasmparser::Operator::F32Sqrt
459 | wasmparser::Operator::F32Add
460 | wasmparser::Operator::F32Sub
461 | wasmparser::Operator::F32Mul
462 | wasmparser::Operator::F32Div
463 | wasmparser::Operator::F32Min
464 | wasmparser::Operator::F32Max => Ok(Some(MaybeNaNKind::F32)),
465 wasmparser::Operator::F32Copysign
467 | wasmparser::Operator::F64Abs
468 | wasmparser::Operator::F64Neg => Ok(None),
469 wasmparser::Operator::F64Ceil
471 | wasmparser::Operator::F64Floor
472 | wasmparser::Operator::F64Trunc
473 | wasmparser::Operator::F64Nearest
474 | wasmparser::Operator::F64Sqrt
475 | wasmparser::Operator::F64Add
476 | wasmparser::Operator::F64Sub
477 | wasmparser::Operator::F64Mul
478 | wasmparser::Operator::F64Div
479 | wasmparser::Operator::F64Min
480 | wasmparser::Operator::F64Max => Ok(Some(MaybeNaNKind::F64)),
481 wasmparser::Operator::F64Copysign => Ok(None),
483 wasmparser::Operator::I32WrapI64 => Ok(None),
485 wasmparser::Operator::I32TruncF32S
487 | wasmparser::Operator::I32TruncF32U
488 | wasmparser::Operator::I32TruncF64S
489 | wasmparser::Operator::I32TruncF64U => Ok(None),
490 wasmparser::Operator::I64ExtendI32S | wasmparser::Operator::I64ExtendI32U => Ok(None),
492 wasmparser::Operator::I64TruncF32S
494 | wasmparser::Operator::I64TruncF32U
495 | wasmparser::Operator::I64TruncF64S
496 | wasmparser::Operator::I64TruncF64U => Ok(None),
497 wasmparser::Operator::F32ConvertI32S
499 | wasmparser::Operator::F32ConvertI32U
500 | wasmparser::Operator::F32ConvertI64S
501 | wasmparser::Operator::F32ConvertI64U => Ok(None),
502 wasmparser::Operator::F32DemoteF64 => Ok(Some(MaybeNaNKind::F32)),
504 wasmparser::Operator::F64ConvertI32S
506 | wasmparser::Operator::F64ConvertI32U
507 | wasmparser::Operator::F64ConvertI64S
508 | wasmparser::Operator::F64ConvertI64U => Ok(None),
509 wasmparser::Operator::F64PromoteF32 => Ok(Some(MaybeNaNKind::F64)),
511 wasmparser::Operator::I32ReinterpretF32
513 | wasmparser::Operator::I64ReinterpretF64
514 | wasmparser::Operator::F32ReinterpretI32
515 | wasmparser::Operator::F64ReinterpretI64 => Ok(None),
516 wasmparser::Operator::I32Extend8S
519 | wasmparser::Operator::I32Extend16S
520 | wasmparser::Operator::I64Extend8S
521 | wasmparser::Operator::I64Extend16S
522 | wasmparser::Operator::I64Extend32S => Ok(None),
523 wasmparser::Operator::StructNew { .. }
526 | wasmparser::Operator::StructNewDefault { .. }
527 | wasmparser::Operator::StructGet { .. }
528 | wasmparser::Operator::StructGetS { .. }
529 | wasmparser::Operator::StructGetU { .. }
530 | wasmparser::Operator::StructSet { .. }
531 | wasmparser::Operator::ArrayNew { .. }
532 | wasmparser::Operator::ArrayNewDefault { .. }
533 | wasmparser::Operator::ArrayNewFixed { .. }
534 | wasmparser::Operator::ArrayNewData { .. }
535 | wasmparser::Operator::ArrayNewElem { .. }
536 | wasmparser::Operator::ArrayGet { .. }
537 | wasmparser::Operator::ArrayGetS { .. }
538 | wasmparser::Operator::ArrayGetU { .. }
539 | wasmparser::Operator::ArraySet { .. }
540 | wasmparser::Operator::ArrayLen
541 | wasmparser::Operator::ArrayFill { .. }
542 | wasmparser::Operator::ArrayCopy { .. }
543 | wasmparser::Operator::ArrayInitData { .. }
544 | wasmparser::Operator::ArrayInitElem { .. }
545 | wasmparser::Operator::RefTestNonNull { .. }
546 | wasmparser::Operator::RefTestNullable { .. }
547 | wasmparser::Operator::RefCastNonNull { .. }
548 | wasmparser::Operator::RefCastNullable { .. }
549 | wasmparser::Operator::BrOnCast { .. }
550 | wasmparser::Operator::BrOnCastFail { .. }
551 | wasmparser::Operator::AnyConvertExtern
552 | wasmparser::Operator::ExternConvertAny
553 | wasmparser::Operator::RefI31
554 | wasmparser::Operator::I31GetS
555 | wasmparser::Operator::I31GetU => Ok(None),
556 wasmparser::Operator::I32TruncSatF32S
559 | wasmparser::Operator::I32TruncSatF32U
560 | wasmparser::Operator::I32TruncSatF64S
561 | wasmparser::Operator::I32TruncSatF64U
562 | wasmparser::Operator::I64TruncSatF32S
563 | wasmparser::Operator::I64TruncSatF32U
564 | wasmparser::Operator::I64TruncSatF64S
565 | wasmparser::Operator::I64TruncSatF64U => Ok(None),
566 wasmparser::Operator::MemoryInit { .. }
569 | wasmparser::Operator::DataDrop { .. }
570 | wasmparser::Operator::MemoryCopy { .. }
571 | wasmparser::Operator::MemoryFill { .. }
572 | wasmparser::Operator::TableInit { .. }
573 | wasmparser::Operator::ElemDrop { .. }
574 | wasmparser::Operator::TableCopy { .. } => Ok(None),
575 wasmparser::Operator::TableFill { .. }
578 | wasmparser::Operator::TableGet { .. }
579 | wasmparser::Operator::TableSet { .. }
580 | wasmparser::Operator::TableGrow { .. }
581 | wasmparser::Operator::TableSize { .. } => Ok(None),
582 wasmparser::Operator::MemoryDiscard { .. } => Ok(None),
585 wasmparser::Operator::MemoryAtomicNotify { .. }
588 | wasmparser::Operator::MemoryAtomicWait32 { .. }
589 | wasmparser::Operator::MemoryAtomicWait64 { .. }
590 | wasmparser::Operator::AtomicFence
591 | wasmparser::Operator::I32AtomicLoad { .. }
592 | wasmparser::Operator::I64AtomicLoad { .. }
593 | wasmparser::Operator::I32AtomicLoad8U { .. }
594 | wasmparser::Operator::I32AtomicLoad16U { .. }
595 | wasmparser::Operator::I64AtomicLoad8U { .. }
596 | wasmparser::Operator::I64AtomicLoad16U { .. }
597 | wasmparser::Operator::I64AtomicLoad32U { .. }
598 | wasmparser::Operator::I32AtomicStore { .. }
599 | wasmparser::Operator::I64AtomicStore { .. }
600 | wasmparser::Operator::I32AtomicStore8 { .. }
601 | wasmparser::Operator::I32AtomicStore16 { .. }
602 | wasmparser::Operator::I64AtomicStore8 { .. }
603 | wasmparser::Operator::I64AtomicStore16 { .. }
604 | wasmparser::Operator::I64AtomicStore32 { .. }
605 | wasmparser::Operator::I32AtomicRmwAdd { .. }
606 | wasmparser::Operator::I64AtomicRmwAdd { .. }
607 | wasmparser::Operator::I32AtomicRmw8AddU { .. }
608 | wasmparser::Operator::I32AtomicRmw16AddU { .. }
609 | wasmparser::Operator::I64AtomicRmw8AddU { .. }
610 | wasmparser::Operator::I64AtomicRmw16AddU { .. }
611 | wasmparser::Operator::I64AtomicRmw32AddU { .. }
612 | wasmparser::Operator::I32AtomicRmwSub { .. }
613 | wasmparser::Operator::I64AtomicRmwSub { .. }
614 | wasmparser::Operator::I32AtomicRmw8SubU { .. }
615 | wasmparser::Operator::I32AtomicRmw16SubU { .. }
616 | wasmparser::Operator::I64AtomicRmw8SubU { .. }
617 | wasmparser::Operator::I64AtomicRmw16SubU { .. }
618 | wasmparser::Operator::I64AtomicRmw32SubU { .. }
619 | wasmparser::Operator::I32AtomicRmwAnd { .. }
620 | wasmparser::Operator::I64AtomicRmwAnd { .. }
621 | wasmparser::Operator::I32AtomicRmw8AndU { .. }
622 | wasmparser::Operator::I32AtomicRmw16AndU { .. }
623 | wasmparser::Operator::I64AtomicRmw8AndU { .. }
624 | wasmparser::Operator::I64AtomicRmw16AndU { .. }
625 | wasmparser::Operator::I64AtomicRmw32AndU { .. }
626 | wasmparser::Operator::I32AtomicRmwOr { .. }
627 | wasmparser::Operator::I64AtomicRmwOr { .. }
628 | wasmparser::Operator::I32AtomicRmw8OrU { .. }
629 | wasmparser::Operator::I32AtomicRmw16OrU { .. }
630 | wasmparser::Operator::I64AtomicRmw8OrU { .. }
631 | wasmparser::Operator::I64AtomicRmw16OrU { .. }
632 | wasmparser::Operator::I64AtomicRmw32OrU { .. }
633 | wasmparser::Operator::I32AtomicRmwXor { .. }
634 | wasmparser::Operator::I64AtomicRmwXor { .. }
635 | wasmparser::Operator::I32AtomicRmw8XorU { .. }
636 | wasmparser::Operator::I32AtomicRmw16XorU { .. }
637 | wasmparser::Operator::I64AtomicRmw8XorU { .. }
638 | wasmparser::Operator::I64AtomicRmw16XorU { .. }
639 | wasmparser::Operator::I64AtomicRmw32XorU { .. }
640 | wasmparser::Operator::I32AtomicRmwXchg { .. }
641 | wasmparser::Operator::I64AtomicRmwXchg { .. }
642 | wasmparser::Operator::I32AtomicRmw8XchgU { .. }
643 | wasmparser::Operator::I32AtomicRmw16XchgU { .. }
644 | wasmparser::Operator::I64AtomicRmw8XchgU { .. }
645 | wasmparser::Operator::I64AtomicRmw16XchgU { .. }
646 | wasmparser::Operator::I64AtomicRmw32XchgU { .. }
647 | wasmparser::Operator::I32AtomicRmwCmpxchg { .. }
648 | wasmparser::Operator::I64AtomicRmwCmpxchg { .. }
649 | wasmparser::Operator::I32AtomicRmw8CmpxchgU { .. }
650 | wasmparser::Operator::I32AtomicRmw16CmpxchgU { .. }
651 | wasmparser::Operator::I64AtomicRmw8CmpxchgU { .. }
652 | wasmparser::Operator::I64AtomicRmw16CmpxchgU { .. }
653 | wasmparser::Operator::I64AtomicRmw32CmpxchgU { .. } => {
654 Err(NonDeterministicWasmFeature::Threads)
655 }
656 wasmparser::Operator::GlobalAtomicGet { .. }
659 | wasmparser::Operator::GlobalAtomicSet { .. }
660 | wasmparser::Operator::GlobalAtomicRmwAdd { .. }
661 | wasmparser::Operator::GlobalAtomicRmwSub { .. }
662 | wasmparser::Operator::GlobalAtomicRmwAnd { .. }
663 | wasmparser::Operator::GlobalAtomicRmwOr { .. }
664 | wasmparser::Operator::GlobalAtomicRmwXor { .. }
665 | wasmparser::Operator::GlobalAtomicRmwXchg { .. }
666 | wasmparser::Operator::GlobalAtomicRmwCmpxchg { .. }
667 | wasmparser::Operator::TableAtomicGet { .. }
668 | wasmparser::Operator::TableAtomicSet { .. }
669 | wasmparser::Operator::TableAtomicRmwXchg { .. }
670 | wasmparser::Operator::TableAtomicRmwCmpxchg { .. }
671 | wasmparser::Operator::StructAtomicGet { .. }
672 | wasmparser::Operator::StructAtomicGetS { .. }
673 | wasmparser::Operator::StructAtomicGetU { .. }
674 | wasmparser::Operator::StructAtomicSet { .. }
675 | wasmparser::Operator::StructAtomicRmwAdd { .. }
676 | wasmparser::Operator::StructAtomicRmwSub { .. }
677 | wasmparser::Operator::StructAtomicRmwAnd { .. }
678 | wasmparser::Operator::StructAtomicRmwOr { .. }
679 | wasmparser::Operator::StructAtomicRmwXor { .. }
680 | wasmparser::Operator::StructAtomicRmwXchg { .. }
681 | wasmparser::Operator::StructAtomicRmwCmpxchg { .. }
682 | wasmparser::Operator::ArrayAtomicGet { .. }
683 | wasmparser::Operator::ArrayAtomicGetS { .. }
684 | wasmparser::Operator::ArrayAtomicGetU { .. }
685 | wasmparser::Operator::ArrayAtomicSet { .. }
686 | wasmparser::Operator::ArrayAtomicRmwAdd { .. }
687 | wasmparser::Operator::ArrayAtomicRmwSub { .. }
688 | wasmparser::Operator::ArrayAtomicRmwAnd { .. }
689 | wasmparser::Operator::ArrayAtomicRmwOr { .. }
690 | wasmparser::Operator::ArrayAtomicRmwXor { .. }
691 | wasmparser::Operator::ArrayAtomicRmwXchg { .. }
692 | wasmparser::Operator::ArrayAtomicRmwCmpxchg { .. }
693 | wasmparser::Operator::RefI31Shared => {
694 Err(NonDeterministicWasmFeature::SharedEverythingThreads)
695 }
696 wasmparser::Operator::V128Load { .. }
699 | wasmparser::Operator::V128Load8x8S { .. }
700 | wasmparser::Operator::V128Load8x8U { .. }
701 | wasmparser::Operator::V128Load16x4S { .. }
702 | wasmparser::Operator::V128Load16x4U { .. }
703 | wasmparser::Operator::V128Load32x2S { .. }
704 | wasmparser::Operator::V128Load32x2U { .. }
705 | wasmparser::Operator::V128Load8Splat { .. }
706 | wasmparser::Operator::V128Load16Splat { .. }
707 | wasmparser::Operator::V128Load32Splat { .. }
708 | wasmparser::Operator::V128Load64Splat { .. }
709 | wasmparser::Operator::V128Load32Zero { .. }
710 | wasmparser::Operator::V128Load64Zero { .. }
711 | wasmparser::Operator::V128Store { .. }
712 | wasmparser::Operator::V128Load8Lane { .. }
713 | wasmparser::Operator::V128Load16Lane { .. }
714 | wasmparser::Operator::V128Load32Lane { .. }
715 | wasmparser::Operator::V128Load64Lane { .. }
716 | wasmparser::Operator::V128Store8Lane { .. }
717 | wasmparser::Operator::V128Store16Lane { .. }
718 | wasmparser::Operator::V128Store32Lane { .. }
719 | wasmparser::Operator::V128Store64Lane { .. } => Ok(None),
720 wasmparser::Operator::V128Const { .. } => Ok(None),
723 wasmparser::Operator::I8x16Shuffle { .. }
725 | wasmparser::Operator::I8x16ExtractLaneS { .. }
726 | wasmparser::Operator::I8x16ExtractLaneU { .. }
727 | wasmparser::Operator::I8x16ReplaceLane { .. }
728 | wasmparser::Operator::I16x8ExtractLaneS { .. }
729 | wasmparser::Operator::I16x8ExtractLaneU { .. }
730 | wasmparser::Operator::I16x8ReplaceLane { .. }
731 | wasmparser::Operator::I32x4ExtractLane { .. }
732 | wasmparser::Operator::I32x4ReplaceLane { .. }
733 | wasmparser::Operator::I64x2ExtractLane { .. }
734 | wasmparser::Operator::I64x2ReplaceLane { .. } => Ok(None),
735 wasmparser::Operator::F32x4ExtractLane { .. }
737 | wasmparser::Operator::F32x4ReplaceLane { .. }
738 | wasmparser::Operator::F64x2ExtractLane { .. }
739 | wasmparser::Operator::F64x2ReplaceLane { .. } => Ok(None),
740 wasmparser::Operator::I8x16Swizzle
742 | wasmparser::Operator::I8x16Splat
743 | wasmparser::Operator::I16x8Splat
744 | wasmparser::Operator::I32x4Splat
745 | wasmparser::Operator::I64x2Splat => Ok(None),
746 wasmparser::Operator::F32x4Splat | wasmparser::Operator::F64x2Splat => Ok(None),
749 wasmparser::Operator::I8x16Eq
751 | wasmparser::Operator::I8x16Ne
752 | wasmparser::Operator::I8x16LtS
753 | wasmparser::Operator::I8x16LtU
754 | wasmparser::Operator::I8x16GtS
755 | wasmparser::Operator::I8x16GtU
756 | wasmparser::Operator::I8x16LeS
757 | wasmparser::Operator::I8x16LeU
758 | wasmparser::Operator::I8x16GeS
759 | wasmparser::Operator::I8x16GeU
760 | wasmparser::Operator::I16x8Eq
761 | wasmparser::Operator::I16x8Ne
762 | wasmparser::Operator::I16x8LtS
763 | wasmparser::Operator::I16x8LtU
764 | wasmparser::Operator::I16x8GtS
765 | wasmparser::Operator::I16x8GtU
766 | wasmparser::Operator::I16x8LeS
767 | wasmparser::Operator::I16x8LeU
768 | wasmparser::Operator::I16x8GeS
769 | wasmparser::Operator::I16x8GeU
770 | wasmparser::Operator::I32x4Eq
771 | wasmparser::Operator::I32x4Ne
772 | wasmparser::Operator::I32x4LtS
773 | wasmparser::Operator::I32x4LtU
774 | wasmparser::Operator::I32x4GtS
775 | wasmparser::Operator::I32x4GtU
776 | wasmparser::Operator::I32x4LeS
777 | wasmparser::Operator::I32x4LeU
778 | wasmparser::Operator::I32x4GeS
779 | wasmparser::Operator::I32x4GeU
780 | wasmparser::Operator::I64x2Eq
781 | wasmparser::Operator::I64x2Ne
782 | wasmparser::Operator::I64x2LtS
783 | wasmparser::Operator::I64x2GtS
784 | wasmparser::Operator::I64x2LeS
785 | wasmparser::Operator::I64x2GeS => Ok(None),
786 wasmparser::Operator::F32x4Eq
788 | wasmparser::Operator::F32x4Ne
789 | wasmparser::Operator::F32x4Lt
790 | wasmparser::Operator::F32x4Gt
791 | wasmparser::Operator::F32x4Le
792 | wasmparser::Operator::F32x4Ge
793 | wasmparser::Operator::F64x2Eq
794 | wasmparser::Operator::F64x2Ne
795 | wasmparser::Operator::F64x2Lt
796 | wasmparser::Operator::F64x2Gt
797 | wasmparser::Operator::F64x2Le
798 | wasmparser::Operator::F64x2Ge => Ok(None),
799 wasmparser::Operator::V128Not
801 | wasmparser::Operator::V128And
802 | wasmparser::Operator::V128AndNot
803 | wasmparser::Operator::V128Or
804 | wasmparser::Operator::V128Xor
805 | wasmparser::Operator::V128Bitselect
806 | wasmparser::Operator::V128AnyTrue
807 | wasmparser::Operator::I8x16Abs
808 | wasmparser::Operator::I8x16Neg
809 | wasmparser::Operator::I8x16Popcnt
810 | wasmparser::Operator::I8x16AllTrue
811 | wasmparser::Operator::I8x16Bitmask
812 | wasmparser::Operator::I8x16NarrowI16x8S
813 | wasmparser::Operator::I8x16NarrowI16x8U
814 | wasmparser::Operator::I8x16Shl
815 | wasmparser::Operator::I8x16ShrS
816 | wasmparser::Operator::I8x16ShrU
817 | wasmparser::Operator::I8x16Add
818 | wasmparser::Operator::I8x16AddSatS
819 | wasmparser::Operator::I8x16AddSatU
820 | wasmparser::Operator::I8x16Sub
821 | wasmparser::Operator::I8x16SubSatS
822 | wasmparser::Operator::I8x16SubSatU
823 | wasmparser::Operator::I8x16MinS
824 | wasmparser::Operator::I8x16MinU
825 | wasmparser::Operator::I8x16MaxS
826 | wasmparser::Operator::I8x16MaxU
827 | wasmparser::Operator::I8x16AvgrU
828 | wasmparser::Operator::I16x8ExtAddPairwiseI8x16S
829 | wasmparser::Operator::I16x8ExtAddPairwiseI8x16U
830 | wasmparser::Operator::I16x8Abs
831 | wasmparser::Operator::I16x8Neg
832 | wasmparser::Operator::I16x8Q15MulrSatS
833 | wasmparser::Operator::I16x8AllTrue
834 | wasmparser::Operator::I16x8Bitmask
835 | wasmparser::Operator::I16x8NarrowI32x4S
836 | wasmparser::Operator::I16x8NarrowI32x4U
837 | wasmparser::Operator::I16x8ExtendLowI8x16S
838 | wasmparser::Operator::I16x8ExtendHighI8x16S
839 | wasmparser::Operator::I16x8ExtendLowI8x16U
840 | wasmparser::Operator::I16x8ExtendHighI8x16U
841 | wasmparser::Operator::I16x8Shl
842 | wasmparser::Operator::I16x8ShrS
843 | wasmparser::Operator::I16x8ShrU
844 | wasmparser::Operator::I16x8Add
845 | wasmparser::Operator::I16x8AddSatS
846 | wasmparser::Operator::I16x8AddSatU
847 | wasmparser::Operator::I16x8Sub
848 | wasmparser::Operator::I16x8SubSatS
849 | wasmparser::Operator::I16x8SubSatU
850 | wasmparser::Operator::I16x8Mul
851 | wasmparser::Operator::I16x8MinS
852 | wasmparser::Operator::I16x8MinU
853 | wasmparser::Operator::I16x8MaxS
854 | wasmparser::Operator::I16x8MaxU
855 | wasmparser::Operator::I16x8AvgrU
856 | wasmparser::Operator::I16x8ExtMulLowI8x16S
857 | wasmparser::Operator::I16x8ExtMulHighI8x16S
858 | wasmparser::Operator::I16x8ExtMulLowI8x16U
859 | wasmparser::Operator::I16x8ExtMulHighI8x16U
860 | wasmparser::Operator::I32x4ExtAddPairwiseI16x8S
861 | wasmparser::Operator::I32x4ExtAddPairwiseI16x8U
862 | wasmparser::Operator::I32x4Abs
863 | wasmparser::Operator::I32x4Neg
864 | wasmparser::Operator::I32x4AllTrue
865 | wasmparser::Operator::I32x4Bitmask
866 | wasmparser::Operator::I32x4ExtendLowI16x8S
867 | wasmparser::Operator::I32x4ExtendHighI16x8S
868 | wasmparser::Operator::I32x4ExtendLowI16x8U
869 | wasmparser::Operator::I32x4ExtendHighI16x8U
870 | wasmparser::Operator::I32x4Shl
871 | wasmparser::Operator::I32x4ShrS
872 | wasmparser::Operator::I32x4ShrU
873 | wasmparser::Operator::I32x4Add
874 | wasmparser::Operator::I32x4Sub
875 | wasmparser::Operator::I32x4Mul
876 | wasmparser::Operator::I32x4MinS
877 | wasmparser::Operator::I32x4MinU
878 | wasmparser::Operator::I32x4MaxS
879 | wasmparser::Operator::I32x4MaxU
880 | wasmparser::Operator::I32x4DotI16x8S
881 | wasmparser::Operator::I32x4ExtMulLowI16x8S
882 | wasmparser::Operator::I32x4ExtMulHighI16x8S
883 | wasmparser::Operator::I32x4ExtMulLowI16x8U
884 | wasmparser::Operator::I32x4ExtMulHighI16x8U
885 | wasmparser::Operator::I64x2Abs
886 | wasmparser::Operator::I64x2Neg
887 | wasmparser::Operator::I64x2AllTrue
888 | wasmparser::Operator::I64x2Bitmask
889 | wasmparser::Operator::I64x2ExtendLowI32x4S
890 | wasmparser::Operator::I64x2ExtendHighI32x4S
891 | wasmparser::Operator::I64x2ExtendLowI32x4U
892 | wasmparser::Operator::I64x2ExtendHighI32x4U
893 | wasmparser::Operator::I64x2Shl
894 | wasmparser::Operator::I64x2ShrS
895 | wasmparser::Operator::I64x2ShrU
896 | wasmparser::Operator::I64x2Add
897 | wasmparser::Operator::I64x2Sub
898 | wasmparser::Operator::I64x2Mul
899 | wasmparser::Operator::I64x2ExtMulLowI32x4S
900 | wasmparser::Operator::I64x2ExtMulHighI32x4S
901 | wasmparser::Operator::I64x2ExtMulLowI32x4U
902 | wasmparser::Operator::I64x2ExtMulHighI32x4U => Ok(None),
903 wasmparser::Operator::F32x4Ceil
905 | wasmparser::Operator::F32x4Floor
906 | wasmparser::Operator::F32x4Trunc
907 | wasmparser::Operator::F32x4Nearest => Ok(Some(MaybeNaNKind::F32x4)),
908 wasmparser::Operator::F32x4Abs | wasmparser::Operator::F32x4Neg => Ok(None),
910 wasmparser::Operator::F32x4Sqrt
912 | wasmparser::Operator::F32x4Add
913 | wasmparser::Operator::F32x4Sub
914 | wasmparser::Operator::F32x4Mul
915 | wasmparser::Operator::F32x4Div
916 | wasmparser::Operator::F32x4Min
917 | wasmparser::Operator::F32x4Max => Ok(Some(MaybeNaNKind::F32x4)),
918 wasmparser::Operator::F32x4PMin | wasmparser::Operator::F32x4PMax => Ok(None),
920 wasmparser::Operator::F64x2Ceil
922 | wasmparser::Operator::F64x2Floor
923 | wasmparser::Operator::F64x2Trunc
924 | wasmparser::Operator::F64x2Nearest => Ok(Some(MaybeNaNKind::F64x2)),
925 wasmparser::Operator::F64x2Abs | wasmparser::Operator::F64x2Neg => Ok(None),
927 wasmparser::Operator::F64x2Sqrt
929 | wasmparser::Operator::F64x2Add
930 | wasmparser::Operator::F64x2Sub
931 | wasmparser::Operator::F64x2Mul
932 | wasmparser::Operator::F64x2Div
933 | wasmparser::Operator::F64x2Min
934 | wasmparser::Operator::F64x2Max => Ok(Some(MaybeNaNKind::F64x2)),
935 wasmparser::Operator::F64x2PMin | wasmparser::Operator::F64x2PMax => Ok(None),
937 wasmparser::Operator::I32x4TruncSatF32x4S
939 | wasmparser::Operator::I32x4TruncSatF32x4U => Ok(None),
940 wasmparser::Operator::F32x4ConvertI32x4S | wasmparser::Operator::F32x4ConvertI32x4U => {
942 Ok(None)
943 }
944 wasmparser::Operator::I32x4TruncSatF64x2SZero
946 | wasmparser::Operator::I32x4TruncSatF64x2UZero => Ok(None),
947 wasmparser::Operator::F64x2ConvertLowI32x4S
949 | wasmparser::Operator::F64x2ConvertLowI32x4U => Ok(None),
950 wasmparser::Operator::F32x4DemoteF64x2Zero => Ok(Some(MaybeNaNKind::F32x4)),
952 wasmparser::Operator::F64x2PromoteLowF32x4 => Ok(Some(MaybeNaNKind::F64x2)),
953 wasmparser::Operator::I8x16RelaxedSwizzle
956 | wasmparser::Operator::I32x4RelaxedTruncF32x4S
957 | wasmparser::Operator::I32x4RelaxedTruncF32x4U
958 | wasmparser::Operator::I32x4RelaxedTruncF64x2SZero
959 | wasmparser::Operator::I32x4RelaxedTruncF64x2UZero
960 | wasmparser::Operator::F32x4RelaxedMadd
961 | wasmparser::Operator::F32x4RelaxedNmadd
962 | wasmparser::Operator::F64x2RelaxedMadd
963 | wasmparser::Operator::F64x2RelaxedNmadd
964 | wasmparser::Operator::I8x16RelaxedLaneselect
965 | wasmparser::Operator::I16x8RelaxedLaneselect
966 | wasmparser::Operator::I32x4RelaxedLaneselect
967 | wasmparser::Operator::I64x2RelaxedLaneselect
968 | wasmparser::Operator::F32x4RelaxedMin
969 | wasmparser::Operator::F32x4RelaxedMax
970 | wasmparser::Operator::F64x2RelaxedMin
971 | wasmparser::Operator::F64x2RelaxedMax
972 | wasmparser::Operator::I16x8RelaxedQ15mulrS
973 | wasmparser::Operator::I16x8RelaxedDotI8x16I7x16S
974 | wasmparser::Operator::I32x4RelaxedDotI8x16I7x16AddS => {
975 Err(NonDeterministicWasmFeature::RelaxedSimd)
976 }
977 wasmparser::Operator::CallRef { .. }
980 | wasmparser::Operator::ReturnCallRef { .. }
981 | wasmparser::Operator::RefAsNonNull
982 | wasmparser::Operator::BrOnNull { .. }
983 | wasmparser::Operator::BrOnNonNull { .. } => Ok(None),
984 wasmparser::Operator::ContNew { .. }
987 | wasmparser::Operator::ContBind { .. }
988 | wasmparser::Operator::Suspend { .. }
989 | wasmparser::Operator::Resume { .. }
990 | wasmparser::Operator::ResumeThrow { .. }
991 | wasmparser::Operator::Switch { .. } => Ok(None),
992 wasmparser::Operator::I64Add128
995 | wasmparser::Operator::I64Sub128
996 | wasmparser::Operator::I64MulWideS
997 | wasmparser::Operator::I64MulWideU => Ok(None),
998 #[cfg(not(test))]
1000 #[expect(clippy::panic)]
1001 _ => panic!("unsupported instruction"),
1002 #[cfg(test)]
1003 #[expect(unsafe_code)]
1004 _ => {
1005 extern "C" {
1006 fn nan_canonicaliser_unhandled_operator() -> !;
1007 }
1008 unsafe { nan_canonicaliser_unhandled_operator() }
1009 }
1010 }
1011 }
1012}
1013
1014#[derive(Copy, Clone)]
1015enum MaybeNaNKind {
1016 F32,
1017 F64,
1018 F32x4,
1019 F64x2,
1020}
1021
1022#[derive(Debug, thiserror::Error)]
1023enum NonDeterministicWasmFeature {
1024 #[error(
1025 "WASM uses the non-deterministic relaxed-simd feature, which may produce \
1026 platform-dependent results"
1027 )]
1028 RelaxedSimd,
1029 #[error("WASM uses the non-deterministic threads feature, which may produce data races")]
1030 Threads,
1031 #[error(
1032 "WASM uses the non-deterministic shared-everything threads feature, which may produce \
1033 data races"
1034 )]
1035 SharedEverythingThreads,
1036}