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