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