Croissant Runtime
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
crsn/csn_asm/src/instr/flatten.rs

104 lines
3.4 KiB

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)
}