feo3boy_opcodes/compiler/
variables.rs1use std::sync::atomic::{AtomicUsize, Ordering};
4
5use quote::{format_ident, ToTokens};
6
7use crate::microcode::ValType;
8
9#[derive(Debug, Copy, Clone)]
11pub enum Conversion {
12 SameSize {
14 from: VarAssignment,
15 to: VarAssignment,
16 },
17 Split {
19 from: VarId,
20 low: VarId,
21 high: VarId,
22 },
23 Merge { low: VarId, high: VarId, to: VarId },
25}
26
27#[derive(Default)]
28pub struct VarAssigner {
29 next_var: AtomicUsize,
30}
31
32impl VarAssigner {
33 fn take_next(&self) -> VarId {
35 VarId(self.next_var.fetch_add(1, Ordering::Relaxed))
36 }
37
38 fn assign(&self, val: ValType) -> VarAssignment {
40 VarAssignment {
41 var: self.take_next(),
42 val,
43 }
44 }
45}
46
47#[derive(Debug, Copy, Clone, Eq, PartialEq)]
48pub struct VarId(usize);
50
51impl ToTokens for VarId {
53 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
54 format_ident!("val{}", self.0).to_tokens(tokens)
55 }
56}
57
58#[derive(Clone)]
60pub struct StackTracker<'a> {
61 pub parent: Option<Box<StackTracker<'a>>>,
63 assigner: &'a VarAssigner,
65 pub local_stack: Vec<VarAssignment>,
67}
68
69impl<'a> PartialEq for StackTracker<'a> {
70 fn eq(&self, other: &Self) -> bool {
71 self.parent == other.parent
72 && self.assigner as *const _ == other.assigner as *const _
73 && self.local_stack == other.local_stack
74 }
75}
76
77impl<'a> StackTracker<'a> {
78 pub fn new_root(assigner: &'a VarAssigner) -> Self {
80 Self {
81 parent: None,
82 assigner,
83 local_stack: Vec::new(),
84 }
85 }
86
87 pub fn make_child(&self) -> Self {
89 Self {
90 parent: Some(Box::new(self.clone())),
91 assigner: self.assigner,
92 local_stack: vec![],
93 }
94 }
95
96 pub fn root_with_stack(assigner: &'a VarAssigner, initial_stack: Vec<VarAssignment>) -> Self {
98 Self {
99 parent: None,
100 assigner,
101 local_stack: initial_stack,
102 }
103 }
104
105 pub fn pop(&mut self, val: ValType) -> (VarAssignment, Vec<Conversion>) {
111 let (assignment, conversions, extra) = self.pop_with_extra(val);
112 if let Some(extra) = extra {
113 self.local_stack.push(extra);
114 }
115 (assignment, conversions)
116 }
117
118 pub fn push(&mut self, val: ValType) -> VarAssignment {
121 let assignment = self.assigner.assign(val);
122 self.local_stack.push(assignment);
123 assignment
124 }
125
126 fn pop_with_extra(
131 &mut self,
132 val: ValType,
133 ) -> (VarAssignment, Vec<Conversion>, Option<VarAssignment>) {
134 if val.bytes() <= self.local_bytes() {
136 let top = self.local_stack.pop().unwrap();
137 if top.val.bytes() == val.bytes() {
138 if top.val == val {
139 (top, vec![], None)
140 } else {
141 let new_assignment = self.assigner.assign(val);
142 (
143 new_assignment,
144 vec![Conversion::SameSize {
145 from: top,
146 to: new_assignment,
147 }],
148 None,
149 )
150 }
151 } else if top.val.bytes() < val.bytes() {
152 assert!(val == ValType::U16);
153 assert!(top.val.bytes() == 1);
154 let second = self.local_stack.pop().unwrap();
155 let mut conversions = vec![];
156 let high = if top.val == ValType::U8 {
157 top
158 } else {
159 let converted_top = self.assigner.assign(ValType::U8);
160 conversions.push(Conversion::SameSize {
161 from: top,
162 to: converted_top,
163 });
164 converted_top
165 };
166
167 let (low, extra) = match second.val {
168 ValType::Bool | ValType::Flags => {
169 let converted_second = self.assigner.assign(ValType::U8);
170 conversions.push(Conversion::SameSize {
171 from: top,
172 to: converted_second,
173 });
174 (converted_second, None)
175 }
176 ValType::U8 => (second, None),
177 ValType::U16 => {
178 let second_low = self.assigner.assign(ValType::U8);
179 let second_high = self.assigner.assign(ValType::U8);
180 conversions.push(Conversion::Split {
181 from: second.var,
182 low: second_low.var,
183 high: second_high.var,
184 });
185 (second_high, Some(second_low))
186 }
187 };
188
189 let final_assignment = self.assigner.assign(ValType::U16);
190 conversions.push(Conversion::Merge {
191 low: low.var,
192 high: high.var,
193 to: final_assignment.var,
194 });
195
196 (final_assignment, conversions, extra)
197 } else {
198 assert!(top.val == ValType::U16);
199 assert!(val.bytes() == 1);
200 let mut conversions = Vec::with_capacity(2);
201 let low = self.assigner.assign(ValType::U8);
202 let high = self.assigner.assign(ValType::U8);
203 conversions.push(Conversion::Split {
204 from: top.var,
205 low: low.var,
206 high: high.var,
207 });
208 let final_assignment = if val == ValType::U8 {
210 high
211 } else {
212 let assignment = self.assigner.assign(val);
213 conversions.push(Conversion::SameSize {
214 from: high,
215 to: assignment,
216 });
217 assignment
218 };
219
220 (final_assignment, conversions, Some(low))
221 }
222 } else if let Some(parent) = self.parent.as_deref_mut() {
223 if self.local_stack.is_empty() {
225 parent.pop_with_extra(val)
226 } else {
227 let top = self.local_stack.pop().unwrap();
228 assert!(top.val.bytes() == 1);
229 assert!(val == ValType::U16);
230 let mut conversions = vec![];
231 let high = if top.val == ValType::U8 {
232 top
233 } else {
234 let high = self.assigner.assign(ValType::U8);
235 conversions.push(Conversion::SameSize {
236 from: top,
237 to: high,
238 });
239 high
240 };
241
242 let (low, parent_conversions, extra) = parent.pop_with_extra(ValType::U8);
243 conversions.extend_from_slice(&parent_conversions);
244
245 let final_assignment = self.assigner.assign(ValType::U16);
246 conversions.push(Conversion::Merge {
247 low: low.var,
248 high: high.var,
249 to: final_assignment.var,
250 });
251
252 (final_assignment, conversions, extra)
253 }
254 } else {
255 panic!("Stack underflow -- not enough bytes at this level, and no parent available");
256 }
257 }
258
259 pub fn local_bytes(&self) -> usize {
261 self.local_stack
262 .iter()
263 .map(|assignment| assignment.val.bytes())
264 .sum()
265 }
266
267 pub fn total_bytes(&self) -> usize {
270 self.local_bytes()
271 + self
272 .parent
273 .as_deref()
274 .map(|p| p.total_bytes())
275 .unwrap_or_default()
276 }
277
278 pub fn snapshot(&self) -> Vec<VarAssignment> {
280 let mut parent_snapshot = self
281 .parent
282 .as_deref()
283 .map(Self::snapshot)
284 .unwrap_or_default();
285 parent_snapshot.extend_from_slice(&self.local_stack);
286 parent_snapshot
287 }
288}
289
290#[derive(Debug, Copy, Clone, Eq, PartialEq)]
292pub struct VarAssignment {
293 pub var: VarId,
295 pub val: ValType,
297}