1use crate::{
2 create_arg_demotion_pass, create_arg_pointee_mutability_tagger_pass, create_ccp_pass,
3 create_const_demotion_pass, create_const_folding_pass, create_cse_pass, create_dce_pass,
4 create_dom_fronts_pass, create_dominators_pass, create_escaped_symbols_pass,
5 create_fn_dedup_debug_profile_pass, create_fn_dedup_release_profile_pass,
6 create_fn_inline_pass, create_globals_dce_pass, create_init_aggr_lowering_pass,
7 create_mem2reg_pass, create_memcpyopt_pass, create_memcpyprop_reverse_pass,
8 create_misc_demotion_pass, create_module_printer_pass, create_module_verifier_pass,
9 create_postorder_pass, create_ret_demotion_pass, create_simplify_cfg_pass, create_sroa_pass,
10 Context, Function, IrError, Module, ARG_DEMOTION_NAME, ARG_POINTEE_MUTABILITY_TAGGER_NAME,
11 CCP_NAME, CONST_DEMOTION_NAME, CONST_FOLDING_NAME, CSE_NAME, DCE_NAME,
12 FN_DEDUP_DEBUG_PROFILE_NAME, FN_DEDUP_RELEASE_PROFILE_NAME, FN_INLINE_NAME, GLOBALS_DCE_NAME,
13 INIT_AGGR_LOWERING_NAME, MEM2REG_NAME, MEMCPYOPT_NAME, MEMCPYPROP_REVERSE_NAME,
14 MISC_DEMOTION_NAME, RET_DEMOTION_NAME, SIMPLIFY_CFG_NAME, SROA_NAME,
15};
16use downcast_rs::{impl_downcast, Downcast};
17use rustc_hash::FxHashMap;
18use std::{
19 any::{type_name, TypeId},
20 cell::RefCell,
21 collections::{hash_map, HashSet},
22 ops::DerefMut,
23};
24
25pub trait AnalysisResultT: Downcast {}
27impl_downcast!(AnalysisResultT);
28pub type AnalysisResult = Box<dyn AnalysisResultT>;
29
30pub trait PassScope {
32 fn get_arena_idx(&self) -> slotmap::DefaultKey;
33}
34impl PassScope for Module {
35 fn get_arena_idx(&self) -> slotmap::DefaultKey {
36 self.0
37 }
38}
39impl PassScope for Function {
40 fn get_arena_idx(&self) -> slotmap::DefaultKey {
41 self.0
42 }
43}
44
45#[derive(Clone)]
47pub enum PassMutability<S: PassScope> {
48 Analysis(fn(&Context, analyses: &AnalysisResults, S) -> Result<AnalysisResult, IrError>),
50 Transform(fn(&mut Context, analyses: &AnalysisResults, S) -> Result<bool, IrError>),
52}
53
54#[derive(Clone)]
56pub enum ScopedPass {
57 ModulePass(PassMutability<Module>),
58 FunctionPass(PassMutability<Function>),
59}
60
61pub struct Pass {
63 pub name: &'static str,
65 pub descr: &'static str,
67 pub deps: Vec<&'static str>,
69 pub runner: ScopedPass,
71}
72
73impl Pass {
74 pub fn is_analysis(&self) -> bool {
75 match &self.runner {
76 ScopedPass::ModulePass(pm) => matches!(pm, PassMutability::Analysis(_)),
77 ScopedPass::FunctionPass(pm) => matches!(pm, PassMutability::Analysis(_)),
78 }
79 }
80
81 pub fn is_transform(&self) -> bool {
82 !self.is_analysis()
83 }
84
85 pub fn is_module_pass(&self) -> bool {
86 matches!(self.runner, ScopedPass::ModulePass(_))
87 }
88
89 pub fn is_function_pass(&self) -> bool {
90 matches!(self.runner, ScopedPass::FunctionPass(_))
91 }
92}
93
94#[derive(Default)]
95pub struct AnalysisResults {
96 results: FxHashMap<(TypeId, (TypeId, slotmap::DefaultKey)), AnalysisResult>,
98 name_typeid_map: FxHashMap<&'static str, TypeId>,
99 pub is_log_enabled: bool,
100 log_string: RefCell<String>,
102}
103
104impl AnalysisResults {
105 pub fn push_log(&self, log: impl AsRef<str>) {
106 if self.is_log_enabled {
107 self.log_string.borrow_mut().push_str(log.as_ref());
108 }
109 }
110
111 pub fn get_analysis_result<T: AnalysisResultT, S: PassScope + 'static>(&self, scope: S) -> &T {
114 self.results
115 .get(&(
116 TypeId::of::<T>(),
117 (TypeId::of::<S>(), scope.get_arena_idx()),
118 ))
119 .unwrap_or_else(|| {
120 panic!(
121 "Internal error. Analysis result {} unavailable for {} with idx {:?}",
122 type_name::<T>(),
123 type_name::<S>(),
124 scope.get_arena_idx()
125 )
126 })
127 .downcast_ref()
128 .expect("AnalysisResult: Incorrect type")
129 }
130
131 fn is_analysis_result_available<S: PassScope + 'static>(
133 &self,
134 name: &'static str,
135 scope: S,
136 ) -> bool {
137 self.name_typeid_map
138 .get(name)
139 .and_then(|result_typeid| {
140 self.results
141 .get(&(*result_typeid, (TypeId::of::<S>(), scope.get_arena_idx())))
142 })
143 .is_some()
144 }
145
146 fn add_result<S: PassScope + 'static>(
148 &mut self,
149 name: &'static str,
150 scope: S,
151 result: AnalysisResult,
152 ) {
153 let result_typeid = (*result).type_id();
154 self.results.insert(
155 (result_typeid, (TypeId::of::<S>(), scope.get_arena_idx())),
156 result,
157 );
158 self.name_typeid_map.insert(name, result_typeid);
159 }
160
161 fn invalidate_all_results_at_scope<S: PassScope + 'static>(&mut self, scope: S) {
163 self.results
164 .retain(|(_result_typeid, (scope_typeid, scope_idx)), _v| {
165 (*scope_typeid, *scope_idx) != (TypeId::of::<S>(), scope.get_arena_idx())
166 });
167 }
168}
169
170#[derive(Debug)]
179pub struct Options {
180 pub print_initial: bool,
181 pub print_final: bool,
182 pub print_modified_only: bool,
183 pub print_metadata: bool,
184 pub print_passes: HashSet<String>,
185 pub force_verify_ir: bool,
186 pub rounds: usize,
187 pub log: bool,
188}
189
190impl Default for Options {
191 fn default() -> Self {
192 Self {
193 print_initial: false,
194 print_final: false,
195 print_modified_only: false,
196 print_metadata: false,
197 print_passes: HashSet::default(),
198 force_verify_ir: false,
199 rounds: 2,
200 log: false,
201 }
202 }
203}
204
205#[derive(Default)]
206pub struct PassManager {
207 passes: FxHashMap<&'static str, Pass>,
208 analyses: AnalysisResults,
209}
210
211impl PassManager {
212 pub const OPTIMIZATION_PASSES: [&'static str; 19] = [
213 ARG_DEMOTION_NAME,
214 ARG_POINTEE_MUTABILITY_TAGGER_NAME,
215 CCP_NAME,
216 CONST_DEMOTION_NAME,
217 CONST_FOLDING_NAME,
218 CSE_NAME,
219 DCE_NAME,
220 FN_DEDUP_DEBUG_PROFILE_NAME,
221 FN_DEDUP_RELEASE_PROFILE_NAME,
222 FN_INLINE_NAME,
223 GLOBALS_DCE_NAME,
224 INIT_AGGR_LOWERING_NAME,
225 MEM2REG_NAME,
226 MEMCPYOPT_NAME,
227 MEMCPYPROP_REVERSE_NAME,
228 MISC_DEMOTION_NAME,
229 RET_DEMOTION_NAME,
230 SIMPLIFY_CFG_NAME,
231 SROA_NAME,
232 ];
233
234 pub fn register(&mut self, pass: Pass) -> &'static str {
236 for dep in &pass.deps {
237 if let Some(dep_t) = self.lookup_registered_pass(dep) {
238 if dep_t.is_transform() {
239 panic!(
240 "Pass {} cannot depend on a transformation pass {}",
241 pass.name, dep
242 );
243 }
244 if pass.is_function_pass() && dep_t.is_module_pass() {
245 panic!(
246 "Function pass {} cannot depend on module pass {}",
247 pass.name, dep
248 );
249 }
250 } else {
251 panic!(
252 "Pass {} depends on a (yet) unregistered pass {}",
253 pass.name, dep
254 );
255 }
256 }
257 let pass_name = pass.name;
258 match self.passes.entry(pass.name) {
259 hash_map::Entry::Occupied(_) => {
260 panic!("Trying to register an already registered pass");
261 }
262 hash_map::Entry::Vacant(entry) => {
263 entry.insert(pass);
264 }
265 }
266 pass_name
267 }
268
269 fn actually_run(&mut self, ir: &mut Context, pass: &'static str) -> Result<bool, IrError> {
270 let mut modified = false;
271
272 fn run_module_pass(
273 pm: &mut PassManager,
274 ir: &mut Context,
275 pass: &'static str,
276 module: Module,
277 ) -> Result<bool, IrError> {
278 let mut modified = false;
279 let pass_t = pm.passes.get(pass).expect("Unregistered pass");
280 for dep in pass_t.deps.clone() {
281 let dep_t = pm.passes.get(dep).expect("Unregistered dependent pass");
282 assert!(dep_t.is_analysis());
284 match dep_t.runner {
285 ScopedPass::ModulePass(_) => {
286 if !pm.analyses.is_analysis_result_available(dep, module) {
287 run_module_pass(pm, ir, dep, module)?;
288 }
289 }
290 ScopedPass::FunctionPass(_) => {
291 for f in module.function_iter(ir) {
292 if !pm.analyses.is_analysis_result_available(dep, f) {
293 run_function_pass(pm, ir, dep, f)?;
294 }
295 }
296 }
297 }
298 }
299
300 let pass_t = pm.passes.get(pass).expect("Unregistered pass");
302 let ScopedPass::ModulePass(mp) = pass_t.runner.clone() else {
303 panic!("Expected a module pass");
304 };
305 match mp {
306 PassMutability::Analysis(analysis) => {
307 let result = analysis(ir, &pm.analyses, module)?;
308 pm.analyses.add_result(pass, module, result);
309 }
310 PassMutability::Transform(transform) => {
311 if transform(ir, &pm.analyses, module)? {
312 pm.analyses.invalidate_all_results_at_scope(module);
313 for f in module.function_iter(ir) {
314 pm.analyses.invalidate_all_results_at_scope(f);
315 }
316 modified = true;
317 }
318 }
319 }
320
321 Ok(modified)
322 }
323
324 fn run_function_pass(
325 pm: &mut PassManager,
326 ir: &mut Context,
327 pass: &'static str,
328 function: Function,
329 ) -> Result<bool, IrError> {
330 let mut modified = false;
331 let pass_t = pm.passes.get(pass).expect("Unregistered pass");
332 for dep in pass_t.deps.clone() {
333 let dep_t = pm.passes.get(dep).expect("Unregistered dependent pass");
334 assert!(dep_t.is_analysis());
336 match dep_t.runner {
337 ScopedPass::ModulePass(_) => {
338 panic!("Function pass {pass} cannot depend on module pass {dep}")
339 }
340 ScopedPass::FunctionPass(_) => {
341 if !pm.analyses.is_analysis_result_available(dep, function) {
342 run_function_pass(pm, ir, dep, function)?;
343 };
344 }
345 }
346 }
347
348 let pass_t = pm.passes.get(pass).expect("Unregistered pass");
350 let ScopedPass::FunctionPass(fp) = pass_t.runner.clone() else {
351 panic!("Expected a function pass");
352 };
353 match fp {
354 PassMutability::Analysis(analysis) => {
355 let result = analysis(ir, &pm.analyses, function)?;
356 pm.analyses.add_result(pass, function, result);
357 }
358 PassMutability::Transform(transform) => {
359 if transform(ir, &pm.analyses, function)? {
360 pm.analyses.invalidate_all_results_at_scope(function);
361 modified = true;
362 }
363 }
364 }
365
366 Ok(modified)
367 }
368
369 for m in ir.module_iter() {
370 let pass_t = self.passes.get(pass).expect("Unregistered pass");
371 let pass_runner = pass_t.runner.clone();
372 match pass_runner {
373 ScopedPass::ModulePass(_) => {
374 modified |= run_module_pass(self, ir, pass, m)?;
375 }
376 ScopedPass::FunctionPass(_) => {
377 for f in m.function_iter(ir) {
378 modified |= run_function_pass(self, ir, pass, f)?;
379 }
380 }
381 }
382 }
383 Ok(modified)
384 }
385
386 pub fn run(
389 &mut self,
390 ir: &mut Context,
391 passes: &PassGroup,
392 options: &Options,
393 ) -> Result<bool, IrError> {
394 if options.print_initial {
395 print_initial_or_final_ir(ir, "Initial", options.print_metadata);
396 }
397
398 self.analyses.is_log_enabled = options.log;
399 self.analyses.log_string.borrow_mut().clear();
400
401 ir.verify()?;
403
404 let mut global_modified = false;
405
406 for _ in 0..options.rounds {
407 let mut iter_modified = false;
408
409 for pass in passes.flatten_pass_group() {
410 let ir_before = if options.force_verify_ir {
412 ir.to_string()
413 } else {
414 String::new()
415 };
416
417 let modified = self.actually_run(ir, pass)?;
419
420 let ir_after = if options.force_verify_ir {
422 ir.to_string()
423 } else {
424 String::new()
425 };
426
427 iter_modified |= modified;
428
429 if options.print_passes.contains(pass) && (!options.print_modified_only || modified)
430 {
431 print_ir_after_pass(
432 ir,
433 self.lookup_registered_pass(pass).unwrap(),
434 options.print_metadata,
435 );
436 }
437
438 ir.verify()?;
439
440 if options.force_verify_ir {
441 let ir_modified = ir_before != ir_after;
443 if modified != ir_modified {
444 return Err(IrError::InvalidPassModified {
445 pass: pass.to_string(),
446 returned: modified,
447 comparison: ir_modified,
448 });
449 }
450 }
451 }
452
453 global_modified |= iter_modified;
454 if !iter_modified {
455 break;
456 }
457 }
458
459 if options.print_final {
460 print_initial_or_final_ir(ir, "Final", options.print_metadata);
461 }
462
463 Ok(global_modified)
464 }
465
466 pub fn lookup_registered_pass(&self, name: &str) -> Option<&Pass> {
468 self.passes.get(name)
469 }
470
471 pub fn take_log(&self) -> String {
472 let mut log = self.analyses.log_string.borrow_mut();
473 std::mem::take(log.deref_mut())
474 }
475
476 pub fn help_text(&self) -> String {
477 let summary = self
478 .passes
479 .iter()
480 .map(|(name, pass)| format!(" {name:16} - {}", pass.descr))
481 .collect::<Vec<_>>()
482 .join("\n");
483
484 format!("Valid pass names are:\n\n{summary}",)
485 }
486}
487
488fn ir_is_empty(ir: &Context) -> bool {
490 ir.functions.is_empty()
491 && ir.blocks.is_empty()
492 && ir.values.is_empty()
493 && ir.local_vars.is_empty()
494}
495
496fn print_ir_after_pass(ir: &Context, pass: &Pass, print_metadata: bool) {
497 if !ir_is_empty(ir) {
498 println!("// IR: [{}] {}", pass.name, pass.descr);
499 println!(
500 "{}",
501 crate::printer::to_string_with_metadata(ir, print_metadata)
502 );
503 }
504}
505
506fn print_initial_or_final_ir(ir: &Context, initial_or_final: &'static str, print_metadata: bool) {
507 if !ir_is_empty(ir) {
508 println!("// IR: {initial_or_final}");
509 println!(
510 "{}",
511 crate::printer::to_string_with_metadata(ir, print_metadata)
512 );
513 }
514}
515
516#[derive(Default)]
519pub struct PassGroup(Vec<PassOrGroup>);
520
521pub enum PassOrGroup {
523 Pass(&'static str),
524 Group(PassGroup),
525}
526
527impl PassGroup {
528 fn flatten_pass_group(&self) -> Vec<&'static str> {
530 let mut output = Vec::<&str>::new();
531 fn inner(output: &mut Vec<&str>, input: &PassGroup) {
532 for pass_or_group in &input.0 {
533 match pass_or_group {
534 PassOrGroup::Pass(pass) => output.push(pass),
535 PassOrGroup::Group(pg) => inner(output, pg),
536 }
537 }
538 }
539 inner(&mut output, self);
540 output
541 }
542
543 pub fn append_pass(&mut self, pass: &'static str) {
545 self.0.push(PassOrGroup::Pass(pass));
546 }
547
548 pub fn append_group(&mut self, group: PassGroup) {
550 self.0.push(PassOrGroup::Group(group));
551 }
552}
553
554pub fn register_known_passes(pm: &mut PassManager) {
556 pm.register(create_postorder_pass());
558 pm.register(create_dominators_pass());
559 pm.register(create_dom_fronts_pass());
560 pm.register(create_escaped_symbols_pass());
561 pm.register(create_module_printer_pass());
562 pm.register(create_module_verifier_pass());
563
564 pm.register(create_init_aggr_lowering_pass());
566
567 pm.register(create_arg_pointee_mutability_tagger_pass());
569 pm.register(create_fn_dedup_release_profile_pass());
570 pm.register(create_fn_dedup_debug_profile_pass());
571 pm.register(create_mem2reg_pass());
572 pm.register(create_sroa_pass());
573 pm.register(create_fn_inline_pass());
574 pm.register(create_const_folding_pass());
575 pm.register(create_ccp_pass());
576 pm.register(create_simplify_cfg_pass());
577 pm.register(create_globals_dce_pass());
578 pm.register(create_dce_pass());
579 pm.register(create_cse_pass());
580 pm.register(create_arg_demotion_pass());
581 pm.register(create_const_demotion_pass());
582 pm.register(create_ret_demotion_pass());
583 pm.register(create_misc_demotion_pass());
584 pm.register(create_memcpyopt_pass());
585 pm.register(create_memcpyprop_reverse_pass());
586}
587
588pub fn create_o1_pass_group() -> PassGroup {
589 let mut o1 = PassGroup::default();
590 o1.append_pass(MEM2REG_NAME);
591 o1.append_pass(FN_DEDUP_RELEASE_PROFILE_NAME);
592 o1.append_pass(FN_INLINE_NAME);
593 o1.append_pass(ARG_POINTEE_MUTABILITY_TAGGER_NAME);
594 o1.append_pass(SIMPLIFY_CFG_NAME);
595 o1.append_pass(GLOBALS_DCE_NAME);
596 o1.append_pass(DCE_NAME);
597 o1.append_pass(FN_INLINE_NAME);
598 o1.append_pass(ARG_POINTEE_MUTABILITY_TAGGER_NAME);
599 o1.append_pass(CCP_NAME);
600 o1.append_pass(CONST_FOLDING_NAME);
601 o1.append_pass(SIMPLIFY_CFG_NAME);
602 o1.append_pass(CSE_NAME);
603 o1.append_pass(CONST_FOLDING_NAME);
604 o1.append_pass(SIMPLIFY_CFG_NAME);
605 o1.append_pass(GLOBALS_DCE_NAME);
606 o1.append_pass(DCE_NAME);
607 o1.append_pass(FN_DEDUP_RELEASE_PROFILE_NAME);
608
609 o1
610}
611
612pub fn insert_after_each(pg: PassGroup, pass: &'static str) -> PassGroup {
618 fn insert_after_each_rec(pg: PassGroup, pass: &'static str) -> Vec<PassOrGroup> {
619 pg.0.into_iter()
620 .flat_map(|p_o_g| match p_o_g {
621 PassOrGroup::Group(group) => vec![PassOrGroup::Group(PassGroup(
622 insert_after_each_rec(group, pass),
623 ))],
624 PassOrGroup::Pass(_) => vec![p_o_g, PassOrGroup::Pass(pass)],
625 })
626 .collect()
627 }
628
629 PassGroup(insert_after_each_rec(pg, pass))
630}