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}