1use crate::lcnf::*;
6use std::collections::{HashMap, HashSet};
7
8use super::functions::*;
9use std::collections::VecDeque;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
13pub struct BlockId(pub u32);
14#[allow(dead_code)]
15#[derive(Debug, Clone)]
16pub struct NatLivenessInfo {
17 pub live_in: Vec<std::collections::HashSet<u32>>,
18 pub live_out: Vec<std::collections::HashSet<u32>>,
19 pub defs: Vec<std::collections::HashSet<u32>>,
20 pub uses: Vec<std::collections::HashSet<u32>>,
21}
22impl NatLivenessInfo {
23 #[allow(dead_code)]
24 pub fn new(block_count: usize) -> Self {
25 NatLivenessInfo {
26 live_in: vec![std::collections::HashSet::new(); block_count],
27 live_out: vec![std::collections::HashSet::new(); block_count],
28 defs: vec![std::collections::HashSet::new(); block_count],
29 uses: vec![std::collections::HashSet::new(); block_count],
30 }
31 }
32 #[allow(dead_code)]
33 pub fn add_def(&mut self, block: usize, var: u32) {
34 if block < self.defs.len() {
35 self.defs[block].insert(var);
36 }
37 }
38 #[allow(dead_code)]
39 pub fn add_use(&mut self, block: usize, var: u32) {
40 if block < self.uses.len() {
41 self.uses[block].insert(var);
42 }
43 }
44 #[allow(dead_code)]
45 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
46 self.live_in
47 .get(block)
48 .map(|s| s.contains(&var))
49 .unwrap_or(false)
50 }
51 #[allow(dead_code)]
52 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
53 self.live_out
54 .get(block)
55 .map(|s| s.contains(&var))
56 .unwrap_or(false)
57 }
58}
59#[allow(dead_code)]
60pub struct NatConstantFoldingHelper;
61impl NatConstantFoldingHelper {
62 #[allow(dead_code)]
63 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
64 a.checked_add(b)
65 }
66 #[allow(dead_code)]
67 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
68 a.checked_sub(b)
69 }
70 #[allow(dead_code)]
71 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
72 a.checked_mul(b)
73 }
74 #[allow(dead_code)]
75 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
76 if b == 0 {
77 None
78 } else {
79 a.checked_div(b)
80 }
81 }
82 #[allow(dead_code)]
83 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
84 a + b
85 }
86 #[allow(dead_code)]
87 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
88 a * b
89 }
90 #[allow(dead_code)]
91 pub fn fold_neg_i64(a: i64) -> Option<i64> {
92 a.checked_neg()
93 }
94 #[allow(dead_code)]
95 pub fn fold_not_bool(a: bool) -> bool {
96 !a
97 }
98 #[allow(dead_code)]
99 pub fn fold_and_bool(a: bool, b: bool) -> bool {
100 a && b
101 }
102 #[allow(dead_code)]
103 pub fn fold_or_bool(a: bool, b: bool) -> bool {
104 a || b
105 }
106 #[allow(dead_code)]
107 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
108 a.checked_shl(b)
109 }
110 #[allow(dead_code)]
111 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
112 a.checked_shr(b)
113 }
114 #[allow(dead_code)]
115 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
116 if b == 0 {
117 None
118 } else {
119 Some(a % b)
120 }
121 }
122 #[allow(dead_code)]
123 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
124 a & b
125 }
126 #[allow(dead_code)]
127 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
128 a | b
129 }
130 #[allow(dead_code)]
131 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
132 a ^ b
133 }
134 #[allow(dead_code)]
135 pub fn fold_bitnot_i64(a: i64) -> i64 {
136 !a
137 }
138}
139#[derive(Debug, Clone)]
141pub struct NativeModule {
142 pub functions: Vec<NativeFunc>,
144 pub globals: Vec<(String, NativeType, Option<i64>)>,
146 pub extern_fns: Vec<(String, Vec<NativeType>, NativeType)>,
148 pub name: String,
150}
151impl NativeModule {
152 pub(super) fn new(name: &str) -> Self {
154 NativeModule {
155 functions: Vec::new(),
156 globals: Vec::new(),
157 extern_fns: Vec::new(),
158 name: name.to_string(),
159 }
160 }
161 pub fn total_instructions(&self) -> usize {
163 self.functions.iter().map(|f| f.instruction_count()).sum()
164 }
165 pub fn get_function(&self, name: &str) -> Option<&NativeFunc> {
167 self.functions.iter().find(|f| f.name == name)
168 }
169}
170#[allow(dead_code)]
171#[derive(Debug, Clone)]
172pub struct NatDepGraph {
173 pub(super) nodes: Vec<u32>,
174 pub(super) edges: Vec<(u32, u32)>,
175}
176impl NatDepGraph {
177 #[allow(dead_code)]
178 pub fn new() -> Self {
179 NatDepGraph {
180 nodes: Vec::new(),
181 edges: Vec::new(),
182 }
183 }
184 #[allow(dead_code)]
185 pub fn add_node(&mut self, id: u32) {
186 if !self.nodes.contains(&id) {
187 self.nodes.push(id);
188 }
189 }
190 #[allow(dead_code)]
191 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
192 self.add_node(dep);
193 self.add_node(dependent);
194 self.edges.push((dep, dependent));
195 }
196 #[allow(dead_code)]
197 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
198 self.edges
199 .iter()
200 .filter(|(d, _)| *d == node)
201 .map(|(_, dep)| *dep)
202 .collect()
203 }
204 #[allow(dead_code)]
205 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
206 self.edges
207 .iter()
208 .filter(|(_, dep)| *dep == node)
209 .map(|(d, _)| *d)
210 .collect()
211 }
212 #[allow(dead_code)]
213 pub fn topological_sort(&self) -> Vec<u32> {
214 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
215 for &n in &self.nodes {
216 in_degree.insert(n, 0);
217 }
218 for (_, dep) in &self.edges {
219 *in_degree.entry(*dep).or_insert(0) += 1;
220 }
221 let mut queue: std::collections::VecDeque<u32> = self
222 .nodes
223 .iter()
224 .filter(|&&n| in_degree[&n] == 0)
225 .copied()
226 .collect();
227 let mut result = Vec::new();
228 while let Some(node) = queue.pop_front() {
229 result.push(node);
230 for dep in self.dependents_of(node) {
231 let cnt = in_degree.entry(dep).or_insert(0);
232 *cnt = cnt.saturating_sub(1);
233 if *cnt == 0 {
234 queue.push_back(dep);
235 }
236 }
237 }
238 result
239 }
240 #[allow(dead_code)]
241 pub fn has_cycle(&self) -> bool {
242 self.topological_sort().len() < self.nodes.len()
243 }
244}
245#[allow(dead_code)]
246#[derive(Debug, Clone)]
247pub struct NatDominatorTree {
248 pub idom: Vec<Option<u32>>,
249 pub dom_children: Vec<Vec<u32>>,
250 pub dom_depth: Vec<u32>,
251}
252impl NatDominatorTree {
253 #[allow(dead_code)]
254 pub fn new(size: usize) -> Self {
255 NatDominatorTree {
256 idom: vec![None; size],
257 dom_children: vec![Vec::new(); size],
258 dom_depth: vec![0; size],
259 }
260 }
261 #[allow(dead_code)]
262 pub fn set_idom(&mut self, node: usize, idom: u32) {
263 self.idom[node] = Some(idom);
264 }
265 #[allow(dead_code)]
266 pub fn dominates(&self, a: usize, b: usize) -> bool {
267 if a == b {
268 return true;
269 }
270 let mut cur = b;
271 loop {
272 match self.idom[cur] {
273 Some(parent) if parent as usize == a => return true,
274 Some(parent) if parent as usize == cur => return false,
275 Some(parent) => cur = parent as usize,
276 None => return false,
277 }
278 }
279 }
280 #[allow(dead_code)]
281 pub fn depth(&self, node: usize) -> u32 {
282 self.dom_depth.get(node).copied().unwrap_or(0)
283 }
284}
285#[derive(Debug, Clone)]
287pub struct NativeEmitConfig {
288 pub opt_level: u8,
290 pub debug_info: bool,
292 pub target_arch: String,
294 pub num_gp_regs: usize,
296 pub emit_comments: bool,
298}
299#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
304pub struct Register(pub u32);
305
306pub(super) const VIRT_PHYS_BOUNDARY: u32 = 65536;
308
309impl Register {
310 pub fn is_virtual(&self) -> bool {
312 self.0 < VIRT_PHYS_BOUNDARY
313 }
314 pub fn is_physical(&self) -> bool {
316 self.0 >= VIRT_PHYS_BOUNDARY
317 }
318 pub fn virt(n: u32) -> Self {
320 assert!(
321 n < VIRT_PHYS_BOUNDARY,
322 "Virtual register index must be < {}",
323 VIRT_PHYS_BOUNDARY
324 );
325 Register(n)
326 }
327 pub fn phys(n: u32) -> Self {
329 Register(n + VIRT_PHYS_BOUNDARY)
330 }
331}
332#[allow(dead_code)]
333#[derive(Debug, Clone, Default)]
334pub struct NatPassStats {
335 pub total_runs: u32,
336 pub successful_runs: u32,
337 pub total_changes: u64,
338 pub time_ms: u64,
339 pub iterations_used: u32,
340}
341impl NatPassStats {
342 #[allow(dead_code)]
343 pub fn new() -> Self {
344 Self::default()
345 }
346 #[allow(dead_code)]
347 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
348 self.total_runs += 1;
349 self.successful_runs += 1;
350 self.total_changes += changes;
351 self.time_ms += time_ms;
352 self.iterations_used = iterations;
353 }
354 #[allow(dead_code)]
355 pub fn average_changes_per_run(&self) -> f64 {
356 if self.total_runs == 0 {
357 return 0.0;
358 }
359 self.total_changes as f64 / self.total_runs as f64
360 }
361 #[allow(dead_code)]
362 pub fn success_rate(&self) -> f64 {
363 if self.total_runs == 0 {
364 return 0.0;
365 }
366 self.successful_runs as f64 / self.total_runs as f64
367 }
368 #[allow(dead_code)]
369 pub fn format_summary(&self) -> String {
370 format!(
371 "Runs: {}/{}, Changes: {}, Time: {}ms",
372 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
373 )
374 }
375}
376pub struct NativeBackend {
380 pub(super) config: NativeEmitConfig,
381 pub(super) stats: NativeEmitStats,
382 pub(super) next_vreg: u32,
384 pub(super) next_block: u32,
386 pub(super) var_map: HashMap<LcnfVarId, Register>,
388 pub(super) next_stack_slot: u32,
390}
391impl NativeBackend {
392 pub fn new(config: NativeEmitConfig) -> Self {
394 NativeBackend {
395 config,
396 stats: NativeEmitStats::default(),
397 next_vreg: 0,
398 next_block: 0,
399 var_map: HashMap::new(),
400 next_stack_slot: 0,
401 }
402 }
403 pub fn default_backend() -> Self {
405 Self::new(NativeEmitConfig::default())
406 }
407 pub(super) fn alloc_vreg(&mut self) -> Register {
409 let r = Register::virt(self.next_vreg);
410 self.next_vreg += 1;
411 self.stats.virtual_regs_used += 1;
412 r
413 }
414 pub(super) fn alloc_block(&mut self) -> BlockId {
416 let id = BlockId(self.next_block);
417 self.next_block += 1;
418 self.stats.blocks_generated += 1;
419 id
420 }
421 pub(super) fn alloc_stack_slot(&mut self) -> u32 {
423 let slot = self.next_stack_slot;
424 self.next_stack_slot += 1;
425 self.stats.stack_slots_allocated += 1;
426 slot
427 }
428 pub(super) fn reset_function_state(&mut self) {
430 self.next_vreg = 0;
431 self.next_block = 0;
432 self.var_map.clear();
433 self.next_stack_slot = 0;
434 }
435 pub(super) fn get_var_reg(&mut self, id: LcnfVarId) -> Register {
437 if let Some(r) = self.var_map.get(&id) {
438 *r
439 } else {
440 let r = self.alloc_vreg();
441 self.var_map.insert(id, r);
442 r
443 }
444 }
445 pub fn compile_module(&mut self, module: &LcnfModule) -> NativeModule {
447 let mut native_module = NativeModule::new(&module.name);
448 for ext in &module.extern_decls {
449 let params: Vec<NativeType> = ext
450 .params
451 .iter()
452 .filter(|p| !p.erased)
453 .map(|p| lcnf_type_to_native(&p.ty))
454 .collect();
455 let ret = lcnf_type_to_native(&ext.ret_type);
456 native_module
457 .extern_fns
458 .push((ext.name.clone(), params, ret));
459 }
460 for fun in &module.fun_decls {
461 let native_func = self.compile_fun_decl(fun);
462 native_module.functions.push(native_func);
463 self.stats.functions_compiled += 1;
464 }
465 native_module
466 }
467 pub fn compile_fun_decl(&mut self, decl: &LcnfFunDecl) -> NativeFunc {
469 self.reset_function_state();
470 let mut params = Vec::new();
471 for p in &decl.params {
472 if p.erased {
473 continue;
474 }
475 let r = self.alloc_vreg();
476 self.var_map.insert(p.id, r);
477 params.push((r, lcnf_type_to_native(&p.ty)));
478 }
479 let ret_type = lcnf_type_to_native(&decl.ret_type);
480 let mut func = NativeFunc::new(&decl.name, params, ret_type);
481 func.is_recursive = decl.is_recursive;
482 let entry_id = self.alloc_block();
483 let mut entry_block = BasicBlock::new(entry_id);
484 if self.config.emit_comments {
485 entry_block.push_inst(NativeInst::Comment(format!("Function: {}", decl.name)));
486 }
487 let mut extra_blocks = Vec::new();
488 self.compile_expr(&decl.body, &mut entry_block, &mut extra_blocks, &ret_type);
489 func.blocks.push(entry_block);
490 func.blocks.extend(extra_blocks);
491 func.stack_size = self.next_stack_slot as usize * 8;
492 func
493 }
494 pub(super) fn compile_expr(
497 &mut self,
498 expr: &LcnfExpr,
499 current_block: &mut BasicBlock,
500 extra_blocks: &mut Vec<BasicBlock>,
501 ret_type: &NativeType,
502 ) {
503 match expr {
504 LcnfExpr::Let {
505 id,
506 ty,
507 value,
508 body,
509 ..
510 } => {
511 let native_ty = lcnf_type_to_native(ty);
512 let dst = self.alloc_vreg();
513 self.var_map.insert(*id, dst);
514 self.compile_let_value(value, dst, &native_ty, current_block);
515 self.compile_expr(body, current_block, extra_blocks, ret_type);
516 }
517 LcnfExpr::Case {
518 scrutinee,
519 alts,
520 default,
521 ..
522 } => {
523 let scrut_reg = self.get_var_reg(*scrutinee);
524 if alts.is_empty() {
525 if let Some(def) = default {
526 self.compile_expr(def, current_block, extra_blocks, ret_type);
527 } else {
528 current_block.push_inst(NativeInst::Ret { value: None });
529 }
530 return;
531 }
532 let merge_id = self.alloc_block();
533 let merge_result_reg = self.alloc_vreg();
534 let mut merge_block = BasicBlock::new(merge_id);
535 if *ret_type != NativeType::Void {
536 merge_block.params.push((merge_result_reg, *ret_type));
537 }
538 if alts.len() == 1 && default.is_some() {
539 let alt = &alts[0];
540 let tag_reg = self.alloc_vreg();
541 current_block.push_inst(NativeInst::Call {
542 dst: Some(tag_reg),
543 func: NativeValue::FRef("lean_obj_tag".to_string()),
544 args: vec![NativeValue::Reg(scrut_reg)],
545 ret_type: NativeType::I32,
546 });
547 let cmp_reg = self.alloc_vreg();
548 current_block.push_inst(NativeInst::Cmp {
549 dst: cmp_reg,
550 cc: CondCode::Eq,
551 ty: NativeType::I32,
552 lhs: NativeValue::Reg(tag_reg),
553 rhs: NativeValue::Imm(alt.ctor_tag as i64),
554 });
555 let then_id = self.alloc_block();
556 let else_id = self.alloc_block();
557 current_block.push_inst(NativeInst::CondBr {
558 cond: NativeValue::Reg(cmp_reg),
559 then_target: then_id,
560 else_target: else_id,
561 });
562 let mut then_block = BasicBlock::new(then_id);
563 self.bind_ctor_params(&alt.params, scrut_reg, &mut then_block);
564 self.compile_expr(&alt.body, &mut then_block, extra_blocks, ret_type);
565 if then_block.terminator.is_none() {
566 then_block.push_inst(NativeInst::Br { target: merge_id });
567 }
568 extra_blocks.push(then_block);
569 let mut else_block = BasicBlock::new(else_id);
570 self.compile_expr(
571 default
572 .as_ref()
573 .expect(
574 "default is Some; guaranteed by default.is_some() check at if condition",
575 ),
576 &mut else_block,
577 extra_blocks,
578 ret_type,
579 );
580 if else_block.terminator.is_none() {
581 else_block.push_inst(NativeInst::Br { target: merge_id });
582 }
583 extra_blocks.push(else_block);
584 } else {
585 let tag_reg = self.alloc_vreg();
586 current_block.push_inst(NativeInst::Call {
587 dst: Some(tag_reg),
588 func: NativeValue::FRef("lean_obj_tag".to_string()),
589 args: vec![NativeValue::Reg(scrut_reg)],
590 ret_type: NativeType::I32,
591 });
592 let default_id = self.alloc_block();
593 let mut targets = Vec::new();
594 for alt in alts {
595 let alt_block_id = self.alloc_block();
596 targets.push((alt.ctor_tag as u64, alt_block_id));
597 let mut alt_block = BasicBlock::new(alt_block_id);
598 self.bind_ctor_params(&alt.params, scrut_reg, &mut alt_block);
599 self.compile_expr(&alt.body, &mut alt_block, extra_blocks, ret_type);
600 if alt_block.terminator.is_none() {
601 alt_block.push_inst(NativeInst::Br { target: merge_id });
602 }
603 extra_blocks.push(alt_block);
604 }
605 current_block.push_inst(NativeInst::Switch {
606 value: NativeValue::Reg(tag_reg),
607 default: default_id,
608 targets,
609 });
610 let mut def_block = BasicBlock::new(default_id);
611 if let Some(def_expr) = default {
612 self.compile_expr(def_expr, &mut def_block, extra_blocks, ret_type);
613 } else {
614 def_block.push_inst(NativeInst::Ret { value: None });
615 }
616 if def_block.terminator.is_none() {
617 def_block.push_inst(NativeInst::Br { target: merge_id });
618 }
619 extra_blocks.push(def_block);
620 }
621 extra_blocks.push(merge_block);
622 }
623 LcnfExpr::Return(arg) => {
624 let val = self.compile_arg(arg, current_block);
625 current_block.push_inst(NativeInst::Ret { value: Some(val) });
626 }
627 LcnfExpr::Unreachable => {
628 current_block.push_inst(NativeInst::Call {
629 dst: None,
630 func: NativeValue::FRef("lean_internal_panic_unreachable".to_string()),
631 args: vec![],
632 ret_type: NativeType::Void,
633 });
634 current_block.push_inst(NativeInst::Ret { value: None });
635 }
636 LcnfExpr::TailCall(func, args) => {
637 let func_val = self.compile_arg(func, current_block);
638 let arg_vals: Vec<NativeValue> = args
639 .iter()
640 .map(|a| self.compile_arg(a, current_block))
641 .collect();
642 let result_reg = self.alloc_vreg();
643 current_block.push_inst(NativeInst::Call {
644 dst: Some(result_reg),
645 func: func_val,
646 args: arg_vals,
647 ret_type: *ret_type,
648 });
649 current_block.push_inst(NativeInst::Ret {
650 value: Some(NativeValue::Reg(result_reg)),
651 });
652 }
653 }
654 }
655 pub(super) fn compile_let_value(
657 &mut self,
658 value: &LcnfLetValue,
659 dst: Register,
660 _ty: &NativeType,
661 block: &mut BasicBlock,
662 ) {
663 match value {
664 LcnfLetValue::App(func, args) => {
665 let func_val = self.compile_arg_to_value(func);
666 let arg_vals: Vec<NativeValue> =
667 args.iter().map(|a| self.compile_arg_to_value(a)).collect();
668 block.push_inst(NativeInst::Call {
669 dst: Some(dst),
670 func: func_val,
671 args: arg_vals,
672 ret_type: NativeType::Ptr,
673 });
674 self.stats.instructions_generated += 1;
675 }
676 LcnfLetValue::Proj(_name, idx, var) => {
677 let obj_reg = self.get_var_reg(*var);
678 block.push_inst(NativeInst::Call {
679 dst: Some(dst),
680 func: NativeValue::FRef("lean_ctor_get".to_string()),
681 args: vec![NativeValue::Reg(obj_reg), NativeValue::Imm(*idx as i64)],
682 ret_type: NativeType::Ptr,
683 });
684 self.stats.instructions_generated += 1;
685 }
686 LcnfLetValue::Ctor(_name, tag, args) => {
687 let num_objs = args.len();
688 block.push_inst(NativeInst::Call {
689 dst: Some(dst),
690 func: NativeValue::FRef("lean_alloc_ctor".to_string()),
691 args: vec![
692 NativeValue::Imm(*tag as i64),
693 NativeValue::Imm(num_objs as i64),
694 NativeValue::Imm(0),
695 ],
696 ret_type: NativeType::Ptr,
697 });
698 self.stats.instructions_generated += 1;
699 for (i, arg) in args.iter().enumerate() {
700 let val = self.compile_arg_to_value(arg);
701 block.push_inst(NativeInst::Call {
702 dst: None,
703 func: NativeValue::FRef("lean_ctor_set".to_string()),
704 args: vec![NativeValue::Reg(dst), NativeValue::Imm(i as i64), val],
705 ret_type: NativeType::Void,
706 });
707 self.stats.instructions_generated += 1;
708 }
709 }
710 LcnfLetValue::Lit(lit) => match lit {
711 LcnfLit::Nat(n) => {
712 block.push_inst(NativeInst::LoadImm {
713 dst,
714 ty: NativeType::I64,
715 value: *n as i64,
716 });
717 self.stats.instructions_generated += 1;
718 }
719 LcnfLit::Int(i) => {
720 block.push_inst(NativeInst::LoadImm {
721 dst,
722 ty: NativeType::I64,
723 value: *i,
724 });
725 self.stats.instructions_generated += 1;
726 }
727 LcnfLit::Str(s) => {
728 block.push_inst(NativeInst::Call {
729 dst: Some(dst),
730 func: NativeValue::FRef("lean_mk_string".to_string()),
731 args: vec![NativeValue::StrRef(s.clone())],
732 ret_type: NativeType::Ptr,
733 });
734 if self.config.emit_comments {
735 block.push_inst(NativeInst::Comment(format!("string: \"{}\"", s)));
736 }
737 self.stats.instructions_generated += 1;
738 }
739 },
740 LcnfLetValue::Erased => {
741 block.push_inst(NativeInst::LoadImm {
742 dst,
743 ty: NativeType::Ptr,
744 value: 0,
745 });
746 self.stats.instructions_generated += 1;
747 }
748 LcnfLetValue::FVar(var) => {
749 let src = self.get_var_reg(*var);
750 block.push_inst(NativeInst::Copy {
751 dst,
752 src: NativeValue::Reg(src),
753 });
754 self.stats.instructions_generated += 1;
755 }
756 LcnfLetValue::Reset(var) => {
757 let obj_reg = self.get_var_reg(*var);
758 block.push_inst(NativeInst::Call {
759 dst: Some(dst),
760 func: NativeValue::FRef("lean_obj_reset".to_string()),
761 args: vec![NativeValue::Reg(obj_reg)],
762 ret_type: NativeType::Ptr,
763 });
764 self.stats.instructions_generated += 1;
765 }
766 LcnfLetValue::Reuse(slot, _name, tag, args) => {
767 let num_objs = args.len();
768 let slot_reg = self.get_var_reg(*slot);
769 block.push_inst(NativeInst::Call {
770 dst: Some(dst),
771 func: NativeValue::FRef("lean_alloc_ctor_using".to_string()),
772 args: vec![
773 NativeValue::Reg(slot_reg),
774 NativeValue::Imm(*tag as i64),
775 NativeValue::Imm(num_objs as i64),
776 NativeValue::Imm(0),
777 ],
778 ret_type: NativeType::Ptr,
779 });
780 self.stats.instructions_generated += 1;
781 for (i, arg) in args.iter().enumerate() {
782 let val = self.compile_arg_to_value(arg);
783 block.push_inst(NativeInst::Call {
784 dst: None,
785 func: NativeValue::FRef("lean_ctor_set".to_string()),
786 args: vec![NativeValue::Reg(dst), NativeValue::Imm(i as i64), val],
787 ret_type: NativeType::Void,
788 });
789 self.stats.instructions_generated += 1;
790 }
791 }
792 }
793 }
794 pub(super) fn bind_ctor_params(
796 &mut self,
797 params: &[LcnfParam],
798 scrut_reg: Register,
799 block: &mut BasicBlock,
800 ) {
801 for (i, param) in params.iter().enumerate() {
802 if param.erased {
803 continue;
804 }
805 let dst = self.alloc_vreg();
806 self.var_map.insert(param.id, dst);
807 block.push_inst(NativeInst::Call {
808 dst: Some(dst),
809 func: NativeValue::FRef("lean_ctor_get".to_string()),
810 args: vec![NativeValue::Reg(scrut_reg), NativeValue::Imm(i as i64)],
811 ret_type: NativeType::Ptr,
812 });
813 self.stats.instructions_generated += 1;
814 }
815 }
816 pub(super) fn compile_arg(&mut self, arg: &LcnfArg, block: &mut BasicBlock) -> NativeValue {
818 match arg {
819 LcnfArg::Var(id) => {
820 let r = self.get_var_reg(*id);
821 NativeValue::Reg(r)
822 }
823 LcnfArg::Lit(lit) => match lit {
824 LcnfLit::Nat(n) => {
825 let r = self.alloc_vreg();
826 block.push_inst(NativeInst::LoadImm {
827 dst: r,
828 ty: NativeType::I64,
829 value: *n as i64,
830 });
831 self.stats.instructions_generated += 1;
832 NativeValue::Reg(r)
833 }
834 LcnfLit::Int(i) => {
835 let r = self.alloc_vreg();
836 block.push_inst(NativeInst::LoadImm {
837 dst: r,
838 ty: NativeType::I64,
839 value: *i,
840 });
841 self.stats.instructions_generated += 1;
842 NativeValue::Reg(r)
843 }
844 LcnfLit::Str(s) => {
845 let r = self.alloc_vreg();
846 block.push_inst(NativeInst::Call {
847 dst: Some(r),
848 func: NativeValue::FRef("lean_mk_string".to_string()),
849 args: vec![NativeValue::StrRef(s.clone())],
850 ret_type: NativeType::Ptr,
851 });
852 self.stats.instructions_generated += 1;
853 NativeValue::Reg(r)
854 }
855 },
856 LcnfArg::Erased | LcnfArg::Type(_) => NativeValue::Imm(0),
857 }
858 }
859 pub(super) fn compile_arg_to_value(&mut self, arg: &LcnfArg) -> NativeValue {
861 match arg {
862 LcnfArg::Var(id) => NativeValue::Reg(self.get_var_reg(*id)),
863 LcnfArg::Lit(LcnfLit::Nat(n)) => NativeValue::Imm(*n as i64),
864 LcnfArg::Lit(LcnfLit::Int(i)) => NativeValue::Imm(*i),
865 LcnfArg::Lit(LcnfLit::Str(s)) => NativeValue::StrRef(s.clone()),
866 LcnfArg::Erased | LcnfArg::Type(_) => NativeValue::Imm(0),
867 }
868 }
869 pub fn stats(&self) -> &NativeEmitStats {
871 &self.stats
872 }
873}
874#[derive(Debug, Clone)]
876struct LiveInterval {
877 pub(super) vreg: Register,
878 pub(super) start: usize,
879 pub(super) end: usize,
880 pub(super) assigned_phys: Option<Register>,
881 pub(super) spill_slot: Option<u32>,
882}
883#[allow(dead_code)]
884#[derive(Debug, Clone, PartialEq)]
885pub enum NatPassPhase {
886 Analysis,
887 Transformation,
888 Verification,
889 Cleanup,
890}
891impl NatPassPhase {
892 #[allow(dead_code)]
893 pub fn name(&self) -> &str {
894 match self {
895 NatPassPhase::Analysis => "analysis",
896 NatPassPhase::Transformation => "transformation",
897 NatPassPhase::Verification => "verification",
898 NatPassPhase::Cleanup => "cleanup",
899 }
900 }
901 #[allow(dead_code)]
902 pub fn is_modifying(&self) -> bool {
903 matches!(self, NatPassPhase::Transformation | NatPassPhase::Cleanup)
904 }
905}
906#[allow(dead_code)]
907pub struct NatPassRegistry {
908 pub(super) configs: Vec<NatPassConfig>,
909 pub(super) stats: std::collections::HashMap<String, NatPassStats>,
910}
911impl NatPassRegistry {
912 #[allow(dead_code)]
913 pub fn new() -> Self {
914 NatPassRegistry {
915 configs: Vec::new(),
916 stats: std::collections::HashMap::new(),
917 }
918 }
919 #[allow(dead_code)]
920 pub fn register(&mut self, config: NatPassConfig) {
921 self.stats
922 .insert(config.pass_name.clone(), NatPassStats::new());
923 self.configs.push(config);
924 }
925 #[allow(dead_code)]
926 pub fn enabled_passes(&self) -> Vec<&NatPassConfig> {
927 self.configs.iter().filter(|c| c.enabled).collect()
928 }
929 #[allow(dead_code)]
930 pub fn get_stats(&self, name: &str) -> Option<&NatPassStats> {
931 self.stats.get(name)
932 }
933 #[allow(dead_code)]
934 pub fn total_passes(&self) -> usize {
935 self.configs.len()
936 }
937 #[allow(dead_code)]
938 pub fn enabled_count(&self) -> usize {
939 self.enabled_passes().len()
940 }
941 #[allow(dead_code)]
942 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
943 if let Some(stats) = self.stats.get_mut(name) {
944 stats.record_run(changes, time_ms, iter);
945 }
946 }
947}
948#[derive(Debug, Clone, PartialEq, Eq, Hash)]
950pub enum NativeValue {
951 Reg(Register),
953 Imm(i64),
955 FRef(String),
957 StackSlot(u32),
959 UImm(u64),
961 StrRef(String),
963}
964impl NativeValue {
965 pub(super) fn reg(r: Register) -> Self {
967 NativeValue::Reg(r)
968 }
969 pub(super) fn imm(n: i64) -> Self {
971 NativeValue::Imm(n)
972 }
973 pub(super) fn uimm(n: u64) -> Self {
975 NativeValue::UImm(n)
976 }
977}
978#[derive(Debug, Clone)]
980pub struct BasicBlock {
981 pub label: BlockId,
983 pub params: Vec<(Register, NativeType)>,
985 pub instructions: Vec<NativeInst>,
987 pub terminator: Option<NativeInst>,
989}
990impl BasicBlock {
991 pub(super) fn new(label: BlockId) -> Self {
993 BasicBlock {
994 label,
995 params: Vec::new(),
996 instructions: Vec::new(),
997 terminator: None,
998 }
999 }
1000 pub(super) fn push_inst(&mut self, inst: NativeInst) {
1002 if inst.is_terminator() {
1003 self.terminator = Some(inst);
1004 } else {
1005 self.instructions.push(inst);
1006 }
1007 }
1008 pub fn successors(&self) -> Vec<BlockId> {
1010 match &self.terminator {
1011 Some(NativeInst::Br { target }) => vec![*target],
1012 Some(NativeInst::CondBr {
1013 then_target,
1014 else_target,
1015 ..
1016 }) => {
1017 vec![*then_target, *else_target]
1018 }
1019 Some(NativeInst::Switch {
1020 default, targets, ..
1021 }) => {
1022 let mut succs = vec![*default];
1023 for (_, t) in targets {
1024 succs.push(*t);
1025 }
1026 succs
1027 }
1028 _ => vec![],
1029 }
1030 }
1031 pub fn instruction_count(&self) -> usize {
1033 self.instructions.len() + if self.terminator.is_some() { 1 } else { 0 }
1034 }
1035}
1036#[allow(dead_code)]
1037#[derive(Debug, Clone)]
1038pub struct NatCacheEntry {
1039 pub key: String,
1040 pub data: Vec<u8>,
1041 pub timestamp: u64,
1042 pub valid: bool,
1043}
1044#[derive(Debug, Clone)]
1046pub struct NativeFunc {
1047 pub name: String,
1049 pub params: Vec<(Register, NativeType)>,
1051 pub ret_type: NativeType,
1053 pub blocks: Vec<BasicBlock>,
1055 pub stack_size: usize,
1057 pub is_recursive: bool,
1059}
1060impl NativeFunc {
1061 pub(super) fn new(
1063 name: &str,
1064 params: Vec<(Register, NativeType)>,
1065 ret_type: NativeType,
1066 ) -> Self {
1067 NativeFunc {
1068 name: name.to_string(),
1069 params,
1070 ret_type,
1071 blocks: Vec::new(),
1072 stack_size: 0,
1073 is_recursive: false,
1074 }
1075 }
1076 pub fn entry_block(&self) -> Option<&BasicBlock> {
1078 self.blocks.first()
1079 }
1080 pub fn instruction_count(&self) -> usize {
1082 self.blocks.iter().map(|b| b.instruction_count()).sum()
1083 }
1084 pub fn get_block(&self, id: BlockId) -> Option<&BasicBlock> {
1086 self.blocks.iter().find(|b| b.label == id)
1087 }
1088 pub fn virtual_registers(&self) -> HashSet<Register> {
1090 let mut regs = HashSet::new();
1091 for (r, _) in &self.params {
1092 if r.is_virtual() {
1093 regs.insert(*r);
1094 }
1095 }
1096 for block in &self.blocks {
1097 for (r, _) in &block.params {
1098 if r.is_virtual() {
1099 regs.insert(*r);
1100 }
1101 }
1102 for inst in &block.instructions {
1103 if let Some(dst) = inst.dst_reg() {
1104 if dst.is_virtual() {
1105 regs.insert(dst);
1106 }
1107 }
1108 for src in inst.src_regs() {
1109 if src.is_virtual() {
1110 regs.insert(src);
1111 }
1112 }
1113 }
1114 if let Some(term) = &block.terminator {
1115 if let Some(dst) = term.dst_reg() {
1116 if dst.is_virtual() {
1117 regs.insert(dst);
1118 }
1119 }
1120 for src in term.src_regs() {
1121 if src.is_virtual() {
1122 regs.insert(src);
1123 }
1124 }
1125 }
1126 }
1127 regs
1128 }
1129}
1130#[derive(Debug, Clone, PartialEq)]
1132pub enum NativeInst {
1133 LoadImm {
1135 dst: Register,
1136 ty: NativeType,
1137 value: i64,
1138 },
1139 Add {
1141 dst: Register,
1142 ty: NativeType,
1143 lhs: NativeValue,
1144 rhs: NativeValue,
1145 },
1146 Sub {
1148 dst: Register,
1149 ty: NativeType,
1150 lhs: NativeValue,
1151 rhs: NativeValue,
1152 },
1153 Mul {
1155 dst: Register,
1156 ty: NativeType,
1157 lhs: NativeValue,
1158 rhs: NativeValue,
1159 },
1160 Div {
1162 dst: Register,
1163 ty: NativeType,
1164 lhs: NativeValue,
1165 rhs: NativeValue,
1166 },
1167 And {
1169 dst: Register,
1170 ty: NativeType,
1171 lhs: NativeValue,
1172 rhs: NativeValue,
1173 },
1174 Or {
1176 dst: Register,
1177 ty: NativeType,
1178 lhs: NativeValue,
1179 rhs: NativeValue,
1180 },
1181 Xor {
1183 dst: Register,
1184 ty: NativeType,
1185 lhs: NativeValue,
1186 rhs: NativeValue,
1187 },
1188 Shl {
1190 dst: Register,
1191 ty: NativeType,
1192 lhs: NativeValue,
1193 rhs: NativeValue,
1194 },
1195 Shr {
1197 dst: Register,
1198 ty: NativeType,
1199 lhs: NativeValue,
1200 rhs: NativeValue,
1201 },
1202 Cmp {
1204 dst: Register,
1205 cc: CondCode,
1206 ty: NativeType,
1207 lhs: NativeValue,
1208 rhs: NativeValue,
1209 },
1210 Br { target: BlockId },
1212 CondBr {
1214 cond: NativeValue,
1215 then_target: BlockId,
1216 else_target: BlockId,
1217 },
1218 Call {
1220 dst: Option<Register>,
1221 func: NativeValue,
1222 args: Vec<NativeValue>,
1223 ret_type: NativeType,
1224 },
1225 Ret { value: Option<NativeValue> },
1227 Load {
1229 dst: Register,
1230 ty: NativeType,
1231 addr: NativeValue,
1232 },
1233 Store {
1235 ty: NativeType,
1236 addr: NativeValue,
1237 value: NativeValue,
1238 },
1239 Alloc {
1241 dst: Register,
1242 size: usize,
1243 align: usize,
1244 },
1245 Free { ptr: NativeValue },
1247 Phi {
1249 dst: Register,
1250 ty: NativeType,
1251 incoming: Vec<(NativeValue, BlockId)>,
1252 },
1253 Select {
1255 dst: Register,
1256 ty: NativeType,
1257 cond: NativeValue,
1258 true_val: NativeValue,
1259 false_val: NativeValue,
1260 },
1261 Copy { dst: Register, src: NativeValue },
1263 Nop,
1265 Comment(String),
1267 IntToPtr { dst: Register, src: NativeValue },
1269 PtrToInt { dst: Register, src: NativeValue },
1271 GetElementPtr {
1273 dst: Register,
1274 base: NativeValue,
1275 offset: NativeValue,
1276 elem_size: usize,
1277 },
1278 Switch {
1280 value: NativeValue,
1281 default: BlockId,
1282 targets: Vec<(u64, BlockId)>,
1283 },
1284}
1285impl NativeInst {
1286 pub fn dst_reg(&self) -> Option<Register> {
1288 match self {
1289 NativeInst::LoadImm { dst, .. }
1290 | NativeInst::Add { dst, .. }
1291 | NativeInst::Sub { dst, .. }
1292 | NativeInst::Mul { dst, .. }
1293 | NativeInst::Div { dst, .. }
1294 | NativeInst::And { dst, .. }
1295 | NativeInst::Or { dst, .. }
1296 | NativeInst::Xor { dst, .. }
1297 | NativeInst::Shl { dst, .. }
1298 | NativeInst::Shr { dst, .. }
1299 | NativeInst::Cmp { dst, .. }
1300 | NativeInst::Load { dst, .. }
1301 | NativeInst::Alloc { dst, .. }
1302 | NativeInst::Phi { dst, .. }
1303 | NativeInst::Select { dst, .. }
1304 | NativeInst::Copy { dst, .. }
1305 | NativeInst::IntToPtr { dst, .. }
1306 | NativeInst::PtrToInt { dst, .. }
1307 | NativeInst::GetElementPtr { dst, .. } => Some(*dst),
1308 NativeInst::Call { dst, .. } => *dst,
1309 _ => None,
1310 }
1311 }
1312 pub fn src_regs(&self) -> Vec<Register> {
1314 let mut regs = Vec::new();
1315 let extract = |v: &NativeValue, regs: &mut Vec<Register>| {
1316 if let NativeValue::Reg(r) = v {
1317 regs.push(*r);
1318 }
1319 };
1320 match self {
1321 NativeInst::Add { lhs, rhs, .. }
1322 | NativeInst::Sub { lhs, rhs, .. }
1323 | NativeInst::Mul { lhs, rhs, .. }
1324 | NativeInst::Div { lhs, rhs, .. }
1325 | NativeInst::And { lhs, rhs, .. }
1326 | NativeInst::Or { lhs, rhs, .. }
1327 | NativeInst::Xor { lhs, rhs, .. }
1328 | NativeInst::Shl { lhs, rhs, .. }
1329 | NativeInst::Shr { lhs, rhs, .. }
1330 | NativeInst::Cmp { lhs, rhs, .. } => {
1331 extract(lhs, &mut regs);
1332 extract(rhs, &mut regs);
1333 }
1334 NativeInst::CondBr { cond, .. } => extract(cond, &mut regs),
1335 NativeInst::Call { func, args, .. } => {
1336 extract(func, &mut regs);
1337 for a in args {
1338 extract(a, &mut regs);
1339 }
1340 }
1341 NativeInst::Ret { value: Some(v) } => extract(v, &mut regs),
1342 NativeInst::Load { addr, .. } => extract(addr, &mut regs),
1343 NativeInst::Store { addr, value, .. } => {
1344 extract(addr, &mut regs);
1345 extract(value, &mut regs);
1346 }
1347 NativeInst::Free { ptr } => extract(ptr, &mut regs),
1348 NativeInst::Phi { incoming, .. } => {
1349 for (v, _) in incoming {
1350 extract(v, &mut regs);
1351 }
1352 }
1353 NativeInst::Select {
1354 cond,
1355 true_val,
1356 false_val,
1357 ..
1358 } => {
1359 extract(cond, &mut regs);
1360 extract(true_val, &mut regs);
1361 extract(false_val, &mut regs);
1362 }
1363 NativeInst::Copy { src, .. } => extract(src, &mut regs),
1364 NativeInst::IntToPtr { src, .. } | NativeInst::PtrToInt { src, .. } => {
1365 extract(src, &mut regs)
1366 }
1367 NativeInst::GetElementPtr { base, offset, .. } => {
1368 extract(base, &mut regs);
1369 extract(offset, &mut regs);
1370 }
1371 NativeInst::Switch { value, .. } => extract(value, &mut regs),
1372 _ => {}
1373 }
1374 regs
1375 }
1376 pub fn is_terminator(&self) -> bool {
1378 matches!(
1379 self,
1380 NativeInst::Br { .. }
1381 | NativeInst::CondBr { .. }
1382 | NativeInst::Ret { .. }
1383 | NativeInst::Switch { .. }
1384 )
1385 }
1386}
1387#[derive(Debug, Clone, Default)]
1389pub struct NativeEmitStats {
1390 pub functions_compiled: usize,
1392 pub blocks_generated: usize,
1394 pub instructions_generated: usize,
1396 pub virtual_regs_used: usize,
1398 pub stack_slots_allocated: usize,
1400 pub spills: usize,
1402}
1403#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1405pub enum NativeType {
1406 I8,
1408 I16,
1410 I32,
1412 I64,
1414 F32,
1416 F64,
1418 Ptr,
1420 FuncRef,
1422 Void,
1424}
1425impl NativeType {
1426 pub fn size_bytes(&self) -> usize {
1428 match self {
1429 NativeType::I8 => 1,
1430 NativeType::I16 => 2,
1431 NativeType::I32 | NativeType::F32 => 4,
1432 NativeType::I64 | NativeType::F64 | NativeType::Ptr | NativeType::FuncRef => 8,
1433 NativeType::Void => 0,
1434 }
1435 }
1436 pub fn alignment(&self) -> usize {
1438 self.size_bytes().max(1)
1439 }
1440 pub fn is_float(&self) -> bool {
1442 matches!(self, NativeType::F32 | NativeType::F64)
1443 }
1444 pub fn is_integer(&self) -> bool {
1446 matches!(
1447 self,
1448 NativeType::I8 | NativeType::I16 | NativeType::I32 | NativeType::I64
1449 )
1450 }
1451 pub fn is_pointer(&self) -> bool {
1453 matches!(self, NativeType::Ptr | NativeType::FuncRef)
1454 }
1455}
1456#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1458pub enum CondCode {
1459 Eq,
1460 Ne,
1461 Lt,
1462 Le,
1463 Gt,
1464 Ge,
1465 Ult,
1467 Ule,
1469 Ugt,
1471 Uge,
1473}
1474#[allow(dead_code)]
1475#[derive(Debug, Clone)]
1476pub struct NatPassConfig {
1477 pub phase: NatPassPhase,
1478 pub enabled: bool,
1479 pub max_iterations: u32,
1480 pub debug_output: bool,
1481 pub pass_name: String,
1482}
1483impl NatPassConfig {
1484 #[allow(dead_code)]
1485 pub fn new(name: impl Into<String>, phase: NatPassPhase) -> Self {
1486 NatPassConfig {
1487 phase,
1488 enabled: true,
1489 max_iterations: 10,
1490 debug_output: false,
1491 pass_name: name.into(),
1492 }
1493 }
1494 #[allow(dead_code)]
1495 pub fn disabled(mut self) -> Self {
1496 self.enabled = false;
1497 self
1498 }
1499 #[allow(dead_code)]
1500 pub fn with_debug(mut self) -> Self {
1501 self.debug_output = true;
1502 self
1503 }
1504 #[allow(dead_code)]
1505 pub fn max_iter(mut self, n: u32) -> Self {
1506 self.max_iterations = n;
1507 self
1508 }
1509}
1510#[allow(dead_code)]
1511#[derive(Debug, Clone)]
1512pub struct NatAnalysisCache {
1513 pub(super) entries: std::collections::HashMap<String, NatCacheEntry>,
1514 pub(super) max_size: usize,
1515 pub(super) hits: u64,
1516 pub(super) misses: u64,
1517}
1518impl NatAnalysisCache {
1519 #[allow(dead_code)]
1520 pub fn new(max_size: usize) -> Self {
1521 NatAnalysisCache {
1522 entries: std::collections::HashMap::new(),
1523 max_size,
1524 hits: 0,
1525 misses: 0,
1526 }
1527 }
1528 #[allow(dead_code)]
1529 pub fn get(&mut self, key: &str) -> Option<&NatCacheEntry> {
1530 if self.entries.contains_key(key) {
1531 self.hits += 1;
1532 self.entries.get(key)
1533 } else {
1534 self.misses += 1;
1535 None
1536 }
1537 }
1538 #[allow(dead_code)]
1539 pub fn insert(&mut self, key: String, data: Vec<u8>) {
1540 if self.entries.len() >= self.max_size {
1541 if let Some(oldest) = self.entries.keys().next().cloned() {
1542 self.entries.remove(&oldest);
1543 }
1544 }
1545 self.entries.insert(
1546 key.clone(),
1547 NatCacheEntry {
1548 key,
1549 data,
1550 timestamp: 0,
1551 valid: true,
1552 },
1553 );
1554 }
1555 #[allow(dead_code)]
1556 pub fn invalidate(&mut self, key: &str) {
1557 if let Some(entry) = self.entries.get_mut(key) {
1558 entry.valid = false;
1559 }
1560 }
1561 #[allow(dead_code)]
1562 pub fn clear(&mut self) {
1563 self.entries.clear();
1564 }
1565 #[allow(dead_code)]
1566 pub fn hit_rate(&self) -> f64 {
1567 let total = self.hits + self.misses;
1568 if total == 0 {
1569 return 0.0;
1570 }
1571 self.hits as f64 / total as f64
1572 }
1573 #[allow(dead_code)]
1574 pub fn size(&self) -> usize {
1575 self.entries.len()
1576 }
1577}
1578pub struct RegisterAllocator {
1583 pub(super) num_phys_regs: usize,
1585 pub(super) intervals: Vec<LiveInterval>,
1587 pub(super) active: Vec<usize>,
1589 pub(super) next_spill: u32,
1591 pub(super) free_regs: Vec<bool>,
1593 pub(super) spill_count: usize,
1595}
1596impl RegisterAllocator {
1597 pub fn new(num_phys_regs: usize) -> Self {
1599 RegisterAllocator {
1600 num_phys_regs,
1601 intervals: Vec::new(),
1602 active: Vec::new(),
1603 next_spill: 0,
1604 free_regs: vec![true; num_phys_regs],
1605 spill_count: 0,
1606 }
1607 }
1608 pub fn allocate(&mut self, func: &NativeFunc) -> HashMap<Register, Register> {
1610 self.intervals.clear();
1611 self.active.clear();
1612 self.free_regs = vec![true; self.num_phys_regs];
1613 self.spill_count = 0;
1614 self.compute_live_intervals(func);
1615 self.intervals.sort_by_key(|i| i.start);
1616 let mut assignment: HashMap<Register, Register> = HashMap::new();
1617 for idx in 0..self.intervals.len() {
1618 let current_start = self.intervals[idx].start;
1619 self.expire_old_intervals(current_start);
1620 if let Some(phys_idx) = self.find_free_reg() {
1621 self.intervals[idx].assigned_phys = Some(Register::phys(phys_idx as u32));
1622 self.free_regs[phys_idx] = false;
1623 self.active.push(idx);
1624 self.active.sort_by_key(|&i| self.intervals[i].end);
1625 assignment.insert(self.intervals[idx].vreg, Register::phys(phys_idx as u32));
1626 } else {
1627 self.spill_at_interval(idx, &mut assignment);
1628 }
1629 }
1630 assignment
1631 }
1632 pub(super) fn compute_live_intervals(&mut self, func: &NativeFunc) {
1634 let mut intervals_map: HashMap<Register, (usize, usize)> = HashMap::new();
1635 let mut position = 0usize;
1636 for block in &func.blocks {
1637 for inst in &block.instructions {
1638 if let Some(dst) = inst.dst_reg() {
1639 if dst.is_virtual() {
1640 intervals_map
1641 .entry(dst)
1642 .and_modify(|(_s, e)| *e = position)
1643 .or_insert((position, position));
1644 }
1645 }
1646 for src in inst.src_regs() {
1647 if src.is_virtual() {
1648 intervals_map
1649 .entry(src)
1650 .and_modify(|(_s, e)| *e = position)
1651 .or_insert((position, position));
1652 }
1653 }
1654 position += 1;
1655 }
1656 if let Some(term) = &block.terminator {
1657 if let Some(dst) = term.dst_reg() {
1658 if dst.is_virtual() {
1659 intervals_map
1660 .entry(dst)
1661 .and_modify(|(_s, e)| *e = position)
1662 .or_insert((position, position));
1663 }
1664 }
1665 for src in term.src_regs() {
1666 if src.is_virtual() {
1667 intervals_map
1668 .entry(src)
1669 .and_modify(|(_s, e)| *e = position)
1670 .or_insert((position, position));
1671 }
1672 }
1673 position += 1;
1674 }
1675 }
1676 for (r, _) in &func.params {
1677 if r.is_virtual() {
1678 intervals_map
1679 .entry(*r)
1680 .and_modify(|(s, _e)| *s = 0)
1681 .or_insert((0, 0));
1682 }
1683 }
1684 self.intervals = intervals_map
1685 .into_iter()
1686 .map(|(vreg, (start, end))| LiveInterval {
1687 vreg,
1688 start,
1689 end,
1690 assigned_phys: None,
1691 spill_slot: None,
1692 })
1693 .collect();
1694 }
1695 pub(super) fn expire_old_intervals(&mut self, current_start: usize) {
1697 let mut to_remove = Vec::new();
1698 for (active_idx, &interval_idx) in self.active.iter().enumerate() {
1699 if self.intervals[interval_idx].end < current_start {
1700 if let Some(phys) = self.intervals[interval_idx].assigned_phys {
1701 let phys_idx = (phys.0 - VIRT_PHYS_BOUNDARY) as usize;
1702 if phys_idx < self.free_regs.len() {
1703 self.free_regs[phys_idx] = true;
1704 }
1705 }
1706 to_remove.push(active_idx);
1707 }
1708 }
1709 for idx in to_remove.into_iter().rev() {
1710 self.active.remove(idx);
1711 }
1712 }
1713 pub(super) fn find_free_reg(&self) -> Option<usize> {
1715 self.free_regs.iter().position(|&free| free)
1716 }
1717 pub(super) fn spill_at_interval(
1719 &mut self,
1720 current_idx: usize,
1721 assignment: &mut HashMap<Register, Register>,
1722 ) {
1723 if !self.active.is_empty() {
1724 let last_active_idx = *self
1725 .active
1726 .last()
1727 .expect("active is non-empty; guaranteed by !self.active.is_empty() guard");
1728 if self.intervals[last_active_idx].end > self.intervals[current_idx].end {
1729 let phys = self
1730 .intervals[last_active_idx]
1731 .assigned_phys
1732 .expect(
1733 "last active interval must have an assigned physical register; invariant of linear scan regalloc",
1734 );
1735 self.intervals[current_idx].assigned_phys = Some(phys);
1736 assignment.insert(self.intervals[current_idx].vreg, phys);
1737 let spill_slot = self.next_spill;
1738 self.next_spill += 1;
1739 self.intervals[last_active_idx].assigned_phys = None;
1740 self.intervals[last_active_idx].spill_slot = Some(spill_slot);
1741 assignment.remove(&self.intervals[last_active_idx].vreg);
1742 self.active.pop();
1743 self.active.push(current_idx);
1744 self.active.sort_by_key(|&i| self.intervals[i].end);
1745 self.spill_count += 1;
1746 return;
1747 }
1748 }
1749 let spill_slot = self.next_spill;
1750 self.next_spill += 1;
1751 self.intervals[current_idx].spill_slot = Some(spill_slot);
1752 self.spill_count += 1;
1753 }
1754 pub fn spill_count(&self) -> usize {
1756 self.spill_count
1757 }
1758}
1759#[allow(dead_code)]
1760#[derive(Debug, Clone)]
1761pub struct NatWorklist {
1762 pub(super) items: std::collections::VecDeque<u32>,
1763 pub(super) in_worklist: std::collections::HashSet<u32>,
1764}
1765impl NatWorklist {
1766 #[allow(dead_code)]
1767 pub fn new() -> Self {
1768 NatWorklist {
1769 items: std::collections::VecDeque::new(),
1770 in_worklist: std::collections::HashSet::new(),
1771 }
1772 }
1773 #[allow(dead_code)]
1774 pub fn push(&mut self, item: u32) -> bool {
1775 if self.in_worklist.insert(item) {
1776 self.items.push_back(item);
1777 true
1778 } else {
1779 false
1780 }
1781 }
1782 #[allow(dead_code)]
1783 pub fn pop(&mut self) -> Option<u32> {
1784 let item = self.items.pop_front()?;
1785 self.in_worklist.remove(&item);
1786 Some(item)
1787 }
1788 #[allow(dead_code)]
1789 pub fn is_empty(&self) -> bool {
1790 self.items.is_empty()
1791 }
1792 #[allow(dead_code)]
1793 pub fn len(&self) -> usize {
1794 self.items.len()
1795 }
1796 #[allow(dead_code)]
1797 pub fn contains(&self, item: u32) -> bool {
1798 self.in_worklist.contains(&item)
1799 }
1800}