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