|
|
|
@ -4,9 +4,10 @@ use std::sync::atomic::{AtomicU32, Ordering}; |
|
|
|
|
use crate::asm::data::{Rd, RdData}; |
|
|
|
|
use crate::asm::data::literal::{Label, Value}; |
|
|
|
|
use crate::asm::error::{AsmError, CrsnError}; |
|
|
|
|
use crate::asm::instr::{Cond, Instr, Op, Routine}; |
|
|
|
|
use crate::builtin::defs::BuiltinOp; |
|
|
|
|
use crate::asm::instr::{Cond, InstrWithBranches, Op, Routine}; |
|
|
|
|
use crate::asm::instr::op::OpKind; |
|
|
|
|
use crate::builtin::defs::Barrier; |
|
|
|
|
use crate::builtin::defs::BuiltinOp; |
|
|
|
|
|
|
|
|
|
/// A trait for something that can turn into multiple instructions
|
|
|
|
|
pub trait Flatten { |
|
|
|
@ -19,7 +20,7 @@ impl Flatten for () { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Flatten for Instr { |
|
|
|
|
impl Flatten for InstrWithBranches { |
|
|
|
|
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> { |
|
|
|
|
let mut ops = vec![self.op]; |
|
|
|
|
|
|
|
|
@ -37,8 +38,21 @@ impl Flatten for Instr { |
|
|
|
|
} else { |
|
|
|
|
Label::unique(label_num) |
|
|
|
|
}; |
|
|
|
|
ops.push(BuiltinOp::JumpIf(!cond, next_lbl.clone()).into()); |
|
|
|
|
ops.extend(branch.flatten(label_num)?); |
|
|
|
|
|
|
|
|
|
let mut flattened = branch.flatten(label_num)?; |
|
|
|
|
|
|
|
|
|
if flattened.len() == 0 { |
|
|
|
|
ops.push(Op { cond: Some(cond), kind: BuiltinOp::Jump(end_lbl.clone()).into() }); |
|
|
|
|
} else if flattened.len() == 1 && flattened[0].cond.is_none() && branch_count == 1 { |
|
|
|
|
// optimization for single-branch conditionals with a single instruction
|
|
|
|
|
ops.push(Op { cond: Some(cond), kind: flattened.remove(0).kind }); |
|
|
|
|
} else { |
|
|
|
|
ops.push(Op { |
|
|
|
|
kind: OpKind::BuiltIn(BuiltinOp::Jump(next_lbl.clone())), |
|
|
|
|
cond: Some(!cond), |
|
|
|
|
}); |
|
|
|
|
ops.extend(flattened); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if cnt != branch_count - 1 { |
|
|
|
|
ops.push(BuiltinOp::Jump(end_lbl.clone()).into()); |
|
|
|
@ -64,14 +78,13 @@ impl Flatten for Vec<Box<dyn Flatten>> { |
|
|
|
|
|
|
|
|
|
impl Flatten for Routine { |
|
|
|
|
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> { |
|
|
|
|
let skip_label = Label::Numbered(label_num.fetch_add(1, Ordering::Relaxed)); |
|
|
|
|
let skip_label = Label::unique(label_num); |
|
|
|
|
|
|
|
|
|
let mut ops: Vec<Op> = vec![ |
|
|
|
|
BuiltinOp::Barrier { |
|
|
|
|
kind: Barrier::Open(skip_label.clone()), |
|
|
|
|
msg: Some(format!("proc {} start", self.name).into()) |
|
|
|
|
msg: Some(format!("proc {} start", self.name).into()), |
|
|
|
|
}.into(), |
|
|
|
|
|
|
|
|
|
BuiltinOp::Routine(self.name.clone()).into(), |
|
|
|
|
]; |
|
|
|
|
|
|
|
|
@ -80,7 +93,7 @@ impl Flatten for Routine { |
|
|
|
|
ops.push( |
|
|
|
|
BuiltinOp::Barrier { |
|
|
|
|
kind: Barrier::Close(skip_label.clone()), |
|
|
|
|
msg: Some(format!("proc {} end", self.name).into()) |
|
|
|
|
msg: Some(format!("proc {} end", self.name).into()), |
|
|
|
|
}.into() |
|
|
|
|
); |
|
|
|
|
|
|
|
|
@ -92,7 +105,7 @@ impl Flatten for Routine { |
|
|
|
|
pub fn labels_to_skips(ops: Vec<Op>) -> Result<Vec<Op>, CrsnError> { |
|
|
|
|
let mut label_positions = HashMap::<Label, usize>::new(); |
|
|
|
|
for (n, op) in ops.iter().enumerate() { |
|
|
|
|
if let Op::BuiltIn(BuiltinOp::Label(name)) = op { |
|
|
|
|
if let OpKind::BuiltIn(BuiltinOp::Label(name)) = &op.kind { |
|
|
|
|
label_positions.insert(name.clone(), n - label_positions.len()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -100,28 +113,23 @@ pub fn labels_to_skips(ops: Vec<Op>) -> Result<Vec<Op>, CrsnError> { |
|
|
|
|
let mut cleaned = vec![]; |
|
|
|
|
let mut skipped = 0; |
|
|
|
|
for (n, op) in ops.into_iter().enumerate() { |
|
|
|
|
match op { |
|
|
|
|
Op::BuiltIn(BuiltinOp::Label(_)) => { |
|
|
|
|
match op.kind { |
|
|
|
|
OpKind::BuiltIn(BuiltinOp::Label(_)) => { |
|
|
|
|
skipped += 1; |
|
|
|
|
} |
|
|
|
|
Op::BuiltIn(BuiltinOp::Jump(target)) => { |
|
|
|
|
if let Some(dest) = label_positions.get(&target) { |
|
|
|
|
let skip = *dest as isize - n as isize + skipped; |
|
|
|
|
cleaned.push(Op::BuiltIn(BuiltinOp::Skip(Rd::new(RdData::Immediate(skip as Value))))); |
|
|
|
|
} else { |
|
|
|
|
return Err(CrsnError::Asm(AsmError::LabelNotDefined(target))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Op::BuiltIn(BuiltinOp::JumpIf(cond, target)) => { |
|
|
|
|
OpKind::BuiltIn(BuiltinOp::Jump(target)) => { |
|
|
|
|
if let Some(dest) = label_positions.get(&target) { |
|
|
|
|
let skip = *dest as isize - n as isize + skipped; |
|
|
|
|
cleaned.push(Op::BuiltIn(BuiltinOp::SkipIf(cond, Rd::new(RdData::Immediate(skip as Value))))); |
|
|
|
|
cleaned.push(Op { |
|
|
|
|
cond: op.cond, |
|
|
|
|
kind: OpKind::BuiltIn(BuiltinOp::Skip(Rd::new(RdData::Immediate(skip as Value)))), |
|
|
|
|
}); |
|
|
|
|
} else { |
|
|
|
|
return Err(CrsnError::Asm(AsmError::LabelNotDefined(target))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
other => { |
|
|
|
|
cleaned.push(other); |
|
|
|
|
_ => { |
|
|
|
|
cleaned.push(op); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|