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