Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/fuzzgen/src/cranelift_arbitrary.rs
3050 views
1
use crate::codegen::ir::{ArgumentExtension, ArgumentPurpose};
2
use anyhow::Result;
3
use cranelift::codegen::data_value::DataValue;
4
use cranelift::codegen::ir::types::*;
5
use cranelift::codegen::ir::{AbiParam, Signature};
6
use cranelift::codegen::isa::CallConv;
7
8
use arbitrary::Unstructured;
9
use cranelift::prelude::{Ieee32, Ieee64};
10
use target_lexicon::Architecture;
11
12
/// A trait for generating random Cranelift datastructures.
13
pub trait CraneliftArbitrary {
14
fn _type(&mut self, simd_enabled: bool) -> Result<Type>;
15
fn callconv(&mut self, architecture: Architecture) -> Result<CallConv>;
16
fn abi_param(&mut self, simd_enabled: bool) -> Result<AbiParam>;
17
fn signature(
18
&mut self,
19
simd_enabled: bool,
20
architecture: Architecture,
21
max_params: usize,
22
max_rets: usize,
23
) -> Result<Signature>;
24
fn datavalue(&mut self, ty: Type) -> Result<DataValue>;
25
}
26
27
impl<'a> CraneliftArbitrary for &mut Unstructured<'a> {
28
fn _type(&mut self, simd_enabled: bool) -> Result<Type> {
29
// TODO: It would be nice if we could get these directly from cranelift
30
let choices: &[Type] = if simd_enabled {
31
&[
32
I8, I16, I32, I64, I128, // Scalar Integers
33
F32, F64, // Scalar Floats
34
I8X16, I16X8, I32X4, I64X2, // SIMD Integers
35
F32X4, F64X2, // SIMD Floats
36
]
37
} else {
38
&[I8, I16, I32, I64, I128, F32, F64]
39
};
40
41
Ok(*self.choose(choices)?)
42
}
43
44
fn callconv(&mut self, architecture: Architecture) -> Result<CallConv> {
45
// These are implemented and should work on all backends
46
let mut allowed_callconvs = vec![
47
CallConv::Fast,
48
CallConv::PreserveAll,
49
CallConv::SystemV,
50
CallConv::Tail,
51
];
52
53
// Fastcall is supposed to work on x86 and aarch64
54
if matches!(
55
architecture,
56
Architecture::X86_64 | Architecture::Aarch64(_)
57
) {
58
allowed_callconvs.push(CallConv::WindowsFastcall);
59
}
60
61
// AArch64 has a few Apple specific calling conventions
62
if matches!(architecture, Architecture::Aarch64(_)) {
63
allowed_callconvs.push(CallConv::AppleAarch64);
64
}
65
66
// The winch calling convention is supposed to work on x64.
67
if matches!(architecture, Architecture::X86_64) {
68
allowed_callconvs.push(CallConv::Winch);
69
}
70
71
Ok(*self.choose(&allowed_callconvs[..])?)
72
}
73
74
fn abi_param(&mut self, simd_enabled: bool) -> Result<AbiParam> {
75
let value_type = self._type(simd_enabled)?;
76
// TODO: There are more argument purposes to be explored...
77
let purpose = ArgumentPurpose::Normal;
78
let extension = if value_type.is_int() {
79
*self.choose(&[
80
ArgumentExtension::Sext,
81
ArgumentExtension::Uext,
82
ArgumentExtension::None,
83
])?
84
} else {
85
ArgumentExtension::None
86
};
87
88
Ok(AbiParam {
89
value_type,
90
purpose,
91
extension,
92
})
93
}
94
95
fn signature(
96
&mut self,
97
mut simd_enabled: bool,
98
architecture: Architecture,
99
max_params: usize,
100
mut max_rets: usize,
101
) -> Result<Signature> {
102
let callconv = self.callconv(architecture)?;
103
104
// Winch doesn't support SIMD yet
105
// https://github.com/bytecodealliance/wasmtime/issues/8093
106
if callconv == CallConv::Winch {
107
simd_enabled = false;
108
}
109
110
// We can't have any returns in the `preserve_all` calling convention.
111
if callconv == CallConv::PreserveAll {
112
max_rets = 0;
113
}
114
115
let mut sig = Signature::new(callconv);
116
117
for _ in 0..max_params {
118
sig.params.push(self.abi_param(simd_enabled)?);
119
}
120
121
for _ in 0..max_rets {
122
sig.returns.push(self.abi_param(simd_enabled)?);
123
}
124
125
Ok(sig)
126
}
127
128
fn datavalue(&mut self, ty: Type) -> Result<DataValue> {
129
Ok(match ty {
130
ty if ty.is_int() => {
131
let imm = match ty {
132
I8 => self.arbitrary::<i8>()? as i128,
133
I16 => self.arbitrary::<i16>()? as i128,
134
I32 => self.arbitrary::<i32>()? as i128,
135
I64 => self.arbitrary::<i64>()? as i128,
136
I128 => self.arbitrary::<i128>()?,
137
_ => unreachable!(),
138
};
139
DataValue::from_integer(imm, ty)?
140
}
141
// f{32,64}::arbitrary does not generate a bunch of important values
142
// such as Signaling NaN's / NaN's with payload, so generate floats from integers.
143
F32 => DataValue::F32(Ieee32::with_bits(self.arbitrary::<u32>()?)),
144
F64 => DataValue::F64(Ieee64::with_bits(self.arbitrary::<u64>()?)),
145
ty if ty.is_vector() && ty.bits() == 128 => {
146
DataValue::V128(self.arbitrary::<[u8; 16]>()?)
147
}
148
_ => unimplemented!(),
149
})
150
}
151
}
152
153