1use crate::lcnf::*;
6use std::collections::HashMap;
7
8use super::functions::*;
9use std::collections::{HashSet, VecDeque};
10
11#[allow(dead_code)]
13#[derive(Debug, Default)]
14pub struct CSEX2PassRegistry {
15 pub(super) configs: Vec<CSEX2PassConfig>,
16 pub(super) stats: Vec<CSEX2PassStats>,
17}
18impl CSEX2PassRegistry {
19 #[allow(dead_code)]
20 pub fn new() -> Self {
21 Self::default()
22 }
23 #[allow(dead_code)]
24 pub fn register(&mut self, c: CSEX2PassConfig) {
25 self.stats.push(CSEX2PassStats::new());
26 self.configs.push(c);
27 }
28 #[allow(dead_code)]
29 pub fn len(&self) -> usize {
30 self.configs.len()
31 }
32 #[allow(dead_code)]
33 pub fn is_empty(&self) -> bool {
34 self.configs.is_empty()
35 }
36 #[allow(dead_code)]
37 pub fn get(&self, i: usize) -> Option<&CSEX2PassConfig> {
38 self.configs.get(i)
39 }
40 #[allow(dead_code)]
41 pub fn get_stats(&self, i: usize) -> Option<&CSEX2PassStats> {
42 self.stats.get(i)
43 }
44 #[allow(dead_code)]
45 pub fn enabled_passes(&self) -> Vec<&CSEX2PassConfig> {
46 self.configs.iter().filter(|c| c.enabled).collect()
47 }
48 #[allow(dead_code)]
49 pub fn passes_in_phase(&self, ph: &CSEX2PassPhase) -> Vec<&CSEX2PassConfig> {
50 self.configs
51 .iter()
52 .filter(|c| c.enabled && &c.phase == ph)
53 .collect()
54 }
55 #[allow(dead_code)]
56 pub fn total_nodes_visited(&self) -> usize {
57 self.stats.iter().map(|s| s.nodes_visited).sum()
58 }
59 #[allow(dead_code)]
60 pub fn any_changed(&self) -> bool {
61 self.stats.iter().any(|s| s.changed)
62 }
63}
64#[allow(dead_code)]
66#[derive(Debug, Clone)]
67pub struct CSEExtPassConfig {
68 pub name: String,
69 pub phase: CSEExtPassPhase,
70 pub enabled: bool,
71 pub max_iterations: usize,
72 pub debug: u32,
73 pub timeout_ms: Option<u64>,
74}
75impl CSEExtPassConfig {
76 #[allow(dead_code)]
77 pub fn new(name: impl Into<String>) -> Self {
78 Self {
79 name: name.into(),
80 phase: CSEExtPassPhase::Middle,
81 enabled: true,
82 max_iterations: 100,
83 debug: 0,
84 timeout_ms: None,
85 }
86 }
87 #[allow(dead_code)]
88 pub fn with_phase(mut self, phase: CSEExtPassPhase) -> Self {
89 self.phase = phase;
90 self
91 }
92 #[allow(dead_code)]
93 pub fn with_max_iter(mut self, n: usize) -> Self {
94 self.max_iterations = n;
95 self
96 }
97 #[allow(dead_code)]
98 pub fn with_debug(mut self, d: u32) -> Self {
99 self.debug = d;
100 self
101 }
102 #[allow(dead_code)]
103 pub fn disabled(mut self) -> Self {
104 self.enabled = false;
105 self
106 }
107 #[allow(dead_code)]
108 pub fn with_timeout(mut self, ms: u64) -> Self {
109 self.timeout_ms = Some(ms);
110 self
111 }
112 #[allow(dead_code)]
113 pub fn is_debug_enabled(&self) -> bool {
114 self.debug > 0
115 }
116}
117#[derive(Clone, Debug)]
123pub struct AvailableExpr {
124 pub key: ExprKey,
126 pub var_id: LcnfVarId,
128 pub name_hint: String,
130 pub depth: usize,
132}
133#[derive(Clone, Debug)]
135pub struct CseConfig {
136 pub max_expr_size: usize,
140 pub track_calls: bool,
143 pub max_candidates: usize,
145 pub pure_functions: Vec<String>,
148}
149#[allow(dead_code)]
151#[derive(Debug, Clone, PartialEq, Eq, Hash)]
152pub enum CSEX2PassPhase {
153 Early,
154 Middle,
155 Late,
156 Finalize,
157}
158impl CSEX2PassPhase {
159 #[allow(dead_code)]
160 pub fn is_early(&self) -> bool {
161 matches!(self, Self::Early)
162 }
163 #[allow(dead_code)]
164 pub fn is_middle(&self) -> bool {
165 matches!(self, Self::Middle)
166 }
167 #[allow(dead_code)]
168 pub fn is_late(&self) -> bool {
169 matches!(self, Self::Late)
170 }
171 #[allow(dead_code)]
172 pub fn is_finalize(&self) -> bool {
173 matches!(self, Self::Finalize)
174 }
175 #[allow(dead_code)]
176 pub fn order(&self) -> u32 {
177 match self {
178 Self::Early => 0,
179 Self::Middle => 1,
180 Self::Late => 2,
181 Self::Finalize => 3,
182 }
183 }
184 #[allow(dead_code)]
185 pub fn from_order(n: u32) -> Option<Self> {
186 match n {
187 0 => Some(Self::Early),
188 1 => Some(Self::Middle),
189 2 => Some(Self::Late),
190 3 => Some(Self::Finalize),
191 _ => None,
192 }
193 }
194}
195#[allow(dead_code)]
197#[derive(Debug, Clone)]
198pub struct CSEX2DomTree {
199 pub(super) idom: Vec<Option<usize>>,
200 pub(super) children: Vec<Vec<usize>>,
201 pub(super) depth: Vec<usize>,
202}
203impl CSEX2DomTree {
204 #[allow(dead_code)]
205 pub fn new(n: usize) -> Self {
206 Self {
207 idom: vec![None; n],
208 children: vec![Vec::new(); n],
209 depth: vec![0; n],
210 }
211 }
212 #[allow(dead_code)]
213 pub fn set_idom(&mut self, node: usize, dom: usize) {
214 if node < self.idom.len() {
215 self.idom[node] = Some(dom);
216 if dom < self.children.len() {
217 self.children[dom].push(node);
218 }
219 self.depth[node] = if dom < self.depth.len() {
220 self.depth[dom] + 1
221 } else {
222 1
223 };
224 }
225 }
226 #[allow(dead_code)]
227 pub fn dominates(&self, a: usize, mut b: usize) -> bool {
228 if a == b {
229 return true;
230 }
231 let n = self.idom.len();
232 for _ in 0..n {
233 match self.idom.get(b).copied().flatten() {
234 None => return false,
235 Some(p) if p == a => return true,
236 Some(p) if p == b => return false,
237 Some(p) => b = p,
238 }
239 }
240 false
241 }
242 #[allow(dead_code)]
243 pub fn children_of(&self, n: usize) -> &[usize] {
244 self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
245 }
246 #[allow(dead_code)]
247 pub fn depth_of(&self, n: usize) -> usize {
248 self.depth.get(n).copied().unwrap_or(0)
249 }
250 #[allow(dead_code)]
251 pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
252 let n = self.idom.len();
253 for _ in 0..(2 * n) {
254 if a == b {
255 return a;
256 }
257 if self.depth_of(a) > self.depth_of(b) {
258 a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
259 } else {
260 b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
261 }
262 }
263 0
264 }
265}
266#[allow(dead_code)]
267#[derive(Debug, Clone)]
268pub struct CSEWorklist {
269 pub(super) items: std::collections::VecDeque<u32>,
270 pub(super) in_worklist: std::collections::HashSet<u32>,
271}
272impl CSEWorklist {
273 #[allow(dead_code)]
274 pub fn new() -> Self {
275 CSEWorklist {
276 items: std::collections::VecDeque::new(),
277 in_worklist: std::collections::HashSet::new(),
278 }
279 }
280 #[allow(dead_code)]
281 pub fn push(&mut self, item: u32) -> bool {
282 if self.in_worklist.insert(item) {
283 self.items.push_back(item);
284 true
285 } else {
286 false
287 }
288 }
289 #[allow(dead_code)]
290 pub fn pop(&mut self) -> Option<u32> {
291 let item = self.items.pop_front()?;
292 self.in_worklist.remove(&item);
293 Some(item)
294 }
295 #[allow(dead_code)]
296 pub fn is_empty(&self) -> bool {
297 self.items.is_empty()
298 }
299 #[allow(dead_code)]
300 pub fn len(&self) -> usize {
301 self.items.len()
302 }
303 #[allow(dead_code)]
304 pub fn contains(&self, item: u32) -> bool {
305 self.in_worklist.contains(&item)
306 }
307}
308#[allow(dead_code)]
310#[derive(Debug, Clone)]
311pub struct CSEExtDepGraph {
312 pub(super) n: usize,
313 pub(super) adj: Vec<Vec<usize>>,
314 pub(super) rev: Vec<Vec<usize>>,
315 pub(super) edge_count: usize,
316}
317impl CSEExtDepGraph {
318 #[allow(dead_code)]
319 pub fn new(n: usize) -> Self {
320 Self {
321 n,
322 adj: vec![Vec::new(); n],
323 rev: vec![Vec::new(); n],
324 edge_count: 0,
325 }
326 }
327 #[allow(dead_code)]
328 pub fn add_edge(&mut self, from: usize, to: usize) {
329 if from < self.n && to < self.n {
330 if !self.adj[from].contains(&to) {
331 self.adj[from].push(to);
332 self.rev[to].push(from);
333 self.edge_count += 1;
334 }
335 }
336 }
337 #[allow(dead_code)]
338 pub fn succs(&self, n: usize) -> &[usize] {
339 self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
340 }
341 #[allow(dead_code)]
342 pub fn preds(&self, n: usize) -> &[usize] {
343 self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
344 }
345 #[allow(dead_code)]
346 pub fn topo_sort(&self) -> Option<Vec<usize>> {
347 let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
348 let mut q: std::collections::VecDeque<usize> =
349 (0..self.n).filter(|&i| deg[i] == 0).collect();
350 let mut out = Vec::with_capacity(self.n);
351 while let Some(u) = q.pop_front() {
352 out.push(u);
353 for &v in &self.adj[u] {
354 deg[v] -= 1;
355 if deg[v] == 0 {
356 q.push_back(v);
357 }
358 }
359 }
360 if out.len() == self.n {
361 Some(out)
362 } else {
363 None
364 }
365 }
366 #[allow(dead_code)]
367 pub fn has_cycle(&self) -> bool {
368 self.topo_sort().is_none()
369 }
370 #[allow(dead_code)]
371 pub fn reachable(&self, start: usize) -> Vec<usize> {
372 let mut vis = vec![false; self.n];
373 let mut stk = vec![start];
374 let mut out = Vec::new();
375 while let Some(u) = stk.pop() {
376 if u < self.n && !vis[u] {
377 vis[u] = true;
378 out.push(u);
379 for &v in &self.adj[u] {
380 if !vis[v] {
381 stk.push(v);
382 }
383 }
384 }
385 }
386 out
387 }
388 #[allow(dead_code)]
389 pub fn scc(&self) -> Vec<Vec<usize>> {
390 let mut visited = vec![false; self.n];
391 let mut order = Vec::new();
392 for i in 0..self.n {
393 if !visited[i] {
394 let mut stk = vec![(i, 0usize)];
395 while let Some((u, idx)) = stk.last_mut() {
396 if !visited[*u] {
397 visited[*u] = true;
398 }
399 if *idx < self.adj[*u].len() {
400 let v = self.adj[*u][*idx];
401 *idx += 1;
402 if !visited[v] {
403 stk.push((v, 0));
404 }
405 } else {
406 order.push(*u);
407 stk.pop();
408 }
409 }
410 }
411 }
412 let mut comp = vec![usize::MAX; self.n];
413 let mut components: Vec<Vec<usize>> = Vec::new();
414 for &start in order.iter().rev() {
415 if comp[start] == usize::MAX {
416 let cid = components.len();
417 let mut component = Vec::new();
418 let mut stk = vec![start];
419 while let Some(u) = stk.pop() {
420 if comp[u] == usize::MAX {
421 comp[u] = cid;
422 component.push(u);
423 for &v in &self.rev[u] {
424 if comp[v] == usize::MAX {
425 stk.push(v);
426 }
427 }
428 }
429 }
430 components.push(component);
431 }
432 }
433 components
434 }
435 #[allow(dead_code)]
436 pub fn node_count(&self) -> usize {
437 self.n
438 }
439 #[allow(dead_code)]
440 pub fn edge_count(&self) -> usize {
441 self.edge_count
442 }
443}
444pub struct CSEPass {
453 pub(super) config: CseConfig,
454 pub(super) report: CseReport,
455}
456impl CSEPass {
457 pub fn new(config: CseConfig) -> Self {
459 CSEPass {
460 config,
461 report: CseReport::default(),
462 }
463 }
464 pub fn run(&mut self, decls: &mut [LcnfFunDecl]) {
466 for decl in decls.iter_mut() {
467 let new_body = self.global_cse_expr(&decl.body.clone(), &mut AvailableSet::new(), 0);
468 decl.body = new_body;
469 }
470 }
471 pub fn global_cse(&mut self, decl: &LcnfFunDecl) -> LcnfFunDecl {
473 let mut avail = AvailableSet::new();
474 let new_body = self.global_cse_expr(&decl.body, &mut avail, 0);
475 LcnfFunDecl {
476 name: decl.name.clone(),
477 original_name: decl.original_name.clone(),
478 params: decl.params.clone(),
479 ret_type: decl.ret_type.clone(),
480 body: new_body,
481 is_recursive: decl.is_recursive,
482 is_lifted: decl.is_lifted,
483 inline_cost: decl.inline_cost,
484 }
485 }
486 pub(super) fn global_cse_expr(
488 &mut self,
489 expr: &LcnfExpr,
490 avail: &mut AvailableSet,
491 depth: usize,
492 ) -> LcnfExpr {
493 match expr {
494 LcnfExpr::Let {
495 id,
496 name,
497 ty,
498 value,
499 body,
500 } => {
501 if let Some(key) = let_value_to_key(value, &self.config.pure_functions) {
502 if avail.len() < self.config.max_candidates {
503 if let Some(existing) = avail.find(&key) {
504 self.report.expressions_found += 1;
505 self.report.expressions_eliminated += 1;
506 let new_value = LcnfLetValue::FVar(existing);
507 let new_body = self.global_cse_expr(body, avail, depth + 1);
508 return LcnfExpr::Let {
509 id: *id,
510 name: name.clone(),
511 ty: ty.clone(),
512 value: new_value,
513 body: Box::new(new_body),
514 };
515 }
516 avail.insert(key, *id, name.clone(), depth);
517 }
518 }
519 let new_body = self.global_cse_expr(body, avail, depth + 1);
520 LcnfExpr::Let {
521 id: *id,
522 name: name.clone(),
523 ty: ty.clone(),
524 value: value.clone(),
525 body: Box::new(new_body),
526 }
527 }
528 LcnfExpr::Case {
529 scrutinee,
530 scrutinee_ty,
531 alts,
532 default,
533 } => {
534 let new_alts = alts
535 .iter()
536 .map(|alt| {
537 let mut branch_avail = avail.clone();
538 let new_body =
539 self.global_cse_expr(&alt.body, &mut branch_avail, depth + 1);
540 LcnfAlt {
541 ctor_name: alt.ctor_name.clone(),
542 ctor_tag: alt.ctor_tag,
543 params: alt.params.clone(),
544 body: new_body,
545 }
546 })
547 .collect();
548 let new_default = default.as_ref().map(|def| {
549 let mut branch_avail = avail.clone();
550 Box::new(self.global_cse_expr(def, &mut branch_avail, depth + 1))
551 });
552 LcnfExpr::Case {
553 scrutinee: *scrutinee,
554 scrutinee_ty: scrutinee_ty.clone(),
555 alts: new_alts,
556 default: new_default,
557 }
558 }
559 LcnfExpr::Return(_) | LcnfExpr::Unreachable | LcnfExpr::TailCall(_, _) => expr.clone(),
560 }
561 }
562 pub fn local_cse(&mut self, expr: &LcnfExpr) -> LcnfExpr {
564 let mut avail = AvailableSet::new();
565 self.global_cse_expr(expr, &mut avail, 0)
566 }
567 pub fn hash_expr(&self, value: &LcnfLetValue) -> Option<ExprKey> {
569 let mut key = let_value_to_key(value, &self.config.pure_functions)?;
570 if let ExprKey::App(LcnfArg::Lit(LcnfLit::Str(fname)), ref args) = key.clone() {
571 if self.is_commutative(&fname) && args.len() == 2 {
572 let mut sorted = args.clone();
573 sorted.sort_by(|a, b| format!("{:?}", a).cmp(&format!("{:?}", b)));
574 key = ExprKey::CommApp(fname.clone(), sorted);
575 }
576 }
577 Some(key)
578 }
579 pub(super) fn is_commutative(&self, name: &str) -> bool {
581 matches!(name, "add" | "mul" | "and" | "or" | "xor" | "min" | "max")
582 }
583 pub fn find_available(&self, avail: &AvailableSet, value: &LcnfLetValue) -> Option<LcnfVarId> {
585 let key = let_value_to_key(value, &self.config.pure_functions)?;
586 avail.find(&key)
587 }
588 pub fn report(&self) -> CseReport {
590 self.report.clone()
591 }
592}
593#[allow(dead_code)]
595#[derive(Debug, Clone)]
596pub struct CSEX2Worklist {
597 pub(super) items: std::collections::VecDeque<usize>,
598 pub(super) present: Vec<bool>,
599}
600impl CSEX2Worklist {
601 #[allow(dead_code)]
602 pub fn new(capacity: usize) -> Self {
603 Self {
604 items: std::collections::VecDeque::new(),
605 present: vec![false; capacity],
606 }
607 }
608 #[allow(dead_code)]
609 pub fn push(&mut self, id: usize) {
610 if id < self.present.len() && !self.present[id] {
611 self.present[id] = true;
612 self.items.push_back(id);
613 }
614 }
615 #[allow(dead_code)]
616 pub fn push_front(&mut self, id: usize) {
617 if id < self.present.len() && !self.present[id] {
618 self.present[id] = true;
619 self.items.push_front(id);
620 }
621 }
622 #[allow(dead_code)]
623 pub fn pop(&mut self) -> Option<usize> {
624 let id = self.items.pop_front()?;
625 if id < self.present.len() {
626 self.present[id] = false;
627 }
628 Some(id)
629 }
630 #[allow(dead_code)]
631 pub fn is_empty(&self) -> bool {
632 self.items.is_empty()
633 }
634 #[allow(dead_code)]
635 pub fn len(&self) -> usize {
636 self.items.len()
637 }
638 #[allow(dead_code)]
639 pub fn contains(&self, id: usize) -> bool {
640 id < self.present.len() && self.present[id]
641 }
642 #[allow(dead_code)]
643 pub fn drain_all(&mut self) -> Vec<usize> {
644 let v: Vec<usize> = self.items.drain(..).collect();
645 for &id in &v {
646 if id < self.present.len() {
647 self.present[id] = false;
648 }
649 }
650 v
651 }
652}
653#[allow(dead_code)]
655#[derive(Debug, Clone, PartialEq, Eq, Hash)]
656pub enum CSEExtPassPhase {
657 Early,
658 Middle,
659 Late,
660 Finalize,
661}
662impl CSEExtPassPhase {
663 #[allow(dead_code)]
664 pub fn is_early(&self) -> bool {
665 matches!(self, Self::Early)
666 }
667 #[allow(dead_code)]
668 pub fn is_middle(&self) -> bool {
669 matches!(self, Self::Middle)
670 }
671 #[allow(dead_code)]
672 pub fn is_late(&self) -> bool {
673 matches!(self, Self::Late)
674 }
675 #[allow(dead_code)]
676 pub fn is_finalize(&self) -> bool {
677 matches!(self, Self::Finalize)
678 }
679 #[allow(dead_code)]
680 pub fn order(&self) -> u32 {
681 match self {
682 Self::Early => 0,
683 Self::Middle => 1,
684 Self::Late => 2,
685 Self::Finalize => 3,
686 }
687 }
688 #[allow(dead_code)]
689 pub fn from_order(n: u32) -> Option<Self> {
690 match n {
691 0 => Some(Self::Early),
692 1 => Some(Self::Middle),
693 2 => Some(Self::Late),
694 3 => Some(Self::Finalize),
695 _ => None,
696 }
697 }
698}
699#[allow(dead_code)]
700#[derive(Debug, Clone)]
701pub struct CSEAnalysisCache {
702 pub(super) entries: std::collections::HashMap<String, CSECacheEntry>,
703 pub(super) max_size: usize,
704 pub(super) hits: u64,
705 pub(super) misses: u64,
706}
707impl CSEAnalysisCache {
708 #[allow(dead_code)]
709 pub fn new(max_size: usize) -> Self {
710 CSEAnalysisCache {
711 entries: std::collections::HashMap::new(),
712 max_size,
713 hits: 0,
714 misses: 0,
715 }
716 }
717 #[allow(dead_code)]
718 pub fn get(&mut self, key: &str) -> Option<&CSECacheEntry> {
719 if self.entries.contains_key(key) {
720 self.hits += 1;
721 self.entries.get(key)
722 } else {
723 self.misses += 1;
724 None
725 }
726 }
727 #[allow(dead_code)]
728 pub fn insert(&mut self, key: String, data: Vec<u8>) {
729 if self.entries.len() >= self.max_size {
730 if let Some(oldest) = self.entries.keys().next().cloned() {
731 self.entries.remove(&oldest);
732 }
733 }
734 self.entries.insert(
735 key.clone(),
736 CSECacheEntry {
737 key,
738 data,
739 timestamp: 0,
740 valid: true,
741 },
742 );
743 }
744 #[allow(dead_code)]
745 pub fn invalidate(&mut self, key: &str) {
746 if let Some(entry) = self.entries.get_mut(key) {
747 entry.valid = false;
748 }
749 }
750 #[allow(dead_code)]
751 pub fn clear(&mut self) {
752 self.entries.clear();
753 }
754 #[allow(dead_code)]
755 pub fn hit_rate(&self) -> f64 {
756 let total = self.hits + self.misses;
757 if total == 0 {
758 return 0.0;
759 }
760 self.hits as f64 / total as f64
761 }
762 #[allow(dead_code)]
763 pub fn size(&self) -> usize {
764 self.entries.len()
765 }
766}
767#[allow(dead_code)]
769#[derive(Debug, Clone, Default)]
770pub struct CSEExtPassStats {
771 pub iterations: usize,
772 pub changed: bool,
773 pub nodes_visited: usize,
774 pub nodes_modified: usize,
775 pub time_ms: u64,
776 pub memory_bytes: usize,
777 pub errors: usize,
778}
779impl CSEExtPassStats {
780 #[allow(dead_code)]
781 pub fn new() -> Self {
782 Self::default()
783 }
784 #[allow(dead_code)]
785 pub fn visit(&mut self) {
786 self.nodes_visited += 1;
787 }
788 #[allow(dead_code)]
789 pub fn modify(&mut self) {
790 self.nodes_modified += 1;
791 self.changed = true;
792 }
793 #[allow(dead_code)]
794 pub fn iterate(&mut self) {
795 self.iterations += 1;
796 }
797 #[allow(dead_code)]
798 pub fn error(&mut self) {
799 self.errors += 1;
800 }
801 #[allow(dead_code)]
802 pub fn efficiency(&self) -> f64 {
803 if self.nodes_visited == 0 {
804 0.0
805 } else {
806 self.nodes_modified as f64 / self.nodes_visited as f64
807 }
808 }
809 #[allow(dead_code)]
810 pub fn merge(&mut self, o: &CSEExtPassStats) {
811 self.iterations += o.iterations;
812 self.changed |= o.changed;
813 self.nodes_visited += o.nodes_visited;
814 self.nodes_modified += o.nodes_modified;
815 self.time_ms += o.time_ms;
816 self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
817 self.errors += o.errors;
818 }
819}
820#[allow(dead_code)]
821#[derive(Debug, Clone, Default)]
822pub struct CSEPassStats {
823 pub total_runs: u32,
824 pub successful_runs: u32,
825 pub total_changes: u64,
826 pub time_ms: u64,
827 pub iterations_used: u32,
828}
829impl CSEPassStats {
830 #[allow(dead_code)]
831 pub fn new() -> Self {
832 Self::default()
833 }
834 #[allow(dead_code)]
835 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
836 self.total_runs += 1;
837 self.successful_runs += 1;
838 self.total_changes += changes;
839 self.time_ms += time_ms;
840 self.iterations_used = iterations;
841 }
842 #[allow(dead_code)]
843 pub fn average_changes_per_run(&self) -> f64 {
844 if self.total_runs == 0 {
845 return 0.0;
846 }
847 self.total_changes as f64 / self.total_runs as f64
848 }
849 #[allow(dead_code)]
850 pub fn success_rate(&self) -> f64 {
851 if self.total_runs == 0 {
852 return 0.0;
853 }
854 self.successful_runs as f64 / self.total_runs as f64
855 }
856 #[allow(dead_code)]
857 pub fn format_summary(&self) -> String {
858 format!(
859 "Runs: {}/{}, Changes: {}, Time: {}ms",
860 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
861 )
862 }
863}
864#[allow(dead_code)]
865#[derive(Debug, Clone)]
866pub struct CSELivenessInfo {
867 pub live_in: Vec<std::collections::HashSet<u32>>,
868 pub live_out: Vec<std::collections::HashSet<u32>>,
869 pub defs: Vec<std::collections::HashSet<u32>>,
870 pub uses: Vec<std::collections::HashSet<u32>>,
871}
872impl CSELivenessInfo {
873 #[allow(dead_code)]
874 pub fn new(block_count: usize) -> Self {
875 CSELivenessInfo {
876 live_in: vec![std::collections::HashSet::new(); block_count],
877 live_out: vec![std::collections::HashSet::new(); block_count],
878 defs: vec![std::collections::HashSet::new(); block_count],
879 uses: vec![std::collections::HashSet::new(); block_count],
880 }
881 }
882 #[allow(dead_code)]
883 pub fn add_def(&mut self, block: usize, var: u32) {
884 if block < self.defs.len() {
885 self.defs[block].insert(var);
886 }
887 }
888 #[allow(dead_code)]
889 pub fn add_use(&mut self, block: usize, var: u32) {
890 if block < self.uses.len() {
891 self.uses[block].insert(var);
892 }
893 }
894 #[allow(dead_code)]
895 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
896 self.live_in
897 .get(block)
898 .map(|s| s.contains(&var))
899 .unwrap_or(false)
900 }
901 #[allow(dead_code)]
902 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
903 self.live_out
904 .get(block)
905 .map(|s| s.contains(&var))
906 .unwrap_or(false)
907 }
908}
909#[allow(dead_code)]
910#[derive(Debug, Clone)]
911pub struct CSECacheEntry {
912 pub key: String,
913 pub data: Vec<u8>,
914 pub timestamp: u64,
915 pub valid: bool,
916}
917#[allow(dead_code)]
919#[derive(Debug, Clone, Default)]
920pub struct CSEExtConstFolder {
921 pub(super) folds: usize,
922 pub(super) failures: usize,
923 pub(super) enabled: bool,
924}
925impl CSEExtConstFolder {
926 #[allow(dead_code)]
927 pub fn new() -> Self {
928 Self {
929 folds: 0,
930 failures: 0,
931 enabled: true,
932 }
933 }
934 #[allow(dead_code)]
935 pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
936 self.folds += 1;
937 a.checked_add(b)
938 }
939 #[allow(dead_code)]
940 pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
941 self.folds += 1;
942 a.checked_sub(b)
943 }
944 #[allow(dead_code)]
945 pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
946 self.folds += 1;
947 a.checked_mul(b)
948 }
949 #[allow(dead_code)]
950 pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
951 if b == 0 {
952 self.failures += 1;
953 None
954 } else {
955 self.folds += 1;
956 a.checked_div(b)
957 }
958 }
959 #[allow(dead_code)]
960 pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
961 if b == 0 {
962 self.failures += 1;
963 None
964 } else {
965 self.folds += 1;
966 a.checked_rem(b)
967 }
968 }
969 #[allow(dead_code)]
970 pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
971 self.folds += 1;
972 a.checked_neg()
973 }
974 #[allow(dead_code)]
975 pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
976 if s >= 64 {
977 self.failures += 1;
978 None
979 } else {
980 self.folds += 1;
981 a.checked_shl(s)
982 }
983 }
984 #[allow(dead_code)]
985 pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
986 if s >= 64 {
987 self.failures += 1;
988 None
989 } else {
990 self.folds += 1;
991 a.checked_shr(s)
992 }
993 }
994 #[allow(dead_code)]
995 pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
996 self.folds += 1;
997 a & b
998 }
999 #[allow(dead_code)]
1000 pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
1001 self.folds += 1;
1002 a | b
1003 }
1004 #[allow(dead_code)]
1005 pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
1006 self.folds += 1;
1007 a ^ b
1008 }
1009 #[allow(dead_code)]
1010 pub fn not_i64(&mut self, a: i64) -> i64 {
1011 self.folds += 1;
1012 !a
1013 }
1014 #[allow(dead_code)]
1015 pub fn fold_count(&self) -> usize {
1016 self.folds
1017 }
1018 #[allow(dead_code)]
1019 pub fn failure_count(&self) -> usize {
1020 self.failures
1021 }
1022 #[allow(dead_code)]
1023 pub fn enable(&mut self) {
1024 self.enabled = true;
1025 }
1026 #[allow(dead_code)]
1027 pub fn disable(&mut self) {
1028 self.enabled = false;
1029 }
1030 #[allow(dead_code)]
1031 pub fn is_enabled(&self) -> bool {
1032 self.enabled
1033 }
1034}
1035#[derive(Clone, Default, Debug)]
1037pub struct CseReport {
1038 pub expressions_found: usize,
1040 pub expressions_eliminated: usize,
1042 pub lets_hoisted: usize,
1044}
1045#[allow(dead_code)]
1046#[derive(Debug, Clone)]
1047pub struct CSEPassConfig {
1048 pub phase: CSEPassPhase,
1049 pub enabled: bool,
1050 pub max_iterations: u32,
1051 pub debug_output: bool,
1052 pub pass_name: String,
1053}
1054impl CSEPassConfig {
1055 #[allow(dead_code)]
1056 pub fn new(name: impl Into<String>, phase: CSEPassPhase) -> Self {
1057 CSEPassConfig {
1058 phase,
1059 enabled: true,
1060 max_iterations: 10,
1061 debug_output: false,
1062 pass_name: name.into(),
1063 }
1064 }
1065 #[allow(dead_code)]
1066 pub fn disabled(mut self) -> Self {
1067 self.enabled = false;
1068 self
1069 }
1070 #[allow(dead_code)]
1071 pub fn with_debug(mut self) -> Self {
1072 self.debug_output = true;
1073 self
1074 }
1075 #[allow(dead_code)]
1076 pub fn max_iter(mut self, n: u32) -> Self {
1077 self.max_iterations = n;
1078 self
1079 }
1080}
1081#[allow(dead_code)]
1082pub struct CSEConstantFoldingHelper;
1083impl CSEConstantFoldingHelper {
1084 #[allow(dead_code)]
1085 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1086 a.checked_add(b)
1087 }
1088 #[allow(dead_code)]
1089 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1090 a.checked_sub(b)
1091 }
1092 #[allow(dead_code)]
1093 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1094 a.checked_mul(b)
1095 }
1096 #[allow(dead_code)]
1097 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1098 if b == 0 {
1099 None
1100 } else {
1101 a.checked_div(b)
1102 }
1103 }
1104 #[allow(dead_code)]
1105 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1106 a + b
1107 }
1108 #[allow(dead_code)]
1109 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1110 a * b
1111 }
1112 #[allow(dead_code)]
1113 pub fn fold_neg_i64(a: i64) -> Option<i64> {
1114 a.checked_neg()
1115 }
1116 #[allow(dead_code)]
1117 pub fn fold_not_bool(a: bool) -> bool {
1118 !a
1119 }
1120 #[allow(dead_code)]
1121 pub fn fold_and_bool(a: bool, b: bool) -> bool {
1122 a && b
1123 }
1124 #[allow(dead_code)]
1125 pub fn fold_or_bool(a: bool, b: bool) -> bool {
1126 a || b
1127 }
1128 #[allow(dead_code)]
1129 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1130 a.checked_shl(b)
1131 }
1132 #[allow(dead_code)]
1133 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1134 a.checked_shr(b)
1135 }
1136 #[allow(dead_code)]
1137 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1138 if b == 0 {
1139 None
1140 } else {
1141 Some(a % b)
1142 }
1143 }
1144 #[allow(dead_code)]
1145 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1146 a & b
1147 }
1148 #[allow(dead_code)]
1149 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1150 a | b
1151 }
1152 #[allow(dead_code)]
1153 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1154 a ^ b
1155 }
1156 #[allow(dead_code)]
1157 pub fn fold_bitnot_i64(a: i64) -> i64 {
1158 !a
1159 }
1160}
1161#[allow(dead_code)]
1163#[derive(Debug, Clone, Default)]
1164pub struct CSEX2Liveness {
1165 pub live_in: Vec<Vec<usize>>,
1166 pub live_out: Vec<Vec<usize>>,
1167 pub defs: Vec<Vec<usize>>,
1168 pub uses: Vec<Vec<usize>>,
1169}
1170impl CSEX2Liveness {
1171 #[allow(dead_code)]
1172 pub fn new(n: usize) -> Self {
1173 Self {
1174 live_in: vec![Vec::new(); n],
1175 live_out: vec![Vec::new(); n],
1176 defs: vec![Vec::new(); n],
1177 uses: vec![Vec::new(); n],
1178 }
1179 }
1180 #[allow(dead_code)]
1181 pub fn live_in(&self, b: usize, v: usize) -> bool {
1182 self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1183 }
1184 #[allow(dead_code)]
1185 pub fn live_out(&self, b: usize, v: usize) -> bool {
1186 self.live_out
1187 .get(b)
1188 .map(|s| s.contains(&v))
1189 .unwrap_or(false)
1190 }
1191 #[allow(dead_code)]
1192 pub fn add_def(&mut self, b: usize, v: usize) {
1193 if let Some(s) = self.defs.get_mut(b) {
1194 if !s.contains(&v) {
1195 s.push(v);
1196 }
1197 }
1198 }
1199 #[allow(dead_code)]
1200 pub fn add_use(&mut self, b: usize, v: usize) {
1201 if let Some(s) = self.uses.get_mut(b) {
1202 if !s.contains(&v) {
1203 s.push(v);
1204 }
1205 }
1206 }
1207 #[allow(dead_code)]
1208 pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
1209 self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1210 }
1211 #[allow(dead_code)]
1212 pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
1213 self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1214 }
1215}
1216#[allow(dead_code)]
1218#[derive(Debug, Clone)]
1219pub struct CSEX2DepGraph {
1220 pub(super) n: usize,
1221 pub(super) adj: Vec<Vec<usize>>,
1222 pub(super) rev: Vec<Vec<usize>>,
1223 pub(super) edge_count: usize,
1224}
1225impl CSEX2DepGraph {
1226 #[allow(dead_code)]
1227 pub fn new(n: usize) -> Self {
1228 Self {
1229 n,
1230 adj: vec![Vec::new(); n],
1231 rev: vec![Vec::new(); n],
1232 edge_count: 0,
1233 }
1234 }
1235 #[allow(dead_code)]
1236 pub fn add_edge(&mut self, from: usize, to: usize) {
1237 if from < self.n && to < self.n {
1238 if !self.adj[from].contains(&to) {
1239 self.adj[from].push(to);
1240 self.rev[to].push(from);
1241 self.edge_count += 1;
1242 }
1243 }
1244 }
1245 #[allow(dead_code)]
1246 pub fn succs(&self, n: usize) -> &[usize] {
1247 self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1248 }
1249 #[allow(dead_code)]
1250 pub fn preds(&self, n: usize) -> &[usize] {
1251 self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1252 }
1253 #[allow(dead_code)]
1254 pub fn topo_sort(&self) -> Option<Vec<usize>> {
1255 let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
1256 let mut q: std::collections::VecDeque<usize> =
1257 (0..self.n).filter(|&i| deg[i] == 0).collect();
1258 let mut out = Vec::with_capacity(self.n);
1259 while let Some(u) = q.pop_front() {
1260 out.push(u);
1261 for &v in &self.adj[u] {
1262 deg[v] -= 1;
1263 if deg[v] == 0 {
1264 q.push_back(v);
1265 }
1266 }
1267 }
1268 if out.len() == self.n {
1269 Some(out)
1270 } else {
1271 None
1272 }
1273 }
1274 #[allow(dead_code)]
1275 pub fn has_cycle(&self) -> bool {
1276 self.topo_sort().is_none()
1277 }
1278 #[allow(dead_code)]
1279 pub fn reachable(&self, start: usize) -> Vec<usize> {
1280 let mut vis = vec![false; self.n];
1281 let mut stk = vec![start];
1282 let mut out = Vec::new();
1283 while let Some(u) = stk.pop() {
1284 if u < self.n && !vis[u] {
1285 vis[u] = true;
1286 out.push(u);
1287 for &v in &self.adj[u] {
1288 if !vis[v] {
1289 stk.push(v);
1290 }
1291 }
1292 }
1293 }
1294 out
1295 }
1296 #[allow(dead_code)]
1297 pub fn scc(&self) -> Vec<Vec<usize>> {
1298 let mut visited = vec![false; self.n];
1299 let mut order = Vec::new();
1300 for i in 0..self.n {
1301 if !visited[i] {
1302 let mut stk = vec![(i, 0usize)];
1303 while let Some((u, idx)) = stk.last_mut() {
1304 if !visited[*u] {
1305 visited[*u] = true;
1306 }
1307 if *idx < self.adj[*u].len() {
1308 let v = self.adj[*u][*idx];
1309 *idx += 1;
1310 if !visited[v] {
1311 stk.push((v, 0));
1312 }
1313 } else {
1314 order.push(*u);
1315 stk.pop();
1316 }
1317 }
1318 }
1319 }
1320 let mut comp = vec![usize::MAX; self.n];
1321 let mut components: Vec<Vec<usize>> = Vec::new();
1322 for &start in order.iter().rev() {
1323 if comp[start] == usize::MAX {
1324 let cid = components.len();
1325 let mut component = Vec::new();
1326 let mut stk = vec![start];
1327 while let Some(u) = stk.pop() {
1328 if comp[u] == usize::MAX {
1329 comp[u] = cid;
1330 component.push(u);
1331 for &v in &self.rev[u] {
1332 if comp[v] == usize::MAX {
1333 stk.push(v);
1334 }
1335 }
1336 }
1337 }
1338 components.push(component);
1339 }
1340 }
1341 components
1342 }
1343 #[allow(dead_code)]
1344 pub fn node_count(&self) -> usize {
1345 self.n
1346 }
1347 #[allow(dead_code)]
1348 pub fn edge_count(&self) -> usize {
1349 self.edge_count
1350 }
1351}
1352#[derive(Clone, Debug, Default)]
1359pub struct GvnTable {
1360 pub table: HashMap<ExprKey, LcnfVarId>,
1362 pub num_classes: usize,
1364}
1365impl GvnTable {
1366 pub fn new() -> Self {
1367 Self::default()
1368 }
1369 pub fn lookup(&self, key: &ExprKey) -> Option<LcnfVarId> {
1371 self.table.get(key).copied()
1372 }
1373 pub fn insert(&mut self, key: ExprKey, var: LcnfVarId) -> LcnfVarId {
1377 *self.table.entry(key).or_insert_with(|| {
1378 self.num_classes += 1;
1379 var
1380 })
1381 }
1382}
1383#[allow(dead_code)]
1385#[derive(Debug, Clone)]
1386pub struct CSEExtDomTree {
1387 pub(super) idom: Vec<Option<usize>>,
1388 pub(super) children: Vec<Vec<usize>>,
1389 pub(super) depth: Vec<usize>,
1390}
1391impl CSEExtDomTree {
1392 #[allow(dead_code)]
1393 pub fn new(n: usize) -> Self {
1394 Self {
1395 idom: vec![None; n],
1396 children: vec![Vec::new(); n],
1397 depth: vec![0; n],
1398 }
1399 }
1400 #[allow(dead_code)]
1401 pub fn set_idom(&mut self, node: usize, dom: usize) {
1402 if node < self.idom.len() {
1403 self.idom[node] = Some(dom);
1404 if dom < self.children.len() {
1405 self.children[dom].push(node);
1406 }
1407 self.depth[node] = if dom < self.depth.len() {
1408 self.depth[dom] + 1
1409 } else {
1410 1
1411 };
1412 }
1413 }
1414 #[allow(dead_code)]
1415 pub fn dominates(&self, a: usize, mut b: usize) -> bool {
1416 if a == b {
1417 return true;
1418 }
1419 let n = self.idom.len();
1420 for _ in 0..n {
1421 match self.idom.get(b).copied().flatten() {
1422 None => return false,
1423 Some(p) if p == a => return true,
1424 Some(p) if p == b => return false,
1425 Some(p) => b = p,
1426 }
1427 }
1428 false
1429 }
1430 #[allow(dead_code)]
1431 pub fn children_of(&self, n: usize) -> &[usize] {
1432 self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1433 }
1434 #[allow(dead_code)]
1435 pub fn depth_of(&self, n: usize) -> usize {
1436 self.depth.get(n).copied().unwrap_or(0)
1437 }
1438 #[allow(dead_code)]
1439 pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
1440 let n = self.idom.len();
1441 for _ in 0..(2 * n) {
1442 if a == b {
1443 return a;
1444 }
1445 if self.depth_of(a) > self.depth_of(b) {
1446 a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
1447 } else {
1448 b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
1449 }
1450 }
1451 0
1452 }
1453}
1454#[allow(dead_code)]
1456#[derive(Debug)]
1457pub struct CSEX2Cache {
1458 pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
1459 pub(super) cap: usize,
1460 pub(super) total_hits: u64,
1461 pub(super) total_misses: u64,
1462}
1463impl CSEX2Cache {
1464 #[allow(dead_code)]
1465 pub fn new(cap: usize) -> Self {
1466 Self {
1467 entries: Vec::new(),
1468 cap,
1469 total_hits: 0,
1470 total_misses: 0,
1471 }
1472 }
1473 #[allow(dead_code)]
1474 pub fn get(&mut self, key: u64) -> Option<&[u8]> {
1475 for e in self.entries.iter_mut() {
1476 if e.0 == key && e.2 {
1477 e.3 += 1;
1478 self.total_hits += 1;
1479 return Some(&e.1);
1480 }
1481 }
1482 self.total_misses += 1;
1483 None
1484 }
1485 #[allow(dead_code)]
1486 pub fn put(&mut self, key: u64, data: Vec<u8>) {
1487 if self.entries.len() >= self.cap {
1488 self.entries.retain(|e| e.2);
1489 if self.entries.len() >= self.cap {
1490 self.entries.remove(0);
1491 }
1492 }
1493 self.entries.push((key, data, true, 0));
1494 }
1495 #[allow(dead_code)]
1496 pub fn invalidate(&mut self) {
1497 for e in self.entries.iter_mut() {
1498 e.2 = false;
1499 }
1500 }
1501 #[allow(dead_code)]
1502 pub fn hit_rate(&self) -> f64 {
1503 let t = self.total_hits + self.total_misses;
1504 if t == 0 {
1505 0.0
1506 } else {
1507 self.total_hits as f64 / t as f64
1508 }
1509 }
1510 #[allow(dead_code)]
1511 pub fn live_count(&self) -> usize {
1512 self.entries.iter().filter(|e| e.2).count()
1513 }
1514}
1515#[allow(dead_code)]
1516#[derive(Debug, Clone)]
1517pub struct CSEDominatorTree {
1518 pub idom: Vec<Option<u32>>,
1519 pub dom_children: Vec<Vec<u32>>,
1520 pub dom_depth: Vec<u32>,
1521}
1522impl CSEDominatorTree {
1523 #[allow(dead_code)]
1524 pub fn new(size: usize) -> Self {
1525 CSEDominatorTree {
1526 idom: vec![None; size],
1527 dom_children: vec![Vec::new(); size],
1528 dom_depth: vec![0; size],
1529 }
1530 }
1531 #[allow(dead_code)]
1532 pub fn set_idom(&mut self, node: usize, idom: u32) {
1533 self.idom[node] = Some(idom);
1534 }
1535 #[allow(dead_code)]
1536 pub fn dominates(&self, a: usize, b: usize) -> bool {
1537 if a == b {
1538 return true;
1539 }
1540 let mut cur = b;
1541 loop {
1542 match self.idom[cur] {
1543 Some(parent) if parent as usize == a => return true,
1544 Some(parent) if parent as usize == cur => return false,
1545 Some(parent) => cur = parent as usize,
1546 None => return false,
1547 }
1548 }
1549 }
1550 #[allow(dead_code)]
1551 pub fn depth(&self, node: usize) -> u32 {
1552 self.dom_depth.get(node).copied().unwrap_or(0)
1553 }
1554}
1555#[allow(dead_code)]
1556#[derive(Debug, Clone, PartialEq)]
1557pub enum CSEPassPhase {
1558 Analysis,
1559 Transformation,
1560 Verification,
1561 Cleanup,
1562}
1563impl CSEPassPhase {
1564 #[allow(dead_code)]
1565 pub fn name(&self) -> &str {
1566 match self {
1567 CSEPassPhase::Analysis => "analysis",
1568 CSEPassPhase::Transformation => "transformation",
1569 CSEPassPhase::Verification => "verification",
1570 CSEPassPhase::Cleanup => "cleanup",
1571 }
1572 }
1573 #[allow(dead_code)]
1574 pub fn is_modifying(&self) -> bool {
1575 matches!(self, CSEPassPhase::Transformation | CSEPassPhase::Cleanup)
1576 }
1577}
1578#[allow(dead_code)]
1579pub struct CSEPassRegistry {
1580 pub(super) configs: Vec<CSEPassConfig>,
1581 pub(super) stats: std::collections::HashMap<String, CSEPassStats>,
1582}
1583impl CSEPassRegistry {
1584 #[allow(dead_code)]
1585 pub fn new() -> Self {
1586 CSEPassRegistry {
1587 configs: Vec::new(),
1588 stats: std::collections::HashMap::new(),
1589 }
1590 }
1591 #[allow(dead_code)]
1592 pub fn register(&mut self, config: CSEPassConfig) {
1593 self.stats
1594 .insert(config.pass_name.clone(), CSEPassStats::new());
1595 self.configs.push(config);
1596 }
1597 #[allow(dead_code)]
1598 pub fn enabled_passes(&self) -> Vec<&CSEPassConfig> {
1599 self.configs.iter().filter(|c| c.enabled).collect()
1600 }
1601 #[allow(dead_code)]
1602 pub fn get_stats(&self, name: &str) -> Option<&CSEPassStats> {
1603 self.stats.get(name)
1604 }
1605 #[allow(dead_code)]
1606 pub fn total_passes(&self) -> usize {
1607 self.configs.len()
1608 }
1609 #[allow(dead_code)]
1610 pub fn enabled_count(&self) -> usize {
1611 self.enabled_passes().len()
1612 }
1613 #[allow(dead_code)]
1614 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
1615 if let Some(stats) = self.stats.get_mut(name) {
1616 stats.record_run(changes, time_ms, iter);
1617 }
1618 }
1619}
1620#[allow(dead_code)]
1621#[derive(Debug, Clone)]
1622pub struct CSEDepGraph {
1623 pub(super) nodes: Vec<u32>,
1624 pub(super) edges: Vec<(u32, u32)>,
1625}
1626impl CSEDepGraph {
1627 #[allow(dead_code)]
1628 pub fn new() -> Self {
1629 CSEDepGraph {
1630 nodes: Vec::new(),
1631 edges: Vec::new(),
1632 }
1633 }
1634 #[allow(dead_code)]
1635 pub fn add_node(&mut self, id: u32) {
1636 if !self.nodes.contains(&id) {
1637 self.nodes.push(id);
1638 }
1639 }
1640 #[allow(dead_code)]
1641 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
1642 self.add_node(dep);
1643 self.add_node(dependent);
1644 self.edges.push((dep, dependent));
1645 }
1646 #[allow(dead_code)]
1647 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
1648 self.edges
1649 .iter()
1650 .filter(|(d, _)| *d == node)
1651 .map(|(_, dep)| *dep)
1652 .collect()
1653 }
1654 #[allow(dead_code)]
1655 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
1656 self.edges
1657 .iter()
1658 .filter(|(_, dep)| *dep == node)
1659 .map(|(d, _)| *d)
1660 .collect()
1661 }
1662 #[allow(dead_code)]
1663 pub fn topological_sort(&self) -> Vec<u32> {
1664 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
1665 for &n in &self.nodes {
1666 in_degree.insert(n, 0);
1667 }
1668 for (_, dep) in &self.edges {
1669 *in_degree.entry(*dep).or_insert(0) += 1;
1670 }
1671 let mut queue: std::collections::VecDeque<u32> = self
1672 .nodes
1673 .iter()
1674 .filter(|&&n| in_degree[&n] == 0)
1675 .copied()
1676 .collect();
1677 let mut result = Vec::new();
1678 while let Some(node) = queue.pop_front() {
1679 result.push(node);
1680 for dep in self.dependents_of(node) {
1681 let cnt = in_degree.entry(dep).or_insert(0);
1682 *cnt = cnt.saturating_sub(1);
1683 if *cnt == 0 {
1684 queue.push_back(dep);
1685 }
1686 }
1687 }
1688 result
1689 }
1690 #[allow(dead_code)]
1691 pub fn has_cycle(&self) -> bool {
1692 self.topological_sort().len() < self.nodes.len()
1693 }
1694}
1695#[allow(dead_code)]
1697#[derive(Debug, Default)]
1698pub struct CSEExtPassRegistry {
1699 pub(super) configs: Vec<CSEExtPassConfig>,
1700 pub(super) stats: Vec<CSEExtPassStats>,
1701}
1702impl CSEExtPassRegistry {
1703 #[allow(dead_code)]
1704 pub fn new() -> Self {
1705 Self::default()
1706 }
1707 #[allow(dead_code)]
1708 pub fn register(&mut self, c: CSEExtPassConfig) {
1709 self.stats.push(CSEExtPassStats::new());
1710 self.configs.push(c);
1711 }
1712 #[allow(dead_code)]
1713 pub fn len(&self) -> usize {
1714 self.configs.len()
1715 }
1716 #[allow(dead_code)]
1717 pub fn is_empty(&self) -> bool {
1718 self.configs.is_empty()
1719 }
1720 #[allow(dead_code)]
1721 pub fn get(&self, i: usize) -> Option<&CSEExtPassConfig> {
1722 self.configs.get(i)
1723 }
1724 #[allow(dead_code)]
1725 pub fn get_stats(&self, i: usize) -> Option<&CSEExtPassStats> {
1726 self.stats.get(i)
1727 }
1728 #[allow(dead_code)]
1729 pub fn enabled_passes(&self) -> Vec<&CSEExtPassConfig> {
1730 self.configs.iter().filter(|c| c.enabled).collect()
1731 }
1732 #[allow(dead_code)]
1733 pub fn passes_in_phase(&self, ph: &CSEExtPassPhase) -> Vec<&CSEExtPassConfig> {
1734 self.configs
1735 .iter()
1736 .filter(|c| c.enabled && &c.phase == ph)
1737 .collect()
1738 }
1739 #[allow(dead_code)]
1740 pub fn total_nodes_visited(&self) -> usize {
1741 self.stats.iter().map(|s| s.nodes_visited).sum()
1742 }
1743 #[allow(dead_code)]
1744 pub fn any_changed(&self) -> bool {
1745 self.stats.iter().any(|s| s.changed)
1746 }
1747}
1748#[allow(dead_code)]
1750#[derive(Debug, Clone)]
1751pub struct CSEExtWorklist {
1752 pub(super) items: std::collections::VecDeque<usize>,
1753 pub(super) present: Vec<bool>,
1754}
1755impl CSEExtWorklist {
1756 #[allow(dead_code)]
1757 pub fn new(capacity: usize) -> Self {
1758 Self {
1759 items: std::collections::VecDeque::new(),
1760 present: vec![false; capacity],
1761 }
1762 }
1763 #[allow(dead_code)]
1764 pub fn push(&mut self, id: usize) {
1765 if id < self.present.len() && !self.present[id] {
1766 self.present[id] = true;
1767 self.items.push_back(id);
1768 }
1769 }
1770 #[allow(dead_code)]
1771 pub fn push_front(&mut self, id: usize) {
1772 if id < self.present.len() && !self.present[id] {
1773 self.present[id] = true;
1774 self.items.push_front(id);
1775 }
1776 }
1777 #[allow(dead_code)]
1778 pub fn pop(&mut self) -> Option<usize> {
1779 let id = self.items.pop_front()?;
1780 if id < self.present.len() {
1781 self.present[id] = false;
1782 }
1783 Some(id)
1784 }
1785 #[allow(dead_code)]
1786 pub fn is_empty(&self) -> bool {
1787 self.items.is_empty()
1788 }
1789 #[allow(dead_code)]
1790 pub fn len(&self) -> usize {
1791 self.items.len()
1792 }
1793 #[allow(dead_code)]
1794 pub fn contains(&self, id: usize) -> bool {
1795 id < self.present.len() && self.present[id]
1796 }
1797 #[allow(dead_code)]
1798 pub fn drain_all(&mut self) -> Vec<usize> {
1799 let v: Vec<usize> = self.items.drain(..).collect();
1800 for &id in &v {
1801 if id < self.present.len() {
1802 self.present[id] = false;
1803 }
1804 }
1805 v
1806 }
1807}
1808#[allow(dead_code)]
1810#[derive(Debug, Clone)]
1811pub struct CSEX2PassConfig {
1812 pub name: String,
1813 pub phase: CSEX2PassPhase,
1814 pub enabled: bool,
1815 pub max_iterations: usize,
1816 pub debug: u32,
1817 pub timeout_ms: Option<u64>,
1818}
1819impl CSEX2PassConfig {
1820 #[allow(dead_code)]
1821 pub fn new(name: impl Into<String>) -> Self {
1822 Self {
1823 name: name.into(),
1824 phase: CSEX2PassPhase::Middle,
1825 enabled: true,
1826 max_iterations: 100,
1827 debug: 0,
1828 timeout_ms: None,
1829 }
1830 }
1831 #[allow(dead_code)]
1832 pub fn with_phase(mut self, phase: CSEX2PassPhase) -> Self {
1833 self.phase = phase;
1834 self
1835 }
1836 #[allow(dead_code)]
1837 pub fn with_max_iter(mut self, n: usize) -> Self {
1838 self.max_iterations = n;
1839 self
1840 }
1841 #[allow(dead_code)]
1842 pub fn with_debug(mut self, d: u32) -> Self {
1843 self.debug = d;
1844 self
1845 }
1846 #[allow(dead_code)]
1847 pub fn disabled(mut self) -> Self {
1848 self.enabled = false;
1849 self
1850 }
1851 #[allow(dead_code)]
1852 pub fn with_timeout(mut self, ms: u64) -> Self {
1853 self.timeout_ms = Some(ms);
1854 self
1855 }
1856 #[allow(dead_code)]
1857 pub fn is_debug_enabled(&self) -> bool {
1858 self.debug > 0
1859 }
1860}
1861#[allow(dead_code)]
1863#[derive(Debug, Clone, Default)]
1864pub struct CSEX2PassStats {
1865 pub iterations: usize,
1866 pub changed: bool,
1867 pub nodes_visited: usize,
1868 pub nodes_modified: usize,
1869 pub time_ms: u64,
1870 pub memory_bytes: usize,
1871 pub errors: usize,
1872}
1873impl CSEX2PassStats {
1874 #[allow(dead_code)]
1875 pub fn new() -> Self {
1876 Self::default()
1877 }
1878 #[allow(dead_code)]
1879 pub fn visit(&mut self) {
1880 self.nodes_visited += 1;
1881 }
1882 #[allow(dead_code)]
1883 pub fn modify(&mut self) {
1884 self.nodes_modified += 1;
1885 self.changed = true;
1886 }
1887 #[allow(dead_code)]
1888 pub fn iterate(&mut self) {
1889 self.iterations += 1;
1890 }
1891 #[allow(dead_code)]
1892 pub fn error(&mut self) {
1893 self.errors += 1;
1894 }
1895 #[allow(dead_code)]
1896 pub fn efficiency(&self) -> f64 {
1897 if self.nodes_visited == 0 {
1898 0.0
1899 } else {
1900 self.nodes_modified as f64 / self.nodes_visited as f64
1901 }
1902 }
1903 #[allow(dead_code)]
1904 pub fn merge(&mut self, o: &CSEX2PassStats) {
1905 self.iterations += o.iterations;
1906 self.changed |= o.changed;
1907 self.nodes_visited += o.nodes_visited;
1908 self.nodes_modified += o.nodes_modified;
1909 self.time_ms += o.time_ms;
1910 self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
1911 self.errors += o.errors;
1912 }
1913}
1914#[allow(dead_code)]
1916#[derive(Debug, Clone, Default)]
1917pub struct CSEExtLiveness {
1918 pub live_in: Vec<Vec<usize>>,
1919 pub live_out: Vec<Vec<usize>>,
1920 pub defs: Vec<Vec<usize>>,
1921 pub uses: Vec<Vec<usize>>,
1922}
1923impl CSEExtLiveness {
1924 #[allow(dead_code)]
1925 pub fn new(n: usize) -> Self {
1926 Self {
1927 live_in: vec![Vec::new(); n],
1928 live_out: vec![Vec::new(); n],
1929 defs: vec![Vec::new(); n],
1930 uses: vec![Vec::new(); n],
1931 }
1932 }
1933 #[allow(dead_code)]
1934 pub fn live_in(&self, b: usize, v: usize) -> bool {
1935 self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1936 }
1937 #[allow(dead_code)]
1938 pub fn live_out(&self, b: usize, v: usize) -> bool {
1939 self.live_out
1940 .get(b)
1941 .map(|s| s.contains(&v))
1942 .unwrap_or(false)
1943 }
1944 #[allow(dead_code)]
1945 pub fn add_def(&mut self, b: usize, v: usize) {
1946 if let Some(s) = self.defs.get_mut(b) {
1947 if !s.contains(&v) {
1948 s.push(v);
1949 }
1950 }
1951 }
1952 #[allow(dead_code)]
1953 pub fn add_use(&mut self, b: usize, v: usize) {
1954 if let Some(s) = self.uses.get_mut(b) {
1955 if !s.contains(&v) {
1956 s.push(v);
1957 }
1958 }
1959 }
1960 #[allow(dead_code)]
1961 pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
1962 self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1963 }
1964 #[allow(dead_code)]
1965 pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
1966 self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1967 }
1968}
1969#[allow(dead_code)]
1971#[derive(Debug, Clone, Default)]
1972pub struct CSEX2ConstFolder {
1973 pub(super) folds: usize,
1974 pub(super) failures: usize,
1975 pub(super) enabled: bool,
1976}
1977impl CSEX2ConstFolder {
1978 #[allow(dead_code)]
1979 pub fn new() -> Self {
1980 Self {
1981 folds: 0,
1982 failures: 0,
1983 enabled: true,
1984 }
1985 }
1986 #[allow(dead_code)]
1987 pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1988 self.folds += 1;
1989 a.checked_add(b)
1990 }
1991 #[allow(dead_code)]
1992 pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1993 self.folds += 1;
1994 a.checked_sub(b)
1995 }
1996 #[allow(dead_code)]
1997 pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1998 self.folds += 1;
1999 a.checked_mul(b)
2000 }
2001 #[allow(dead_code)]
2002 pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
2003 if b == 0 {
2004 self.failures += 1;
2005 None
2006 } else {
2007 self.folds += 1;
2008 a.checked_div(b)
2009 }
2010 }
2011 #[allow(dead_code)]
2012 pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
2013 if b == 0 {
2014 self.failures += 1;
2015 None
2016 } else {
2017 self.folds += 1;
2018 a.checked_rem(b)
2019 }
2020 }
2021 #[allow(dead_code)]
2022 pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
2023 self.folds += 1;
2024 a.checked_neg()
2025 }
2026 #[allow(dead_code)]
2027 pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
2028 if s >= 64 {
2029 self.failures += 1;
2030 None
2031 } else {
2032 self.folds += 1;
2033 a.checked_shl(s)
2034 }
2035 }
2036 #[allow(dead_code)]
2037 pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
2038 if s >= 64 {
2039 self.failures += 1;
2040 None
2041 } else {
2042 self.folds += 1;
2043 a.checked_shr(s)
2044 }
2045 }
2046 #[allow(dead_code)]
2047 pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
2048 self.folds += 1;
2049 a & b
2050 }
2051 #[allow(dead_code)]
2052 pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
2053 self.folds += 1;
2054 a | b
2055 }
2056 #[allow(dead_code)]
2057 pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
2058 self.folds += 1;
2059 a ^ b
2060 }
2061 #[allow(dead_code)]
2062 pub fn not_i64(&mut self, a: i64) -> i64 {
2063 self.folds += 1;
2064 !a
2065 }
2066 #[allow(dead_code)]
2067 pub fn fold_count(&self) -> usize {
2068 self.folds
2069 }
2070 #[allow(dead_code)]
2071 pub fn failure_count(&self) -> usize {
2072 self.failures
2073 }
2074 #[allow(dead_code)]
2075 pub fn enable(&mut self) {
2076 self.enabled = true;
2077 }
2078 #[allow(dead_code)]
2079 pub fn disable(&mut self) {
2080 self.enabled = false;
2081 }
2082 #[allow(dead_code)]
2083 pub fn is_enabled(&self) -> bool {
2084 self.enabled
2085 }
2086}
2087#[derive(Clone, PartialEq, Eq, Hash, Debug)]
2093pub enum ExprKey {
2094 Lit(LcnfLit),
2096 Var(LcnfVarId),
2098 Ctor(String, u32, Vec<LcnfArg>),
2100 Proj(String, u32, LcnfVarId),
2102 App(LcnfArg, Vec<LcnfArg>),
2104 CommApp(String, Vec<LcnfArg>),
2106}
2107#[derive(Clone, Debug, Default)]
2114pub struct DominatorTree {
2115 pub depth_map: HashMap<LcnfVarId, usize>,
2117}
2118impl DominatorTree {
2119 pub fn new() -> Self {
2120 Self::default()
2121 }
2122 pub fn insert(&mut self, var: LcnfVarId, depth: usize) {
2124 self.depth_map.insert(var, depth);
2125 }
2126 pub fn depth_of(&self, var: LcnfVarId) -> Option<usize> {
2128 self.depth_map.get(&var).copied()
2129 }
2130 pub fn dominates(&self, a: LcnfVarId, b: LcnfVarId) -> bool {
2133 match (self.depth_of(a), self.depth_of(b)) {
2134 (Some(da), Some(db)) => da <= db,
2135 _ => false,
2136 }
2137 }
2138}
2139#[allow(dead_code)]
2141#[derive(Debug)]
2142pub struct CSEExtCache {
2143 pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
2144 pub(super) cap: usize,
2145 pub(super) total_hits: u64,
2146 pub(super) total_misses: u64,
2147}
2148impl CSEExtCache {
2149 #[allow(dead_code)]
2150 pub fn new(cap: usize) -> Self {
2151 Self {
2152 entries: Vec::new(),
2153 cap,
2154 total_hits: 0,
2155 total_misses: 0,
2156 }
2157 }
2158 #[allow(dead_code)]
2159 pub fn get(&mut self, key: u64) -> Option<&[u8]> {
2160 for e in self.entries.iter_mut() {
2161 if e.0 == key && e.2 {
2162 e.3 += 1;
2163 self.total_hits += 1;
2164 return Some(&e.1);
2165 }
2166 }
2167 self.total_misses += 1;
2168 None
2169 }
2170 #[allow(dead_code)]
2171 pub fn put(&mut self, key: u64, data: Vec<u8>) {
2172 if self.entries.len() >= self.cap {
2173 self.entries.retain(|e| e.2);
2174 if self.entries.len() >= self.cap {
2175 self.entries.remove(0);
2176 }
2177 }
2178 self.entries.push((key, data, true, 0));
2179 }
2180 #[allow(dead_code)]
2181 pub fn invalidate(&mut self) {
2182 for e in self.entries.iter_mut() {
2183 e.2 = false;
2184 }
2185 }
2186 #[allow(dead_code)]
2187 pub fn hit_rate(&self) -> f64 {
2188 let t = self.total_hits + self.total_misses;
2189 if t == 0 {
2190 0.0
2191 } else {
2192 self.total_hits as f64 / t as f64
2193 }
2194 }
2195 #[allow(dead_code)]
2196 pub fn live_count(&self) -> usize {
2197 self.entries.iter().filter(|e| e.2).count()
2198 }
2199}
2200#[derive(Clone, Default, Debug)]
2206pub struct AvailableSet {
2207 pub(super) inner: HashMap<ExprKey, AvailableExpr>,
2210}
2211impl AvailableSet {
2212 pub fn new() -> Self {
2213 Self::default()
2214 }
2215 pub fn insert(&mut self, key: ExprKey, var_id: LcnfVarId, name: String, depth: usize) {
2217 self.inner.insert(
2218 key.clone(),
2219 AvailableExpr {
2220 key,
2221 var_id,
2222 name_hint: name,
2223 depth,
2224 },
2225 );
2226 }
2227 pub fn find(&self, key: &ExprKey) -> Option<LcnfVarId> {
2230 self.inner.get(key).map(|ae| ae.var_id)
2231 }
2232 pub fn kill_above_depth(&mut self, kill_depth: usize) {
2235 self.inner.retain(|_, ae| ae.depth < kill_depth);
2236 }
2237 pub fn len(&self) -> usize {
2239 self.inner.len()
2240 }
2241 pub fn is_empty(&self) -> bool {
2242 self.inner.is_empty()
2243 }
2244}