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