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