diff --git a/crates/fuzzing/src/generators/single_inst_module.rs b/crates/fuzzing/src/generators/single_inst_module.rs index 8f77d24f40..317592b188 100644 --- a/crates/fuzzing/src/generators/single_inst_module.rs +++ b/crates/fuzzing/src/generators/single_inst_module.rs @@ -20,6 +20,23 @@ pub struct SingleInstModule<'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> { @@ -59,11 +76,77 @@ impl<'a> SingleInstModule<'a> { // Encode the code section. let mut codes = CodeSection::new(); - let mut f = Function::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); @@ -93,6 +176,9 @@ macro_rules! valtype { (f64) => { ValType::F64 }; + (v128) => { + ValType::V128 + }; } macro_rules! inst { @@ -100,15 +186,25 @@ macro_rules! inst { 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 @@ -257,6 +353,221 @@ static INSTRUCTIONS: &[SingleInstModule] = &[ 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!(I8x16RoundingAverageU, (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!(I16x8RoundingAverageU, (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)] @@ -270,6 +581,7 @@ mod test { 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(); diff --git a/crates/fuzzing/src/generators/value.rs b/crates/fuzzing/src/generators/value.rs index e4d5102023..e432bcfd76 100644 --- a/crates/fuzzing/src/generators/value.rs +++ b/crates/fuzzing/src/generators/value.rs @@ -91,7 +91,91 @@ impl DiffValue { }; DiffValue::F64(bits) } - V128 => DiffValue::V128(biased_arbitrary_value(u, KNOWN_U128_VALUES)?), + V128 => { + // Generate known values for each sub-type of V128. + let ty: DiffSimdTy = u.arbitrary()?; + match ty { + DiffSimdTy::I8x16 => { + let mut i8 = || biased_arbitrary_value(u, KNOWN_I8_VALUES).map(|b| b as u8); + let vector = u128::from_le_bytes([ + i8()?, + i8()?, + i8()?, + i8()?, + i8()?, + i8()?, + i8()?, + i8()?, + i8()?, + i8()?, + i8()?, + i8()?, + i8()?, + i8()?, + i8()?, + i8()?, + ]); + DiffValue::V128(vector) + } + DiffSimdTy::I16x8 => { + let mut i16 = + || biased_arbitrary_value(u, KNOWN_I16_VALUES).map(i16::to_le_bytes); + let vector: Vec = i16()? + .into_iter() + .chain(i16()?) + .chain(i16()?) + .chain(i16()?) + .chain(i16()?) + .chain(i16()?) + .chain(i16()?) + .chain(i16()?) + .collect(); + DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap())) + } + DiffSimdTy::I32x4 => { + let mut i32 = + || biased_arbitrary_value(u, KNOWN_I32_VALUES).map(i32::to_le_bytes); + let vector: Vec = i32()? + .into_iter() + .chain(i32()?) + .chain(i32()?) + .chain(i32()?) + .collect(); + DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap())) + } + DiffSimdTy::I64x2 => { + let mut i64 = + || biased_arbitrary_value(u, KNOWN_I64_VALUES).map(i64::to_le_bytes); + let vector: Vec = i64()?.into_iter().chain(i64()?).collect(); + DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap())) + } + DiffSimdTy::F32x4 => { + let mut f32 = || { + Self::arbitrary_of_type(u, DiffValueType::F32).map(|v| match v { + DiffValue::F32(v) => v.to_le_bytes(), + _ => unreachable!(), + }) + }; + let vector: Vec = f32()? + .into_iter() + .chain(f32()?) + .chain(f32()?) + .chain(f32()?) + .collect(); + DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap())) + } + DiffSimdTy::F64x2 => { + let mut f64 = || { + Self::arbitrary_of_type(u, DiffValueType::F64).map(|v| match v { + DiffValue::F64(v) => v.to_le_bytes(), + _ => unreachable!(), + }) + }; + let vector: Vec = f64()?.into_iter().chain(f64()?).collect(); + DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap())) + } + } + } // TODO: this isn't working in most engines so just always pass a // null in which if an engine supports this is should at least @@ -103,9 +187,10 @@ impl DiffValue { } } +const KNOWN_I8_VALUES: &[i8] = &[i8::MIN, -1, 0, 1, i8::MAX]; +const KNOWN_I16_VALUES: &[i16] = &[i16::MIN, -1, 0, 1, i16::MAX]; const KNOWN_I32_VALUES: &[i32] = &[i32::MIN, -1, 0, 1, i32::MAX]; const KNOWN_I64_VALUES: &[i64] = &[i64::MIN, -1, 0, 1, i64::MAX]; -const KNOWN_U128_VALUES: &[u128] = &[u128::MIN, 1, u128::MAX]; /// Helper function to pick a known value from the list of `known_values` half /// the time. @@ -210,3 +295,15 @@ impl TryFrom for DiffValueType { } } } + +/// Enumerate the types of v128. +#[derive(Copy, Clone, Debug, Arbitrary, Hash)] +#[allow(missing_docs)] +pub enum DiffSimdTy { + I8x16, + I16x8, + I32x4, + I64x2, + F32x4, + F64x2, +}