1use rucc_ir::Func;
36
37use crate::machine::Machine;
38use crate::predict::Callees;
39use crate::{
40 Cfg, ControlDependence, Dominators, Frequencies, Frontiers, Liveness, Loops, PostDominators,
41 Pressure,
42};
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
50pub enum Analysis {
51 Cfg,
53 Dominators,
55 PostDominators,
57 Loops,
59 Frontiers,
61 ControlDependence,
63 Frequencies,
65 Liveness,
67 Pressure,
69}
70
71impl Analysis {
72 pub const EVERY: &'static [Analysis] = &[
74 Analysis::Cfg,
75 Analysis::Dominators,
76 Analysis::PostDominators,
77 Analysis::Loops,
78 Analysis::Frontiers,
79 Analysis::ControlDependence,
80 Analysis::Frequencies,
81 Analysis::Liveness,
82 Analysis::Pressure,
83 ];
84
85 #[must_use]
87 pub const fn name(self) -> &'static str {
88 match self {
89 Self::Cfg => "the control flow graph",
90 Self::Dominators => "the dominator tree",
91 Self::PostDominators => "the post-dominator tree",
92 Self::Loops => "the loop forest",
93 Self::Frontiers => "the dominance frontiers",
94 Self::ControlDependence => "the control dependence relation",
95 Self::Frequencies => "the block frequencies",
96 Self::Liveness => "the liveness",
97 Self::Pressure => "the register pressure",
98 }
99 }
100
101 #[must_use]
106 pub const fn needs(self) -> &'static [Analysis] {
107 match self {
108 Self::Cfg => &[],
109 Self::Dominators | Self::PostDominators => &[Analysis::Cfg],
110 Self::Loops | Self::Frontiers => &[Analysis::Cfg, Analysis::Dominators],
111 Self::ControlDependence => &[Analysis::Cfg, Analysis::PostDominators],
112 Self::Frequencies => &[Analysis::Cfg, Analysis::Dominators, Analysis::Loops],
113 Self::Liveness => &[Analysis::Cfg],
114 Self::Pressure => &[Analysis::Cfg, Analysis::Liveness],
115 }
116 }
117
118 const fn bit(self) -> u16 {
120 1 << (self as u16)
121 }
122}
123
124#[derive(Clone, Copy, Debug, PartialEq, Eq)]
132pub struct Preserved(u16);
133
134impl Preserved {
135 pub const ALL: Preserved = Preserved(u16::MAX);
137
138 pub const NONE: Preserved = Preserved(0);
140
141 #[must_use]
143 pub const fn and(self, analysis: Analysis) -> Self {
144 Self(self.0 | analysis.bit())
145 }
146
147 #[must_use]
153 pub const fn without(self, analysis: Analysis) -> Self {
154 Self(self.0 & !analysis.bit())
155 }
156
157 #[must_use]
159 pub const fn keeps(self, analysis: Analysis) -> bool {
160 self.0 & analysis.bit() != 0
161 }
162}
163
164#[derive(Clone, Debug)]
169pub struct Analyses {
170 machine: Machine,
171 cfg: Option<Cfg>,
172 doms: Option<Dominators>,
173 post: Option<PostDominators>,
174 loops: Option<Loops>,
175 frontiers: Option<Frontiers>,
176 control: Option<ControlDependence>,
177 frequencies: Option<Frequencies>,
178 live: Option<Liveness>,
179 pressure: Option<Pressure>,
180}
181
182impl Analyses {
183 #[must_use]
190 pub fn new(machine: Machine) -> Self {
191 Self {
192 machine,
193 cfg: None,
194 doms: None,
195 post: None,
196 loops: None,
197 frontiers: None,
198 control: None,
199 frequencies: None,
200 live: None,
201 pressure: None,
202 }
203 }
204
205 #[must_use]
210 pub const fn machine(&self) -> Machine {
211 self.machine
212 }
213
214 pub fn cfg(&mut self, func: &Func) -> &Cfg {
216 self.cfg.get_or_insert_with(|| Cfg::new(func))
217 }
218
219 pub fn dominators(&mut self, func: &Func) -> &Dominators {
226 let cfg: &Cfg = self.cfg.get_or_insert_with(|| Cfg::new(func));
227 self.doms.get_or_insert_with(|| Dominators::new(cfg))
228 }
229
230 pub fn post_dominators(&mut self, func: &Func) -> &PostDominators {
237 let cfg: &Cfg = self.cfg.get_or_insert_with(|| Cfg::new(func));
238 self.post.get_or_insert_with(|| PostDominators::new(cfg))
239 }
240
241 pub fn loops(&mut self, func: &Func) -> &Loops {
243 let cfg: &Cfg = self.cfg.get_or_insert_with(|| Cfg::new(func));
244 let doms: &Dominators = self.doms.get_or_insert_with(|| Dominators::new(cfg));
245 self.loops.get_or_insert_with(|| Loops::new(cfg, doms))
246 }
247
248 pub fn frontiers(&mut self, func: &Func) -> &Frontiers {
250 let cfg: &Cfg = self.cfg.get_or_insert_with(|| Cfg::new(func));
251 let doms: &Dominators = self.doms.get_or_insert_with(|| Dominators::new(cfg));
252 self.frontiers.get_or_insert_with(|| Frontiers::new(cfg, doms))
253 }
254
255 pub fn control_dependence(&mut self, func: &Func) -> &ControlDependence {
261 let cfg: &Cfg = self.cfg.get_or_insert_with(|| Cfg::new(func));
262 let post: &PostDominators = self.post.get_or_insert_with(|| PostDominators::new(cfg));
263 self.control.get_or_insert_with(|| ControlDependence::new(cfg, post))
264 }
265
266 pub fn frequencies(&mut self, func: &Func) -> &Frequencies {
275 let cfg: &Cfg = self.cfg.get_or_insert_with(|| Cfg::new(func));
276 let doms: &Dominators = self.doms.get_or_insert_with(|| Dominators::new(cfg));
277 let loops: &Loops = self.loops.get_or_insert_with(|| Loops::new(cfg, doms));
278 self.frequencies
279 .get_or_insert_with(|| Frequencies::of(func, cfg, loops, &Callees::nothing()))
280 }
281
282 pub fn live(&mut self, func: &Func) -> &Liveness {
284 let cfg: &Cfg = self.cfg.get_or_insert_with(|| Cfg::new(func));
285 self.live.get_or_insert_with(|| Liveness::of(func, cfg))
286 }
287
288 pub fn pressure(&mut self, func: &Func) -> &Pressure {
295 let cfg: &Cfg = self.cfg.get_or_insert_with(|| Cfg::new(func));
296 let live: &Liveness = self.live.get_or_insert_with(|| Liveness::of(func, cfg));
297 self.pressure.get_or_insert_with(|| Pressure::of(func, cfg, live))
298 }
299
300 #[must_use]
306 pub fn holds(&self, analysis: Analysis) -> bool {
307 match analysis {
308 Analysis::Cfg => self.cfg.is_some(),
309 Analysis::Dominators => self.doms.is_some(),
310 Analysis::PostDominators => self.post.is_some(),
311 Analysis::Loops => self.loops.is_some(),
312 Analysis::Frontiers => self.frontiers.is_some(),
313 Analysis::ControlDependence => self.control.is_some(),
314 Analysis::Frequencies => self.frequencies.is_some(),
315 Analysis::Liveness => self.live.is_some(),
316 Analysis::Pressure => self.pressure.is_some(),
317 }
318 }
319
320 pub fn settle(&mut self, func: &Func, keeps: Preserved, check: bool) -> Vec<Analysis> {
332 let lied = if check { self.lies(func, keeps) } else { Vec::new() };
333 let mut keeps = keeps;
338 for &analysis in &lied {
339 keeps = keeps.without(analysis);
340 }
341 let alive = Self::survivors(keeps);
342 for &analysis in Analysis::EVERY {
343 if !alive[analysis as usize] {
344 self.drop(analysis);
345 }
346 }
347 lied
348 }
349
350 fn survivors(keeps: Preserved) -> [bool; Analysis::EVERY.len()] {
363 let mut alive = [false; Analysis::EVERY.len()];
364 for &analysis in Analysis::EVERY {
365 alive[analysis as usize] =
366 keeps.keeps(analysis) && analysis.needs().iter().all(|&need| alive[need as usize]);
367 }
368 alive
369 }
370
371 pub fn clear(&mut self) {
377 *self = Self::new(self.machine);
378 }
379
380 fn drop(&mut self, analysis: Analysis) {
382 match analysis {
383 Analysis::Cfg => self.cfg = None,
384 Analysis::Dominators => self.doms = None,
385 Analysis::PostDominators => self.post = None,
386 Analysis::Loops => self.loops = None,
387 Analysis::Frontiers => self.frontiers = None,
388 Analysis::ControlDependence => self.control = None,
389 Analysis::Frequencies => self.frequencies = None,
390 Analysis::Liveness => self.live = None,
391 Analysis::Pressure => self.pressure = None,
392 }
393 }
394
395 fn lies(&self, func: &Func, keeps: Preserved) -> Vec<Analysis> {
403 let alive = Self::survivors(keeps);
404 let wanted: Vec<Analysis> = Analysis::EVERY
405 .iter()
406 .copied()
407 .filter(|&it| self.holds(it) && alive[it as usize])
408 .collect();
409 if wanted.is_empty() {
410 return Vec::new();
411 }
412 let cfg = Cfg::new(func);
415 let mut lied = Vec::new();
416 for analysis in wanted {
417 let same = match analysis {
418 Analysis::Cfg => self.cfg.as_ref() == Some(&cfg),
419 Analysis::Dominators => self.doms.as_ref() == Some(&Dominators::new(&cfg)),
420 Analysis::PostDominators => self.post.as_ref() == Some(&PostDominators::new(&cfg)),
421 Analysis::Loops => {
422 self.loops.as_ref() == Some(&Loops::new(&cfg, &Dominators::new(&cfg)))
423 }
424 Analysis::Frontiers => {
425 self.frontiers.as_ref() == Some(&Frontiers::new(&cfg, &Dominators::new(&cfg)))
426 }
427 Analysis::ControlDependence => {
428 self.control.as_ref()
429 == Some(&ControlDependence::new(&cfg, &PostDominators::new(&cfg)))
430 }
431 Analysis::Frequencies => {
432 let doms = Dominators::new(&cfg);
433 let loops = Loops::new(&cfg, &doms);
434 let now = Frequencies::of(func, &cfg, &loops, &Callees::nothing());
435 self.frequencies.as_ref() == Some(&now)
436 }
437 Analysis::Liveness => self.live.as_ref() == Some(&Liveness::of(func, &cfg)),
438 Analysis::Pressure => {
439 let live = Liveness::of(func, &cfg);
440 self.pressure.as_ref() == Some(&Pressure::of(func, &cfg, &live))
441 }
442 };
443 if !same {
444 lied.push(analysis);
445 }
446 }
447 lied
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use rucc_base::Interner;
454 use rucc_ir::{Block, Func, Signature};
455
456 use super::{Analysis, Preserved};
457 use crate::testing::graph;
458
459 fn func() -> Func {
462 graph(&[&[1, 2], &[3], &[3], &[4, 1], &[]])
463 }
464
465 #[test]
466 fn an_analysis_is_built_out_of_ones_that_come_before_it() {
467 for &analysis in Analysis::EVERY {
470 for &need in analysis.needs() {
471 assert!(need < analysis, "{} is built out of a later analysis", analysis.name());
472 }
473 }
474 }
475
476 #[test]
477 fn every_analysis_is_in_the_list_once() {
478 for &analysis in Analysis::EVERY {
479 let found = Analysis::EVERY.iter().filter(|&&it| it == analysis).count();
480 assert_eq!(found, 1, "{} appears twice", analysis.name());
481 }
482 assert_eq!(Analysis::EVERY.len(), 9);
483 }
484
485 #[test]
486 fn all_keeps_everything_and_none_keeps_nothing() {
487 for &analysis in Analysis::EVERY {
488 assert!(Preserved::ALL.keeps(analysis));
489 assert!(!Preserved::NONE.keeps(analysis));
490 }
491 }
492
493 #[test]
494 fn a_named_set_holds_what_was_named_and_nothing_else() {
495 let keeps = Preserved::NONE.and(Analysis::Cfg).and(Analysis::Loops);
496 assert!(keeps.keeps(Analysis::Cfg));
497 assert!(keeps.keeps(Analysis::Loops));
498 assert!(!keeps.keeps(Analysis::Dominators));
499 assert!(!keeps.keeps(Analysis::PostDominators));
500 }
501
502 #[test]
503 fn nothing_is_computed_until_it_is_asked_for() {
504 let mut an = crate::machine::fixtures::analyses();
505 for &analysis in Analysis::EVERY {
506 assert!(!an.holds(analysis));
507 }
508 let func = func();
509 an.dominators(&func);
510 assert!(an.holds(Analysis::Cfg));
513 assert!(an.holds(Analysis::Dominators));
514 assert!(!an.holds(Analysis::Loops));
515 assert!(!an.holds(Analysis::PostDominators));
516 }
517
518 #[test]
519 fn asking_twice_gives_the_same_answer_and_the_second_one_is_free() {
520 let func = func();
521 let mut an = crate::machine::fixtures::analyses();
522 let first = an.cfg(&func).clone();
523 let second = an.cfg(&func);
524 assert_eq!(&first, second);
525 }
526
527 #[test]
528 fn the_loop_forest_pulls_in_what_it_is_built_out_of() {
529 let func = func();
530 let mut an = crate::machine::fixtures::analyses();
531 an.loops(&func);
532 assert!(an.holds(Analysis::Cfg));
533 assert!(an.holds(Analysis::Dominators));
534 assert!(an.holds(Analysis::Loops));
535 }
536
537 #[test]
538 fn preserving_everything_keeps_everything() {
539 let func = func();
540 let mut an = crate::machine::fixtures::analyses();
541 an.loops(&func);
542 an.frontiers(&func);
543 an.control_dependence(&func);
544 an.frequencies(&func);
545 an.pressure(&func);
546 assert!(an.settle(&func, Preserved::ALL, true).is_empty());
547 for &analysis in Analysis::EVERY {
548 assert!(an.holds(analysis), "{} was thrown away", analysis.name());
549 }
550 }
551
552 #[test]
553 fn the_pressure_falls_with_the_liveness_it_was_counted_from() {
554 let func = func();
555 let mut an = crate::machine::fixtures::analyses();
556 an.pressure(&func);
557 assert!(an.holds(Analysis::Liveness), "it had to be computed to count anything");
558 let keeps = Preserved::NONE.and(Analysis::Cfg).and(Analysis::Pressure);
559 an.settle(&func, keeps, false);
560 assert!(an.holds(Analysis::Cfg));
561 assert!(!an.holds(Analysis::Liveness));
562 assert!(!an.holds(Analysis::Pressure), "a count outlived what it counted");
563 }
564
565 #[test]
566 fn a_claim_is_read_with_what_each_analysis_is_built_out_of() {
567 let alive = super::Analyses::survivors(Preserved::ALL.without(Analysis::Liveness));
571 assert!(!alive[Analysis::Liveness as usize]);
572 assert!(!alive[Analysis::Pressure as usize], "a count survived what it was counted from");
573 assert!(alive[Analysis::Loops as usize], "the shape of the function did not change");
574 }
575
576 #[test]
577 fn preserving_nothing_empties_the_cache() {
578 let func = func();
579 let mut an = crate::machine::fixtures::analyses();
580 an.loops(&func);
581 an.frontiers(&func);
582 an.control_dependence(&func);
583 an.settle(&func, Preserved::NONE, false);
584 for &analysis in Analysis::EVERY {
585 assert!(!an.holds(analysis), "{} outlived the pass", analysis.name());
586 }
587 }
588
589 #[test]
590 fn losing_the_graph_loses_what_was_built_on_it() {
591 let func = func();
592 let mut an = crate::machine::fixtures::analyses();
593 an.loops(&func);
594 an.post_dominators(&func);
595 let keeps = Preserved::NONE
599 .and(Analysis::Dominators)
600 .and(Analysis::PostDominators)
601 .and(Analysis::Loops);
602 an.settle(&func, keeps, false);
603 for &analysis in Analysis::EVERY {
604 assert!(!an.holds(analysis), "{} outlived the graph", analysis.name());
605 }
606 }
607
608 #[test]
609 fn losing_the_dominator_tree_loses_the_forest_and_leaves_the_graph() {
610 let func = func();
611 let mut an = crate::machine::fixtures::analyses();
612 an.loops(&func);
613 an.post_dominators(&func);
614 let keeps =
615 Preserved::NONE.and(Analysis::Cfg).and(Analysis::PostDominators).and(Analysis::Loops);
616 an.settle(&func, keeps, false);
617 assert!(an.holds(Analysis::Cfg));
618 assert!(an.holds(Analysis::PostDominators));
619 assert!(!an.holds(Analysis::Dominators), "the tree was not preserved");
620 assert!(!an.holds(Analysis::Loops), "the forest outlived the tree it needs");
621 }
622
623 #[test]
624 fn each_frontier_falls_with_the_tree_it_was_walked_on_and_not_the_other_one() {
625 let func = func();
629 let mut an = crate::machine::fixtures::analyses();
630 an.frontiers(&func);
631 an.control_dependence(&func);
632 let keeps = Preserved::NONE
633 .and(Analysis::Cfg)
634 .and(Analysis::Dominators)
635 .and(Analysis::Frontiers)
636 .and(Analysis::ControlDependence);
637 an.settle(&func, keeps, false);
638 assert!(an.holds(Analysis::Frontiers), "the frontier stands on a tree that stood");
639 assert!(!an.holds(Analysis::ControlDependence), "the post-dominator tree went with it");
640 }
641
642 #[test]
643 fn the_frequencies_fall_with_the_loop_forest_they_were_worked_out_from() {
644 let func = func();
645 let mut an = crate::machine::fixtures::analyses();
646 an.frequencies(&func);
647 for analysis in [Analysis::Cfg, Analysis::Dominators, Analysis::Loops] {
650 assert!(an.holds(analysis), "{} was not pulled in", analysis.name());
651 }
652 let keeps =
653 Preserved::NONE.and(Analysis::Cfg).and(Analysis::Dominators).and(Analysis::Frequencies);
654 an.settle(&func, keeps, false);
655 assert!(!an.holds(Analysis::Loops), "the forest was not preserved");
656 assert!(!an.holds(Analysis::Frequencies), "a frequency outlived the loop it counted");
657 }
658
659 #[test]
660 fn a_pass_that_says_it_kept_the_graph_and_moved_an_edge_is_caught() {
661 let mut func = func();
662 let mut an = crate::machine::fixtures::analyses();
663 an.loops(&func);
664 let block = Block::from_usize(3);
668 let term = func.terminator(block).expect("the helper gives every block a terminator");
669 func.remove_inst(term);
670 let mut build = rucc_ir::Builder::new(&mut func, block);
671 build.ret(&[]);
672 let lied = an.settle(&func, Preserved::ALL, true);
673 assert_eq!(lied, vec![Analysis::Cfg, Analysis::Dominators, Analysis::Loops]);
674 for &analysis in Analysis::EVERY {
677 assert!(!an.holds(analysis));
678 }
679 }
680
681 #[test]
682 fn a_lie_about_the_frontiers_is_caught_the_same_way() {
683 let mut func = func();
684 let mut an = crate::machine::fixtures::analyses();
685 an.frontiers(&func);
686 an.control_dependence(&func);
687 let block = Block::from_usize(3);
690 let term = func.terminator(block).expect("the helper gives every block a terminator");
691 func.remove_inst(term);
692 let mut build = rucc_ir::Builder::new(&mut func, block);
693 build.ret(&[]);
694 let lied = an.settle(&func, Preserved::ALL, true);
695 assert!(lied.contains(&Analysis::Frontiers));
696 assert!(lied.contains(&Analysis::ControlDependence));
697 }
698
699 #[test]
700 fn the_check_costs_nothing_when_it_is_off() {
701 let mut func = func();
702 let mut an = crate::machine::fixtures::analyses();
703 an.cfg(&func);
704 let block = Block::from_usize(3);
705 let term = func.terminator(block).expect("the helper gives every block a terminator");
706 func.remove_inst(term);
707 let mut build = rucc_ir::Builder::new(&mut func, block);
708 build.ret(&[]);
709 assert!(an.settle(&func, Preserved::ALL, false).is_empty());
710 assert!(an.holds(Analysis::Cfg));
713 }
714
715 #[test]
716 fn an_analysis_nobody_asked_for_is_not_checked() {
717 let func = func();
718 let mut an = crate::machine::fixtures::analyses();
719 assert!(an.settle(&func, Preserved::ALL, true).is_empty());
720 }
721
722 #[test]
723 fn a_declaration_has_analyses_like_anything_else() {
724 let mut names = Interner::new();
727 let func = Func::new(names.intern("declared"), Signature::new());
728 let mut an = crate::machine::fixtures::analyses();
729 assert!(an.cfg(&func).entry().is_none());
730 an.loops(&func);
731 an.post_dominators(&func);
732 assert!(an.settle(&func, Preserved::ALL, true).is_empty());
733 }
734
735 #[test]
736 fn clearing_takes_everything() {
737 let func = func();
738 let mut an = crate::machine::fixtures::analyses();
739 an.loops(&func);
740 an.clear();
741 for &analysis in Analysis::EVERY {
742 assert!(!an.holds(analysis));
743 }
744 }
745}