use crate::asm::instr::{lower, Op}; use crate::asm::instr::op::AsmModule; pub mod data; pub mod error; pub mod instr; pub mod parse; pub mod patches; /// Parse a program from string and assemble a low level instruction sequence from it. pub fn assemble(source: &str, parsers: &[Box]) -> Result, error::Error> { let parsed = parse::parse(source, parsers)?; 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::::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 { 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![ 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); } }