1 //! Generate Cranelift compiler settings. 2 3 use arbitrary::{Arbitrary, Unstructured}; 4 5 /// Choose between matching the host architecture or a cross-compilation target. 6 #[derive(Clone, Debug, Eq, Hash, PartialEq)] 7 pub enum CodegenSettings { 8 /// Use the host's feature set. 9 Native, 10 /// Generate a modified flag set for the current host. 11 #[allow(dead_code)] 12 Target { 13 /// The target triple of the host. 14 target: String, 15 /// A list of CPU features to enable, e.g., `("has_avx", "false")`. 16 flags: Vec<(String, String)>, 17 }, 18 } 19 20 impl CodegenSettings { 21 /// Configure Wasmtime with these codegen settings. 22 pub fn configure(&self, config: &mut wasmtime::Config) { 23 match self { 24 CodegenSettings::Native => {} 25 CodegenSettings::Target { target, flags } => { 26 config.target(target).unwrap(); 27 for (key, value) in flags { 28 unsafe { 29 config.cranelift_flag_set(key, value); 30 } 31 } 32 } 33 } 34 } 35 } 36 37 impl<'a> Arbitrary<'a> for CodegenSettings { 38 #[allow(unused_macros, unused_variables)] 39 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> { 40 // Helper macro to enable clif features based on what the native host 41 // supports. If the input says to enable a feature and the host doesn't 42 // support it then that test case is rejected with a warning. 43 // 44 // Note that this specifically consumes bytes from the fuzz input for 45 // features for all targets, discarding anything which isn't applicable 46 // to the current target. The theory behind this is that most fuzz bugs 47 // won't be related to this feature selection so by consistently 48 // consuming input irrespective of the current platform reproducing fuzz 49 // bugs should be easier between different architectures. 50 macro_rules! target_features { 51 ( 52 $( 53 $arch:tt => { 54 test:$test:ident, 55 $(std: $std:tt => clif: $clif:tt $(ratio: $a:tt in $b:tt)?,)* 56 }, 57 )* 58 ) => ({ 59 let mut flags = Vec::new(); 60 $( // for each `$arch` 61 $( // for each `$std`/`$clif` pair 62 // Use the input to generate whether `$clif` will be 63 // enabled. By default this is a 1 in 2 chance but each 64 // feature supports a custom ratio as well which shadows 65 // the (low, hi) 66 let (low, hi) = (1, 2); 67 $(let (low, hi) = ($a, $b);)? 68 let enable = u.ratio(low, hi)?; 69 70 // If we're actually on the relevant platform and the 71 // feature is enabled be sure to check that this host 72 // supports it. If the host doesn't support it then 73 // print a warning and return an error because this fuzz 74 // input must be discarded. 75 #[cfg(target_arch = $arch)] 76 if enable && !std::arch::$test!($std) { 77 log::warn!("want to enable clif `{}` but host doesn't support it", 78 $clif); 79 return Err(arbitrary::Error::EmptyChoose) 80 } 81 82 // And finally actually push the feature into the set of 83 // flags to enable, but only if we're on the right 84 // architecture. 85 if cfg!(target_arch = $arch) { 86 flags.push(( 87 $clif.to_string(), 88 enable.to_string(), 89 )); 90 } 91 )* 92 )* 93 flags 94 }) 95 } 96 if u.ratio(1, 10)? { 97 let flags = target_features! { 98 "x86_64" => { 99 test: is_x86_feature_detected, 100 101 std:"sse3" => clif:"has_sse3", 102 std:"ssse3" => clif:"has_ssse3", 103 std:"sse4.1" => clif:"has_sse41", 104 std:"sse4.2" => clif:"has_sse42", 105 std:"popcnt" => clif:"has_popcnt", 106 std:"avx" => clif:"has_avx", 107 std:"avx2" => clif:"has_avx2", 108 std:"fma" => clif:"has_fma", 109 std:"bmi1" => clif:"has_bmi1", 110 std:"bmi2" => clif:"has_bmi2", 111 std:"lzcnt" => clif:"has_lzcnt", 112 113 // not a lot of of cpus support avx512 so these are weighted 114 // to get enabled much less frequently. 115 std:"avx512bitalg" => clif:"has_avx512bitalg" ratio:1 in 1000, 116 std:"avx512dq" => clif:"has_avx512dq" ratio: 1 in 1000, 117 std:"avx512f" => clif:"has_avx512f" ratio: 1 in 1000, 118 std:"avx512vl" => clif:"has_avx512vl" ratio: 1 in 1000, 119 std:"avx512vbmi" => clif:"has_avx512vbmi" ratio: 1 in 1000, 120 }, 121 "aarch64" => { 122 test: is_aarch64_feature_detected, 123 124 std: "bti" => clif: "use_bti", 125 std: "lse" => clif: "has_lse", 126 std: "fp16" => clif: "has_fp16", 127 // even though the natural correspondence seems to be 128 // between "paca" and "has_pauth", the latter has no effect 129 // in isolation, so we actually use the setting that affects 130 // code generation 131 std: "paca" => clif: "sign_return_address", 132 // "paca" and "pacg" check for the same underlying 133 // architectural feature, so we use the latter to cover more 134 // code generation settings, of which we have chosen the one 135 // with the most significant effect 136 std: "pacg" => clif: "sign_return_address_all" ratio: 1 in 2, 137 }, 138 }; 139 return Ok(CodegenSettings::Target { 140 target: target_lexicon::Triple::host().to_string(), 141 flags, 142 }); 143 } 144 Ok(CodegenSettings::Native) 145 } 146 } 147