forked from MightyPork/crsn
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.
104 lines
3.4 KiB
104 lines
3.4 KiB
4 years ago
|
use std::sync::atomic::AtomicU32;
|
||
4 years ago
|
use crate::instr::{HLOp, Instr, Cond, Routine, Op};
|
||
4 years ago
|
use crate::error::{Error, AsmError};
|
||
|
use std::collections::HashMap;
|
||
|
use crate::data::literal::{Label, Value};
|
||
4 years ago
|
use crate::data::{Rd, SrcDisp};
|
||
4 years ago
|
|
||
|
/// A trait for something that can turn into multiple instructions
|
||
|
pub trait Flatten {
|
||
4 years ago
|
fn flatten(self, label_num: &AtomicU32) -> Result<Vec<HLOp>, Error>;
|
||
4 years ago
|
}
|
||
|
|
||
|
impl Flatten for Instr {
|
||
4 years ago
|
fn flatten(self, label_num: &AtomicU32) -> Result<Vec<HLOp>, Error> {
|
||
4 years ago
|
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)
|
||
|
};
|
||
4 years ago
|
ops.push(HLOp::JumpIf(!cond, next_lbl.clone()));
|
||
4 years ago
|
|
||
|
for branch_instr in branch {
|
||
|
ops.extend(branch_instr.flatten(label_num)?);
|
||
|
}
|
||
|
|
||
|
if cnt != branch_count - 1 {
|
||
4 years ago
|
ops.push(HLOp::Jump(end_lbl.clone()));
|
||
|
ops.push(HLOp::Label(next_lbl));
|
||
4 years ago
|
}
|
||
|
}
|
||
4 years ago
|
ops.push(HLOp::Label(end_lbl));
|
||
4 years ago
|
}
|
||
|
|
||
|
Ok(ops)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Flatten for Routine {
|
||
4 years ago
|
fn flatten(self, label_num: &AtomicU32) -> Result<Vec<HLOp>, Error> {
|
||
4 years ago
|
let mut ops = vec![
|
||
4 years ago
|
Op::Routine(self.name.clone()).into(),
|
||
4 years ago
|
];
|
||
|
|
||
|
for instr in self.body {
|
||
|
ops.extend(instr.flatten(label_num)?);
|
||
|
}
|
||
|
|
||
4 years ago
|
ops.push(Op::Barrier(Some(format!("Routine \"{}\" overrun", self.name).into())).into());
|
||
4 years ago
|
Ok(ops)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Convert jumps to relative skips
|
||
4 years ago
|
pub fn lower(ops: Vec<HLOp>) -> Result<Vec<Op>, Error> {
|
||
4 years ago
|
let mut label_positions = HashMap::<Label, usize>::new();
|
||
|
for (n, op) in ops.iter().enumerate() {
|
||
4 years ago
|
if let HLOp::Label(name) = op {
|
||
4 years ago
|
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 {
|
||
4 years ago
|
HLOp::Label(_) => {
|
||
4 years ago
|
skipped += 1;
|
||
|
}
|
||
4 years ago
|
HLOp::Jump(target) => {
|
||
4 years ago
|
if let Some(dest) = label_positions.get(&target) {
|
||
|
let skip = *dest as isize - n as isize + skipped;
|
||
4 years ago
|
cleaned.push(Op::Skip(Rd::new(SrcDisp::Immediate(skip as Value))));
|
||
4 years ago
|
} else {
|
||
|
return Err(Error::Asm(AsmError::LabelNotDefined(target)));
|
||
|
}
|
||
|
}
|
||
4 years ago
|
HLOp::JumpIf(cond, target) => {
|
||
4 years ago
|
if let Some(dest) = label_positions.get(&target) {
|
||
|
let skip = *dest as isize - n as isize + skipped;
|
||
4 years ago
|
cleaned.push(Op::SkipIf(cond, Rd::new(SrcDisp::Immediate(skip as Value))));
|
||
4 years ago
|
} else {
|
||
|
return Err(Error::Asm(AsmError::LabelNotDefined(target)));
|
||
|
}
|
||
|
}
|
||
4 years ago
|
HLOp::L(op) => {
|
||
|
cleaned.push(op);
|
||
4 years ago
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ok(cleaned)
|
||
|
}
|