|
|
|
@ -1,15 +1,21 @@ |
|
|
|
|
mod data; |
|
|
|
|
mod error; |
|
|
|
|
mod instr; |
|
|
|
|
mod parse; |
|
|
|
|
mod patches; |
|
|
|
|
pub mod data; |
|
|
|
|
pub mod error; |
|
|
|
|
pub mod instr; |
|
|
|
|
pub mod parse; |
|
|
|
|
pub mod patches; |
|
|
|
|
|
|
|
|
|
pub use parse::parse; |
|
|
|
|
use crate::instr::{Op, lower}; |
|
|
|
|
|
|
|
|
|
/// Parse a program from string and assemble a low level instruction sequence from it.
|
|
|
|
|
pub fn assemble(source : &str) -> Result<Vec<Op>, error::Error> { |
|
|
|
|
let parsed = parse::parse(source)?; |
|
|
|
|
Ok(lower(parsed)?) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
|
mod tests { |
|
|
|
|
use crate::parse; |
|
|
|
|
use crate::instr::{Op, Flatten, Instr, jumps_to_skips}; |
|
|
|
|
use crate::parse::{parse, parse_instructions}; |
|
|
|
|
use crate::instr::{HLOp, Op, Flatten, Instr, lower}; |
|
|
|
|
use crate::data::{Wr, DstDisp, Register, SrcDisp, Rd}; |
|
|
|
|
use crate::data::literal::{Value, Addr, Label}; |
|
|
|
|
use std::sync::atomic::AtomicU32; |
|
|
|
@ -20,7 +26,7 @@ mod tests { |
|
|
|
|
let parsed = parse(" |
|
|
|
|
() |
|
|
|
|
").unwrap(); |
|
|
|
|
assert_eq!(Vec::<Op>::new(), parsed); |
|
|
|
|
assert_eq!(Vec::<HLOp>::new(), parsed); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
@ -31,8 +37,8 @@ mod tests { |
|
|
|
|
) |
|
|
|
|
").unwrap(); |
|
|
|
|
assert_eq!(vec![ |
|
|
|
|
Op::Routine("hello".into()), |
|
|
|
|
Op::Barrier(Some("Routine \"hello\" overrun".into())) |
|
|
|
|
HLOp::L(Op::Routine("hello".into())), |
|
|
|
|
HLOp::L(Op::Barrier(Some("Routine \"hello\" overrun".into()))) |
|
|
|
|
], parsed); |
|
|
|
|
|
|
|
|
|
let parsed = parse(" |
|
|
|
@ -42,10 +48,10 @@ mod tests { |
|
|
|
|
) |
|
|
|
|
").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())) |
|
|
|
|
HLOp::L(Op::Routine("hello".into())), |
|
|
|
|
HLOp::L(Op::Barrier(Some("Routine \"hello\" overrun".into()))), |
|
|
|
|
HLOp::L(Op::Routine("world".into())), |
|
|
|
|
HLOp::L(Op::Barrier(Some("Routine \"world\" overrun".into()))) |
|
|
|
|
], parsed); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -71,81 +77,81 @@ mod tests { |
|
|
|
|
) |
|
|
|
|
").unwrap(); |
|
|
|
|
assert_eq!(vec![ |
|
|
|
|
Op::Routine("move".into()), |
|
|
|
|
HLOp::L(Op::Routine("move".into())), |
|
|
|
|
// (mov r0 r1)
|
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(1))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
// (mov r15 7)
|
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(15))), |
|
|
|
|
Rd::new(SrcDisp::Immediate(Value(7))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
// (mov r15 0xabcd)
|
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(15))), |
|
|
|
|
Rd::new(SrcDisp::Immediate(Value(0xabcd))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
// (mov r7 0b11110000)
|
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(7))), |
|
|
|
|
Rd::new(SrcDisp::Immediate(Value(0b11110000))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
// (mov r7 arg1)
|
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(7))), |
|
|
|
|
Rd::new(SrcDisp::Register(Register::Arg(1))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
// (mov r255 arg255)
|
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(255))), |
|
|
|
|
Rd::new(SrcDisp::Register(Register::Arg(255))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
// (mov r7 res0)
|
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(7))), |
|
|
|
|
Rd::new(SrcDisp::Register(Register::Res(0))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
// (mov r7 res255)
|
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(7))), |
|
|
|
|
Rd::new(SrcDisp::Register(Register::Res(255))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
// (mov @r0 @r0)
|
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))), |
|
|
|
|
Rd::new(SrcDisp::RegisterPtr(Register::Gen(0))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
// (mov @r0 @arg0)
|
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))), |
|
|
|
|
Rd::new(SrcDisp::RegisterPtr(Register::Arg(0))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
// (mov @r0 @res0)
|
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))), |
|
|
|
|
Rd::new(SrcDisp::RegisterPtr(Register::Res(0))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
// (mov @123456 @0x123456)
|
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::ImmediatePtr(Addr(123456))), |
|
|
|
|
Rd::new(SrcDisp::ImmediatePtr(Addr(0x123456))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
// (mov @0b010101 @0b010101)
|
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::ImmediatePtr(Addr(0b010101))), |
|
|
|
|
Rd::new(SrcDisp::ImmediatePtr(Addr(0b010101))), |
|
|
|
|
), |
|
|
|
|
Op::Barrier(Some("Routine \"move\" overrun".into())), |
|
|
|
|
)), |
|
|
|
|
HLOp::L(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)) |
|
|
|
|
Ok(parse_instructions(vec![sexp::parse(src)?])?.remove(0)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn parse_single_op(src : &str) -> anyhow::Result<Vec<Op>> { |
|
|
|
|
fn parse_single_op(src : &str) -> anyhow::Result<Vec<HLOp>> { |
|
|
|
|
let num = AtomicU32::new(0); |
|
|
|
|
Ok(parse_single_instr(src)?.flatten(&num)?) |
|
|
|
|
} |
|
|
|
@ -154,10 +160,10 @@ mod tests { |
|
|
|
|
fn test_parse_single() { |
|
|
|
|
let parsed = parse_single_op("(mov r0 r1)").unwrap(); |
|
|
|
|
assert_eq!(vec![ |
|
|
|
|
Op::Mov( |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(1))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
], parsed); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -168,26 +174,26 @@ mod tests { |
|
|
|
|
").unwrap(); |
|
|
|
|
assert_eq!( |
|
|
|
|
Instr { |
|
|
|
|
op: Op::Cmp( |
|
|
|
|
op: HLOp::L(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( |
|
|
|
|
op: HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
branches: None |
|
|
|
|
}, |
|
|
|
|
Instr { |
|
|
|
|
op: Op::Mov( |
|
|
|
|
op: HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(1))), |
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(2))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
branches: None |
|
|
|
|
} |
|
|
|
|
] |
|
|
|
@ -196,17 +202,17 @@ mod tests { |
|
|
|
|
Cond::Greater, |
|
|
|
|
vec![ |
|
|
|
|
Instr { |
|
|
|
|
op: Op::Mov( |
|
|
|
|
op: HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
branches: None |
|
|
|
|
}, |
|
|
|
|
Instr { |
|
|
|
|
op: Op::Mov( |
|
|
|
|
op: HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(1))), |
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(1))), |
|
|
|
|
), |
|
|
|
|
)), |
|
|
|
|
branches: None |
|
|
|
|
} |
|
|
|
|
] |
|
|
|
@ -229,31 +235,31 @@ mod tests { |
|
|
|
|
").unwrap(); |
|
|
|
|
assert_eq!( |
|
|
|
|
vec![ |
|
|
|
|
Op::Cmp( |
|
|
|
|
HLOp::L(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( |
|
|
|
|
)), |
|
|
|
|
HLOp::JumpIf(Cond::NotEqual, Label::Numbered(1)), |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))), |
|
|
|
|
), |
|
|
|
|
Op::Mov( |
|
|
|
|
)), |
|
|
|
|
HLOp::L(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( |
|
|
|
|
)), |
|
|
|
|
HLOp::Jump(Label::Numbered(0)), |
|
|
|
|
HLOp::Label(Label::Numbered(1)), |
|
|
|
|
HLOp::JumpIf(Cond::LessOrEqual, Label::Numbered(0)), |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(0))), |
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(0))), |
|
|
|
|
), |
|
|
|
|
Op::Mov( |
|
|
|
|
)), |
|
|
|
|
HLOp::L(Op::Mov( |
|
|
|
|
Wr::new(DstDisp::Register(Register::Gen(1))), |
|
|
|
|
Rd::new(SrcDisp::Register(Register::Gen(1))), |
|
|
|
|
), |
|
|
|
|
Op::Label(Label::Numbered(0)), |
|
|
|
|
)), |
|
|
|
|
HLOp::Label(Label::Numbered(0)), |
|
|
|
|
], parsed); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -277,32 +283,32 @@ mod tests { |
|
|
|
|
|
|
|
|
|
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::Routine("foo".into()).into(), |
|
|
|
|
HLOp::Label(Label::Named("foo".to_string())), |
|
|
|
|
HLOp::Label(Label::Named("unused".to_string())), |
|
|
|
|
HLOp::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())), |
|
|
|
|
).into(), |
|
|
|
|
HLOp::Jump(Label::Named("foo".to_string())), |
|
|
|
|
HLOp::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))), |
|
|
|
|
), |
|
|
|
|
Op::Jump(Label::Named("whatever".to_string())), |
|
|
|
|
Op::JumpIf(Cond::Equal, Label::Named("whatever".to_string())), |
|
|
|
|
Op::Barrier(Some("Routine \"foo\" overrun".into())), |
|
|
|
|
).into(), |
|
|
|
|
HLOp::Jump(Label::Named("whatever".to_string())), |
|
|
|
|
HLOp::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 = jumps_to_skips(parsed).unwrap(); |
|
|
|
|
let cleaned = lower(parsed).unwrap(); |
|
|
|
|
|
|
|
|
|
assert_eq!( |
|
|
|
|
vec![ |
|
|
|
|