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