1 //! Generate Wasm modules that contain a single instruction.
2 
3 use super::ModuleConfig;
4 use arbitrary::Unstructured;
5 use wasm_encoder::{
6     CodeSection, ExportKind, ExportSection, Function, FunctionSection, Instruction, Module,
7     TypeSection, ValType,
8 };
9 
10 /// The name of the function generated by this module.
11 const FUNCTION_NAME: &'static str = "test";
12 
13 /// Configure a single instruction module.
14 ///
15 /// By explicitly defining the parameter and result types (versus generating the
16 /// module directly), we can more easily generate values of the right type.
17 #[derive(Clone)]
18 pub struct SingleInstModule<'a> {
19     instruction: Instruction<'a>,
20     parameters: &'a [ValType],
21     results: &'a [ValType],
22     feature: fn(&ModuleConfig) -> bool,
23     canonicalize_nan: Option<NanType>,
24 }
25 
26 /// Valid types for NaN canonicalization.
27 ///
28 /// When fuzzing floating point values, a NaN result can have non-deterministic
29 /// bits in the payload. In order to compare these results, [`SingleInstModule`]
30 /// can convert any NaN values (or NaN lanes) to a canonical NaN value for any
31 /// of these types.
32 #[derive(Clone)]
33 enum NanType {
34     #[expect(dead_code, reason = "expected to be used in the future")]
35     F32,
36     #[expect(dead_code, reason = "expected to be used in the future")]
37     F64,
38     F32x4,
39     F64x2,
40 }
41 
42 impl<'a> SingleInstModule<'a> {
43     /// Choose a single-instruction module that matches `config`.
new(u: &mut Unstructured<'a>, config: &ModuleConfig) -> arbitrary::Result<&'a Self>44     pub fn new(u: &mut Unstructured<'a>, config: &ModuleConfig) -> arbitrary::Result<&'a Self> {
45         // Only select instructions that match the `ModuleConfig`.
46         let instructions = &INSTRUCTIONS
47             .iter()
48             .filter(|i| (i.feature)(config))
49             .collect::<Vec<_>>();
50         u.choose(&instructions[..]).copied()
51     }
52 
53     /// Encode a binary Wasm module with a single exported function, `test`,
54     /// that executes the single instruction.
to_bytes(&self) -> Vec<u8>55     pub fn to_bytes(&self) -> Vec<u8> {
56         let mut module = Module::new();
57 
58         // Encode the type section.
59         let mut types = TypeSection::new();
60         types.ty().function(
61             self.parameters.iter().cloned(),
62             self.results.iter().cloned(),
63         );
64         module.section(&types);
65 
66         // Encode the function section.
67         let mut functions = FunctionSection::new();
68         let type_index = 0;
69         functions.function(type_index);
70         module.section(&functions);
71 
72         // Encode the export section.
73         let mut exports = ExportSection::new();
74         exports.export(FUNCTION_NAME, ExportKind::Func, 0);
75         module.section(&exports);
76 
77         // Encode the code section.
78         let mut codes = CodeSection::new();
79 
80         // Set up the single-instruction function. Note that if we have chosen
81         // to canonicalize NaNs, this function will contain more than one
82         // instruction and the function will need a scratch local.
83         let mut f = if let Some(ty) = &self.canonicalize_nan {
84             Function::new(match ty {
85                 NanType::F32 => vec![(1, ValType::F32)],
86                 NanType::F64 => vec![(1, ValType::F64)],
87                 NanType::F32x4 | NanType::F64x2 => vec![(1, ValType::V128)],
88             })
89         } else {
90             Function::new([])
91         };
92 
93         // Retrieve the input values and execute the chosen instruction.
94         for (index, _) in self.parameters.iter().enumerate() {
95             f.instruction(&Instruction::LocalGet(index as u32));
96         }
97         f.instruction(&self.instruction);
98 
99         // If we have configured to canonicalize NaNs, we add a sequence that
100         // masks off the NaN payload bits to make them 0s (i.e., a canonical
101         // NaN). This sequence is adapted from wasm-smiths version; see
102         // https://github.com/bytecodealliance/wasm-tools/blob/6c127a6/crates/wasm-smith/src/core/code_builder.rs#L927.
103         if let Some(ty) = &self.canonicalize_nan {
104             // Save the previous instruction's result into the scratch local.
105             // This also leaves a value on the stack as for the `select`
106             // instruction.
107             let local = self.parameters.len() as u32;
108             f.instruction(&Instruction::LocalTee(local));
109 
110             // The other input to the `select` below--a canonical NaN. Note how
111             // the payload bits of the NaN are cleared.
112             const CANON_32BIT_NAN: u32 = 0b01111111110000000000000000000000;
113             const CANON_64BIT_NAN: u64 =
114                 0b0111111111111000000000000000000000000000000000000000000000000000;
115             let mask = match ty {
116                 NanType::F32 => Instruction::F32Const(f32::from_bits(CANON_32BIT_NAN).into()),
117                 NanType::F64 => Instruction::F64Const(f64::from_bits(CANON_64BIT_NAN).into()),
118                 NanType::F32x4 => {
119                     let nan = CANON_32BIT_NAN as i128;
120                     Instruction::V128Const(nan | (nan << 32) | (nan << 64) | (nan << 96))
121                 }
122                 NanType::F64x2 => {
123                     let nan = CANON_64BIT_NAN as i128;
124                     Instruction::V128Const(nan | (nan << 64))
125                 }
126             };
127             f.instruction(&mask);
128 
129             // The `select` condition. NaNs never equal each other, so here the
130             // result value is compared against itself.
131             f.instruction(&Instruction::LocalGet(local));
132             f.instruction(&Instruction::LocalGet(local));
133             f.instruction(match ty {
134                 NanType::F32 => &Instruction::F32Eq,
135                 NanType::F64 => &Instruction::F64Eq,
136                 NanType::F32x4 => &Instruction::F32x4Eq,
137                 NanType::F64x2 => &Instruction::F64x2Eq,
138             });
139 
140             // Select the result. If the condition is nonzero (i.e., the float
141             // is equal to itself) it picks the original value; otherwise, if
142             // zero (i.e., the float is a NaN) it picks the canonical NaN value.
143             f.instruction(match ty {
144                 NanType::F32 | NanType::F64 => &Instruction::Select,
145                 NanType::F32x4 | NanType::F64x2 => &Instruction::V128Bitselect,
146             });
147         }
148 
149         // Wrap up the function and section.
150         f.instruction(&Instruction::End);
151         codes.function(&f);
152         module.section(&codes);
153 
154         // Extract the encoded Wasm bytes for this module.
155         module.finish()
156     }
157 }
158 
159 // MACROS
160 //
161 // These macros make it a bit easier to define the instructions available for
162 // generation. The idea is that, with these macros, we can define the list of
163 // instructions compactly and allow for easier changes to the Rust code (e.g.,
164 // `SingleInstModule`).
165 
166 macro_rules! valtypes {
167     (@list ($($ty:tt),*)) => {&[$(valtypes!(@one $ty)),*]};
168     (@list $ty:tt) => {&[valtypes!(@one $ty)]};
169     (@one i32) => {
170         ValType::I32
171     };
172     (@one i64) => {
173         ValType::I64
174     };
175     (@one f32) => {
176         ValType::F32
177     };
178     (@one f64) => {
179         ValType::F64
180     };
181     (@one v128) => {
182         ValType::V128
183     };
184 }
185 
186 macro_rules! inst {
187     ($inst:ident, $arguments:tt -> $results:tt) => {
188         inst! { $inst, $arguments -> $results, |_| true }
189     };
190     ($inst:ident, $arguments:tt -> $results:tt, $feature:expr) => {
191         inst! { $inst, $arguments -> $results, $feature, None }
192     };
193     ($inst:ident, $arguments:tt -> $results:tt, $feature:expr, $nan:expr) => {
194         SingleInstModule {
195             instruction: Instruction::$inst,
196             parameters: valtypes!(@list $arguments),
197             results: valtypes!(@list $results),
198             feature: $feature,
199             canonicalize_nan: $nan,
200         }
201     };
202 }
203 
204 // INSTRUCTIONS
205 //
206 // This list of WebAssembly instructions attempts to roughly follow the
207 // structure of the W3C specification:
208 // https://webassembly.github.io/spec/core/appendix/index-instructions.html#index-instr.
209 // Certain kinds of instructions (e.g., memory access) are skipped for now.
210 static INSTRUCTIONS: &[SingleInstModule] = &[
211     // Integer arithmetic.
212     // I32Const
213     // I64Const
214     // F32Const
215     // F64Const
216     inst!(I32Clz, (i32) -> i32),
217     inst!(I64Clz, (i64) -> i64),
218     inst!(I32Ctz, (i32) -> i32),
219     inst!(I64Ctz, (i64) -> i64),
220     inst!(I32Popcnt, (i32) -> i32),
221     inst!(I64Popcnt, (i64) -> i64),
222     inst!(I32Add, (i32, i32) -> i32),
223     inst!(I64Add, (i64, i64) -> i64),
224     inst!(I32Sub, (i32, i32) -> i32),
225     inst!(I64Sub, (i64, i64) -> i64),
226     inst!(I32Mul, (i32, i32) -> i32),
227     inst!(I64Mul, (i64, i64) -> i64),
228     inst!(I32DivS, (i32, i32) -> i32),
229     inst!(I64DivS, (i64, i64) -> i64),
230     inst!(I32DivU, (i32, i32) -> i32),
231     inst!(I64DivU, (i64, i64) -> i64),
232     inst!(I32RemS, (i32, i32) -> i32),
233     inst!(I64RemS, (i64, i64) -> i64),
234     inst!(I32RemU, (i32, i32) -> i32),
235     inst!(I64RemU, (i64, i64) -> i64),
236     // Integer bitwise.
237     inst!(I32And, (i32, i32) -> i32),
238     inst!(I64And, (i64, i64) -> i64),
239     inst!(I32Or, (i32, i32) -> i32),
240     inst!(I64Or, (i64, i64) -> i64),
241     inst!(I32Xor, (i32, i32) -> i32),
242     inst!(I64Xor, (i64, i64) -> i64),
243     inst!(I32Shl, (i32, i32) -> i32),
244     inst!(I64Shl, (i64, i64) -> i64),
245     inst!(I32ShrS, (i32, i32) -> i32),
246     inst!(I64ShrS, (i64, i64) -> i64),
247     inst!(I32ShrU, (i32, i32) -> i32),
248     inst!(I64ShrU, (i64, i64) -> i64),
249     inst!(I32Rotl, (i32, i32) -> i32),
250     inst!(I64Rotl, (i64, i64) -> i64),
251     inst!(I32Rotr, (i32, i32) -> i32),
252     inst!(I64Rotr, (i64, i64) -> i64),
253     // Integer comparison.
254     inst!(I32Eqz, (i32) -> i32),
255     inst!(I64Eqz, (i64) -> i32),
256     inst!(I32Eq, (i32, i32) -> i32),
257     inst!(I64Eq, (i64, i64) -> i32),
258     inst!(I32Ne, (i32, i32) -> i32),
259     inst!(I64Ne, (i64, i64) -> i32),
260     inst!(I32LtS, (i32, i32) -> i32),
261     inst!(I64LtS, (i64, i64) -> i32),
262     inst!(I32LtU, (i32, i32) -> i32),
263     inst!(I64LtU, (i64, i64) -> i32),
264     inst!(I32GtS, (i32, i32) -> i32),
265     inst!(I64GtS, (i64, i64) -> i32),
266     inst!(I32GtU, (i32, i32) -> i32),
267     inst!(I64GtU, (i64, i64) -> i32),
268     inst!(I32LeS, (i32, i32) -> i32),
269     inst!(I64LeS, (i64, i64) -> i32),
270     inst!(I32LeU, (i32, i32) -> i32),
271     inst!(I64LeU, (i64, i64) -> i32),
272     inst!(I32GeS, (i32, i32) -> i32),
273     inst!(I64GeS, (i64, i64) -> i32),
274     inst!(I32GeU, (i32, i32) -> i32),
275     inst!(I64GeU, (i64, i64) -> i32),
276     // Floating-point arithmetic.
277     inst!(F32Abs, (f32) -> f32),
278     inst!(F64Abs, (f64) -> f64),
279     inst!(F32Sqrt, (f32) -> f32),
280     inst!(F64Sqrt, (f64) -> f64),
281     inst!(F32Ceil, (f32) -> f32),
282     inst!(F64Ceil, (f64) -> f64),
283     inst!(F32Floor, (f32) -> f32),
284     inst!(F64Floor, (f64) -> f64),
285     inst!(F32Trunc, (f32) -> f32),
286     inst!(F64Trunc, (f64) -> f64),
287     inst!(F32Nearest, (f32) -> f32),
288     inst!(F64Nearest, (f64) -> f64),
289     inst!(F32Neg, (f32) -> f32),
290     inst!(F64Neg, (f64) -> f64),
291     inst!(F32Add, (f32, f32) -> f32),
292     inst!(F64Add, (f64, f64) -> f64),
293     inst!(F32Sub, (f32, f32) -> f32),
294     inst!(F64Sub, (f64, f64) -> f64),
295     inst!(F32Mul, (f32, f32) -> f32),
296     inst!(F64Mul, (f64, f64) -> f64),
297     inst!(F32Div, (f32, f32) -> f32),
298     inst!(F64Div, (f64, f64) -> f64),
299     inst!(F32Min, (f32, f32) -> f32),
300     inst!(F64Min, (f64, f64) -> f64),
301     inst!(F32Max, (f32, f32) -> f32),
302     inst!(F64Max, (f64, f64) -> f64),
303     inst!(F32Copysign, (f32, f32) -> f32),
304     inst!(F64Copysign, (f64, f64) -> f64),
305     // Floating-point comparison.
306     inst!(F32Eq, (f32, f32) -> i32),
307     inst!(F64Eq, (f64, f64) -> i32),
308     inst!(F32Ne, (f32, f32) -> i32),
309     inst!(F64Ne, (f64, f64) -> i32),
310     inst!(F32Lt, (f32, f32) -> i32),
311     inst!(F64Lt, (f64, f64) -> i32),
312     inst!(F32Gt, (f32, f32) -> i32),
313     inst!(F64Gt, (f64, f64) -> i32),
314     inst!(F32Le, (f32, f32) -> i32),
315     inst!(F64Le, (f64, f64) -> i32),
316     inst!(F32Ge, (f32, f32) -> i32),
317     inst!(F64Ge, (f64, f64) -> i32),
318     // Integer conversions ("to integer").
319     inst!(I32Extend8S, (i32) -> i32, |c| c.config.sign_extension_ops_enabled),
320     inst!(I32Extend16S, (i32) -> i32, |c| c.config.sign_extension_ops_enabled),
321     inst!(I64Extend8S, (i64) -> i64, |c| c.config.sign_extension_ops_enabled),
322     inst!(I64Extend16S, (i64) -> i64, |c| c.config.sign_extension_ops_enabled),
323     inst!(I64Extend32S, (i64) -> i64, |c| c.config.sign_extension_ops_enabled),
324     inst!(I32WrapI64, (i64) -> i32),
325     inst!(I64ExtendI32S, (i32) -> i64),
326     inst!(I64ExtendI32U, (i32) -> i64),
327     inst!(I32TruncF32S, (f32) -> i32),
328     inst!(I32TruncF32U, (f32) -> i32),
329     inst!(I32TruncF64S, (f64) -> i32),
330     inst!(I32TruncF64U, (f64) -> i32),
331     inst!(I64TruncF32S, (f32) -> i64),
332     inst!(I64TruncF32U, (f32) -> i64),
333     inst!(I64TruncF64S, (f64) -> i64),
334     inst!(I64TruncF64U, (f64) -> i64),
335     inst!(I32TruncSatF32S, (f32) -> i32, |c| c.config.saturating_float_to_int_enabled),
336     inst!(I32TruncSatF32U, (f32) -> i32, |c| c.config.saturating_float_to_int_enabled),
337     inst!(I32TruncSatF64S, (f64) -> i32, |c| c.config.saturating_float_to_int_enabled),
338     inst!(I32TruncSatF64U, (f64) -> i32, |c| c.config.saturating_float_to_int_enabled),
339     inst!(I64TruncSatF32S, (f32) -> i64, |c| c.config.saturating_float_to_int_enabled),
340     inst!(I64TruncSatF32U, (f32) -> i64, |c| c.config.saturating_float_to_int_enabled),
341     inst!(I64TruncSatF64S, (f64) -> i64, |c| c.config.saturating_float_to_int_enabled),
342     inst!(I64TruncSatF64U, (f64) -> i64, |c| c.config.saturating_float_to_int_enabled),
343     inst!(I32ReinterpretF32, (f32) -> i32),
344     inst!(I64ReinterpretF64, (f64) -> i64),
345     // Floating-point conversions ("to float").
346     inst!(F32DemoteF64, (f64) -> f32),
347     inst!(F64PromoteF32, (f32) -> f64),
348     inst!(F32ConvertI32S, (i32) -> f32),
349     inst!(F32ConvertI32U, (i32) -> f32),
350     inst!(F32ConvertI64S, (i64) -> f32),
351     inst!(F32ConvertI64U, (i64) -> f32),
352     inst!(F64ConvertI32S, (i32) -> f64),
353     inst!(F64ConvertI32U, (i32) -> f64),
354     inst!(F64ConvertI64S, (i64) -> f64),
355     inst!(F64ConvertI64U, (i64) -> f64),
356     inst!(F32ReinterpretI32, (i32) -> f32),
357     inst!(F64ReinterpretI64, (i64) -> f64),
358     // SIMD instructions.
359     // V128Const
360     // I8x16Shuffle
361     inst!(I8x16Swizzle, (v128, v128) -> v128, |c| c.config.simd_enabled),
362     inst!(I8x16Splat, (i32) -> v128, |c| c.config.simd_enabled),
363     inst!(I16x8Splat, (i32) -> v128, |c| c.config.simd_enabled),
364     inst!(I32x4Splat, (i32) -> v128, |c| c.config.simd_enabled),
365     inst!(I64x2Splat, (i64) -> v128, |c| c.config.simd_enabled),
366     inst!(F32x4Splat, (f32) -> v128, |c| c.config.simd_enabled),
367     inst!(F64x2Splat, (f64) -> v128, |c| c.config.simd_enabled),
368     // I8x16ExtractLaneS
369     // I8x16ExtractLaneU
370     // I8x16ReplaceLane
371     // I16x8ExtractLaneS
372     // I16x8ExtractLaneU
373     // I16x8ReplaceLane
374     // I32x4ExtractLane
375     // I32x4ReplaceLane
376     // I64x2ExtractLane
377     // I64x2ReplaceLane
378     // F32x4ExtractLane
379     // F32x4ReplaceLane
380     // F64x2ExtractLane
381     // F64x2ReplaceLane
382     inst!(I8x16Eq, (v128, v128) -> v128, |c| c.config.simd_enabled),
383     inst!(I8x16Ne, (v128, v128) -> v128, |c| c.config.simd_enabled),
384     inst!(I8x16LtS, (v128, v128) -> v128, |c| c.config.simd_enabled),
385     inst!(I8x16LtU, (v128, v128) -> v128, |c| c.config.simd_enabled),
386     inst!(I8x16GtS, (v128, v128) -> v128, |c| c.config.simd_enabled),
387     inst!(I8x16GtU, (v128, v128) -> v128, |c| c.config.simd_enabled),
388     inst!(I8x16LeS, (v128, v128) -> v128, |c| c.config.simd_enabled),
389     inst!(I8x16LeU, (v128, v128) -> v128, |c| c.config.simd_enabled),
390     inst!(I8x16GeS, (v128, v128) -> v128, |c| c.config.simd_enabled),
391     inst!(I8x16GeU, (v128, v128) -> v128, |c| c.config.simd_enabled),
392     inst!(I16x8Eq, (v128, v128) -> v128, |c| c.config.simd_enabled),
393     inst!(I16x8Ne, (v128, v128) -> v128, |c| c.config.simd_enabled),
394     inst!(I16x8LtS, (v128, v128) -> v128, |c| c.config.simd_enabled),
395     inst!(I16x8LtU, (v128, v128) -> v128, |c| c.config.simd_enabled),
396     inst!(I16x8GtS, (v128, v128) -> v128, |c| c.config.simd_enabled),
397     inst!(I16x8GtU, (v128, v128) -> v128, |c| c.config.simd_enabled),
398     inst!(I16x8LeS, (v128, v128) -> v128, |c| c.config.simd_enabled),
399     inst!(I16x8LeU, (v128, v128) -> v128, |c| c.config.simd_enabled),
400     inst!(I16x8GeS, (v128, v128) -> v128, |c| c.config.simd_enabled),
401     inst!(I16x8GeU, (v128, v128) -> v128, |c| c.config.simd_enabled),
402     inst!(I32x4Eq, (v128, v128) -> v128, |c| c.config.simd_enabled),
403     inst!(I32x4Ne, (v128, v128) -> v128, |c| c.config.simd_enabled),
404     inst!(I32x4LtS, (v128, v128) -> v128, |c| c.config.simd_enabled),
405     inst!(I32x4LtU, (v128, v128) -> v128, |c| c.config.simd_enabled),
406     inst!(I32x4GtS, (v128, v128) -> v128, |c| c.config.simd_enabled),
407     inst!(I32x4GtU, (v128, v128) -> v128, |c| c.config.simd_enabled),
408     inst!(I32x4LeS, (v128, v128) -> v128, |c| c.config.simd_enabled),
409     inst!(I32x4LeU, (v128, v128) -> v128, |c| c.config.simd_enabled),
410     inst!(I32x4GeS, (v128, v128) -> v128, |c| c.config.simd_enabled),
411     inst!(I32x4GeU, (v128, v128) -> v128, |c| c.config.simd_enabled),
412     inst!(I64x2Eq, (v128, v128) -> v128, |c| c.config.simd_enabled),
413     inst!(I64x2Ne, (v128, v128) -> v128, |c| c.config.simd_enabled),
414     inst!(I64x2LtS, (v128, v128) -> v128, |c| c.config.simd_enabled),
415     inst!(I64x2GtS, (v128, v128) -> v128, |c| c.config.simd_enabled),
416     inst!(I64x2LeS, (v128, v128) -> v128, |c| c.config.simd_enabled),
417     inst!(I64x2GeS, (v128, v128) -> v128, |c| c.config.simd_enabled),
418     inst!(F32x4Eq, (v128, v128) -> v128, |c| c.config.simd_enabled),
419     inst!(F32x4Ne, (v128, v128) -> v128, |c| c.config.simd_enabled),
420     inst!(F32x4Lt, (v128, v128) -> v128, |c| c.config.simd_enabled),
421     inst!(F32x4Gt, (v128, v128) -> v128, |c| c.config.simd_enabled),
422     inst!(F32x4Le, (v128, v128) -> v128, |c| c.config.simd_enabled),
423     inst!(F32x4Ge, (v128, v128) -> v128, |c| c.config.simd_enabled),
424     inst!(F64x2Eq, (v128, v128) -> v128, |c| c.config.simd_enabled),
425     inst!(F64x2Ne, (v128, v128) -> v128, |c| c.config.simd_enabled),
426     inst!(F64x2Lt, (v128, v128) -> v128, |c| c.config.simd_enabled),
427     inst!(F64x2Gt, (v128, v128) -> v128, |c| c.config.simd_enabled),
428     inst!(F64x2Le, (v128, v128) -> v128, |c| c.config.simd_enabled),
429     inst!(F64x2Ge, (v128, v128) -> v128, |c| c.config.simd_enabled),
430     inst!(V128Not, (v128) -> v128, |c| c.config.simd_enabled),
431     inst!(V128And, (v128, v128) -> v128, |c| c.config.simd_enabled),
432     inst!(V128AndNot, (v128, v128) -> v128, |c| c.config.simd_enabled),
433     inst!(V128Or, (v128, v128) -> v128, |c| c.config.simd_enabled),
434     inst!(V128Xor, (v128, v128) -> v128, |c| c.config.simd_enabled),
435     inst!(V128Bitselect, (v128, v128, v128) -> v128, |c| c.config.simd_enabled),
436     inst!(V128AnyTrue, (v128) -> i32, |c| c.config.simd_enabled),
437     inst!(I8x16Abs, (v128) -> v128, |c| c.config.simd_enabled),
438     inst!(I8x16Neg, (v128) -> v128, |c| c.config.simd_enabled),
439     inst!(I8x16Popcnt, (v128) -> v128, |c| c.config.simd_enabled),
440     inst!(I8x16AllTrue, (v128) -> i32, |c| c.config.simd_enabled),
441     inst!(I8x16Bitmask, (v128) -> i32, |c| c.config.simd_enabled),
442     inst!(I8x16NarrowI16x8S, (v128, v128) -> v128, |c| c.config.simd_enabled),
443     inst!(I8x16NarrowI16x8U, (v128, v128) -> v128, |c| c.config.simd_enabled),
444     inst!(I8x16Shl, (v128, i32) -> v128, |c| c.config.simd_enabled),
445     inst!(I8x16ShrS, (v128, i32) -> v128, |c| c.config.simd_enabled),
446     inst!(I8x16ShrU, (v128, i32) -> v128, |c| c.config.simd_enabled),
447     inst!(I8x16Add, (v128, v128) -> v128, |c| c.config.simd_enabled),
448     inst!(I8x16AddSatS, (v128, v128) -> v128, |c| c.config.simd_enabled),
449     inst!(I8x16AddSatU, (v128, v128) -> v128, |c| c.config.simd_enabled),
450     inst!(I8x16Sub, (v128, v128) -> v128, |c| c.config.simd_enabled),
451     inst!(I8x16SubSatS, (v128, v128) -> v128, |c| c.config.simd_enabled),
452     inst!(I8x16SubSatU, (v128, v128) -> v128, |c| c.config.simd_enabled),
453     inst!(I8x16MinS, (v128, v128) -> v128, |c| c.config.simd_enabled),
454     inst!(I8x16MinU, (v128, v128) -> v128, |c| c.config.simd_enabled),
455     inst!(I8x16MaxS, (v128, v128) -> v128, |c| c.config.simd_enabled),
456     inst!(I8x16MaxU, (v128, v128) -> v128, |c| c.config.simd_enabled),
457     inst!(I8x16AvgrU, (v128, v128) -> v128, |c| c.config.simd_enabled),
458     inst!(I16x8ExtAddPairwiseI8x16S, (v128) -> v128, |c| c.config.simd_enabled),
459     inst!(I16x8ExtAddPairwiseI8x16U, (v128) -> v128, |c| c.config.simd_enabled),
460     inst!(I16x8Abs, (v128) -> v128, |c| c.config.simd_enabled),
461     inst!(I16x8Neg, (v128) -> v128, |c| c.config.simd_enabled),
462     inst!(I16x8Q15MulrSatS, (v128, v128) -> v128, |c| c.config.simd_enabled),
463     inst!(I16x8AllTrue, (v128) -> i32, |c| c.config.simd_enabled),
464     inst!(I16x8Bitmask, (v128) -> i32, |c| c.config.simd_enabled),
465     inst!(I16x8NarrowI32x4S, (v128, v128) -> v128, |c| c.config.simd_enabled),
466     inst!(I16x8NarrowI32x4U, (v128, v128) -> v128, |c| c.config.simd_enabled),
467     inst!(I16x8ExtendLowI8x16S, (v128) -> v128, |c| c.config.simd_enabled),
468     inst!(I16x8ExtendHighI8x16S, (v128) -> v128, |c| c.config.simd_enabled),
469     inst!(I16x8ExtendLowI8x16U, (v128) -> v128, |c| c.config.simd_enabled),
470     inst!(I16x8ExtendHighI8x16U, (v128) -> v128, |c| c.config.simd_enabled),
471     inst!(I16x8Shl, (v128, i32) -> v128, |c| c.config.simd_enabled),
472     inst!(I16x8ShrS, (v128, i32) -> v128, |c| c.config.simd_enabled),
473     inst!(I16x8ShrU, (v128, i32) -> v128, |c| c.config.simd_enabled),
474     inst!(I16x8Add, (v128, v128) -> v128, |c| c.config.simd_enabled),
475     inst!(I16x8AddSatS, (v128, v128) -> v128, |c| c.config.simd_enabled),
476     inst!(I16x8AddSatU, (v128, v128) -> v128, |c| c.config.simd_enabled),
477     inst!(I16x8Sub, (v128, v128) -> v128, |c| c.config.simd_enabled),
478     inst!(I16x8SubSatS, (v128, v128) -> v128, |c| c.config.simd_enabled),
479     inst!(I16x8SubSatU, (v128, v128) -> v128, |c| c.config.simd_enabled),
480     inst!(I16x8Mul, (v128, v128) -> v128, |c| c.config.simd_enabled),
481     inst!(I16x8MinS, (v128, v128) -> v128, |c| c.config.simd_enabled),
482     inst!(I16x8MinU, (v128, v128) -> v128, |c| c.config.simd_enabled),
483     inst!(I16x8MaxS, (v128, v128) -> v128, |c| c.config.simd_enabled),
484     inst!(I16x8MaxU, (v128, v128) -> v128, |c| c.config.simd_enabled),
485     inst!(I16x8AvgrU, (v128, v128) -> v128, |c| c.config.simd_enabled),
486     inst!(I16x8ExtMulLowI8x16S, (v128, v128) -> v128, |c| c.config.simd_enabled),
487     inst!(I16x8ExtMulHighI8x16S, (v128, v128) -> v128, |c| c.config.simd_enabled),
488     inst!(I16x8ExtMulLowI8x16U, (v128, v128) -> v128, |c| c.config.simd_enabled),
489     inst!(I16x8ExtMulHighI8x16U, (v128, v128) -> v128, |c| c.config.simd_enabled),
490     inst!(I32x4ExtAddPairwiseI16x8S, (v128) -> v128, |c| c.config.simd_enabled),
491     inst!(I32x4ExtAddPairwiseI16x8U, (v128) -> v128, |c| c.config.simd_enabled),
492     inst!(I32x4Abs, (v128) -> v128, |c| c.config.simd_enabled),
493     inst!(I32x4Neg, (v128) -> v128, |c| c.config.simd_enabled),
494     inst!(I32x4AllTrue, (v128) -> i32, |c| c.config.simd_enabled),
495     inst!(I32x4Bitmask, (v128) -> i32, |c| c.config.simd_enabled),
496     inst!(I32x4ExtendLowI16x8S, (v128) -> v128, |c| c.config.simd_enabled),
497     inst!(I32x4ExtendHighI16x8S, (v128) -> v128, |c| c.config.simd_enabled),
498     inst!(I32x4ExtendLowI16x8U, (v128) -> v128, |c| c.config.simd_enabled),
499     inst!(I32x4ExtendHighI16x8U, (v128) -> v128, |c| c.config.simd_enabled),
500     inst!(I32x4Shl, (v128, i32) -> v128, |c| c.config.simd_enabled),
501     inst!(I32x4ShrS, (v128, i32) -> v128, |c| c.config.simd_enabled),
502     inst!(I32x4ShrU, (v128, i32) -> v128, |c| c.config.simd_enabled),
503     inst!(I32x4Add, (v128, v128) -> v128, |c| c.config.simd_enabled),
504     inst!(I32x4Sub, (v128, v128) -> v128, |c| c.config.simd_enabled),
505     inst!(I32x4Mul, (v128, v128) -> v128, |c| c.config.simd_enabled),
506     inst!(I32x4MinS, (v128, v128) -> v128, |c| c.config.simd_enabled),
507     inst!(I32x4MinU, (v128, v128) -> v128, |c| c.config.simd_enabled),
508     inst!(I32x4MaxS, (v128, v128) -> v128, |c| c.config.simd_enabled),
509     inst!(I32x4MaxU, (v128, v128) -> v128, |c| c.config.simd_enabled),
510     inst!(I32x4DotI16x8S, (v128, v128) -> v128, |c| c.config.simd_enabled),
511     inst!(I32x4ExtMulLowI16x8S, (v128, v128) -> v128, |c| c.config.simd_enabled),
512     inst!(I32x4ExtMulHighI16x8S, (v128, v128) -> v128, |c| c.config.simd_enabled),
513     inst!(I32x4ExtMulLowI16x8U, (v128, v128) -> v128, |c| c.config.simd_enabled),
514     inst!(I32x4ExtMulHighI16x8U, (v128, v128) -> v128, |c| c.config.simd_enabled),
515     inst!(I64x2Abs, (v128) -> v128, |c| c.config.simd_enabled),
516     inst!(I64x2Neg, (v128) -> v128, |c| c.config.simd_enabled),
517     inst!(I64x2AllTrue, (v128) -> i32, |c| c.config.simd_enabled),
518     inst!(I64x2Bitmask, (v128) -> i32, |c| c.config.simd_enabled),
519     inst!(I64x2ExtendLowI32x4S, (v128) -> v128, |c| c.config.simd_enabled),
520     inst!(I64x2ExtendHighI32x4S, (v128) -> v128, |c| c.config.simd_enabled),
521     inst!(I64x2ExtendLowI32x4U, (v128) -> v128, |c| c.config.simd_enabled),
522     inst!(I64x2ExtendHighI32x4U, (v128) -> v128, |c| c.config.simd_enabled),
523     inst!(I64x2Shl, (v128, i32) -> v128, |c| c.config.simd_enabled),
524     inst!(I64x2ShrS, (v128, i32) -> v128, |c| c.config.simd_enabled),
525     inst!(I64x2ShrU, (v128, i32) -> v128, |c| c.config.simd_enabled),
526     inst!(I64x2Add, (v128, v128) -> v128, |c| c.config.simd_enabled),
527     inst!(I64x2Sub, (v128, v128) -> v128, |c| c.config.simd_enabled),
528     inst!(I64x2Mul, (v128, v128) -> v128, |c| c.config.simd_enabled),
529     inst!(I64x2ExtMulLowI32x4S, (v128, v128) -> v128, |c| c.config.simd_enabled),
530     inst!(I64x2ExtMulHighI32x4S, (v128, v128) -> v128, |c| c.config.simd_enabled),
531     inst!(I64x2ExtMulLowI32x4U, (v128, v128) -> v128, |c| c.config.simd_enabled),
532     inst!(I64x2ExtMulHighI32x4U, (v128, v128) -> v128, |c| c.config.simd_enabled),
533     inst!(F32x4Ceil, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)),
534     inst!(F32x4Floor, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)),
535     inst!(F32x4Trunc, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)),
536     inst!(F32x4Nearest, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)),
537     inst!(F32x4Abs, (v128) -> v128, |c| c.config.simd_enabled),
538     inst!(F32x4Neg, (v128) -> v128, |c| c.config.simd_enabled),
539     inst!(F32x4Sqrt, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)),
540     inst!(F32x4Add, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)),
541     inst!(F32x4Sub, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)),
542     inst!(F32x4Mul, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)),
543     inst!(F32x4Div, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)),
544     inst!(F32x4Min, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)),
545     inst!(F32x4Max, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)),
546     inst!(F32x4PMin, (v128, v128) -> v128, |c| c.config.simd_enabled),
547     inst!(F32x4PMax, (v128, v128) -> v128, |c| c.config.simd_enabled),
548     inst!(F64x2Ceil, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)),
549     inst!(F64x2Floor, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)),
550     inst!(F64x2Trunc, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)),
551     inst!(F64x2Nearest, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)),
552     inst!(F64x2Abs, (v128) -> v128, |c| c.config.simd_enabled),
553     inst!(F64x2Neg, (v128) -> v128, |c| c.config.simd_enabled),
554     inst!(F64x2Sqrt, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)),
555     inst!(F64x2Add, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)),
556     inst!(F64x2Sub, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)),
557     inst!(F64x2Mul, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)),
558     inst!(F64x2Div, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)),
559     inst!(F64x2Min, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)),
560     inst!(F64x2Max, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)),
561     inst!(F64x2PMin, (v128, v128) -> v128, |c| c.config.simd_enabled),
562     inst!(F64x2PMax, (v128, v128) -> v128, |c| c.config.simd_enabled),
563     inst!(I32x4TruncSatF32x4S, (v128) -> v128, |c| c.config.simd_enabled),
564     inst!(I32x4TruncSatF32x4U, (v128) -> v128, |c| c.config.simd_enabled),
565     inst!(F32x4ConvertI32x4S, (v128) -> v128, |c| c.config.simd_enabled),
566     inst!(F32x4ConvertI32x4U, (v128) -> v128, |c| c.config.simd_enabled),
567     inst!(I32x4TruncSatF64x2SZero, (v128) -> v128, |c| c.config.simd_enabled),
568     inst!(I32x4TruncSatF64x2UZero, (v128) -> v128, |c| c.config.simd_enabled),
569     inst!(F64x2ConvertLowI32x4S, (v128) -> v128, |c| c.config.simd_enabled),
570     inst!(F64x2ConvertLowI32x4U, (v128) -> v128, |c| c.config.simd_enabled),
571     inst!(F32x4DemoteF64x2Zero, (v128) -> v128, |c| c.config.simd_enabled),
572     inst!(F64x2PromoteLowF32x4, (v128) -> v128, |c| c.config.simd_enabled),
573     // wide arithmetic
574     inst!(I64Add128, (i64, i64, i64, i64) -> (i64, i64), |c| c.config.wide_arithmetic_enabled && c.config.multi_value_enabled),
575     inst!(I64Sub128, (i64, i64, i64, i64) -> (i64, i64), |c| c.config.wide_arithmetic_enabled && c.config.multi_value_enabled),
576     inst!(I64MulWideS, (i64, i64) -> (i64, i64), |c| c.config.wide_arithmetic_enabled && c.config.multi_value_enabled),
577     inst!(I64MulWideU, (i64, i64) -> (i64, i64), |c| c.config.wide_arithmetic_enabled && c.config.multi_value_enabled),
578 ];
579 
580 #[cfg(test)]
581 mod test {
582     use super::*;
583 
584     #[test]
sanity()585     fn sanity() {
586         let sut = SingleInstModule {
587             instruction: Instruction::I32Add,
588             parameters: &[ValType::I32, ValType::I32],
589             results: &[ValType::I32],
590             feature: |_| true,
591             canonicalize_nan: None,
592         };
593         let wasm = sut.to_bytes();
594         let wat = wasmprinter::print_bytes(wasm).unwrap();
595         assert_eq!(
596             wat,
597             r#"(module
598   (type (;0;) (func (param i32 i32) (result i32)))
599   (export "test" (func 0))
600   (func (;0;) (type 0) (param i32 i32) (result i32)
601     local.get 0
602     local.get 1
603     i32.add
604   )
605 )
606 "#
607         )
608     }
609 
610     #[test]
instructions_encode_to_valid_modules()611     fn instructions_encode_to_valid_modules() {
612         for inst in INSTRUCTIONS {
613             assert!(wat::parse_bytes(&inst.to_bytes()).is_ok());
614         }
615     }
616 }
617