1c227063fSAndrew Brown //! Generate Cranelift compiler settings.
2c227063fSAndrew Brown 
3*9b896d2aSAlex Crichton use crate::generators::ModuleConfig;
4c227063fSAndrew Brown use arbitrary::{Arbitrary, Unstructured};
5*9b896d2aSAlex Crichton use std::collections::HashMap;
6c227063fSAndrew Brown 
7c227063fSAndrew Brown /// Choose between matching the host architecture or a cross-compilation target.
8c227063fSAndrew Brown #[derive(Clone, Debug, Eq, Hash, PartialEq)]
9c227063fSAndrew Brown pub enum CodegenSettings {
10c227063fSAndrew Brown     /// Use the host's feature set.
11c227063fSAndrew Brown     Native,
12c227063fSAndrew Brown     /// Generate a modified flag set for the current host.
13c227063fSAndrew Brown     #[allow(dead_code)]
14c227063fSAndrew Brown     Target {
15c227063fSAndrew Brown         /// The target triple of the host.
16c227063fSAndrew Brown         target: String,
17c227063fSAndrew Brown         /// A list of CPU features to enable, e.g., `("has_avx", "false")`.
18c227063fSAndrew Brown         flags: Vec<(String, String)>,
19c227063fSAndrew Brown     },
20c227063fSAndrew Brown }
21c227063fSAndrew Brown 
22c227063fSAndrew Brown impl CodegenSettings {
23c227063fSAndrew Brown     /// Configure Wasmtime with these codegen settings.
24c227063fSAndrew Brown     pub fn configure(&self, config: &mut wasmtime::Config) {
25c227063fSAndrew Brown         match self {
26c227063fSAndrew Brown             CodegenSettings::Native => {}
27c227063fSAndrew Brown             CodegenSettings::Target { target, flags } => {
28c227063fSAndrew Brown                 config.target(target).unwrap();
29c227063fSAndrew Brown                 for (key, value) in flags {
30c227063fSAndrew Brown                     unsafe {
31c227063fSAndrew Brown                         config.cranelift_flag_set(key, value);
32c227063fSAndrew Brown                     }
33c227063fSAndrew Brown                 }
34c227063fSAndrew Brown             }
35c227063fSAndrew Brown         }
36c227063fSAndrew Brown     }
37*9b896d2aSAlex Crichton 
38*9b896d2aSAlex Crichton     /// Features such as sse4.2 are unconditionally enabled on the x86_64 target
39*9b896d2aSAlex Crichton     /// because they are hard required for SIMD, but when SIMD is disabled, for
40*9b896d2aSAlex Crichton     /// example, we support disabling these features.
41*9b896d2aSAlex Crichton     ///
42*9b896d2aSAlex Crichton     /// This method will take the wasm feature selection chosen, through
43*9b896d2aSAlex Crichton     /// `module_config`, and possibly try to disable some more features by
44*9b896d2aSAlex Crichton     /// reading more of the input.
45*9b896d2aSAlex Crichton     pub fn maybe_disable_more_features(
46*9b896d2aSAlex Crichton         &mut self,
47*9b896d2aSAlex Crichton         module_config: &ModuleConfig,
48*9b896d2aSAlex Crichton         u: &mut Unstructured<'_>,
49*9b896d2aSAlex Crichton     ) -> arbitrary::Result<()> {
50*9b896d2aSAlex Crichton         let flags = match self {
51*9b896d2aSAlex Crichton             CodegenSettings::Target { flags, .. } => flags,
52*9b896d2aSAlex Crichton             _ => return Ok(()),
53*9b896d2aSAlex Crichton         };
54*9b896d2aSAlex Crichton 
55*9b896d2aSAlex Crichton         if !module_config.config.simd_enabled {
56*9b896d2aSAlex Crichton             // Note that regardless of architecture these booleans are generated
57*9b896d2aSAlex Crichton             // to have test case failures unrelated to codegen setting input
58*9b896d2aSAlex Crichton             // that fail on one architecture to fail on other architectures as
59*9b896d2aSAlex Crichton             // well.
60*9b896d2aSAlex Crichton             let new_flags = ["has_sse3", "has_ssse3", "has_sse41", "has_sse42"]
61*9b896d2aSAlex Crichton                 .into_iter()
62*9b896d2aSAlex Crichton                 .map(|name| Ok((name, u.arbitrary()?)))
63*9b896d2aSAlex Crichton                 .collect::<arbitrary::Result<HashMap<_, bool>>>()?;
64*9b896d2aSAlex Crichton 
65*9b896d2aSAlex Crichton             for (name, val) in flags {
66*9b896d2aSAlex Crichton                 if let Some(new_value) = new_flags.get(name.as_str()) {
67*9b896d2aSAlex Crichton                     *val = new_value.to_string();
68*9b896d2aSAlex Crichton                 }
69*9b896d2aSAlex Crichton             }
70*9b896d2aSAlex Crichton         }
71*9b896d2aSAlex Crichton         Ok(())
72*9b896d2aSAlex Crichton     }
73c227063fSAndrew Brown }
74c227063fSAndrew Brown 
75c227063fSAndrew Brown impl<'a> Arbitrary<'a> for CodegenSettings {
76c227063fSAndrew Brown     #[allow(unused_macros, unused_variables)]
77c227063fSAndrew Brown     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
78c227063fSAndrew Brown         // Helper macro to enable clif features based on what the native host
79c227063fSAndrew Brown         // supports. If the input says to enable a feature and the host doesn't
80c227063fSAndrew Brown         // support it then that test case is rejected with a warning.
81c227063fSAndrew Brown         //
82c227063fSAndrew Brown         // Note that this specifically consumes bytes from the fuzz input for
83c227063fSAndrew Brown         // features for all targets, discarding anything which isn't applicable
84c227063fSAndrew Brown         // to the current target. The theory behind this is that most fuzz bugs
85c227063fSAndrew Brown         // won't be related to this feature selection so by consistently
86c227063fSAndrew Brown         // consuming input irrespective of the current platform reproducing fuzz
87c227063fSAndrew Brown         // bugs should be easier between different architectures.
88c227063fSAndrew Brown         macro_rules! target_features {
89c227063fSAndrew Brown             (
90c227063fSAndrew Brown                 $(
91c227063fSAndrew Brown                     $arch:tt => {
92c227063fSAndrew Brown                         test:$test:ident,
93c227063fSAndrew Brown                         $(std: $std:tt => clif: $clif:tt $(ratio: $a:tt in $b:tt)?,)*
94c227063fSAndrew Brown                     },
95c227063fSAndrew Brown                 )*
96c227063fSAndrew Brown             ) => ({
97c227063fSAndrew Brown                 let mut flags = Vec::new();
98c227063fSAndrew Brown                 $( // for each `$arch`
99c227063fSAndrew Brown                     $( // for each `$std`/`$clif` pair
100c227063fSAndrew Brown                         // Use the input to generate whether `$clif` will be
101c227063fSAndrew Brown                         // enabled. By default this is a 1 in 2 chance but each
102c227063fSAndrew Brown                         // feature supports a custom ratio as well which shadows
103c227063fSAndrew Brown                         // the (low, hi)
104c227063fSAndrew Brown                         let (low, hi) = (1, 2);
105c227063fSAndrew Brown                         $(let (low, hi) = ($a, $b);)?
106c227063fSAndrew Brown                         let enable = u.ratio(low, hi)?;
107c227063fSAndrew Brown 
108c227063fSAndrew Brown                         // If we're actually on the relevant platform and the
109c227063fSAndrew Brown                         // feature is enabled be sure to check that this host
110c227063fSAndrew Brown                         // supports it. If the host doesn't support it then
111c227063fSAndrew Brown                         // print a warning and return an error because this fuzz
112c227063fSAndrew Brown                         // input must be discarded.
113c227063fSAndrew Brown                         #[cfg(target_arch = $arch)]
114c227063fSAndrew Brown                         if enable && !std::arch::$test!($std) {
115c227063fSAndrew Brown                             log::warn!("want to enable clif `{}` but host doesn't support it",
116c227063fSAndrew Brown                                 $clif);
117c227063fSAndrew Brown                             return Err(arbitrary::Error::EmptyChoose)
118c227063fSAndrew Brown                         }
119c227063fSAndrew Brown 
120c227063fSAndrew Brown                         // And finally actually push the feature into the set of
121c227063fSAndrew Brown                         // flags to enable, but only if we're on the right
122c227063fSAndrew Brown                         // architecture.
123c227063fSAndrew Brown                         if cfg!(target_arch = $arch) {
124c227063fSAndrew Brown                             flags.push((
125c227063fSAndrew Brown                                 $clif.to_string(),
126c227063fSAndrew Brown                                 enable.to_string(),
127c227063fSAndrew Brown                             ));
128c227063fSAndrew Brown                         }
129c227063fSAndrew Brown                     )*
130c227063fSAndrew Brown                 )*
131c227063fSAndrew Brown                 flags
132c227063fSAndrew Brown             })
133c227063fSAndrew Brown         }
134c227063fSAndrew Brown         if u.ratio(1, 10)? {
135c227063fSAndrew Brown             let flags = target_features! {
136c227063fSAndrew Brown                 "x86_64" => {
137c227063fSAndrew Brown                     test: is_x86_feature_detected,
138c227063fSAndrew Brown 
139c227063fSAndrew Brown                     // These features are considered to be baseline required by
140c227063fSAndrew Brown                     // Wasmtime. Currently some SIMD code generation will
141c227063fSAndrew Brown                     // fail if these features are disabled, so unconditionally
142c227063fSAndrew Brown                     // enable them as we're not interested in fuzzing without
143c227063fSAndrew Brown                     // them.
144*9b896d2aSAlex Crichton                     //
145*9b896d2aSAlex Crichton                     // Note that these may still be disabled above in
146*9b896d2aSAlex Crichton                     // `maybe_disable_more_features`.
147c227063fSAndrew Brown                     std:"sse3" => clif:"has_sse3" ratio: 1 in 1,
148c227063fSAndrew Brown                     std:"ssse3" => clif:"has_ssse3" ratio: 1 in 1,
149c227063fSAndrew Brown                     std:"sse4.1" => clif:"has_sse41" ratio: 1 in 1,
150c227063fSAndrew Brown                     std:"sse4.2" => clif:"has_sse42" ratio: 1 in 1,
151c227063fSAndrew Brown 
152c227063fSAndrew Brown                     std:"popcnt" => clif:"has_popcnt",
153c227063fSAndrew Brown                     std:"avx" => clif:"has_avx",
154c227063fSAndrew Brown                     std:"avx2" => clif:"has_avx2",
15502c3b47dSAfonso Bordado                     std:"fma" => clif:"has_fma",
156c227063fSAndrew Brown                     std:"bmi1" => clif:"has_bmi1",
157c227063fSAndrew Brown                     std:"bmi2" => clif:"has_bmi2",
158c227063fSAndrew Brown                     std:"lzcnt" => clif:"has_lzcnt",
159c227063fSAndrew Brown 
160c227063fSAndrew Brown                     // not a lot of of cpus support avx512 so these are weighted
161c227063fSAndrew Brown                     // to get enabled much less frequently.
162c227063fSAndrew Brown                     std:"avx512bitalg" => clif:"has_avx512bitalg" ratio:1 in 1000,
163c227063fSAndrew Brown                     std:"avx512dq" => clif:"has_avx512dq" ratio: 1 in 1000,
164c227063fSAndrew Brown                     std:"avx512f" => clif:"has_avx512f" ratio: 1 in 1000,
165c227063fSAndrew Brown                     std:"avx512vl" => clif:"has_avx512vl" ratio: 1 in 1000,
166c227063fSAndrew Brown                     std:"avx512vbmi" => clif:"has_avx512vbmi" ratio: 1 in 1000,
167c227063fSAndrew Brown                 },
168c227063fSAndrew Brown                 "aarch64" => {
169c227063fSAndrew Brown                     test: is_aarch64_feature_detected,
170c227063fSAndrew Brown 
171d8b29089SAnton Kirilov                     std: "bti" => clif: "use_bti",
172c227063fSAndrew Brown                     std: "lse" => clif: "has_lse",
1731481721cSAnton Kirilov                     // even though the natural correspondence seems to be
1741481721cSAnton Kirilov                     // between "paca" and "has_pauth", the latter has no effect
1751481721cSAnton Kirilov                     // in isolation, so we actually use the setting that affects
1761481721cSAnton Kirilov                     // code generation
1771481721cSAnton Kirilov                     std: "paca" => clif: "sign_return_address",
1781481721cSAnton Kirilov                     // "paca" and "pacg" check for the same underlying
1791481721cSAnton Kirilov                     // architectural feature, so we use the latter to cover more
1801481721cSAnton Kirilov                     // code generation settings, of which we have chosen the one
1811481721cSAnton Kirilov                     // with the most significant effect
1821481721cSAnton Kirilov                     std: "pacg" => clif: "sign_return_address_all" ratio: 1 in 2,
183c227063fSAndrew Brown                 },
184c227063fSAndrew Brown             };
185c227063fSAndrew Brown             return Ok(CodegenSettings::Target {
186c227063fSAndrew Brown                 target: target_lexicon::Triple::host().to_string(),
187c227063fSAndrew Brown                 flags,
188c227063fSAndrew Brown             });
189c227063fSAndrew Brown         }
190c227063fSAndrew Brown         Ok(CodegenSettings::Native)
191c227063fSAndrew Brown     }
192c227063fSAndrew Brown }
193