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