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