1use crate::lcnf::*;
6use std::collections::HashMap;
7
8use super::functions::ValueNumber;
9
10use super::functions::*;
11use std::collections::{HashSet, VecDeque};
12
13#[allow(dead_code)]
14#[derive(Debug, Clone)]
15pub struct GVNAnalysisCache {
16 pub(super) entries: std::collections::HashMap<String, GVNCacheEntry>,
17 pub(super) max_size: usize,
18 pub(super) hits: u64,
19 pub(super) misses: u64,
20}
21impl GVNAnalysisCache {
22 #[allow(dead_code)]
23 pub fn new(max_size: usize) -> Self {
24 GVNAnalysisCache {
25 entries: std::collections::HashMap::new(),
26 max_size,
27 hits: 0,
28 misses: 0,
29 }
30 }
31 #[allow(dead_code)]
32 pub fn get(&mut self, key: &str) -> Option<&GVNCacheEntry> {
33 if self.entries.contains_key(key) {
34 self.hits += 1;
35 self.entries.get(key)
36 } else {
37 self.misses += 1;
38 None
39 }
40 }
41 #[allow(dead_code)]
42 pub fn insert(&mut self, key: String, data: Vec<u8>) {
43 if self.entries.len() >= self.max_size {
44 if let Some(oldest) = self.entries.keys().next().cloned() {
45 self.entries.remove(&oldest);
46 }
47 }
48 self.entries.insert(
49 key.clone(),
50 GVNCacheEntry {
51 key,
52 data,
53 timestamp: 0,
54 valid: true,
55 },
56 );
57 }
58 #[allow(dead_code)]
59 pub fn invalidate(&mut self, key: &str) {
60 if let Some(entry) = self.entries.get_mut(key) {
61 entry.valid = false;
62 }
63 }
64 #[allow(dead_code)]
65 pub fn clear(&mut self) {
66 self.entries.clear();
67 }
68 #[allow(dead_code)]
69 pub fn hit_rate(&self) -> f64 {
70 let total = self.hits + self.misses;
71 if total == 0 {
72 return 0.0;
73 }
74 self.hits as f64 / total as f64
75 }
76 #[allow(dead_code)]
77 pub fn size(&self) -> usize {
78 self.entries.len()
79 }
80}
81#[derive(Debug, Default)]
87pub struct ExprCanonicaliser {
88 pub commutative_fns: std::collections::HashSet<String>,
90 pub canonicalisations: usize,
92}
93impl ExprCanonicaliser {
94 pub fn new() -> Self {
95 let mut c = ExprCanonicaliser::default();
96 c.commutative_fns.insert("add".to_string());
97 c.commutative_fns.insert("mul".to_string());
98 c.commutative_fns.insert("and".to_string());
99 c.commutative_fns.insert("or".to_string());
100 c
101 }
102 pub fn canonicalise(&mut self, expr: NormExpr) -> NormExpr {
104 match &expr {
105 NormExpr::App(NormArg::Vn(_), args) if args.len() == 2 => {
106 let mut sorted_args = args.clone();
107 sorted_args.sort_by_key(|a| match a {
108 NormArg::Vn(vn) => *vn,
109 NormArg::LitNat(n) => *n as ValueNumber,
110 _ => u32::MAX,
111 });
112 if sorted_args != *args {
113 self.canonicalisations += 1;
114 NormExpr::App(
115 match &expr {
116 NormExpr::App(f, _) => f.clone(),
117 _ => unreachable!(),
118 },
119 sorted_args,
120 )
121 } else {
122 expr
123 }
124 }
125 _ => expr,
126 }
127 }
128}
129#[derive(Debug, Clone, Default)]
131pub struct GVNStatistics {
132 pub total_vns: usize,
134 pub lit_redundancies: usize,
136 pub proj_redundancies: usize,
138 pub ctor_redundancies: usize,
140 pub app_redundancies: usize,
142 pub fvar_redundancies: usize,
144 pub phi_translations: usize,
146 pub alg_simplifications: usize,
148 pub time_ns: u64,
150}
151impl GVNStatistics {
152 pub fn new() -> Self {
153 GVNStatistics::default()
154 }
155 pub fn total_redundancies(&self) -> usize {
156 self.lit_redundancies
157 + self.proj_redundancies
158 + self.ctor_redundancies
159 + self.app_redundancies
160 + self.fvar_redundancies
161 }
162 pub fn print_summary(&self) {
163 let _ = format!(
164 "GVN: {} VNs, {} redundancies ({} lit, {} proj, {} ctor, {} app, {} fvar)",
165 self.total_vns,
166 self.total_redundancies(),
167 self.lit_redundancies,
168 self.proj_redundancies,
169 self.ctor_redundancies,
170 self.app_redundancies,
171 self.fvar_redundancies,
172 );
173 }
174}
175#[derive(Debug, Clone, Default)]
177pub struct GVNReport {
178 pub expressions_numbered: usize,
180 pub redundancies_eliminated: usize,
182 pub phi_translations: usize,
184}
185impl GVNReport {
186 pub fn merge(&mut self, other: &GVNReport) {
187 self.expressions_numbered += other.expressions_numbered;
188 self.redundancies_eliminated += other.redundancies_eliminated;
189 self.phi_translations += other.phi_translations;
190 }
191}
192#[derive(Debug, Default)]
197pub struct CCPState {
198 pub known: HashMap<LcnfVarId, KnownConstant>,
200 pub folded: usize,
202 pub dead_branches: usize,
204}
205impl CCPState {
206 pub fn new() -> Self {
207 CCPState::default()
208 }
209 pub fn set_known(&mut self, var: LcnfVarId, val: KnownConstant) {
210 self.known.insert(var, val);
211 }
212 pub fn get_known(&self, var: &LcnfVarId) -> &KnownConstant {
213 self.known.get(var).unwrap_or(&KnownConstant::Top)
214 }
215 pub fn run(&mut self, decl: &mut LcnfFunDecl) {
218 self.propagate_in_expr(&mut decl.body);
219 }
220 pub(super) fn propagate_in_expr(&mut self, expr: &mut LcnfExpr) {
221 match expr {
222 LcnfExpr::Let {
223 id, value, body, ..
224 } => {
225 if let LcnfLetValue::Lit(lit) = value {
226 self.set_known(*id, KnownConstant::Lit(lit.clone()));
227 }
228 self.propagate_in_expr(body);
229 }
230 LcnfExpr::Case {
231 scrutinee,
232 alts,
233 default,
234 ..
235 } => {
236 match self.get_known(scrutinee).clone() {
237 KnownConstant::Lit(LcnfLit::Nat(n)) => {
238 if let Some(alt) = alts.iter().find(|a| a.ctor_tag == n as u32) {
239 self.dead_branches += alts.len() - 1;
240 let matching_body = alt.body.clone();
241 *expr = matching_body;
242 self.folded += 1;
243 return;
244 }
245 }
246 _ => {}
247 }
248 for alt in alts.iter_mut() {
249 self.propagate_in_expr(&mut alt.body);
250 }
251 if let Some(d) = default {
252 self.propagate_in_expr(d);
253 }
254 }
255 _ => {}
256 }
257 }
258}
259#[derive(Debug, Clone, Default)]
261pub struct GVNFunctionSummary {
262 pub return_vns: Vec<ValueNumber>,
264 pub is_pure_fn: bool,
266 pub param_equalities: Vec<(usize, usize)>,
268}
269impl GVNFunctionSummary {
270 pub fn new() -> Self {
271 GVNFunctionSummary::default()
272 }
273 pub fn mark_pure(&mut self) {
274 self.is_pure_fn = true;
275 }
276}
277#[derive(Debug, Default)]
280pub struct ScopedValueContext {
281 pub stack: Vec<Vec<(LcnfVarId, ValueNumber)>>,
283 pub current: HashMap<LcnfVarId, ValueNumber>,
285}
286impl ScopedValueContext {
287 pub fn new() -> Self {
288 ScopedValueContext {
289 stack: vec![Vec::new()],
290 current: HashMap::new(),
291 }
292 }
293 pub fn push_scope(&mut self) {
295 self.stack.push(Vec::new());
296 }
297 pub fn pop_scope(&mut self) {
299 if let Some(scope) = self.stack.pop() {
300 for (var, _) in scope {
301 self.current.remove(&var);
302 }
303 }
304 }
305 pub fn bind(&mut self, var: LcnfVarId, vn: ValueNumber) {
307 self.current.insert(var, vn);
308 if let Some(scope) = self.stack.last_mut() {
309 scope.push((var, vn));
310 }
311 }
312 pub fn lookup(&self, var: &LcnfVarId) -> Option<ValueNumber> {
314 self.current.get(var).copied()
315 }
316 pub fn scope_depth(&self) -> usize {
317 self.stack.len()
318 }
319}
320#[derive(Debug, Default)]
323pub struct HashConsingTable {
324 pub(super) table: HashMap<NormExpr, LcnfLetValue>,
325}
326impl HashConsingTable {
327 pub fn new() -> Self {
328 HashConsingTable::default()
329 }
330 pub fn intern(&mut self, key: NormExpr, value: LcnfLetValue) -> &LcnfLetValue {
333 self.table.entry(key).or_insert(value)
334 }
335 pub fn len(&self) -> usize {
336 self.table.len()
337 }
338 pub fn is_empty(&self) -> bool {
339 self.table.is_empty()
340 }
341}
342#[derive(Debug, Default)]
344pub struct RedundancyCollector {
345 pub redundancies: Vec<Redundancy>,
346}
347impl RedundancyCollector {
348 pub fn new() -> Self {
349 RedundancyCollector::default()
350 }
351 pub fn collect(&mut self, decl: &LcnfFunDecl) {
353 let mut pass = GVNPass::default();
354 let mut table = ValueTable::new();
355 let mut fact = GVNFact::new();
356 pass.assign_value_numbers(decl, &mut table, &mut fact);
357 self.find_redundant(&decl.body, &table, &GVNFact::new());
358 }
359 pub(super) fn find_redundant(&mut self, expr: &LcnfExpr, table: &ValueTable, fact: &GVNFact) {
360 let mut fact = fact.clone();
361 match expr {
362 LcnfExpr::Let {
363 id, value, body, ..
364 } => {
365 let key = gvn_norm_value(value, &fact);
366 if let Some(vn) = table.lookup(&key) {
367 if let Some(canon) = table.canonical_var(vn) {
368 if canon != *id {
369 self.redundancies.push(Redundancy {
370 redundant_var: *id,
371 canonical_var: canon,
372 vn,
373 });
374 }
375 }
376 fact.insert(*id, vn);
377 }
378 self.find_redundant(body, table, &fact);
379 }
380 LcnfExpr::Case { alts, default, .. } => {
381 for alt in alts {
382 self.find_redundant(&alt.body, table, &fact);
383 }
384 if let Some(d) = default {
385 self.find_redundant(d, table, &fact);
386 }
387 }
388 _ => {}
389 }
390 }
391 pub fn num_redundancies(&self) -> usize {
392 self.redundancies.len()
393 }
394}
395#[allow(dead_code)]
396#[derive(Debug, Clone)]
397pub struct GVNDominatorTree {
398 pub idom: Vec<Option<u32>>,
399 pub dom_children: Vec<Vec<u32>>,
400 pub dom_depth: Vec<u32>,
401}
402impl GVNDominatorTree {
403 #[allow(dead_code)]
404 pub fn new(size: usize) -> Self {
405 GVNDominatorTree {
406 idom: vec![None; size],
407 dom_children: vec![Vec::new(); size],
408 dom_depth: vec![0; size],
409 }
410 }
411 #[allow(dead_code)]
412 pub fn set_idom(&mut self, node: usize, idom: u32) {
413 self.idom[node] = Some(idom);
414 }
415 #[allow(dead_code)]
416 pub fn dominates(&self, a: usize, b: usize) -> bool {
417 if a == b {
418 return true;
419 }
420 let mut cur = b;
421 loop {
422 match self.idom[cur] {
423 Some(parent) if parent as usize == a => return true,
424 Some(parent) if parent as usize == cur => return false,
425 Some(parent) => cur = parent as usize,
426 None => return false,
427 }
428 }
429 }
430 #[allow(dead_code)]
431 pub fn depth(&self, node: usize) -> u32 {
432 self.dom_depth.get(node).copied().unwrap_or(0)
433 }
434}
435#[derive(Debug, Clone)]
437pub struct GVNConfig {
438 pub do_phi_translation: bool,
441 pub max_depth: usize,
443}
444#[derive(Debug, Clone, PartialEq, Eq, Hash)]
447pub enum Predicate {
448 EqLit(LcnfVarId, LcnfLit),
450 NeLit(LcnfVarId, LcnfLit),
452 VarEq(LcnfVarId, LcnfVarId),
454}
455#[derive(Debug, Clone, Default)]
460pub struct AnticipationSet {
461 pub anticipated: std::collections::HashSet<NormExpr>,
463}
464impl AnticipationSet {
465 pub fn new() -> Self {
466 AnticipationSet::default()
467 }
468 pub fn add(&mut self, expr: NormExpr) {
469 self.anticipated.insert(expr);
470 }
471 pub fn contains(&self, expr: &NormExpr) -> bool {
472 self.anticipated.contains(expr)
473 }
474 pub fn meet(&self, other: &AnticipationSet) -> AnticipationSet {
476 AnticipationSet {
477 anticipated: self
478 .anticipated
479 .intersection(&other.anticipated)
480 .cloned()
481 .collect(),
482 }
483 }
484 pub fn is_empty(&self) -> bool {
485 self.anticipated.is_empty()
486 }
487}
488#[allow(dead_code)]
489#[derive(Debug, Clone)]
490pub struct GVNLivenessInfo {
491 pub live_in: Vec<std::collections::HashSet<u32>>,
492 pub live_out: Vec<std::collections::HashSet<u32>>,
493 pub defs: Vec<std::collections::HashSet<u32>>,
494 pub uses: Vec<std::collections::HashSet<u32>>,
495}
496impl GVNLivenessInfo {
497 #[allow(dead_code)]
498 pub fn new(block_count: usize) -> Self {
499 GVNLivenessInfo {
500 live_in: vec![std::collections::HashSet::new(); block_count],
501 live_out: vec![std::collections::HashSet::new(); block_count],
502 defs: vec![std::collections::HashSet::new(); block_count],
503 uses: vec![std::collections::HashSet::new(); block_count],
504 }
505 }
506 #[allow(dead_code)]
507 pub fn add_def(&mut self, block: usize, var: u32) {
508 if block < self.defs.len() {
509 self.defs[block].insert(var);
510 }
511 }
512 #[allow(dead_code)]
513 pub fn add_use(&mut self, block: usize, var: u32) {
514 if block < self.uses.len() {
515 self.uses[block].insert(var);
516 }
517 }
518 #[allow(dead_code)]
519 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
520 self.live_in
521 .get(block)
522 .map(|s| s.contains(&var))
523 .unwrap_or(false)
524 }
525 #[allow(dead_code)]
526 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
527 self.live_out
528 .get(block)
529 .map(|s| s.contains(&var))
530 .unwrap_or(false)
531 }
532}
533#[allow(dead_code)]
534#[derive(Debug, Clone, Default)]
535pub struct GVNPassStats {
536 pub total_runs: u32,
537 pub successful_runs: u32,
538 pub total_changes: u64,
539 pub time_ms: u64,
540 pub iterations_used: u32,
541}
542impl GVNPassStats {
543 #[allow(dead_code)]
544 pub fn new() -> Self {
545 Self::default()
546 }
547 #[allow(dead_code)]
548 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
549 self.total_runs += 1;
550 self.successful_runs += 1;
551 self.total_changes += changes;
552 self.time_ms += time_ms;
553 self.iterations_used = iterations;
554 }
555 #[allow(dead_code)]
556 pub fn average_changes_per_run(&self) -> f64 {
557 if self.total_runs == 0 {
558 return 0.0;
559 }
560 self.total_changes as f64 / self.total_runs as f64
561 }
562 #[allow(dead_code)]
563 pub fn success_rate(&self) -> f64 {
564 if self.total_runs == 0 {
565 return 0.0;
566 }
567 self.successful_runs as f64 / self.total_runs as f64
568 }
569 #[allow(dead_code)]
570 pub fn format_summary(&self) -> String {
571 format!(
572 "Runs: {}/{}, Changes: {}, Time: {}ms",
573 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
574 )
575 }
576}
577#[derive(Debug, Clone, PartialEq, Eq, Hash)]
579pub enum NormArg {
580 Vn(ValueNumber),
581 LitNat(u64),
582 LitStr(String),
583 Erased,
584}
585#[derive(Debug, Default)]
589pub struct LoadEliminatorGVN {
590 pub eliminated: usize,
592 pub(super) load_cache: HashMap<(LcnfVarId, u32), LcnfVarId>,
594}
595impl LoadEliminatorGVN {
596 pub fn new() -> Self {
597 LoadEliminatorGVN::default()
598 }
599 pub fn run(&mut self, decl: &mut LcnfFunDecl) {
601 self.load_cache.clear();
602 self.elim_in_expr(&mut decl.body);
603 }
604 pub(super) fn elim_in_expr(&mut self, expr: &mut LcnfExpr) {
605 match expr {
606 LcnfExpr::Let {
607 id, value, body, ..
608 } => {
609 if let LcnfLetValue::Proj(_, idx, src) = value {
610 let key = (*src, *idx);
611 if let Some(&cached) = self.load_cache.get(&key) {
612 *value = LcnfLetValue::FVar(cached);
613 self.eliminated += 1;
614 } else {
615 self.load_cache.insert(key, *id);
616 }
617 }
618 if let LcnfLetValue::Reuse(slot, _, _, _) = value {
619 let slot_copy = *slot;
620 self.load_cache.retain(|&(src, _), _| src != slot_copy);
621 }
622 self.elim_in_expr(body);
623 }
624 LcnfExpr::Case { alts, default, .. } => {
625 let saved = self.load_cache.clone();
626 for alt in alts.iter_mut() {
627 self.load_cache = saved.clone();
628 self.elim_in_expr(&mut alt.body);
629 }
630 if let Some(d) = default {
631 self.load_cache = saved;
632 self.elim_in_expr(d);
633 }
634 }
635 _ => {}
636 }
637 }
638}
639#[derive(Debug, Default)]
645pub struct PredicateGVN {
646 pub(super) active_preds: Vec<Predicate>,
648 pub equalities_derived: usize,
650}
651impl PredicateGVN {
652 pub fn new() -> Self {
653 PredicateGVN::default()
654 }
655 pub fn enter_branch(&mut self, scrutinee: LcnfVarId, ctor_tag: u32) {
657 self.active_preds
658 .push(Predicate::EqLit(scrutinee, LcnfLit::Nat(ctor_tag as u64)));
659 }
660 pub fn exit_branch(&mut self) {
662 self.active_preds.pop();
663 }
664 pub fn knows_eq_lit(&self, var: LcnfVarId, lit: &LcnfLit) -> bool {
666 self.active_preds
667 .contains(&Predicate::EqLit(var, lit.clone()))
668 }
669 pub fn run(&mut self, decl: &mut LcnfFunDecl, base_pass: &mut GVNPass) {
671 self.run_in_expr(&mut decl.body, base_pass);
672 }
673 pub(super) fn run_in_expr(&mut self, expr: &mut LcnfExpr, base_pass: &mut GVNPass) {
674 match expr {
675 LcnfExpr::Let { body, .. } => {
676 self.run_in_expr(body, base_pass);
677 }
678 LcnfExpr::Case {
679 scrutinee,
680 alts,
681 default,
682 ..
683 } => {
684 for alt in alts.iter_mut() {
685 self.enter_branch(*scrutinee, alt.ctor_tag);
686 self.equalities_derived += 1;
687 self.run_in_expr(&mut alt.body, base_pass);
688 self.exit_branch();
689 }
690 if let Some(d) = default {
691 self.run_in_expr(d, base_pass);
692 }
693 }
694 _ => {}
695 }
696 }
697}
698#[derive(Debug)]
707pub struct AlgebraicSimplifier {
708 pub rules: Vec<AlgRule>,
710 pub total_simplified: usize,
712}
713impl AlgebraicSimplifier {
714 pub fn new() -> Self {
715 AlgebraicSimplifier::default()
716 }
717 pub fn simplify(&mut self, expr: &NormExpr, fact: &GVNFact) -> Option<NormExpr> {
720 let _ = fact;
721 match expr {
722 NormExpr::App(NormArg::Vn(_), args) if args.last() == Some(&NormArg::LitNat(0)) => {
723 self.rules[0].applied += 1;
724 self.total_simplified += 1;
725 if let Some(NormArg::Vn(vn)) = args.first() {
726 Some(NormExpr::FVar(*vn))
727 } else {
728 None
729 }
730 }
731 NormExpr::App(NormArg::Vn(_), args)
732 if args.last() == Some(&NormArg::LitNat(1)) && args.len() == 2 =>
733 {
734 self.rules[1].applied += 1;
735 self.total_simplified += 1;
736 if let Some(NormArg::Vn(vn)) = args.first() {
737 Some(NormExpr::FVar(*vn))
738 } else {
739 None
740 }
741 }
742 _ => None,
743 }
744 }
745 pub fn run(&mut self, _decl: &mut LcnfFunDecl) {}
747}
748#[derive(Debug, Clone)]
752pub struct DomTreeNode {
753 pub var: LcnfVarId,
755 pub children: Vec<LcnfVarId>,
757 pub depth: u32,
759}
760#[derive(Debug, Default)]
762pub struct PhiNodeSet {
763 pub phis: Vec<PhiNode>,
764 pub(super) next_vn: ValueNumber,
766}
767impl PhiNodeSet {
768 pub fn new(start_vn: ValueNumber) -> Self {
769 PhiNodeSet {
770 phis: Vec::new(),
771 next_vn: start_vn,
772 }
773 }
774 pub fn add_phi(&mut self, var: LcnfVarId, operands: Vec<PhiOperand>) -> &PhiNode {
776 let vn = self.next_vn;
777 self.next_vn += 1;
778 self.phis.push(PhiNode::new(var, operands, vn));
779 self.phis
780 .last()
781 .expect("phis is non-empty after push; invariant guaranteed by add_phi")
782 }
783 pub fn remove_trivial(&mut self) -> usize {
785 let before = self.phis.len();
786 self.phis.retain(|p| !p.is_trivial());
787 before - self.phis.len()
788 }
789 pub fn num_phis(&self) -> usize {
790 self.phis.len()
791 }
792}
793#[allow(dead_code)]
794pub struct GVNConstantFoldingHelper;
795impl GVNConstantFoldingHelper {
796 #[allow(dead_code)]
797 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
798 a.checked_add(b)
799 }
800 #[allow(dead_code)]
801 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
802 a.checked_sub(b)
803 }
804 #[allow(dead_code)]
805 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
806 a.checked_mul(b)
807 }
808 #[allow(dead_code)]
809 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
810 if b == 0 {
811 None
812 } else {
813 a.checked_div(b)
814 }
815 }
816 #[allow(dead_code)]
817 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
818 a + b
819 }
820 #[allow(dead_code)]
821 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
822 a * b
823 }
824 #[allow(dead_code)]
825 pub fn fold_neg_i64(a: i64) -> Option<i64> {
826 a.checked_neg()
827 }
828 #[allow(dead_code)]
829 pub fn fold_not_bool(a: bool) -> bool {
830 !a
831 }
832 #[allow(dead_code)]
833 pub fn fold_and_bool(a: bool, b: bool) -> bool {
834 a && b
835 }
836 #[allow(dead_code)]
837 pub fn fold_or_bool(a: bool, b: bool) -> bool {
838 a || b
839 }
840 #[allow(dead_code)]
841 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
842 a.checked_shl(b)
843 }
844 #[allow(dead_code)]
845 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
846 a.checked_shr(b)
847 }
848 #[allow(dead_code)]
849 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
850 if b == 0 {
851 None
852 } else {
853 Some(a % b)
854 }
855 }
856 #[allow(dead_code)]
857 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
858 a & b
859 }
860 #[allow(dead_code)]
861 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
862 a | b
863 }
864 #[allow(dead_code)]
865 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
866 a ^ b
867 }
868 #[allow(dead_code)]
869 pub fn fold_bitnot_i64(a: i64) -> i64 {
870 !a
871 }
872}
873#[allow(dead_code)]
874#[derive(Debug, Clone)]
875pub struct GVNDepGraph {
876 pub(super) nodes: Vec<u32>,
877 pub(super) edges: Vec<(u32, u32)>,
878}
879impl GVNDepGraph {
880 #[allow(dead_code)]
881 pub fn new() -> Self {
882 GVNDepGraph {
883 nodes: Vec::new(),
884 edges: Vec::new(),
885 }
886 }
887 #[allow(dead_code)]
888 pub fn add_node(&mut self, id: u32) {
889 if !self.nodes.contains(&id) {
890 self.nodes.push(id);
891 }
892 }
893 #[allow(dead_code)]
894 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
895 self.add_node(dep);
896 self.add_node(dependent);
897 self.edges.push((dep, dependent));
898 }
899 #[allow(dead_code)]
900 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
901 self.edges
902 .iter()
903 .filter(|(d, _)| *d == node)
904 .map(|(_, dep)| *dep)
905 .collect()
906 }
907 #[allow(dead_code)]
908 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
909 self.edges
910 .iter()
911 .filter(|(_, dep)| *dep == node)
912 .map(|(d, _)| *d)
913 .collect()
914 }
915 #[allow(dead_code)]
916 pub fn topological_sort(&self) -> Vec<u32> {
917 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
918 for &n in &self.nodes {
919 in_degree.insert(n, 0);
920 }
921 for (_, dep) in &self.edges {
922 *in_degree.entry(*dep).or_insert(0) += 1;
923 }
924 let mut queue: std::collections::VecDeque<u32> = self
925 .nodes
926 .iter()
927 .filter(|&&n| in_degree[&n] == 0)
928 .copied()
929 .collect();
930 let mut result = Vec::new();
931 while let Some(node) = queue.pop_front() {
932 result.push(node);
933 for dep in self.dependents_of(node) {
934 let cnt = in_degree.entry(dep).or_insert(0);
935 *cnt = cnt.saturating_sub(1);
936 if *cnt == 0 {
937 queue.push_back(dep);
938 }
939 }
940 }
941 result
942 }
943 #[allow(dead_code)]
944 pub fn has_cycle(&self) -> bool {
945 self.topological_sort().len() < self.nodes.len()
946 }
947}
948#[derive(Debug, Default)]
951pub struct FixpointGVN {
952 pub max_iter: usize,
954 pub iterations: usize,
956 pub converged: bool,
958 pub total_redundancies: usize,
960}
961impl FixpointGVN {
962 pub fn new(max_iter: usize) -> Self {
963 FixpointGVN {
964 max_iter,
965 iterations: 0,
966 converged: false,
967 total_redundancies: 0,
968 }
969 }
970 pub fn run(&mut self, decl: &mut LcnfFunDecl) {
972 let mut prev_state = FixpointState::new();
973 for iter in 0..self.max_iter {
974 self.iterations = iter + 1;
975 let mut pass = GVNPass::default();
976 let mut table = ValueTable::new();
977 let mut fact = GVNFact::new();
978 pass.assign_value_numbers(decl, &mut table, &mut fact);
979 let redundancies = pass.report().redundancies_eliminated;
980 self.total_redundancies += redundancies;
981 let curr_state = FixpointState {
982 table: table.clone(),
983 exit_fact: fact,
984 redundancies,
985 };
986 if curr_state.table.len() == prev_state.table.len() {
987 self.converged = true;
988 break;
989 }
990 pass.eliminate_redundant(decl, &mut table);
991 prev_state = curr_state;
992 }
993 }
994}
995#[derive(Debug, Clone)]
997pub struct AlgRule {
998 pub name: String,
1000 pub applied: usize,
1002}
1003impl AlgRule {
1004 pub fn new(name: &str) -> Self {
1005 AlgRule {
1006 name: name.to_string(),
1007 applied: 0,
1008 }
1009 }
1010}
1011#[derive(Debug, Default)]
1013pub struct DomTree {
1014 pub nodes: HashMap<LcnfVarId, DomTreeNode>,
1016 pub roots: Vec<LcnfVarId>,
1018}
1019impl DomTree {
1020 pub fn new() -> Self {
1021 DomTree::default()
1022 }
1023 pub fn build_from_decl(decl: &LcnfFunDecl) -> Self {
1025 let mut dt = DomTree::new();
1026 let mut parent: Option<LcnfVarId> = None;
1027 dt.build_in_expr(&decl.body, &mut parent, 0);
1028 dt
1029 }
1030 pub(super) fn build_in_expr(
1031 &mut self,
1032 expr: &LcnfExpr,
1033 parent: &mut Option<LcnfVarId>,
1034 depth: u32,
1035 ) {
1036 match expr {
1037 LcnfExpr::Let { id, body, .. } => {
1038 let node = DomTreeNode {
1039 var: *id,
1040 children: Vec::new(),
1041 depth,
1042 };
1043 self.nodes.insert(*id, node);
1044 if let Some(p) = *parent {
1045 if let Some(pn) = self.nodes.get_mut(&p) {
1046 pn.children.push(*id);
1047 }
1048 } else {
1049 self.roots.push(*id);
1050 }
1051 let prev = *parent;
1052 *parent = Some(*id);
1053 self.build_in_expr(body, parent, depth + 1);
1054 *parent = prev;
1055 }
1056 LcnfExpr::Case { alts, default, .. } => {
1057 for alt in alts {
1058 let mut br_parent = *parent;
1059 self.build_in_expr(&alt.body, &mut br_parent, depth);
1060 }
1061 if let Some(d) = default {
1062 let mut br_parent = *parent;
1063 self.build_in_expr(d, &mut br_parent, depth);
1064 }
1065 }
1066 _ => {}
1067 }
1068 }
1069 pub fn dominates(&self, a: LcnfVarId, b: LcnfVarId) -> bool {
1071 if a == b {
1072 return true;
1073 }
1074 if let Some(node_b) = self.nodes.get(&b) {
1075 let _ = node_b;
1076 }
1077 self.is_ancestor(a, b)
1078 }
1079 pub(super) fn is_ancestor(&self, a: LcnfVarId, target: LcnfVarId) -> bool {
1080 if let Some(node) = self.nodes.get(&a) {
1081 for &child in &node.children {
1082 if child == target || self.is_ancestor(child, target) {
1083 return true;
1084 }
1085 }
1086 }
1087 false
1088 }
1089 pub fn num_nodes(&self) -> usize {
1090 self.nodes.len()
1091 }
1092}
1093#[derive(Debug, Default)]
1097pub struct GVNPrePass {
1098 pub insertions: usize,
1100 pub eliminations: usize,
1102 pub anticipation: HashMap<LcnfVarId, AnticipationSet>,
1104}
1105impl GVNPrePass {
1106 pub fn new() -> Self {
1107 GVNPrePass::default()
1108 }
1109 pub fn compute_anticipation(&mut self, decl: &LcnfFunDecl) {
1111 let mut anticipated = AnticipationSet::new();
1112 self.anticip_in_expr(&decl.body, &mut anticipated);
1113 }
1114 pub(super) fn anticip_in_expr(&mut self, expr: &LcnfExpr, anticipated: &mut AnticipationSet) {
1115 match expr {
1116 LcnfExpr::Let {
1117 id, value, body, ..
1118 } => {
1119 self.anticipation.insert(*id, anticipated.clone());
1120 let key = norm_expr_from_value_conservative(value);
1121 anticipated.add(key);
1122 self.anticip_in_expr(body, anticipated);
1123 }
1124 LcnfExpr::Case { alts, default, .. } => {
1125 let mut branch_sets: Vec<AnticipationSet> = Vec::new();
1126 for alt in alts {
1127 let mut br = anticipated.clone();
1128 self.anticip_in_expr(&alt.body, &mut br);
1129 branch_sets.push(br);
1130 }
1131 if let Some(d) = default {
1132 let mut br = anticipated.clone();
1133 self.anticip_in_expr(d, &mut br);
1134 branch_sets.push(br);
1135 }
1136 if let Some(first) = branch_sets.first() {
1137 let mut meet = first.clone();
1138 for bs in branch_sets.iter().skip(1) {
1139 meet = meet.meet(bs);
1140 }
1141 *anticipated = meet;
1142 }
1143 }
1144 _ => {}
1145 }
1146 }
1147 pub fn run(&mut self, decl: &mut LcnfFunDecl) {
1150 self.compute_anticipation(decl);
1151 self.insertions = self
1152 .anticipation
1153 .values()
1154 .map(|a| a.anticipated.len())
1155 .sum();
1156 }
1157}
1158#[derive(Debug, Clone, PartialEq, Eq)]
1160pub struct PhiOperand {
1161 pub branch_idx: usize,
1163 pub vn: ValueNumber,
1165}
1166pub struct GVNPass {
1168 pub(super) config: GVNConfig,
1169 pub(super) report: GVNReport,
1170}
1171impl GVNPass {
1172 pub fn new(config: GVNConfig) -> Self {
1173 GVNPass {
1174 config,
1175 report: GVNReport::default(),
1176 }
1177 }
1178 pub fn run(&mut self, decls: &mut [LcnfFunDecl]) {
1181 for decl in decls.iter_mut() {
1182 let mut table = ValueTable::new();
1183 let mut fact = GVNFact::new();
1184 self.assign_value_numbers(decl, &mut table, &mut fact);
1185 self.eliminate_redundant(decl, &mut table);
1186 }
1187 }
1188 pub fn assign_value_numbers(
1191 &mut self,
1192 decl: &LcnfFunDecl,
1193 table: &mut ValueTable,
1194 fact: &mut GVNFact,
1195 ) {
1196 let mut depth = 0usize;
1197 self.vn_expr(&decl.body, table, fact, &mut depth);
1198 }
1199 pub fn lookup_or_assign(
1202 &mut self,
1203 var: LcnfVarId,
1204 value: &LcnfLetValue,
1205 table: &mut ValueTable,
1206 fact: &mut GVNFact,
1207 ) -> ValueNumber {
1208 let key = self.normalise_let_value(value, fact);
1209 if let Some(vn) = table.lookup(&key) {
1210 fact.insert(var, vn);
1211 vn
1212 } else {
1213 let vn = table.insert(key, value.clone(), var);
1214 fact.insert(var, vn);
1215 self.report.expressions_numbered += 1;
1216 vn
1217 }
1218 }
1219 pub fn eliminate_redundant(&mut self, decl: &mut LcnfFunDecl, table: &mut ValueTable) {
1222 let mut fact = GVNFact::new();
1223 self.rewrite_expr(&mut decl.body, table, &mut fact);
1224 }
1225 pub fn report(&self) -> GVNReport {
1227 self.report.clone()
1228 }
1229 pub(super) fn vn_expr(
1231 &mut self,
1232 expr: &LcnfExpr,
1233 table: &mut ValueTable,
1234 fact: &mut GVNFact,
1235 depth: &mut usize,
1236 ) {
1237 if *depth >= self.config.max_depth {
1238 return;
1239 }
1240 *depth += 1;
1241 match expr {
1242 LcnfExpr::Let {
1243 id, value, body, ..
1244 } => {
1245 self.lookup_or_assign(*id, value, table, fact);
1246 self.vn_expr(body, table, fact, depth);
1247 }
1248 LcnfExpr::Case { alts, default, .. } => {
1249 for alt in alts {
1250 let mut branch_fact = fact.clone();
1251 let mut d = *depth;
1252 self.vn_expr(&alt.body, table, &mut branch_fact, &mut d);
1253 if self.config.do_phi_translation {
1254 self.report.phi_translations += 1;
1255 }
1256 }
1257 if let Some(def) = default {
1258 let mut branch_fact = fact.clone();
1259 let mut d = *depth;
1260 self.vn_expr(def, table, &mut branch_fact, &mut d);
1261 }
1262 }
1263 LcnfExpr::Return(_) | LcnfExpr::Unreachable | LcnfExpr::TailCall(..) => {}
1264 }
1265 *depth -= 1;
1266 }
1267 pub(super) fn rewrite_expr(
1270 &mut self,
1271 expr: &mut LcnfExpr,
1272 table: &mut ValueTable,
1273 fact: &mut GVNFact,
1274 ) {
1275 match expr {
1276 LcnfExpr::Let {
1277 id,
1278 value,
1279 body,
1280 ty,
1281 ..
1282 } => {
1283 let key = self.normalise_let_value(value, fact);
1284 if let Some(vn) = table.lookup(&key) {
1285 if let Some(canon) = table.canonical_var(vn) {
1286 if canon != *id {
1287 *value = LcnfLetValue::FVar(canon);
1288 fact.insert(*id, vn);
1289 self.report.redundancies_eliminated += 1;
1290 } else {
1291 fact.insert(*id, vn);
1292 }
1293 } else {
1294 fact.insert(*id, vn);
1295 }
1296 } else {
1297 let vn = table.insert(key, value.clone(), *id);
1298 fact.insert(*id, vn);
1299 let _ = ty;
1300 }
1301 self.rewrite_expr(body, table, fact);
1302 }
1303 LcnfExpr::Case { alts, default, .. } => {
1304 for alt in alts.iter_mut() {
1305 let mut branch_fact = fact.clone();
1306 self.rewrite_expr(&mut alt.body, table, &mut branch_fact);
1307 }
1308 if let Some(def) = default {
1309 let mut branch_fact = fact.clone();
1310 self.rewrite_expr(def, table, &mut branch_fact);
1311 }
1312 }
1313 LcnfExpr::Return(_) | LcnfExpr::Unreachable | LcnfExpr::TailCall(..) => {}
1314 }
1315 }
1316 pub(super) fn normalise_let_value(&self, value: &LcnfLetValue, fact: &GVNFact) -> NormExpr {
1319 match value {
1320 LcnfLetValue::Lit(LcnfLit::Nat(n)) => NormExpr::Lit(*n),
1321 LcnfLetValue::Lit(LcnfLit::Str(s)) => NormExpr::LitStr(s.clone()),
1322 LcnfLetValue::Erased => NormExpr::Erased,
1323 LcnfLetValue::FVar(v) => {
1324 let vn = fact.get(v).unwrap_or(v.0 as ValueNumber + 1_000_000);
1325 NormExpr::FVar(vn)
1326 }
1327 LcnfLetValue::Proj(name, idx, v) => {
1328 let vn = fact.get(v).unwrap_or(v.0 as ValueNumber + 1_000_000);
1329 NormExpr::Proj(name.clone(), *idx, vn)
1330 }
1331 LcnfLetValue::Ctor(name, tag, args) => {
1332 let nargs = args.iter().map(|a| self.norm_arg(a, fact)).collect();
1333 NormExpr::Ctor(name.clone(), *tag, nargs)
1334 }
1335 LcnfLetValue::App(f, args) => {
1336 let nf = self.norm_arg(f, fact);
1337 let nargs = args.iter().map(|a| self.norm_arg(a, fact)).collect();
1338 NormExpr::App(nf, nargs)
1339 }
1340 LcnfLetValue::Reset(v) => {
1341 let vn = fact.get(v).unwrap_or(v.0 as ValueNumber + 1_000_000);
1342 NormExpr::Reset(vn)
1343 }
1344 LcnfLetValue::Reuse(..) => NormExpr::Unknown,
1345 }
1346 }
1347 pub(super) fn norm_arg(&self, arg: &LcnfArg, fact: &GVNFact) -> NormArg {
1348 match arg {
1349 LcnfArg::Var(v) => {
1350 let vn = fact.get(v).unwrap_or(v.0 as ValueNumber + 1_000_000);
1351 NormArg::Vn(vn)
1352 }
1353 LcnfArg::Lit(LcnfLit::Nat(n)) => NormArg::LitNat(*n),
1354 LcnfArg::Lit(LcnfLit::Str(s)) => NormArg::LitStr(s.clone()),
1355 LcnfArg::Erased => NormArg::Erased,
1356 LcnfArg::Type(_) => NormArg::Erased,
1357 }
1358 }
1359}
1360#[allow(dead_code)]
1361#[derive(Debug, Clone)]
1362pub struct GVNWorklist {
1363 pub(super) items: std::collections::VecDeque<u32>,
1364 pub(super) in_worklist: std::collections::HashSet<u32>,
1365}
1366impl GVNWorklist {
1367 #[allow(dead_code)]
1368 pub fn new() -> Self {
1369 GVNWorklist {
1370 items: std::collections::VecDeque::new(),
1371 in_worklist: std::collections::HashSet::new(),
1372 }
1373 }
1374 #[allow(dead_code)]
1375 pub fn push(&mut self, item: u32) -> bool {
1376 if self.in_worklist.insert(item) {
1377 self.items.push_back(item);
1378 true
1379 } else {
1380 false
1381 }
1382 }
1383 #[allow(dead_code)]
1384 pub fn pop(&mut self) -> Option<u32> {
1385 let item = self.items.pop_front()?;
1386 self.in_worklist.remove(&item);
1387 Some(item)
1388 }
1389 #[allow(dead_code)]
1390 pub fn is_empty(&self) -> bool {
1391 self.items.is_empty()
1392 }
1393 #[allow(dead_code)]
1394 pub fn len(&self) -> usize {
1395 self.items.len()
1396 }
1397 #[allow(dead_code)]
1398 pub fn contains(&self, item: u32) -> bool {
1399 self.in_worklist.contains(&item)
1400 }
1401}
1402#[derive(Debug, Default)]
1408pub struct CongruenceClosure {
1409 pub(super) parent: HashMap<ValueNumber, ValueNumber>,
1411}
1412impl CongruenceClosure {
1413 pub fn new() -> Self {
1414 CongruenceClosure::default()
1415 }
1416 pub fn union(&mut self, a: ValueNumber, b: ValueNumber) {
1418 let ra = self.find(a);
1419 let rb = self.find(b);
1420 if ra != rb {
1421 self.parent.insert(ra, rb);
1422 }
1423 }
1424 pub fn find(&mut self, vn: ValueNumber) -> ValueNumber {
1426 match self.parent.get(&vn).copied() {
1427 None => vn,
1428 Some(p) if p == vn => vn,
1429 Some(p) => {
1430 let root = self.find(p);
1431 self.parent.insert(vn, root);
1432 root
1433 }
1434 }
1435 }
1436 pub fn are_equal(&mut self, a: ValueNumber, b: ValueNumber) -> bool {
1438 self.find(a) == self.find(b)
1439 }
1440 pub fn num_classes(&self) -> usize {
1441 self.parent.len()
1442 }
1443}
1444#[derive(Debug, Clone, Default)]
1446pub struct GVNFact {
1447 pub var_to_vn: HashMap<LcnfVarId, ValueNumber>,
1448}
1449impl GVNFact {
1450 pub fn new() -> Self {
1451 GVNFact::default()
1452 }
1453 pub fn get(&self, var: &LcnfVarId) -> Option<ValueNumber> {
1454 self.var_to_vn.get(var).copied()
1455 }
1456 pub fn insert(&mut self, var: LcnfVarId, vn: ValueNumber) {
1457 self.var_to_vn.insert(var, vn);
1458 }
1459 pub fn meet(&self, other: &GVNFact) -> GVNFact {
1462 let mut result = GVNFact::new();
1463 for (var, &vn) in &self.var_to_vn {
1464 if other.var_to_vn.get(var) == Some(&vn) {
1465 result.var_to_vn.insert(*var, vn);
1466 }
1467 }
1468 result
1469 }
1470}
1471#[derive(Debug, Default)]
1477pub struct LeaderFinder {
1478 pub(super) leaders: HashMap<ValueNumber, LcnfVarId>,
1480 pub(super) members: HashMap<ValueNumber, Vec<LcnfVarId>>,
1482}
1483impl LeaderFinder {
1484 pub fn new() -> Self {
1485 LeaderFinder::default()
1486 }
1487 pub fn record(&mut self, vn: ValueNumber, var: LcnfVarId) {
1489 self.members.entry(vn).or_default().push(var);
1490 self.leaders.entry(vn).or_insert(var);
1491 }
1492 pub fn leader(&self, vn: ValueNumber) -> Option<LcnfVarId> {
1494 self.leaders.get(&vn).copied()
1495 }
1496 pub fn members(&self, vn: ValueNumber) -> &[LcnfVarId] {
1498 self.members.get(&vn).map(|v| v.as_slice()).unwrap_or(&[])
1499 }
1500 pub fn num_redundancies(&self) -> usize {
1502 self.members.values().filter(|v| v.len() > 1).count()
1503 }
1504}
1505pub struct GVNPipeline {
1507 pub do_base_gvn: bool,
1509 pub do_load_elim: bool,
1511 pub do_pre: bool,
1513 pub do_ccp: bool,
1515 pub do_fixpoint: bool,
1517 pub max_fixpoint_iter: usize,
1519 pub stats: GVNStatistics,
1521}
1522impl GVNPipeline {
1523 pub fn new() -> Self {
1524 GVNPipeline::default()
1525 }
1526 pub fn run(&mut self, decls: &mut [LcnfFunDecl]) {
1528 for decl in decls.iter_mut() {
1529 if self.do_base_gvn {
1530 let mut pass = GVNPass::default();
1531 pass.run(std::slice::from_mut(decl));
1532 let r = pass.report();
1533 self.stats.total_vns += r.expressions_numbered;
1534 self.stats.phi_translations += r.phi_translations;
1535 }
1536 if self.do_load_elim {
1537 let mut le = LoadEliminatorGVN::new();
1538 le.run(decl);
1539 self.stats.proj_redundancies += le.eliminated;
1540 }
1541 if self.do_pre {
1542 let mut pre = GVNPrePass::new();
1543 pre.run(decl);
1544 }
1545 if self.do_ccp {
1546 let mut ccp = CCPState::new();
1547 ccp.run(decl);
1548 self.stats.lit_redundancies += ccp.folded;
1549 }
1550 if self.do_fixpoint {
1551 let mut fp = FixpointGVN::new(self.max_fixpoint_iter);
1552 fp.run(decl);
1553 self.stats.total_vns += fp.total_redundancies;
1554 }
1555 }
1556 }
1557 pub fn total_redundancies(&self) -> usize {
1558 self.stats.total_redundancies()
1559 }
1560}
1561#[allow(dead_code)]
1562#[derive(Debug, Clone, PartialEq)]
1563pub enum GVNPassPhase {
1564 Analysis,
1565 Transformation,
1566 Verification,
1567 Cleanup,
1568}
1569impl GVNPassPhase {
1570 #[allow(dead_code)]
1571 pub fn name(&self) -> &str {
1572 match self {
1573 GVNPassPhase::Analysis => "analysis",
1574 GVNPassPhase::Transformation => "transformation",
1575 GVNPassPhase::Verification => "verification",
1576 GVNPassPhase::Cleanup => "cleanup",
1577 }
1578 }
1579 #[allow(dead_code)]
1580 pub fn is_modifying(&self) -> bool {
1581 matches!(self, GVNPassPhase::Transformation | GVNPassPhase::Cleanup)
1582 }
1583}
1584#[allow(dead_code)]
1585#[derive(Debug, Clone)]
1586pub struct GVNCacheEntry {
1587 pub key: String,
1588 pub data: Vec<u8>,
1589 pub timestamp: u64,
1590 pub valid: bool,
1591}
1592#[derive(Debug, Clone)]
1594pub struct FixpointState {
1595 pub table: ValueTable,
1597 pub exit_fact: GVNFact,
1599 pub redundancies: usize,
1601}
1602impl FixpointState {
1603 pub fn new() -> Self {
1604 FixpointState {
1605 table: ValueTable::new(),
1606 exit_fact: GVNFact::new(),
1607 redundancies: 0,
1608 }
1609 }
1610}
1611#[derive(Debug, Clone, Default)]
1617pub struct ValueTable {
1618 pub(super) expr_to_vn: HashMap<NormExpr, ValueNumber>,
1620 pub(super) vn_to_expr: HashMap<ValueNumber, LcnfLetValue>,
1622 pub(super) vn_to_var: HashMap<ValueNumber, LcnfVarId>,
1624 pub(super) next_vn: ValueNumber,
1626}
1627impl ValueTable {
1628 pub fn new() -> Self {
1629 ValueTable::default()
1630 }
1631 pub fn lookup(&self, key: &NormExpr) -> Option<ValueNumber> {
1633 self.expr_to_vn.get(key).copied()
1634 }
1635 pub fn canonical_var(&self, vn: ValueNumber) -> Option<LcnfVarId> {
1637 self.vn_to_var.get(&vn).copied()
1638 }
1639 pub fn insert(&mut self, key: NormExpr, value: LcnfLetValue, var: LcnfVarId) -> ValueNumber {
1642 let vn = self.next_vn;
1643 self.next_vn += 1;
1644 self.expr_to_vn.insert(key, vn);
1645 self.vn_to_expr.insert(vn, value);
1646 self.vn_to_var.insert(vn, var);
1647 vn
1648 }
1649 pub fn len(&self) -> usize {
1651 self.next_vn as usize
1652 }
1653 pub fn is_empty(&self) -> bool {
1654 self.next_vn == 0
1655 }
1656 pub fn snapshot(&self) -> Vec<(NormExpr, ValueNumber)> {
1658 self.expr_to_vn
1659 .iter()
1660 .map(|(k, v)| (k.clone(), *v))
1661 .collect()
1662 }
1663 pub fn try_merge(&mut self, other: &ValueTable) -> bool {
1666 for (key, &vn) in &other.expr_to_vn {
1667 if let Some(&existing_vn) = self.expr_to_vn.get(key) {
1668 if existing_vn != vn {
1669 return false;
1670 }
1671 } else {
1672 self.expr_to_vn.insert(key.clone(), vn);
1673 if let Some(expr) = other.vn_to_expr.get(&vn) {
1674 self.vn_to_expr.insert(vn, expr.clone());
1675 }
1676 if let Some(&cvar) = other.vn_to_var.get(&vn) {
1677 self.vn_to_var.insert(vn, cvar);
1678 }
1679 if vn >= self.next_vn {
1680 self.next_vn = vn + 1;
1681 }
1682 }
1683 }
1684 true
1685 }
1686}
1687#[derive(Debug, Default)]
1690pub struct InterproceduralGVN {
1691 pub summaries: HashMap<String, GVNFunctionSummary>,
1693 pub cross_fn_equalities: usize,
1695}
1696impl InterproceduralGVN {
1697 pub fn new() -> Self {
1698 InterproceduralGVN::default()
1699 }
1700 pub fn add_summary(&mut self, name: String, summary: GVNFunctionSummary) {
1702 self.summaries.insert(name, summary);
1703 }
1704 pub fn calls_are_equal(&self, fn_name: &str) -> bool {
1707 self.summaries
1708 .get(fn_name)
1709 .map(|s| s.is_pure_fn)
1710 .unwrap_or(false)
1711 }
1712 pub fn run(&mut self, decls: &mut [LcnfFunDecl]) {
1714 for decl in decls.iter_mut() {
1715 if let Some(summary) = self.summaries.get(&decl.name) {
1716 if summary.is_pure_fn {
1717 self.cross_fn_equalities += 1;
1718 }
1719 }
1720 }
1721 }
1722}
1723#[allow(dead_code)]
1724#[derive(Debug, Clone)]
1725pub struct GVNPassConfig {
1726 pub phase: GVNPassPhase,
1727 pub enabled: bool,
1728 pub max_iterations: u32,
1729 pub debug_output: bool,
1730 pub pass_name: String,
1731}
1732impl GVNPassConfig {
1733 #[allow(dead_code)]
1734 pub fn new(name: impl Into<String>, phase: GVNPassPhase) -> Self {
1735 GVNPassConfig {
1736 phase,
1737 enabled: true,
1738 max_iterations: 10,
1739 debug_output: false,
1740 pass_name: name.into(),
1741 }
1742 }
1743 #[allow(dead_code)]
1744 pub fn disabled(mut self) -> Self {
1745 self.enabled = false;
1746 self
1747 }
1748 #[allow(dead_code)]
1749 pub fn with_debug(mut self) -> Self {
1750 self.debug_output = true;
1751 self
1752 }
1753 #[allow(dead_code)]
1754 pub fn max_iter(mut self, n: u32) -> Self {
1755 self.max_iterations = n;
1756 self
1757 }
1758}
1759#[derive(Debug, Clone)]
1764pub struct PhiNode {
1765 pub var: LcnfVarId,
1767 pub operands: Vec<PhiOperand>,
1769 pub vn: ValueNumber,
1771}
1772impl PhiNode {
1773 pub fn new(var: LcnfVarId, operands: Vec<PhiOperand>, vn: ValueNumber) -> Self {
1774 PhiNode { var, operands, vn }
1775 }
1776 pub fn is_trivial(&self) -> bool {
1778 self.operands.windows(2).all(|w| w[0].vn == w[1].vn)
1779 }
1780 pub fn trivial_vn(&self) -> Option<ValueNumber> {
1782 if self.is_trivial() {
1783 self.operands.first().map(|op| op.vn)
1784 } else {
1785 None
1786 }
1787 }
1788}
1789#[derive(Debug, Clone, PartialEq, Eq)]
1791pub enum KnownConstant {
1792 Lit(LcnfLit),
1794 Top,
1796 Bottom,
1798}
1799#[derive(Debug, Clone)]
1801pub struct Redundancy {
1802 pub redundant_var: LcnfVarId,
1804 pub canonical_var: LcnfVarId,
1806 pub vn: ValueNumber,
1808}
1809#[allow(dead_code)]
1810pub struct GVNPassRegistry {
1811 pub(super) configs: Vec<GVNPassConfig>,
1812 pub(super) stats: std::collections::HashMap<String, GVNPassStats>,
1813}
1814impl GVNPassRegistry {
1815 #[allow(dead_code)]
1816 pub fn new() -> Self {
1817 GVNPassRegistry {
1818 configs: Vec::new(),
1819 stats: std::collections::HashMap::new(),
1820 }
1821 }
1822 #[allow(dead_code)]
1823 pub fn register(&mut self, config: GVNPassConfig) {
1824 self.stats
1825 .insert(config.pass_name.clone(), GVNPassStats::new());
1826 self.configs.push(config);
1827 }
1828 #[allow(dead_code)]
1829 pub fn enabled_passes(&self) -> Vec<&GVNPassConfig> {
1830 self.configs.iter().filter(|c| c.enabled).collect()
1831 }
1832 #[allow(dead_code)]
1833 pub fn get_stats(&self, name: &str) -> Option<&GVNPassStats> {
1834 self.stats.get(name)
1835 }
1836 #[allow(dead_code)]
1837 pub fn total_passes(&self) -> usize {
1838 self.configs.len()
1839 }
1840 #[allow(dead_code)]
1841 pub fn enabled_count(&self) -> usize {
1842 self.enabled_passes().len()
1843 }
1844 #[allow(dead_code)]
1845 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
1846 if let Some(stats) = self.stats.get_mut(name) {
1847 stats.record_run(changes, time_ms, iter);
1848 }
1849 }
1850}
1851#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1857pub enum NormExpr {
1858 Lit(u64),
1859 LitStr(String),
1860 Erased,
1861 FVar(ValueNumber),
1862 Proj(String, u32, ValueNumber),
1863 Ctor(String, u32, Vec<NormArg>),
1864 App(NormArg, Vec<NormArg>),
1865 Reset(ValueNumber),
1866 Unknown,
1867}