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