lamina_codegen/aarch64/
regalloc.rs1use std::collections::{HashMap, HashSet, VecDeque};
2
3use crate::regalloc::{Allocation, LocalRegisterAllocator as MirRegisterAllocator};
4use lamina_mir::{Register, RegisterClass, VirtualReg};
5
6pub struct A64RegAlloc {
14 free_gprs: VecDeque<&'static str>,
15 used_gprs: HashSet<&'static str>,
16 vreg_to_preg: HashMap<VirtualReg, &'static str>,
17 scratch_free: VecDeque<&'static str>,
18 scratch_used: HashSet<&'static str>,
19}
20
21const MAP_GPRS: &[&str] = &["x13", "x14", "x15"];
24
25const SCRATCH_GPRS: &[&str] = &["x9", "x10", "x11", "x12"];
26
27impl Default for A64RegAlloc {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl A64RegAlloc {
34 pub fn new() -> Self {
36 let mut free_gprs = VecDeque::new();
37 for &r in MAP_GPRS {
38 free_gprs.push_back(r);
39 }
40
41 let mut scratch_free = VecDeque::new();
42 for &r in SCRATCH_GPRS {
43 scratch_free.push_back(r);
44 }
45
46 Self {
47 free_gprs,
48 used_gprs: HashSet::new(),
49 vreg_to_preg: HashMap::new(),
50 scratch_free,
51 scratch_used: HashSet::new(),
52 }
53 }
54
55 pub fn set_conservative_mode(&mut self) {
57 self.free_gprs.clear();
58 self.free_gprs.push_back("x13");
59 self.free_gprs.push_back("x14");
60 self.free_gprs.push_back("x15");
61 self.used_gprs.retain(|r| self.free_gprs.contains(r));
63 let free_set: HashSet<&str> = self.free_gprs.iter().copied().collect();
64 self.vreg_to_preg.retain(|_, preg| free_set.contains(preg));
65 }
66
67 #[inline]
68 pub fn alloc_scratch(&mut self) -> Option<&'static str> {
69 MirRegisterAllocator::alloc_scratch(self)
70 }
71
72 #[inline]
73 pub fn free_scratch(&mut self, phys: &'static str) {
74 MirRegisterAllocator::free_scratch(self, phys);
75 }
76
77 #[inline]
78 pub fn is_occupied(&self, phys: &'static str) -> bool {
79 MirRegisterAllocator::is_occupied(self, phys)
80 }
81
82 #[inline]
83 pub fn occupy(&mut self, phys: &'static str) {
84 MirRegisterAllocator::occupy(self, phys);
85 }
86
87 #[inline]
88 pub fn release(&mut self, phys: &'static str) {
89 MirRegisterAllocator::release(self, phys);
90 }
91
92 #[inline]
93 pub fn get_mapping_for(&self, v: &VirtualReg) -> Option<&'static str> {
94 MirRegisterAllocator::get_mapping(self, v)
95 }
96
97 #[inline]
98 pub fn ensure_mapping(&mut self, v: VirtualReg) -> Option<&'static str> {
99 MirRegisterAllocator::ensure_mapping(self, v)
100 }
101
102 #[inline]
103 pub fn ensure_mapping_for_gpr(&mut self, v: VirtualReg) -> Option<&'static str> {
104 MirRegisterAllocator::ensure_mapping(self, v)
105 }
106
107 #[inline]
108 pub fn mapped_for_register(&self, r: &Register) -> Option<&'static str> {
109 MirRegisterAllocator::mapped_for_register(self, r)
110 }
111
112 pub fn gpr_pool_for_global_allocation() -> Vec<&'static str> {
113 MAP_GPRS.to_vec()
114 }
115
116 pub fn from_global_plan(plan: &HashMap<VirtualReg, Allocation<&'static str>>) -> Self {
117 let mut s = Self::new();
118 for (&vreg, alloc) in plan {
119 if vreg.class != RegisterClass::Gpr {
120 continue;
121 }
122 if let Allocation::Register(phys) = alloc
123 && MAP_GPRS.contains(phys)
124 && s.vreg_to_preg.insert(vreg, *phys).is_none()
125 {
126 s.used_gprs.insert(*phys);
127 if let Some(pos) = s.free_gprs.iter().position(|&p| p == *phys) {
128 let _ = s.free_gprs.remove(pos);
129 }
130 }
131 }
132 s
133 }
134}
135
136impl MirRegisterAllocator for A64RegAlloc {
137 type PhysReg = &'static str;
138
139 fn alloc_scratch(&mut self) -> Option<Self::PhysReg> {
140 if let Some(phys) = self.scratch_free.pop_front() {
141 self.scratch_used.insert(phys);
142 Some(phys)
143 } else {
144 None
145 }
146 }
147
148 fn free_scratch(&mut self, phys: Self::PhysReg) {
149 if self.scratch_used.remove(&phys) {
150 self.scratch_free.push_back(phys);
151 }
152 }
153
154 fn get_mapping(&self, vreg: &VirtualReg) -> Option<Self::PhysReg> {
155 self.vreg_to_preg.get(vreg).copied()
156 }
157
158 fn ensure_mapping(&mut self, vreg: VirtualReg) -> Option<Self::PhysReg> {
159 if let Some(&phys) = self.vreg_to_preg.get(&vreg) {
160 return Some(phys);
161 }
162
163 if vreg.class != RegisterClass::Gpr {
164 return None;
165 }
166
167 if let Some(phys) = self.free_gprs.pop_front() {
168 self.used_gprs.insert(phys);
169 self.vreg_to_preg.insert(vreg, phys);
170 Some(phys)
171 } else if let Some((vreg_to_replace, &phys)) = self.vreg_to_preg.iter().next() {
172 let vreg_to_replace = *vreg_to_replace;
173 self.vreg_to_preg.remove(&vreg_to_replace);
174 self.vreg_to_preg.insert(vreg, phys);
175 Some(phys)
176 } else {
177 None
178 }
179 }
180
181 fn mapped_for_register(&self, reg: &Register) -> Option<Self::PhysReg> {
182 match reg {
183 Register::Virtual(v) => self.vreg_to_preg.get(v).copied(),
184 Register::Physical(p) => Some(p.name),
185 }
186 }
187
188 fn occupy(&mut self, phys: Self::PhysReg) {
189 if self.used_gprs.insert(phys)
190 && let Some(pos) = self.free_gprs.iter().position(|&p| p == phys)
191 {
192 self.free_gprs.remove(pos);
193 }
194 }
195
196 fn release(&mut self, phys: Self::PhysReg) {
197 if self.used_gprs.remove(&phys) {
198 self.free_gprs.push_back(phys);
199 }
200 }
201
202 fn is_occupied(&self, phys: Self::PhysReg) -> bool {
203 self.used_gprs.contains(&phys)
204 }
205}