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::Int(_)) => NormExpr::Unknown,
1322 LcnfLetValue::Lit(LcnfLit::Str(s)) => NormExpr::LitStr(s.clone()),
1323 LcnfLetValue::Erased => NormExpr::Erased,
1324 LcnfLetValue::FVar(v) => {
1325 let vn = fact.get(v).unwrap_or(v.0 as ValueNumber + 1_000_000);
1326 NormExpr::FVar(vn)
1327 }
1328 LcnfLetValue::Proj(name, idx, v) => {
1329 let vn = fact.get(v).unwrap_or(v.0 as ValueNumber + 1_000_000);
1330 NormExpr::Proj(name.clone(), *idx, vn)
1331 }
1332 LcnfLetValue::Ctor(name, tag, args) => {
1333 let nargs = args.iter().map(|a| self.norm_arg(a, fact)).collect();
1334 NormExpr::Ctor(name.clone(), *tag, nargs)
1335 }
1336 LcnfLetValue::App(f, args) => {
1337 let nf = self.norm_arg(f, fact);
1338 let nargs = args.iter().map(|a| self.norm_arg(a, fact)).collect();
1339 NormExpr::App(nf, nargs)
1340 }
1341 LcnfLetValue::Reset(v) => {
1342 let vn = fact.get(v).unwrap_or(v.0 as ValueNumber + 1_000_000);
1343 NormExpr::Reset(vn)
1344 }
1345 LcnfLetValue::Reuse(..) => NormExpr::Unknown,
1346 }
1347 }
1348 pub(super) fn norm_arg(&self, arg: &LcnfArg, fact: &GVNFact) -> NormArg {
1349 match arg {
1350 LcnfArg::Var(v) => {
1351 let vn = fact.get(v).unwrap_or(v.0 as ValueNumber + 1_000_000);
1352 NormArg::Vn(vn)
1353 }
1354 LcnfArg::Lit(LcnfLit::Nat(n)) => NormArg::LitNat(*n),
1355 LcnfArg::Lit(LcnfLit::Int(_)) => NormArg::Erased,
1356 LcnfArg::Lit(LcnfLit::Str(s)) => NormArg::LitStr(s.clone()),
1357 LcnfArg::Erased => NormArg::Erased,
1358 LcnfArg::Type(_) => NormArg::Erased,
1359 }
1360 }
1361}
1362#[allow(dead_code)]
1363#[derive(Debug, Clone)]
1364pub struct GVNWorklist {
1365 pub(super) items: std::collections::VecDeque<u32>,
1366 pub(super) in_worklist: std::collections::HashSet<u32>,
1367}
1368impl GVNWorklist {
1369 #[allow(dead_code)]
1370 pub fn new() -> Self {
1371 GVNWorklist {
1372 items: std::collections::VecDeque::new(),
1373 in_worklist: std::collections::HashSet::new(),
1374 }
1375 }
1376 #[allow(dead_code)]
1377 pub fn push(&mut self, item: u32) -> bool {
1378 if self.in_worklist.insert(item) {
1379 self.items.push_back(item);
1380 true
1381 } else {
1382 false
1383 }
1384 }
1385 #[allow(dead_code)]
1386 pub fn pop(&mut self) -> Option<u32> {
1387 let item = self.items.pop_front()?;
1388 self.in_worklist.remove(&item);
1389 Some(item)
1390 }
1391 #[allow(dead_code)]
1392 pub fn is_empty(&self) -> bool {
1393 self.items.is_empty()
1394 }
1395 #[allow(dead_code)]
1396 pub fn len(&self) -> usize {
1397 self.items.len()
1398 }
1399 #[allow(dead_code)]
1400 pub fn contains(&self, item: u32) -> bool {
1401 self.in_worklist.contains(&item)
1402 }
1403}
1404#[derive(Debug, Default)]
1410pub struct CongruenceClosure {
1411 pub(super) parent: HashMap<ValueNumber, ValueNumber>,
1413}
1414impl CongruenceClosure {
1415 pub fn new() -> Self {
1416 CongruenceClosure::default()
1417 }
1418 pub fn union(&mut self, a: ValueNumber, b: ValueNumber) {
1420 let ra = self.find(a);
1421 let rb = self.find(b);
1422 if ra != rb {
1423 self.parent.insert(ra, rb);
1424 }
1425 }
1426 pub fn find(&mut self, vn: ValueNumber) -> ValueNumber {
1428 match self.parent.get(&vn).copied() {
1429 None => vn,
1430 Some(p) if p == vn => vn,
1431 Some(p) => {
1432 let root = self.find(p);
1433 self.parent.insert(vn, root);
1434 root
1435 }
1436 }
1437 }
1438 pub fn are_equal(&mut self, a: ValueNumber, b: ValueNumber) -> bool {
1440 self.find(a) == self.find(b)
1441 }
1442 pub fn num_classes(&self) -> usize {
1443 self.parent.len()
1444 }
1445}
1446#[derive(Debug, Clone, Default)]
1448pub struct GVNFact {
1449 pub var_to_vn: HashMap<LcnfVarId, ValueNumber>,
1450}
1451impl GVNFact {
1452 pub fn new() -> Self {
1453 GVNFact::default()
1454 }
1455 pub fn get(&self, var: &LcnfVarId) -> Option<ValueNumber> {
1456 self.var_to_vn.get(var).copied()
1457 }
1458 pub fn insert(&mut self, var: LcnfVarId, vn: ValueNumber) {
1459 self.var_to_vn.insert(var, vn);
1460 }
1461 pub fn meet(&self, other: &GVNFact) -> GVNFact {
1464 let mut result = GVNFact::new();
1465 for (var, &vn) in &self.var_to_vn {
1466 if other.var_to_vn.get(var) == Some(&vn) {
1467 result.var_to_vn.insert(*var, vn);
1468 }
1469 }
1470 result
1471 }
1472}
1473#[derive(Debug, Default)]
1479pub struct LeaderFinder {
1480 pub(super) leaders: HashMap<ValueNumber, LcnfVarId>,
1482 pub(super) members: HashMap<ValueNumber, Vec<LcnfVarId>>,
1484}
1485impl LeaderFinder {
1486 pub fn new() -> Self {
1487 LeaderFinder::default()
1488 }
1489 pub fn record(&mut self, vn: ValueNumber, var: LcnfVarId) {
1491 self.members.entry(vn).or_default().push(var);
1492 self.leaders.entry(vn).or_insert(var);
1493 }
1494 pub fn leader(&self, vn: ValueNumber) -> Option<LcnfVarId> {
1496 self.leaders.get(&vn).copied()
1497 }
1498 pub fn members(&self, vn: ValueNumber) -> &[LcnfVarId] {
1500 self.members.get(&vn).map(|v| v.as_slice()).unwrap_or(&[])
1501 }
1502 pub fn num_redundancies(&self) -> usize {
1504 self.members.values().filter(|v| v.len() > 1).count()
1505 }
1506}
1507pub struct GVNPipeline {
1509 pub do_base_gvn: bool,
1511 pub do_load_elim: bool,
1513 pub do_pre: bool,
1515 pub do_ccp: bool,
1517 pub do_fixpoint: bool,
1519 pub max_fixpoint_iter: usize,
1521 pub stats: GVNStatistics,
1523}
1524impl GVNPipeline {
1525 pub fn new() -> Self {
1526 GVNPipeline::default()
1527 }
1528 pub fn run(&mut self, decls: &mut [LcnfFunDecl]) {
1530 for decl in decls.iter_mut() {
1531 if self.do_base_gvn {
1532 let mut pass = GVNPass::default();
1533 pass.run(std::slice::from_mut(decl));
1534 let r = pass.report();
1535 self.stats.total_vns += r.expressions_numbered;
1536 self.stats.phi_translations += r.phi_translations;
1537 }
1538 if self.do_load_elim {
1539 let mut le = LoadEliminatorGVN::new();
1540 le.run(decl);
1541 self.stats.proj_redundancies += le.eliminated;
1542 }
1543 if self.do_pre {
1544 let mut pre = GVNPrePass::new();
1545 pre.run(decl);
1546 }
1547 if self.do_ccp {
1548 let mut ccp = CCPState::new();
1549 ccp.run(decl);
1550 self.stats.lit_redundancies += ccp.folded;
1551 }
1552 if self.do_fixpoint {
1553 let mut fp = FixpointGVN::new(self.max_fixpoint_iter);
1554 fp.run(decl);
1555 self.stats.total_vns += fp.total_redundancies;
1556 }
1557 }
1558 }
1559 pub fn total_redundancies(&self) -> usize {
1560 self.stats.total_redundancies()
1561 }
1562}
1563#[allow(dead_code)]
1564#[derive(Debug, Clone, PartialEq)]
1565pub enum GVNPassPhase {
1566 Analysis,
1567 Transformation,
1568 Verification,
1569 Cleanup,
1570}
1571impl GVNPassPhase {
1572 #[allow(dead_code)]
1573 pub fn name(&self) -> &str {
1574 match self {
1575 GVNPassPhase::Analysis => "analysis",
1576 GVNPassPhase::Transformation => "transformation",
1577 GVNPassPhase::Verification => "verification",
1578 GVNPassPhase::Cleanup => "cleanup",
1579 }
1580 }
1581 #[allow(dead_code)]
1582 pub fn is_modifying(&self) -> bool {
1583 matches!(self, GVNPassPhase::Transformation | GVNPassPhase::Cleanup)
1584 }
1585}
1586#[allow(dead_code)]
1587#[derive(Debug, Clone)]
1588pub struct GVNCacheEntry {
1589 pub key: String,
1590 pub data: Vec<u8>,
1591 pub timestamp: u64,
1592 pub valid: bool,
1593}
1594#[derive(Debug, Clone)]
1596pub struct FixpointState {
1597 pub table: ValueTable,
1599 pub exit_fact: GVNFact,
1601 pub redundancies: usize,
1603}
1604impl FixpointState {
1605 pub fn new() -> Self {
1606 FixpointState {
1607 table: ValueTable::new(),
1608 exit_fact: GVNFact::new(),
1609 redundancies: 0,
1610 }
1611 }
1612}
1613#[derive(Debug, Clone, Default)]
1619pub struct ValueTable {
1620 pub(super) expr_to_vn: HashMap<NormExpr, ValueNumber>,
1622 pub(super) vn_to_expr: HashMap<ValueNumber, LcnfLetValue>,
1624 pub(super) vn_to_var: HashMap<ValueNumber, LcnfVarId>,
1626 pub(super) next_vn: ValueNumber,
1628}
1629impl ValueTable {
1630 pub fn new() -> Self {
1631 ValueTable::default()
1632 }
1633 pub fn lookup(&self, key: &NormExpr) -> Option<ValueNumber> {
1635 self.expr_to_vn.get(key).copied()
1636 }
1637 pub fn canonical_var(&self, vn: ValueNumber) -> Option<LcnfVarId> {
1639 self.vn_to_var.get(&vn).copied()
1640 }
1641 pub fn insert(&mut self, key: NormExpr, value: LcnfLetValue, var: LcnfVarId) -> ValueNumber {
1644 let vn = self.next_vn;
1645 self.next_vn += 1;
1646 self.expr_to_vn.insert(key, vn);
1647 self.vn_to_expr.insert(vn, value);
1648 self.vn_to_var.insert(vn, var);
1649 vn
1650 }
1651 pub fn len(&self) -> usize {
1653 self.next_vn as usize
1654 }
1655 pub fn is_empty(&self) -> bool {
1656 self.next_vn == 0
1657 }
1658 pub fn snapshot(&self) -> Vec<(NormExpr, ValueNumber)> {
1660 self.expr_to_vn
1661 .iter()
1662 .map(|(k, v)| (k.clone(), *v))
1663 .collect()
1664 }
1665 pub fn try_merge(&mut self, other: &ValueTable) -> bool {
1668 for (key, &vn) in &other.expr_to_vn {
1669 if let Some(&existing_vn) = self.expr_to_vn.get(key) {
1670 if existing_vn != vn {
1671 return false;
1672 }
1673 } else {
1674 self.expr_to_vn.insert(key.clone(), vn);
1675 if let Some(expr) = other.vn_to_expr.get(&vn) {
1676 self.vn_to_expr.insert(vn, expr.clone());
1677 }
1678 if let Some(&cvar) = other.vn_to_var.get(&vn) {
1679 self.vn_to_var.insert(vn, cvar);
1680 }
1681 if vn >= self.next_vn {
1682 self.next_vn = vn + 1;
1683 }
1684 }
1685 }
1686 true
1687 }
1688}
1689#[derive(Debug, Default)]
1692pub struct InterproceduralGVN {
1693 pub summaries: HashMap<String, GVNFunctionSummary>,
1695 pub cross_fn_equalities: usize,
1697}
1698impl InterproceduralGVN {
1699 pub fn new() -> Self {
1700 InterproceduralGVN::default()
1701 }
1702 pub fn add_summary(&mut self, name: String, summary: GVNFunctionSummary) {
1704 self.summaries.insert(name, summary);
1705 }
1706 pub fn calls_are_equal(&self, fn_name: &str) -> bool {
1709 self.summaries
1710 .get(fn_name)
1711 .map(|s| s.is_pure_fn)
1712 .unwrap_or(false)
1713 }
1714 pub fn run(&mut self, decls: &mut [LcnfFunDecl]) {
1716 for decl in decls.iter_mut() {
1717 if let Some(summary) = self.summaries.get(&decl.name) {
1718 if summary.is_pure_fn {
1719 self.cross_fn_equalities += 1;
1720 }
1721 }
1722 }
1723 }
1724}
1725#[allow(dead_code)]
1726#[derive(Debug, Clone)]
1727pub struct GVNPassConfig {
1728 pub phase: GVNPassPhase,
1729 pub enabled: bool,
1730 pub max_iterations: u32,
1731 pub debug_output: bool,
1732 pub pass_name: String,
1733}
1734impl GVNPassConfig {
1735 #[allow(dead_code)]
1736 pub fn new(name: impl Into<String>, phase: GVNPassPhase) -> Self {
1737 GVNPassConfig {
1738 phase,
1739 enabled: true,
1740 max_iterations: 10,
1741 debug_output: false,
1742 pass_name: name.into(),
1743 }
1744 }
1745 #[allow(dead_code)]
1746 pub fn disabled(mut self) -> Self {
1747 self.enabled = false;
1748 self
1749 }
1750 #[allow(dead_code)]
1751 pub fn with_debug(mut self) -> Self {
1752 self.debug_output = true;
1753 self
1754 }
1755 #[allow(dead_code)]
1756 pub fn max_iter(mut self, n: u32) -> Self {
1757 self.max_iterations = n;
1758 self
1759 }
1760}
1761#[derive(Debug, Clone)]
1766pub struct PhiNode {
1767 pub var: LcnfVarId,
1769 pub operands: Vec<PhiOperand>,
1771 pub vn: ValueNumber,
1773}
1774impl PhiNode {
1775 pub fn new(var: LcnfVarId, operands: Vec<PhiOperand>, vn: ValueNumber) -> Self {
1776 PhiNode { var, operands, vn }
1777 }
1778 pub fn is_trivial(&self) -> bool {
1780 self.operands.windows(2).all(|w| w[0].vn == w[1].vn)
1781 }
1782 pub fn trivial_vn(&self) -> Option<ValueNumber> {
1784 if self.is_trivial() {
1785 self.operands.first().map(|op| op.vn)
1786 } else {
1787 None
1788 }
1789 }
1790}
1791#[derive(Debug, Clone, PartialEq, Eq)]
1793pub enum KnownConstant {
1794 Lit(LcnfLit),
1796 Top,
1798 Bottom,
1800}
1801#[derive(Debug, Clone)]
1803pub struct Redundancy {
1804 pub redundant_var: LcnfVarId,
1806 pub canonical_var: LcnfVarId,
1808 pub vn: ValueNumber,
1810}
1811#[allow(dead_code)]
1812pub struct GVNPassRegistry {
1813 pub(super) configs: Vec<GVNPassConfig>,
1814 pub(super) stats: std::collections::HashMap<String, GVNPassStats>,
1815}
1816impl GVNPassRegistry {
1817 #[allow(dead_code)]
1818 pub fn new() -> Self {
1819 GVNPassRegistry {
1820 configs: Vec::new(),
1821 stats: std::collections::HashMap::new(),
1822 }
1823 }
1824 #[allow(dead_code)]
1825 pub fn register(&mut self, config: GVNPassConfig) {
1826 self.stats
1827 .insert(config.pass_name.clone(), GVNPassStats::new());
1828 self.configs.push(config);
1829 }
1830 #[allow(dead_code)]
1831 pub fn enabled_passes(&self) -> Vec<&GVNPassConfig> {
1832 self.configs.iter().filter(|c| c.enabled).collect()
1833 }
1834 #[allow(dead_code)]
1835 pub fn get_stats(&self, name: &str) -> Option<&GVNPassStats> {
1836 self.stats.get(name)
1837 }
1838 #[allow(dead_code)]
1839 pub fn total_passes(&self) -> usize {
1840 self.configs.len()
1841 }
1842 #[allow(dead_code)]
1843 pub fn enabled_count(&self) -> usize {
1844 self.enabled_passes().len()
1845 }
1846 #[allow(dead_code)]
1847 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
1848 if let Some(stats) = self.stats.get_mut(name) {
1849 stats.record_run(changes, time_ms, iter);
1850 }
1851 }
1852}
1853#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1859pub enum NormExpr {
1860 Lit(u64),
1861 LitStr(String),
1862 Erased,
1863 FVar(ValueNumber),
1864 Proj(String, u32, ValueNumber),
1865 Ctor(String, u32, Vec<NormArg>),
1866 App(NormArg, Vec<NormArg>),
1867 Reset(ValueNumber),
1868 Unknown,
1869}