add jumps_to_skips transform

pull/21/head
Ondřej Hruška 4 years ago
parent a52f1e5e72
commit ab843c0c87
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 2
      csn_asm/src/data/literal.rs
  2. 3
      csn_asm/src/error.rs
  3. 104
      csn_asm/src/instr/flatten.rs
  4. 67
      csn_asm/src/instr/mod.rs
  5. 2
      csn_asm/src/instr/op.rs
  6. 72
      csn_asm/src/lib.rs
  7. 11
      csn_asm/src/parse/parse_op.rs

@ -56,7 +56,7 @@ impl From<u64> for Addr {
}
/// Label name
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Label {
Named(String),
Numbered(u32),

@ -2,6 +2,7 @@ use crate::instr::{Cond};
use crate::data::{Mask, Register};
use thiserror::Error;
use std::borrow::Cow;
use crate::data::literal::Label;
/// csn_asm unified error type
@ -34,6 +35,8 @@ pub enum AsmError {
ValueAsOutput,
#[error("Conditional branch already defined for \"{0}\"")]
ConditionalAlreadyUsed(Cond),
#[error("Label \"{0:?}\" not defined")]
LabelNotDefined(Label),
}
/// Architectural error - the code is syntactically OK, but cannot run

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

@ -1,12 +1,16 @@
mod op;
mod cond;
mod flatten;
pub use flatten::Flatten;
pub use flatten::jumps_to_skips;
pub use op::Op;
pub use cond::Cond;
use crate::data::literal::{Label, RoutineName};
use std::sync::atomic::{AtomicU32};
use std::collections::HashMap;
use crate::error::{AsmError, Error};
use crate::data::literal::{RoutineName};
/// A higher-level instruction
#[derive(Debug, Clone, Eq, PartialEq)]
@ -21,58 +25,3 @@ pub struct Routine {
pub body: Vec<Instr>,
}
/// 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)
}
}

@ -37,6 +37,8 @@ pub enum Op {
Skip(Rd),
/// Jump to a label if a flag is set
JumpIf(Cond, Label),
/// Skip if a flag is set
SkipIf(Cond, Rd),
/// Deny jumps, skips and run across this address, producing a run-time fault with a message.
Barrier(Option<DebugMsg>),
/// Generate a run-time fault with a debugger message

@ -9,7 +9,7 @@ pub use parse::parse;
#[cfg(test)]
mod tests {
use crate::parse;
use crate::instr::{Op, Flatten, Instr};
use crate::instr::{Op, Flatten, Instr, jumps_to_skips};
use crate::data::{Wr, DstDisp, Register, SrcDisp, Rd};
use crate::data::literal::{Value, Addr, Label};
use std::sync::atomic::AtomicU32;
@ -256,4 +256,74 @@ mod tests {
Op::Label(Label::Numbered(0)),
], parsed);
}
#[test]
fn test_jumps_to_skips() {
let parsed = parse("(
(foo
(:foo)
(:unused)
(:whatever)
(mov r0 r0)
(j :foo)
(j :foo)
(mov r0 r0)
(mov r0 r0)
(j :whatever)
(j.if eq :whatever)
)
)").unwrap();
assert_eq!(
vec![
Op::Routine("foo".into()),
Op::Label(Label::Named("foo".to_string())),
Op::Label(Label::Named("unused".to_string())),
Op::Label(Label::Named("whatever".to_string())),
Op::Mov(
Wr::new(DstDisp::Register(Register::Gen(0))),
Rd::new(SrcDisp::Register(Register::Gen(0))),
),
Op::Jump(Label::Named("foo".to_string())),
Op::Jump(Label::Named("foo".to_string())),
Op::Mov(
Wr::new(DstDisp::Register(Register::Gen(0))),
Rd::new(SrcDisp::Register(Register::Gen(0))),
),
Op::Mov(
Wr::new(DstDisp::Register(Register::Gen(0))),
Rd::new(SrcDisp::Register(Register::Gen(0))),
),
Op::Jump(Label::Named("whatever".to_string())),
Op::JumpIf(Cond::Equal, Label::Named("whatever".to_string())),
Op::Barrier(Some("Routine \"foo\" overrun".into())),
], parsed);
// Labels are removed and jumps become skips
let cleaned = jumps_to_skips(parsed).unwrap();
assert_eq!(
vec![
Op::Routine("foo".into()),
Op::Mov(
Wr::new(DstDisp::Register(Register::Gen(0))),
Rd::new(SrcDisp::Register(Register::Gen(0))),
),
Op::Skip(Rd::new(SrcDisp::Immediate(Value(-1)))),
Op::Skip(Rd::new(SrcDisp::Immediate(Value(-2)))),
Op::Mov(
Wr::new(DstDisp::Register(Register::Gen(0))),
Rd::new(SrcDisp::Register(Register::Gen(0))),
),
Op::Mov(
Wr::new(DstDisp::Register(Register::Gen(0))),
Rd::new(SrcDisp::Register(Register::Gen(0))),
),
Op::Skip(Rd::new(SrcDisp::Immediate(Value(-5)))),
Op::SkipIf(Cond::Equal, Rd::new(SrcDisp::Immediate(Value(-6)))),
Op::Barrier(Some("Routine \"foo\" overrun".into())),
], cleaned);
}
}

@ -11,9 +11,9 @@ pub fn parse_op(keyword: &str, far : bool, mut arg_tokens: impl Iterator<Item=Se
"jmp" | "j" => {
let dest = parse_label(arg_tokens.next())?;
if far {
Op::Jump(dest)
} else {
Op::FarJump(dest)
} else {
Op::Jump(dest)
}
}
@ -45,8 +45,9 @@ pub fn parse_op(keyword: &str, far : bool, mut arg_tokens: impl Iterator<Item=Se
}
"jmp.if" | "j.if" => {
let cond = parse_cond(&expect_string_atom(arg_tokens.next())?)?;
let dest = parse_label(arg_tokens.next())?;
Op::JumpIf(parse_cond(&expect_string_atom(arg_tokens.next())?)?, dest)
Op::JumpIf(cond, dest)
}
"barrier" => {
@ -93,9 +94,9 @@ pub fn parse_op(keyword: &str, far : bool, mut arg_tokens: impl Iterator<Item=Se
if let Some(label) = other.strip_prefix(':') {
let label = Label::Named(label.to_string());
if far {
Op::Label(label)
} else {
Op::FarLabel(label)
} else {
Op::Label(label)
}
} else {
return Err(Error::Parse(format!("Unknown instruction: {}", other).into()));

Loading…
Cancel
Save