wasmtime_fuzzing/generators/
single_inst_module.rs

1//! Generate Wasm modules that contain a single instruction.
2
3use super::ModuleConfig;
4use arbitrary::Unstructured;
5use 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.
11const 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)]
18pub 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)]
33enum 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
42impl<'a> SingleInstModule<'a> {
43    /// Choose a single-instruction module that matches `config`.
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.
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)),
117                NanType::F64 => Instruction::F64Const(f64::from_bits(CANON_64BIT_NAN)),
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
166macro_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
186macro_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.
210static 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)]
581mod test {
582    use super::*;
583
584    #[test]
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]
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}