[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

@@ -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<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
// 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<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,
}