[fuzz] Add SIMD to single-instruction generator (#4778)

* [fuzz] Add SIMD to single-instruction generator

This change extends the single-instruction generator with most of the
SIMD instructions. Examples of instructions that were excluded are: all
memory-related instructions, any instruction with an immediate.

* [fuzz] Generate V128s with known values from each type

To better cover the fuzzing search space, `DiffValue` will generate
better known values for the `V128` type. First, it uses arbitrary data
to select a sub-type (e.g., `I8x16`, `F32x4`, etc.) and then it fills in
the bytes by generating biased values for each of the lanes.

* [fuzz] Canonicalize NaN values in SIMD lanes

This change ports the NaN canonicalization logic from `wasm-smith`
([here]) to the single-instruction generator.

[here]: https://github.com/bytecodealliance/wasm-tools/blob/6c127a6/crates/wasm-smith/src/core/code_builder.rs#L927
This commit is contained in:
Andrew Brown
2022-09-06 14:54:39 -07:00
committed by GitHub
parent 65930640f8
commit cd982c5a3f
2 changed files with 412 additions and 3 deletions

View File

@@ -20,6 +20,23 @@ pub struct SingleInstModule<'a> {
parameters: &'a [ValType], parameters: &'a [ValType],
results: &'a [ValType], results: &'a [ValType],
feature: fn(&ModuleConfig) -> bool, feature: fn(&ModuleConfig) -> bool,
canonicalize_nan: Option<NanType>,
}
/// 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> { impl<'a> SingleInstModule<'a> {
@@ -59,11 +76,77 @@ impl<'a> SingleInstModule<'a> {
// Encode the code section. // Encode the code section.
let mut codes = CodeSection::new(); 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() { for (index, _) in self.parameters.iter().enumerate() {
f.instruction(&Instruction::LocalGet(index as u32)); f.instruction(&Instruction::LocalGet(index as u32));
} }
f.instruction(&self.instruction); 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); f.instruction(&Instruction::End);
codes.function(&f); codes.function(&f);
module.section(&codes); module.section(&codes);
@@ -93,6 +176,9 @@ macro_rules! valtype {
(f64) => { (f64) => {
ValType::F64 ValType::F64
}; };
(v128) => {
ValType::V128
};
} }
macro_rules! inst { macro_rules! inst {
@@ -100,15 +186,25 @@ macro_rules! inst {
inst! { $inst, ($($arguments_ty),*) -> $result_ty, |_| true } inst! { $inst, ($($arguments_ty),*) -> $result_ty, |_| true }
}; };
($inst:ident, ($($arguments_ty:tt),*) -> $result_ty:tt, $feature:expr) => { ($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 { SingleInstModule {
instruction: Instruction::$inst, instruction: Instruction::$inst,
parameters: &[$(valtype!($arguments_ty)),*], parameters: &[$(valtype!($arguments_ty)),*],
results: &[valtype!($result_ty)], results: &[valtype!($result_ty)],
feature: $feature, 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] = &[ static INSTRUCTIONS: &[SingleInstModule] = &[
// Integer arithmetic. // Integer arithmetic.
// I32Const // I32Const
@@ -257,6 +353,221 @@ static INSTRUCTIONS: &[SingleInstModule] = &[
inst!(F64ConvertI64U, (i64) -> f64), inst!(F64ConvertI64U, (i64) -> f64),
inst!(F32ReinterpretI32, (i32) -> f32), inst!(F32ReinterpretI32, (i32) -> f32),
inst!(F64ReinterpretI64, (i64) -> f64), 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)] #[cfg(test)]
@@ -270,6 +581,7 @@ mod test {
parameters: &[ValType::I32, ValType::I32], parameters: &[ValType::I32, ValType::I32],
results: &[ValType::I32], results: &[ValType::I32],
feature: |_| true, feature: |_| true,
canonicalize_nan: None,
}; };
let wasm = sut.to_bytes(); let wasm = sut.to_bytes();
let wat = wasmprinter::print_bytes(wasm).unwrap(); let wat = wasmprinter::print_bytes(wasm).unwrap();

View File

@@ -91,7 +91,91 @@ impl DiffValue {
}; };
DiffValue::F64(bits) 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<u8> = 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<u8> = 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<u8> = 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<u8> = 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<u8> = 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 // 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 // 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_I32_VALUES: &[i32] = &[i32::MIN, -1, 0, 1, i32::MAX];
const KNOWN_I64_VALUES: &[i64] = &[i64::MIN, -1, 0, 1, i64::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 /// Helper function to pick a known value from the list of `known_values` half
/// the time. /// the time.
@@ -210,3 +295,15 @@ impl TryFrom<wasmtime::ValType> 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,
}