1use crate::lcnf::*;
6use std::collections::HashMap;
7
8use super::functions::LUA_KEYWORDS;
9
10use super::functions::*;
11use std::collections::{HashSet, VecDeque};
12
13#[derive(Debug, Default)]
15pub struct LuaExtSourceBuffer {
16 pub(super) buf: String,
17 pub(super) indent_level: usize,
18 pub(super) indent_str: String,
19}
20impl LuaExtSourceBuffer {
21 pub fn new() -> Self {
22 LuaExtSourceBuffer {
23 buf: String::new(),
24 indent_level: 0,
25 indent_str: " ".to_string(),
26 }
27 }
28 pub fn with_indent(mut self, indent: impl Into<String>) -> Self {
29 self.indent_str = indent.into();
30 self
31 }
32 pub fn push_line(&mut self, line: &str) {
33 for _ in 0..self.indent_level {
34 self.buf.push_str(&self.indent_str);
35 }
36 self.buf.push_str(line);
37 self.buf.push('\n');
38 }
39 pub fn push_raw(&mut self, s: &str) {
40 self.buf.push_str(s);
41 }
42 pub fn indent(&mut self) {
43 self.indent_level += 1;
44 }
45 pub fn dedent(&mut self) {
46 self.indent_level = self.indent_level.saturating_sub(1);
47 }
48 pub fn as_str(&self) -> &str {
49 &self.buf
50 }
51 pub fn len(&self) -> usize {
52 self.buf.len()
53 }
54 pub fn is_empty(&self) -> bool {
55 self.buf.is_empty()
56 }
57 pub fn line_count(&self) -> usize {
58 self.buf.lines().count()
59 }
60 pub fn into_string(self) -> String {
61 self.buf
62 }
63 pub fn reset(&mut self) {
64 self.buf.clear();
65 self.indent_level = 0;
66 }
67}
68#[allow(dead_code)]
69#[derive(Debug, Clone)]
70pub struct LuaDominatorTree {
71 pub idom: Vec<Option<u32>>,
72 pub dom_children: Vec<Vec<u32>>,
73 pub dom_depth: Vec<u32>,
74}
75impl LuaDominatorTree {
76 #[allow(dead_code)]
77 pub fn new(size: usize) -> Self {
78 LuaDominatorTree {
79 idom: vec![None; size],
80 dom_children: vec![Vec::new(); size],
81 dom_depth: vec![0; size],
82 }
83 }
84 #[allow(dead_code)]
85 pub fn set_idom(&mut self, node: usize, idom: u32) {
86 self.idom[node] = Some(idom);
87 }
88 #[allow(dead_code)]
89 pub fn dominates(&self, a: usize, b: usize) -> bool {
90 if a == b {
91 return true;
92 }
93 let mut cur = b;
94 loop {
95 match self.idom[cur] {
96 Some(parent) if parent as usize == a => return true,
97 Some(parent) if parent as usize == cur => return false,
98 Some(parent) => cur = parent as usize,
99 None => return false,
100 }
101 }
102 }
103 #[allow(dead_code)]
104 pub fn depth(&self, node: usize) -> u32 {
105 self.dom_depth.get(node).copied().unwrap_or(0)
106 }
107}
108#[derive(Debug, Clone, Default)]
110pub struct LuaExtEmitStats {
111 pub bytes_emitted: usize,
112 pub items_emitted: usize,
113 pub errors: usize,
114 pub warnings: usize,
115 pub elapsed_ms: u64,
116}
117impl LuaExtEmitStats {
118 pub fn new() -> Self {
119 LuaExtEmitStats::default()
120 }
121 pub fn throughput_bps(&self) -> f64 {
122 if self.elapsed_ms == 0 {
123 0.0
124 } else {
125 self.bytes_emitted as f64 / (self.elapsed_ms as f64 / 1000.0)
126 }
127 }
128 pub fn is_clean(&self) -> bool {
129 self.errors == 0
130 }
131}
132#[allow(dead_code)]
133#[derive(Debug, Clone)]
134pub struct LuaPassConfig {
135 pub phase: LuaPassPhase,
136 pub enabled: bool,
137 pub max_iterations: u32,
138 pub debug_output: bool,
139 pub pass_name: String,
140}
141impl LuaPassConfig {
142 #[allow(dead_code)]
143 pub fn new(name: impl Into<String>, phase: LuaPassPhase) -> Self {
144 LuaPassConfig {
145 phase,
146 enabled: true,
147 max_iterations: 10,
148 debug_output: false,
149 pass_name: name.into(),
150 }
151 }
152 #[allow(dead_code)]
153 pub fn disabled(mut self) -> Self {
154 self.enabled = false;
155 self
156 }
157 #[allow(dead_code)]
158 pub fn with_debug(mut self) -> Self {
159 self.debug_output = true;
160 self
161 }
162 #[allow(dead_code)]
163 pub fn max_iter(mut self, n: u32) -> Self {
164 self.max_iterations = n;
165 self
166 }
167}
168#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
170pub struct LuaExtVersion {
171 pub major: u32,
172 pub minor: u32,
173 pub patch: u32,
174 pub pre: Option<String>,
175}
176impl LuaExtVersion {
177 pub fn new(major: u32, minor: u32, patch: u32) -> Self {
178 LuaExtVersion {
179 major,
180 minor,
181 patch,
182 pre: None,
183 }
184 }
185 pub fn with_pre(mut self, pre: impl Into<String>) -> Self {
186 self.pre = Some(pre.into());
187 self
188 }
189 pub fn is_stable(&self) -> bool {
190 self.pre.is_none()
191 }
192 pub fn is_compatible_with(&self, other: &LuaExtVersion) -> bool {
193 self.major == other.major && self.minor >= other.minor
194 }
195}
196#[derive(Debug, Clone, PartialEq, Eq, Hash)]
198pub enum LuaType {
199 Nil,
201 Boolean,
203 Number(bool),
205 String,
207 Table,
209 Function,
211 Userdata,
213 Thread,
215 Custom(std::string::String),
217}
218#[derive(Debug, Clone, PartialEq)]
220pub struct LuaFunction {
221 pub name: Option<std::string::String>,
223 pub params: Vec<std::string::String>,
225 pub vararg: bool,
227 pub body: Vec<LuaStmt>,
229 pub is_local: bool,
231 pub is_method: bool,
233}
234impl LuaFunction {
235 pub fn new(
237 name: impl Into<std::string::String>,
238 params: Vec<std::string::String>,
239 body: Vec<LuaStmt>,
240 ) -> Self {
241 LuaFunction {
242 name: Some(name.into()),
243 params,
244 vararg: false,
245 body,
246 is_local: false,
247 is_method: false,
248 }
249 }
250 pub fn new_local(
252 name: impl Into<std::string::String>,
253 params: Vec<std::string::String>,
254 body: Vec<LuaStmt>,
255 ) -> Self {
256 LuaFunction {
257 name: Some(name.into()),
258 params,
259 vararg: false,
260 body,
261 is_local: true,
262 is_method: false,
263 }
264 }
265}
266#[allow(dead_code)]
267#[derive(Debug, Clone, PartialEq)]
268pub enum LuaPassPhase {
269 Analysis,
270 Transformation,
271 Verification,
272 Cleanup,
273}
274impl LuaPassPhase {
275 #[allow(dead_code)]
276 pub fn name(&self) -> &str {
277 match self {
278 LuaPassPhase::Analysis => "analysis",
279 LuaPassPhase::Transformation => "transformation",
280 LuaPassPhase::Verification => "verification",
281 LuaPassPhase::Cleanup => "cleanup",
282 }
283 }
284 #[allow(dead_code)]
285 pub fn is_modifying(&self) -> bool {
286 matches!(self, LuaPassPhase::Transformation | LuaPassPhase::Cleanup)
287 }
288}
289#[allow(dead_code)]
291#[derive(Debug, Clone)]
292pub struct LuaExtWorklist {
293 pub(super) items: std::collections::VecDeque<usize>,
294 pub(super) present: Vec<bool>,
295}
296impl LuaExtWorklist {
297 #[allow(dead_code)]
298 pub fn new(capacity: usize) -> Self {
299 Self {
300 items: std::collections::VecDeque::new(),
301 present: vec![false; capacity],
302 }
303 }
304 #[allow(dead_code)]
305 pub fn push(&mut self, id: usize) {
306 if id < self.present.len() && !self.present[id] {
307 self.present[id] = true;
308 self.items.push_back(id);
309 }
310 }
311 #[allow(dead_code)]
312 pub fn push_front(&mut self, id: usize) {
313 if id < self.present.len() && !self.present[id] {
314 self.present[id] = true;
315 self.items.push_front(id);
316 }
317 }
318 #[allow(dead_code)]
319 pub fn pop(&mut self) -> Option<usize> {
320 let id = self.items.pop_front()?;
321 if id < self.present.len() {
322 self.present[id] = false;
323 }
324 Some(id)
325 }
326 #[allow(dead_code)]
327 pub fn is_empty(&self) -> bool {
328 self.items.is_empty()
329 }
330 #[allow(dead_code)]
331 pub fn len(&self) -> usize {
332 self.items.len()
333 }
334 #[allow(dead_code)]
335 pub fn contains(&self, id: usize) -> bool {
336 id < self.present.len() && self.present[id]
337 }
338 #[allow(dead_code)]
339 pub fn drain_all(&mut self) -> Vec<usize> {
340 let v: Vec<usize> = self.items.drain(..).collect();
341 for &id in &v {
342 if id < self.present.len() {
343 self.present[id] = false;
344 }
345 }
346 v
347 }
348}
349#[allow(dead_code)]
351#[derive(Debug, Clone, Default)]
352pub struct LuaExtPassStats {
353 pub iterations: usize,
354 pub changed: bool,
355 pub nodes_visited: usize,
356 pub nodes_modified: usize,
357 pub time_ms: u64,
358 pub memory_bytes: usize,
359 pub errors: usize,
360}
361impl LuaExtPassStats {
362 #[allow(dead_code)]
363 pub fn new() -> Self {
364 Self::default()
365 }
366 #[allow(dead_code)]
367 pub fn visit(&mut self) {
368 self.nodes_visited += 1;
369 }
370 #[allow(dead_code)]
371 pub fn modify(&mut self) {
372 self.nodes_modified += 1;
373 self.changed = true;
374 }
375 #[allow(dead_code)]
376 pub fn iterate(&mut self) {
377 self.iterations += 1;
378 }
379 #[allow(dead_code)]
380 pub fn error(&mut self) {
381 self.errors += 1;
382 }
383 #[allow(dead_code)]
384 pub fn efficiency(&self) -> f64 {
385 if self.nodes_visited == 0 {
386 0.0
387 } else {
388 self.nodes_modified as f64 / self.nodes_visited as f64
389 }
390 }
391 #[allow(dead_code)]
392 pub fn merge(&mut self, o: &LuaExtPassStats) {
393 self.iterations += o.iterations;
394 self.changed |= o.changed;
395 self.nodes_visited += o.nodes_visited;
396 self.nodes_modified += o.nodes_modified;
397 self.time_ms += o.time_ms;
398 self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
399 self.errors += o.errors;
400 }
401}
402#[allow(dead_code)]
403#[derive(Debug, Clone)]
404pub struct LuaDepGraph {
405 pub(super) nodes: Vec<u32>,
406 pub(super) edges: Vec<(u32, u32)>,
407}
408impl LuaDepGraph {
409 #[allow(dead_code)]
410 pub fn new() -> Self {
411 LuaDepGraph {
412 nodes: Vec::new(),
413 edges: Vec::new(),
414 }
415 }
416 #[allow(dead_code)]
417 pub fn add_node(&mut self, id: u32) {
418 if !self.nodes.contains(&id) {
419 self.nodes.push(id);
420 }
421 }
422 #[allow(dead_code)]
423 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
424 self.add_node(dep);
425 self.add_node(dependent);
426 self.edges.push((dep, dependent));
427 }
428 #[allow(dead_code)]
429 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
430 self.edges
431 .iter()
432 .filter(|(d, _)| *d == node)
433 .map(|(_, dep)| *dep)
434 .collect()
435 }
436 #[allow(dead_code)]
437 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
438 self.edges
439 .iter()
440 .filter(|(_, dep)| *dep == node)
441 .map(|(d, _)| *d)
442 .collect()
443 }
444 #[allow(dead_code)]
445 pub fn topological_sort(&self) -> Vec<u32> {
446 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
447 for &n in &self.nodes {
448 in_degree.insert(n, 0);
449 }
450 for (_, dep) in &self.edges {
451 *in_degree.entry(*dep).or_insert(0) += 1;
452 }
453 let mut queue: std::collections::VecDeque<u32> = self
454 .nodes
455 .iter()
456 .filter(|&&n| in_degree[&n] == 0)
457 .copied()
458 .collect();
459 let mut result = Vec::new();
460 while let Some(node) = queue.pop_front() {
461 result.push(node);
462 for dep in self.dependents_of(node) {
463 let cnt = in_degree.entry(dep).or_insert(0);
464 *cnt = cnt.saturating_sub(1);
465 if *cnt == 0 {
466 queue.push_back(dep);
467 }
468 }
469 }
470 result
471 }
472 #[allow(dead_code)]
473 pub fn has_cycle(&self) -> bool {
474 self.topological_sort().len() < self.nodes.len()
475 }
476}
477#[allow(dead_code)]
478#[derive(Debug, Clone, Default)]
479pub struct LuaPassStats {
480 pub total_runs: u32,
481 pub successful_runs: u32,
482 pub total_changes: u64,
483 pub time_ms: u64,
484 pub iterations_used: u32,
485}
486impl LuaPassStats {
487 #[allow(dead_code)]
488 pub fn new() -> Self {
489 Self::default()
490 }
491 #[allow(dead_code)]
492 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
493 self.total_runs += 1;
494 self.successful_runs += 1;
495 self.total_changes += changes;
496 self.time_ms += time_ms;
497 self.iterations_used = iterations;
498 }
499 #[allow(dead_code)]
500 pub fn average_changes_per_run(&self) -> f64 {
501 if self.total_runs == 0 {
502 return 0.0;
503 }
504 self.total_changes as f64 / self.total_runs as f64
505 }
506 #[allow(dead_code)]
507 pub fn success_rate(&self) -> f64 {
508 if self.total_runs == 0 {
509 return 0.0;
510 }
511 self.successful_runs as f64 / self.total_runs as f64
512 }
513 #[allow(dead_code)]
514 pub fn format_summary(&self) -> String {
515 format!(
516 "Runs: {}/{}, Changes: {}, Time: {}ms",
517 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
518 )
519 }
520}
521#[derive(Debug, Clone, PartialEq)]
523pub enum LuaStmt {
524 Assign {
526 targets: Vec<LuaExpr>,
527 values: Vec<LuaExpr>,
528 },
529 LocalAssign {
531 names: Vec<std::string::String>,
532 attribs: Vec<Option<std::string::String>>,
533 values: Vec<LuaExpr>,
534 },
535 Do(Vec<LuaStmt>),
537 While { cond: LuaExpr, body: Vec<LuaStmt> },
539 Repeat { body: Vec<LuaStmt>, cond: LuaExpr },
541 If {
543 cond: LuaExpr,
544 then_body: Vec<LuaStmt>,
545 elseif_clauses: Vec<(LuaExpr, Vec<LuaStmt>)>,
546 else_body: Option<Vec<LuaStmt>>,
547 },
548 For {
550 var: std::string::String,
551 start: LuaExpr,
552 limit: LuaExpr,
553 step: Option<LuaExpr>,
554 body: Vec<LuaStmt>,
555 },
556 ForIn {
558 names: Vec<std::string::String>,
559 exprs: Vec<LuaExpr>,
560 body: Vec<LuaStmt>,
561 },
562 Function(LuaFunction),
564 Local(LuaFunction),
566 Return(Vec<LuaExpr>),
568 Break,
570 Goto(std::string::String),
572 Label(std::string::String),
574 Call(LuaExpr),
576}
577#[derive(Debug)]
579pub struct LuaExtEventLog {
580 pub(super) entries: std::collections::VecDeque<String>,
581 pub(super) capacity: usize,
582}
583impl LuaExtEventLog {
584 pub fn new(capacity: usize) -> Self {
585 LuaExtEventLog {
586 entries: std::collections::VecDeque::with_capacity(capacity),
587 capacity,
588 }
589 }
590 pub fn push(&mut self, event: impl Into<String>) {
591 if self.entries.len() >= self.capacity {
592 self.entries.pop_front();
593 }
594 self.entries.push_back(event.into());
595 }
596 pub fn iter(&self) -> impl Iterator<Item = &String> {
597 self.entries.iter()
598 }
599 pub fn len(&self) -> usize {
600 self.entries.len()
601 }
602 pub fn is_empty(&self) -> bool {
603 self.entries.is_empty()
604 }
605 pub fn capacity(&self) -> usize {
606 self.capacity
607 }
608 pub fn clear(&mut self) {
609 self.entries.clear();
610 }
611}
612#[allow(dead_code)]
614#[derive(Debug, Clone, Default)]
615pub struct LuaExtLiveness {
616 pub live_in: Vec<Vec<usize>>,
617 pub live_out: Vec<Vec<usize>>,
618 pub defs: Vec<Vec<usize>>,
619 pub uses: Vec<Vec<usize>>,
620}
621impl LuaExtLiveness {
622 #[allow(dead_code)]
623 pub fn new(n: usize) -> Self {
624 Self {
625 live_in: vec![Vec::new(); n],
626 live_out: vec![Vec::new(); n],
627 defs: vec![Vec::new(); n],
628 uses: vec![Vec::new(); n],
629 }
630 }
631 #[allow(dead_code)]
632 pub fn live_in(&self, b: usize, v: usize) -> bool {
633 self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
634 }
635 #[allow(dead_code)]
636 pub fn live_out(&self, b: usize, v: usize) -> bool {
637 self.live_out
638 .get(b)
639 .map(|s| s.contains(&v))
640 .unwrap_or(false)
641 }
642 #[allow(dead_code)]
643 pub fn add_def(&mut self, b: usize, v: usize) {
644 if let Some(s) = self.defs.get_mut(b) {
645 if !s.contains(&v) {
646 s.push(v);
647 }
648 }
649 }
650 #[allow(dead_code)]
651 pub fn add_use(&mut self, b: usize, v: usize) {
652 if let Some(s) = self.uses.get_mut(b) {
653 if !s.contains(&v) {
654 s.push(v);
655 }
656 }
657 }
658 #[allow(dead_code)]
659 pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
660 self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
661 }
662 #[allow(dead_code)]
663 pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
664 self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
665 }
666}
667#[derive(Debug, Default)]
669pub struct LuaExtProfiler {
670 pub(super) timings: Vec<LuaExtPassTiming>,
671}
672impl LuaExtProfiler {
673 pub fn new() -> Self {
674 LuaExtProfiler::default()
675 }
676 pub fn record(&mut self, t: LuaExtPassTiming) {
677 self.timings.push(t);
678 }
679 pub fn total_elapsed_us(&self) -> u64 {
680 self.timings.iter().map(|t| t.elapsed_us).sum()
681 }
682 pub fn slowest_pass(&self) -> Option<&LuaExtPassTiming> {
683 self.timings.iter().max_by_key(|t| t.elapsed_us)
684 }
685 pub fn num_passes(&self) -> usize {
686 self.timings.len()
687 }
688 pub fn profitable_passes(&self) -> Vec<&LuaExtPassTiming> {
689 self.timings.iter().filter(|t| t.is_profitable()).collect()
690 }
691}
692#[derive(Debug, Default)]
694pub struct LuaExtDiagCollector {
695 pub(super) msgs: Vec<LuaExtDiagMsg>,
696}
697impl LuaExtDiagCollector {
698 pub fn new() -> Self {
699 LuaExtDiagCollector::default()
700 }
701 pub fn emit(&mut self, d: LuaExtDiagMsg) {
702 self.msgs.push(d);
703 }
704 pub fn has_errors(&self) -> bool {
705 self.msgs
706 .iter()
707 .any(|d| d.severity == LuaExtDiagSeverity::Error)
708 }
709 pub fn errors(&self) -> Vec<&LuaExtDiagMsg> {
710 self.msgs
711 .iter()
712 .filter(|d| d.severity == LuaExtDiagSeverity::Error)
713 .collect()
714 }
715 pub fn warnings(&self) -> Vec<&LuaExtDiagMsg> {
716 self.msgs
717 .iter()
718 .filter(|d| d.severity == LuaExtDiagSeverity::Warning)
719 .collect()
720 }
721 pub fn len(&self) -> usize {
722 self.msgs.len()
723 }
724 pub fn is_empty(&self) -> bool {
725 self.msgs.is_empty()
726 }
727 pub fn clear(&mut self) {
728 self.msgs.clear();
729 }
730}
731#[derive(Debug, Clone, PartialEq, Eq, Hash)]
733pub struct LuaExtIncrKey {
734 pub content_hash: u64,
735 pub config_hash: u64,
736}
737impl LuaExtIncrKey {
738 pub fn new(content: u64, config: u64) -> Self {
739 LuaExtIncrKey {
740 content_hash: content,
741 config_hash: config,
742 }
743 }
744 pub fn combined_hash(&self) -> u64 {
745 self.content_hash.wrapping_mul(0x9e3779b97f4a7c15) ^ self.config_hash
746 }
747 pub fn matches(&self, other: &LuaExtIncrKey) -> bool {
748 self.content_hash == other.content_hash && self.config_hash == other.config_hash
749 }
750}
751pub struct LuaBackend {
753 pub(super) fresh_counter: u64,
755 pub(super) name_cache: HashMap<std::string::String, std::string::String>,
757}
758impl LuaBackend {
759 pub fn new() -> Self {
761 LuaBackend {
762 fresh_counter: 0,
763 name_cache: HashMap::new(),
764 }
765 }
766 pub fn fresh_var(&mut self) -> std::string::String {
768 let n = self.fresh_counter;
769 self.fresh_counter += 1;
770 format!("_t{}", n)
771 }
772 pub fn mangle_name(&mut self, name: &str) -> std::string::String {
774 if let Some(cached) = self.name_cache.get(name) {
775 return cached.clone();
776 }
777 if name.is_empty() {
778 return "_anon".to_string();
779 }
780 let mangled: std::string::String = name
781 .chars()
782 .map(|c| match c {
783 '.' | ':' => '_',
784 '\'' => '_',
785 c if c.is_alphanumeric() || c == '_' => c,
786 _ => '_',
787 })
788 .collect();
789 let mangled = if LUA_KEYWORDS.contains(&mangled.as_str())
790 || mangled.starts_with(|c: char| c.is_ascii_digit())
791 {
792 format!("_{}", mangled)
793 } else {
794 mangled
795 };
796 self.name_cache.insert(name.to_string(), mangled.clone());
797 mangled
798 }
799 pub fn lcnf_to_lua_type(ty: &LcnfType) -> LuaType {
801 match ty {
802 LcnfType::Nat => LuaType::Number(true),
803 LcnfType::LcnfString => LuaType::String,
804 LcnfType::Unit | LcnfType::Erased | LcnfType::Irrelevant => LuaType::Nil,
805 LcnfType::Object => LuaType::Table,
806 LcnfType::Var(name) => LuaType::Custom(name.clone()),
807 LcnfType::Fun(..) => LuaType::Function,
808 LcnfType::Ctor(name, _) => LuaType::Custom(name.clone()),
809 }
810 }
811 pub fn compile_lit(lit: &LcnfLit) -> LuaExpr {
813 match lit {
814 LcnfLit::Nat(n) => LuaExpr::Int(*n as i64),
815 LcnfLit::Str(s) => LuaExpr::Str(s.clone()),
816 }
817 }
818 pub fn compile_let_value(&mut self, value: &LcnfLetValue) -> LuaExpr {
820 match value {
821 LcnfLetValue::App(func, args) => {
822 let func_expr = self.compile_arg(func);
823 let lua_args: Vec<_> = args.iter().map(|a| self.compile_arg(a)).collect();
824 LuaExpr::Call {
825 func: Box::new(func_expr),
826 args: lua_args,
827 }
828 }
829 LcnfLetValue::Ctor(ctor_name, _tag, fields) => {
830 let mut all_fields = vec![LuaTableField::NamedField(
831 "tag".to_string(),
832 LuaExpr::Str(ctor_name.clone()),
833 )];
834 for f in fields {
835 all_fields.push(LuaTableField::ArrayItem(self.compile_arg(f)));
836 }
837 LuaExpr::TableConstructor(all_fields)
838 }
839 LcnfLetValue::Proj(_name, index, var) => {
840 let val_expr = LuaExpr::Var(var.to_string());
841 LuaExpr::IndexAccess {
842 table: Box::new(val_expr),
843 key: Box::new(LuaExpr::Int(*index as i64 + 2)),
844 }
845 }
846 LcnfLetValue::Lit(lit) => Self::compile_lit(lit),
847 LcnfLetValue::Erased => LuaExpr::Nil,
848 LcnfLetValue::FVar(id) => LuaExpr::Var(id.to_string()),
849 LcnfLetValue::Reset(_) => LuaExpr::Nil,
850 LcnfLetValue::Reuse(_slot, ctor_name, _tag, fields) => {
851 let mut all_fields = vec![LuaTableField::NamedField(
852 "tag".to_string(),
853 LuaExpr::Str(ctor_name.clone()),
854 )];
855 for f in fields {
856 all_fields.push(LuaTableField::ArrayItem(self.compile_arg(f)));
857 }
858 LuaExpr::TableConstructor(all_fields)
859 }
860 }
861 }
862 pub fn compile_arg(&mut self, arg: &LcnfArg) -> LuaExpr {
864 match arg {
865 LcnfArg::Var(id) => LuaExpr::Var(id.to_string()),
866 LcnfArg::Lit(lit) => Self::compile_lit(lit),
867 LcnfArg::Erased => LuaExpr::Nil,
868 LcnfArg::Type(_) => LuaExpr::Nil,
869 }
870 }
871 pub fn compile_expr(&mut self, expr: &LcnfExpr, stmts: &mut Vec<LuaStmt>) -> LuaExpr {
874 match expr {
875 LcnfExpr::Return(arg) => self.compile_arg(arg),
876 LcnfExpr::Let {
877 id,
878 ty: _,
879 value,
880 body,
881 ..
882 } => {
883 let val_expr = self.compile_let_value(value);
884 stmts.push(LuaStmt::LocalAssign {
885 names: vec![id.to_string()],
886 attribs: vec![None],
887 values: vec![val_expr],
888 });
889 self.compile_expr(body, stmts)
890 }
891 LcnfExpr::TailCall(func, args) => {
892 let func_expr = self.compile_arg(func);
893 let lua_args: Vec<_> = args.iter().map(|a| self.compile_arg(a)).collect();
894 LuaExpr::Call {
895 func: Box::new(func_expr),
896 args: lua_args,
897 }
898 }
899 LcnfExpr::Case {
900 scrutinee,
901 alts,
902 default,
903 ..
904 } => {
905 let scrut_expr = LuaExpr::Var(scrutinee.to_string());
906 let result_var = self.fresh_var();
907 stmts.push(LuaStmt::LocalAssign {
908 names: vec![result_var.clone()],
909 attribs: vec![None],
910 values: vec![],
911 });
912 let tag_expr = LuaExpr::FieldAccess {
913 table: Box::new(scrut_expr.clone()),
914 field: "tag".to_string(),
915 };
916 let mut if_cond: Option<LuaExpr> = None;
917 let mut then_stmts: Vec<LuaStmt> = Vec::new();
918 let mut elseif_clauses: Vec<(LuaExpr, Vec<LuaStmt>)> = Vec::new();
919 for (idx, alt) in alts.iter().enumerate() {
920 let mut case_stmts: Vec<LuaStmt> = Vec::new();
921 for (field_idx, param) in alt.params.iter().enumerate() {
922 let field_access = LuaExpr::IndexAccess {
923 table: Box::new(scrut_expr.clone()),
924 key: Box::new(LuaExpr::Int(field_idx as i64 + 2)),
925 };
926 case_stmts.push(LuaStmt::LocalAssign {
927 names: vec![param.id.to_string()],
928 attribs: vec![None],
929 values: vec![field_access],
930 });
931 }
932 let case_result = self.compile_expr(&alt.body, &mut case_stmts);
933 case_stmts.push(LuaStmt::Assign {
934 targets: vec![LuaExpr::Var(result_var.clone())],
935 values: vec![case_result],
936 });
937 let cond = LuaExpr::BinOp {
938 op: "==".to_string(),
939 lhs: Box::new(tag_expr.clone()),
940 rhs: Box::new(LuaExpr::Str(alt.ctor_name.clone())),
941 };
942 if idx == 0 {
943 if_cond = Some(cond);
944 then_stmts = case_stmts;
945 } else {
946 elseif_clauses.push((cond, case_stmts));
947 }
948 }
949 let else_body = if let Some(def) = default {
950 let mut def_stmts: Vec<LuaStmt> = Vec::new();
951 let def_result = self.compile_expr(def, &mut def_stmts);
952 def_stmts.push(LuaStmt::Assign {
953 targets: vec![LuaExpr::Var(result_var.clone())],
954 values: vec![def_result],
955 });
956 Some(def_stmts)
957 } else {
958 None
959 };
960 if let Some(cond) = if_cond {
961 stmts.push(LuaStmt::If {
962 cond,
963 then_body: then_stmts,
964 elseif_clauses,
965 else_body,
966 });
967 }
968 LuaExpr::Var(result_var)
969 }
970 LcnfExpr::Unreachable => LuaExpr::Call {
971 func: Box::new(LuaExpr::Var("error".to_string())),
972 args: vec![LuaExpr::Str("unreachable".to_string())],
973 },
974 }
975 }
976 pub fn compile_decl(&mut self, decl: &LcnfFunDecl) -> Result<LuaFunction, std::string::String> {
978 let lua_name = self.mangle_name(&decl.name);
979 let params: Vec<_> = decl.params.iter().map(|p| p.id.to_string()).collect();
980 let mut body_stmts: Vec<LuaStmt> = Vec::new();
981 let result_expr = self.compile_expr(&decl.body, &mut body_stmts);
982 body_stmts.push(LuaStmt::Return(vec![result_expr]));
983 Ok(LuaFunction {
984 name: Some(lua_name),
985 params,
986 vararg: false,
987 body: body_stmts,
988 is_local: false,
989 is_method: false,
990 })
991 }
992 pub fn emit_module(&mut self, decls: &[LcnfFunDecl]) -> LuaModule {
994 let mut module = LuaModule::new();
995 for decl in decls {
996 if let Ok(func) = self.compile_decl(decl) {
997 module.functions.push(func);
998 }
999 }
1000 module
1001 }
1002}
1003#[derive(Debug, Clone, Default)]
1005pub struct LuaExtConfig {
1006 pub(super) entries: std::collections::HashMap<String, String>,
1007}
1008impl LuaExtConfig {
1009 pub fn new() -> Self {
1010 LuaExtConfig::default()
1011 }
1012 pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) {
1013 self.entries.insert(key.into(), value.into());
1014 }
1015 pub fn get(&self, key: &str) -> Option<&str> {
1016 self.entries.get(key).map(|s| s.as_str())
1017 }
1018 pub fn get_bool(&self, key: &str) -> bool {
1019 matches!(self.get(key), Some("true") | Some("1") | Some("yes"))
1020 }
1021 pub fn get_int(&self, key: &str) -> Option<i64> {
1022 self.get(key)?.parse().ok()
1023 }
1024 pub fn len(&self) -> usize {
1025 self.entries.len()
1026 }
1027 pub fn is_empty(&self) -> bool {
1028 self.entries.is_empty()
1029 }
1030}
1031#[allow(dead_code)]
1032#[derive(Debug, Clone)]
1033pub struct LuaCacheEntry {
1034 pub key: String,
1035 pub data: Vec<u8>,
1036 pub timestamp: u64,
1037 pub valid: bool,
1038}
1039#[derive(Debug, Clone, PartialEq)]
1041pub struct LuaModule {
1042 pub requires: Vec<(std::string::String, std::string::String)>,
1044 pub local_decls: Vec<LuaStmt>,
1046 pub functions: Vec<LuaFunction>,
1048 pub main_block: Vec<LuaStmt>,
1050}
1051impl LuaModule {
1052 pub fn new() -> Self {
1054 LuaModule {
1055 requires: Vec::new(),
1056 local_decls: Vec::new(),
1057 functions: Vec::new(),
1058 main_block: Vec::new(),
1059 }
1060 }
1061 pub fn emit(&self) -> std::string::String {
1063 let mut out = std::string::String::new();
1064 for (alias, path) in &self.requires {
1065 out.push_str(&format!("local {} = require(\"{}\")\n", alias, path));
1066 }
1067 if !self.requires.is_empty() {
1068 out.push('\n');
1069 }
1070 for decl in &self.local_decls {
1071 out.push_str(&format!("{}\n", emit_stmt(decl, 0)));
1072 }
1073 if !self.local_decls.is_empty() {
1074 out.push('\n');
1075 }
1076 for func in &self.functions {
1077 out.push_str(&format!("{}\n\n", emit_function(func, 0, false)));
1078 }
1079 for stmt in &self.main_block {
1080 out.push_str(&format!("{}\n", emit_stmt(stmt, 0)));
1081 }
1082 out
1083 }
1084}
1085#[derive(Debug, Default)]
1087pub struct LuaExtNameScope {
1088 pub(super) declared: std::collections::HashSet<String>,
1089 pub(super) depth: usize,
1090 pub(super) parent: Option<Box<LuaExtNameScope>>,
1091}
1092impl LuaExtNameScope {
1093 pub fn new() -> Self {
1094 LuaExtNameScope::default()
1095 }
1096 pub fn declare(&mut self, name: impl Into<String>) -> bool {
1097 self.declared.insert(name.into())
1098 }
1099 pub fn is_declared(&self, name: &str) -> bool {
1100 self.declared.contains(name)
1101 }
1102 pub fn push_scope(self) -> Self {
1103 LuaExtNameScope {
1104 declared: std::collections::HashSet::new(),
1105 depth: self.depth + 1,
1106 parent: Some(Box::new(self)),
1107 }
1108 }
1109 pub fn pop_scope(self) -> Self {
1110 *self.parent.unwrap_or_default()
1111 }
1112 pub fn depth(&self) -> usize {
1113 self.depth
1114 }
1115 pub fn len(&self) -> usize {
1116 self.declared.len()
1117 }
1118}
1119#[derive(Debug, Clone, PartialEq)]
1121pub struct LuaClass {
1122 pub name: std::string::String,
1124 pub methods: Vec<LuaFunction>,
1126 pub index: Option<LuaExpr>,
1128 pub newindex: Option<LuaExpr>,
1130 pub tostring_body: Option<Vec<LuaStmt>>,
1132}
1133impl LuaClass {
1134 pub fn new(name: impl Into<std::string::String>) -> Self {
1136 LuaClass {
1137 name: name.into(),
1138 methods: Vec::new(),
1139 index: None,
1140 newindex: None,
1141 tostring_body: None,
1142 }
1143 }
1144 pub fn emit(&self) -> std::string::String {
1146 let mut out = std::string::String::new();
1147 let n = &self.name;
1148 out.push_str(&format!("{} = {{}}\n", n));
1149 let index_str = match &self.index {
1150 Some(expr) => expr.to_string(),
1151 None => n.clone(),
1152 };
1153 out.push_str(&format!("{}.__index = {}\n", n, index_str));
1154 if let Some(ts_body) = &self.tostring_body {
1155 out.push_str(&format!(
1156 "{}.__tostring = function(self)\n{}\nend\n",
1157 n,
1158 emit_stmts(ts_body, 1)
1159 ));
1160 }
1161 if let Some(ni) = &self.newindex {
1162 out.push_str(&format!("{}.__newindex = {}\n", n, ni));
1163 }
1164 out.push_str(
1165 &format!(
1166 "function {}:new(o)\n o = o or {{}}\n setmetatable(o, self)\n self.__index = self\n return o\nend\n",
1167 n
1168 ),
1169 );
1170 for method in &self.methods {
1171 let mut m = method.clone();
1172 m.is_method = true;
1173 if let Some(ref mname) = m.name.clone() {
1174 m.name = Some(format!("{}.{}", n, mname));
1175 }
1176 out.push_str(&format!("{}\n", emit_function(&m, 0, false)));
1177 }
1178 out
1179 }
1180}
1181#[allow(dead_code)]
1182#[derive(Debug, Clone)]
1183pub struct LuaWorklist {
1184 pub(super) items: std::collections::VecDeque<u32>,
1185 pub(super) in_worklist: std::collections::HashSet<u32>,
1186}
1187impl LuaWorklist {
1188 #[allow(dead_code)]
1189 pub fn new() -> Self {
1190 LuaWorklist {
1191 items: std::collections::VecDeque::new(),
1192 in_worklist: std::collections::HashSet::new(),
1193 }
1194 }
1195 #[allow(dead_code)]
1196 pub fn push(&mut self, item: u32) -> bool {
1197 if self.in_worklist.insert(item) {
1198 self.items.push_back(item);
1199 true
1200 } else {
1201 false
1202 }
1203 }
1204 #[allow(dead_code)]
1205 pub fn pop(&mut self) -> Option<u32> {
1206 let item = self.items.pop_front()?;
1207 self.in_worklist.remove(&item);
1208 Some(item)
1209 }
1210 #[allow(dead_code)]
1211 pub fn is_empty(&self) -> bool {
1212 self.items.is_empty()
1213 }
1214 #[allow(dead_code)]
1215 pub fn len(&self) -> usize {
1216 self.items.len()
1217 }
1218 #[allow(dead_code)]
1219 pub fn contains(&self, item: u32) -> bool {
1220 self.in_worklist.contains(&item)
1221 }
1222}
1223#[allow(dead_code)]
1225#[derive(Debug, Default)]
1226pub struct LuaExtPassRegistry {
1227 pub(super) configs: Vec<LuaExtPassConfig>,
1228 pub(super) stats: Vec<LuaExtPassStats>,
1229}
1230impl LuaExtPassRegistry {
1231 #[allow(dead_code)]
1232 pub fn new() -> Self {
1233 Self::default()
1234 }
1235 #[allow(dead_code)]
1236 pub fn register(&mut self, c: LuaExtPassConfig) {
1237 self.stats.push(LuaExtPassStats::new());
1238 self.configs.push(c);
1239 }
1240 #[allow(dead_code)]
1241 pub fn len(&self) -> usize {
1242 self.configs.len()
1243 }
1244 #[allow(dead_code)]
1245 pub fn is_empty(&self) -> bool {
1246 self.configs.is_empty()
1247 }
1248 #[allow(dead_code)]
1249 pub fn get(&self, i: usize) -> Option<&LuaExtPassConfig> {
1250 self.configs.get(i)
1251 }
1252 #[allow(dead_code)]
1253 pub fn get_stats(&self, i: usize) -> Option<&LuaExtPassStats> {
1254 self.stats.get(i)
1255 }
1256 #[allow(dead_code)]
1257 pub fn enabled_passes(&self) -> Vec<&LuaExtPassConfig> {
1258 self.configs.iter().filter(|c| c.enabled).collect()
1259 }
1260 #[allow(dead_code)]
1261 pub fn passes_in_phase(&self, ph: &LuaExtPassPhase) -> Vec<&LuaExtPassConfig> {
1262 self.configs
1263 .iter()
1264 .filter(|c| c.enabled && &c.phase == ph)
1265 .collect()
1266 }
1267 #[allow(dead_code)]
1268 pub fn total_nodes_visited(&self) -> usize {
1269 self.stats.iter().map(|s| s.nodes_visited).sum()
1270 }
1271 #[allow(dead_code)]
1272 pub fn any_changed(&self) -> bool {
1273 self.stats.iter().any(|s| s.changed)
1274 }
1275}
1276#[derive(Debug, Clone, Default)]
1278pub struct LuaExtFeatures {
1279 pub(super) flags: std::collections::HashSet<String>,
1280}
1281impl LuaExtFeatures {
1282 pub fn new() -> Self {
1283 LuaExtFeatures::default()
1284 }
1285 pub fn enable(&mut self, flag: impl Into<String>) {
1286 self.flags.insert(flag.into());
1287 }
1288 pub fn disable(&mut self, flag: &str) {
1289 self.flags.remove(flag);
1290 }
1291 pub fn is_enabled(&self, flag: &str) -> bool {
1292 self.flags.contains(flag)
1293 }
1294 pub fn len(&self) -> usize {
1295 self.flags.len()
1296 }
1297 pub fn is_empty(&self) -> bool {
1298 self.flags.is_empty()
1299 }
1300 pub fn union(&self, other: &LuaExtFeatures) -> LuaExtFeatures {
1301 LuaExtFeatures {
1302 flags: self.flags.union(&other.flags).cloned().collect(),
1303 }
1304 }
1305 pub fn intersection(&self, other: &LuaExtFeatures) -> LuaExtFeatures {
1306 LuaExtFeatures {
1307 flags: self.flags.intersection(&other.flags).cloned().collect(),
1308 }
1309 }
1310}
1311#[derive(Debug, Clone, PartialEq)]
1313pub enum LuaExpr {
1314 Nil,
1316 True,
1318 False,
1320 Int(i64),
1322 Float(f64),
1324 Str(std::string::String),
1326 Var(std::string::String),
1328 BinOp {
1330 op: std::string::String,
1331 lhs: Box<LuaExpr>,
1332 rhs: Box<LuaExpr>,
1333 },
1334 UnaryOp {
1336 op: std::string::String,
1337 operand: Box<LuaExpr>,
1338 },
1339 Call {
1341 func: Box<LuaExpr>,
1342 args: Vec<LuaExpr>,
1343 },
1344 MethodCall {
1346 obj: Box<LuaExpr>,
1347 method: std::string::String,
1348 args: Vec<LuaExpr>,
1349 },
1350 TableConstructor(Vec<LuaTableField>),
1352 IndexAccess {
1354 table: Box<LuaExpr>,
1355 key: Box<LuaExpr>,
1356 },
1357 FieldAccess {
1359 table: Box<LuaExpr>,
1360 field: std::string::String,
1361 },
1362 Lambda {
1364 params: Vec<std::string::String>,
1365 vararg: bool,
1366 body: Vec<LuaStmt>,
1367 },
1368 Ellipsis,
1370}
1371#[allow(dead_code)]
1373#[derive(Debug, Clone)]
1374pub struct LuaExtPassConfig {
1375 pub name: String,
1376 pub phase: LuaExtPassPhase,
1377 pub enabled: bool,
1378 pub max_iterations: usize,
1379 pub debug: u32,
1380 pub timeout_ms: Option<u64>,
1381}
1382impl LuaExtPassConfig {
1383 #[allow(dead_code)]
1384 pub fn new(name: impl Into<String>) -> Self {
1385 Self {
1386 name: name.into(),
1387 phase: LuaExtPassPhase::Middle,
1388 enabled: true,
1389 max_iterations: 100,
1390 debug: 0,
1391 timeout_ms: None,
1392 }
1393 }
1394 #[allow(dead_code)]
1395 pub fn with_phase(mut self, phase: LuaExtPassPhase) -> Self {
1396 self.phase = phase;
1397 self
1398 }
1399 #[allow(dead_code)]
1400 pub fn with_max_iter(mut self, n: usize) -> Self {
1401 self.max_iterations = n;
1402 self
1403 }
1404 #[allow(dead_code)]
1405 pub fn with_debug(mut self, d: u32) -> Self {
1406 self.debug = d;
1407 self
1408 }
1409 #[allow(dead_code)]
1410 pub fn disabled(mut self) -> Self {
1411 self.enabled = false;
1412 self
1413 }
1414 #[allow(dead_code)]
1415 pub fn with_timeout(mut self, ms: u64) -> Self {
1416 self.timeout_ms = Some(ms);
1417 self
1418 }
1419 #[allow(dead_code)]
1420 pub fn is_debug_enabled(&self) -> bool {
1421 self.debug > 0
1422 }
1423}
1424#[allow(dead_code)]
1425#[derive(Debug, Clone)]
1426pub struct LuaLivenessInfo {
1427 pub live_in: Vec<std::collections::HashSet<u32>>,
1428 pub live_out: Vec<std::collections::HashSet<u32>>,
1429 pub defs: Vec<std::collections::HashSet<u32>>,
1430 pub uses: Vec<std::collections::HashSet<u32>>,
1431}
1432impl LuaLivenessInfo {
1433 #[allow(dead_code)]
1434 pub fn new(block_count: usize) -> Self {
1435 LuaLivenessInfo {
1436 live_in: vec![std::collections::HashSet::new(); block_count],
1437 live_out: vec![std::collections::HashSet::new(); block_count],
1438 defs: vec![std::collections::HashSet::new(); block_count],
1439 uses: vec![std::collections::HashSet::new(); block_count],
1440 }
1441 }
1442 #[allow(dead_code)]
1443 pub fn add_def(&mut self, block: usize, var: u32) {
1444 if block < self.defs.len() {
1445 self.defs[block].insert(var);
1446 }
1447 }
1448 #[allow(dead_code)]
1449 pub fn add_use(&mut self, block: usize, var: u32) {
1450 if block < self.uses.len() {
1451 self.uses[block].insert(var);
1452 }
1453 }
1454 #[allow(dead_code)]
1455 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
1456 self.live_in
1457 .get(block)
1458 .map(|s| s.contains(&var))
1459 .unwrap_or(false)
1460 }
1461 #[allow(dead_code)]
1462 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
1463 self.live_out
1464 .get(block)
1465 .map(|s| s.contains(&var))
1466 .unwrap_or(false)
1467 }
1468}
1469#[derive(Debug, Default)]
1471pub struct LuaExtIdGen {
1472 pub(super) next: u32,
1473}
1474impl LuaExtIdGen {
1475 pub fn new() -> Self {
1476 LuaExtIdGen::default()
1477 }
1478 pub fn next_id(&mut self) -> u32 {
1479 let id = self.next;
1480 self.next += 1;
1481 id
1482 }
1483 pub fn peek_next(&self) -> u32 {
1484 self.next
1485 }
1486 pub fn reset(&mut self) {
1487 self.next = 0;
1488 }
1489 pub fn skip(&mut self, n: u32) {
1490 self.next += n;
1491 }
1492}
1493#[allow(dead_code)]
1494pub struct LuaPassRegistry {
1495 pub(super) configs: Vec<LuaPassConfig>,
1496 pub(super) stats: std::collections::HashMap<String, LuaPassStats>,
1497}
1498impl LuaPassRegistry {
1499 #[allow(dead_code)]
1500 pub fn new() -> Self {
1501 LuaPassRegistry {
1502 configs: Vec::new(),
1503 stats: std::collections::HashMap::new(),
1504 }
1505 }
1506 #[allow(dead_code)]
1507 pub fn register(&mut self, config: LuaPassConfig) {
1508 self.stats
1509 .insert(config.pass_name.clone(), LuaPassStats::new());
1510 self.configs.push(config);
1511 }
1512 #[allow(dead_code)]
1513 pub fn enabled_passes(&self) -> Vec<&LuaPassConfig> {
1514 self.configs.iter().filter(|c| c.enabled).collect()
1515 }
1516 #[allow(dead_code)]
1517 pub fn get_stats(&self, name: &str) -> Option<&LuaPassStats> {
1518 self.stats.get(name)
1519 }
1520 #[allow(dead_code)]
1521 pub fn total_passes(&self) -> usize {
1522 self.configs.len()
1523 }
1524 #[allow(dead_code)]
1525 pub fn enabled_count(&self) -> usize {
1526 self.enabled_passes().len()
1527 }
1528 #[allow(dead_code)]
1529 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
1530 if let Some(stats) = self.stats.get_mut(name) {
1531 stats.record_run(changes, time_ms, iter);
1532 }
1533 }
1534}
1535#[allow(dead_code)]
1537#[derive(Debug, Clone, Default)]
1538pub struct LuaExtConstFolder {
1539 pub(super) folds: usize,
1540 pub(super) failures: usize,
1541 pub(super) enabled: bool,
1542}
1543impl LuaExtConstFolder {
1544 #[allow(dead_code)]
1545 pub fn new() -> Self {
1546 Self {
1547 folds: 0,
1548 failures: 0,
1549 enabled: true,
1550 }
1551 }
1552 #[allow(dead_code)]
1553 pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1554 self.folds += 1;
1555 a.checked_add(b)
1556 }
1557 #[allow(dead_code)]
1558 pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1559 self.folds += 1;
1560 a.checked_sub(b)
1561 }
1562 #[allow(dead_code)]
1563 pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1564 self.folds += 1;
1565 a.checked_mul(b)
1566 }
1567 #[allow(dead_code)]
1568 pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1569 if b == 0 {
1570 self.failures += 1;
1571 None
1572 } else {
1573 self.folds += 1;
1574 a.checked_div(b)
1575 }
1576 }
1577 #[allow(dead_code)]
1578 pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1579 if b == 0 {
1580 self.failures += 1;
1581 None
1582 } else {
1583 self.folds += 1;
1584 a.checked_rem(b)
1585 }
1586 }
1587 #[allow(dead_code)]
1588 pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
1589 self.folds += 1;
1590 a.checked_neg()
1591 }
1592 #[allow(dead_code)]
1593 pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1594 if s >= 64 {
1595 self.failures += 1;
1596 None
1597 } else {
1598 self.folds += 1;
1599 a.checked_shl(s)
1600 }
1601 }
1602 #[allow(dead_code)]
1603 pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1604 if s >= 64 {
1605 self.failures += 1;
1606 None
1607 } else {
1608 self.folds += 1;
1609 a.checked_shr(s)
1610 }
1611 }
1612 #[allow(dead_code)]
1613 pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
1614 self.folds += 1;
1615 a & b
1616 }
1617 #[allow(dead_code)]
1618 pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
1619 self.folds += 1;
1620 a | b
1621 }
1622 #[allow(dead_code)]
1623 pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
1624 self.folds += 1;
1625 a ^ b
1626 }
1627 #[allow(dead_code)]
1628 pub fn not_i64(&mut self, a: i64) -> i64 {
1629 self.folds += 1;
1630 !a
1631 }
1632 #[allow(dead_code)]
1633 pub fn fold_count(&self) -> usize {
1634 self.folds
1635 }
1636 #[allow(dead_code)]
1637 pub fn failure_count(&self) -> usize {
1638 self.failures
1639 }
1640 #[allow(dead_code)]
1641 pub fn enable(&mut self) {
1642 self.enabled = true;
1643 }
1644 #[allow(dead_code)]
1645 pub fn disable(&mut self) {
1646 self.enabled = false;
1647 }
1648 #[allow(dead_code)]
1649 pub fn is_enabled(&self) -> bool {
1650 self.enabled
1651 }
1652}
1653#[allow(dead_code)]
1655#[derive(Debug)]
1656pub struct LuaExtCache {
1657 pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
1658 pub(super) cap: usize,
1659 pub(super) total_hits: u64,
1660 pub(super) total_misses: u64,
1661}
1662impl LuaExtCache {
1663 #[allow(dead_code)]
1664 pub fn new(cap: usize) -> Self {
1665 Self {
1666 entries: Vec::new(),
1667 cap,
1668 total_hits: 0,
1669 total_misses: 0,
1670 }
1671 }
1672 #[allow(dead_code)]
1673 pub fn get(&mut self, key: u64) -> Option<&[u8]> {
1674 for e in self.entries.iter_mut() {
1675 if e.0 == key && e.2 {
1676 e.3 += 1;
1677 self.total_hits += 1;
1678 return Some(&e.1);
1679 }
1680 }
1681 self.total_misses += 1;
1682 None
1683 }
1684 #[allow(dead_code)]
1685 pub fn put(&mut self, key: u64, data: Vec<u8>) {
1686 if self.entries.len() >= self.cap {
1687 self.entries.retain(|e| e.2);
1688 if self.entries.len() >= self.cap {
1689 self.entries.remove(0);
1690 }
1691 }
1692 self.entries.push((key, data, true, 0));
1693 }
1694 #[allow(dead_code)]
1695 pub fn invalidate(&mut self) {
1696 for e in self.entries.iter_mut() {
1697 e.2 = false;
1698 }
1699 }
1700 #[allow(dead_code)]
1701 pub fn hit_rate(&self) -> f64 {
1702 let t = self.total_hits + self.total_misses;
1703 if t == 0 {
1704 0.0
1705 } else {
1706 self.total_hits as f64 / t as f64
1707 }
1708 }
1709 #[allow(dead_code)]
1710 pub fn live_count(&self) -> usize {
1711 self.entries.iter().filter(|e| e.2).count()
1712 }
1713}
1714#[derive(Debug, Clone)]
1716pub struct LuaExtPassTiming {
1717 pub pass_name: String,
1718 pub elapsed_us: u64,
1719 pub items_processed: usize,
1720 pub bytes_before: usize,
1721 pub bytes_after: usize,
1722}
1723impl LuaExtPassTiming {
1724 pub fn new(
1725 pass_name: impl Into<String>,
1726 elapsed_us: u64,
1727 items: usize,
1728 before: usize,
1729 after: usize,
1730 ) -> Self {
1731 LuaExtPassTiming {
1732 pass_name: pass_name.into(),
1733 elapsed_us,
1734 items_processed: items,
1735 bytes_before: before,
1736 bytes_after: after,
1737 }
1738 }
1739 pub fn throughput_mps(&self) -> f64 {
1740 if self.elapsed_us == 0 {
1741 0.0
1742 } else {
1743 self.items_processed as f64 / (self.elapsed_us as f64 / 1_000_000.0)
1744 }
1745 }
1746 pub fn size_ratio(&self) -> f64 {
1747 if self.bytes_before == 0 {
1748 1.0
1749 } else {
1750 self.bytes_after as f64 / self.bytes_before as f64
1751 }
1752 }
1753 pub fn is_profitable(&self) -> bool {
1754 self.size_ratio() <= 1.05
1755 }
1756}
1757#[allow(dead_code)]
1759#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1760pub enum LuaExtPassPhase {
1761 Early,
1762 Middle,
1763 Late,
1764 Finalize,
1765}
1766impl LuaExtPassPhase {
1767 #[allow(dead_code)]
1768 pub fn is_early(&self) -> bool {
1769 matches!(self, Self::Early)
1770 }
1771 #[allow(dead_code)]
1772 pub fn is_middle(&self) -> bool {
1773 matches!(self, Self::Middle)
1774 }
1775 #[allow(dead_code)]
1776 pub fn is_late(&self) -> bool {
1777 matches!(self, Self::Late)
1778 }
1779 #[allow(dead_code)]
1780 pub fn is_finalize(&self) -> bool {
1781 matches!(self, Self::Finalize)
1782 }
1783 #[allow(dead_code)]
1784 pub fn order(&self) -> u32 {
1785 match self {
1786 Self::Early => 0,
1787 Self::Middle => 1,
1788 Self::Late => 2,
1789 Self::Finalize => 3,
1790 }
1791 }
1792 #[allow(dead_code)]
1793 pub fn from_order(n: u32) -> Option<Self> {
1794 match n {
1795 0 => Some(Self::Early),
1796 1 => Some(Self::Middle),
1797 2 => Some(Self::Late),
1798 3 => Some(Self::Finalize),
1799 _ => None,
1800 }
1801 }
1802}
1803#[derive(Debug, Clone, PartialEq)]
1805pub enum LuaTableField {
1806 ArrayItem(LuaExpr),
1808 NamedField(std::string::String, LuaExpr),
1810 IndexedField(LuaExpr, LuaExpr),
1812}
1813#[allow(dead_code)]
1814#[derive(Debug, Clone)]
1815pub struct LuaAnalysisCache {
1816 pub(super) entries: std::collections::HashMap<String, LuaCacheEntry>,
1817 pub(super) max_size: usize,
1818 pub(super) hits: u64,
1819 pub(super) misses: u64,
1820}
1821impl LuaAnalysisCache {
1822 #[allow(dead_code)]
1823 pub fn new(max_size: usize) -> Self {
1824 LuaAnalysisCache {
1825 entries: std::collections::HashMap::new(),
1826 max_size,
1827 hits: 0,
1828 misses: 0,
1829 }
1830 }
1831 #[allow(dead_code)]
1832 pub fn get(&mut self, key: &str) -> Option<&LuaCacheEntry> {
1833 if self.entries.contains_key(key) {
1834 self.hits += 1;
1835 self.entries.get(key)
1836 } else {
1837 self.misses += 1;
1838 None
1839 }
1840 }
1841 #[allow(dead_code)]
1842 pub fn insert(&mut self, key: String, data: Vec<u8>) {
1843 if self.entries.len() >= self.max_size {
1844 if let Some(oldest) = self.entries.keys().next().cloned() {
1845 self.entries.remove(&oldest);
1846 }
1847 }
1848 self.entries.insert(
1849 key.clone(),
1850 LuaCacheEntry {
1851 key,
1852 data,
1853 timestamp: 0,
1854 valid: true,
1855 },
1856 );
1857 }
1858 #[allow(dead_code)]
1859 pub fn invalidate(&mut self, key: &str) {
1860 if let Some(entry) = self.entries.get_mut(key) {
1861 entry.valid = false;
1862 }
1863 }
1864 #[allow(dead_code)]
1865 pub fn clear(&mut self) {
1866 self.entries.clear();
1867 }
1868 #[allow(dead_code)]
1869 pub fn hit_rate(&self) -> f64 {
1870 let total = self.hits + self.misses;
1871 if total == 0 {
1872 return 0.0;
1873 }
1874 self.hits as f64 / total as f64
1875 }
1876 #[allow(dead_code)]
1877 pub fn size(&self) -> usize {
1878 self.entries.len()
1879 }
1880}
1881#[allow(dead_code)]
1882pub struct LuaConstantFoldingHelper;
1883impl LuaConstantFoldingHelper {
1884 #[allow(dead_code)]
1885 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1886 a.checked_add(b)
1887 }
1888 #[allow(dead_code)]
1889 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1890 a.checked_sub(b)
1891 }
1892 #[allow(dead_code)]
1893 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1894 a.checked_mul(b)
1895 }
1896 #[allow(dead_code)]
1897 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1898 if b == 0 {
1899 None
1900 } else {
1901 a.checked_div(b)
1902 }
1903 }
1904 #[allow(dead_code)]
1905 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1906 a + b
1907 }
1908 #[allow(dead_code)]
1909 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1910 a * b
1911 }
1912 #[allow(dead_code)]
1913 pub fn fold_neg_i64(a: i64) -> Option<i64> {
1914 a.checked_neg()
1915 }
1916 #[allow(dead_code)]
1917 pub fn fold_not_bool(a: bool) -> bool {
1918 !a
1919 }
1920 #[allow(dead_code)]
1921 pub fn fold_and_bool(a: bool, b: bool) -> bool {
1922 a && b
1923 }
1924 #[allow(dead_code)]
1925 pub fn fold_or_bool(a: bool, b: bool) -> bool {
1926 a || b
1927 }
1928 #[allow(dead_code)]
1929 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1930 a.checked_shl(b)
1931 }
1932 #[allow(dead_code)]
1933 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1934 a.checked_shr(b)
1935 }
1936 #[allow(dead_code)]
1937 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1938 if b == 0 {
1939 None
1940 } else {
1941 Some(a % b)
1942 }
1943 }
1944 #[allow(dead_code)]
1945 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1946 a & b
1947 }
1948 #[allow(dead_code)]
1949 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1950 a | b
1951 }
1952 #[allow(dead_code)]
1953 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1954 a ^ b
1955 }
1956 #[allow(dead_code)]
1957 pub fn fold_bitnot_i64(a: i64) -> i64 {
1958 !a
1959 }
1960}
1961#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1963pub enum LuaExtDiagSeverity {
1964 Note,
1965 Warning,
1966 Error,
1967}
1968#[derive(Debug, Clone)]
1970pub struct LuaExtDiagMsg {
1971 pub severity: LuaExtDiagSeverity,
1972 pub pass: String,
1973 pub message: String,
1974}
1975impl LuaExtDiagMsg {
1976 pub fn error(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1977 LuaExtDiagMsg {
1978 severity: LuaExtDiagSeverity::Error,
1979 pass: pass.into(),
1980 message: msg.into(),
1981 }
1982 }
1983 pub fn warning(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1984 LuaExtDiagMsg {
1985 severity: LuaExtDiagSeverity::Warning,
1986 pass: pass.into(),
1987 message: msg.into(),
1988 }
1989 }
1990 pub fn note(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1991 LuaExtDiagMsg {
1992 severity: LuaExtDiagSeverity::Note,
1993 pass: pass.into(),
1994 message: msg.into(),
1995 }
1996 }
1997}
1998#[allow(dead_code)]
2000#[derive(Debug, Clone)]
2001pub struct LuaExtDomTree {
2002 pub(super) idom: Vec<Option<usize>>,
2003 pub(super) children: Vec<Vec<usize>>,
2004 pub(super) depth: Vec<usize>,
2005}
2006impl LuaExtDomTree {
2007 #[allow(dead_code)]
2008 pub fn new(n: usize) -> Self {
2009 Self {
2010 idom: vec![None; n],
2011 children: vec![Vec::new(); n],
2012 depth: vec![0; n],
2013 }
2014 }
2015 #[allow(dead_code)]
2016 pub fn set_idom(&mut self, node: usize, dom: usize) {
2017 if node < self.idom.len() {
2018 self.idom[node] = Some(dom);
2019 if dom < self.children.len() {
2020 self.children[dom].push(node);
2021 }
2022 self.depth[node] = if dom < self.depth.len() {
2023 self.depth[dom] + 1
2024 } else {
2025 1
2026 };
2027 }
2028 }
2029 #[allow(dead_code)]
2030 pub fn dominates(&self, a: usize, mut b: usize) -> bool {
2031 if a == b {
2032 return true;
2033 }
2034 let n = self.idom.len();
2035 for _ in 0..n {
2036 match self.idom.get(b).copied().flatten() {
2037 None => return false,
2038 Some(p) if p == a => return true,
2039 Some(p) if p == b => return false,
2040 Some(p) => b = p,
2041 }
2042 }
2043 false
2044 }
2045 #[allow(dead_code)]
2046 pub fn children_of(&self, n: usize) -> &[usize] {
2047 self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
2048 }
2049 #[allow(dead_code)]
2050 pub fn depth_of(&self, n: usize) -> usize {
2051 self.depth.get(n).copied().unwrap_or(0)
2052 }
2053 #[allow(dead_code)]
2054 pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
2055 let n = self.idom.len();
2056 for _ in 0..(2 * n) {
2057 if a == b {
2058 return a;
2059 }
2060 if self.depth_of(a) > self.depth_of(b) {
2061 a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
2062 } else {
2063 b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
2064 }
2065 }
2066 0
2067 }
2068}
2069#[allow(dead_code)]
2071#[derive(Debug, Clone)]
2072pub struct LuaExtDepGraph {
2073 pub(super) n: usize,
2074 pub(super) adj: Vec<Vec<usize>>,
2075 pub(super) rev: Vec<Vec<usize>>,
2076 pub(super) edge_count: usize,
2077}
2078impl LuaExtDepGraph {
2079 #[allow(dead_code)]
2080 pub fn new(n: usize) -> Self {
2081 Self {
2082 n,
2083 adj: vec![Vec::new(); n],
2084 rev: vec![Vec::new(); n],
2085 edge_count: 0,
2086 }
2087 }
2088 #[allow(dead_code)]
2089 pub fn add_edge(&mut self, from: usize, to: usize) {
2090 if from < self.n && to < self.n {
2091 if !self.adj[from].contains(&to) {
2092 self.adj[from].push(to);
2093 self.rev[to].push(from);
2094 self.edge_count += 1;
2095 }
2096 }
2097 }
2098 #[allow(dead_code)]
2099 pub fn succs(&self, n: usize) -> &[usize] {
2100 self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
2101 }
2102 #[allow(dead_code)]
2103 pub fn preds(&self, n: usize) -> &[usize] {
2104 self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
2105 }
2106 #[allow(dead_code)]
2107 pub fn topo_sort(&self) -> Option<Vec<usize>> {
2108 let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
2109 let mut q: std::collections::VecDeque<usize> =
2110 (0..self.n).filter(|&i| deg[i] == 0).collect();
2111 let mut out = Vec::with_capacity(self.n);
2112 while let Some(u) = q.pop_front() {
2113 out.push(u);
2114 for &v in &self.adj[u] {
2115 deg[v] -= 1;
2116 if deg[v] == 0 {
2117 q.push_back(v);
2118 }
2119 }
2120 }
2121 if out.len() == self.n {
2122 Some(out)
2123 } else {
2124 None
2125 }
2126 }
2127 #[allow(dead_code)]
2128 pub fn has_cycle(&self) -> bool {
2129 self.topo_sort().is_none()
2130 }
2131 #[allow(dead_code)]
2132 pub fn reachable(&self, start: usize) -> Vec<usize> {
2133 let mut vis = vec![false; self.n];
2134 let mut stk = vec![start];
2135 let mut out = Vec::new();
2136 while let Some(u) = stk.pop() {
2137 if u < self.n && !vis[u] {
2138 vis[u] = true;
2139 out.push(u);
2140 for &v in &self.adj[u] {
2141 if !vis[v] {
2142 stk.push(v);
2143 }
2144 }
2145 }
2146 }
2147 out
2148 }
2149 #[allow(dead_code)]
2150 pub fn scc(&self) -> Vec<Vec<usize>> {
2151 let mut visited = vec![false; self.n];
2152 let mut order = Vec::new();
2153 for i in 0..self.n {
2154 if !visited[i] {
2155 let mut stk = vec![(i, 0usize)];
2156 while let Some((u, idx)) = stk.last_mut() {
2157 if !visited[*u] {
2158 visited[*u] = true;
2159 }
2160 if *idx < self.adj[*u].len() {
2161 let v = self.adj[*u][*idx];
2162 *idx += 1;
2163 if !visited[v] {
2164 stk.push((v, 0));
2165 }
2166 } else {
2167 order.push(*u);
2168 stk.pop();
2169 }
2170 }
2171 }
2172 }
2173 let mut comp = vec![usize::MAX; self.n];
2174 let mut components: Vec<Vec<usize>> = Vec::new();
2175 for &start in order.iter().rev() {
2176 if comp[start] == usize::MAX {
2177 let cid = components.len();
2178 let mut component = Vec::new();
2179 let mut stk = vec![start];
2180 while let Some(u) = stk.pop() {
2181 if comp[u] == usize::MAX {
2182 comp[u] = cid;
2183 component.push(u);
2184 for &v in &self.rev[u] {
2185 if comp[v] == usize::MAX {
2186 stk.push(v);
2187 }
2188 }
2189 }
2190 }
2191 components.push(component);
2192 }
2193 }
2194 components
2195 }
2196 #[allow(dead_code)]
2197 pub fn node_count(&self) -> usize {
2198 self.n
2199 }
2200 #[allow(dead_code)]
2201 pub fn edge_count(&self) -> usize {
2202 self.edge_count
2203 }
2204}