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 } 17 18 impl DiffValue { 19 fn ty(&self) -> DiffValueType { 20 match self { 21 DiffValue::I32(_) => DiffValueType::I32, 22 DiffValue::I64(_) => DiffValueType::I64, 23 DiffValue::F32(_) => DiffValueType::F32, 24 DiffValue::F64(_) => DiffValueType::F64, 25 DiffValue::V128(_) => DiffValueType::V128, 26 } 27 } 28 29 /// Generate a [`DiffValue`] of the given `ty` type. 30 /// 31 /// This function will bias the returned value 50% of the time towards one 32 /// of a set of known values (e.g., NaN, -1, 0, infinity, etc.). 33 pub fn arbitrary_of_type( 34 u: &mut Unstructured<'_>, 35 ty: DiffValueType, 36 ) -> arbitrary::Result<Self> { 37 use DiffValueType::*; 38 let val = match ty { 39 I32 => DiffValue::I32(biased_arbitrary_value(u, KNOWN_I32_VALUES)?), 40 I64 => DiffValue::I64(biased_arbitrary_value(u, KNOWN_I64_VALUES)?), 41 F32 => { 42 // TODO once `to_bits` is stable as a `const` function, move 43 // this to a `const` definition. 44 let known_f32_values = &[ 45 f32::NAN.to_bits(), 46 f32::INFINITY.to_bits(), 47 f32::NEG_INFINITY.to_bits(), 48 f32::MIN.to_bits(), 49 (-1.0f32).to_bits(), 50 (0.0f32).to_bits(), 51 (1.0f32).to_bits(), 52 f32::MAX.to_bits(), 53 ]; 54 DiffValue::F32(biased_arbitrary_value(u, known_f32_values)?) 55 } 56 F64 => { 57 // TODO once `to_bits` is stable as a `const` function, move 58 // this to a `const` definition. 59 let known_f64_values = &[ 60 f64::NAN.to_bits(), 61 f64::INFINITY.to_bits(), 62 f64::NEG_INFINITY.to_bits(), 63 f64::MIN.to_bits(), 64 (-1.0f64).to_bits(), 65 (0.0f64).to_bits(), 66 (1.0f64).to_bits(), 67 f64::MAX.to_bits(), 68 ]; 69 DiffValue::F64(biased_arbitrary_value(u, known_f64_values)?) 70 } 71 V128 => DiffValue::V128(biased_arbitrary_value(u, KNOWN_U128_VALUES)?), 72 }; 73 arbitrary::Result::Ok(val) 74 } 75 } 76 77 const KNOWN_I32_VALUES: &[i32] = &[i32::MIN, -1, 0, 1, i32::MAX]; 78 const KNOWN_I64_VALUES: &[i64] = &[i64::MIN, -1, 0, 1, i64::MAX]; 79 const KNOWN_U128_VALUES: &[u128] = &[u128::MIN, 1, u128::MAX]; 80 81 /// Helper function to pick a known value from the list of `known_values` half 82 /// the time. 83 fn biased_arbitrary_value<'a, T>( 84 u: &mut Unstructured<'a>, 85 known_values: &[T], 86 ) -> arbitrary::Result<T> 87 where 88 T: Arbitrary<'a> + Copy, 89 { 90 let pick_from_known_values: bool = u.arbitrary()?; 91 if pick_from_known_values { 92 Ok(*u.choose(known_values)?) 93 } else { 94 u.arbitrary() 95 } 96 } 97 98 impl<'a> Arbitrary<'a> for DiffValue { 99 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> { 100 let ty: DiffValueType = u.arbitrary()?; 101 DiffValue::arbitrary_of_type(u, ty) 102 } 103 } 104 105 impl Hash for DiffValue { 106 fn hash<H: std::hash::Hasher>(&self, state: &mut H) { 107 self.ty().hash(state); 108 match self { 109 DiffValue::I32(n) => n.hash(state), 110 DiffValue::I64(n) => n.hash(state), 111 DiffValue::F32(n) => n.hash(state), 112 DiffValue::F64(n) => n.hash(state), 113 DiffValue::V128(n) => n.hash(state), 114 } 115 } 116 } 117 118 /// Implement equality checks. Note that floating-point values are not compared 119 /// bit-for-bit in the case of NaNs: because Wasm floating-point numbers may be 120 /// [arithmetic NaNs with arbitrary payloads] and Wasm operations are [not 121 /// required to propagate NaN payloads], we simply check that both sides are 122 /// NaNs here. We could be more strict, though: we could check that the NaN 123 /// signs are equal and that [canonical NaN payloads remain canonical]. 124 /// 125 /// [arithmetic NaNs with arbitrary payloads]: 126 /// https://webassembly.github.io/spec/core/bikeshed/index.html#floating-point%E2%91%A0 127 /// [not required to propagate NaN payloads]: 128 /// https://webassembly.github.io/spec/core/bikeshed/index.html#floating-point-operations%E2%91%A0 129 /// [canonical NaN payloads remain canonical]: 130 /// https://webassembly.github.io/spec/core/bikeshed/index.html#nan-propagation%E2%91%A0 131 impl PartialEq for DiffValue { 132 fn eq(&self, other: &Self) -> bool { 133 match (self, other) { 134 (Self::I32(l0), Self::I32(r0)) => l0 == r0, 135 (Self::I64(l0), Self::I64(r0)) => l0 == r0, 136 (Self::V128(l0), Self::V128(r0)) => l0 == r0, 137 (Self::F32(l0), Self::F32(r0)) => { 138 let l0 = f32::from_bits(*l0); 139 let r0 = f32::from_bits(*r0); 140 l0 == r0 || (l0.is_nan() && r0.is_nan()) 141 } 142 (Self::F64(l0), Self::F64(r0)) => { 143 let l0 = f64::from_bits(*l0); 144 let r0 = f64::from_bits(*r0); 145 l0 == r0 || (l0.is_nan() && r0.is_nan()) 146 } 147 _ => false, 148 } 149 } 150 } 151 152 /// Enumerate the supported value types. 153 #[derive(Clone, Debug, Arbitrary, Hash)] 154 #[allow(missing_docs)] 155 pub enum DiffValueType { 156 I32, 157 I64, 158 F32, 159 F64, 160 V128, 161 } 162 163 impl TryFrom<wasmtime::ValType> for DiffValueType { 164 type Error = &'static str; 165 fn try_from(ty: wasmtime::ValType) -> Result<Self, Self::Error> { 166 use wasmtime::ValType::*; 167 match ty { 168 I32 => Ok(Self::I32), 169 I64 => Ok(Self::I64), 170 F32 => Ok(Self::F32), 171 F64 => Ok(Self::F64), 172 V128 => Ok(Self::V128), 173 FuncRef => Err("unable to convert reference types"), 174 ExternRef => Err("unable to convert reference types"), 175 } 176 } 177 } 178