1use crate::HashSet;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
11pub enum OptLevel {
12 O0,
16 #[default]
20 O1,
21 O2,
26}
27
28impl OptLevel {
29 pub fn default_enabled(self, pass: SirPass) -> bool {
31 match self {
32 OptLevel::O0 => matches!(pass, SirPass::TailCallSplit),
33 OptLevel::O1 | OptLevel::O2 => true,
34 }
35 }
36
37 pub fn parse(s: &str) -> Option<Self> {
39 match s {
40 "O0" | "o0" => Some(Self::O0),
41 "O1" | "o1" => Some(Self::O1),
42 "O2" | "o2" => Some(Self::O2),
43 _ => None,
44 }
45 }
46
47 pub fn as_str(self) -> &'static str {
49 match self {
50 OptLevel::O0 => "O0",
51 OptLevel::O1 => "O1",
52 OptLevel::O2 => "O2",
53 }
54 }
55}
56
57macro_rules! define_sir_passes {
60 ($( $(#[$meta:meta])* $variant:ident => $name:literal ),+ $(,)?) => {
61 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
63 pub enum SirPass {
64 $( $(#[$meta])* $variant, )+
65 }
66
67 impl SirPass {
68 pub const ALL: &'static [Self] = &[$(Self::$variant),+];
70
71 pub fn as_str(self) -> &'static str {
73 match self {
74 $(Self::$variant => $name),+
75 }
76 }
77
78 pub fn parse(value: &str) -> Option<Self> {
80 match value {
81 $($name => Some(Self::$variant),)+
82 _ => None,
83 }
84 }
85 }
86 };
87}
88
89define_sir_passes! {
90 StoreLoadForwarding => "store_load_forwarding",
91 ControlFlowSimplify => "control_flow_simplify",
92 HoistCommonBranchLoads => "hoist_common_branch_loads",
93 BitExtractPeephole => "bit_extract_peephole",
94 OptimizeBlocks => "optimize_blocks",
95 SplitWideCommits => "split_wide_commits",
96 CommitSinking => "commit_sinking",
97 InlineCommitForwarding => "inline_commit_forwarding",
98 EliminateDeadWorkingStores => "eliminate_dead_working_stores",
99 Reschedule => "reschedule",
100 CoalesceStores => "coalesce_stores",
101 Gvn => "gvn",
102 ConcatFolding => "concat_folding",
103 XorChainFolding => "xor_chain_folding",
104 VectorizeConcat => "vectorize_concat",
105 MaskedArrayAny => "masked_array_any",
106 CircularPriority => "circular_priority",
107 IndexedStoreRecovery => "indexed_store_recovery",
108 GuardedRegionSinking => "guarded_region_sinking",
109 LoopIdiom => "loop_idiom",
110 PackedScatterStore => "packed_scatter_store",
111 SparseCaseDispatch => "sparse_case_dispatch",
112 BranchifyMux => "branchify_mux",
113 SplitCoalescedStores => "split_coalesced_stores",
114 PartialForward => "partial_forward",
115 IdentityStoreBypass => "identity_store_bypass",
116 TailCallSplit => "tail_call_split",
119}
120#[derive(Debug, Clone, Default, PartialEq, Eq)]
125pub struct SirDiagnostics {
126 pub pass_timing: bool,
127 pub branchify_stats: bool,
128 pub mux_chain_stats: bool,
129 pub verify_boundaries: bool,
130 pub verify_passes: bool,
131 pub branchify_verify: bool,
132 pub branchify_trace_reg: Option<usize>,
133 pub effect_case_dispatch: bool,
134}
135
136#[derive(Debug, Clone)]
157pub struct OptimizeOptions {
158 opt_level: OptLevel,
159 enabled: HashSet<SirPass>,
160 disabled: HashSet<SirPass>,
161 max_native_memory_width: usize,
162 pub diagnostics: SirDiagnostics,
163}
164
165impl Default for OptimizeOptions {
166 fn default() -> Self {
167 Self::new(OptLevel::default())
168 }
169}
170
171impl OptimizeOptions {
172 pub fn new(level: OptLevel) -> Self {
174 Self {
175 opt_level: level,
176 enabled: HashSet::default(),
177 disabled: HashSet::default(),
178 max_native_memory_width: if cfg!(feature = "wide-native-memory") {
179 128
180 } else {
181 64
182 },
183 diagnostics: SirDiagnostics::default(),
184 }
185 }
186
187 pub fn all() -> Self {
189 Self::new(OptLevel::O1)
190 }
191
192 pub fn none() -> Self {
194 Self::new(OptLevel::O0)
195 }
196
197 pub fn enable(mut self, pass: SirPass) -> Self {
199 self.disabled.remove(&pass);
200 self.enabled.insert(pass);
201 self
202 }
203
204 pub fn disable(mut self, pass: SirPass) -> Self {
206 self.enabled.remove(&pass);
207 self.disabled.insert(pass);
208 self
209 }
210
211 pub fn with_max_native_memory_width(mut self, width: usize) -> Self {
216 assert!(
217 matches!(width, 64 | 128),
218 "coalesced Store width must be 64 or 128 bits"
219 );
220 self.max_native_memory_width = width;
221 self
222 }
223
224 pub fn max_native_memory_width(&self) -> usize {
225 self.max_native_memory_width
226 }
227
228 pub fn is_enabled(&self, pass: SirPass) -> bool {
230 if self.enabled.contains(&pass) {
231 return true;
232 }
233 if self.disabled.contains(&pass) {
234 return false;
235 }
236 self.opt_level.default_enabled(pass)
237 }
238
239 pub fn any_enabled(&self) -> bool {
241 SirPass::ALL
242 .iter()
243 .any(|&p| p != SirPass::TailCallSplit && self.is_enabled(p))
244 }
245
246 pub fn opt_level(&self) -> OptLevel {
248 self.opt_level
249 }
250}
251
252#[derive(Debug, Clone)]
253pub struct PassOptions {
254 pub max_inflight_loads: usize,
255 pub four_state: bool,
256 pub optimize_options: OptimizeOptions,
257 pub preserve_element_storage_layout: bool,
260}
261
262impl Default for PassOptions {
263 fn default() -> Self {
264 Self {
265 max_inflight_loads: 8,
266 four_state: false,
267 optimize_options: OptimizeOptions::default(),
268 preserve_element_storage_layout: false,
269 }
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::{OptLevel, OptimizeOptions, SirPass};
276 use crate::HashSet;
277
278 #[test]
279 fn every_pass_name_round_trips_and_is_unique() {
280 let names = SirPass::ALL
281 .iter()
282 .map(|&pass| {
283 let name = pass.as_str();
284 assert_eq!(SirPass::parse(name), Some(pass));
285 name
286 })
287 .collect::<HashSet<_>>();
288
289 assert_eq!(names.len(), SirPass::ALL.len());
290 }
291
292 #[test]
293 fn o0_only_enables_the_backend_compatibility_selector() {
294 let options = OptimizeOptions::new(OptLevel::O0);
295 for &pass in SirPass::ALL {
296 assert_eq!(options.is_enabled(pass), pass == SirPass::TailCallSplit);
297 }
298 assert!(!options.any_enabled());
299 }
300
301 #[test]
302 fn explicit_overrides_take_precedence_over_the_level() {
303 let options = OptimizeOptions::new(OptLevel::O0)
304 .enable(SirPass::Gvn)
305 .disable(SirPass::TailCallSplit);
306 assert!(options.is_enabled(SirPass::Gvn));
307 assert!(!options.is_enabled(SirPass::TailCallSplit));
308 assert!(options.any_enabled());
309 }
310
311 #[test]
312 fn branchify_is_enabled_by_optimization_presets() {
313 assert!(OptimizeOptions::new(OptLevel::O1).is_enabled(SirPass::BranchifyMux));
314 assert!(OptimizeOptions::new(OptLevel::O2).is_enabled(SirPass::BranchifyMux));
315 assert!(OptimizeOptions::all().is_enabled(SirPass::BranchifyMux));
316 }
317
318 #[test]
319 fn control_flow_simplify_is_a_production_default() {
320 assert!(OptimizeOptions::new(OptLevel::O1).is_enabled(SirPass::ControlFlowSimplify));
321 assert!(OptimizeOptions::new(OptLevel::O2).is_enabled(SirPass::ControlFlowSimplify));
322 assert!(!OptimizeOptions::new(OptLevel::O0).is_enabled(SirPass::ControlFlowSimplify));
323 }
324
325 #[test]
326 fn masked_array_any_is_cli_addressable_and_a_production_default() {
327 assert_eq!(
328 SirPass::parse("masked_array_any"),
329 Some(SirPass::MaskedArrayAny)
330 );
331 assert_eq!(SirPass::MaskedArrayAny.as_str(), "masked_array_any");
332 assert!(OptimizeOptions::new(OptLevel::O1).is_enabled(SirPass::MaskedArrayAny));
333 assert!(OptimizeOptions::new(OptLevel::O2).is_enabled(SirPass::MaskedArrayAny));
334 assert!(!OptimizeOptions::new(OptLevel::O0).is_enabled(SirPass::MaskedArrayAny));
335 }
336
337 #[test]
338 fn circular_priority_is_cli_addressable_and_a_production_default() {
339 assert_eq!(
340 SirPass::parse("circular_priority"),
341 Some(SirPass::CircularPriority)
342 );
343 assert_eq!(SirPass::CircularPriority.as_str(), "circular_priority");
344 assert!(OptimizeOptions::new(OptLevel::O1).is_enabled(SirPass::CircularPriority));
345 assert!(OptimizeOptions::new(OptLevel::O2).is_enabled(SirPass::CircularPriority));
346 assert!(!OptimizeOptions::new(OptLevel::O0).is_enabled(SirPass::CircularPriority));
347 }
348
349 #[test]
350 fn indexed_store_recovery_is_cli_addressable_and_a_production_default() {
351 assert_eq!(
352 SirPass::parse("indexed_store_recovery"),
353 Some(SirPass::IndexedStoreRecovery)
354 );
355 assert_eq!(
356 SirPass::IndexedStoreRecovery.as_str(),
357 "indexed_store_recovery"
358 );
359 assert!(OptimizeOptions::new(OptLevel::O1).is_enabled(SirPass::IndexedStoreRecovery));
360 assert!(OptimizeOptions::new(OptLevel::O2).is_enabled(SirPass::IndexedStoreRecovery));
361 assert!(!OptimizeOptions::new(OptLevel::O0).is_enabled(SirPass::IndexedStoreRecovery));
362 assert!(
363 !OptimizeOptions::new(OptLevel::O2)
364 .disable(SirPass::IndexedStoreRecovery)
365 .is_enabled(SirPass::IndexedStoreRecovery)
366 );
367 }
368}