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