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