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