wasmtime_fuzzing/generators/
value.rs1use arbitrary::{Arbitrary, Unstructured};
4use std::hash::Hash;
5use wasmtime::HeapType;
6
7#[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}
21
22impl DiffValue {
23 fn ty(&self) -> DiffValueType {
24 match self {
25 DiffValue::I32(_) => DiffValueType::I32,
26 DiffValue::I64(_) => DiffValueType::I64,
27 DiffValue::F32(_) => DiffValueType::F32,
28 DiffValue::F64(_) => DiffValueType::F64,
29 DiffValue::V128(_) => DiffValueType::V128,
30 DiffValue::FuncRef { .. } => DiffValueType::FuncRef,
31 DiffValue::ExternRef { .. } => DiffValueType::ExternRef,
32 DiffValue::AnyRef { .. } => DiffValueType::AnyRef,
33 }
34 }
35
36 pub fn arbitrary_of_type(
41 u: &mut Unstructured<'_>,
42 ty: DiffValueType,
43 ) -> arbitrary::Result<Self> {
44 use DiffValueType::*;
45 let val = match ty {
46 I32 => DiffValue::I32(biased_arbitrary_value(u, KNOWN_I32_VALUES)?),
47 I64 => DiffValue::I64(biased_arbitrary_value(u, KNOWN_I64_VALUES)?),
48 F32 => {
49 let known_f32_values = &[
52 f32::NAN.to_bits(),
53 f32::INFINITY.to_bits(),
54 f32::NEG_INFINITY.to_bits(),
55 f32::MIN.to_bits(),
56 (-1.0f32).to_bits(),
57 (0.0f32).to_bits(),
58 (1.0f32).to_bits(),
59 f32::MAX.to_bits(),
60 ];
61 let bits = biased_arbitrary_value(u, known_f32_values)?;
62
63 let bits = if f32::from_bits(bits).is_nan() {
68 f32::NAN.to_bits()
69 } else {
70 bits
71 };
72 DiffValue::F32(bits)
73 }
74 F64 => {
75 let known_f64_values = &[
78 f64::NAN.to_bits(),
79 f64::INFINITY.to_bits(),
80 f64::NEG_INFINITY.to_bits(),
81 f64::MIN.to_bits(),
82 (-1.0f64).to_bits(),
83 (0.0f64).to_bits(),
84 (1.0f64).to_bits(),
85 f64::MAX.to_bits(),
86 ];
87 let bits = biased_arbitrary_value(u, known_f64_values)?;
88 let bits = if f64::from_bits(bits).is_nan() {
91 f64::NAN.to_bits()
92 } else {
93 bits
94 };
95 DiffValue::F64(bits)
96 }
97 V128 => {
98 let ty: DiffSimdTy = u.arbitrary()?;
100 match ty {
101 DiffSimdTy::I8x16 => {
102 let mut i8 = || biased_arbitrary_value(u, KNOWN_I8_VALUES).map(|b| b as u8);
103 let vector = u128::from_le_bytes([
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 i8()?,
119 i8()?,
120 ]);
121 DiffValue::V128(vector)
122 }
123 DiffSimdTy::I16x8 => {
124 let mut i16 =
125 || biased_arbitrary_value(u, KNOWN_I16_VALUES).map(i16::to_le_bytes);
126 let vector: Vec<u8> = i16()?
127 .into_iter()
128 .chain(i16()?)
129 .chain(i16()?)
130 .chain(i16()?)
131 .chain(i16()?)
132 .chain(i16()?)
133 .chain(i16()?)
134 .chain(i16()?)
135 .collect();
136 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
137 }
138 DiffSimdTy::I32x4 => {
139 let mut i32 =
140 || biased_arbitrary_value(u, KNOWN_I32_VALUES).map(i32::to_le_bytes);
141 let vector: Vec<u8> = i32()?
142 .into_iter()
143 .chain(i32()?)
144 .chain(i32()?)
145 .chain(i32()?)
146 .collect();
147 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
148 }
149 DiffSimdTy::I64x2 => {
150 let mut i64 =
151 || biased_arbitrary_value(u, KNOWN_I64_VALUES).map(i64::to_le_bytes);
152 let vector: Vec<u8> = i64()?.into_iter().chain(i64()?).collect();
153 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
154 }
155 DiffSimdTy::F32x4 => {
156 let mut f32 = || {
157 Self::arbitrary_of_type(u, DiffValueType::F32).map(|v| match v {
158 DiffValue::F32(v) => v.to_le_bytes(),
159 _ => unreachable!(),
160 })
161 };
162 let vector: Vec<u8> = f32()?
163 .into_iter()
164 .chain(f32()?)
165 .chain(f32()?)
166 .chain(f32()?)
167 .collect();
168 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
169 }
170 DiffSimdTy::F64x2 => {
171 let mut f64 = || {
172 Self::arbitrary_of_type(u, DiffValueType::F64).map(|v| match v {
173 DiffValue::F64(v) => v.to_le_bytes(),
174 _ => unreachable!(),
175 })
176 };
177 let vector: Vec<u8> = f64()?.into_iter().chain(f64()?).collect();
178 DiffValue::V128(u128::from_le_bytes(vector.try_into().unwrap()))
179 }
180 }
181 }
182
183 FuncRef => DiffValue::FuncRef { null: true },
187 ExternRef => DiffValue::ExternRef { null: true },
188 AnyRef => DiffValue::AnyRef { null: true },
189 };
190 arbitrary::Result::Ok(val)
191 }
192}
193
194const KNOWN_I8_VALUES: &[i8] = &[i8::MIN, -1, 0, 1, i8::MAX];
195const KNOWN_I16_VALUES: &[i16] = &[i16::MIN, -1, 0, 1, i16::MAX];
196const KNOWN_I32_VALUES: &[i32] = &[i32::MIN, -1, 0, 1, i32::MAX];
197const KNOWN_I64_VALUES: &[i64] = &[i64::MIN, -1, 0, 1, i64::MAX];
198
199fn biased_arbitrary_value<'a, T>(
202 u: &mut Unstructured<'a>,
203 known_values: &[T],
204) -> arbitrary::Result<T>
205where
206 T: Arbitrary<'a> + Copy,
207{
208 let pick_from_known_values: bool = u.arbitrary()?;
209 if pick_from_known_values {
210 Ok(*u.choose(known_values)?)
211 } else {
212 u.arbitrary()
213 }
214}
215
216impl<'a> Arbitrary<'a> for DiffValue {
217 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
218 let ty: DiffValueType = u.arbitrary()?;
219 DiffValue::arbitrary_of_type(u, ty)
220 }
221}
222
223impl Hash for DiffValue {
224 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
225 self.ty().hash(state);
226 match self {
227 DiffValue::I32(n) => n.hash(state),
228 DiffValue::I64(n) => n.hash(state),
229 DiffValue::F32(n) => n.hash(state),
230 DiffValue::F64(n) => n.hash(state),
231 DiffValue::V128(n) => n.hash(state),
232 DiffValue::ExternRef { null } => null.hash(state),
233 DiffValue::FuncRef { null } => null.hash(state),
234 DiffValue::AnyRef { null } => null.hash(state),
235 }
236 }
237}
238
239impl PartialEq for DiffValue {
253 fn eq(&self, other: &Self) -> bool {
254 match (self, other) {
255 (Self::I32(l0), Self::I32(r0)) => l0 == r0,
256 (Self::I64(l0), Self::I64(r0)) => l0 == r0,
257 (Self::V128(l0), Self::V128(r0)) => l0 == r0,
258 (Self::F32(l0), Self::F32(r0)) => {
259 let l0 = f32::from_bits(*l0);
260 let r0 = f32::from_bits(*r0);
261 l0 == r0 || (l0.is_nan() && r0.is_nan())
262 }
263 (Self::F64(l0), Self::F64(r0)) => {
264 let l0 = f64::from_bits(*l0);
265 let r0 = f64::from_bits(*r0);
266 l0 == r0 || (l0.is_nan() && r0.is_nan())
267 }
268 (Self::FuncRef { null: a }, Self::FuncRef { null: b }) => a == b,
269 (Self::ExternRef { null: a }, Self::ExternRef { null: b }) => a == b,
270 (Self::AnyRef { null: a }, Self::AnyRef { null: b }) => a == b,
271 _ => false,
272 }
273 }
274}
275
276#[derive(Copy, Clone, Debug, Arbitrary, Hash)]
278#[expect(missing_docs, reason = "self-describing variants")]
279pub enum DiffValueType {
280 I32,
281 I64,
282 F32,
283 F64,
284 V128,
285 FuncRef,
286 ExternRef,
287 AnyRef,
288}
289
290impl TryFrom<wasmtime::ValType> for DiffValueType {
291 type Error = &'static str;
292 fn try_from(ty: wasmtime::ValType) -> Result<Self, Self::Error> {
293 use wasmtime::ValType::*;
294 match ty {
295 I32 => Ok(Self::I32),
296 I64 => Ok(Self::I64),
297 F32 => Ok(Self::F32),
298 F64 => Ok(Self::F64),
299 V128 => Ok(Self::V128),
300 Ref(r) => match (r.is_nullable(), r.heap_type()) {
301 (true, HeapType::Func) => Ok(Self::FuncRef),
302 (true, HeapType::Extern) => Ok(Self::ExternRef),
303 (true, HeapType::Any) => Ok(Self::AnyRef),
304 (true, HeapType::I31) => Ok(Self::AnyRef),
305 (true, HeapType::None) => Ok(Self::AnyRef),
306 _ => Err("non-null reference types are not supported yet"),
307 },
308 }
309 }
310}
311
312#[derive(Copy, Clone, Debug, Arbitrary, Hash)]
314pub enum DiffSimdTy {
315 I8x16,
316 I16x8,
317 I32x4,
318 I64x2,
319 F32x4,
320 F64x2,
321}