revmc_codegen/compiler/translate/
vstack.rs1use core::ops::Range;
8
9#[derive(Clone, Copy, Debug)]
11pub(super) enum VSlot<T> {
12 Materialized,
14 Virtual(T),
16}
17
18#[derive(Clone, Debug)]
25pub(super) struct VStack<T> {
26 base_offset: i32,
28 top_offset: i32,
31 slots: Vec<Option<VSlot<T>>>,
33}
34
35impl<T> Default for VStack<T> {
36 fn default() -> Self {
37 Self { base_offset: 0, top_offset: 0, slots: Vec::new() }
38 }
39}
40
41impl<T: Copy> VStack<T> {
42 pub(super) fn reset(&mut self, inputs: usize, max_growth: usize) {
51 self.base_offset = -(inputs as i32);
52 self.top_offset = 0;
53 let capacity = inputs + max_growth;
54 self.slots.clear();
55 self.slots.resize(capacity, None);
56
57 for i in 0..inputs {
59 self.slots[i] = Some(VSlot::Materialized);
60 }
61 }
62
63 pub(super) fn top_offset(&self) -> i32 {
65 self.top_offset
66 }
67
68 pub(super) fn offset_at_depth(&self, depth: usize) -> i32 {
71 self.top_offset - 1 - depth as i32
72 }
73
74 fn idx(&self, offset: i32) -> usize {
75 let idx = offset - self.base_offset;
76 debug_assert!(
77 idx >= 0 && (idx as usize) < self.slots.len(),
78 "VStack: offset {offset} out of range [{}..{}), base={}, top={}, cap={}",
79 self.base_offset,
80 self.base_offset + self.slots.len() as i32,
81 self.base_offset,
82 self.top_offset,
83 self.slots.len(),
84 );
85 idx as usize
86 }
87
88 pub(super) fn get(&self, depth: usize) -> VSlot<T> {
90 let idx = self.idx(self.offset_at_depth(depth));
91 self.slots[idx].unwrap()
92 }
93
94 pub(super) fn get_at_offset(&self, offset: i32) -> VSlot<T> {
97 let idx = offset - self.base_offset;
98 if idx < 0 || idx as usize >= self.slots.len() {
99 return VSlot::Materialized;
100 }
101 self.slots[idx as usize].unwrap_or(VSlot::Materialized)
102 }
103
104 pub(super) fn set(&mut self, depth: usize, value: T) {
106 let idx = self.idx(self.offset_at_depth(depth));
107 self.slots[idx] = Some(VSlot::Virtual(value));
108 }
109
110 pub(super) fn set_at_offset(&mut self, offset: i32, value: T) {
112 let idx = self.idx(offset);
113 self.slots[idx] = Some(VSlot::Virtual(value));
114 }
115
116 pub(super) fn push(&mut self, value: T) {
118 let idx = self.idx(self.top_offset);
119 self.slots[idx] = Some(VSlot::Virtual(value));
120 self.top_offset += 1;
121 }
122
123 pub(super) fn push_mem(&mut self) {
126 let idx = self.idx(self.top_offset);
127 self.slots[idx] = Some(VSlot::Materialized);
128 self.top_offset += 1;
129 }
130
131 pub(super) fn drop_top(&mut self, n: usize) {
133 self.top_offset -= n as i32;
134 debug_assert!(
135 self.top_offset >= self.base_offset,
136 "VStack::drop_top({n}): top={} dropped below base={}",
137 self.top_offset,
138 self.base_offset,
139 );
140 }
141
142 pub(super) fn live_range(&self) -> Range<i32> {
144 self.base_offset..self.top_offset
145 }
146
147 pub(super) fn virtual_count(&self) -> usize {
149 self.pending_stores(self.live_range()).count()
150 }
151
152 pub(super) fn pending_stores(&self, range: Range<i32>) -> impl Iterator<Item = (i32, T)> + '_ {
158 range.filter_map(|off| {
159 let idx = off - self.base_offset;
160 if idx < 0 || idx as usize >= self.slots.len() {
161 return None;
162 }
163 match self.slots.get(idx as usize)?.as_ref()? {
164 VSlot::Virtual(v) => Some((off, *v)),
165 VSlot::Materialized => None,
166 }
167 })
168 }
169
170 pub(super) fn mark_materialized_range(&mut self, range: Range<i32>) {
174 for off in range {
175 let idx = off - self.base_offset;
176 if idx < 0 || idx as usize >= self.slots.len() {
177 continue;
178 }
179 if let Some(slot) = self.slots.get_mut(idx as usize)
180 && slot.is_some()
181 {
182 *slot = Some(VSlot::Materialized);
183 }
184 }
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn push_pop_basic() {
194 let mut vs = VStack::<i32>::default();
195 vs.reset(0, 4);
197 assert_eq!(vs.top_offset(), 0);
198
199 vs.push(10);
200 vs.push(20);
201 assert_eq!(vs.top_offset(), 2);
202
203 match vs.get(0) {
205 VSlot::Virtual(v) => assert_eq!(v, 20),
206 _ => panic!("expected virtual"),
207 }
208 match vs.get(1) {
209 VSlot::Virtual(v) => assert_eq!(v, 10),
210 _ => panic!("expected virtual"),
211 }
212
213 vs.drop_top(1);
214 assert_eq!(vs.top_offset(), 1);
215 match vs.get(0) {
216 VSlot::Virtual(v) => assert_eq!(v, 10),
217 _ => panic!("expected virtual"),
218 }
219 }
220
221 #[test]
222 fn section_inputs_materialized() {
223 let mut vs = VStack::<i32>::default();
224 vs.reset(2, 2);
226 assert_eq!(vs.top_offset(), 0);
227 assert_eq!(vs.live_range(), -2..0);
228
229 let pending: Vec<_> = vs.pending_stores(-2..0).collect();
239 assert!(pending.is_empty(), "inputs should be materialized");
240 }
241
242 #[test]
243 fn set_swap() {
244 let mut vs = VStack::<i32>::default();
245 vs.reset(0, 4);
246 vs.push(10);
247 vs.push(20);
248 vs.push(30);
249
250 let a = match vs.get(0) {
252 VSlot::Virtual(v) => v,
253 _ => panic!("expected virtual"),
254 };
255 let b = match vs.get(2) {
256 VSlot::Virtual(v) => v,
257 _ => panic!("expected virtual"),
258 };
259 vs.set(0, b);
260 vs.set(2, a);
261
262 match vs.get(0) {
263 VSlot::Virtual(v) => assert_eq!(v, 10),
264 _ => panic!("expected virtual"),
265 }
266 match vs.get(2) {
267 VSlot::Virtual(v) => assert_eq!(v, 30),
268 _ => panic!("expected virtual"),
269 }
270 }
271
272 #[test]
273 fn materialize_range() {
274 let mut vs = VStack::<i32>::default();
275 vs.reset(0, 4);
276 vs.push(10);
277 vs.push(20);
278 vs.push(30);
279
280 let pending: Vec<_> = vs.pending_stores(0..3).collect();
281 assert_eq!(pending.len(), 3);
282
283 vs.mark_materialized_range(0..3);
284 let pending: Vec<_> = vs.pending_stores(0..3).collect();
285 assert_eq!(pending.len(), 0);
286 }
287}