1use crate::{
23 analysis::Validator,
24 mir::{Function, Module},
25 transform::{
26 AdcePass, CfgSimplifyPass, CheckElimPass, CsePass, DcePass, FrameSlotPromotionPass,
27 FunctionDcePass, GvnPass, IndVarSimplifyPass, InlinePass, InstSimplifyPass,
28 JumpThreadingPass, LicmPass, LoadPrePass, LoopCanonicalizePass, MemoryDsePass, PrePass,
29 PureEvalPass, SccpTransformPass, StorageDsePass, StorageLoadCsePass,
30 StorageScalarPromotionPass,
31 },
32};
33use solar_data_structures::map::FxHashMap;
34use std::any::{Any, TypeId};
35
36type PassFactory = fn() -> Box<dyn ModulePass>;
37
38#[derive(Clone, Copy, Debug)]
40pub struct PassInfo {
41 pub name: &'static str,
43 pub description: &'static str,
45 make_pass: PassFactory,
46}
47
48impl PassInfo {
49 const fn new(name: &'static str, description: &'static str, make_pass: PassFactory) -> Self {
50 Self { name, description, make_pass }
51 }
52
53 fn make_pass(&self) -> Box<dyn ModulePass> {
54 (self.make_pass)()
55 }
56}
57
58macro_rules! declare_passes {
59 ($(
60 $(#[doc = $description:literal])+
61 $vis:vis const $const_name:ident -> $name:literal = $pass:expr;
62 )+) => {
63 $(
64 $(#[doc = $description])+
65 $vis const $const_name: PassInfo = PassInfo::new(
66 $name,
67 concat!($($description, "\n"),+).trim_ascii(),
68 || Box::new($pass),
69 );
70 )+
71 };
72}
73
74declare_passes! {
75 pub const INLINE_PASS -> "inline" = InlinePass;
77
78 pub const FUNCTION_DCE_PASS -> "function-dce" = FunctionDcePass;
80
81 pub const SCCP_PASS -> "sccp" = SccpTransformPass;
83
84 pub const PURE_EVAL_PASS -> "pure-eval" = PureEvalPass;
86
87 pub const INST_SIMPLIFY_PASS -> "inst-simplify" = InstSimplifyPass;
89
90 pub const CSE_PASS -> "cse" = CsePass;
92
93 pub const PRE_PASS -> "pre" = PrePass;
95
96 pub const GVN_PASS -> "gvn" = GvnPass;
98
99 pub const STORAGE_LOAD_CSE_PASS -> "storage-load-cse" = StorageLoadCsePass;
101
102 pub const STORAGE_DSE_PASS -> "storage-dse" = StorageDsePass;
104
105 pub const LOAD_PRE_PASS -> "load-pre" = LoadPrePass;
107
108 pub const LOOP_CANONICALIZE_PASS -> "loop-canonicalize" = LoopCanonicalizePass;
110
111 pub const INDVAR_SIMPLIFY_PASS -> "indvar-simplify" = IndVarSimplifyPass;
113
114 pub const STORAGE_PROMOTION_PASS -> "storage-promotion" = StorageScalarPromotionPass;
116
117 pub const LICM_PASS -> "licm" = LicmPass;
119
120 pub const CHECK_ELIM_PASS -> "check-elim" = CheckElimPass;
122
123 pub const JUMP_THREADING_PASS -> "jump-threading" = JumpThreadingPass;
125
126 pub const CFG_SIMPLIFY_PASS -> "cfg-simplify" = CfgSimplifyPass;
128
129 pub const FRAME_SLOT_PROMOTION_PASS -> "frame-slot-promotion" = FrameSlotPromotionPass;
131
132 pub const MEMORY_DSE_PASS -> "memory-dse" = MemoryDsePass;
134
135 pub const DCE_PASS -> "dce" = DcePass;
137
138 pub const ADCE_PASS -> "adce" = AdcePass;
140}
141
142pub const PASS_REGISTRY: &[PassInfo] = &[
144 INLINE_PASS,
145 FUNCTION_DCE_PASS,
146 ADCE_PASS,
147 DCE_PASS,
148 INST_SIMPLIFY_PASS,
149 CSE_PASS,
150 GVN_PASS,
151 PRE_PASS,
152 STORAGE_LOAD_CSE_PASS,
153 STORAGE_DSE_PASS,
154 LOAD_PRE_PASS,
155 LOOP_CANONICALIZE_PASS,
156 INDVAR_SIMPLIFY_PASS,
157 SCCP_PASS,
158 PURE_EVAL_PASS,
159 LICM_PASS,
160 CHECK_ELIM_PASS,
161 CFG_SIMPLIFY_PASS,
162 JUMP_THREADING_PASS,
163 FRAME_SLOT_PROMOTION_PASS,
164 MEMORY_DSE_PASS,
165 STORAGE_PROMOTION_PASS,
166];
167
168pub fn lookup_pass(name: &str) -> Option<&'static PassInfo> {
170 PASS_REGISTRY.iter().find(|pass| pass.name == name)
171}
172
173pub const DEFAULT_PIPELINE: &[PassInfo] = &[
175 INLINE_PASS,
176 FUNCTION_DCE_PASS,
177 SCCP_PASS,
178 PURE_EVAL_PASS,
179 INST_SIMPLIFY_PASS,
180 CSE_PASS,
181 GVN_PASS,
182 PRE_PASS,
183 STORAGE_LOAD_CSE_PASS,
184 STORAGE_DSE_PASS,
185 LOAD_PRE_PASS,
186 FRAME_SLOT_PROMOTION_PASS,
187 LOOP_CANONICALIZE_PASS,
188 INDVAR_SIMPLIFY_PASS,
189 STORAGE_PROMOTION_PASS,
190 LICM_PASS,
191 CHECK_ELIM_PASS,
192 JUMP_THREADING_PASS,
193 CFG_SIMPLIFY_PASS,
194 MEMORY_DSE_PASS,
195 ADCE_PASS,
196 DCE_PASS,
197];
198
199pub const DEFAULT_CLEANUP_PIPELINE: &[PassInfo] = &[
206 SCCP_PASS,
207 PURE_EVAL_PASS,
208 INST_SIMPLIFY_PASS,
209 CSE_PASS,
210 GVN_PASS,
211 PRE_PASS,
212 STORAGE_LOAD_CSE_PASS,
213 STORAGE_DSE_PASS,
214 LOAD_PRE_PASS,
215 CHECK_ELIM_PASS,
216 JUMP_THREADING_PASS,
217 CFG_SIMPLIFY_PASS,
218 FRAME_SLOT_PROMOTION_PASS,
219 MEMORY_DSE_PASS,
220 ADCE_PASS,
221 DCE_PASS,
222];
223
224const DEFAULT_CLEANUP_MAX_ROUNDS: usize = 3;
225
226#[derive(Clone, Copy, Debug)]
228pub struct PipelineOptions {
229 pub print_after_each: bool,
231 pub validate_after_each: bool,
233}
234
235impl Default for PipelineOptions {
236 fn default() -> Self {
237 Self { print_after_each: false, validate_after_each: cfg!(debug_assertions) }
238 }
239}
240
241pub fn run_pass(module: &mut Module, pass: &PassInfo) -> bool {
243 run_pass_with_options(module, pass, PipelineOptions::default())
244}
245
246fn run_pass_with_options(module: &mut Module, pass: &PassInfo, options: PipelineOptions) -> bool {
247 let mut pm = PassManager::new();
248 pm.set_validate_after_each(options.validate_after_each);
249 pm.add_pass(pass.make_pass());
250 pm.run(module).1
251}
252
253pub fn run_pipeline(module: &mut Module, passes: &[PassInfo]) -> bool {
255 let mut changed = false;
256 for pass in passes {
257 changed |= run_pass(module, pass);
258 }
259 changed
260}
261
262pub fn run_pipeline_with_options(
264 module: &mut Module,
265 passes: &[PassInfo],
266 options: PipelineOptions,
267) -> bool {
268 let mut changed = false;
269 for pass in passes {
270 changed |= run_pass_with_options(module, pass, options);
271 if options.print_after_each {
272 println!("// === {} (after {}) ===", module.name, pass.name);
273 print!("{}", module.to_text());
274 }
275 }
276 changed
277}
278
279pub fn run_default_pipeline(module: &mut Module) -> bool {
281 run_default_pipeline_with_options(module, PipelineOptions::default())
282}
283
284pub fn run_default_pipeline_with_options(module: &mut Module, options: PipelineOptions) -> bool {
286 let mut changed = run_pipeline_with_options(module, DEFAULT_PIPELINE, options);
287 changed |=
288 run_cleanup_pipeline_to_fixpoint(module, DEFAULT_CLEANUP_PIPELINE, options, "cleanup");
289 changed
290}
291
292fn run_cleanup_pipeline_to_fixpoint(
293 module: &mut Module,
294 passes: &[PassInfo],
295 options: PipelineOptions,
296 label: &str,
297) -> bool {
298 let mut changed = false;
299 for round in 1..=DEFAULT_CLEANUP_MAX_ROUNDS {
300 let mut round_changed = false;
301 for pass in passes {
302 let pass_changed = run_pass_with_options(module, pass, options);
303 round_changed |= pass_changed;
304 if options.print_after_each {
305 println!("// === {} (after {label}-{round}:{}) ===", module.name, pass.name);
306 print!("{}", module.to_text());
307 }
308 }
309 if !round_changed {
310 break;
311 }
312 changed = true;
313 }
314 changed
315}
316
317#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
319pub struct AnalysisKey(TypeId);
320
321impl AnalysisKey {
322 pub fn of<T: 'static>() -> Self {
324 Self(TypeId::of::<T>())
325 }
326}
327
328pub trait AnalysisPass {
333 type Result: 'static;
335
336 fn name(&self) -> &str;
338
339 fn run(&self, func: &Function) -> Self::Result;
341}
342
343pub trait ModulePass {
348 fn name(&self) -> &str;
350
351 fn run(&mut self, module: &mut Module) -> bool;
355}
356
357pub trait FunctionPass {
359 fn name(&self) -> &str;
361
362 fn run_on_function(&mut self, func: &mut Function) -> bool;
364}
365
366impl<T: FunctionPass> ModulePass for T {
367 fn name(&self) -> &str {
368 FunctionPass::name(self)
369 }
370
371 fn run(&mut self, module: &mut Module) -> bool {
372 let mut changed = false;
373 for func in module.functions.iter_mut().filter(|func| !func.blocks.is_empty()) {
374 changed |= self.run_on_function(func);
375 }
376 changed
377 }
378}
379
380#[derive(Default)]
384pub struct AnalysisManager {
385 results: FxHashMap<AnalysisKey, Box<dyn Any>>,
386}
387
388impl AnalysisManager {
389 pub fn new() -> Self {
391 Self::default()
392 }
393
394 pub fn get<T: 'static>(&self) -> Option<&T> {
396 let key = AnalysisKey::of::<T>();
397 self.results.get(&key)?.downcast_ref::<T>()
398 }
399
400 pub fn insert<T: 'static>(&mut self, result: T) {
402 let key = AnalysisKey::of::<T>();
403 self.results.insert(key, Box::new(result));
404 }
405
406 pub fn get_or_compute<A: AnalysisPass>(&mut self, analysis: &A, func: &Function) -> &A::Result {
411 let key = AnalysisKey::of::<A::Result>();
412 self.results.entry(key).or_insert_with(|| {
413 let result = analysis.run(func);
414 Box::new(result)
415 });
416 self.results[&key].downcast_ref::<A::Result>().unwrap()
417 }
418
419 pub fn invalidate_all(&mut self) {
421 self.results.clear();
422 }
423
424 pub fn invalidate<T: 'static>(&mut self) {
426 let key = AnalysisKey::of::<T>();
427 self.results.remove(&key);
428 }
429}
430
431pub struct PassManager {
435 passes: Vec<Box<dyn ModulePass>>,
436 validate_after_each: bool,
437}
438
439impl Default for PassManager {
440 fn default() -> Self {
441 Self { passes: Vec::new(), validate_after_each: cfg!(debug_assertions) }
442 }
443}
444
445impl PassManager {
446 pub fn new() -> Self {
448 Self::default()
449 }
450
451 pub fn add_pass(&mut self, pass: Box<dyn ModulePass>) {
453 self.passes.push(pass);
454 }
455
456 pub const fn set_validate_after_each(&mut self, enabled: bool) {
458 self.validate_after_each = enabled;
459 }
460
461 pub fn run(&mut self, module: &mut Module) -> (AnalysisManager, bool) {
464 let mut am = AnalysisManager::new();
465 let mut changed = false;
466 for pass in &mut self.passes {
467 let pass_name = pass.name().to_string();
468 if pass.run(module) {
469 changed = true;
470 am.invalidate_all();
471 }
472 if self.validate_after_each {
473 validate_module_after_pass(module, &pass_name);
474 }
475 }
476 (am, changed)
477 }
478}
479
480fn validate_module_after_pass(module: &Module, pass_name: &str) {
481 let errors = Validator::validate_module(module);
482 if errors.is_empty() {
483 return;
484 }
485
486 let mut message = format!("MIR validation failed after `{pass_name}`");
487 for error in errors {
488 message.push_str("\n ");
489 message.push_str(&error.to_string());
490 }
491 panic!("{message}");
492}
493
494pub struct LivenessAnalysis;
496
497impl AnalysisPass for LivenessAnalysis {
498 type Result = crate::analysis::Liveness;
499
500 fn name(&self) -> &str {
501 "liveness"
502 }
503
504 fn run(&self, func: &Function) -> Self::Result {
505 crate::analysis::Liveness::compute(func)
506 }
507}