Croissant Runtime
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
crsn/crsn/src/asm/mod.rs

337 lines
12 KiB

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<dyn AsmModule>]) -> Result<Vec<Op>, 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::<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);
}
}