parent
a52f1e5e72
commit
ab843c0c87
@ -0,0 +1,104 @@ |
||||
use std::sync::atomic::AtomicU32; |
||||
use crate::instr::{Op, Instr, Cond, Routine}; |
||||
use crate::error::{Error, AsmError}; |
||||
use std::collections::HashMap; |
||||
use crate::data::literal::{Label, Value}; |
||||
use crate::instr::op::Op::{Skip, SkipIf}; |
||||
use crate::data::{Rd, SrcDisp, Mask}; |
||||
|
||||
/// A trait for something that can turn into multiple instructions
|
||||
pub trait Flatten { |
||||
fn flatten(self, label_num: &AtomicU32) -> Result<Vec<Op>, Error>; |
||||
} |
||||
|
||||
impl Flatten for Instr { |
||||
fn flatten(self, label_num: &AtomicU32) -> Result<Vec<Op>, Error> { |
||||
let mut ops = vec![self.op]; |
||||
|
||||
if let Some(branches) = self.branches { |
||||
let labels = HashMap::<Cond, u32>::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<Vec<Op>, Error> { |
||||
let mut ops = vec![ |
||||
Op::Routine(self.name.clone()), |
||||
]; |
||||
|
||||
for instr in self.body { |
||||
ops.extend(instr.flatten(label_num)?); |
||||
} |
||||
|
||||
ops.push(Op::Barrier(Some(format!("Routine \"{}\" overrun", self.name).into()))); |
||||
Ok(ops) |
||||
} |
||||
} |
||||
|
||||
/// Convert jumps to relative skips
|
||||
pub fn jumps_to_skips(ops: Vec<Op>) -> Result<Vec<Op>, Error> { |
||||
let mut label_positions = HashMap::<Label, usize>::new(); |
||||
for (n, op) in ops.iter().enumerate() { |
||||
if let Op::Label(name) = op { |
||||
label_positions.insert(name.clone(), n - label_positions.len()); |
||||
} |
||||
} |
||||
|
||||
let mut cleaned = vec![]; |
||||
let mut skipped = 0; |
||||
for (n, op) in ops.into_iter().enumerate() { |
||||
match op { |
||||
Op::Label(_) => { |
||||
skipped += 1; |
||||
} |
||||
Op::Jump(target) => { |
||||
if let Some(dest) = label_positions.get(&target) { |
||||
let skip = *dest as isize - n as isize + skipped; |
||||
cleaned.push(Skip(Rd(SrcDisp::Immediate(Value(skip as i64)), Mask::default()))); |
||||
} else { |
||||
return Err(Error::Asm(AsmError::LabelNotDefined(target))); |
||||
} |
||||
} |
||||
Op::JumpIf(cond, target) => { |
||||
if let Some(dest) = label_positions.get(&target) { |
||||
let skip = *dest as isize - n as isize + skipped; |
||||
cleaned.push(SkipIf(cond, Rd(SrcDisp::Immediate(Value(skip as i64)), Mask::default()))); |
||||
} else { |
||||
return Err(Error::Asm(AsmError::LabelNotDefined(target))); |
||||
} |
||||
} |
||||
other => { |
||||
cleaned.push(other); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Ok(cleaned) |
||||
} |
Loading…
Reference in new issue