1use super::defs::*;
6use super::impls1::*;
7use crate::c_backend::{self, CEmitConfig, COutput};
8use crate::closure_convert::{ClosureConvertConfig, ClosureConverter};
9use crate::lcnf::*;
10use crate::native_backend::{self, NativeEmitConfig, NativeModule};
11use crate::opt_dce::{self, DceConfig};
12use crate::to_lcnf::{self, ToLcnfConfig};
13use crate::CodegenTarget;
14use oxilean_kernel::expr::Expr;
15use oxilean_kernel::Name;
16
17use super::super::functions::LcnfDeclInput;
18
19use super::super::functions::*;
20use std::collections::{HashMap, HashSet, VecDeque};
21
22#[derive(Debug, Clone)]
24pub struct PipelineResult {
25 pub c_output: Option<COutput>,
27 pub native_output: Option<NativeModule>,
29 pub lcnf_module: LcnfModule,
31 pub stats: PipelineStats,
33}
34#[allow(dead_code)]
36#[derive(Debug, Clone)]
37pub struct PipeExtWorklist {
38 pub(crate) items: std::collections::VecDeque<usize>,
39 pub(crate) present: Vec<bool>,
40}
41impl PipeExtWorklist {
42 #[allow(dead_code)]
43 pub fn new(capacity: usize) -> Self {
44 Self {
45 items: std::collections::VecDeque::new(),
46 present: vec![false; capacity],
47 }
48 }
49 #[allow(dead_code)]
50 pub fn push(&mut self, id: usize) {
51 if id < self.present.len() && !self.present[id] {
52 self.present[id] = true;
53 self.items.push_back(id);
54 }
55 }
56 #[allow(dead_code)]
57 pub fn push_front(&mut self, id: usize) {
58 if id < self.present.len() && !self.present[id] {
59 self.present[id] = true;
60 self.items.push_front(id);
61 }
62 }
63 #[allow(dead_code)]
64 pub fn pop(&mut self) -> Option<usize> {
65 let id = self.items.pop_front()?;
66 if id < self.present.len() {
67 self.present[id] = false;
68 }
69 Some(id)
70 }
71 #[allow(dead_code)]
72 pub fn is_empty(&self) -> bool {
73 self.items.is_empty()
74 }
75 #[allow(dead_code)]
76 pub fn len(&self) -> usize {
77 self.items.len()
78 }
79 #[allow(dead_code)]
80 pub fn contains(&self, id: usize) -> bool {
81 id < self.present.len() && self.present[id]
82 }
83 #[allow(dead_code)]
84 pub fn drain_all(&mut self) -> Vec<usize> {
85 let v: Vec<usize> = self.items.drain(..).collect();
86 for &id in &v {
87 if id < self.present.len() {
88 self.present[id] = false;
89 }
90 }
91 v
92 }
93}
94#[allow(dead_code)]
96#[derive(Debug, Clone, Default)]
97pub struct PipeExtLiveness {
98 pub live_in: Vec<Vec<usize>>,
99 pub live_out: Vec<Vec<usize>>,
100 pub defs: Vec<Vec<usize>>,
101 pub uses: Vec<Vec<usize>>,
102}
103impl PipeExtLiveness {
104 #[allow(dead_code)]
105 pub fn new(n: usize) -> Self {
106 Self {
107 live_in: vec![Vec::new(); n],
108 live_out: vec![Vec::new(); n],
109 defs: vec![Vec::new(); n],
110 uses: vec![Vec::new(); n],
111 }
112 }
113 #[allow(dead_code)]
114 pub fn live_in(&self, b: usize, v: usize) -> bool {
115 self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
116 }
117 #[allow(dead_code)]
118 pub fn live_out(&self, b: usize, v: usize) -> bool {
119 self.live_out
120 .get(b)
121 .map(|s| s.contains(&v))
122 .unwrap_or(false)
123 }
124 #[allow(dead_code)]
125 pub fn add_def(&mut self, b: usize, v: usize) {
126 if let Some(s) = self.defs.get_mut(b) {
127 if !s.contains(&v) {
128 s.push(v);
129 }
130 }
131 }
132 #[allow(dead_code)]
133 pub fn add_use(&mut self, b: usize, v: usize) {
134 if let Some(s) = self.uses.get_mut(b) {
135 if !s.contains(&v) {
136 s.push(v);
137 }
138 }
139 }
140 #[allow(dead_code)]
141 pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
142 self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
143 }
144 #[allow(dead_code)]
145 pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
146 self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
147 }
148}
149#[allow(dead_code)]
151#[derive(Debug)]
152pub struct PipeX2Cache {
153 pub(crate) entries: Vec<(u64, Vec<u8>, bool, u32)>,
154 pub(crate) cap: usize,
155 pub(crate) total_hits: u64,
156 pub(crate) total_misses: u64,
157}
158impl PipeX2Cache {
159 #[allow(dead_code)]
160 pub fn new(cap: usize) -> Self {
161 Self {
162 entries: Vec::new(),
163 cap,
164 total_hits: 0,
165 total_misses: 0,
166 }
167 }
168 #[allow(dead_code)]
169 pub fn get(&mut self, key: u64) -> Option<&[u8]> {
170 for e in self.entries.iter_mut() {
171 if e.0 == key && e.2 {
172 e.3 += 1;
173 self.total_hits += 1;
174 return Some(&e.1);
175 }
176 }
177 self.total_misses += 1;
178 None
179 }
180 #[allow(dead_code)]
181 pub fn put(&mut self, key: u64, data: Vec<u8>) {
182 if self.entries.len() >= self.cap {
183 self.entries.retain(|e| e.2);
184 if self.entries.len() >= self.cap {
185 self.entries.remove(0);
186 }
187 }
188 self.entries.push((key, data, true, 0));
189 }
190 #[allow(dead_code)]
191 pub fn invalidate(&mut self) {
192 for e in self.entries.iter_mut() {
193 e.2 = false;
194 }
195 }
196 #[allow(dead_code)]
197 pub fn hit_rate(&self) -> f64 {
198 let t = self.total_hits + self.total_misses;
199 if t == 0 {
200 0.0
201 } else {
202 self.total_hits as f64 / t as f64
203 }
204 }
205 #[allow(dead_code)]
206 pub fn live_count(&self) -> usize {
207 self.entries.iter().filter(|e| e.2).count()
208 }
209}
210#[allow(dead_code)]
211#[derive(Debug, Clone)]
212pub struct PipeLivenessInfo {
213 pub live_in: Vec<std::collections::HashSet<u32>>,
214 pub live_out: Vec<std::collections::HashSet<u32>>,
215 pub defs: Vec<std::collections::HashSet<u32>>,
216 pub uses: Vec<std::collections::HashSet<u32>>,
217}
218impl PipeLivenessInfo {
219 #[allow(dead_code)]
220 pub fn new(block_count: usize) -> Self {
221 PipeLivenessInfo {
222 live_in: vec![std::collections::HashSet::new(); block_count],
223 live_out: vec![std::collections::HashSet::new(); block_count],
224 defs: vec![std::collections::HashSet::new(); block_count],
225 uses: vec![std::collections::HashSet::new(); block_count],
226 }
227 }
228 #[allow(dead_code)]
229 pub fn add_def(&mut self, block: usize, var: u32) {
230 if block < self.defs.len() {
231 self.defs[block].insert(var);
232 }
233 }
234 #[allow(dead_code)]
235 pub fn add_use(&mut self, block: usize, var: u32) {
236 if block < self.uses.len() {
237 self.uses[block].insert(var);
238 }
239 }
240 #[allow(dead_code)]
241 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
242 self.live_in
243 .get(block)
244 .map(|s| s.contains(&var))
245 .unwrap_or(false)
246 }
247 #[allow(dead_code)]
248 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
249 self.live_out
250 .get(block)
251 .map(|s| s.contains(&var))
252 .unwrap_or(false)
253 }
254}
255#[allow(dead_code)]
256#[derive(Debug, Clone, PartialEq)]
257pub enum PipePassPhase {
258 Analysis,
259 Transformation,
260 Verification,
261 Cleanup,
262}
263impl PipePassPhase {
264 #[allow(dead_code)]
265 pub fn name(&self) -> &str {
266 match self {
267 PipePassPhase::Analysis => "analysis",
268 PipePassPhase::Transformation => "transformation",
269 PipePassPhase::Verification => "verification",
270 PipePassPhase::Cleanup => "cleanup",
271 }
272 }
273 #[allow(dead_code)]
274 pub fn is_modifying(&self) -> bool {
275 matches!(self, PipePassPhase::Transformation | PipePassPhase::Cleanup)
276 }
277}
278#[derive(Debug, Clone, Default)]
280pub struct PipelineStats {
281 pub per_pass: Vec<(PassId, PassStats)>,
283 pub total_time_us: u64,
285 pub iterations: usize,
287 pub input_decls: usize,
289 pub output_decls: usize,
291}
292#[derive(Debug, Clone, PartialEq, Eq, Hash)]
294pub enum PassId {
295 JoinPoints,
297 Specialize,
299 Reuse,
301 Dce,
303 ClosureConvert,
305 Custom(String),
307}
308#[derive(Debug, Clone, Default)]
310pub struct PassStats {
311 pub decls_processed: usize,
313 pub transformations: usize,
315 pub time_us: u64,
317}
318#[allow(dead_code)]
320#[derive(Debug, Clone, PartialEq, Eq, Hash)]
321pub enum PipeExtPassPhase {
322 Early,
323 Middle,
324 Late,
325 Finalize,
326}
327impl PipeExtPassPhase {
328 #[allow(dead_code)]
329 pub fn is_early(&self) -> bool {
330 matches!(self, Self::Early)
331 }
332 #[allow(dead_code)]
333 pub fn is_middle(&self) -> bool {
334 matches!(self, Self::Middle)
335 }
336 #[allow(dead_code)]
337 pub fn is_late(&self) -> bool {
338 matches!(self, Self::Late)
339 }
340 #[allow(dead_code)]
341 pub fn is_finalize(&self) -> bool {
342 matches!(self, Self::Finalize)
343 }
344 #[allow(dead_code)]
345 pub fn order(&self) -> u32 {
346 match self {
347 Self::Early => 0,
348 Self::Middle => 1,
349 Self::Late => 2,
350 Self::Finalize => 3,
351 }
352 }
353 #[allow(dead_code)]
354 pub fn from_order(n: u32) -> Option<Self> {
355 match n {
356 0 => Some(Self::Early),
357 1 => Some(Self::Middle),
358 2 => Some(Self::Late),
359 3 => Some(Self::Finalize),
360 _ => None,
361 }
362 }
363}
364#[allow(dead_code)]
366#[derive(Debug, Clone)]
367pub struct PipeExtDepGraph {
368 pub(crate) n: usize,
369 pub(crate) adj: Vec<Vec<usize>>,
370 pub(crate) rev: Vec<Vec<usize>>,
371 pub(crate) edge_count: usize,
372}
373impl PipeExtDepGraph {
374 #[allow(dead_code)]
375 pub fn new(n: usize) -> Self {
376 Self {
377 n,
378 adj: vec![Vec::new(); n],
379 rev: vec![Vec::new(); n],
380 edge_count: 0,
381 }
382 }
383 #[allow(dead_code)]
384 pub fn add_edge(&mut self, from: usize, to: usize) {
385 if from < self.n && to < self.n {
386 if !self.adj[from].contains(&to) {
387 self.adj[from].push(to);
388 self.rev[to].push(from);
389 self.edge_count += 1;
390 }
391 }
392 }
393 #[allow(dead_code)]
394 pub fn succs(&self, n: usize) -> &[usize] {
395 self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
396 }
397 #[allow(dead_code)]
398 pub fn preds(&self, n: usize) -> &[usize] {
399 self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
400 }
401 #[allow(dead_code)]
402 pub fn topo_sort(&self) -> Option<Vec<usize>> {
403 let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
404 let mut q: std::collections::VecDeque<usize> =
405 (0..self.n).filter(|&i| deg[i] == 0).collect();
406 let mut out = Vec::with_capacity(self.n);
407 while let Some(u) = q.pop_front() {
408 out.push(u);
409 for &v in &self.adj[u] {
410 deg[v] -= 1;
411 if deg[v] == 0 {
412 q.push_back(v);
413 }
414 }
415 }
416 if out.len() == self.n {
417 Some(out)
418 } else {
419 None
420 }
421 }
422 #[allow(dead_code)]
423 pub fn has_cycle(&self) -> bool {
424 self.topo_sort().is_none()
425 }
426 #[allow(dead_code)]
427 pub fn reachable(&self, start: usize) -> Vec<usize> {
428 let mut vis = vec![false; self.n];
429 let mut stk = vec![start];
430 let mut out = Vec::new();
431 while let Some(u) = stk.pop() {
432 if u < self.n && !vis[u] {
433 vis[u] = true;
434 out.push(u);
435 for &v in &self.adj[u] {
436 if !vis[v] {
437 stk.push(v);
438 }
439 }
440 }
441 }
442 out
443 }
444 #[allow(dead_code)]
445 pub fn scc(&self) -> Vec<Vec<usize>> {
446 let mut visited = vec![false; self.n];
447 let mut order = Vec::new();
448 for i in 0..self.n {
449 if !visited[i] {
450 let mut stk = vec![(i, 0usize)];
451 while let Some((u, idx)) = stk.last_mut() {
452 if !visited[*u] {
453 visited[*u] = true;
454 }
455 if *idx < self.adj[*u].len() {
456 let v = self.adj[*u][*idx];
457 *idx += 1;
458 if !visited[v] {
459 stk.push((v, 0));
460 }
461 } else {
462 order.push(*u);
463 stk.pop();
464 }
465 }
466 }
467 }
468 let mut comp = vec![usize::MAX; self.n];
469 let mut components: Vec<Vec<usize>> = Vec::new();
470 for &start in order.iter().rev() {
471 if comp[start] == usize::MAX {
472 let cid = components.len();
473 let mut component = Vec::new();
474 let mut stk = vec![start];
475 while let Some(u) = stk.pop() {
476 if comp[u] == usize::MAX {
477 comp[u] = cid;
478 component.push(u);
479 for &v in &self.rev[u] {
480 if comp[v] == usize::MAX {
481 stk.push(v);
482 }
483 }
484 }
485 }
486 components.push(component);
487 }
488 }
489 components
490 }
491 #[allow(dead_code)]
492 pub fn node_count(&self) -> usize {
493 self.n
494 }
495 #[allow(dead_code)]
496 pub fn edge_count(&self) -> usize {
497 self.edge_count
498 }
499}
500#[derive(Debug, Clone)]
502pub struct PipelineConfig {
503 pub opt_level: OptLevel,
505 pub target: CodegenTarget,
507 pub debug: bool,
509 pub emit_ir: bool,
511 pub passes: Vec<PassId>,
513 pub max_iterations: usize,
515 pub emit_comments: bool,
517}
518impl PipelineConfig {
519 pub fn effective_passes(&self) -> Vec<PassId> {
521 if self.passes.is_empty() {
522 self.opt_level.default_passes()
523 } else {
524 self.passes.clone()
525 }
526 }
527 pub fn effective_max_iterations(&self) -> usize {
529 if self.max_iterations > 0 {
530 self.max_iterations
531 } else {
532 self.opt_level.max_iterations()
533 }
534 }
535}
536#[allow(dead_code)]
538#[derive(Debug, Clone)]
539pub struct PipeX2PassConfig {
540 pub name: String,
541 pub phase: PipeX2PassPhase,
542 pub enabled: bool,
543 pub max_iterations: usize,
544 pub debug: u32,
545 pub timeout_ms: Option<u64>,
546}
547impl PipeX2PassConfig {
548 #[allow(dead_code)]
549 pub fn new(name: impl Into<String>) -> Self {
550 Self {
551 name: name.into(),
552 phase: PipeX2PassPhase::Middle,
553 enabled: true,
554 max_iterations: 100,
555 debug: 0,
556 timeout_ms: None,
557 }
558 }
559 #[allow(dead_code)]
560 pub fn with_phase(mut self, phase: PipeX2PassPhase) -> Self {
561 self.phase = phase;
562 self
563 }
564 #[allow(dead_code)]
565 pub fn with_max_iter(mut self, n: usize) -> Self {
566 self.max_iterations = n;
567 self
568 }
569 #[allow(dead_code)]
570 pub fn with_debug(mut self, d: u32) -> Self {
571 self.debug = d;
572 self
573 }
574 #[allow(dead_code)]
575 pub fn disabled(mut self) -> Self {
576 self.enabled = false;
577 self
578 }
579 #[allow(dead_code)]
580 pub fn with_timeout(mut self, ms: u64) -> Self {
581 self.timeout_ms = Some(ms);
582 self
583 }
584 #[allow(dead_code)]
585 pub fn is_debug_enabled(&self) -> bool {
586 self.debug > 0
587 }
588}
589#[allow(dead_code)]
591#[derive(Debug, Clone, Default)]
592pub struct PipeX2PassStats {
593 pub iterations: usize,
594 pub changed: bool,
595 pub nodes_visited: usize,
596 pub nodes_modified: usize,
597 pub time_ms: u64,
598 pub memory_bytes: usize,
599 pub errors: usize,
600}
601impl PipeX2PassStats {
602 #[allow(dead_code)]
603 pub fn new() -> Self {
604 Self::default()
605 }
606 #[allow(dead_code)]
607 pub fn visit(&mut self) {
608 self.nodes_visited += 1;
609 }
610 #[allow(dead_code)]
611 pub fn modify(&mut self) {
612 self.nodes_modified += 1;
613 self.changed = true;
614 }
615 #[allow(dead_code)]
616 pub fn iterate(&mut self) {
617 self.iterations += 1;
618 }
619 #[allow(dead_code)]
620 pub fn error(&mut self) {
621 self.errors += 1;
622 }
623 #[allow(dead_code)]
624 pub fn efficiency(&self) -> f64 {
625 if self.nodes_visited == 0 {
626 0.0
627 } else {
628 self.nodes_modified as f64 / self.nodes_visited as f64
629 }
630 }
631 #[allow(dead_code)]
632 pub fn merge(&mut self, o: &PipeX2PassStats) {
633 self.iterations += o.iterations;
634 self.changed |= o.changed;
635 self.nodes_visited += o.nodes_visited;
636 self.nodes_modified += o.nodes_modified;
637 self.time_ms += o.time_ms;
638 self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
639 self.errors += o.errors;
640 }
641}
642#[allow(dead_code)]
643pub struct PipePassRegistry {
644 pub(crate) configs: Vec<PipePassConfig>,
645 pub(crate) stats: std::collections::HashMap<String, PipePassStats>,
646}
647impl PipePassRegistry {
648 #[allow(dead_code)]
649 pub fn new() -> Self {
650 PipePassRegistry {
651 configs: Vec::new(),
652 stats: std::collections::HashMap::new(),
653 }
654 }
655 #[allow(dead_code)]
656 pub fn register(&mut self, config: PipePassConfig) {
657 self.stats
658 .insert(config.pass_name.clone(), PipePassStats::new());
659 self.configs.push(config);
660 }
661 #[allow(dead_code)]
662 pub fn enabled_passes(&self) -> Vec<&PipePassConfig> {
663 self.configs.iter().filter(|c| c.enabled).collect()
664 }
665 #[allow(dead_code)]
666 pub fn get_stats(&self, name: &str) -> Option<&PipePassStats> {
667 self.stats.get(name)
668 }
669 #[allow(dead_code)]
670 pub fn total_passes(&self) -> usize {
671 self.configs.len()
672 }
673 #[allow(dead_code)]
674 pub fn enabled_count(&self) -> usize {
675 self.enabled_passes().len()
676 }
677 #[allow(dead_code)]
678 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
679 if let Some(stats) = self.stats.get_mut(name) {
680 stats.record_run(changes, time_ms, iter);
681 }
682 }
683}
684#[allow(dead_code)]
686#[derive(Debug, Default)]
687pub struct PipeX2PassRegistry {
688 pub(crate) configs: Vec<PipeX2PassConfig>,
689 pub(crate) stats: Vec<PipeX2PassStats>,
690}
691impl PipeX2PassRegistry {
692 #[allow(dead_code)]
693 pub fn new() -> Self {
694 Self::default()
695 }
696 #[allow(dead_code)]
697 pub fn register(&mut self, c: PipeX2PassConfig) {
698 self.stats.push(PipeX2PassStats::new());
699 self.configs.push(c);
700 }
701 #[allow(dead_code)]
702 pub fn len(&self) -> usize {
703 self.configs.len()
704 }
705 #[allow(dead_code)]
706 pub fn is_empty(&self) -> bool {
707 self.configs.is_empty()
708 }
709 #[allow(dead_code)]
710 pub fn get(&self, i: usize) -> Option<&PipeX2PassConfig> {
711 self.configs.get(i)
712 }
713 #[allow(dead_code)]
714 pub fn get_stats(&self, i: usize) -> Option<&PipeX2PassStats> {
715 self.stats.get(i)
716 }
717 #[allow(dead_code)]
718 pub fn enabled_passes(&self) -> Vec<&PipeX2PassConfig> {
719 self.configs.iter().filter(|c| c.enabled).collect()
720 }
721 #[allow(dead_code)]
722 pub fn passes_in_phase(&self, ph: &PipeX2PassPhase) -> Vec<&PipeX2PassConfig> {
723 self.configs
724 .iter()
725 .filter(|c| c.enabled && &c.phase == ph)
726 .collect()
727 }
728 #[allow(dead_code)]
729 pub fn total_nodes_visited(&self) -> usize {
730 self.stats.iter().map(|s| s.nodes_visited).sum()
731 }
732 #[allow(dead_code)]
733 pub fn any_changed(&self) -> bool {
734 self.stats.iter().any(|s| s.changed)
735 }
736}
737#[allow(dead_code)]
739#[derive(Debug, Clone)]
740pub struct PipeX2Worklist {
741 pub(crate) items: std::collections::VecDeque<usize>,
742 pub(crate) present: Vec<bool>,
743}
744impl PipeX2Worklist {
745 #[allow(dead_code)]
746 pub fn new(capacity: usize) -> Self {
747 Self {
748 items: std::collections::VecDeque::new(),
749 present: vec![false; capacity],
750 }
751 }
752 #[allow(dead_code)]
753 pub fn push(&mut self, id: usize) {
754 if id < self.present.len() && !self.present[id] {
755 self.present[id] = true;
756 self.items.push_back(id);
757 }
758 }
759 #[allow(dead_code)]
760 pub fn push_front(&mut self, id: usize) {
761 if id < self.present.len() && !self.present[id] {
762 self.present[id] = true;
763 self.items.push_front(id);
764 }
765 }
766 #[allow(dead_code)]
767 pub fn pop(&mut self) -> Option<usize> {
768 let id = self.items.pop_front()?;
769 if id < self.present.len() {
770 self.present[id] = false;
771 }
772 Some(id)
773 }
774 #[allow(dead_code)]
775 pub fn is_empty(&self) -> bool {
776 self.items.is_empty()
777 }
778 #[allow(dead_code)]
779 pub fn len(&self) -> usize {
780 self.items.len()
781 }
782 #[allow(dead_code)]
783 pub fn contains(&self, id: usize) -> bool {
784 id < self.present.len() && self.present[id]
785 }
786 #[allow(dead_code)]
787 pub fn drain_all(&mut self) -> Vec<usize> {
788 let v: Vec<usize> = self.items.drain(..).collect();
789 for &id in &v {
790 if id < self.present.len() {
791 self.present[id] = false;
792 }
793 }
794 v
795 }
796}