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::::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 { Ok(parse::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![ 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); } }