//! Generate Wasm modules that contain a single instruction. use super::ModuleConfig; use arbitrary::Unstructured; use wasm_encoder::{ CodeSection, ExportKind, ExportSection, Function, FunctionSection, Instruction, Module, TypeSection, ValType, }; /// The name of the function generated by this module. const FUNCTION_NAME: &'static str = "test"; /// Configure a single instruction module. /// /// By explicitly defining the parameter and result types (versus generating the /// module directly), we can more easily generate values of the right type. #[derive(Clone)] pub struct SingleInstModule<'a> { instruction: Instruction<'a>, parameters: &'a [ValType], results: &'a [ValType], feature: fn(&ModuleConfig) -> bool, canonicalize_nan: Option, } /// Valid types for NaN canonicalization. /// /// When fuzzing floating point values, a NaN result can have non-deterministic /// bits in the payload. In order to compare these results, [`SingleInstModule`] /// can convert any NaN values (or NaN lanes) to a canonical NaN value for any /// of these types. #[derive(Clone)] enum NanType { #[allow(dead_code)] F32, #[allow(dead_code)] F64, F32x4, F64x2, } impl<'a> SingleInstModule<'a> { /// Choose a single-instruction module that matches `config`. pub fn new(u: &mut Unstructured<'a>, config: &ModuleConfig) -> arbitrary::Result<&'a Self> { // Only select instructions that match the `ModuleConfig`. let instructions = &INSTRUCTIONS .iter() .filter(|i| (i.feature)(config)) .collect::>(); u.choose(&instructions[..]).copied() } /// Encode a binary Wasm module with a single exported function, `test`, /// that executes the single instruction. pub fn to_bytes(&self) -> Vec { let mut module = Module::new(); // Encode the type section. let mut types = TypeSection::new(); types.function( self.parameters.iter().cloned(), self.results.iter().cloned(), ); module.section(&types); // Encode the function section. let mut functions = FunctionSection::new(); let type_index = 0; functions.function(type_index); module.section(&functions); // Encode the export section. let mut exports = ExportSection::new(); exports.export(FUNCTION_NAME, ExportKind::Func, 0); module.section(&exports); // Encode the code section. let mut codes = CodeSection::new(); // Set up the single-instruction function. Note that if we have chosen // to canonicalize NaNs, this function will contain more than one // instruction and the function will need a scratch local. let mut f = if let Some(ty) = &self.canonicalize_nan { Function::new(match ty { NanType::F32 => vec![(1, ValType::F32)], NanType::F64 => vec![(1, ValType::F64)], NanType::F32x4 | NanType::F64x2 => vec![(1, ValType::V128)], }) } else { Function::new([]) }; // Retrieve the input values and execute the chosen instruction. for (index, _) in self.parameters.iter().enumerate() { f.instruction(&Instruction::LocalGet(index as u32)); } f.instruction(&self.instruction); // If we have configured to canonicalize NaNs, we add a sequence that // masks off the NaN payload bits to make them 0s (i.e., a canonical // NaN). This sequence is adapted from wasm-smiths version; see // https://github.com/bytecodealliance/wasm-tools/blob/6c127a6/crates/wasm-smith/src/core/code_builder.rs#L927. if let Some(ty) = &self.canonicalize_nan { // Save the previous instruction's result into the scratch local. // This also leaves a value on the stack as for the `select` // instruction. let local = self.parameters.len() as u32; f.instruction(&Instruction::LocalTee(local)); // The other input to the `select` below--a canonical NaN. Note how // the payload bits of the NaN are cleared. const CANON_32BIT_NAN: u32 = 0b01111111110000000000000000000000; const CANON_64BIT_NAN: u64 = 0b0111111111111000000000000000000000000000000000000000000000000000; let mask = match ty { NanType::F32 => Instruction::F32Const(f32::from_bits(CANON_32BIT_NAN)), NanType::F64 => Instruction::F64Const(f64::from_bits(CANON_64BIT_NAN)), NanType::F32x4 => { let nan = CANON_32BIT_NAN as i128; Instruction::V128Const(nan | (nan << 32) | (nan << 64) | (nan << 96)) } NanType::F64x2 => { let nan = CANON_64BIT_NAN as i128; Instruction::V128Const(nan | (nan << 64)) } }; f.instruction(&mask); // The `select` condition. NaNs never equal each other, so here the // result value is compared against itself. f.instruction(&Instruction::LocalGet(local)); f.instruction(&Instruction::LocalGet(local)); f.instruction(match ty { NanType::F32 => &Instruction::F32Eq, NanType::F64 => &Instruction::F64Eq, NanType::F32x4 => &Instruction::F32x4Eq, NanType::F64x2 => &Instruction::F64x2Eq, }); // Select the result. If the condition is nonzero (i.e., the float // is equal to itself) it picks the original value; otherwise, if // zero (i.e., the float is a NaN) it picks the canonical NaN value. f.instruction(match ty { NanType::F32 | NanType::F64 => &Instruction::Select, NanType::F32x4 | NanType::F64x2 => &Instruction::V128Bitselect, }); } // Wrap up the function and section. f.instruction(&Instruction::End); codes.function(&f); module.section(&codes); // Extract the encoded Wasm bytes for this module. module.finish() } } // MACROS // // These macros make it a bit easier to define the instructions available for // generation. The idea is that, with these macros, we can define the list of // instructions compactly and allow for easier changes to the Rust code (e.g., // `SingleInstModule`). macro_rules! valtype { (i32) => { ValType::I32 }; (i64) => { ValType::I64 }; (f32) => { ValType::F32 }; (f64) => { ValType::F64 }; (v128) => { ValType::V128 }; } macro_rules! inst { ($inst:ident, ($($arguments_ty:tt),*) -> $result_ty:tt) => { inst! { $inst, ($($arguments_ty),*) -> $result_ty, |_| true } }; ($inst:ident, ($($arguments_ty:tt),*) -> $result_ty:tt, $feature:expr) => { inst! { $inst, ($($arguments_ty),*) -> $result_ty, $feature, None } }; ($inst:ident, ($($arguments_ty:tt),*) -> $result_ty:tt, $feature:expr, $nan:expr) => { SingleInstModule { instruction: Instruction::$inst, parameters: &[$(valtype!($arguments_ty)),*], results: &[valtype!($result_ty)], feature: $feature, canonicalize_nan: $nan, } }; } // INSTRUCTIONS // // This list of WebAssembly instructions attempts to roughly follow the // structure of the W3C specification: // https://webassembly.github.io/spec/core/appendix/index-instructions.html#index-instr. // Certain kinds of instructions (e.g., memory access) are skipped for now. static INSTRUCTIONS: &[SingleInstModule] = &[ // Integer arithmetic. // I32Const // I64Const // F32Const // F64Const inst!(I32Clz, (i32) -> i32), inst!(I64Clz, (i64) -> i64), inst!(I32Ctz, (i32) -> i32), inst!(I64Ctz, (i64) -> i64), inst!(I32Popcnt, (i32) -> i32), inst!(I64Popcnt, (i64) -> i64), inst!(I32Add, (i32, i32) -> i32), inst!(I64Add, (i64, i64) -> i64), inst!(I32Sub, (i32, i32) -> i32), inst!(I64Sub, (i64, i64) -> i64), inst!(I32Mul, (i32, i32) -> i32), inst!(I64Mul, (i64, i64) -> i64), inst!(I32DivS, (i32, i32) -> i32), inst!(I64DivS, (i64, i64) -> i64), inst!(I32DivU, (i32, i32) -> i32), inst!(I64DivU, (i64, i64) -> i64), inst!(I32RemS, (i32, i32) -> i32), inst!(I64RemS, (i64, i64) -> i64), inst!(I32RemU, (i32, i32) -> i32), inst!(I64RemU, (i64, i64) -> i64), // Integer bitwise. inst!(I32And, (i32, i32) -> i32), inst!(I64And, (i64, i64) -> i64), inst!(I32Or, (i32, i32) -> i32), inst!(I64Or, (i64, i64) -> i64), inst!(I32Xor, (i32, i32) -> i32), inst!(I64Xor, (i64, i64) -> i64), inst!(I32Shl, (i32, i32) -> i32), inst!(I64Shl, (i64, i64) -> i64), inst!(I32ShrS, (i32, i32) -> i32), inst!(I64ShrS, (i64, i64) -> i64), inst!(I32ShrU, (i32, i32) -> i32), inst!(I64ShrU, (i64, i64) -> i64), inst!(I32Rotl, (i32, i32) -> i32), inst!(I64Rotl, (i64, i64) -> i64), inst!(I32Rotr, (i32, i32) -> i32), inst!(I64Rotr, (i64, i64) -> i64), // Integer comparison. inst!(I32Eqz, (i32) -> i32), inst!(I64Eqz, (i64) -> i32), inst!(I32Eq, (i32, i32) -> i32), inst!(I64Eq, (i64, i64) -> i32), inst!(I32Ne, (i32, i32) -> i32), inst!(I64Ne, (i64, i64) -> i32), inst!(I32LtS, (i32, i32) -> i32), inst!(I64LtS, (i64, i64) -> i32), inst!(I32LtU, (i32, i32) -> i32), inst!(I64LtU, (i64, i64) -> i32), inst!(I32GtS, (i32, i32) -> i32), inst!(I64GtS, (i64, i64) -> i32), inst!(I32GtU, (i32, i32) -> i32), inst!(I64GtU, (i64, i64) -> i32), inst!(I32LeS, (i32, i32) -> i32), inst!(I64LeS, (i64, i64) -> i32), inst!(I32LeU, (i32, i32) -> i32), inst!(I64LeU, (i64, i64) -> i32), inst!(I32GeS, (i32, i32) -> i32), inst!(I64GeS, (i64, i64) -> i32), inst!(I32GeU, (i32, i32) -> i32), inst!(I64GeU, (i64, i64) -> i32), // Floating-point arithmetic. inst!(F32Abs, (f32) -> f32), inst!(F64Abs, (f64) -> f64), inst!(F32Sqrt, (f32) -> f32), inst!(F64Sqrt, (f64) -> f64), inst!(F32Ceil, (f32) -> f32), inst!(F64Ceil, (f64) -> f64), inst!(F32Floor, (f32) -> f32), inst!(F64Floor, (f64) -> f64), inst!(F32Trunc, (f32) -> f32), inst!(F64Trunc, (f64) -> f64), inst!(F32Nearest, (f32) -> f32), inst!(F64Nearest, (f64) -> f64), inst!(F32Neg, (f32) -> f32), inst!(F64Neg, (f64) -> f64), inst!(F32Add, (f32, f32) -> f32), inst!(F64Add, (f64, f64) -> f64), inst!(F32Sub, (f32, f32) -> f32), inst!(F64Sub, (f64, f64) -> f64), inst!(F32Mul, (f32, f32) -> f32), inst!(F64Mul, (f64, f64) -> f64), inst!(F32Div, (f32, f32) -> f32), inst!(F64Div, (f64, f64) -> f64), inst!(F32Min, (f32, f32) -> f32), inst!(F64Min, (f64, f64) -> f64), inst!(F32Max, (f32, f32) -> f32), inst!(F64Max, (f64, f64) -> f64), inst!(F32Copysign, (f32, f32) -> f32), inst!(F64Copysign, (f64, f64) -> f64), // Floating-point comparison. inst!(F32Eq, (f32, f32) -> i32), inst!(F64Eq, (f64, f64) -> i32), inst!(F32Ne, (f32, f32) -> i32), inst!(F64Ne, (f64, f64) -> i32), inst!(F32Lt, (f32, f32) -> i32), inst!(F64Lt, (f64, f64) -> i32), inst!(F32Gt, (f32, f32) -> i32), inst!(F64Gt, (f64, f64) -> i32), inst!(F32Le, (f32, f32) -> i32), inst!(F64Le, (f64, f64) -> i32), inst!(F32Ge, (f32, f32) -> i32), inst!(F64Ge, (f64, f64) -> i32), // Integer conversions ("to integer"). inst!(I32Extend8S, (i32) -> i32, |c| c.config.sign_extension_enabled), inst!(I32Extend16S, (i32) -> i32, |c| c.config.sign_extension_enabled), inst!(I64Extend8S, (i64) -> i64, |c| c.config.sign_extension_enabled), inst!(I64Extend16S, (i64) -> i64, |c| c.config.sign_extension_enabled), inst!(I64Extend32S, (i64) -> i64, |c| c.config.sign_extension_enabled), inst!(I32WrapI64, (i64) -> i32), inst!(I64ExtendI32S, (i32) -> i64), inst!(I64ExtendI32U, (i32) -> i64), inst!(I32TruncF32S, (f32) -> i32), inst!(I32TruncF32U, (f32) -> i32), inst!(I32TruncF64S, (f64) -> i32), inst!(I32TruncF64U, (f64) -> i32), inst!(I64TruncF32S, (f32) -> i64), inst!(I64TruncF32U, (f32) -> i64), inst!(I64TruncF64S, (f64) -> i64), inst!(I64TruncF64U, (f64) -> i64), inst!(I32TruncSatF32S, (f32) -> i32, |c| c.config.saturating_float_to_int_enabled), inst!(I32TruncSatF32U, (f32) -> i32, |c| c.config.saturating_float_to_int_enabled), inst!(I32TruncSatF64S, (f64) -> i32, |c| c.config.saturating_float_to_int_enabled), inst!(I32TruncSatF64U, (f64) -> i32, |c| c.config.saturating_float_to_int_enabled), inst!(I64TruncSatF32S, (f32) -> i64, |c| c.config.saturating_float_to_int_enabled), inst!(I64TruncSatF32U, (f32) -> i64, |c| c.config.saturating_float_to_int_enabled), inst!(I64TruncSatF64S, (f64) -> i64, |c| c.config.saturating_float_to_int_enabled), inst!(I64TruncSatF64U, (f64) -> i64, |c| c.config.saturating_float_to_int_enabled), inst!(I32ReinterpretF32, (f32) -> i32), inst!(I64ReinterpretF64, (f64) -> i64), // Floating-point conversions ("to float"). inst!(F32DemoteF64, (f64) -> f32), inst!(F64PromoteF32, (f32) -> f64), inst!(F32ConvertI32S, (i32) -> f32), inst!(F32ConvertI32U, (i32) -> f32), inst!(F32ConvertI64S, (i64) -> f32), inst!(F32ConvertI64U, (i64) -> f32), inst!(F64ConvertI32S, (i32) -> f64), inst!(F64ConvertI32U, (i32) -> f64), inst!(F64ConvertI64S, (i64) -> f64), inst!(F64ConvertI64U, (i64) -> f64), inst!(F32ReinterpretI32, (i32) -> f32), inst!(F64ReinterpretI64, (i64) -> f64), // SIMD instructions. // V128Const // I8x16Shuffle inst!(I8x16Swizzle, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16Splat, (i32) -> v128, |c| c.config.simd_enabled), inst!(I16x8Splat, (i32) -> v128, |c| c.config.simd_enabled), inst!(I32x4Splat, (i32) -> v128, |c| c.config.simd_enabled), inst!(I64x2Splat, (i64) -> v128, |c| c.config.simd_enabled), inst!(F32x4Splat, (f32) -> v128, |c| c.config.simd_enabled), inst!(F64x2Splat, (f64) -> v128, |c| c.config.simd_enabled), // I8x16ExtractLaneS // I8x16ExtractLaneU // I8x16ReplaceLane // I16x8ExtractLaneS // I16x8ExtractLaneU // I16x8ReplaceLane // I32x4ExtractLane // I32x4ReplaceLane // I64x2ExtractLane // I64x2ReplaceLane // F32x4ExtractLane // F32x4ReplaceLane // F64x2ExtractLane // F64x2ReplaceLane inst!(I8x16Eq, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16Ne, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16LtS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16LtU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16GtS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16GtU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16LeS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16LeU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16GeS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16GeU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8Eq, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8Ne, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8LtS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8LtU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8GtS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8GtU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8LeS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8LeU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8GeS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8GeU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4Eq, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4Ne, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4LtS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4LtU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4GtS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4GtU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4LeS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4LeU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4GeS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4GeU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2Eq, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2Ne, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2LtS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2GtS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2LeS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2GeS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F32x4Eq, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F32x4Ne, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F32x4Lt, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F32x4Gt, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F32x4Le, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F32x4Ge, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F64x2Eq, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F64x2Ne, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F64x2Lt, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F64x2Gt, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F64x2Le, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F64x2Ge, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(V128Not, (v128) -> v128, |c| c.config.simd_enabled), inst!(V128And, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(V128AndNot, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(V128Or, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(V128Xor, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(V128Bitselect, (v128, v128, v128) -> v128, |c| c.config.simd_enabled), inst!(V128AnyTrue, (v128) -> i32, |c| c.config.simd_enabled), inst!(I8x16Abs, (v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16Neg, (v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16Popcnt, (v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16AllTrue, (v128) -> i32, |c| c.config.simd_enabled), inst!(I8x16Bitmask, (v128) -> i32, |c| c.config.simd_enabled), inst!(I8x16NarrowI16x8S, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16NarrowI16x8U, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16Shl, (v128, i32) -> v128, |c| c.config.simd_enabled), inst!(I8x16ShrS, (v128, i32) -> v128, |c| c.config.simd_enabled), inst!(I8x16ShrU, (v128, i32) -> v128, |c| c.config.simd_enabled), inst!(I8x16Add, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16AddSatS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16AddSatU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16Sub, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16SubSatS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16SubSatU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16MinS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16MinU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16MaxS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16MaxU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I8x16AvgrU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8ExtAddPairwiseI8x16S, (v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8ExtAddPairwiseI8x16U, (v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8Abs, (v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8Neg, (v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8Q15MulrSatS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8AllTrue, (v128) -> i32, |c| c.config.simd_enabled), inst!(I16x8Bitmask, (v128) -> i32, |c| c.config.simd_enabled), inst!(I16x8NarrowI32x4S, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8NarrowI32x4U, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8ExtendLowI8x16S, (v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8ExtendHighI8x16S, (v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8ExtendLowI8x16U, (v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8ExtendHighI8x16U, (v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8Shl, (v128, i32) -> v128, |c| c.config.simd_enabled), inst!(I16x8ShrS, (v128, i32) -> v128, |c| c.config.simd_enabled), inst!(I16x8ShrU, (v128, i32) -> v128, |c| c.config.simd_enabled), inst!(I16x8Add, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8AddSatS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8AddSatU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8Sub, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8SubSatS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8SubSatU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8Mul, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8MinS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8MinU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8MaxS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8MaxU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8AvgrU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8ExtMulLowI8x16S, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8ExtMulHighI8x16S, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8ExtMulLowI8x16U, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I16x8ExtMulHighI8x16U, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4ExtAddPairwiseI16x8S, (v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4ExtAddPairwiseI16x8U, (v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4Abs, (v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4Neg, (v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4AllTrue, (v128) -> i32, |c| c.config.simd_enabled), inst!(I32x4Bitmask, (v128) -> i32, |c| c.config.simd_enabled), inst!(I32x4ExtendLowI16x8S, (v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4ExtendHighI16x8S, (v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4ExtendLowI16x8U, (v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4ExtendHighI16x8U, (v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4Shl, (v128, i32) -> v128, |c| c.config.simd_enabled), inst!(I32x4ShrS, (v128, i32) -> v128, |c| c.config.simd_enabled), inst!(I32x4ShrU, (v128, i32) -> v128, |c| c.config.simd_enabled), inst!(I32x4Add, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4Sub, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4Mul, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4MinS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4MinU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4MaxS, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4MaxU, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4DotI16x8S, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4ExtMulLowI16x8S, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4ExtMulHighI16x8S, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4ExtMulLowI16x8U, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4ExtMulHighI16x8U, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2Abs, (v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2Neg, (v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2AllTrue, (v128) -> i32, |c| c.config.simd_enabled), inst!(I64x2Bitmask, (v128) -> i32, |c| c.config.simd_enabled), inst!(I64x2ExtendLowI32x4S, (v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2ExtendHighI32x4S, (v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2ExtendLowI32x4U, (v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2ExtendHighI32x4U, (v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2Shl, (v128, i32) -> v128, |c| c.config.simd_enabled), inst!(I64x2ShrS, (v128, i32) -> v128, |c| c.config.simd_enabled), inst!(I64x2ShrU, (v128, i32) -> v128, |c| c.config.simd_enabled), inst!(I64x2Add, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2Sub, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2Mul, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2ExtMulLowI32x4S, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2ExtMulHighI32x4S, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2ExtMulLowI32x4U, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I64x2ExtMulHighI32x4U, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F32x4Ceil, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), inst!(F32x4Floor, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), inst!(F32x4Trunc, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), inst!(F32x4Nearest, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), inst!(F32x4Abs, (v128) -> v128, |c| c.config.simd_enabled), inst!(F32x4Neg, (v128) -> v128, |c| c.config.simd_enabled), inst!(F32x4Sqrt, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), inst!(F32x4Add, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), inst!(F32x4Sub, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), inst!(F32x4Mul, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), inst!(F32x4Div, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), inst!(F32x4Min, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), inst!(F32x4Max, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F32x4)), inst!(F32x4PMin, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F32x4PMax, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F64x2Ceil, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), inst!(F64x2Floor, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), inst!(F64x2Trunc, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), inst!(F64x2Nearest, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), inst!(F64x2Abs, (v128) -> v128, |c| c.config.simd_enabled), inst!(F64x2Neg, (v128) -> v128, |c| c.config.simd_enabled), inst!(F64x2Sqrt, (v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), inst!(F64x2Add, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), inst!(F64x2Sub, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), inst!(F64x2Mul, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), inst!(F64x2Div, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), inst!(F64x2Min, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), inst!(F64x2Max, (v128, v128) -> v128, |c| c.config.simd_enabled, Some(NanType::F64x2)), inst!(F64x2PMin, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(F64x2PMax, (v128, v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4TruncSatF32x4S, (v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4TruncSatF32x4U, (v128) -> v128, |c| c.config.simd_enabled), inst!(F32x4ConvertI32x4S, (v128) -> v128, |c| c.config.simd_enabled), inst!(F32x4ConvertI32x4U, (v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4TruncSatF64x2SZero, (v128) -> v128, |c| c.config.simd_enabled), inst!(I32x4TruncSatF64x2UZero, (v128) -> v128, |c| c.config.simd_enabled), inst!(F64x2ConvertLowI32x4S, (v128) -> v128, |c| c.config.simd_enabled), inst!(F64x2ConvertLowI32x4U, (v128) -> v128, |c| c.config.simd_enabled), inst!(F32x4DemoteF64x2Zero, (v128) -> v128, |c| c.config.simd_enabled), inst!(F64x2PromoteLowF32x4, (v128) -> v128, |c| c.config.simd_enabled), ]; #[cfg(test)] mod test { use super::*; #[test] fn sanity() { let sut = SingleInstModule { instruction: Instruction::I32Add, parameters: &[ValType::I32, ValType::I32], results: &[ValType::I32], feature: |_| true, canonicalize_nan: None, }; let wasm = sut.to_bytes(); let wat = wasmprinter::print_bytes(wasm).unwrap(); assert_eq!( wat, r#"(module (type (;0;) (func (param i32 i32) (result i32))) (func (;0;) (type 0) (param i32 i32) (result i32) local.get 0 local.get 1 i32.add ) (export "test" (func 0)) )"# ) } #[test] fn instructions_encode_to_valid_modules() { for inst in INSTRUCTIONS { assert!(wat::parse_bytes(&inst.to_bytes()).is_ok()); } } }