feo3boy_opcodes/compiler/
direct_executor_generation.rs1use crate::compiler::args::Arg;
4use crate::compiler::instr::flow::{Block, Branch, Element};
5use crate::compiler::variables::{Conversion, StackTracker, VarAssigner, VarAssignment, VarId};
6use crate::microcode::{Microcode, ValType};
7
8use super::instr::InstrDef;
9
10#[derive(Debug)]
12pub enum FuncElement {
13 Microcode(AssignedMicrocode),
15 Block(AssignedBlock),
17 Branch(AssignedBranch),
19}
20
21impl FuncElement {
22 pub fn new(instr: InstrDef) -> Self {
24 let assigner = VarAssigner::default();
25 let mut stack = StackTracker::new_root(&assigner);
26 let assigned = FuncElement::build_assignment(instr.flow(), &mut stack);
27 assert!(
28 stack.total_bytes() == 0,
29 "Stack was not empty at the end of instruction execution."
30 );
31 assigned
32 }
33
34 pub fn build_assignment(elem: &Element, parent_stack: &mut StackTracker) -> Self {
36 match elem {
37 Element::Microcode(microcode) => FuncElement::Microcode(
38 AssignedMicrocode::build_assignment(*microcode, parent_stack),
39 ),
40 Element::Block(block) => {
41 FuncElement::Block(AssignedBlock::build_assignment(block, parent_stack))
42 }
43 Element::Branch(branch) => {
44 FuncElement::Branch(AssignedBranch::build_assignment(branch, parent_stack))
45 }
46 }
47 }
48}
49
50#[derive(Debug)]
52pub struct AssignedMicrocode {
53 pub microcode: Microcode,
55 pub conversions: Vec<Conversion>,
57 pub args: Vec<Arg<VarAssignment>>,
59 pub returns: Vec<VarAssignment>,
61}
62
63impl AssignedMicrocode {
64 fn build_assignment(microcode: Microcode, parent_stack: &mut StackTracker) -> Self {
68 let desc = microcode.descriptor();
69 let mut conversions = vec![];
70 let args = desc
71 .args
72 .into_iter()
73 .map(|arg| match arg {
74 Arg::Literal(lit) => Arg::Literal(lit),
75 Arg::StackValue(val) => {
76 let (assignment, c) = parent_stack.pop(val);
77 conversions.extend_from_slice(&c);
78 Arg::StackValue(assignment)
79 }
80 })
81 .collect();
82 let returns = desc
83 .returns
84 .into_iter()
85 .map(|ret| parent_stack.push(ret))
86 .collect();
87
88 Self {
89 microcode,
90 conversions,
91 args,
92 returns,
93 }
94 }
95}
96
97#[derive(Debug)]
100pub struct AssignedBlock {
101 pub elements: Vec<FuncElement>,
103}
104
105impl AssignedBlock {
106 fn build_assignment(block: &Block, parent_stack: &mut StackTracker) -> Self {
107 let elements = block
108 .elements
109 .iter()
110 .map(|elem| FuncElement::build_assignment(elem, parent_stack))
111 .collect();
112
113 Self { elements }
114 }
115}
116
117#[derive(Debug)]
118pub struct AssignedBranch {
119 pub cond_conversion: Vec<Conversion>,
121 pub cond: VarId,
123
124 pub code_if_true: Box<FuncElement>,
126
127 pub true_returns: Vec<VarAssignment>,
130
131 pub true_end_conv: Vec<Conversion>,
134
135 pub code_if_false: Box<FuncElement>,
137
138 pub false_end_conv: Vec<Conversion>,
141
142 pub false_returns: Vec<VarAssignment>,
145
146 pub pushes: Vec<VarAssignment>,
148}
149
150impl AssignedBranch {
151 fn build_assignment(branch: &Branch, parent_stack: &mut StackTracker) -> Self {
152 let (cond, cond_conversion) = parent_stack.pop(ValType::Bool);
153
154 let mut true_stack = parent_stack.make_child();
155 let code_if_true = Box::new(FuncElement::build_assignment(
156 &branch.code_if_true,
157 &mut true_stack,
158 ));
159
160 let mut false_stack = parent_stack.make_child();
161 let code_if_false = Box::new(FuncElement::build_assignment(
162 &branch.code_if_false,
163 &mut false_stack,
164 ));
165
166 assert!(
167 true_stack.total_bytes() == false_stack.total_bytes(),
168 "True and false branches have different byte counts"
169 );
170
171 let true_terminal = branch.code_if_true.ends_with_terminal();
172 let false_terminal = branch.code_if_false.ends_with_terminal();
173
174 let mut true_end_conv = vec![];
175 let mut false_end_conv = vec![];
176 let pushes = if true_terminal && false_terminal {
177 assert!(
180 true_stack.total_bytes() == 0,
181 "Terminal branch has non-empty stack"
182 );
183 assert!(
184 false_stack.total_bytes() == 0,
185 "Terminal branch has non-empty stack"
186 );
187 vec![]
188 } else if true_terminal {
189 assert!(
193 true_stack.total_bytes() == 0,
194 "Terminal branch has non-empty stack"
195 );
196 *parent_stack = *false_stack.parent.unwrap();
197 false_stack
198 .local_stack
199 .iter()
200 .map(|asgn| parent_stack.push(asgn.val))
201 .collect()
202 } else if false_terminal {
203 assert!(
207 false_stack.total_bytes() == 0,
208 "Terminal branch has non-empty stack"
209 );
210 *parent_stack = *true_stack.parent.unwrap();
211 true_stack
212 .local_stack
213 .iter()
214 .map(|asgn| parent_stack.push(asgn.val))
215 .collect()
216 } else {
217 if true_stack.local_bytes() < false_stack.local_bytes() {
221 let mut true_pushes_rev = Vec::with_capacity(false_stack.local_stack.len());
225 for VarAssignment { val, .. } in false_stack.local_stack.iter().rev().copied() {
226 let (var, conv) = true_stack.pop(val);
227 true_end_conv.extend_from_slice(&conv);
228 true_pushes_rev.push(var);
229 }
230 true_pushes_rev.reverse();
231 assert!(true_stack.local_stack.is_empty());
232 true_stack.local_stack = true_pushes_rev;
233 } else if true_stack.local_bytes() > false_stack.local_bytes() {
234 let mut false_pushes_rev = Vec::with_capacity(true_stack.local_stack.len());
238 for VarAssignment { val, .. } in true_stack.local_stack.iter().rev().copied() {
239 let (var, conv) = false_stack.pop(val);
240 false_end_conv.extend_from_slice(&conv);
241 false_pushes_rev.push(var);
242 }
243 false_pushes_rev.reverse();
244 assert!(false_stack.local_stack.is_empty());
245 false_stack.local_stack = false_pushes_rev;
246 }
247
248 assert!(true_stack.parent == false_stack.parent);
250 assert!(
251 true_stack
252 .local_stack
253 .iter()
254 .map(|asgn| asgn.val)
255 .collect::<Vec<_>>()
256 == false_stack
257 .local_stack
258 .iter()
259 .map(|asgn| asgn.val)
260 .collect::<Vec<_>>()
261 );
262
263 *parent_stack = *true_stack.parent.unwrap();
264
265 true_stack
266 .local_stack
267 .iter()
268 .map(|asgn| parent_stack.push(asgn.val))
269 .collect()
270 };
271
272 Self {
273 cond_conversion,
274 cond: cond.var,
275 code_if_true,
276 true_end_conv,
277 true_returns: true_stack.local_stack,
278 code_if_false,
279 false_end_conv,
280 false_returns: false_stack.local_stack,
281 pushes,
282 }
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289 use crate::opcode::{CBOpcode, InternalFetch, Opcode};
290
291 #[test]
292 fn assign_all_instrs() {
293 FuncElement::new(InternalFetch.into());
294 for opcode in 0..=0xffu8 {
295 FuncElement::new(Opcode::decode(opcode).into());
296 }
297 for cbopcode in 0..=0xffu8 {
298 FuncElement::new(CBOpcode::decode(cbopcode).into());
299 }
300 }
301}