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 => DiffValue::V128(biased_arbitrary_value(u, KNOWN_U128_VALUES)?), 95 96 // TODO: this isn't working in most engines so just always pass a 97 // null in which if an engine supports this is should at least 98 // support doing that. 99 FuncRef => DiffValue::FuncRef { null: true }, 100 ExternRef => DiffValue::ExternRef { null: true }, 101 }; 102 arbitrary::Result::Ok(val) 103 } 104 } 105 106 const KNOWN_I32_VALUES: &[i32] = &[i32::MIN, -1, 0, 1, i32::MAX]; 107 const KNOWN_I64_VALUES: &[i64] = &[i64::MIN, -1, 0, 1, i64::MAX]; 108 const KNOWN_U128_VALUES: &[u128] = &[u128::MIN, 1, u128::MAX]; 109 110 /// Helper function to pick a known value from the list of `known_values` half 111 /// the time. 112 fn biased_arbitrary_value<'a, T>( 113 u: &mut Unstructured<'a>, 114 known_values: &[T], 115 ) -> arbitrary::Result<T> 116 where 117 T: Arbitrary<'a> + Copy, 118 { 119 let pick_from_known_values: bool = u.arbitrary()?; 120 if pick_from_known_values { 121 Ok(*u.choose(known_values)?) 122 } else { 123 u.arbitrary() 124 } 125 } 126 127 impl<'a> Arbitrary<'a> for DiffValue { 128 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> { 129 let ty: DiffValueType = u.arbitrary()?; 130 DiffValue::arbitrary_of_type(u, ty) 131 } 132 } 133 134 impl Hash for DiffValue { 135 fn hash<H: std::hash::Hasher>(&self, state: &mut H) { 136 self.ty().hash(state); 137 match self { 138 DiffValue::I32(n) => n.hash(state), 139 DiffValue::I64(n) => n.hash(state), 140 DiffValue::F32(n) => n.hash(state), 141 DiffValue::F64(n) => n.hash(state), 142 DiffValue::V128(n) => n.hash(state), 143 DiffValue::ExternRef { null } => null.hash(state), 144 DiffValue::FuncRef { null } => null.hash(state), 145 } 146 } 147 } 148 149 /// Implement equality checks. Note that floating-point values are not compared 150 /// bit-for-bit in the case of NaNs: because Wasm floating-point numbers may be 151 /// [arithmetic NaNs with arbitrary payloads] and Wasm operations are [not 152 /// required to propagate NaN payloads], we simply check that both sides are 153 /// NaNs here. We could be more strict, though: we could check that the NaN 154 /// signs are equal and that [canonical NaN payloads remain canonical]. 155 /// 156 /// [arithmetic NaNs with arbitrary payloads]: 157 /// https://webassembly.github.io/spec/core/bikeshed/index.html#floating-point%E2%91%A0 158 /// [not required to propagate NaN payloads]: 159 /// https://webassembly.github.io/spec/core/bikeshed/index.html#floating-point-operations%E2%91%A0 160 /// [canonical NaN payloads remain canonical]: 161 /// https://webassembly.github.io/spec/core/bikeshed/index.html#nan-propagation%E2%91%A0 162 impl PartialEq for DiffValue { 163 fn eq(&self, other: &Self) -> bool { 164 match (self, other) { 165 (Self::I32(l0), Self::I32(r0)) => l0 == r0, 166 (Self::I64(l0), Self::I64(r0)) => l0 == r0, 167 (Self::V128(l0), Self::V128(r0)) => l0 == r0, 168 (Self::F32(l0), Self::F32(r0)) => { 169 let l0 = f32::from_bits(*l0); 170 let r0 = f32::from_bits(*r0); 171 l0 == r0 || (l0.is_nan() && r0.is_nan()) 172 } 173 (Self::F64(l0), Self::F64(r0)) => { 174 let l0 = f64::from_bits(*l0); 175 let r0 = f64::from_bits(*r0); 176 l0 == r0 || (l0.is_nan() && r0.is_nan()) 177 } 178 (Self::FuncRef { null: a }, Self::FuncRef { null: b }) => a == b, 179 (Self::ExternRef { null: a }, Self::ExternRef { null: b }) => a == b, 180 _ => false, 181 } 182 } 183 } 184 185 /// Enumerate the supported value types. 186 #[derive(Copy, Clone, Debug, Arbitrary, Hash)] 187 #[allow(missing_docs)] 188 pub enum DiffValueType { 189 I32, 190 I64, 191 F32, 192 F64, 193 V128, 194 FuncRef, 195 ExternRef, 196 } 197 198 impl TryFrom<wasmtime::ValType> for DiffValueType { 199 type Error = &'static str; 200 fn try_from(ty: wasmtime::ValType) -> Result<Self, Self::Error> { 201 use wasmtime::ValType::*; 202 match ty { 203 I32 => Ok(Self::I32), 204 I64 => Ok(Self::I64), 205 F32 => Ok(Self::F32), 206 F64 => Ok(Self::F64), 207 V128 => Ok(Self::V128), 208 FuncRef => Ok(Self::FuncRef), 209 ExternRef => Ok(Self::ExternRef), 210 } 211 } 212 } 213