|
|
@ -12,326 +12,3 @@ pub fn assemble(source: &str, parsers: &[Box<dyn AsmModule>]) -> Result<Vec<Op>, |
|
|
|
let parsed = parse::parse(source, parsers)?; |
|
|
|
let parsed = parse::parse(source, parsers)?; |
|
|
|
Ok(lower(parsed)?) |
|
|
|
Ok(lower(parsed)?) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
|
|
|
|
mod tests { |
|
|
|
|
|
|
|
use std::sync::atomic::AtomicU32; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use crate::asm::data::{DstDisp, Rd, Register, SrcDisp, Wr}; |
|
|
|
|
|
|
|
use crate::asm::data::literal::{Addr, Label}; |
|
|
|
|
|
|
|
use crate::asm::instr::{Flatten, OpWrapper, Instr, lower, Op}; |
|
|
|
|
|
|
|
use crate::asm::instr::Cond; |
|
|
|
|
|
|
|
use crate::asm::parse::{parse, parse_instructions}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn test_parse_empty() { |
|
|
|
|
|
|
|
let parsed = parse(" |
|
|
|
|
|
|
|
() |
|
|
|
|
|
|
|
").unwrap(); |
|
|
|
|
|
|
|
assert_eq!(Vec::<OpWrapper>::new(), parsed); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn test_parse_empty_routine() { |
|
|
|
|
|
|
|
let parsed = parse(" |
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
(hello) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
").unwrap(); |
|
|
|
|
|
|
|
assert_eq!(vec![ |
|
|
|
|
|
|
|
OpWrapper::Op(Op::Routine("hello".into())), |
|
|
|
|
|
|
|
OpWrapper::Op(Op::Barrier(Some("Routine \"hello\" overrun".into()))) |
|
|
|
|
|
|
|
], parsed); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let parsed = parse(" |
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
(hello) |
|
|
|
|
|
|
|
(world) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
").unwrap(); |
|
|
|
|
|
|
|
assert_eq!(vec![ |
|
|
|
|
|
|
|
OpWrapper::Op(Op::Routine("hello".into())), |
|
|
|
|
|
|
|
OpWrapper::Op(Op::Barrier(Some("Routine \"hello\" overrun".into()))), |
|
|
|
|
|
|
|
OpWrapper::Op(Op::Routine("world".into())), |
|
|
|
|
|
|
|
OpWrapper::Op(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![ |
|
|
|
|
|
|
|
OpWrapper::Op(Op::Routine("move".into())), |
|
|
|
|
|
|
|
// (mov r0 r1)
|
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(1))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
// (mov r15 7)
|
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(15))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Immediate(7)), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
// (mov r15 0xabcd)
|
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(15))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Immediate(0xabcd)), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
// (mov r7 0b11110000)
|
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(7))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Immediate(0b11110000)), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
// (mov r7 arg1)
|
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(7))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Arg(1))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
// (mov r255 arg255)
|
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(255))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Arg(255))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
// (mov r7 res0)
|
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(7))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Res(0))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
// (mov r7 res255)
|
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(7))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Res(255))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
// (mov @r0 @r0)
|
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::RegisterPtr(Register::Gen(0))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
// (mov @r0 @arg0)
|
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::RegisterPtr(Register::Arg(0))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
// (mov @r0 @res0)
|
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::RegisterPtr(Register::Res(0))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
// (mov @123456 @0x123456)
|
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::ImmediatePtr(Addr(123456))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::ImmediatePtr(Addr(0x123456))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
// (mov @0b010101 @0b010101)
|
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::ImmediatePtr(Addr(0b010101))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::ImmediatePtr(Addr(0b010101))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
OpWrapper::Op(Op::Barrier(Some("Routine \"move\" overrun".into()))), |
|
|
|
|
|
|
|
], parsed); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn parse_single_instr(src: &str) -> anyhow::Result<Instr> { |
|
|
|
|
|
|
|
Ok(parse_instructions(vec![sexp::parse(src)?])?.remove(0)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn parse_single_op(src: &str) -> anyhow::Result<Vec<OpWrapper>> { |
|
|
|
|
|
|
|
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![ |
|
|
|
|
|
|
|
OpWrapper::Op(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: OpWrapper::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: OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
branches: None, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
Instr { |
|
|
|
|
|
|
|
op: OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(1))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(2))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
branches: None, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
] |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
Cond::Greater, |
|
|
|
|
|
|
|
vec![ |
|
|
|
|
|
|
|
Instr { |
|
|
|
|
|
|
|
op: OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
branches: None, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
Instr { |
|
|
|
|
|
|
|
op: OpWrapper::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![ |
|
|
|
|
|
|
|
OpWrapper::Op(Op::Cmp( |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(1))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
OpWrapper::JumpIf(Cond::NotEqual, Label::Numbered(1)), |
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(1))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(2))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
OpWrapper::Jump(Label::Numbered(0)), |
|
|
|
|
|
|
|
OpWrapper::Label(Label::Numbered(1)), |
|
|
|
|
|
|
|
OpWrapper::JumpIf(Cond::LowerOrEqual, Label::Numbered(0)), |
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
OpWrapper::Op(Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(1))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(1))), |
|
|
|
|
|
|
|
)), |
|
|
|
|
|
|
|
OpWrapper::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()).into(), |
|
|
|
|
|
|
|
OpWrapper::Label(Label::Named("foo".to_string())), |
|
|
|
|
|
|
|
OpWrapper::Label(Label::Named("unused".to_string())), |
|
|
|
|
|
|
|
OpWrapper::Label(Label::Named("whatever".to_string())), |
|
|
|
|
|
|
|
Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
).into(), |
|
|
|
|
|
|
|
OpWrapper::Jump(Label::Named("foo".to_string())), |
|
|
|
|
|
|
|
OpWrapper::Jump(Label::Named("foo".to_string())), |
|
|
|
|
|
|
|
Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
).into(), |
|
|
|
|
|
|
|
Op::Mov( |
|
|
|
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))), |
|
|
|
|
|
|
|
).into(), |
|
|
|
|
|
|
|
OpWrapper::Jump(Label::Named("whatever".to_string())), |
|
|
|
|
|
|
|
OpWrapper::JumpIf(Cond::Equal, Label::Named("whatever".to_string())), |
|
|
|
|
|
|
|
Op::Barrier(Some("Routine \"foo\" overrun".into())).into(), |
|
|
|
|
|
|
|
], parsed); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Labels are removed and jumps become skips
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let cleaned = lower(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(-1))), |
|
|
|
|
|
|
|
Op::Skip(Rd::new(SrcDisp::Immediate(-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(-5))), |
|
|
|
|
|
|
|
Op::SkipIf(Cond::Equal, Rd::new(SrcDisp::Immediate(-6))), |
|
|
|
|
|
|
|
Op::Barrier(Some("Routine \"foo\" overrun".into())), |
|
|
|
|
|
|
|
], cleaned); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|