1use std::collections::HashMap;
6use std::collections::{HashSet, VecDeque};
7use std::fmt;
8
9#[allow(dead_code)]
11#[derive(Debug, Clone, Default)]
12pub struct JuliaExtConstFolder {
13 pub(super) folds: usize,
14 pub(super) failures: usize,
15 pub(super) enabled: bool,
16}
17impl JuliaExtConstFolder {
18 #[allow(dead_code)]
19 pub fn new() -> Self {
20 Self {
21 folds: 0,
22 failures: 0,
23 enabled: true,
24 }
25 }
26 #[allow(dead_code)]
27 pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
28 self.folds += 1;
29 a.checked_add(b)
30 }
31 #[allow(dead_code)]
32 pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
33 self.folds += 1;
34 a.checked_sub(b)
35 }
36 #[allow(dead_code)]
37 pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
38 self.folds += 1;
39 a.checked_mul(b)
40 }
41 #[allow(dead_code)]
42 pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
43 if b == 0 {
44 self.failures += 1;
45 None
46 } else {
47 self.folds += 1;
48 a.checked_div(b)
49 }
50 }
51 #[allow(dead_code)]
52 pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
53 if b == 0 {
54 self.failures += 1;
55 None
56 } else {
57 self.folds += 1;
58 a.checked_rem(b)
59 }
60 }
61 #[allow(dead_code)]
62 pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
63 self.folds += 1;
64 a.checked_neg()
65 }
66 #[allow(dead_code)]
67 pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
68 if s >= 64 {
69 self.failures += 1;
70 None
71 } else {
72 self.folds += 1;
73 a.checked_shl(s)
74 }
75 }
76 #[allow(dead_code)]
77 pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
78 if s >= 64 {
79 self.failures += 1;
80 None
81 } else {
82 self.folds += 1;
83 a.checked_shr(s)
84 }
85 }
86 #[allow(dead_code)]
87 pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
88 self.folds += 1;
89 a & b
90 }
91 #[allow(dead_code)]
92 pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
93 self.folds += 1;
94 a | b
95 }
96 #[allow(dead_code)]
97 pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
98 self.folds += 1;
99 a ^ b
100 }
101 #[allow(dead_code)]
102 pub fn not_i64(&mut self, a: i64) -> i64 {
103 self.folds += 1;
104 !a
105 }
106 #[allow(dead_code)]
107 pub fn fold_count(&self) -> usize {
108 self.folds
109 }
110 #[allow(dead_code)]
111 pub fn failure_count(&self) -> usize {
112 self.failures
113 }
114 #[allow(dead_code)]
115 pub fn enable(&mut self) {
116 self.enabled = true;
117 }
118 #[allow(dead_code)]
119 pub fn disable(&mut self) {
120 self.enabled = false;
121 }
122 #[allow(dead_code)]
123 pub fn is_enabled(&self) -> bool {
124 self.enabled
125 }
126}
127#[derive(Debug, Clone, PartialEq)]
129pub enum JuliaStmt {
130 Expr(JuliaExpr),
132 Assign(JuliaExpr, JuliaExpr),
134 AugAssign(JuliaExpr, String, JuliaExpr),
136 Local(String, Option<JuliaType>, Option<JuliaExpr>),
138 Global(String),
140 Const(String, Option<JuliaType>, JuliaExpr),
142 Return(Option<JuliaExpr>),
144 Break,
146 Continue,
148 If {
150 cond: JuliaExpr,
151 then_body: Vec<JuliaStmt>,
152 elseif_branches: Vec<(JuliaExpr, Vec<JuliaStmt>)>,
153 else_body: Option<Vec<JuliaStmt>>,
154 },
155 For {
157 vars: Vec<String>,
158 iter: JuliaExpr,
159 body: Vec<JuliaStmt>,
160 },
161 While {
163 cond: JuliaExpr,
164 body: Vec<JuliaStmt>,
165 },
166 TryCatch {
168 try_body: Vec<JuliaStmt>,
169 catch_var: Option<String>,
170 catch_body: Vec<JuliaStmt>,
171 finally_body: Option<Vec<JuliaStmt>>,
172 },
173 FunctionDef(JuliaFunction),
175 StructDef(JuliaStruct),
177 AbstractTypeDef {
179 name: String,
180 type_params: Vec<String>,
181 supertype: Option<String>,
182 },
183 PrimitiveTypeDef {
185 name: String,
186 bits: u32,
187 supertype: Option<String>,
188 },
189 ModuleDef(JuliaModule),
191 Using(Vec<String>),
193 Import(String, Vec<String>),
195 Export(Vec<String>),
197 Include(String),
199 MacroDef {
201 name: String,
202 params: Vec<String>,
203 body: Vec<JuliaStmt>,
204 },
205 Comment(String),
207 Blank,
209}
210#[derive(Debug, Clone, PartialEq)]
212pub enum JuliaStringPart {
213 Text(String),
215 Expr(JuliaExpr),
217}
218#[allow(dead_code)]
219#[derive(Debug, Clone)]
220pub struct JulPassConfig {
221 pub phase: JulPassPhase,
222 pub enabled: bool,
223 pub max_iterations: u32,
224 pub debug_output: bool,
225 pub pass_name: String,
226}
227impl JulPassConfig {
228 #[allow(dead_code)]
229 pub fn new(name: impl Into<String>, phase: JulPassPhase) -> Self {
230 JulPassConfig {
231 phase,
232 enabled: true,
233 max_iterations: 10,
234 debug_output: false,
235 pass_name: name.into(),
236 }
237 }
238 #[allow(dead_code)]
239 pub fn disabled(mut self) -> Self {
240 self.enabled = false;
241 self
242 }
243 #[allow(dead_code)]
244 pub fn with_debug(mut self) -> Self {
245 self.debug_output = true;
246 self
247 }
248 #[allow(dead_code)]
249 pub fn max_iter(mut self, n: u32) -> Self {
250 self.max_iterations = n;
251 self
252 }
253}
254#[allow(dead_code)]
256#[derive(Debug, Clone)]
257pub struct JuliaExtDomTree {
258 pub(super) idom: Vec<Option<usize>>,
259 pub(super) children: Vec<Vec<usize>>,
260 pub(super) depth: Vec<usize>,
261}
262impl JuliaExtDomTree {
263 #[allow(dead_code)]
264 pub fn new(n: usize) -> Self {
265 Self {
266 idom: vec![None; n],
267 children: vec![Vec::new(); n],
268 depth: vec![0; n],
269 }
270 }
271 #[allow(dead_code)]
272 pub fn set_idom(&mut self, node: usize, dom: usize) {
273 if node < self.idom.len() {
274 self.idom[node] = Some(dom);
275 if dom < self.children.len() {
276 self.children[dom].push(node);
277 }
278 self.depth[node] = if dom < self.depth.len() {
279 self.depth[dom] + 1
280 } else {
281 1
282 };
283 }
284 }
285 #[allow(dead_code)]
286 pub fn dominates(&self, a: usize, mut b: usize) -> bool {
287 if a == b {
288 return true;
289 }
290 let n = self.idom.len();
291 for _ in 0..n {
292 match self.idom.get(b).copied().flatten() {
293 None => return false,
294 Some(p) if p == a => return true,
295 Some(p) if p == b => return false,
296 Some(p) => b = p,
297 }
298 }
299 false
300 }
301 #[allow(dead_code)]
302 pub fn children_of(&self, n: usize) -> &[usize] {
303 self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
304 }
305 #[allow(dead_code)]
306 pub fn depth_of(&self, n: usize) -> usize {
307 self.depth.get(n).copied().unwrap_or(0)
308 }
309 #[allow(dead_code)]
310 pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
311 let n = self.idom.len();
312 for _ in 0..(2 * n) {
313 if a == b {
314 return a;
315 }
316 if self.depth_of(a) > self.depth_of(b) {
317 a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
318 } else {
319 b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
320 }
321 }
322 0
323 }
324}
325#[derive(Debug, Clone, PartialEq)]
327pub struct JuliaStruct {
328 pub name: String,
330 pub type_params: Vec<String>,
332 pub supertype: Option<String>,
334 pub is_mutable: bool,
336 pub fields: Vec<(String, Option<JuliaType>, Option<JuliaExpr>)>,
338 pub inner_constructors: Vec<JuliaFunction>,
340 pub doc: Option<String>,
342}
343impl JuliaStruct {
344 pub fn new(name: impl Into<String>) -> Self {
346 JuliaStruct {
347 name: name.into(),
348 type_params: vec![],
349 supertype: None,
350 is_mutable: false,
351 fields: vec![],
352 inner_constructors: vec![],
353 doc: None,
354 }
355 }
356 pub fn mutable(name: impl Into<String>) -> Self {
358 let mut s = JuliaStruct::new(name);
359 s.is_mutable = true;
360 s
361 }
362 pub fn with_field(mut self, name: impl Into<String>, ty: JuliaType) -> Self {
364 self.fields.push((name.into(), Some(ty), None));
365 self
366 }
367 pub fn with_supertype(mut self, supertype: impl Into<String>) -> Self {
369 self.supertype = Some(supertype.into());
370 self
371 }
372 pub fn with_type_param(mut self, param: impl Into<String>) -> Self {
374 self.type_params.push(param.into());
375 self
376 }
377}
378#[derive(Debug, Clone, PartialEq)]
380pub struct JuliaParam {
381 pub name: String,
383 pub ty: Option<JuliaType>,
385 pub default: Option<JuliaExpr>,
387 pub is_keyword: bool,
389 pub is_splat: bool,
391}
392impl JuliaParam {
393 pub fn simple(name: impl Into<String>) -> Self {
395 JuliaParam {
396 name: name.into(),
397 ty: None,
398 default: None,
399 is_keyword: false,
400 is_splat: false,
401 }
402 }
403 pub fn typed(name: impl Into<String>, ty: JuliaType) -> Self {
405 JuliaParam {
406 name: name.into(),
407 ty: Some(ty),
408 default: None,
409 is_keyword: false,
410 is_splat: false,
411 }
412 }
413 pub fn keyword(name: impl Into<String>, default: JuliaExpr) -> Self {
415 JuliaParam {
416 name: name.into(),
417 ty: None,
418 default: Some(default),
419 is_keyword: true,
420 is_splat: false,
421 }
422 }
423}
424#[allow(dead_code)]
425#[derive(Debug, Clone, Default)]
426pub struct JulPassStats {
427 pub total_runs: u32,
428 pub successful_runs: u32,
429 pub total_changes: u64,
430 pub time_ms: u64,
431 pub iterations_used: u32,
432}
433impl JulPassStats {
434 #[allow(dead_code)]
435 pub fn new() -> Self {
436 Self::default()
437 }
438 #[allow(dead_code)]
439 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
440 self.total_runs += 1;
441 self.successful_runs += 1;
442 self.total_changes += changes;
443 self.time_ms += time_ms;
444 self.iterations_used = iterations;
445 }
446 #[allow(dead_code)]
447 pub fn average_changes_per_run(&self) -> f64 {
448 if self.total_runs == 0 {
449 return 0.0;
450 }
451 self.total_changes as f64 / self.total_runs as f64
452 }
453 #[allow(dead_code)]
454 pub fn success_rate(&self) -> f64 {
455 if self.total_runs == 0 {
456 return 0.0;
457 }
458 self.successful_runs as f64 / self.total_runs as f64
459 }
460 #[allow(dead_code)]
461 pub fn format_summary(&self) -> String {
462 format!(
463 "Runs: {}/{}, Changes: {}, Time: {}ms",
464 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
465 )
466 }
467}
468#[allow(dead_code)]
469#[derive(Debug, Clone)]
470pub struct JulAnalysisCache {
471 pub(super) entries: std::collections::HashMap<String, JulCacheEntry>,
472 pub(super) max_size: usize,
473 pub(super) hits: u64,
474 pub(super) misses: u64,
475}
476impl JulAnalysisCache {
477 #[allow(dead_code)]
478 pub fn new(max_size: usize) -> Self {
479 JulAnalysisCache {
480 entries: std::collections::HashMap::new(),
481 max_size,
482 hits: 0,
483 misses: 0,
484 }
485 }
486 #[allow(dead_code)]
487 pub fn get(&mut self, key: &str) -> Option<&JulCacheEntry> {
488 if self.entries.contains_key(key) {
489 self.hits += 1;
490 self.entries.get(key)
491 } else {
492 self.misses += 1;
493 None
494 }
495 }
496 #[allow(dead_code)]
497 pub fn insert(&mut self, key: String, data: Vec<u8>) {
498 if self.entries.len() >= self.max_size {
499 if let Some(oldest) = self.entries.keys().next().cloned() {
500 self.entries.remove(&oldest);
501 }
502 }
503 self.entries.insert(
504 key.clone(),
505 JulCacheEntry {
506 key,
507 data,
508 timestamp: 0,
509 valid: true,
510 },
511 );
512 }
513 #[allow(dead_code)]
514 pub fn invalidate(&mut self, key: &str) {
515 if let Some(entry) = self.entries.get_mut(key) {
516 entry.valid = false;
517 }
518 }
519 #[allow(dead_code)]
520 pub fn clear(&mut self) {
521 self.entries.clear();
522 }
523 #[allow(dead_code)]
524 pub fn hit_rate(&self) -> f64 {
525 let total = self.hits + self.misses;
526 if total == 0 {
527 return 0.0;
528 }
529 self.hits as f64 / total as f64
530 }
531 #[allow(dead_code)]
532 pub fn size(&self) -> usize {
533 self.entries.len()
534 }
535}
536#[allow(dead_code)]
538#[derive(Debug, Clone)]
539pub struct JuliaExtPassConfig {
540 pub name: String,
541 pub phase: JuliaExtPassPhase,
542 pub enabled: bool,
543 pub max_iterations: usize,
544 pub debug: u32,
545 pub timeout_ms: Option<u64>,
546}
547impl JuliaExtPassConfig {
548 #[allow(dead_code)]
549 pub fn new(name: impl Into<String>) -> Self {
550 Self {
551 name: name.into(),
552 phase: JuliaExtPassPhase::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: JuliaExtPassPhase) -> 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}
589pub struct JuliaExprDisplay<'a>(pub(super) &'a JuliaExpr);
591#[allow(dead_code)]
592#[derive(Debug, Clone)]
593pub struct JulDominatorTree {
594 pub idom: Vec<Option<u32>>,
595 pub dom_children: Vec<Vec<u32>>,
596 pub dom_depth: Vec<u32>,
597}
598impl JulDominatorTree {
599 #[allow(dead_code)]
600 pub fn new(size: usize) -> Self {
601 JulDominatorTree {
602 idom: vec![None; size],
603 dom_children: vec![Vec::new(); size],
604 dom_depth: vec![0; size],
605 }
606 }
607 #[allow(dead_code)]
608 pub fn set_idom(&mut self, node: usize, idom: u32) {
609 self.idom[node] = Some(idom);
610 }
611 #[allow(dead_code)]
612 pub fn dominates(&self, a: usize, b: usize) -> bool {
613 if a == b {
614 return true;
615 }
616 let mut cur = b;
617 loop {
618 match self.idom[cur] {
619 Some(parent) if parent as usize == a => return true,
620 Some(parent) if parent as usize == cur => return false,
621 Some(parent) => cur = parent as usize,
622 None => return false,
623 }
624 }
625 }
626 #[allow(dead_code)]
627 pub fn depth(&self, node: usize) -> u32 {
628 self.dom_depth.get(node).copied().unwrap_or(0)
629 }
630}
631#[allow(dead_code)]
633#[derive(Debug, Clone, Default)]
634pub struct JuliaExtLiveness {
635 pub live_in: Vec<Vec<usize>>,
636 pub live_out: Vec<Vec<usize>>,
637 pub defs: Vec<Vec<usize>>,
638 pub uses: Vec<Vec<usize>>,
639}
640impl JuliaExtLiveness {
641 #[allow(dead_code)]
642 pub fn new(n: usize) -> Self {
643 Self {
644 live_in: vec![Vec::new(); n],
645 live_out: vec![Vec::new(); n],
646 defs: vec![Vec::new(); n],
647 uses: vec![Vec::new(); n],
648 }
649 }
650 #[allow(dead_code)]
651 pub fn live_in(&self, b: usize, v: usize) -> bool {
652 self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
653 }
654 #[allow(dead_code)]
655 pub fn live_out(&self, b: usize, v: usize) -> bool {
656 self.live_out
657 .get(b)
658 .map(|s| s.contains(&v))
659 .unwrap_or(false)
660 }
661 #[allow(dead_code)]
662 pub fn add_def(&mut self, b: usize, v: usize) {
663 if let Some(s) = self.defs.get_mut(b) {
664 if !s.contains(&v) {
665 s.push(v);
666 }
667 }
668 }
669 #[allow(dead_code)]
670 pub fn add_use(&mut self, b: usize, v: usize) {
671 if let Some(s) = self.uses.get_mut(b) {
672 if !s.contains(&v) {
673 s.push(v);
674 }
675 }
676 }
677 #[allow(dead_code)]
678 pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
679 self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
680 }
681 #[allow(dead_code)]
682 pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
683 self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
684 }
685}
686#[derive(Debug, Clone)]
688pub struct DispatchTable {
689 pub name: String,
691 pub methods: Vec<JuliaFunction>,
693}
694impl DispatchTable {
695 pub fn new(name: impl Into<String>) -> Self {
697 DispatchTable {
698 name: name.into(),
699 methods: vec![],
700 }
701 }
702 pub fn add_method(&mut self, method: JuliaFunction) {
704 self.methods.push(method);
705 }
706 pub fn num_methods(&self) -> usize {
708 self.methods.len()
709 }
710 pub fn find_method(&self, arg_types: &[JuliaType]) -> Option<&JuliaFunction> {
713 for method in &self.methods {
714 if method.params.len() == arg_types.len() {
715 let matches = method
716 .params
717 .iter()
718 .zip(arg_types.iter())
719 .all(|(p, t)| p.ty.as_ref().is_none_or(|pt| pt == t));
720 if matches {
721 return Some(method);
722 }
723 }
724 }
725 self.methods
726 .iter()
727 .find(|m| m.params.iter().all(|p| p.ty.is_none()))
728 }
729}
730#[allow(dead_code)]
732#[derive(Debug)]
733pub struct JuliaExtCache {
734 pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
735 pub(super) cap: usize,
736 pub(super) total_hits: u64,
737 pub(super) total_misses: u64,
738}
739impl JuliaExtCache {
740 #[allow(dead_code)]
741 pub fn new(cap: usize) -> Self {
742 Self {
743 entries: Vec::new(),
744 cap,
745 total_hits: 0,
746 total_misses: 0,
747 }
748 }
749 #[allow(dead_code)]
750 pub fn get(&mut self, key: u64) -> Option<&[u8]> {
751 for e in self.entries.iter_mut() {
752 if e.0 == key && e.2 {
753 e.3 += 1;
754 self.total_hits += 1;
755 return Some(&e.1);
756 }
757 }
758 self.total_misses += 1;
759 None
760 }
761 #[allow(dead_code)]
762 pub fn put(&mut self, key: u64, data: Vec<u8>) {
763 if self.entries.len() >= self.cap {
764 self.entries.retain(|e| e.2);
765 if self.entries.len() >= self.cap {
766 self.entries.remove(0);
767 }
768 }
769 self.entries.push((key, data, true, 0));
770 }
771 #[allow(dead_code)]
772 pub fn invalidate(&mut self) {
773 for e in self.entries.iter_mut() {
774 e.2 = false;
775 }
776 }
777 #[allow(dead_code)]
778 pub fn hit_rate(&self) -> f64 {
779 let t = self.total_hits + self.total_misses;
780 if t == 0 {
781 0.0
782 } else {
783 self.total_hits as f64 / t as f64
784 }
785 }
786 #[allow(dead_code)]
787 pub fn live_count(&self) -> usize {
788 self.entries.iter().filter(|e| e.2).count()
789 }
790}
791pub struct JuliaBackend {
793 pub(super) indent: usize,
795 pub(super) output: String,
797 pub(super) dispatch_tables: HashMap<String, DispatchTable>,
799}
800impl JuliaBackend {
801 pub fn new() -> Self {
803 JuliaBackend {
804 indent: 0,
805 output: String::new(),
806 dispatch_tables: HashMap::new(),
807 }
808 }
809 pub(super) fn indent_str(&self) -> String {
811 " ".repeat(self.indent)
812 }
813 pub(super) fn push_line(&mut self, line: &str) {
815 let indent = self.indent_str();
816 self.output.push_str(&indent);
817 self.output.push_str(line);
818 self.output.push('\n');
819 }
820 pub(super) fn push_blank(&mut self) {
822 self.output.push('\n');
823 }
824 pub fn register_method(&mut self, func: JuliaFunction) {
826 let table = self
827 .dispatch_tables
828 .entry(func.name.clone())
829 .or_insert_with(|| DispatchTable::new(func.name.clone()));
830 table.add_method(func);
831 }
832 pub fn emit_expr(&self, expr: &JuliaExpr) -> String {
834 let mut s = String::new();
835 struct FmtStr<'a>(&'a mut String);
836 impl<'a> fmt::Write for FmtStr<'a> {
837 fn write_str(&mut self, s: &str) -> fmt::Result {
838 self.0.push_str(s);
839 Ok(())
840 }
841 }
842 use std::fmt::Write as FmtWrite;
843 let _ = write!(FmtStr(&mut s), "{}", JuliaExprDisplay(expr));
844 s
845 }
846 pub fn emit_type(&self, ty: &JuliaType) -> String {
848 ty.to_string()
849 }
850 pub fn emit_function(&mut self, func: &JuliaFunction) {
852 if let Some(ref doc) = func.doc {
853 self.push_line("\"\"\"");
854 for line in doc.lines() {
855 self.push_line(line);
856 }
857 self.push_line("\"\"\"");
858 }
859 let sig = func.emit_signature();
860 self.push_line(&sig);
861 self.indent += 1;
862 for stmt in &func.body {
863 self.emit_stmt(stmt);
864 }
865 self.indent -= 1;
866 self.push_line("end");
867 }
868 pub fn emit_struct(&mut self, s: &JuliaStruct) {
870 if let Some(ref doc) = s.doc {
871 self.push_line("\"\"\"");
872 for line in doc.lines() {
873 self.push_line(line);
874 }
875 self.push_line("\"\"\"");
876 }
877 let kw = if s.is_mutable {
878 "mutable struct"
879 } else {
880 "struct"
881 };
882 let mut header = format!("{} {}", kw, s.name);
883 if !s.type_params.is_empty() {
884 header.push('{');
885 header.push_str(&s.type_params.join(", "));
886 header.push('}');
887 }
888 if let Some(ref sup) = s.supertype {
889 header.push_str(&format!(" <: {}", sup));
890 }
891 self.push_line(&header);
892 self.indent += 1;
893 for (name, ty, default) in &s.fields {
894 let mut field_str = name.clone();
895 if let Some(ref t) = ty {
896 field_str.push_str(&format!("::{}", t));
897 }
898 if let Some(ref d) = default {
899 field_str.push_str(&format!(" = {}", self.emit_expr(d)));
900 }
901 self.push_line(&field_str);
902 }
903 for ctor in &s.inner_constructors {
904 self.emit_function(ctor);
905 }
906 self.indent -= 1;
907 self.push_line("end");
908 }
909 pub fn emit_module(&mut self, m: &JuliaModule) {
911 let kw = if m.is_bare { "baremodule" } else { "module" };
912 self.push_line(&format!("{} {}", kw, m.name));
913 self.push_blank();
914 self.indent += 1;
915 for mods in &m.usings {
916 self.push_line(&format!("using {}", mods.join(", ")));
917 }
918 for (module, syms) in &m.imports {
919 if syms.is_empty() {
920 self.push_line(&format!("import {}", module));
921 } else {
922 self.push_line(&format!("import {}: {}", module, syms.join(", ")));
923 }
924 }
925 if !m.exports.is_empty() {
926 self.push_line(&format!("export {}", m.exports.join(", ")));
927 }
928 if !m.usings.is_empty() || !m.imports.is_empty() || !m.exports.is_empty() {
929 self.push_blank();
930 }
931 for stmt in &m.body {
932 self.emit_stmt(stmt);
933 }
934 self.indent -= 1;
935 self.push_line("end");
936 }
937 pub fn emit_stmt(&mut self, stmt: &JuliaStmt) {
939 match stmt {
940 JuliaStmt::Expr(e) => {
941 let s = self.emit_expr(e);
942 self.push_line(&s);
943 }
944 JuliaStmt::Assign(lhs, rhs) => {
945 let l = self.emit_expr(lhs);
946 let r = self.emit_expr(rhs);
947 self.push_line(&format!("{} = {}", l, r));
948 }
949 JuliaStmt::AugAssign(lhs, op, rhs) => {
950 let l = self.emit_expr(lhs);
951 let r = self.emit_expr(rhs);
952 self.push_line(&format!("{} {}= {}", l, op, r));
953 }
954 JuliaStmt::Local(name, ty, init) => {
955 let mut s = format!("local {}", name);
956 if let Some(ref t) = ty {
957 s.push_str(&format!("::{}", t));
958 }
959 if let Some(ref e) = init {
960 s.push_str(&format!(" = {}", self.emit_expr(e)));
961 }
962 self.push_line(&s);
963 }
964 JuliaStmt::Global(name) => {
965 self.push_line(&format!("global {}", name));
966 }
967 JuliaStmt::Const(name, ty, val) => {
968 let mut s = format!("const {}", name);
969 if let Some(ref t) = ty {
970 s.push_str(&format!("::{}", t));
971 }
972 s.push_str(&format!(" = {}", self.emit_expr(val)));
973 self.push_line(&s);
974 }
975 JuliaStmt::Return(Some(e)) => {
976 let s = self.emit_expr(e);
977 self.push_line(&format!("return {}", s));
978 }
979 JuliaStmt::Return(None) => {
980 self.push_line("return");
981 }
982 JuliaStmt::Break => self.push_line("break"),
983 JuliaStmt::Continue => self.push_line("continue"),
984 JuliaStmt::If {
985 cond,
986 then_body,
987 elseif_branches,
988 else_body,
989 } => {
990 let c = self.emit_expr(cond);
991 self.push_line(&format!("if {}", c));
992 self.indent += 1;
993 for s in then_body {
994 self.emit_stmt(s);
995 }
996 self.indent -= 1;
997 for (econd, ebody) in elseif_branches {
998 let ec = self.emit_expr(econd);
999 self.push_line(&format!("elseif {}", ec));
1000 self.indent += 1;
1001 for s in ebody {
1002 self.emit_stmt(s);
1003 }
1004 self.indent -= 1;
1005 }
1006 if let Some(ref eb) = else_body {
1007 self.push_line("else");
1008 self.indent += 1;
1009 for s in eb {
1010 self.emit_stmt(s);
1011 }
1012 self.indent -= 1;
1013 }
1014 self.push_line("end");
1015 }
1016 JuliaStmt::For { vars, iter, body } => {
1017 let iter_s = self.emit_expr(iter);
1018 self.push_line(&format!("for {} in {}", vars.join(", "), iter_s));
1019 self.indent += 1;
1020 for s in body {
1021 self.emit_stmt(s);
1022 }
1023 self.indent -= 1;
1024 self.push_line("end");
1025 }
1026 JuliaStmt::While { cond, body } => {
1027 let c = self.emit_expr(cond);
1028 self.push_line(&format!("while {}", c));
1029 self.indent += 1;
1030 for s in body {
1031 self.emit_stmt(s);
1032 }
1033 self.indent -= 1;
1034 self.push_line("end");
1035 }
1036 JuliaStmt::TryCatch {
1037 try_body,
1038 catch_var,
1039 catch_body,
1040 finally_body,
1041 } => {
1042 self.push_line("try");
1043 self.indent += 1;
1044 for s in try_body {
1045 self.emit_stmt(s);
1046 }
1047 self.indent -= 1;
1048 if let Some(ref cv) = catch_var {
1049 self.push_line(&format!("catch {}", cv));
1050 } else {
1051 self.push_line("catch");
1052 }
1053 self.indent += 1;
1054 for s in catch_body {
1055 self.emit_stmt(s);
1056 }
1057 self.indent -= 1;
1058 if let Some(ref fb) = finally_body {
1059 self.push_line("finally");
1060 self.indent += 1;
1061 for s in fb {
1062 self.emit_stmt(s);
1063 }
1064 self.indent -= 1;
1065 }
1066 self.push_line("end");
1067 }
1068 JuliaStmt::FunctionDef(f) => {
1069 self.push_blank();
1070 self.emit_function(f);
1071 self.push_blank();
1072 }
1073 JuliaStmt::StructDef(s) => {
1074 self.push_blank();
1075 self.emit_struct(s);
1076 self.push_blank();
1077 }
1078 JuliaStmt::AbstractTypeDef {
1079 name,
1080 type_params,
1081 supertype,
1082 } => {
1083 let mut s = format!("abstract type {}", name);
1084 if !type_params.is_empty() {
1085 s.push('{');
1086 s.push_str(&type_params.join(", "));
1087 s.push('}');
1088 }
1089 if let Some(ref sup) = supertype {
1090 s.push_str(&format!(" <: {}", sup));
1091 }
1092 s.push_str(" end");
1093 self.push_line(&s);
1094 }
1095 JuliaStmt::PrimitiveTypeDef {
1096 name,
1097 bits,
1098 supertype,
1099 } => {
1100 let mut s = format!("primitive type {} {}", name, bits);
1101 if let Some(ref sup) = supertype {
1102 s.push_str(&format!(" <: {}", sup));
1103 }
1104 s.push_str(" end");
1105 self.push_line(&s);
1106 }
1107 JuliaStmt::ModuleDef(m) => {
1108 self.push_blank();
1109 self.emit_module(m);
1110 self.push_blank();
1111 }
1112 JuliaStmt::Using(mods) => {
1113 self.push_line(&format!("using {}", mods.join(", ")));
1114 }
1115 JuliaStmt::Import(module, syms) => {
1116 if syms.is_empty() {
1117 self.push_line(&format!("import {}", module));
1118 } else {
1119 self.push_line(&format!("import {}: {}", module, syms.join(", ")));
1120 }
1121 }
1122 JuliaStmt::Export(syms) => {
1123 self.push_line(&format!("export {}", syms.join(", ")));
1124 }
1125 JuliaStmt::Include(path) => {
1126 self.push_line(&format!("include(\"{}\")", path));
1127 }
1128 JuliaStmt::MacroDef { name, params, body } => {
1129 self.push_line(&format!("macro {}({})", name, params.join(", ")));
1130 self.indent += 1;
1131 for s in body {
1132 self.emit_stmt(s);
1133 }
1134 self.indent -= 1;
1135 self.push_line("end");
1136 }
1137 JuliaStmt::Comment(s) => {
1138 self.push_line(&format!("# {}", s));
1139 }
1140 JuliaStmt::Blank => {
1141 self.push_blank();
1142 }
1143 }
1144 }
1145 pub fn emit_dispatch_tables(&mut self) {
1147 let names: Vec<String> = self.dispatch_tables.keys().cloned().collect();
1148 for name in names {
1149 let methods: Vec<JuliaFunction> = self.dispatch_tables[&name].methods.clone();
1150 self.push_line(&format!(
1151 "# Multiple dispatch: {} methods for '{}'",
1152 methods.len(),
1153 name
1154 ));
1155 for method in methods {
1156 self.emit_function(&method);
1157 self.push_blank();
1158 }
1159 }
1160 }
1161 pub fn take_output(&mut self) -> String {
1163 std::mem::take(&mut self.output)
1164 }
1165 pub fn output(&self) -> &str {
1167 &self.output
1168 }
1169}
1170#[derive(Debug, Clone, PartialEq)]
1172pub struct JuliaModule {
1173 pub name: String,
1175 pub is_bare: bool,
1177 pub usings: Vec<Vec<String>>,
1179 pub imports: Vec<(String, Vec<String>)>,
1181 pub exports: Vec<String>,
1183 pub body: Vec<JuliaStmt>,
1185}
1186impl JuliaModule {
1187 pub fn new(name: impl Into<String>) -> Self {
1189 JuliaModule {
1190 name: name.into(),
1191 is_bare: false,
1192 usings: vec![],
1193 imports: vec![],
1194 exports: vec![],
1195 body: vec![],
1196 }
1197 }
1198 pub fn export(mut self, sym: impl Into<String>) -> Self {
1200 self.exports.push(sym.into());
1201 self
1202 }
1203 pub fn using(mut self, modules: Vec<String>) -> Self {
1205 self.usings.push(modules);
1206 self
1207 }
1208 pub fn push(mut self, stmt: JuliaStmt) -> Self {
1210 self.body.push(stmt);
1211 self
1212 }
1213}
1214#[allow(dead_code)]
1216#[derive(Debug, Clone)]
1217pub struct JuliaExtDepGraph {
1218 pub(super) n: usize,
1219 pub(super) adj: Vec<Vec<usize>>,
1220 pub(super) rev: Vec<Vec<usize>>,
1221 pub(super) edge_count: usize,
1222}
1223impl JuliaExtDepGraph {
1224 #[allow(dead_code)]
1225 pub fn new(n: usize) -> Self {
1226 Self {
1227 n,
1228 adj: vec![Vec::new(); n],
1229 rev: vec![Vec::new(); n],
1230 edge_count: 0,
1231 }
1232 }
1233 #[allow(dead_code)]
1234 pub fn add_edge(&mut self, from: usize, to: usize) {
1235 if from < self.n && to < self.n {
1236 if !self.adj[from].contains(&to) {
1237 self.adj[from].push(to);
1238 self.rev[to].push(from);
1239 self.edge_count += 1;
1240 }
1241 }
1242 }
1243 #[allow(dead_code)]
1244 pub fn succs(&self, n: usize) -> &[usize] {
1245 self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1246 }
1247 #[allow(dead_code)]
1248 pub fn preds(&self, n: usize) -> &[usize] {
1249 self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1250 }
1251 #[allow(dead_code)]
1252 pub fn topo_sort(&self) -> Option<Vec<usize>> {
1253 let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
1254 let mut q: std::collections::VecDeque<usize> =
1255 (0..self.n).filter(|&i| deg[i] == 0).collect();
1256 let mut out = Vec::with_capacity(self.n);
1257 while let Some(u) = q.pop_front() {
1258 out.push(u);
1259 for &v in &self.adj[u] {
1260 deg[v] -= 1;
1261 if deg[v] == 0 {
1262 q.push_back(v);
1263 }
1264 }
1265 }
1266 if out.len() == self.n {
1267 Some(out)
1268 } else {
1269 None
1270 }
1271 }
1272 #[allow(dead_code)]
1273 pub fn has_cycle(&self) -> bool {
1274 self.topo_sort().is_none()
1275 }
1276 #[allow(dead_code)]
1277 pub fn reachable(&self, start: usize) -> Vec<usize> {
1278 let mut vis = vec![false; self.n];
1279 let mut stk = vec![start];
1280 let mut out = Vec::new();
1281 while let Some(u) = stk.pop() {
1282 if u < self.n && !vis[u] {
1283 vis[u] = true;
1284 out.push(u);
1285 for &v in &self.adj[u] {
1286 if !vis[v] {
1287 stk.push(v);
1288 }
1289 }
1290 }
1291 }
1292 out
1293 }
1294 #[allow(dead_code)]
1295 pub fn scc(&self) -> Vec<Vec<usize>> {
1296 let mut visited = vec![false; self.n];
1297 let mut order = Vec::new();
1298 for i in 0..self.n {
1299 if !visited[i] {
1300 let mut stk = vec![(i, 0usize)];
1301 while let Some((u, idx)) = stk.last_mut() {
1302 if !visited[*u] {
1303 visited[*u] = true;
1304 }
1305 if *idx < self.adj[*u].len() {
1306 let v = self.adj[*u][*idx];
1307 *idx += 1;
1308 if !visited[v] {
1309 stk.push((v, 0));
1310 }
1311 } else {
1312 order.push(*u);
1313 stk.pop();
1314 }
1315 }
1316 }
1317 }
1318 let mut comp = vec![usize::MAX; self.n];
1319 let mut components: Vec<Vec<usize>> = Vec::new();
1320 for &start in order.iter().rev() {
1321 if comp[start] == usize::MAX {
1322 let cid = components.len();
1323 let mut component = Vec::new();
1324 let mut stk = vec![start];
1325 while let Some(u) = stk.pop() {
1326 if comp[u] == usize::MAX {
1327 comp[u] = cid;
1328 component.push(u);
1329 for &v in &self.rev[u] {
1330 if comp[v] == usize::MAX {
1331 stk.push(v);
1332 }
1333 }
1334 }
1335 }
1336 components.push(component);
1337 }
1338 }
1339 components
1340 }
1341 #[allow(dead_code)]
1342 pub fn node_count(&self) -> usize {
1343 self.n
1344 }
1345 #[allow(dead_code)]
1346 pub fn edge_count(&self) -> usize {
1347 self.edge_count
1348 }
1349}
1350#[allow(dead_code)]
1351#[derive(Debug, Clone)]
1352pub struct JulWorklist {
1353 pub(super) items: std::collections::VecDeque<u32>,
1354 pub(super) in_worklist: std::collections::HashSet<u32>,
1355}
1356impl JulWorklist {
1357 #[allow(dead_code)]
1358 pub fn new() -> Self {
1359 JulWorklist {
1360 items: std::collections::VecDeque::new(),
1361 in_worklist: std::collections::HashSet::new(),
1362 }
1363 }
1364 #[allow(dead_code)]
1365 pub fn push(&mut self, item: u32) -> bool {
1366 if self.in_worklist.insert(item) {
1367 self.items.push_back(item);
1368 true
1369 } else {
1370 false
1371 }
1372 }
1373 #[allow(dead_code)]
1374 pub fn pop(&mut self) -> Option<u32> {
1375 let item = self.items.pop_front()?;
1376 self.in_worklist.remove(&item);
1377 Some(item)
1378 }
1379 #[allow(dead_code)]
1380 pub fn is_empty(&self) -> bool {
1381 self.items.is_empty()
1382 }
1383 #[allow(dead_code)]
1384 pub fn len(&self) -> usize {
1385 self.items.len()
1386 }
1387 #[allow(dead_code)]
1388 pub fn contains(&self, item: u32) -> bool {
1389 self.in_worklist.contains(&item)
1390 }
1391}
1392#[allow(dead_code)]
1393#[derive(Debug, Clone, PartialEq)]
1394pub enum JulPassPhase {
1395 Analysis,
1396 Transformation,
1397 Verification,
1398 Cleanup,
1399}
1400impl JulPassPhase {
1401 #[allow(dead_code)]
1402 pub fn name(&self) -> &str {
1403 match self {
1404 JulPassPhase::Analysis => "analysis",
1405 JulPassPhase::Transformation => "transformation",
1406 JulPassPhase::Verification => "verification",
1407 JulPassPhase::Cleanup => "cleanup",
1408 }
1409 }
1410 #[allow(dead_code)]
1411 pub fn is_modifying(&self) -> bool {
1412 matches!(self, JulPassPhase::Transformation | JulPassPhase::Cleanup)
1413 }
1414}
1415#[allow(dead_code)]
1416#[derive(Debug, Clone)]
1417pub struct JulLivenessInfo {
1418 pub live_in: Vec<std::collections::HashSet<u32>>,
1419 pub live_out: Vec<std::collections::HashSet<u32>>,
1420 pub defs: Vec<std::collections::HashSet<u32>>,
1421 pub uses: Vec<std::collections::HashSet<u32>>,
1422}
1423impl JulLivenessInfo {
1424 #[allow(dead_code)]
1425 pub fn new(block_count: usize) -> Self {
1426 JulLivenessInfo {
1427 live_in: vec![std::collections::HashSet::new(); block_count],
1428 live_out: vec![std::collections::HashSet::new(); block_count],
1429 defs: vec![std::collections::HashSet::new(); block_count],
1430 uses: vec![std::collections::HashSet::new(); block_count],
1431 }
1432 }
1433 #[allow(dead_code)]
1434 pub fn add_def(&mut self, block: usize, var: u32) {
1435 if block < self.defs.len() {
1436 self.defs[block].insert(var);
1437 }
1438 }
1439 #[allow(dead_code)]
1440 pub fn add_use(&mut self, block: usize, var: u32) {
1441 if block < self.uses.len() {
1442 self.uses[block].insert(var);
1443 }
1444 }
1445 #[allow(dead_code)]
1446 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
1447 self.live_in
1448 .get(block)
1449 .map(|s| s.contains(&var))
1450 .unwrap_or(false)
1451 }
1452 #[allow(dead_code)]
1453 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
1454 self.live_out
1455 .get(block)
1456 .map(|s| s.contains(&var))
1457 .unwrap_or(false)
1458 }
1459}
1460#[allow(dead_code)]
1461pub struct JulConstantFoldingHelper;
1462impl JulConstantFoldingHelper {
1463 #[allow(dead_code)]
1464 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1465 a.checked_add(b)
1466 }
1467 #[allow(dead_code)]
1468 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1469 a.checked_sub(b)
1470 }
1471 #[allow(dead_code)]
1472 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1473 a.checked_mul(b)
1474 }
1475 #[allow(dead_code)]
1476 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1477 if b == 0 {
1478 None
1479 } else {
1480 a.checked_div(b)
1481 }
1482 }
1483 #[allow(dead_code)]
1484 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1485 a + b
1486 }
1487 #[allow(dead_code)]
1488 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1489 a * b
1490 }
1491 #[allow(dead_code)]
1492 pub fn fold_neg_i64(a: i64) -> Option<i64> {
1493 a.checked_neg()
1494 }
1495 #[allow(dead_code)]
1496 pub fn fold_not_bool(a: bool) -> bool {
1497 !a
1498 }
1499 #[allow(dead_code)]
1500 pub fn fold_and_bool(a: bool, b: bool) -> bool {
1501 a && b
1502 }
1503 #[allow(dead_code)]
1504 pub fn fold_or_bool(a: bool, b: bool) -> bool {
1505 a || b
1506 }
1507 #[allow(dead_code)]
1508 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1509 a.checked_shl(b)
1510 }
1511 #[allow(dead_code)]
1512 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1513 a.checked_shr(b)
1514 }
1515 #[allow(dead_code)]
1516 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1517 if b == 0 {
1518 None
1519 } else {
1520 Some(a % b)
1521 }
1522 }
1523 #[allow(dead_code)]
1524 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1525 a & b
1526 }
1527 #[allow(dead_code)]
1528 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1529 a | b
1530 }
1531 #[allow(dead_code)]
1532 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1533 a ^ b
1534 }
1535 #[allow(dead_code)]
1536 pub fn fold_bitnot_i64(a: i64) -> i64 {
1537 !a
1538 }
1539}
1540pub struct JuliaStmtDisplay<'a>(pub(super) &'a JuliaStmt);
1541#[allow(dead_code)]
1542#[derive(Debug, Clone)]
1543pub struct JulCacheEntry {
1544 pub key: String,
1545 pub data: Vec<u8>,
1546 pub timestamp: u64,
1547 pub valid: bool,
1548}
1549#[allow(dead_code)]
1551#[derive(Debug, Default)]
1552pub struct JuliaExtPassRegistry {
1553 pub(super) configs: Vec<JuliaExtPassConfig>,
1554 pub(super) stats: Vec<JuliaExtPassStats>,
1555}
1556impl JuliaExtPassRegistry {
1557 #[allow(dead_code)]
1558 pub fn new() -> Self {
1559 Self::default()
1560 }
1561 #[allow(dead_code)]
1562 pub fn register(&mut self, c: JuliaExtPassConfig) {
1563 self.stats.push(JuliaExtPassStats::new());
1564 self.configs.push(c);
1565 }
1566 #[allow(dead_code)]
1567 pub fn len(&self) -> usize {
1568 self.configs.len()
1569 }
1570 #[allow(dead_code)]
1571 pub fn is_empty(&self) -> bool {
1572 self.configs.is_empty()
1573 }
1574 #[allow(dead_code)]
1575 pub fn get(&self, i: usize) -> Option<&JuliaExtPassConfig> {
1576 self.configs.get(i)
1577 }
1578 #[allow(dead_code)]
1579 pub fn get_stats(&self, i: usize) -> Option<&JuliaExtPassStats> {
1580 self.stats.get(i)
1581 }
1582 #[allow(dead_code)]
1583 pub fn enabled_passes(&self) -> Vec<&JuliaExtPassConfig> {
1584 self.configs.iter().filter(|c| c.enabled).collect()
1585 }
1586 #[allow(dead_code)]
1587 pub fn passes_in_phase(&self, ph: &JuliaExtPassPhase) -> Vec<&JuliaExtPassConfig> {
1588 self.configs
1589 .iter()
1590 .filter(|c| c.enabled && &c.phase == ph)
1591 .collect()
1592 }
1593 #[allow(dead_code)]
1594 pub fn total_nodes_visited(&self) -> usize {
1595 self.stats.iter().map(|s| s.nodes_visited).sum()
1596 }
1597 #[allow(dead_code)]
1598 pub fn any_changed(&self) -> bool {
1599 self.stats.iter().any(|s| s.changed)
1600 }
1601}
1602#[allow(dead_code)]
1603pub struct JulPassRegistry {
1604 pub(super) configs: Vec<JulPassConfig>,
1605 pub(super) stats: std::collections::HashMap<String, JulPassStats>,
1606}
1607impl JulPassRegistry {
1608 #[allow(dead_code)]
1609 pub fn new() -> Self {
1610 JulPassRegistry {
1611 configs: Vec::new(),
1612 stats: std::collections::HashMap::new(),
1613 }
1614 }
1615 #[allow(dead_code)]
1616 pub fn register(&mut self, config: JulPassConfig) {
1617 self.stats
1618 .insert(config.pass_name.clone(), JulPassStats::new());
1619 self.configs.push(config);
1620 }
1621 #[allow(dead_code)]
1622 pub fn enabled_passes(&self) -> Vec<&JulPassConfig> {
1623 self.configs.iter().filter(|c| c.enabled).collect()
1624 }
1625 #[allow(dead_code)]
1626 pub fn get_stats(&self, name: &str) -> Option<&JulPassStats> {
1627 self.stats.get(name)
1628 }
1629 #[allow(dead_code)]
1630 pub fn total_passes(&self) -> usize {
1631 self.configs.len()
1632 }
1633 #[allow(dead_code)]
1634 pub fn enabled_count(&self) -> usize {
1635 self.enabled_passes().len()
1636 }
1637 #[allow(dead_code)]
1638 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
1639 if let Some(stats) = self.stats.get_mut(name) {
1640 stats.record_run(changes, time_ms, iter);
1641 }
1642 }
1643}
1644#[allow(dead_code)]
1646#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1647pub enum JuliaExtPassPhase {
1648 Early,
1649 Middle,
1650 Late,
1651 Finalize,
1652}
1653impl JuliaExtPassPhase {
1654 #[allow(dead_code)]
1655 pub fn is_early(&self) -> bool {
1656 matches!(self, Self::Early)
1657 }
1658 #[allow(dead_code)]
1659 pub fn is_middle(&self) -> bool {
1660 matches!(self, Self::Middle)
1661 }
1662 #[allow(dead_code)]
1663 pub fn is_late(&self) -> bool {
1664 matches!(self, Self::Late)
1665 }
1666 #[allow(dead_code)]
1667 pub fn is_finalize(&self) -> bool {
1668 matches!(self, Self::Finalize)
1669 }
1670 #[allow(dead_code)]
1671 pub fn order(&self) -> u32 {
1672 match self {
1673 Self::Early => 0,
1674 Self::Middle => 1,
1675 Self::Late => 2,
1676 Self::Finalize => 3,
1677 }
1678 }
1679 #[allow(dead_code)]
1680 pub fn from_order(n: u32) -> Option<Self> {
1681 match n {
1682 0 => Some(Self::Early),
1683 1 => Some(Self::Middle),
1684 2 => Some(Self::Late),
1685 3 => Some(Self::Finalize),
1686 _ => None,
1687 }
1688 }
1689}
1690#[derive(Debug, Clone, PartialEq)]
1692pub enum JuliaType {
1693 Int8,
1695 Int16,
1697 Int32,
1699 Int64,
1701 Int128,
1703 UInt8,
1705 UInt16,
1707 UInt32,
1709 UInt64,
1711 UInt128,
1713 Float32,
1715 Float64,
1717 Bool,
1719 String,
1721 Char,
1723 Nothing,
1725 Any,
1727 Vector(Box<JuliaType>),
1729 Matrix(Box<JuliaType>),
1731 Array(Box<JuliaType>, u32),
1733 Tuple(Vec<JuliaType>),
1735 NamedTuple(Vec<(String, JuliaType)>),
1737 Union(Vec<JuliaType>),
1739 Abstract(String),
1741 Parametric(String, Vec<JuliaType>),
1743 TypeVar(String),
1745 Function,
1747 Dict(Box<JuliaType>, Box<JuliaType>),
1749 Set(Box<JuliaType>),
1751 Ref(Box<JuliaType>),
1753 Named(String),
1755}
1756#[derive(Debug, Clone, PartialEq)]
1758pub struct JuliaFunction {
1759 pub name: String,
1761 pub type_params: Vec<String>,
1763 pub type_param_bounds: Vec<(String, String)>,
1765 pub params: Vec<JuliaParam>,
1767 pub kwargs: Vec<JuliaParam>,
1769 pub return_type: Option<JuliaType>,
1771 pub body: Vec<JuliaStmt>,
1773 pub is_inner: bool,
1775 pub doc: Option<String>,
1777}
1778impl JuliaFunction {
1779 pub fn new(name: impl Into<String>) -> Self {
1781 JuliaFunction {
1782 name: name.into(),
1783 type_params: vec![],
1784 type_param_bounds: vec![],
1785 params: vec![],
1786 kwargs: vec![],
1787 return_type: None,
1788 body: vec![],
1789 is_inner: false,
1790 doc: None,
1791 }
1792 }
1793 pub fn with_param(mut self, param: JuliaParam) -> Self {
1795 self.params.push(param);
1796 self
1797 }
1798 pub fn with_return_type(mut self, ty: JuliaType) -> Self {
1800 self.return_type = Some(ty);
1801 self
1802 }
1803 pub fn with_body(mut self, body: Vec<JuliaStmt>) -> Self {
1805 self.body = body;
1806 self
1807 }
1808 pub fn with_type_param(mut self, param: impl Into<String>) -> Self {
1810 self.type_params.push(param.into());
1811 self
1812 }
1813 pub fn with_type_param_bound(
1815 mut self,
1816 param: impl Into<String>,
1817 bound: impl Into<String>,
1818 ) -> Self {
1819 let p = param.into();
1820 self.type_params.push(p.clone());
1821 self.type_param_bounds.push((p, bound.into()));
1822 self
1823 }
1824 pub fn emit_signature(&self) -> String {
1826 let mut s = String::new();
1827 s.push_str("function ");
1828 s.push_str(&self.name);
1829 if !self.type_params.is_empty() {
1830 s.push('{');
1831 for (i, tp) in self.type_params.iter().enumerate() {
1832 if i > 0 {
1833 s.push_str(", ");
1834 }
1835 let bound = self.type_param_bounds.iter().find(|(n, _)| n == tp);
1836 if let Some((_, b)) = bound {
1837 s.push_str(&format!("{} <: {}", tp, b));
1838 } else {
1839 s.push_str(tp);
1840 }
1841 }
1842 s.push('}');
1843 }
1844 s.push('(');
1845 for (i, p) in self.params.iter().enumerate() {
1846 if i > 0 {
1847 s.push_str(", ");
1848 }
1849 s.push_str(&p.to_string());
1850 }
1851 if !self.kwargs.is_empty() {
1852 if !self.params.is_empty() {
1853 s.push_str("; ");
1854 } else {
1855 s.push(';');
1856 }
1857 for (i, kw) in self.kwargs.iter().enumerate() {
1858 if i > 0 {
1859 s.push_str(", ");
1860 }
1861 s.push_str(&kw.to_string());
1862 }
1863 }
1864 s.push(')');
1865 if let Some(ref rt) = self.return_type {
1866 s.push_str(&format!("::{}", rt));
1867 }
1868 s
1869 }
1870}
1871#[allow(dead_code)]
1872#[derive(Debug, Clone)]
1873pub struct JulDepGraph {
1874 pub(super) nodes: Vec<u32>,
1875 pub(super) edges: Vec<(u32, u32)>,
1876}
1877impl JulDepGraph {
1878 #[allow(dead_code)]
1879 pub fn new() -> Self {
1880 JulDepGraph {
1881 nodes: Vec::new(),
1882 edges: Vec::new(),
1883 }
1884 }
1885 #[allow(dead_code)]
1886 pub fn add_node(&mut self, id: u32) {
1887 if !self.nodes.contains(&id) {
1888 self.nodes.push(id);
1889 }
1890 }
1891 #[allow(dead_code)]
1892 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
1893 self.add_node(dep);
1894 self.add_node(dependent);
1895 self.edges.push((dep, dependent));
1896 }
1897 #[allow(dead_code)]
1898 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
1899 self.edges
1900 .iter()
1901 .filter(|(d, _)| *d == node)
1902 .map(|(_, dep)| *dep)
1903 .collect()
1904 }
1905 #[allow(dead_code)]
1906 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
1907 self.edges
1908 .iter()
1909 .filter(|(_, dep)| *dep == node)
1910 .map(|(d, _)| *d)
1911 .collect()
1912 }
1913 #[allow(dead_code)]
1914 pub fn topological_sort(&self) -> Vec<u32> {
1915 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
1916 for &n in &self.nodes {
1917 in_degree.insert(n, 0);
1918 }
1919 for (_, dep) in &self.edges {
1920 *in_degree.entry(*dep).or_insert(0) += 1;
1921 }
1922 let mut queue: std::collections::VecDeque<u32> = self
1923 .nodes
1924 .iter()
1925 .filter(|&&n| in_degree[&n] == 0)
1926 .copied()
1927 .collect();
1928 let mut result = Vec::new();
1929 while let Some(node) = queue.pop_front() {
1930 result.push(node);
1931 for dep in self.dependents_of(node) {
1932 let cnt = in_degree.entry(dep).or_insert(0);
1933 *cnt = cnt.saturating_sub(1);
1934 if *cnt == 0 {
1935 queue.push_back(dep);
1936 }
1937 }
1938 }
1939 result
1940 }
1941 #[allow(dead_code)]
1942 pub fn has_cycle(&self) -> bool {
1943 self.topological_sort().len() < self.nodes.len()
1944 }
1945}
1946#[allow(dead_code)]
1948#[derive(Debug, Clone, Default)]
1949pub struct JuliaExtPassStats {
1950 pub iterations: usize,
1951 pub changed: bool,
1952 pub nodes_visited: usize,
1953 pub nodes_modified: usize,
1954 pub time_ms: u64,
1955 pub memory_bytes: usize,
1956 pub errors: usize,
1957}
1958impl JuliaExtPassStats {
1959 #[allow(dead_code)]
1960 pub fn new() -> Self {
1961 Self::default()
1962 }
1963 #[allow(dead_code)]
1964 pub fn visit(&mut self) {
1965 self.nodes_visited += 1;
1966 }
1967 #[allow(dead_code)]
1968 pub fn modify(&mut self) {
1969 self.nodes_modified += 1;
1970 self.changed = true;
1971 }
1972 #[allow(dead_code)]
1973 pub fn iterate(&mut self) {
1974 self.iterations += 1;
1975 }
1976 #[allow(dead_code)]
1977 pub fn error(&mut self) {
1978 self.errors += 1;
1979 }
1980 #[allow(dead_code)]
1981 pub fn efficiency(&self) -> f64 {
1982 if self.nodes_visited == 0 {
1983 0.0
1984 } else {
1985 self.nodes_modified as f64 / self.nodes_visited as f64
1986 }
1987 }
1988 #[allow(dead_code)]
1989 pub fn merge(&mut self, o: &JuliaExtPassStats) {
1990 self.iterations += o.iterations;
1991 self.changed |= o.changed;
1992 self.nodes_visited += o.nodes_visited;
1993 self.nodes_modified += o.nodes_modified;
1994 self.time_ms += o.time_ms;
1995 self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
1996 self.errors += o.errors;
1997 }
1998}
1999#[allow(dead_code)]
2001#[derive(Debug, Clone)]
2002pub struct JuliaExtWorklist {
2003 pub(super) items: std::collections::VecDeque<usize>,
2004 pub(super) present: Vec<bool>,
2005}
2006impl JuliaExtWorklist {
2007 #[allow(dead_code)]
2008 pub fn new(capacity: usize) -> Self {
2009 Self {
2010 items: std::collections::VecDeque::new(),
2011 present: vec![false; capacity],
2012 }
2013 }
2014 #[allow(dead_code)]
2015 pub fn push(&mut self, id: usize) {
2016 if id < self.present.len() && !self.present[id] {
2017 self.present[id] = true;
2018 self.items.push_back(id);
2019 }
2020 }
2021 #[allow(dead_code)]
2022 pub fn push_front(&mut self, id: usize) {
2023 if id < self.present.len() && !self.present[id] {
2024 self.present[id] = true;
2025 self.items.push_front(id);
2026 }
2027 }
2028 #[allow(dead_code)]
2029 pub fn pop(&mut self) -> Option<usize> {
2030 let id = self.items.pop_front()?;
2031 if id < self.present.len() {
2032 self.present[id] = false;
2033 }
2034 Some(id)
2035 }
2036 #[allow(dead_code)]
2037 pub fn is_empty(&self) -> bool {
2038 self.items.is_empty()
2039 }
2040 #[allow(dead_code)]
2041 pub fn len(&self) -> usize {
2042 self.items.len()
2043 }
2044 #[allow(dead_code)]
2045 pub fn contains(&self, id: usize) -> bool {
2046 id < self.present.len() && self.present[id]
2047 }
2048 #[allow(dead_code)]
2049 pub fn drain_all(&mut self) -> Vec<usize> {
2050 let v: Vec<usize> = self.items.drain(..).collect();
2051 for &id in &v {
2052 if id < self.present.len() {
2053 self.present[id] = false;
2054 }
2055 }
2056 v
2057 }
2058}
2059#[derive(Debug, Clone, PartialEq)]
2061pub enum JuliaExpr {
2062 IntLit(i64),
2064 UIntLit(u64),
2066 FloatLit(f64),
2068 BoolLit(bool),
2070 StringLit(String),
2072 CharLit(char),
2074 Nothing,
2076 Var(String),
2078 Field(Box<JuliaExpr>, String),
2080 Index(Box<JuliaExpr>, Vec<JuliaExpr>),
2082 Slice(
2084 Box<JuliaExpr>,
2085 Option<Box<JuliaExpr>>,
2086 Option<Box<JuliaExpr>>,
2087 ),
2088 Call(Box<JuliaExpr>, Vec<JuliaExpr>),
2090 CallKw(Box<JuliaExpr>, Vec<JuliaExpr>, Vec<(String, JuliaExpr)>),
2092 Broadcast(Box<JuliaExpr>, Vec<JuliaExpr>),
2094 BinOp(String, Box<JuliaExpr>, Box<JuliaExpr>),
2096 UnOp(String, Box<JuliaExpr>),
2098 CompareChain(Vec<JuliaExpr>, Vec<String>),
2100 ArrayLit(Vec<JuliaExpr>),
2102 MatrixLit(Vec<Vec<JuliaExpr>>),
2104 Range(Box<JuliaExpr>, Option<Box<JuliaExpr>>, Box<JuliaExpr>),
2106 TupleLit(Vec<JuliaExpr>),
2108 ArrayComp(
2110 Box<JuliaExpr>,
2111 Vec<(String, JuliaExpr)>,
2112 Option<Box<JuliaExpr>>,
2113 ),
2114 Generator(
2116 Box<JuliaExpr>,
2117 Vec<(String, JuliaExpr)>,
2118 Option<Box<JuliaExpr>>,
2119 ),
2120 DictComp(Box<JuliaExpr>, Box<JuliaExpr>, Vec<(String, JuliaExpr)>),
2122 Lambda(Vec<JuliaParam>, Box<JuliaExpr>),
2124 DoBlock(Box<JuliaExpr>, Vec<String>, Vec<JuliaStmt>),
2126 Ternary(Box<JuliaExpr>, Box<JuliaExpr>, Box<JuliaExpr>),
2128 TypeAssert(Box<JuliaExpr>, JuliaType),
2130 Convert(JuliaType, Box<JuliaExpr>),
2132 IsA(Box<JuliaExpr>, JuliaType),
2134 TypeOf(Box<JuliaExpr>),
2136 Macro(String, Vec<JuliaExpr>),
2138 Interpolated(Vec<JuliaStringPart>),
2140 Splat(Box<JuliaExpr>),
2142 NamedArg(String, Box<JuliaExpr>),
2144 Pair(Box<JuliaExpr>, Box<JuliaExpr>),
2146 Block(Vec<JuliaStmt>),
2148}