wasmtime_fuzzing/generators/
value.rs

1//! Generate Wasm values, primarily for differential execution.
2
3use arbitrary::{Arbitrary, Unstructured};
4use std::hash::Hash;
5use 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#[expect(missing_docs, reason = "self-describing fields")]
11pub 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    AnyRef { null: bool },
20    ExnRef { null: bool },
21    ContRef { null: bool },
22}
23
24impl DiffValue {
25    fn ty(&self) -> DiffValueType {
26        match self {
27            DiffValue::I32(_) => DiffValueType::I32,
28            DiffValue::I64(_) => DiffValueType::I64,
29            DiffValue::F32(_) => DiffValueType::F32,
30            DiffValue::F64(_) => DiffValueType::F64,
31            DiffValue::V128(_) => DiffValueType::V128,
32            DiffValue::FuncRef { .. } => DiffValueType::FuncRef,
33            DiffValue::ExternRef { .. } => DiffValueType::ExternRef,
34            DiffValue::AnyRef { .. } => DiffValueType::AnyRef,
35            DiffValue::ExnRef { .. } => DiffValueType::ExnRef,
36            DiffValue::ContRef { .. } => DiffValueType::ContRef,
37        }
38    }
39
40    /// Generate a [`DiffValue`] of the given `ty` type.
41    ///
42    /// This function will bias the returned value 50% of the time towards one
43    /// of a set of known values (e.g., NaN, -1, 0, infinity, etc.).
44    pub fn arbitrary_of_type(
45        u: &mut Unstructured<'_>,
46        ty: DiffValueType,
47    ) -> arbitrary::Result<Self> {
48        use DiffValueType::*;
49        let val = match ty {
50            I32 => DiffValue::I32(biased_arbitrary_value(u, KNOWN_I32_VALUES)?),
51            I64 => DiffValue::I64(biased_arbitrary_value(u, KNOWN_I64_VALUES)?),
52            F32 => {
53                // TODO once `to_bits` is stable as a `const` function, move
54                // this to a `const` definition.
55                let known_f32_values = &[
56                    f32::NAN.to_bits(),
57                    f32::INFINITY.to_bits(),
58                    f32::NEG_INFINITY.to_bits(),
59                    f32::MIN.to_bits(),
60                    (-1.0f32).to_bits(),
61                    (0.0f32).to_bits(),
62                    (1.0f32).to_bits(),
63                    f32::MAX.to_bits(),
64                ];
65                let bits = biased_arbitrary_value(u, known_f32_values)?;
66
67                // If the chosen bits are NaN then always use the canonical bit
68                // pattern of NaN to enable better compatibility with engines
69                // where arbitrary NaN patterns can't make their way into wasm
70                // (e.g. v8 through JS can't do that).
71                let bits = if f32::from_bits(bits).is_nan() {
72                    f32::NAN.to_bits()
73                } else {
74                    bits
75                };
76                DiffValue::F32(bits)
77            }
78            F64 => {
79                // TODO once `to_bits` is stable as a `const` function, move
80                // this to a `const` definition.
81                let known_f64_values = &[
82                    f64::NAN.to_bits(),
83                    f64::INFINITY.to_bits(),
84                    f64::NEG_INFINITY.to_bits(),
85                    f64::MIN.to_bits(),
86                    (-1.0f64).to_bits(),
87                    (0.0f64).to_bits(),
88                    (1.0f64).to_bits(),
89                    f64::MAX.to_bits(),
90                ];
91                let bits = biased_arbitrary_value(u, known_f64_values)?;
92                // See `f32` above for why canonical NaN patterns are always
93                // used.
94                let bits = if f64::from_bits(bits).is_nan() {
95                    f64::NAN.to_bits()
96                } else {
97                    bits
98                };
99                DiffValue::F64(bits)
100            }
101            V128 => {
102                // Generate known values for each sub-type of V128.
103                let ty: DiffSimdTy = u.arbitrary()?;
104                match ty {
105                    DiffSimdTy::I8x16 => {
106                        let mut i8 = || biased_arbitrary_value(u, KNOWN_I8_VALUES).map(|b| b as u8);
107                        let vector = u128::from_le_bytes([
108                            i8()?,
109                            i8()?,
110                            i8()?,
111                            i8()?,
112                            i8()?,
113                            i8()?,
114                            i8()?,
115                            i8()?,
116                            i8()?,
117                            i8()?,
118                            i8()?,
119                            i8()?,
120                            i8()?,
121                            i8()?,
122                            i8()?,
123                            i8()?,
124                        ]);
125                        DiffValue::V128(vector)
126                    }
127                    DiffSimdTy::I16x8 => {
128                        let mut i16 =
129                            || biased_arbitrary_value(u, KNOWN_I16_VALUES).map(i16::to_le_bytes);
130                        let vector: Vec<u8> = i16()?
131                            .into_iter()
132                            .chain(i16()?)
133                            .chain(i16()?)
134                            .chain(i16()?)
135                            .chain(i16()?)
136                            .chain(i16()?)
137                            .chain(i16()?)
138                            .chain(i16()?)
139                            .collect();
140                        DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
141                    }
142                    DiffSimdTy::I32x4 => {
143                        let mut i32 =
144                            || biased_arbitrary_value(u, KNOWN_I32_VALUES).map(i32::to_le_bytes);
145                        let vector: Vec<u8> = i32()?
146                            .into_iter()
147                            .chain(i32()?)
148                            .chain(i32()?)
149                            .chain(i32()?)
150                            .collect();
151                        DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
152                    }
153                    DiffSimdTy::I64x2 => {
154                        let mut i64 =
155                            || biased_arbitrary_value(u, KNOWN_I64_VALUES).map(i64::to_le_bytes);
156                        let vector: Vec<u8> = i64()?.into_iter().chain(i64()?).collect();
157                        DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
158                    }
159                    DiffSimdTy::F32x4 => {
160                        let mut f32 = || {
161                            Self::arbitrary_of_type(u, DiffValueType::F32).map(|v| match v {
162                                DiffValue::F32(v) => v.to_le_bytes(),
163                                _ => unreachable!(),
164                            })
165                        };
166                        let vector: Vec<u8> = f32()?
167                            .into_iter()
168                            .chain(f32()?)
169                            .chain(f32()?)
170                            .chain(f32()?)
171                            .collect();
172                        DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
173                    }
174                    DiffSimdTy::F64x2 => {
175                        let mut f64 = || {
176                            Self::arbitrary_of_type(u, DiffValueType::F64).map(|v| match v {
177                                DiffValue::F64(v) => v.to_le_bytes(),
178                                _ => unreachable!(),
179                            })
180                        };
181                        let vector: Vec<u8> = f64()?.into_iter().chain(f64()?).collect();
182                        DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
183                    }
184                }
185            }
186
187            // TODO: this isn't working in most engines so just always pass a
188            // null in which if an engine supports this is should at least
189            // support doing that.
190            FuncRef => DiffValue::FuncRef { null: true },
191            ExternRef => DiffValue::ExternRef { null: true },
192            AnyRef => DiffValue::AnyRef { null: true },
193            ExnRef => DiffValue::ExnRef { null: true },
194            ContRef => DiffValue::ContRef { null: true },
195        };
196        arbitrary::Result::Ok(val)
197    }
198}
199
200const KNOWN_I8_VALUES: &[i8] = &[i8::MIN, -1, 0, 1, i8::MAX];
201const KNOWN_I16_VALUES: &[i16] = &[i16::MIN, -1, 0, 1, i16::MAX];
202const KNOWN_I32_VALUES: &[i32] = &[i32::MIN, -1, 0, 1, i32::MAX];
203const KNOWN_I64_VALUES: &[i64] = &[i64::MIN, -1, 0, 1, i64::MAX];
204
205/// Helper function to pick a known value from the list of `known_values` half
206/// the time.
207fn biased_arbitrary_value<'a, T>(
208    u: &mut Unstructured<'a>,
209    known_values: &[T],
210) -> arbitrary::Result<T>
211where
212    T: Arbitrary<'a> + Copy,
213{
214    let pick_from_known_values: bool = u.arbitrary()?;
215    if pick_from_known_values {
216        Ok(*u.choose(known_values)?)
217    } else {
218        u.arbitrary()
219    }
220}
221
222impl<'a> Arbitrary<'a> for DiffValue {
223    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
224        let ty: DiffValueType = u.arbitrary()?;
225        DiffValue::arbitrary_of_type(u, ty)
226    }
227}
228
229impl Hash for DiffValue {
230    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
231        self.ty().hash(state);
232        match self {
233            DiffValue::I32(n) => n.hash(state),
234            DiffValue::I64(n) => n.hash(state),
235            DiffValue::F32(n) => n.hash(state),
236            DiffValue::F64(n) => n.hash(state),
237            DiffValue::V128(n) => n.hash(state),
238            DiffValue::ExternRef { null } => null.hash(state),
239            DiffValue::FuncRef { null } => null.hash(state),
240            DiffValue::AnyRef { null } => null.hash(state),
241            DiffValue::ExnRef { null } => null.hash(state),
242            DiffValue::ContRef { null } => null.hash(state),
243        }
244    }
245}
246
247/// Implement equality checks. Note that floating-point values are not compared
248/// bit-for-bit in the case of NaNs: because Wasm floating-point numbers may be
249/// [arithmetic NaNs with arbitrary payloads] and Wasm operations are [not
250/// required to propagate NaN payloads], we simply check that both sides are
251/// NaNs here. We could be more strict, though: we could check that the NaN
252/// signs are equal and that [canonical NaN payloads remain canonical].
253///
254/// [arithmetic NaNs with arbitrary payloads]:
255///     https://webassembly.github.io/spec/core/bikeshed/index.html#floating-point%E2%91%A0
256/// [not required to propagate NaN payloads]:
257///     https://webassembly.github.io/spec/core/bikeshed/index.html#floating-point-operations%E2%91%A0
258/// [canonical NaN payloads remain canonical]:
259///     https://webassembly.github.io/spec/core/bikeshed/index.html#nan-propagation%E2%91%A0
260impl PartialEq for DiffValue {
261    fn eq(&self, other: &Self) -> bool {
262        match (self, other) {
263            (Self::I32(l0), Self::I32(r0)) => l0 == r0,
264            (Self::I64(l0), Self::I64(r0)) => l0 == r0,
265            (Self::V128(l0), Self::V128(r0)) => l0 == r0,
266            (Self::F32(l0), Self::F32(r0)) => {
267                let l0 = f32::from_bits(*l0);
268                let r0 = f32::from_bits(*r0);
269                l0 == r0 || (l0.is_nan() && r0.is_nan())
270            }
271            (Self::F64(l0), Self::F64(r0)) => {
272                let l0 = f64::from_bits(*l0);
273                let r0 = f64::from_bits(*r0);
274                l0 == r0 || (l0.is_nan() && r0.is_nan())
275            }
276            (Self::FuncRef { null: a }, Self::FuncRef { null: b }) => a == b,
277            (Self::ExternRef { null: a }, Self::ExternRef { null: b }) => a == b,
278            (Self::AnyRef { null: a }, Self::AnyRef { null: b }) => a == b,
279            (Self::ExnRef { null: a }, Self::ExnRef { null: b }) => a == b,
280            (Self::ContRef { null: a }, Self::ContRef { null: b }) => a == b,
281            _ => false,
282        }
283    }
284}
285
286/// Enumerate the supported value types.
287#[derive(Copy, Clone, Debug, Arbitrary, Hash)]
288#[expect(missing_docs, reason = "self-describing variants")]
289pub enum DiffValueType {
290    I32,
291    I64,
292    F32,
293    F64,
294    V128,
295    FuncRef,
296    ExternRef,
297    AnyRef,
298    ExnRef,
299    ContRef,
300}
301
302impl TryFrom<wasmtime::ValType> for DiffValueType {
303    type Error = &'static str;
304    fn try_from(ty: wasmtime::ValType) -> Result<Self, Self::Error> {
305        use wasmtime::ValType::*;
306        match ty {
307            I32 => Ok(Self::I32),
308            I64 => Ok(Self::I64),
309            F32 => Ok(Self::F32),
310            F64 => Ok(Self::F64),
311            V128 => Ok(Self::V128),
312            Ref(r) => match (r.is_nullable(), r.heap_type()) {
313                (true, HeapType::Func) => Ok(Self::FuncRef),
314                (true, HeapType::Extern) => Ok(Self::ExternRef),
315                (true, HeapType::Any) => Ok(Self::AnyRef),
316                (true, HeapType::I31) => Ok(Self::AnyRef),
317                (true, HeapType::None) => Ok(Self::AnyRef),
318                (true, HeapType::Exn) => Ok(Self::ExnRef),
319                (true, HeapType::Cont) => Ok(Self::ContRef),
320                _ => Err("non-null reference types are not supported yet"),
321            },
322        }
323    }
324}
325
326/// Enumerate the types of v128.
327#[derive(Copy, Clone, Debug, Arbitrary, Hash)]
328pub enum DiffSimdTy {
329    I8x16,
330    I16x8,
331    I32x4,
332    I64x2,
333    F32x4,
334    F64x2,
335}