1 //! Generate Wasm values, primarily for differential execution. 2 3 use arbitrary::{Arbitrary, Unstructured}; 4 use std::hash::Hash; 5 use wasmtime::HeapType; 6 7 /// A value passed to and from evaluation. Note that reference types are not 8 /// (yet) supported. 9 #[derive(Clone, Debug)] 10 #[allow(missing_docs)] 11 pub enum DiffValue { 12 I32(i32), 13 I64(i64), 14 F32(u32), 15 F64(u64), 16 V128(u128), 17 FuncRef { null: bool }, 18 ExternRef { null: bool }, 19 } 20 21 impl DiffValue { 22 fn ty(&self) -> DiffValueType { 23 match self { 24 DiffValue::I32(_) => DiffValueType::I32, 25 DiffValue::I64(_) => DiffValueType::I64, 26 DiffValue::F32(_) => DiffValueType::F32, 27 DiffValue::F64(_) => DiffValueType::F64, 28 DiffValue::V128(_) => DiffValueType::V128, 29 DiffValue::FuncRef { .. } => DiffValueType::FuncRef, 30 DiffValue::ExternRef { .. } => DiffValueType::ExternRef, 31 } 32 } 33 34 /// Generate a [`DiffValue`] of the given `ty` type. 35 /// 36 /// This function will bias the returned value 50% of the time towards one 37 /// of a set of known values (e.g., NaN, -1, 0, infinity, etc.). 38 pub fn arbitrary_of_type( 39 u: &mut Unstructured<'_>, 40 ty: DiffValueType, 41 ) -> arbitrary::Result<Self> { 42 use DiffValueType::*; 43 let val = match ty { 44 I32 => DiffValue::I32(biased_arbitrary_value(u, KNOWN_I32_VALUES)?), 45 I64 => DiffValue::I64(biased_arbitrary_value(u, KNOWN_I64_VALUES)?), 46 F32 => { 47 // TODO once `to_bits` is stable as a `const` function, move 48 // this to a `const` definition. 49 let known_f32_values = &[ 50 f32::NAN.to_bits(), 51 f32::INFINITY.to_bits(), 52 f32::NEG_INFINITY.to_bits(), 53 f32::MIN.to_bits(), 54 (-1.0f32).to_bits(), 55 (0.0f32).to_bits(), 56 (1.0f32).to_bits(), 57 f32::MAX.to_bits(), 58 ]; 59 let bits = biased_arbitrary_value(u, known_f32_values)?; 60 61 // If the chosen bits are NAN then always use the canonical bit 62 // pattern of nan to enable better compatibility with engines 63 // where arbitrary nan patterns can't make their way into wasm 64 // (e.g. v8 through JS can't do that). 65 let bits = if f32::from_bits(bits).is_nan() { 66 f32::NAN.to_bits() 67 } else { 68 bits 69 }; 70 DiffValue::F32(bits) 71 } 72 F64 => { 73 // TODO once `to_bits` is stable as a `const` function, move 74 // this to a `const` definition. 75 let known_f64_values = &[ 76 f64::NAN.to_bits(), 77 f64::INFINITY.to_bits(), 78 f64::NEG_INFINITY.to_bits(), 79 f64::MIN.to_bits(), 80 (-1.0f64).to_bits(), 81 (0.0f64).to_bits(), 82 (1.0f64).to_bits(), 83 f64::MAX.to_bits(), 84 ]; 85 let bits = biased_arbitrary_value(u, known_f64_values)?; 86 // See `f32` above for why canonical nan patterns are always 87 // used. 88 let bits = if f64::from_bits(bits).is_nan() { 89 f64::NAN.to_bits() 90 } else { 91 bits 92 }; 93 DiffValue::F64(bits) 94 } 95 V128 => { 96 // Generate known values for each sub-type of V128. 97 let ty: DiffSimdTy = u.arbitrary()?; 98 match ty { 99 DiffSimdTy::I8x16 => { 100 let mut i8 = || biased_arbitrary_value(u, KNOWN_I8_VALUES).map(|b| b as u8); 101 let vector = u128::from_le_bytes([ 102 i8()?, 103 i8()?, 104 i8()?, 105 i8()?, 106 i8()?, 107 i8()?, 108 i8()?, 109 i8()?, 110 i8()?, 111 i8()?, 112 i8()?, 113 i8()?, 114 i8()?, 115 i8()?, 116 i8()?, 117 i8()?, 118 ]); 119 DiffValue::V128(vector) 120 } 121 DiffSimdTy::I16x8 => { 122 let mut i16 = 123 || biased_arbitrary_value(u, KNOWN_I16_VALUES).map(i16::to_le_bytes); 124 let vector: Vec<u8> = i16()? 125 .into_iter() 126 .chain(i16()?) 127 .chain(i16()?) 128 .chain(i16()?) 129 .chain(i16()?) 130 .chain(i16()?) 131 .chain(i16()?) 132 .chain(i16()?) 133 .collect(); 134 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap())) 135 } 136 DiffSimdTy::I32x4 => { 137 let mut i32 = 138 || biased_arbitrary_value(u, KNOWN_I32_VALUES).map(i32::to_le_bytes); 139 let vector: Vec<u8> = i32()? 140 .into_iter() 141 .chain(i32()?) 142 .chain(i32()?) 143 .chain(i32()?) 144 .collect(); 145 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap())) 146 } 147 DiffSimdTy::I64x2 => { 148 let mut i64 = 149 || biased_arbitrary_value(u, KNOWN_I64_VALUES).map(i64::to_le_bytes); 150 let vector: Vec<u8> = i64()?.into_iter().chain(i64()?).collect(); 151 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap())) 152 } 153 DiffSimdTy::F32x4 => { 154 let mut f32 = || { 155 Self::arbitrary_of_type(u, DiffValueType::F32).map(|v| match v { 156 DiffValue::F32(v) => v.to_le_bytes(), 157 _ => unreachable!(), 158 }) 159 }; 160 let vector: Vec<u8> = f32()? 161 .into_iter() 162 .chain(f32()?) 163 .chain(f32()?) 164 .chain(f32()?) 165 .collect(); 166 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap())) 167 } 168 DiffSimdTy::F64x2 => { 169 let mut f64 = || { 170 Self::arbitrary_of_type(u, DiffValueType::F64).map(|v| match v { 171 DiffValue::F64(v) => v.to_le_bytes(), 172 _ => unreachable!(), 173 }) 174 }; 175 let vector: Vec<u8> = f64()?.into_iter().chain(f64()?).collect(); 176 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap())) 177 } 178 } 179 } 180 181 // TODO: this isn't working in most engines so just always pass a 182 // null in which if an engine supports this is should at least 183 // support doing that. 184 FuncRef => DiffValue::FuncRef { null: true }, 185 ExternRef => DiffValue::ExternRef { null: true }, 186 }; 187 arbitrary::Result::Ok(val) 188 } 189 } 190 191 const KNOWN_I8_VALUES: &[i8] = &[i8::MIN, -1, 0, 1, i8::MAX]; 192 const KNOWN_I16_VALUES: &[i16] = &[i16::MIN, -1, 0, 1, i16::MAX]; 193 const KNOWN_I32_VALUES: &[i32] = &[i32::MIN, -1, 0, 1, i32::MAX]; 194 const KNOWN_I64_VALUES: &[i64] = &[i64::MIN, -1, 0, 1, i64::MAX]; 195 196 /// Helper function to pick a known value from the list of `known_values` half 197 /// the time. 198 fn biased_arbitrary_value<'a, T>( 199 u: &mut Unstructured<'a>, 200 known_values: &[T], 201 ) -> arbitrary::Result<T> 202 where 203 T: Arbitrary<'a> + Copy, 204 { 205 let pick_from_known_values: bool = u.arbitrary()?; 206 if pick_from_known_values { 207 Ok(*u.choose(known_values)?) 208 } else { 209 u.arbitrary() 210 } 211 } 212 213 impl<'a> Arbitrary<'a> for DiffValue { 214 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> { 215 let ty: DiffValueType = u.arbitrary()?; 216 DiffValue::arbitrary_of_type(u, ty) 217 } 218 } 219 220 impl Hash for DiffValue { 221 fn hash<H: std::hash::Hasher>(&self, state: &mut H) { 222 self.ty().hash(state); 223 match self { 224 DiffValue::I32(n) => n.hash(state), 225 DiffValue::I64(n) => n.hash(state), 226 DiffValue::F32(n) => n.hash(state), 227 DiffValue::F64(n) => n.hash(state), 228 DiffValue::V128(n) => n.hash(state), 229 DiffValue::ExternRef { null } => null.hash(state), 230 DiffValue::FuncRef { null } => null.hash(state), 231 } 232 } 233 } 234 235 /// Implement equality checks. Note that floating-point values are not compared 236 /// bit-for-bit in the case of NaNs: because Wasm floating-point numbers may be 237 /// [arithmetic NaNs with arbitrary payloads] and Wasm operations are [not 238 /// required to propagate NaN payloads], we simply check that both sides are 239 /// NaNs here. We could be more strict, though: we could check that the NaN 240 /// signs are equal and that [canonical NaN payloads remain canonical]. 241 /// 242 /// [arithmetic NaNs with arbitrary payloads]: 243 /// https://webassembly.github.io/spec/core/bikeshed/index.html#floating-point%E2%91%A0 244 /// [not required to propagate NaN payloads]: 245 /// https://webassembly.github.io/spec/core/bikeshed/index.html#floating-point-operations%E2%91%A0 246 /// [canonical NaN payloads remain canonical]: 247 /// https://webassembly.github.io/spec/core/bikeshed/index.html#nan-propagation%E2%91%A0 248 impl PartialEq for DiffValue { 249 fn eq(&self, other: &Self) -> bool { 250 match (self, other) { 251 (Self::I32(l0), Self::I32(r0)) => l0 == r0, 252 (Self::I64(l0), Self::I64(r0)) => l0 == r0, 253 (Self::V128(l0), Self::V128(r0)) => l0 == r0, 254 (Self::F32(l0), Self::F32(r0)) => { 255 let l0 = f32::from_bits(*l0); 256 let r0 = f32::from_bits(*r0); 257 l0 == r0 || (l0.is_nan() && r0.is_nan()) 258 } 259 (Self::F64(l0), Self::F64(r0)) => { 260 let l0 = f64::from_bits(*l0); 261 let r0 = f64::from_bits(*r0); 262 l0 == r0 || (l0.is_nan() && r0.is_nan()) 263 } 264 (Self::FuncRef { null: a }, Self::FuncRef { null: b }) => a == b, 265 (Self::ExternRef { null: a }, Self::ExternRef { null: b }) => a == b, 266 _ => false, 267 } 268 } 269 } 270 271 /// Enumerate the supported value types. 272 #[derive(Copy, Clone, Debug, Arbitrary, Hash)] 273 #[allow(missing_docs)] 274 pub enum DiffValueType { 275 I32, 276 I64, 277 F32, 278 F64, 279 V128, 280 FuncRef, 281 ExternRef, 282 } 283 284 impl TryFrom<wasmtime::ValType> for DiffValueType { 285 type Error = &'static str; 286 fn try_from(ty: wasmtime::ValType) -> Result<Self, Self::Error> { 287 use wasmtime::ValType::*; 288 match ty { 289 I32 => Ok(Self::I32), 290 I64 => Ok(Self::I64), 291 F32 => Ok(Self::F32), 292 F64 => Ok(Self::F64), 293 V128 => Ok(Self::V128), 294 Ref(r) => match (r.is_nullable(), r.heap_type()) { 295 (true, HeapType::Func) => Ok(Self::FuncRef), 296 (true, HeapType::Extern) => Ok(Self::ExternRef), 297 _ => Err("non-funcref and non-externref reference types are not suported yet"), 298 }, 299 } 300 } 301 } 302 303 /// Enumerate the types of v128. 304 #[derive(Copy, Clone, Debug, Arbitrary, Hash)] 305 #[allow(missing_docs)] 306 pub enum DiffSimdTy { 307 I8x16, 308 I16x8, 309 I32x4, 310 I64x2, 311 F32x4, 312 F64x2, 313 } 314