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 mut alive = [false; Analysis::EVERY.len()];
346 for &analysis in Analysis::EVERY {
347 let kept =
348 keeps.keeps(analysis) && analysis.needs().iter().all(|&need| alive[need as usize]);
349 alive[analysis as usize] = kept;
350 if !kept {
351 self.drop(analysis);
352 }
353 }
354 lied
355 }
356
357 pub fn clear(&mut self) {
363 *self = Self::new(self.machine);
364 }
365
366 fn drop(&mut self, analysis: Analysis) {
368 match analysis {
369 Analysis::Cfg => self.cfg = None,
370 Analysis::Dominators => self.doms = None,
371 Analysis::PostDominators => self.post = None,
372 Analysis::Loops => self.loops = None,
373 Analysis::Frontiers => self.frontiers = None,
374 Analysis::ControlDependence => self.control = None,
375 Analysis::Frequencies => self.frequencies = None,
376 Analysis::Liveness => self.live = None,
377 Analysis::Pressure => self.pressure = None,
378 }
379 }
380
381 fn lies(&self, func: &Func, keeps: Preserved) -> Vec<Analysis> {
388 let wanted: Vec<Analysis> = Analysis::EVERY
389 .iter()
390 .copied()
391 .filter(|&it| self.holds(it) && keeps.keeps(it))
392 .collect();
393 if wanted.is_empty() {
394 return Vec::new();
395 }
396 let cfg = Cfg::new(func);
399 let mut lied = Vec::new();
400 for analysis in wanted {
401 let same = match analysis {
402 Analysis::Cfg => self.cfg.as_ref() == Some(&cfg),
403 Analysis::Dominators => self.doms.as_ref() == Some(&Dominators::new(&cfg)),
404 Analysis::PostDominators => self.post.as_ref() == Some(&PostDominators::new(&cfg)),
405 Analysis::Loops => {
406 self.loops.as_ref() == Some(&Loops::new(&cfg, &Dominators::new(&cfg)))
407 }
408 Analysis::Frontiers => {
409 self.frontiers.as_ref() == Some(&Frontiers::new(&cfg, &Dominators::new(&cfg)))
410 }
411 Analysis::ControlDependence => {
412 self.control.as_ref()
413 == Some(&ControlDependence::new(&cfg, &PostDominators::new(&cfg)))
414 }
415 Analysis::Frequencies => {
416 let doms = Dominators::new(&cfg);
417 let loops = Loops::new(&cfg, &doms);
418 let now = Frequencies::of(func, &cfg, &loops, &Callees::nothing());
419 self.frequencies.as_ref() == Some(&now)
420 }
421 Analysis::Liveness => self.live.as_ref() == Some(&Liveness::of(func, &cfg)),
422 Analysis::Pressure => {
423 let live = Liveness::of(func, &cfg);
424 self.pressure.as_ref() == Some(&Pressure::of(func, &cfg, &live))
425 }
426 };
427 if !same {
428 lied.push(analysis);
429 }
430 }
431 lied
432 }
433}
434
435#[cfg(test)]
436mod tests {
437 use rucc_base::Interner;
438 use rucc_ir::{Block, Func, Signature};
439
440 use super::{Analysis, Preserved};
441 use crate::testing::graph;
442
443 fn func() -> Func {
446 graph(&[&[1, 2], &[3], &[3], &[4, 1], &[]])
447 }
448
449 #[test]
450 fn an_analysis_is_built_out_of_ones_that_come_before_it() {
451 for &analysis in Analysis::EVERY {
454 for &need in analysis.needs() {
455 assert!(need < analysis, "{} is built out of a later analysis", analysis.name());
456 }
457 }
458 }
459
460 #[test]
461 fn every_analysis_is_in_the_list_once() {
462 for &analysis in Analysis::EVERY {
463 let found = Analysis::EVERY.iter().filter(|&&it| it == analysis).count();
464 assert_eq!(found, 1, "{} appears twice", analysis.name());
465 }
466 assert_eq!(Analysis::EVERY.len(), 9);
467 }
468
469 #[test]
470 fn all_keeps_everything_and_none_keeps_nothing() {
471 for &analysis in Analysis::EVERY {
472 assert!(Preserved::ALL.keeps(analysis));
473 assert!(!Preserved::NONE.keeps(analysis));
474 }
475 }
476
477 #[test]
478 fn a_named_set_holds_what_was_named_and_nothing_else() {
479 let keeps = Preserved::NONE.and(Analysis::Cfg).and(Analysis::Loops);
480 assert!(keeps.keeps(Analysis::Cfg));
481 assert!(keeps.keeps(Analysis::Loops));
482 assert!(!keeps.keeps(Analysis::Dominators));
483 assert!(!keeps.keeps(Analysis::PostDominators));
484 }
485
486 #[test]
487 fn nothing_is_computed_until_it_is_asked_for() {
488 let mut an = crate::machine::fixtures::analyses();
489 for &analysis in Analysis::EVERY {
490 assert!(!an.holds(analysis));
491 }
492 let func = func();
493 an.dominators(&func);
494 assert!(an.holds(Analysis::Cfg));
497 assert!(an.holds(Analysis::Dominators));
498 assert!(!an.holds(Analysis::Loops));
499 assert!(!an.holds(Analysis::PostDominators));
500 }
501
502 #[test]
503 fn asking_twice_gives_the_same_answer_and_the_second_one_is_free() {
504 let func = func();
505 let mut an = crate::machine::fixtures::analyses();
506 let first = an.cfg(&func).clone();
507 let second = an.cfg(&func);
508 assert_eq!(&first, second);
509 }
510
511 #[test]
512 fn the_loop_forest_pulls_in_what_it_is_built_out_of() {
513 let func = func();
514 let mut an = crate::machine::fixtures::analyses();
515 an.loops(&func);
516 assert!(an.holds(Analysis::Cfg));
517 assert!(an.holds(Analysis::Dominators));
518 assert!(an.holds(Analysis::Loops));
519 }
520
521 #[test]
522 fn preserving_everything_keeps_everything() {
523 let func = func();
524 let mut an = crate::machine::fixtures::analyses();
525 an.loops(&func);
526 an.frontiers(&func);
527 an.control_dependence(&func);
528 an.frequencies(&func);
529 an.pressure(&func);
530 assert!(an.settle(&func, Preserved::ALL, true).is_empty());
531 for &analysis in Analysis::EVERY {
532 assert!(an.holds(analysis), "{} was thrown away", analysis.name());
533 }
534 }
535
536 #[test]
537 fn the_pressure_falls_with_the_liveness_it_was_counted_from() {
538 let func = func();
539 let mut an = crate::machine::fixtures::analyses();
540 an.pressure(&func);
541 assert!(an.holds(Analysis::Liveness), "it had to be computed to count anything");
542 let keeps = Preserved::NONE.and(Analysis::Cfg).and(Analysis::Pressure);
543 an.settle(&func, keeps, false);
544 assert!(an.holds(Analysis::Cfg));
545 assert!(!an.holds(Analysis::Liveness));
546 assert!(!an.holds(Analysis::Pressure), "a count outlived what it counted");
547 }
548
549 #[test]
550 fn preserving_nothing_empties_the_cache() {
551 let func = func();
552 let mut an = crate::machine::fixtures::analyses();
553 an.loops(&func);
554 an.frontiers(&func);
555 an.control_dependence(&func);
556 an.settle(&func, Preserved::NONE, false);
557 for &analysis in Analysis::EVERY {
558 assert!(!an.holds(analysis), "{} outlived the pass", analysis.name());
559 }
560 }
561
562 #[test]
563 fn losing_the_graph_loses_what_was_built_on_it() {
564 let func = func();
565 let mut an = crate::machine::fixtures::analyses();
566 an.loops(&func);
567 an.post_dominators(&func);
568 let keeps = Preserved::NONE
572 .and(Analysis::Dominators)
573 .and(Analysis::PostDominators)
574 .and(Analysis::Loops);
575 an.settle(&func, keeps, false);
576 for &analysis in Analysis::EVERY {
577 assert!(!an.holds(analysis), "{} outlived the graph", analysis.name());
578 }
579 }
580
581 #[test]
582 fn losing_the_dominator_tree_loses_the_forest_and_leaves_the_graph() {
583 let func = func();
584 let mut an = crate::machine::fixtures::analyses();
585 an.loops(&func);
586 an.post_dominators(&func);
587 let keeps =
588 Preserved::NONE.and(Analysis::Cfg).and(Analysis::PostDominators).and(Analysis::Loops);
589 an.settle(&func, keeps, false);
590 assert!(an.holds(Analysis::Cfg));
591 assert!(an.holds(Analysis::PostDominators));
592 assert!(!an.holds(Analysis::Dominators), "the tree was not preserved");
593 assert!(!an.holds(Analysis::Loops), "the forest outlived the tree it needs");
594 }
595
596 #[test]
597 fn each_frontier_falls_with_the_tree_it_was_walked_on_and_not_the_other_one() {
598 let func = func();
602 let mut an = crate::machine::fixtures::analyses();
603 an.frontiers(&func);
604 an.control_dependence(&func);
605 let keeps = Preserved::NONE
606 .and(Analysis::Cfg)
607 .and(Analysis::Dominators)
608 .and(Analysis::Frontiers)
609 .and(Analysis::ControlDependence);
610 an.settle(&func, keeps, false);
611 assert!(an.holds(Analysis::Frontiers), "the frontier stands on a tree that stood");
612 assert!(!an.holds(Analysis::ControlDependence), "the post-dominator tree went with it");
613 }
614
615 #[test]
616 fn the_frequencies_fall_with_the_loop_forest_they_were_worked_out_from() {
617 let func = func();
618 let mut an = crate::machine::fixtures::analyses();
619 an.frequencies(&func);
620 for analysis in [Analysis::Cfg, Analysis::Dominators, Analysis::Loops] {
623 assert!(an.holds(analysis), "{} was not pulled in", analysis.name());
624 }
625 let keeps =
626 Preserved::NONE.and(Analysis::Cfg).and(Analysis::Dominators).and(Analysis::Frequencies);
627 an.settle(&func, keeps, false);
628 assert!(!an.holds(Analysis::Loops), "the forest was not preserved");
629 assert!(!an.holds(Analysis::Frequencies), "a frequency outlived the loop it counted");
630 }
631
632 #[test]
633 fn a_pass_that_says_it_kept_the_graph_and_moved_an_edge_is_caught() {
634 let mut func = func();
635 let mut an = crate::machine::fixtures::analyses();
636 an.loops(&func);
637 let block = Block::from_usize(3);
641 let term = func.terminator(block).expect("the helper gives every block a terminator");
642 func.remove_inst(term);
643 let mut build = rucc_ir::Builder::new(&mut func, block);
644 build.ret(&[]);
645 let lied = an.settle(&func, Preserved::ALL, true);
646 assert_eq!(lied, vec![Analysis::Cfg, Analysis::Dominators, Analysis::Loops]);
647 for &analysis in Analysis::EVERY {
650 assert!(!an.holds(analysis));
651 }
652 }
653
654 #[test]
655 fn a_lie_about_the_frontiers_is_caught_the_same_way() {
656 let mut func = func();
657 let mut an = crate::machine::fixtures::analyses();
658 an.frontiers(&func);
659 an.control_dependence(&func);
660 let block = Block::from_usize(3);
663 let term = func.terminator(block).expect("the helper gives every block a terminator");
664 func.remove_inst(term);
665 let mut build = rucc_ir::Builder::new(&mut func, block);
666 build.ret(&[]);
667 let lied = an.settle(&func, Preserved::ALL, true);
668 assert!(lied.contains(&Analysis::Frontiers));
669 assert!(lied.contains(&Analysis::ControlDependence));
670 }
671
672 #[test]
673 fn the_check_costs_nothing_when_it_is_off() {
674 let mut func = func();
675 let mut an = crate::machine::fixtures::analyses();
676 an.cfg(&func);
677 let block = Block::from_usize(3);
678 let term = func.terminator(block).expect("the helper gives every block a terminator");
679 func.remove_inst(term);
680 let mut build = rucc_ir::Builder::new(&mut func, block);
681 build.ret(&[]);
682 assert!(an.settle(&func, Preserved::ALL, false).is_empty());
683 assert!(an.holds(Analysis::Cfg));
686 }
687
688 #[test]
689 fn an_analysis_nobody_asked_for_is_not_checked() {
690 let func = func();
691 let mut an = crate::machine::fixtures::analyses();
692 assert!(an.settle(&func, Preserved::ALL, true).is_empty());
693 }
694
695 #[test]
696 fn a_declaration_has_analyses_like_anything_else() {
697 let mut names = Interner::new();
700 let func = Func::new(names.intern("declared"), Signature::new());
701 let mut an = crate::machine::fixtures::analyses();
702 assert!(an.cfg(&func).entry().is_none());
703 an.loops(&func);
704 an.post_dominators(&func);
705 assert!(an.settle(&func, Preserved::ALL, true).is_empty());
706 }
707
708 #[test]
709 fn clearing_takes_everything() {
710 let func = func();
711 let mut an = crate::machine::fixtures::analyses();
712 an.loops(&func);
713 an.clear();
714 for &analysis in Analysis::EVERY {
715 assert!(!an.holds(analysis));
716 }
717 }
718}