solar_codegen/backend/evm/stack/
spill.rs1use crate::mir::ValueId;
7use solar_data_structures::map::{FxHashMap, FxHashSet};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
11pub struct SpillSlot {
12 pub offset: u32,
14}
15
16impl SpillSlot {
17 #[must_use]
19 pub const fn byte_offset(&self) -> u32 {
20 0x1000 + self.offset * 32
45 }
46}
47
48#[derive(Clone, Debug)]
50pub struct SpillManager {
51 slots: FxHashMap<ValueId, SpillSlot>,
53 reloadable: FxHashSet<ValueId>,
55 stored: FxHashSet<ValueId>,
57 next_offset: u32,
59 max_offset: u32,
61}
62
63impl SpillManager {
64 #[must_use]
66 pub fn new() -> Self {
67 Self {
68 slots: FxHashMap::default(),
69 reloadable: FxHashSet::default(),
70 stored: FxHashSet::default(),
71 next_offset: 0,
72 max_offset: 0,
73 }
74 }
75
76 pub fn allocate(&mut self, value: ValueId) -> SpillSlot {
79 if let Some(&slot) = self.slots.get(&value) {
80 return slot;
81 }
82
83 let slot = SpillSlot { offset: self.next_offset };
84 self.slots.insert(value, slot);
85 self.next_offset += 1;
86 self.max_offset = self.max_offset.max(self.next_offset);
87 slot
88 }
89
90 #[must_use]
92 pub fn get(&self, value: ValueId) -> Option<SpillSlot> {
93 self.slots.get(&value).copied()
94 }
95
96 #[must_use]
98 pub fn is_spilled(&self, value: ValueId) -> bool {
99 self.slots.contains_key(&value)
100 }
101
102 pub fn mark_reloadable(&mut self, value: ValueId) {
104 debug_assert!(self.slots.contains_key(&value));
105 self.reloadable.insert(value);
106 }
107
108 pub fn mark_stored(&mut self, value: ValueId) {
110 debug_assert!(self.slots.contains_key(&value));
111 self.reloadable.insert(value);
112 self.stored.insert(value);
113 }
114
115 #[must_use]
117 pub fn is_reloadable(&self, value: ValueId) -> bool {
118 self.reloadable.contains(&value)
119 }
120
121 #[must_use]
123 pub fn is_stored(&self, value: ValueId) -> bool {
124 self.stored.contains(&value)
125 }
126
127 pub fn free(&mut self, value: ValueId) {
130 self.slots.remove(&value);
131 self.reloadable.remove(&value);
132 self.stored.remove(&value);
133 }
134
135 #[must_use]
137 pub fn spill_area_size(&self) -> u32 {
138 self.max_offset * 32
139 }
140
141 pub fn clear(&mut self) {
143 self.slots.clear();
144 self.reloadable.clear();
145 self.stored.clear();
146 self.next_offset = 0;
147 self.max_offset = 0;
148 }
149
150 #[must_use]
152 pub fn count(&self) -> usize {
153 self.slots.len()
154 }
155}
156
157impl Default for SpillManager {
158 fn default() -> Self {
159 Self::new()
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn test_allocate() {
169 let mut manager = SpillManager::new();
170 let v0 = ValueId::from_usize(0);
171 let v1 = ValueId::from_usize(1);
172 let v2 = ValueId::from_usize(2);
173
174 let slot0 = manager.allocate(v0);
175 let slot1 = manager.allocate(v1);
176 let slot2 = manager.allocate(v2);
177
178 assert_eq!(slot0.offset, 0);
179 assert_eq!(slot1.offset, 1);
180 assert_eq!(slot2.offset, 2);
181 assert_eq!(slot0.byte_offset(), 0x1000);
183 assert_eq!(slot1.byte_offset(), 0x1000 + 32);
184 assert_eq!(slot2.byte_offset(), 0x1000 + 64);
185 }
186
187 #[test]
188 fn test_allocate_idempotent() {
189 let mut manager = SpillManager::new();
190 let v0 = ValueId::from_usize(0);
191
192 let slot1 = manager.allocate(v0);
193 let slot2 = manager.allocate(v0);
194
195 assert_eq!(slot1, slot2);
196 assert_eq!(manager.count(), 1);
197 }
198
199 #[test]
200 fn test_free() {
201 let mut manager = SpillManager::new();
202 let v0 = ValueId::from_usize(0);
203
204 manager.allocate(v0);
205 assert!(manager.is_spilled(v0));
206
207 manager.free(v0);
208 assert!(!manager.is_spilled(v0));
209 }
210
211 #[test]
212 fn test_reloadable_and_stored_are_distinct() {
213 let mut manager = SpillManager::new();
214 let v0 = ValueId::from_usize(0);
215 let v1 = ValueId::from_usize(1);
216
217 manager.allocate(v0);
218 assert!(!manager.is_reloadable(v0));
219 assert!(!manager.is_stored(v0));
220
221 manager.mark_reloadable(v0);
222 assert!(manager.is_reloadable(v0));
223 assert!(!manager.is_stored(v0));
224
225 manager.allocate(v1);
226 manager.mark_stored(v1);
227 assert!(manager.is_reloadable(v1));
228 assert!(manager.is_stored(v1));
229 }
230}