|
|
|
mod data;
|
|
|
|
mod error;
|
|
|
|
mod instr;
|
|
|
|
mod parse;
|
|
|
|
mod patches;
|
|
|
|
|
|
|
|
pub use parse::parse;
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::parse;
|
|
|
|
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;
|
|
|
|
use crate::instr::Cond;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_empty() {
|
|
|
|
let parsed = parse("
|
|
|
|
()
|
|
|
|
").unwrap();
|
|
|
|
assert_eq!(Vec::<Op>::new(), parsed);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_empty_routine() {
|
|
|
|
let parsed = parse("
|
|
|
|
(
|
|
|
|
(hello)
|
|
|
|
)
|
|
|
|
").unwrap();
|
|
|
|
assert_eq!(vec![
|
|
|
|
Op::Routine("hello".into()),
|
|
|
|
Op::Barrier(Some("Routine \"hello\" overrun".into()))
|
|
|
|
], parsed);
|
|
|
|
|
|
|
|
let parsed = parse("
|
|
|
|
(
|
|
|
|
(hello)
|
|
|
|
(world)
|
|
|
|
)
|
|
|
|
").unwrap();
|
|
|
|
assert_eq!(vec![
|
|
|
|
Op::Routine("hello".into()),
|
|
|
|
Op::Barrier(Some("Routine \"hello\" overrun".into())),
|
|
|
|
Op::Routine("world".into()),
|
|
|
|
Op::Barrier(Some("Routine \"world\" overrun".into()))
|
|
|
|
], parsed);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_data_formats() {
|
|
|
|
let parsed = parse("
|
|
|
|
(
|
|
|
|
(move
|
|
|
|
(mov r0 r1)
|
|
|
|
(mov r15 7)
|
|
|
|
(mov r15 0xabcd)
|
|
|
|
(mov r7 0b11110000)
|
|
|
|
(mov r7 arg1)
|
|
|
|
(mov r255 arg255)
|
|
|
|
(mov r7 res0)
|
|
|
|
(mov r7 res255)
|
|
|
|
(mov @r0 @r0) ; test in both Rd and Wr positions
|
|
|
|
(mov @r0 @arg0)
|
|
|
|
(mov @r0 @res0)
|
|
|
|
(mov @123456 @0x123456)
|
|
|
|
(mov @0b010101 @0b010101)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
").unwrap();
|
|
|
|
assert_eq!(vec![
|
|
|
|
Op::Routine("move".into()),
|
|
|
|
// (mov r0 r1)
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(1))),
|
|
|
|
),
|
|
|
|
// (mov r15 7)
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(15))),
|
|
|
|
Rd::new(SrcDisp::Immediate(Value(7))),
|
|
|
|
),
|
|
|
|
// (mov r15 0xabcd)
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(15))),
|
|
|
|
Rd::new(SrcDisp::Immediate(Value(0xabcd))),
|
|
|
|
),
|
|
|
|
// (mov r7 0b11110000)
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(7))),
|
|
|
|
Rd::new(SrcDisp::Immediate(Value(0b11110000))),
|
|
|
|
),
|
|
|
|
// (mov r7 arg1)
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(7))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Arg(1))),
|
|
|
|
),
|
|
|
|
// (mov r255 arg255)
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(255))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Arg(255))),
|
|
|
|
),
|
|
|
|
// (mov r7 res0)
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(7))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Res(0))),
|
|
|
|
),
|
|
|
|
// (mov r7 res255)
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(7))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Res(255))),
|
|
|
|
),
|
|
|
|
// (mov @r0 @r0)
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))),
|
|
|
|
Rd::new(SrcDisp::RegisterPtr(Register::Gen(0))),
|
|
|
|
),
|
|
|
|
// (mov @r0 @arg0)
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))),
|
|
|
|
Rd::new(SrcDisp::RegisterPtr(Register::Arg(0))),
|
|
|
|
),
|
|
|
|
// (mov @r0 @res0)
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))),
|
|
|
|
Rd::new(SrcDisp::RegisterPtr(Register::Res(0))),
|
|
|
|
),
|
|
|
|
// (mov @123456 @0x123456)
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::ImmediatePtr(Addr(123456))),
|
|
|
|
Rd::new(SrcDisp::ImmediatePtr(Addr(0x123456))),
|
|
|
|
),
|
|
|
|
// (mov @0b010101 @0b010101)
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::ImmediatePtr(Addr(0b010101))),
|
|
|
|
Rd::new(SrcDisp::ImmediatePtr(Addr(0b010101))),
|
|
|
|
),
|
|
|
|
Op::Barrier(Some("Routine \"move\" overrun".into())),
|
|
|
|
], parsed);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_single_instr(src : &str) -> anyhow::Result<Instr> {
|
|
|
|
Ok(parse::parse_instructions(vec![sexp::parse(src)?])?.remove(0))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_single_op(src : &str) -> anyhow::Result<Vec<Op>> {
|
|
|
|
let num = AtomicU32::new(0);
|
|
|
|
Ok(parse_single_instr(src)?.flatten(&num)?)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_single() {
|
|
|
|
let parsed = parse_single_op("(mov r0 r1)").unwrap();
|
|
|
|
assert_eq!(vec![
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(1))),
|
|
|
|
),
|
|
|
|
], parsed);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_branches_instr() {
|
|
|
|
let parsed = parse_single_instr("
|
|
|
|
(cmp r0 r1 (eq? (mov r0 r0) (mov r1 r2)) (>? (mov r0 r0) (mov r1 r1)))
|
|
|
|
").unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Instr {
|
|
|
|
op: Op::Cmp(
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(1))),
|
|
|
|
),
|
|
|
|
branches: Some(vec![
|
|
|
|
(
|
|
|
|
Cond::Equal,
|
|
|
|
vec![
|
|
|
|
Instr {
|
|
|
|
op: Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
|
|
|
),
|
|
|
|
branches: None
|
|
|
|
},
|
|
|
|
Instr {
|
|
|
|
op: Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(1))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(2))),
|
|
|
|
),
|
|
|
|
branches: None
|
|
|
|
}
|
|
|
|
]
|
|
|
|
),
|
|
|
|
(
|
|
|
|
Cond::Greater,
|
|
|
|
vec![
|
|
|
|
Instr {
|
|
|
|
op: Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
|
|
|
),
|
|
|
|
branches: None
|
|
|
|
},
|
|
|
|
Instr {
|
|
|
|
op: Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(1))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(1))),
|
|
|
|
),
|
|
|
|
branches: None
|
|
|
|
}
|
|
|
|
]
|
|
|
|
)
|
|
|
|
])
|
|
|
|
}
|
|
|
|
, parsed);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_branches_op() {
|
|
|
|
let parsed = parse_single_op("
|
|
|
|
(cmp r0 r1
|
|
|
|
(eq?
|
|
|
|
(mov r0 r0)
|
|
|
|
(mov r1 r2))
|
|
|
|
(>?
|
|
|
|
(mov r0 r0)
|
|
|
|
(mov r1 r1)))
|
|
|
|
").unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
vec![
|
|
|
|
Op::Cmp(
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(1))),
|
|
|
|
),
|
|
|
|
Op::JumpIf(Cond::NotEqual, Label::Numbered(1)),
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
|
|
|
),
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(1))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(2))),
|
|
|
|
),
|
|
|
|
Op::Jump(Label::Numbered(0)),
|
|
|
|
Op::Label(Label::Numbered(1)),
|
|
|
|
Op::JumpIf(Cond::LessOrEqual, Label::Numbered(0)),
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
|
|
|
),
|
|
|
|
Op::Mov(
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(1))),
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(1))),
|
|
|
|
),
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|