use std::collections::HashMap; use std::sync::atomic::AtomicU32; use crate::asm::data::{Rd, SrcDisp}; use crate::asm::data::literal::{Label, Value}; use crate::asm::error::{AsmError, Error}; use crate::asm::instr::{Cond, Instr, Op, Routine}; /// A trait for something that can turn into multiple instructions pub trait Flatten { fn flatten(self, label_num: &AtomicU32) -> Result, Error>; } impl Flatten for Instr { fn flatten(self, label_num: &AtomicU32) -> Result, Error> { let mut ops = vec![self.op]; if let Some(branches) = self.branches { let labels = HashMap::::new(); let branch_count = branches.len(); let end_lbl = Label::unique(label_num); for (cnt, (cond, branch)) in branches.into_iter().enumerate() { if labels.contains_key(&cond) { return Err(Error::Asm(AsmError::ConditionalAlreadyUsed(cond))); } let next_lbl = if cnt == branch_count - 1 { end_lbl.clone() } else { Label::unique(label_num) }; ops.push(Op::JumpIf(!cond, next_lbl.clone())); for branch_instr in branch { ops.extend(branch_instr.flatten(label_num)?); } if cnt != branch_count - 1 { ops.push(Op::Jump(end_lbl.clone())); ops.push(Op::Label(next_lbl)); } } ops.push(Op::Label(end_lbl)); } Ok(ops) } } impl Flatten for Routine { fn flatten(self, label_num: &AtomicU32) -> Result, Error> { let mut ops = vec![ Op::Routine(self.name.clone()).into(), ]; for instr in self.body { ops.extend(instr.flatten(label_num)?); } ops.push(Op::Barrier(Some(format!("Routine \"{}\" overrun", self.name).into())).into()); Ok(ops) } }