wasmtime_fuzzing/generators/
codegen_settings.rs

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