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