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