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