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 36 impl<'a> Arbitrary<'a> for CodegenSettings { 37 #[expect(unused_variables, reason = "macro-generated code")] 38 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> { 39 // Helper macro to enable clif features based on what the native host 40 // supports. If the input says to enable a feature and the host doesn't 41 // support it then that test case is rejected with a warning. 42 // 43 // Note that this specifically consumes bytes from the fuzz input for 44 // features for all targets, discarding anything which isn't applicable 45 // to the current target. The theory behind this is that most fuzz bugs 46 // won't be related to this feature selection so by consistently 47 // consuming input irrespective of the current platform reproducing fuzz 48 // bugs should be easier between different architectures. 49 macro_rules! target_features { 50 ( 51 $( 52 $arch:tt => { 53 test:$test:ident, 54 $(std: $std:tt => clif: $clif:tt $(ratio: $a:tt in $b:tt)?,)* 55 }, 56 )* 57 ) => ({ 58 let mut flags = Vec::new(); 59 $( // for each `$arch` 60 $( // for each `$std`/`$clif` pair 61 // Use the input to generate whether `$clif` will be 62 // enabled. By default this is a 1 in 2 chance but each 63 // feature supports a custom ratio as well which shadows 64 // the (low, hi) 65 let (low, hi) = (1, 2); 66 $(let (low, hi) = ($a, $b);)? 67 let enable = u.ratio(low, hi)?; 68 69 // If we're actually on the relevant platform and the 70 // feature is enabled be sure to check that this host 71 // supports it. If the host doesn't support it then 72 // print a warning and return an error because this fuzz 73 // input must be discarded. 74 #[cfg(target_arch = $arch)] 75 if enable && !std::arch::$test!($std) { 76 log::warn!("want to enable clif `{}` but host doesn't support it", 77 $clif); 78 return Err(arbitrary::Error::EmptyChoose) 79 } 80 81 // And finally actually push the feature into the set of 82 // flags to enable, but only if we're on the right 83 // architecture. 84 if cfg!(target_arch = $arch) { 85 flags.push(( 86 $clif.to_string(), 87 enable.to_string(), 88 )); 89 } 90 )* 91 )* 92 flags 93 }) 94 } 95 if u.ratio(1, 10)? { 96 let flags = target_features! { 97 "x86_64" => { 98 test: is_x86_feature_detected, 99 100 std:"cmpxchg16b" => clif:"has_cmpxchg16b", 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