forked from MightyPork/crsn
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