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 {
_type(&mut self, simd_enabled: bool) -> Result<Type>14     fn _type(&mut self, simd_enabled: bool) -> Result<Type>;
callconv(&mut self, architecture: Architecture) -> Result<CallConv>15     fn callconv(&mut self, architecture: Architecture) -> Result<CallConv>;
abi_param(&mut self, simd_enabled: bool) -> Result<AbiParam>16     fn abi_param(&mut self, simd_enabled: bool) -> Result<AbiParam>;
signature( &mut self, simd_enabled: bool, architecture: Architecture, max_params: usize, max_rets: usize, ) -> Result<Signature>17     fn signature(
18         &mut self,
19         simd_enabled: bool,
20         architecture: Architecture,
21         max_params: usize,
22         max_rets: usize,
23     ) -> Result<Signature>;
datavalue(&mut self, ty: Type) -> Result<DataValue>24     fn datavalue(&mut self, ty: Type) -> Result<DataValue>;
25 }
26 
27 impl<'a> CraneliftArbitrary for &mut Unstructured<'a> {
_type(&mut self, simd_enabled: bool) -> Result<Type>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 
callconv(&mut self, architecture: Architecture) -> Result<CallConv>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 
abi_param(&mut self, simd_enabled: bool) -> Result<AbiParam>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 
signature( &mut self, mut simd_enabled: bool, architecture: Architecture, max_params: usize, mut max_rets: usize, ) -> Result<Signature>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 
datavalue(&mut self, ty: Type) -> Result<DataValue>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