1use super::functions::*;
6use crate::lcnf::{LcnfArg, LcnfExpr, LcnfFunDecl, LcnfLetValue, LcnfType, LcnfVarId};
7use std::collections::{HashMap, HashSet, VecDeque};
8
9#[allow(dead_code)]
10#[derive(Debug, Clone)]
11pub struct RADominatorTree {
12 pub idom: Vec<Option<u32>>,
13 pub dom_children: Vec<Vec<u32>>,
14 pub dom_depth: Vec<u32>,
15}
16impl RADominatorTree {
17 #[allow(dead_code)]
18 pub fn new(size: usize) -> Self {
19 RADominatorTree {
20 idom: vec![None; size],
21 dom_children: vec![Vec::new(); size],
22 dom_depth: vec![0; size],
23 }
24 }
25 #[allow(dead_code)]
26 pub fn set_idom(&mut self, node: usize, idom: u32) {
27 self.idom[node] = Some(idom);
28 }
29 #[allow(dead_code)]
30 pub fn dominates(&self, a: usize, b: usize) -> bool {
31 if a == b {
32 return true;
33 }
34 let mut cur = b;
35 loop {
36 match self.idom[cur] {
37 Some(parent) if parent as usize == a => return true,
38 Some(parent) if parent as usize == cur => return false,
39 Some(parent) => cur = parent as usize,
40 None => return false,
41 }
42 }
43 }
44 #[allow(dead_code)]
45 pub fn depth(&self, node: usize) -> u32 {
46 self.dom_depth.get(node).copied().unwrap_or(0)
47 }
48}
49#[allow(dead_code)]
51#[derive(Debug, Clone, PartialEq, Eq, Hash)]
52pub enum RAExtPassPhase {
53 Early,
54 Middle,
55 Late,
56 Finalize,
57}
58impl RAExtPassPhase {
59 #[allow(dead_code)]
60 pub fn is_early(&self) -> bool {
61 matches!(self, Self::Early)
62 }
63 #[allow(dead_code)]
64 pub fn is_middle(&self) -> bool {
65 matches!(self, Self::Middle)
66 }
67 #[allow(dead_code)]
68 pub fn is_late(&self) -> bool {
69 matches!(self, Self::Late)
70 }
71 #[allow(dead_code)]
72 pub fn is_finalize(&self) -> bool {
73 matches!(self, Self::Finalize)
74 }
75 #[allow(dead_code)]
76 pub fn order(&self) -> u32 {
77 match self {
78 Self::Early => 0,
79 Self::Middle => 1,
80 Self::Late => 2,
81 Self::Finalize => 3,
82 }
83 }
84 #[allow(dead_code)]
85 pub fn from_order(n: u32) -> Option<Self> {
86 match n {
87 0 => Some(Self::Early),
88 1 => Some(Self::Middle),
89 2 => Some(Self::Late),
90 3 => Some(Self::Finalize),
91 _ => None,
92 }
93 }
94}
95#[derive(Debug)]
104pub struct GraphColoringAllocator {
105 pub num_colors: usize,
107 pub phys_regs: Vec<PhysReg>,
109 pub graph: InterferenceGraph,
111 pub(super) simplify_stack: Vec<LcnfVarId>,
113 pub(super) potential_spills: Vec<LcnfVarId>,
115 pub coalesced_count: usize,
117 pub iterations: usize,
119}
120impl GraphColoringAllocator {
121 pub fn new(phys_regs: Vec<PhysReg>) -> Self {
123 let k = phys_regs.len();
124 GraphColoringAllocator {
125 num_colors: k,
126 phys_regs,
127 graph: InterferenceGraph::new(),
128 simplify_stack: Vec::new(),
129 potential_spills: Vec::new(),
130 coalesced_count: 0,
131 iterations: 0,
132 }
133 }
134 pub fn build_interference_graph(&mut self, intervals: &[LiveInterval]) -> InterferenceGraph {
136 self.graph = InterferenceGraph::build_from_intervals(intervals);
137 self.graph.clone()
138 }
139 pub fn simplify(&mut self) -> usize {
143 self.iterations += 1;
144 let k = self.num_colors;
145 let low_degree: Vec<LcnfVarId> = self
146 .graph
147 .nodes
148 .iter()
149 .copied()
150 .filter(|&v| self.graph.degree(v) < k)
151 .collect();
152 let removed = low_degree.len();
153 for v in low_degree {
154 self.graph.remove_node(v);
155 self.simplify_stack.push(v);
156 }
157 removed
158 }
159 pub fn coalesce(&mut self, vreg_map: &mut HashMap<LcnfVarId, LcnfVarId>) {
163 let pairs = self.graph.move_pairs.clone();
164 for (u, v) in pairs {
165 let ru = *vreg_map.get(&u).unwrap_or(&u);
166 let rv = *vreg_map.get(&v).unwrap_or(&v);
167 if ru == rv {
168 continue;
169 }
170 if !self.graph.interferes(ru, rv) {
171 let neighbors: Vec<LcnfVarId> = self
172 .graph
173 .edges
174 .get(&rv)
175 .cloned()
176 .unwrap_or_default()
177 .into_iter()
178 .collect();
179 for n in neighbors {
180 self.graph.add_edge(ru, n);
181 }
182 self.graph.remove_node(rv);
183 vreg_map.insert(rv, ru);
184 self.coalesced_count += 1;
185 }
186 }
187 }
188 pub fn spill_select(&mut self, intervals: &[LiveInterval]) -> Option<LcnfVarId> {
190 if self.graph.nodes.is_empty() {
191 return None;
192 }
193 let candidate = self.graph.nodes.iter().copied().min_by(|&a, &b| {
194 let weight_a = intervals
195 .iter()
196 .find(|iv| iv.vreg == a)
197 .map(|iv| iv.spill_weight)
198 .unwrap_or(1.0);
199 let weight_b = intervals
200 .iter()
201 .find(|iv| iv.vreg == b)
202 .map(|iv| iv.spill_weight)
203 .unwrap_or(1.0);
204 weight_a
205 .partial_cmp(&weight_b)
206 .unwrap_or(std::cmp::Ordering::Equal)
207 });
208 if let Some(v) = candidate {
209 self.graph.remove_node(v);
210 self.simplify_stack.push(v);
211 self.potential_spills.push(v);
212 }
213 candidate
214 }
215 #[allow(clippy::too_many_arguments)]
219 pub fn color(
220 &self,
221 k: usize,
222 original_graph: &InterferenceGraph,
223 ) -> HashMap<LcnfVarId, PhysReg> {
224 let mut coloring: HashMap<LcnfVarId, PhysReg> = HashMap::new();
225 for &vreg in self.simplify_stack.iter().rev() {
226 let used_colors: HashSet<u32> = original_graph
227 .edges
228 .get(&vreg)
229 .iter()
230 .flat_map(|neighbors| neighbors.iter())
231 .filter_map(|n| coloring.get(n))
232 .map(|pr| pr.id)
233 .collect();
234 let assigned = (0..k).find(|&c| !used_colors.contains(&(c as u32)));
235 if let Some(c) = assigned {
236 coloring.insert(vreg, self.phys_regs[c].clone());
237 }
238 }
239 coloring
240 }
241 pub fn allocate(&mut self, intervals: &[LiveInterval]) -> Allocation {
245 let original_graph = self.build_interference_graph(intervals);
246 let mut vreg_map: HashMap<LcnfVarId, LcnfVarId> = HashMap::new();
247 loop {
248 let removed = self.simplify();
249 if removed == 0 {
250 if self.graph.nodes.is_empty() {
251 break;
252 }
253 self.coalesce(&mut vreg_map);
254 if self.spill_select(intervals).is_none() {
255 break;
256 }
257 }
258 if self.graph.nodes.is_empty() {
259 break;
260 }
261 }
262 let coloring = self.color(self.num_colors, &original_graph);
263 let mut alloc = Allocation::new();
264 for iv in intervals {
265 let canonical = *vreg_map.get(&iv.vreg).unwrap_or(&iv.vreg);
266 if let Some(preg) = coloring.get(&canonical).or_else(|| coloring.get(&iv.vreg)) {
267 alloc.assign(iv.vreg, preg.clone());
268 } else {
269 alloc.spill(iv.vreg, 8);
270 }
271 }
272 alloc
273 }
274}
275#[derive(Debug, Clone, Default)]
277pub struct RegAllocFeatures {
278 pub(super) flags: std::collections::HashSet<String>,
279}
280impl RegAllocFeatures {
281 pub fn new() -> Self {
282 RegAllocFeatures::default()
283 }
284 pub fn enable(&mut self, flag: impl Into<String>) {
285 self.flags.insert(flag.into());
286 }
287 pub fn disable(&mut self, flag: &str) {
288 self.flags.remove(flag);
289 }
290 pub fn is_enabled(&self, flag: &str) -> bool {
291 self.flags.contains(flag)
292 }
293 pub fn len(&self) -> usize {
294 self.flags.len()
295 }
296 pub fn is_empty(&self) -> bool {
297 self.flags.is_empty()
298 }
299 pub fn union(&self, other: &RegAllocFeatures) -> RegAllocFeatures {
300 RegAllocFeatures {
301 flags: self.flags.union(&other.flags).cloned().collect(),
302 }
303 }
304 pub fn intersection(&self, other: &RegAllocFeatures) -> RegAllocFeatures {
305 RegAllocFeatures {
306 flags: self.flags.intersection(&other.flags).cloned().collect(),
307 }
308 }
309}
310#[derive(Debug, Clone)]
312pub struct SpillCandidate {
313 pub vreg: LcnfVarId,
315 pub cost: f64,
317}
318impl SpillCandidate {
319 pub fn new(vreg: LcnfVarId, cost: f64) -> Self {
321 SpillCandidate { vreg, cost }
322 }
323}
324#[allow(dead_code)]
325#[derive(Debug, Clone)]
326pub struct RAAnalysisCache {
327 pub(super) entries: std::collections::HashMap<String, RACacheEntry>,
328 pub(super) max_size: usize,
329 pub(super) hits: u64,
330 pub(super) misses: u64,
331}
332impl RAAnalysisCache {
333 #[allow(dead_code)]
334 pub fn new(max_size: usize) -> Self {
335 RAAnalysisCache {
336 entries: std::collections::HashMap::new(),
337 max_size,
338 hits: 0,
339 misses: 0,
340 }
341 }
342 #[allow(dead_code)]
343 pub fn get(&mut self, key: &str) -> Option<&RACacheEntry> {
344 if self.entries.contains_key(key) {
345 self.hits += 1;
346 self.entries.get(key)
347 } else {
348 self.misses += 1;
349 None
350 }
351 }
352 #[allow(dead_code)]
353 pub fn insert(&mut self, key: String, data: Vec<u8>) {
354 if self.entries.len() >= self.max_size {
355 if let Some(oldest) = self.entries.keys().next().cloned() {
356 self.entries.remove(&oldest);
357 }
358 }
359 self.entries.insert(
360 key.clone(),
361 RACacheEntry {
362 key,
363 data,
364 timestamp: 0,
365 valid: true,
366 },
367 );
368 }
369 #[allow(dead_code)]
370 pub fn invalidate(&mut self, key: &str) {
371 if let Some(entry) = self.entries.get_mut(key) {
372 entry.valid = false;
373 }
374 }
375 #[allow(dead_code)]
376 pub fn clear(&mut self) {
377 self.entries.clear();
378 }
379 #[allow(dead_code)]
380 pub fn hit_rate(&self) -> f64 {
381 let total = self.hits + self.misses;
382 if total == 0 {
383 return 0.0;
384 }
385 self.hits as f64 / total as f64
386 }
387 #[allow(dead_code)]
388 pub fn size(&self) -> usize {
389 self.entries.len()
390 }
391}
392#[derive(Debug, Clone, Default)]
394pub struct RegAllocReport {
395 pub vregs_allocated: usize,
397 pub spills: usize,
399 pub copies_coalesced: usize,
401 pub coloring_iterations: usize,
403 pub stack_frame_bytes: u32,
405 pub functions_processed: usize,
407}
408impl RegAllocReport {
409 pub fn spill_ratio(&self) -> f64 {
411 let total = self.vregs_allocated + self.spills;
412 if total == 0 {
413 0.0
414 } else {
415 self.spills as f64 / total as f64
416 }
417 }
418}
419#[derive(Debug, Default)]
421pub struct RegAllocDiagCollector {
422 pub(super) msgs: Vec<RegAllocDiagMsg>,
423}
424impl RegAllocDiagCollector {
425 pub fn new() -> Self {
426 RegAllocDiagCollector::default()
427 }
428 pub fn emit(&mut self, d: RegAllocDiagMsg) {
429 self.msgs.push(d);
430 }
431 pub fn has_errors(&self) -> bool {
432 self.msgs
433 .iter()
434 .any(|d| d.severity == RegAllocDiagSeverity::Error)
435 }
436 pub fn errors(&self) -> Vec<&RegAllocDiagMsg> {
437 self.msgs
438 .iter()
439 .filter(|d| d.severity == RegAllocDiagSeverity::Error)
440 .collect()
441 }
442 pub fn warnings(&self) -> Vec<&RegAllocDiagMsg> {
443 self.msgs
444 .iter()
445 .filter(|d| d.severity == RegAllocDiagSeverity::Warning)
446 .collect()
447 }
448 pub fn len(&self) -> usize {
449 self.msgs.len()
450 }
451 pub fn is_empty(&self) -> bool {
452 self.msgs.is_empty()
453 }
454 pub fn clear(&mut self) {
455 self.msgs.clear();
456 }
457}
458#[derive(Debug)]
462pub struct RegAllocPass {
463 pub phys_regs: Vec<PhysReg>,
465 pub use_graph_coloring: bool,
467 pub(super) report: RegAllocReport,
469 pub allocations: HashMap<String, Allocation>,
471}
472impl RegAllocPass {
473 pub fn new(num_regs: u32) -> Self {
475 RegAllocPass {
476 phys_regs: PhysReg::integer_bank(num_regs),
477 use_graph_coloring: false,
478 report: RegAllocReport::default(),
479 allocations: HashMap::new(),
480 }
481 }
482 pub fn graph_coloring(num_regs: u32) -> Self {
484 RegAllocPass {
485 phys_regs: PhysReg::integer_bank(num_regs),
486 use_graph_coloring: true,
487 report: RegAllocReport::default(),
488 allocations: HashMap::new(),
489 }
490 }
491 pub fn with_regs(phys_regs: Vec<PhysReg>, use_graph_coloring: bool) -> Self {
493 RegAllocPass {
494 phys_regs,
495 use_graph_coloring,
496 report: RegAllocReport::default(),
497 allocations: HashMap::new(),
498 }
499 }
500 pub fn run(&mut self, decls: &mut [LcnfFunDecl]) {
502 for decl in decls.iter() {
503 self.report.functions_processed += 1;
504 let alloc = if self.use_graph_coloring {
505 self.run_graph_coloring(decl)
506 } else {
507 self.run_linear_scan(decl)
508 };
509 self.report.vregs_allocated += alloc.reg_map.len();
510 self.report.spills += alloc.spills.len();
511 self.report.stack_frame_bytes += alloc.stack_frame_size();
512 self.allocations.insert(decl.name.clone(), alloc);
513 }
514 }
515 pub(super) fn run_linear_scan(&self, decl: &LcnfFunDecl) -> Allocation {
517 let mut lsa = LinearScanAllocator::new(self.phys_regs.clone());
518 let intervals = lsa.build_live_intervals(decl);
519 let n = self.phys_regs.len();
520 lsa.linear_scan(intervals, n)
521 }
522 pub(super) fn run_graph_coloring(&self, decl: &LcnfFunDecl) -> Allocation {
524 let lsa = LinearScanAllocator::new(self.phys_regs.clone());
525 let intervals = lsa.build_live_intervals(decl);
526 let mut gca = GraphColoringAllocator::new(self.phys_regs.clone());
527 let alloc = gca.allocate(&intervals);
528 let _ = self.report.clone();
529 alloc
530 }
531 pub fn report(&self) -> RegAllocReport {
533 self.report.clone()
534 }
535 pub fn allocation_for(&self, func: &str) -> Option<&Allocation> {
537 self.allocations.get(func)
538 }
539}
540#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
542pub enum RegClass {
543 Integer,
545 Float,
547 Vector,
549 Predicate,
551}
552impl RegClass {
553 pub fn prefix(&self) -> &'static str {
555 match self {
556 RegClass::Integer => "r",
557 RegClass::Float => "f",
558 RegClass::Vector => "v",
559 RegClass::Predicate => "p",
560 }
561 }
562}
563#[derive(Debug, Clone, Default)]
565pub struct Allocation {
566 pub reg_map: HashMap<LcnfVarId, PhysReg>,
568 pub spills: Vec<SpillSlot>,
570 pub(super) next_stack_offset: u32,
572}
573impl Allocation {
574 pub fn new() -> Self {
576 Allocation::default()
577 }
578 pub fn assign(&mut self, vreg: LcnfVarId, preg: PhysReg) {
580 self.reg_map.insert(vreg, preg);
581 }
582 pub fn spill(&mut self, vreg: LcnfVarId, size: u32) -> SpillSlot {
584 let slot = SpillSlot::new(vreg, self.next_stack_offset, size);
585 self.next_stack_offset += size;
586 self.spills.push(slot.clone());
587 slot
588 }
589 pub fn lookup(&self, vreg: LcnfVarId) -> Option<&PhysReg> {
591 self.reg_map.get(&vreg)
592 }
593 pub fn is_spilled(&self, vreg: LcnfVarId) -> bool {
595 self.spills.iter().any(|s| s.vreg == vreg)
596 }
597 pub fn stack_frame_size(&self) -> u32 {
599 self.next_stack_offset
600 }
601}
602#[derive(Debug, Clone)]
604pub struct RegAllocDiagMsg {
605 pub severity: RegAllocDiagSeverity,
606 pub pass: String,
607 pub message: String,
608}
609impl RegAllocDiagMsg {
610 pub fn error(pass: impl Into<String>, msg: impl Into<String>) -> Self {
611 RegAllocDiagMsg {
612 severity: RegAllocDiagSeverity::Error,
613 pass: pass.into(),
614 message: msg.into(),
615 }
616 }
617 pub fn warning(pass: impl Into<String>, msg: impl Into<String>) -> Self {
618 RegAllocDiagMsg {
619 severity: RegAllocDiagSeverity::Warning,
620 pass: pass.into(),
621 message: msg.into(),
622 }
623 }
624 pub fn note(pass: impl Into<String>, msg: impl Into<String>) -> Self {
625 RegAllocDiagMsg {
626 severity: RegAllocDiagSeverity::Note,
627 pass: pass.into(),
628 message: msg.into(),
629 }
630 }
631}
632#[allow(dead_code)]
633#[derive(Debug, Clone)]
634pub struct RACacheEntry {
635 pub key: String,
636 pub data: Vec<u8>,
637 pub timestamp: u64,
638 pub valid: bool,
639}
640#[allow(dead_code)]
642#[derive(Debug, Clone)]
643pub struct RAExtDepGraph {
644 pub(super) n: usize,
645 pub(super) adj: Vec<Vec<usize>>,
646 pub(super) rev: Vec<Vec<usize>>,
647 pub(super) edge_count: usize,
648}
649impl RAExtDepGraph {
650 #[allow(dead_code)]
651 pub fn new(n: usize) -> Self {
652 Self {
653 n,
654 adj: vec![Vec::new(); n],
655 rev: vec![Vec::new(); n],
656 edge_count: 0,
657 }
658 }
659 #[allow(dead_code)]
660 pub fn add_edge(&mut self, from: usize, to: usize) {
661 if from < self.n && to < self.n {
662 if !self.adj[from].contains(&to) {
663 self.adj[from].push(to);
664 self.rev[to].push(from);
665 self.edge_count += 1;
666 }
667 }
668 }
669 #[allow(dead_code)]
670 pub fn succs(&self, n: usize) -> &[usize] {
671 self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
672 }
673 #[allow(dead_code)]
674 pub fn preds(&self, n: usize) -> &[usize] {
675 self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
676 }
677 #[allow(dead_code)]
678 pub fn topo_sort(&self) -> Option<Vec<usize>> {
679 let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
680 let mut q: std::collections::VecDeque<usize> =
681 (0..self.n).filter(|&i| deg[i] == 0).collect();
682 let mut out = Vec::with_capacity(self.n);
683 while let Some(u) = q.pop_front() {
684 out.push(u);
685 for &v in &self.adj[u] {
686 deg[v] -= 1;
687 if deg[v] == 0 {
688 q.push_back(v);
689 }
690 }
691 }
692 if out.len() == self.n {
693 Some(out)
694 } else {
695 None
696 }
697 }
698 #[allow(dead_code)]
699 pub fn has_cycle(&self) -> bool {
700 self.topo_sort().is_none()
701 }
702 #[allow(dead_code)]
703 pub fn reachable(&self, start: usize) -> Vec<usize> {
704 let mut vis = vec![false; self.n];
705 let mut stk = vec![start];
706 let mut out = Vec::new();
707 while let Some(u) = stk.pop() {
708 if u < self.n && !vis[u] {
709 vis[u] = true;
710 out.push(u);
711 for &v in &self.adj[u] {
712 if !vis[v] {
713 stk.push(v);
714 }
715 }
716 }
717 }
718 out
719 }
720 #[allow(dead_code)]
721 pub fn scc(&self) -> Vec<Vec<usize>> {
722 let mut visited = vec![false; self.n];
723 let mut order = Vec::new();
724 for i in 0..self.n {
725 if !visited[i] {
726 let mut stk = vec![(i, 0usize)];
727 while let Some((u, idx)) = stk.last_mut() {
728 if !visited[*u] {
729 visited[*u] = true;
730 }
731 if *idx < self.adj[*u].len() {
732 let v = self.adj[*u][*idx];
733 *idx += 1;
734 if !visited[v] {
735 stk.push((v, 0));
736 }
737 } else {
738 order.push(*u);
739 stk.pop();
740 }
741 }
742 }
743 }
744 let mut comp = vec![usize::MAX; self.n];
745 let mut components: Vec<Vec<usize>> = Vec::new();
746 for &start in order.iter().rev() {
747 if comp[start] == usize::MAX {
748 let cid = components.len();
749 let mut component = Vec::new();
750 let mut stk = vec![start];
751 while let Some(u) = stk.pop() {
752 if comp[u] == usize::MAX {
753 comp[u] = cid;
754 component.push(u);
755 for &v in &self.rev[u] {
756 if comp[v] == usize::MAX {
757 stk.push(v);
758 }
759 }
760 }
761 }
762 components.push(component);
763 }
764 }
765 components
766 }
767 #[allow(dead_code)]
768 pub fn node_count(&self) -> usize {
769 self.n
770 }
771 #[allow(dead_code)]
772 pub fn edge_count(&self) -> usize {
773 self.edge_count
774 }
775}
776#[derive(Debug, Default)]
778pub struct RegAllocProfiler {
779 pub(super) timings: Vec<RegAllocPassTiming>,
780}
781impl RegAllocProfiler {
782 pub fn new() -> Self {
783 RegAllocProfiler::default()
784 }
785 pub fn record(&mut self, t: RegAllocPassTiming) {
786 self.timings.push(t);
787 }
788 pub fn total_elapsed_us(&self) -> u64 {
789 self.timings.iter().map(|t| t.elapsed_us).sum()
790 }
791 pub fn slowest_pass(&self) -> Option<&RegAllocPassTiming> {
792 self.timings.iter().max_by_key(|t| t.elapsed_us)
793 }
794 pub fn num_passes(&self) -> usize {
795 self.timings.len()
796 }
797 pub fn profitable_passes(&self) -> Vec<&RegAllocPassTiming> {
798 self.timings.iter().filter(|t| t.is_profitable()).collect()
799 }
800}
801#[derive(Debug, Clone, Default)]
803pub struct RegAllocEmitStats {
804 pub bytes_emitted: usize,
805 pub items_emitted: usize,
806 pub errors: usize,
807 pub warnings: usize,
808 pub elapsed_ms: u64,
809}
810impl RegAllocEmitStats {
811 pub fn new() -> Self {
812 RegAllocEmitStats::default()
813 }
814 pub fn throughput_bps(&self) -> f64 {
815 if self.elapsed_ms == 0 {
816 0.0
817 } else {
818 self.bytes_emitted as f64 / (self.elapsed_ms as f64 / 1000.0)
819 }
820 }
821 pub fn is_clean(&self) -> bool {
822 self.errors == 0
823 }
824}
825#[derive(Debug, Clone, Default)]
830pub struct InterferenceGraph {
831 pub edges: HashMap<LcnfVarId, HashSet<LcnfVarId>>,
833 pub nodes: HashSet<LcnfVarId>,
835 pub move_pairs: Vec<(LcnfVarId, LcnfVarId)>,
838}
839impl InterferenceGraph {
840 pub fn new() -> Self {
842 InterferenceGraph::default()
843 }
844 pub fn add_node(&mut self, vreg: LcnfVarId) {
846 self.nodes.insert(vreg);
847 self.edges.entry(vreg).or_default();
848 }
849 pub fn add_edge(&mut self, u: LcnfVarId, v: LcnfVarId) {
851 if u == v {
852 return;
853 }
854 self.nodes.insert(u);
855 self.nodes.insert(v);
856 self.edges.entry(u).or_default().insert(v);
857 self.edges.entry(v).or_default().insert(u);
858 }
859 pub fn interferes(&self, u: LcnfVarId, v: LcnfVarId) -> bool {
861 self.edges.get(&u).map(|s| s.contains(&v)).unwrap_or(false)
862 }
863 pub fn degree(&self, vreg: LcnfVarId) -> usize {
865 self.edges.get(&vreg).map(|s| s.len()).unwrap_or(0)
866 }
867 pub fn remove_node(&mut self, vreg: LcnfVarId) {
869 if let Some(neighbors) = self.edges.remove(&vreg) {
870 for n in neighbors {
871 if let Some(s) = self.edges.get_mut(&n) {
872 s.remove(&vreg);
873 }
874 }
875 }
876 self.nodes.remove(&vreg);
877 }
878 pub fn add_move_pair(&mut self, u: LcnfVarId, v: LcnfVarId) {
880 if u != v {
881 self.move_pairs.push((u, v));
882 }
883 }
884 pub fn build_from_intervals(intervals: &[LiveInterval]) -> Self {
886 let mut g = InterferenceGraph::new();
887 for iv in intervals {
888 g.add_node(iv.vreg);
889 }
890 for i in 0..intervals.len() {
891 for j in (i + 1)..intervals.len() {
892 if intervals[i].overlaps(&intervals[j]) {
893 g.add_edge(intervals[i].vreg, intervals[j].vreg);
894 }
895 }
896 }
897 g
898 }
899}
900#[derive(Debug, Clone, PartialEq, Eq, Hash)]
902pub struct RegAllocIncrKey {
903 pub content_hash: u64,
904 pub config_hash: u64,
905}
906impl RegAllocIncrKey {
907 pub fn new(content: u64, config: u64) -> Self {
908 RegAllocIncrKey {
909 content_hash: content,
910 config_hash: config,
911 }
912 }
913 pub fn combined_hash(&self) -> u64 {
914 self.content_hash.wrapping_mul(0x9e3779b97f4a7c15) ^ self.config_hash
915 }
916 pub fn matches(&self, other: &RegAllocIncrKey) -> bool {
917 self.content_hash == other.content_hash && self.config_hash == other.config_hash
918 }
919}
920#[allow(dead_code)]
921#[derive(Debug, Clone)]
922pub struct RALivenessInfo {
923 pub live_in: Vec<std::collections::HashSet<u32>>,
924 pub live_out: Vec<std::collections::HashSet<u32>>,
925 pub defs: Vec<std::collections::HashSet<u32>>,
926 pub uses: Vec<std::collections::HashSet<u32>>,
927}
928impl RALivenessInfo {
929 #[allow(dead_code)]
930 pub fn new(block_count: usize) -> Self {
931 RALivenessInfo {
932 live_in: vec![std::collections::HashSet::new(); block_count],
933 live_out: vec![std::collections::HashSet::new(); block_count],
934 defs: vec![std::collections::HashSet::new(); block_count],
935 uses: vec![std::collections::HashSet::new(); block_count],
936 }
937 }
938 #[allow(dead_code)]
939 pub fn add_def(&mut self, block: usize, var: u32) {
940 if block < self.defs.len() {
941 self.defs[block].insert(var);
942 }
943 }
944 #[allow(dead_code)]
945 pub fn add_use(&mut self, block: usize, var: u32) {
946 if block < self.uses.len() {
947 self.uses[block].insert(var);
948 }
949 }
950 #[allow(dead_code)]
951 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
952 self.live_in
953 .get(block)
954 .map(|s| s.contains(&var))
955 .unwrap_or(false)
956 }
957 #[allow(dead_code)]
958 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
959 self.live_out
960 .get(block)
961 .map(|s| s.contains(&var))
962 .unwrap_or(false)
963 }
964}
965#[allow(dead_code)]
967#[derive(Debug, Clone, Default)]
968pub struct RAExtLiveness {
969 pub live_in: Vec<Vec<usize>>,
970 pub live_out: Vec<Vec<usize>>,
971 pub defs: Vec<Vec<usize>>,
972 pub uses: Vec<Vec<usize>>,
973}
974impl RAExtLiveness {
975 #[allow(dead_code)]
976 pub fn new(n: usize) -> Self {
977 Self {
978 live_in: vec![Vec::new(); n],
979 live_out: vec![Vec::new(); n],
980 defs: vec![Vec::new(); n],
981 uses: vec![Vec::new(); n],
982 }
983 }
984 #[allow(dead_code)]
985 pub fn live_in(&self, b: usize, v: usize) -> bool {
986 self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
987 }
988 #[allow(dead_code)]
989 pub fn live_out(&self, b: usize, v: usize) -> bool {
990 self.live_out
991 .get(b)
992 .map(|s| s.contains(&v))
993 .unwrap_or(false)
994 }
995 #[allow(dead_code)]
996 pub fn add_def(&mut self, b: usize, v: usize) {
997 if let Some(s) = self.defs.get_mut(b) {
998 if !s.contains(&v) {
999 s.push(v);
1000 }
1001 }
1002 }
1003 #[allow(dead_code)]
1004 pub fn add_use(&mut self, b: usize, v: usize) {
1005 if let Some(s) = self.uses.get_mut(b) {
1006 if !s.contains(&v) {
1007 s.push(v);
1008 }
1009 }
1010 }
1011 #[allow(dead_code)]
1012 pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
1013 self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1014 }
1015 #[allow(dead_code)]
1016 pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
1017 self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1018 }
1019}
1020#[derive(Debug, Clone, Default)]
1022pub struct RegAllocConfig {
1023 pub(super) entries: std::collections::HashMap<String, String>,
1024}
1025impl RegAllocConfig {
1026 pub fn new() -> Self {
1027 RegAllocConfig::default()
1028 }
1029 pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) {
1030 self.entries.insert(key.into(), value.into());
1031 }
1032 pub fn get(&self, key: &str) -> Option<&str> {
1033 self.entries.get(key).map(|s| s.as_str())
1034 }
1035 pub fn get_bool(&self, key: &str) -> bool {
1036 matches!(self.get(key), Some("true") | Some("1") | Some("yes"))
1037 }
1038 pub fn get_int(&self, key: &str) -> Option<i64> {
1039 self.get(key)?.parse().ok()
1040 }
1041 pub fn len(&self) -> usize {
1042 self.entries.len()
1043 }
1044 pub fn is_empty(&self) -> bool {
1045 self.entries.is_empty()
1046 }
1047}
1048#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1050pub enum RegAllocDiagSeverity {
1051 Note,
1052 Warning,
1053 Error,
1054}
1055#[allow(dead_code)]
1056#[derive(Debug, Clone)]
1057pub struct RAWorklist {
1058 pub(super) items: std::collections::VecDeque<u32>,
1059 pub(super) in_worklist: std::collections::HashSet<u32>,
1060}
1061impl RAWorklist {
1062 #[allow(dead_code)]
1063 pub fn new() -> Self {
1064 RAWorklist {
1065 items: std::collections::VecDeque::new(),
1066 in_worklist: std::collections::HashSet::new(),
1067 }
1068 }
1069 #[allow(dead_code)]
1070 pub fn push(&mut self, item: u32) -> bool {
1071 if self.in_worklist.insert(item) {
1072 self.items.push_back(item);
1073 true
1074 } else {
1075 false
1076 }
1077 }
1078 #[allow(dead_code)]
1079 pub fn pop(&mut self) -> Option<u32> {
1080 let item = self.items.pop_front()?;
1081 self.in_worklist.remove(&item);
1082 Some(item)
1083 }
1084 #[allow(dead_code)]
1085 pub fn is_empty(&self) -> bool {
1086 self.items.is_empty()
1087 }
1088 #[allow(dead_code)]
1089 pub fn len(&self) -> usize {
1090 self.items.len()
1091 }
1092 #[allow(dead_code)]
1093 pub fn contains(&self, item: u32) -> bool {
1094 self.in_worklist.contains(&item)
1095 }
1096}
1097#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1099pub struct PhysReg {
1100 pub id: u32,
1102 pub name: String,
1104 pub class: RegClass,
1106}
1107impl PhysReg {
1108 pub fn new(id: u32, name: impl Into<String>, class: RegClass) -> Self {
1110 PhysReg {
1111 id,
1112 name: name.into(),
1113 class,
1114 }
1115 }
1116 pub fn integer_bank(n: u32) -> Vec<PhysReg> {
1118 (0..n)
1119 .map(|i| PhysReg::new(i, format!("r{}", i), RegClass::Integer))
1120 .collect()
1121 }
1122 pub fn float_bank(n: u32) -> Vec<PhysReg> {
1124 (0..n)
1125 .map(|i| PhysReg::new(i, format!("f{}", i), RegClass::Float))
1126 .collect()
1127 }
1128}
1129#[derive(Debug, Default)]
1131pub struct RegAllocIdGen {
1132 pub(super) next: u32,
1133}
1134impl RegAllocIdGen {
1135 pub fn new() -> Self {
1136 RegAllocIdGen::default()
1137 }
1138 pub fn next_id(&mut self) -> u32 {
1139 let id = self.next;
1140 self.next += 1;
1141 id
1142 }
1143 pub fn peek_next(&self) -> u32 {
1144 self.next
1145 }
1146 pub fn reset(&mut self) {
1147 self.next = 0;
1148 }
1149 pub fn skip(&mut self, n: u32) {
1150 self.next += n;
1151 }
1152}
1153#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1155pub struct SpillSlot {
1156 pub vreg: LcnfVarId,
1158 pub offset: u32,
1160 pub size: u32,
1162}
1163impl SpillSlot {
1164 pub fn new(vreg: LcnfVarId, offset: u32, size: u32) -> Self {
1166 SpillSlot { vreg, offset, size }
1167 }
1168}
1169#[allow(dead_code)]
1170#[derive(Debug, Clone)]
1171pub struct RAPassConfig {
1172 pub phase: RAPassPhase,
1173 pub enabled: bool,
1174 pub max_iterations: u32,
1175 pub debug_output: bool,
1176 pub pass_name: String,
1177}
1178impl RAPassConfig {
1179 #[allow(dead_code)]
1180 pub fn new(name: impl Into<String>, phase: RAPassPhase) -> Self {
1181 RAPassConfig {
1182 phase,
1183 enabled: true,
1184 max_iterations: 10,
1185 debug_output: false,
1186 pass_name: name.into(),
1187 }
1188 }
1189 #[allow(dead_code)]
1190 pub fn disabled(mut self) -> Self {
1191 self.enabled = false;
1192 self
1193 }
1194 #[allow(dead_code)]
1195 pub fn with_debug(mut self) -> Self {
1196 self.debug_output = true;
1197 self
1198 }
1199 #[allow(dead_code)]
1200 pub fn max_iter(mut self, n: u32) -> Self {
1201 self.max_iterations = n;
1202 self
1203 }
1204}
1205#[derive(Debug, Default)]
1207pub struct RegAllocSourceBuffer {
1208 pub(super) buf: String,
1209 pub(super) indent_level: usize,
1210 pub(super) indent_str: String,
1211}
1212impl RegAllocSourceBuffer {
1213 pub fn new() -> Self {
1214 RegAllocSourceBuffer {
1215 buf: String::new(),
1216 indent_level: 0,
1217 indent_str: " ".to_string(),
1218 }
1219 }
1220 pub fn with_indent(mut self, indent: impl Into<String>) -> Self {
1221 self.indent_str = indent.into();
1222 self
1223 }
1224 pub fn push_line(&mut self, line: &str) {
1225 for _ in 0..self.indent_level {
1226 self.buf.push_str(&self.indent_str);
1227 }
1228 self.buf.push_str(line);
1229 self.buf.push('\n');
1230 }
1231 pub fn push_raw(&mut self, s: &str) {
1232 self.buf.push_str(s);
1233 }
1234 pub fn indent(&mut self) {
1235 self.indent_level += 1;
1236 }
1237 pub fn dedent(&mut self) {
1238 self.indent_level = self.indent_level.saturating_sub(1);
1239 }
1240 pub fn as_str(&self) -> &str {
1241 &self.buf
1242 }
1243 pub fn len(&self) -> usize {
1244 self.buf.len()
1245 }
1246 pub fn is_empty(&self) -> bool {
1247 self.buf.is_empty()
1248 }
1249 pub fn line_count(&self) -> usize {
1250 self.buf.lines().count()
1251 }
1252 pub fn into_string(self) -> String {
1253 self.buf
1254 }
1255 pub fn reset(&mut self) {
1256 self.buf.clear();
1257 self.indent_level = 0;
1258 }
1259}
1260#[derive(Debug, Default)]
1262pub struct RegAllocNameScope {
1263 pub(super) declared: std::collections::HashSet<String>,
1264 pub(super) depth: usize,
1265 pub(super) parent: Option<Box<RegAllocNameScope>>,
1266}
1267impl RegAllocNameScope {
1268 pub fn new() -> Self {
1269 RegAllocNameScope::default()
1270 }
1271 pub fn declare(&mut self, name: impl Into<String>) -> bool {
1272 self.declared.insert(name.into())
1273 }
1274 pub fn is_declared(&self, name: &str) -> bool {
1275 self.declared.contains(name)
1276 }
1277 pub fn push_scope(self) -> Self {
1278 RegAllocNameScope {
1279 declared: std::collections::HashSet::new(),
1280 depth: self.depth + 1,
1281 parent: Some(Box::new(self)),
1282 }
1283 }
1284 pub fn pop_scope(self) -> Self {
1285 *self.parent.unwrap_or_default()
1286 }
1287 pub fn depth(&self) -> usize {
1288 self.depth
1289 }
1290 pub fn len(&self) -> usize {
1291 self.declared.len()
1292 }
1293}
1294#[derive(Debug, Clone)]
1296pub struct RegAllocPassTiming {
1297 pub pass_name: String,
1298 pub elapsed_us: u64,
1299 pub items_processed: usize,
1300 pub bytes_before: usize,
1301 pub bytes_after: usize,
1302}
1303impl RegAllocPassTiming {
1304 pub fn new(
1305 pass_name: impl Into<String>,
1306 elapsed_us: u64,
1307 items: usize,
1308 before: usize,
1309 after: usize,
1310 ) -> Self {
1311 RegAllocPassTiming {
1312 pass_name: pass_name.into(),
1313 elapsed_us,
1314 items_processed: items,
1315 bytes_before: before,
1316 bytes_after: after,
1317 }
1318 }
1319 pub fn throughput_mps(&self) -> f64 {
1320 if self.elapsed_us == 0 {
1321 0.0
1322 } else {
1323 self.items_processed as f64 / (self.elapsed_us as f64 / 1_000_000.0)
1324 }
1325 }
1326 pub fn size_ratio(&self) -> f64 {
1327 if self.bytes_before == 0 {
1328 1.0
1329 } else {
1330 self.bytes_after as f64 / self.bytes_before as f64
1331 }
1332 }
1333 pub fn is_profitable(&self) -> bool {
1334 self.size_ratio() <= 1.05
1335 }
1336}
1337#[allow(dead_code)]
1338#[derive(Debug, Clone, Default)]
1339pub struct RAPassStats {
1340 pub total_runs: u32,
1341 pub successful_runs: u32,
1342 pub total_changes: u64,
1343 pub time_ms: u64,
1344 pub iterations_used: u32,
1345}
1346impl RAPassStats {
1347 #[allow(dead_code)]
1348 pub fn new() -> Self {
1349 Self::default()
1350 }
1351 #[allow(dead_code)]
1352 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1353 self.total_runs += 1;
1354 self.successful_runs += 1;
1355 self.total_changes += changes;
1356 self.time_ms += time_ms;
1357 self.iterations_used = iterations;
1358 }
1359 #[allow(dead_code)]
1360 pub fn average_changes_per_run(&self) -> f64 {
1361 if self.total_runs == 0 {
1362 return 0.0;
1363 }
1364 self.total_changes as f64 / self.total_runs as f64
1365 }
1366 #[allow(dead_code)]
1367 pub fn success_rate(&self) -> f64 {
1368 if self.total_runs == 0 {
1369 return 0.0;
1370 }
1371 self.successful_runs as f64 / self.total_runs as f64
1372 }
1373 #[allow(dead_code)]
1374 pub fn format_summary(&self) -> String {
1375 format!(
1376 "Runs: {}/{}, Changes: {}, Time: {}ms",
1377 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1378 )
1379 }
1380}
1381#[allow(dead_code)]
1382pub struct RAConstantFoldingHelper;
1383impl RAConstantFoldingHelper {
1384 #[allow(dead_code)]
1385 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1386 a.checked_add(b)
1387 }
1388 #[allow(dead_code)]
1389 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1390 a.checked_sub(b)
1391 }
1392 #[allow(dead_code)]
1393 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1394 a.checked_mul(b)
1395 }
1396 #[allow(dead_code)]
1397 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1398 if b == 0 {
1399 None
1400 } else {
1401 a.checked_div(b)
1402 }
1403 }
1404 #[allow(dead_code)]
1405 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1406 a + b
1407 }
1408 #[allow(dead_code)]
1409 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1410 a * b
1411 }
1412 #[allow(dead_code)]
1413 pub fn fold_neg_i64(a: i64) -> Option<i64> {
1414 a.checked_neg()
1415 }
1416 #[allow(dead_code)]
1417 pub fn fold_not_bool(a: bool) -> bool {
1418 !a
1419 }
1420 #[allow(dead_code)]
1421 pub fn fold_and_bool(a: bool, b: bool) -> bool {
1422 a && b
1423 }
1424 #[allow(dead_code)]
1425 pub fn fold_or_bool(a: bool, b: bool) -> bool {
1426 a || b
1427 }
1428 #[allow(dead_code)]
1429 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1430 a.checked_shl(b)
1431 }
1432 #[allow(dead_code)]
1433 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1434 a.checked_shr(b)
1435 }
1436 #[allow(dead_code)]
1437 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1438 if b == 0 {
1439 None
1440 } else {
1441 Some(a % b)
1442 }
1443 }
1444 #[allow(dead_code)]
1445 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1446 a & b
1447 }
1448 #[allow(dead_code)]
1449 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1450 a | b
1451 }
1452 #[allow(dead_code)]
1453 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1454 a ^ b
1455 }
1456 #[allow(dead_code)]
1457 pub fn fold_bitnot_i64(a: i64) -> i64 {
1458 !a
1459 }
1460}
1461#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1463pub struct VirtualReg {
1464 pub id: u32,
1466 pub ty: LcnfType,
1468 pub hint: Option<PhysReg>,
1470}
1471impl VirtualReg {
1472 pub fn new(id: u32, ty: LcnfType) -> Self {
1474 VirtualReg { id, ty, hint: None }
1475 }
1476 pub fn with_hint(id: u32, ty: LcnfType, hint: PhysReg) -> Self {
1478 VirtualReg {
1479 id,
1480 ty,
1481 hint: Some(hint),
1482 }
1483 }
1484 pub fn reg_class(&self) -> RegClass {
1486 match &self.ty {
1487 LcnfType::Fun(_, _) => RegClass::Integer,
1488 LcnfType::Nat => RegClass::Integer,
1489 LcnfType::LcnfString => RegClass::Integer,
1490 LcnfType::Object => RegClass::Integer,
1491 LcnfType::Ctor(_, _) => RegClass::Integer,
1492 LcnfType::Var(_) => RegClass::Integer,
1493 LcnfType::Erased | LcnfType::Unit | LcnfType::Irrelevant => RegClass::Integer,
1494 }
1495 }
1496}
1497#[derive(Debug, Clone, PartialEq)]
1502pub struct LiveInterval {
1503 pub vreg: LcnfVarId,
1505 pub start: u32,
1507 pub end: u32,
1509 pub uses: Vec<u32>,
1511 pub defs: Vec<u32>,
1513 pub spill_weight: f64,
1515}
1516impl LiveInterval {
1517 pub fn new(vreg: LcnfVarId, start: u32, end: u32) -> Self {
1519 LiveInterval {
1520 vreg,
1521 start,
1522 end,
1523 uses: vec![],
1524 defs: vec![],
1525 spill_weight: 1.0,
1526 }
1527 }
1528 pub fn overlaps(&self, other: &LiveInterval) -> bool {
1530 self.start < other.end && other.start < self.end
1531 }
1532 pub fn length(&self) -> u32 {
1534 self.end.saturating_sub(self.start)
1535 }
1536 pub fn add_use(&mut self, pos: u32) {
1538 if !self.uses.contains(&pos) {
1539 self.uses.push(pos);
1540 if pos >= self.end {
1541 self.end = pos + 1;
1542 }
1543 }
1544 }
1545 pub fn add_def(&mut self, pos: u32) {
1547 if !self.defs.contains(&pos) {
1548 self.defs.push(pos);
1549 if pos < self.start {
1550 self.start = pos;
1551 }
1552 }
1553 }
1554 pub fn compute_spill_weight(&mut self) {
1556 let len = self.length().max(1) as f64;
1557 self.spill_weight = self.uses.len() as f64 / len;
1558 }
1559}
1560#[allow(dead_code)]
1561pub struct RAPassRegistry {
1562 pub(super) configs: Vec<RAPassConfig>,
1563 pub(super) stats: std::collections::HashMap<String, RAPassStats>,
1564}
1565impl RAPassRegistry {
1566 #[allow(dead_code)]
1567 pub fn new() -> Self {
1568 RAPassRegistry {
1569 configs: Vec::new(),
1570 stats: std::collections::HashMap::new(),
1571 }
1572 }
1573 #[allow(dead_code)]
1574 pub fn register(&mut self, config: RAPassConfig) {
1575 self.stats
1576 .insert(config.pass_name.clone(), RAPassStats::new());
1577 self.configs.push(config);
1578 }
1579 #[allow(dead_code)]
1580 pub fn enabled_passes(&self) -> Vec<&RAPassConfig> {
1581 self.configs.iter().filter(|c| c.enabled).collect()
1582 }
1583 #[allow(dead_code)]
1584 pub fn get_stats(&self, name: &str) -> Option<&RAPassStats> {
1585 self.stats.get(name)
1586 }
1587 #[allow(dead_code)]
1588 pub fn total_passes(&self) -> usize {
1589 self.configs.len()
1590 }
1591 #[allow(dead_code)]
1592 pub fn enabled_count(&self) -> usize {
1593 self.enabled_passes().len()
1594 }
1595 #[allow(dead_code)]
1596 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
1597 if let Some(stats) = self.stats.get_mut(name) {
1598 stats.record_run(changes, time_ms, iter);
1599 }
1600 }
1601}
1602#[allow(dead_code)]
1604#[derive(Debug, Clone)]
1605pub struct RAExtPassConfig {
1606 pub name: String,
1607 pub phase: RAExtPassPhase,
1608 pub enabled: bool,
1609 pub max_iterations: usize,
1610 pub debug: u32,
1611 pub timeout_ms: Option<u64>,
1612}
1613impl RAExtPassConfig {
1614 #[allow(dead_code)]
1615 pub fn new(name: impl Into<String>) -> Self {
1616 Self {
1617 name: name.into(),
1618 phase: RAExtPassPhase::Middle,
1619 enabled: true,
1620 max_iterations: 100,
1621 debug: 0,
1622 timeout_ms: None,
1623 }
1624 }
1625 #[allow(dead_code)]
1626 pub fn with_phase(mut self, phase: RAExtPassPhase) -> Self {
1627 self.phase = phase;
1628 self
1629 }
1630 #[allow(dead_code)]
1631 pub fn with_max_iter(mut self, n: usize) -> Self {
1632 self.max_iterations = n;
1633 self
1634 }
1635 #[allow(dead_code)]
1636 pub fn with_debug(mut self, d: u32) -> Self {
1637 self.debug = d;
1638 self
1639 }
1640 #[allow(dead_code)]
1641 pub fn disabled(mut self) -> Self {
1642 self.enabled = false;
1643 self
1644 }
1645 #[allow(dead_code)]
1646 pub fn with_timeout(mut self, ms: u64) -> Self {
1647 self.timeout_ms = Some(ms);
1648 self
1649 }
1650 #[allow(dead_code)]
1651 pub fn is_debug_enabled(&self) -> bool {
1652 self.debug > 0
1653 }
1654}
1655#[allow(dead_code)]
1657#[derive(Debug, Clone)]
1658pub struct RAExtWorklist {
1659 pub(super) items: std::collections::VecDeque<usize>,
1660 pub(super) present: Vec<bool>,
1661}
1662impl RAExtWorklist {
1663 #[allow(dead_code)]
1664 pub fn new(capacity: usize) -> Self {
1665 Self {
1666 items: std::collections::VecDeque::new(),
1667 present: vec![false; capacity],
1668 }
1669 }
1670 #[allow(dead_code)]
1671 pub fn push(&mut self, id: usize) {
1672 if id < self.present.len() && !self.present[id] {
1673 self.present[id] = true;
1674 self.items.push_back(id);
1675 }
1676 }
1677 #[allow(dead_code)]
1678 pub fn push_front(&mut self, id: usize) {
1679 if id < self.present.len() && !self.present[id] {
1680 self.present[id] = true;
1681 self.items.push_front(id);
1682 }
1683 }
1684 #[allow(dead_code)]
1685 pub fn pop(&mut self) -> Option<usize> {
1686 let id = self.items.pop_front()?;
1687 if id < self.present.len() {
1688 self.present[id] = false;
1689 }
1690 Some(id)
1691 }
1692 #[allow(dead_code)]
1693 pub fn is_empty(&self) -> bool {
1694 self.items.is_empty()
1695 }
1696 #[allow(dead_code)]
1697 pub fn len(&self) -> usize {
1698 self.items.len()
1699 }
1700 #[allow(dead_code)]
1701 pub fn contains(&self, id: usize) -> bool {
1702 id < self.present.len() && self.present[id]
1703 }
1704 #[allow(dead_code)]
1705 pub fn drain_all(&mut self) -> Vec<usize> {
1706 let v: Vec<usize> = self.items.drain(..).collect();
1707 for &id in &v {
1708 if id < self.present.len() {
1709 self.present[id] = false;
1710 }
1711 }
1712 v
1713 }
1714}
1715#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1717pub struct RegAllocVersion {
1718 pub major: u32,
1719 pub minor: u32,
1720 pub patch: u32,
1721 pub pre: Option<String>,
1722}
1723impl RegAllocVersion {
1724 pub fn new(major: u32, minor: u32, patch: u32) -> Self {
1725 RegAllocVersion {
1726 major,
1727 minor,
1728 patch,
1729 pre: None,
1730 }
1731 }
1732 pub fn with_pre(mut self, pre: impl Into<String>) -> Self {
1733 self.pre = Some(pre.into());
1734 self
1735 }
1736 pub fn is_stable(&self) -> bool {
1737 self.pre.is_none()
1738 }
1739 pub fn is_compatible_with(&self, other: &RegAllocVersion) -> bool {
1740 self.major == other.major && self.minor >= other.minor
1741 }
1742}
1743#[allow(dead_code)]
1744#[derive(Debug, Clone, PartialEq)]
1745pub enum RAPassPhase {
1746 Analysis,
1747 Transformation,
1748 Verification,
1749 Cleanup,
1750}
1751impl RAPassPhase {
1752 #[allow(dead_code)]
1753 pub fn name(&self) -> &str {
1754 match self {
1755 RAPassPhase::Analysis => "analysis",
1756 RAPassPhase::Transformation => "transformation",
1757 RAPassPhase::Verification => "verification",
1758 RAPassPhase::Cleanup => "cleanup",
1759 }
1760 }
1761 #[allow(dead_code)]
1762 pub fn is_modifying(&self) -> bool {
1763 matches!(self, RAPassPhase::Transformation | RAPassPhase::Cleanup)
1764 }
1765}
1766#[allow(dead_code)]
1768#[derive(Debug, Clone, Default)]
1769pub struct RAExtPassStats {
1770 pub iterations: usize,
1771 pub changed: bool,
1772 pub nodes_visited: usize,
1773 pub nodes_modified: usize,
1774 pub time_ms: u64,
1775 pub memory_bytes: usize,
1776 pub errors: usize,
1777}
1778impl RAExtPassStats {
1779 #[allow(dead_code)]
1780 pub fn new() -> Self {
1781 Self::default()
1782 }
1783 #[allow(dead_code)]
1784 pub fn visit(&mut self) {
1785 self.nodes_visited += 1;
1786 }
1787 #[allow(dead_code)]
1788 pub fn modify(&mut self) {
1789 self.nodes_modified += 1;
1790 self.changed = true;
1791 }
1792 #[allow(dead_code)]
1793 pub fn iterate(&mut self) {
1794 self.iterations += 1;
1795 }
1796 #[allow(dead_code)]
1797 pub fn error(&mut self) {
1798 self.errors += 1;
1799 }
1800 #[allow(dead_code)]
1801 pub fn efficiency(&self) -> f64 {
1802 if self.nodes_visited == 0 {
1803 0.0
1804 } else {
1805 self.nodes_modified as f64 / self.nodes_visited as f64
1806 }
1807 }
1808 #[allow(dead_code)]
1809 pub fn merge(&mut self, o: &RAExtPassStats) {
1810 self.iterations += o.iterations;
1811 self.changed |= o.changed;
1812 self.nodes_visited += o.nodes_visited;
1813 self.nodes_modified += o.nodes_modified;
1814 self.time_ms += o.time_ms;
1815 self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
1816 self.errors += o.errors;
1817 }
1818}
1819#[allow(dead_code)]
1821#[derive(Debug, Default)]
1822pub struct RAExtPassRegistry {
1823 pub(super) configs: Vec<RAExtPassConfig>,
1824 pub(super) stats: Vec<RAExtPassStats>,
1825}
1826impl RAExtPassRegistry {
1827 #[allow(dead_code)]
1828 pub fn new() -> Self {
1829 Self::default()
1830 }
1831 #[allow(dead_code)]
1832 pub fn register(&mut self, c: RAExtPassConfig) {
1833 self.stats.push(RAExtPassStats::new());
1834 self.configs.push(c);
1835 }
1836 #[allow(dead_code)]
1837 pub fn len(&self) -> usize {
1838 self.configs.len()
1839 }
1840 #[allow(dead_code)]
1841 pub fn is_empty(&self) -> bool {
1842 self.configs.is_empty()
1843 }
1844 #[allow(dead_code)]
1845 pub fn get(&self, i: usize) -> Option<&RAExtPassConfig> {
1846 self.configs.get(i)
1847 }
1848 #[allow(dead_code)]
1849 pub fn get_stats(&self, i: usize) -> Option<&RAExtPassStats> {
1850 self.stats.get(i)
1851 }
1852 #[allow(dead_code)]
1853 pub fn enabled_passes(&self) -> Vec<&RAExtPassConfig> {
1854 self.configs.iter().filter(|c| c.enabled).collect()
1855 }
1856 #[allow(dead_code)]
1857 pub fn passes_in_phase(&self, ph: &RAExtPassPhase) -> Vec<&RAExtPassConfig> {
1858 self.configs
1859 .iter()
1860 .filter(|c| c.enabled && &c.phase == ph)
1861 .collect()
1862 }
1863 #[allow(dead_code)]
1864 pub fn total_nodes_visited(&self) -> usize {
1865 self.stats.iter().map(|s| s.nodes_visited).sum()
1866 }
1867 #[allow(dead_code)]
1868 pub fn any_changed(&self) -> bool {
1869 self.stats.iter().any(|s| s.changed)
1870 }
1871}
1872#[allow(dead_code)]
1874#[derive(Debug)]
1875pub struct RAExtCache {
1876 pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
1877 pub(super) cap: usize,
1878 pub(super) total_hits: u64,
1879 pub(super) total_misses: u64,
1880}
1881impl RAExtCache {
1882 #[allow(dead_code)]
1883 pub fn new(cap: usize) -> Self {
1884 Self {
1885 entries: Vec::new(),
1886 cap,
1887 total_hits: 0,
1888 total_misses: 0,
1889 }
1890 }
1891 #[allow(dead_code)]
1892 pub fn get(&mut self, key: u64) -> Option<&[u8]> {
1893 for e in self.entries.iter_mut() {
1894 if e.0 == key && e.2 {
1895 e.3 += 1;
1896 self.total_hits += 1;
1897 return Some(&e.1);
1898 }
1899 }
1900 self.total_misses += 1;
1901 None
1902 }
1903 #[allow(dead_code)]
1904 pub fn put(&mut self, key: u64, data: Vec<u8>) {
1905 if self.entries.len() >= self.cap {
1906 self.entries.retain(|e| e.2);
1907 if self.entries.len() >= self.cap {
1908 self.entries.remove(0);
1909 }
1910 }
1911 self.entries.push((key, data, true, 0));
1912 }
1913 #[allow(dead_code)]
1914 pub fn invalidate(&mut self) {
1915 for e in self.entries.iter_mut() {
1916 e.2 = false;
1917 }
1918 }
1919 #[allow(dead_code)]
1920 pub fn hit_rate(&self) -> f64 {
1921 let t = self.total_hits + self.total_misses;
1922 if t == 0 {
1923 0.0
1924 } else {
1925 self.total_hits as f64 / t as f64
1926 }
1927 }
1928 #[allow(dead_code)]
1929 pub fn live_count(&self) -> usize {
1930 self.entries.iter().filter(|e| e.2).count()
1931 }
1932}
1933#[allow(dead_code)]
1935#[derive(Debug, Clone, Default)]
1936pub struct RAExtConstFolder {
1937 pub(super) folds: usize,
1938 pub(super) failures: usize,
1939 pub(super) enabled: bool,
1940}
1941impl RAExtConstFolder {
1942 #[allow(dead_code)]
1943 pub fn new() -> Self {
1944 Self {
1945 folds: 0,
1946 failures: 0,
1947 enabled: true,
1948 }
1949 }
1950 #[allow(dead_code)]
1951 pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1952 self.folds += 1;
1953 a.checked_add(b)
1954 }
1955 #[allow(dead_code)]
1956 pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1957 self.folds += 1;
1958 a.checked_sub(b)
1959 }
1960 #[allow(dead_code)]
1961 pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1962 self.folds += 1;
1963 a.checked_mul(b)
1964 }
1965 #[allow(dead_code)]
1966 pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1967 if b == 0 {
1968 self.failures += 1;
1969 None
1970 } else {
1971 self.folds += 1;
1972 a.checked_div(b)
1973 }
1974 }
1975 #[allow(dead_code)]
1976 pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1977 if b == 0 {
1978 self.failures += 1;
1979 None
1980 } else {
1981 self.folds += 1;
1982 a.checked_rem(b)
1983 }
1984 }
1985 #[allow(dead_code)]
1986 pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
1987 self.folds += 1;
1988 a.checked_neg()
1989 }
1990 #[allow(dead_code)]
1991 pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1992 if s >= 64 {
1993 self.failures += 1;
1994 None
1995 } else {
1996 self.folds += 1;
1997 a.checked_shl(s)
1998 }
1999 }
2000 #[allow(dead_code)]
2001 pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
2002 if s >= 64 {
2003 self.failures += 1;
2004 None
2005 } else {
2006 self.folds += 1;
2007 a.checked_shr(s)
2008 }
2009 }
2010 #[allow(dead_code)]
2011 pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
2012 self.folds += 1;
2013 a & b
2014 }
2015 #[allow(dead_code)]
2016 pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
2017 self.folds += 1;
2018 a | b
2019 }
2020 #[allow(dead_code)]
2021 pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
2022 self.folds += 1;
2023 a ^ b
2024 }
2025 #[allow(dead_code)]
2026 pub fn not_i64(&mut self, a: i64) -> i64 {
2027 self.folds += 1;
2028 !a
2029 }
2030 #[allow(dead_code)]
2031 pub fn fold_count(&self) -> usize {
2032 self.folds
2033 }
2034 #[allow(dead_code)]
2035 pub fn failure_count(&self) -> usize {
2036 self.failures
2037 }
2038 #[allow(dead_code)]
2039 pub fn enable(&mut self) {
2040 self.enabled = true;
2041 }
2042 #[allow(dead_code)]
2043 pub fn disable(&mut self) {
2044 self.enabled = false;
2045 }
2046 #[allow(dead_code)]
2047 pub fn is_enabled(&self) -> bool {
2048 self.enabled
2049 }
2050}
2051#[derive(Debug)]
2060pub struct LinearScanAllocator {
2061 pub phys_regs: Vec<PhysReg>,
2063 pub spill_count: usize,
2065}
2066impl LinearScanAllocator {
2067 pub fn new(phys_regs: Vec<PhysReg>) -> Self {
2069 LinearScanAllocator {
2070 phys_regs,
2071 spill_count: 0,
2072 }
2073 }
2074 pub fn build_live_intervals(&self, decl: &LcnfFunDecl) -> Vec<LiveInterval> {
2078 let mut counter = 0u32;
2079 let mut intervals: HashMap<LcnfVarId, LiveInterval> = HashMap::new();
2080 for param in &decl.params {
2081 let mut iv = LiveInterval::new(param.id, 0, 1);
2082 iv.add_def(0);
2083 intervals.insert(param.id, iv);
2084 }
2085 collect_intervals_from_expr(&decl.body, &mut counter, &mut intervals);
2086 let mut result: Vec<LiveInterval> = intervals.into_values().collect();
2087 for iv in &mut result {
2088 iv.compute_spill_weight();
2089 }
2090 result.sort_by_key(|iv| iv.start);
2091 result
2092 }
2093 pub fn linear_scan(&mut self, intervals: Vec<LiveInterval>, num_phys: usize) -> Allocation {
2097 let mut alloc = Allocation::new();
2098 let mut free_regs: Vec<usize> = (0..num_phys.min(self.phys_regs.len())).collect();
2099 let mut active: Vec<(u32, LcnfVarId, usize)> = Vec::new();
2100 for iv in &intervals {
2101 active.retain(|&(end, old_vreg, preg_idx)| {
2102 if end <= iv.start {
2103 free_regs.push(preg_idx);
2104 let _ = old_vreg;
2105 false
2106 } else {
2107 true
2108 }
2109 });
2110 if let Some(preg_idx) = free_regs.pop() {
2111 alloc.assign(iv.vreg, self.phys_regs[preg_idx].clone());
2112 active.push((iv.end, iv.vreg, preg_idx));
2113 active.sort_by_key(|&(end, _, _)| end);
2114 } else {
2115 if let Some(&(far_end, far_vreg, preg_idx)) = active.last() {
2116 if far_end > iv.end {
2117 alloc.spill(far_vreg, 8);
2118 self.spill_count += 1;
2119 active.pop();
2120 alloc.assign(iv.vreg, self.phys_regs[preg_idx].clone());
2121 active.push((iv.end, iv.vreg, preg_idx));
2122 active.sort_by_key(|&(end, _, _)| end);
2123 } else {
2124 alloc.spill(iv.vreg, 8);
2125 self.spill_count += 1;
2126 }
2127 } else {
2128 alloc.spill(iv.vreg, 8);
2129 self.spill_count += 1;
2130 }
2131 }
2132 }
2133 alloc
2134 }
2135 pub fn handle_spill(&mut self, vreg: LcnfVarId, alloc: &mut Allocation) -> SpillSlot {
2137 self.spill_count += 1;
2138 alloc.spill(vreg, 8)
2139 }
2140}
2141#[derive(Debug)]
2143pub struct RegAllocEventLog {
2144 pub(super) entries: std::collections::VecDeque<String>,
2145 pub(super) capacity: usize,
2146}
2147impl RegAllocEventLog {
2148 pub fn new(capacity: usize) -> Self {
2149 RegAllocEventLog {
2150 entries: std::collections::VecDeque::with_capacity(capacity),
2151 capacity,
2152 }
2153 }
2154 pub fn push(&mut self, event: impl Into<String>) {
2155 if self.entries.len() >= self.capacity {
2156 self.entries.pop_front();
2157 }
2158 self.entries.push_back(event.into());
2159 }
2160 pub fn iter(&self) -> impl Iterator<Item = &String> {
2161 self.entries.iter()
2162 }
2163 pub fn len(&self) -> usize {
2164 self.entries.len()
2165 }
2166 pub fn is_empty(&self) -> bool {
2167 self.entries.is_empty()
2168 }
2169 pub fn capacity(&self) -> usize {
2170 self.capacity
2171 }
2172 pub fn clear(&mut self) {
2173 self.entries.clear();
2174 }
2175}
2176#[allow(dead_code)]
2177#[derive(Debug, Clone)]
2178pub struct RADepGraph {
2179 pub(super) nodes: Vec<u32>,
2180 pub(super) edges: Vec<(u32, u32)>,
2181}
2182impl RADepGraph {
2183 #[allow(dead_code)]
2184 pub fn new() -> Self {
2185 RADepGraph {
2186 nodes: Vec::new(),
2187 edges: Vec::new(),
2188 }
2189 }
2190 #[allow(dead_code)]
2191 pub fn add_node(&mut self, id: u32) {
2192 if !self.nodes.contains(&id) {
2193 self.nodes.push(id);
2194 }
2195 }
2196 #[allow(dead_code)]
2197 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
2198 self.add_node(dep);
2199 self.add_node(dependent);
2200 self.edges.push((dep, dependent));
2201 }
2202 #[allow(dead_code)]
2203 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
2204 self.edges
2205 .iter()
2206 .filter(|(d, _)| *d == node)
2207 .map(|(_, dep)| *dep)
2208 .collect()
2209 }
2210 #[allow(dead_code)]
2211 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
2212 self.edges
2213 .iter()
2214 .filter(|(_, dep)| *dep == node)
2215 .map(|(d, _)| *d)
2216 .collect()
2217 }
2218 #[allow(dead_code)]
2219 pub fn topological_sort(&self) -> Vec<u32> {
2220 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
2221 for &n in &self.nodes {
2222 in_degree.insert(n, 0);
2223 }
2224 for (_, dep) in &self.edges {
2225 *in_degree.entry(*dep).or_insert(0) += 1;
2226 }
2227 let mut queue: std::collections::VecDeque<u32> = self
2228 .nodes
2229 .iter()
2230 .filter(|&&n| in_degree[&n] == 0)
2231 .copied()
2232 .collect();
2233 let mut result = Vec::new();
2234 while let Some(node) = queue.pop_front() {
2235 result.push(node);
2236 for dep in self.dependents_of(node) {
2237 let cnt = in_degree.entry(dep).or_insert(0);
2238 *cnt = cnt.saturating_sub(1);
2239 if *cnt == 0 {
2240 queue.push_back(dep);
2241 }
2242 }
2243 }
2244 result
2245 }
2246 #[allow(dead_code)]
2247 pub fn has_cycle(&self) -> bool {
2248 self.topological_sort().len() < self.nodes.len()
2249 }
2250}
2251#[allow(dead_code)]
2253#[derive(Debug, Clone)]
2254pub struct RAExtDomTree {
2255 pub(super) idom: Vec<Option<usize>>,
2256 pub(super) children: Vec<Vec<usize>>,
2257 pub(super) depth: Vec<usize>,
2258}
2259impl RAExtDomTree {
2260 #[allow(dead_code)]
2261 pub fn new(n: usize) -> Self {
2262 Self {
2263 idom: vec![None; n],
2264 children: vec![Vec::new(); n],
2265 depth: vec![0; n],
2266 }
2267 }
2268 #[allow(dead_code)]
2269 pub fn set_idom(&mut self, node: usize, dom: usize) {
2270 if node < self.idom.len() {
2271 self.idom[node] = Some(dom);
2272 if dom < self.children.len() {
2273 self.children[dom].push(node);
2274 }
2275 self.depth[node] = if dom < self.depth.len() {
2276 self.depth[dom] + 1
2277 } else {
2278 1
2279 };
2280 }
2281 }
2282 #[allow(dead_code)]
2283 pub fn dominates(&self, a: usize, mut b: usize) -> bool {
2284 if a == b {
2285 return true;
2286 }
2287 let n = self.idom.len();
2288 for _ in 0..n {
2289 match self.idom.get(b).copied().flatten() {
2290 None => return false,
2291 Some(p) if p == a => return true,
2292 Some(p) if p == b => return false,
2293 Some(p) => b = p,
2294 }
2295 }
2296 false
2297 }
2298 #[allow(dead_code)]
2299 pub fn children_of(&self, n: usize) -> &[usize] {
2300 self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
2301 }
2302 #[allow(dead_code)]
2303 pub fn depth_of(&self, n: usize) -> usize {
2304 self.depth.get(n).copied().unwrap_or(0)
2305 }
2306 #[allow(dead_code)]
2307 pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
2308 let n = self.idom.len();
2309 for _ in 0..(2 * n) {
2310 if a == b {
2311 return a;
2312 }
2313 if self.depth_of(a) > self.depth_of(b) {
2314 a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
2315 } else {
2316 b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
2317 }
2318 }
2319 0
2320 }
2321}