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