|
|
|
@ -25,21 +25,21 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok |
|
|
|
|
BuiltinOp::Halt |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"uslp" => { |
|
|
|
|
"uslp" | "usleep" => { |
|
|
|
|
BuiltinOp::Sleep { |
|
|
|
|
count: args.next_rd()?, |
|
|
|
|
unit_us: SleepUnit::Usec, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"mslp" => { |
|
|
|
|
"mslp" | "msleep" => { |
|
|
|
|
BuiltinOp::Sleep { |
|
|
|
|
count: args.next_rd()?, |
|
|
|
|
unit_us: SleepUnit::Msec, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"sslp" => { |
|
|
|
|
"sslp" | "ssleep" => { |
|
|
|
|
BuiltinOp::Sleep { |
|
|
|
|
count: args.next_rd()?, |
|
|
|
|
unit_us: SleepUnit::Sec, |
|
|
|
@ -132,7 +132,7 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok |
|
|
|
|
for t in args { |
|
|
|
|
call_args.push(parse_rd(t, pcx)?); |
|
|
|
|
} |
|
|
|
|
BuiltinOp::Call(dest, call_args) |
|
|
|
|
BuiltinOp::Call { proc: dest, args: call_args } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"ret" => { |
|
|
|
@ -183,6 +183,21 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"crit-begin" => { |
|
|
|
|
BuiltinOp::CriticalBegin |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"crit-end" => { |
|
|
|
|
BuiltinOp::CriticalEnd |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"rt-opt" => { |
|
|
|
|
BuiltinOp::RuntimeOpt { |
|
|
|
|
opt: args.next_rd()?, |
|
|
|
|
value: args.next_rd()?, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"ld" => { |
|
|
|
|
BuiltinOp::Load { |
|
|
|
|
dst: args.next_wr()?, |
|
|
|
@ -258,6 +273,38 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"spawn" => { |
|
|
|
|
let handle = args.next_wr()?; |
|
|
|
|
let dest = RoutineName { name: args.next_string()?.0, arity: args.len() as u8 }; |
|
|
|
|
|
|
|
|
|
let mut call_args = vec![]; |
|
|
|
|
for t in args { |
|
|
|
|
call_args.push(parse_rd(t, pcx)?); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
BuiltinOp::Spawn { |
|
|
|
|
handle, |
|
|
|
|
proc: dest, |
|
|
|
|
args: call_args |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"join" => { |
|
|
|
|
BuiltinOp::Join(args.next_rdobj()?) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"yield" => { |
|
|
|
|
if args.have_more() { |
|
|
|
|
BuiltinOp::Yield { |
|
|
|
|
value: Some(args.next_rd()?), |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
BuiltinOp::Yield { |
|
|
|
|
value: None, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"del" => { |
|
|
|
|
BuiltinOp::Delete(args.next_rdobj()?) |
|
|
|
|
} |
|
|
|
@ -289,6 +336,10 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok |
|
|
|
|
return Ok(ParseRes::builtin(BuiltinOp::Exchange { a, b, mask })); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if let Some((dst, expected, src, mask)) = args.parse_masked_rdwr_rd_rd(other, "cas")? { |
|
|
|
|
return Ok(ParseRes::builtin(BuiltinOp::CompareSwap { dst, expected, src, mask })); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if other.starts_with(':') { |
|
|
|
|
BuiltinOp::Label(parse_label_str(other, &op_pos)?) |
|
|
|
|
} else { |
|
|
|
@ -313,6 +364,9 @@ pub(crate) fn parse_routine_name(name: String, pos: &SourcePosition) -> Result<R |
|
|
|
|
|
|
|
|
|
pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp { |
|
|
|
|
match op { |
|
|
|
|
BuiltinOp::CriticalBegin => sexp::list(&[A("crit-begin")]), |
|
|
|
|
BuiltinOp::CriticalEnd => sexp::list(&[A("crit-end")]), |
|
|
|
|
BuiltinOp::RuntimeOpt { opt, value } => sexp::list(&[A("rt-opt"), A(opt), A(value)]), |
|
|
|
|
BuiltinOp::Nop => sexp::list(&[A("nop")]), |
|
|
|
|
BuiltinOp::Halt => sexp::list(&[A("halt")]), |
|
|
|
|
BuiltinOp::Sleep { count: micros, unit_us: SleepUnit::Sec } => sexp::list(&[A("sslp"), A(micros)]), |
|
|
|
@ -322,7 +376,7 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp { |
|
|
|
|
BuiltinOp::Jump(label) => sexp::list(&[A("j"), A(label)]), |
|
|
|
|
BuiltinOp::FarLabel(label) => sexp::list(&[A("far"), A(label)]), |
|
|
|
|
BuiltinOp::FarJump(label) => sexp::list(&[A("fj"), A(label)]), |
|
|
|
|
BuiltinOp::Call(name, args) => { |
|
|
|
|
BuiltinOp::Call { proc: name, args } => { |
|
|
|
|
if args.is_empty() { |
|
|
|
|
sexp::list(&[A("call"), A(&name.name)]) |
|
|
|
|
} else { |
|
|
|
@ -389,6 +443,13 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp { |
|
|
|
|
sexp::list(&[A(format!("xch{}", mask.width)), AM(a, mask.dst_pos), AM(b, mask.src_pos)]) |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
BuiltinOp::CompareSwap { dst, expected, src, mask } => { |
|
|
|
|
if mask.is_full() { |
|
|
|
|
sexp::list(&[A("cas"), A(dst), A(expected), A(src)]) |
|
|
|
|
} else { |
|
|
|
|
sexp::list(&[A(format!("cas{}", mask.width)), AM(dst, mask.dst_pos), AM(expected, mask.src_pos), AM(src, mask.src2_pos)]) |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
BuiltinOp::StoreFlags { dst } => sexp::list(&[A("stf"), A(dst)]), |
|
|
|
|
BuiltinOp::LoadFlags { src } => sexp::list(&[A("ldf"), A(src)]), |
|
|
|
|
BuiltinOp::LoadSequence { dst, value } => { |
|
|
|
@ -405,6 +466,22 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
BuiltinOp::Spawn { handle, proc, args } => { |
|
|
|
|
if args.is_empty() { |
|
|
|
|
sexp::list(&[A("spawn"), A(handle), A(proc)]) |
|
|
|
|
} else { |
|
|
|
|
let mut v = vec![A("spawn"), A(handle), A(&proc.name)]; |
|
|
|
|
v.extend(args.iter().map(|r| A(r))); |
|
|
|
|
sexp::list(&v) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
BuiltinOp::Join(handle) => sexp::list(&[A("join"), A(handle)]), |
|
|
|
|
BuiltinOp::Yield { value } => { |
|
|
|
|
match value { |
|
|
|
|
None => sexp::list(&[A("yield")]), |
|
|
|
|
Some(rd) => sexp::list(&[A("yield"), A(rd)]), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -414,10 +491,15 @@ mod test { |
|
|
|
|
|
|
|
|
|
use sexp::SourcePosition; |
|
|
|
|
|
|
|
|
|
use crate::asm::parse::{parse_instructions, ParserContext}; |
|
|
|
|
use crate::asm::parse::{parse_instructions, ParserContext, ParserState}; |
|
|
|
|
use crate::asm::parse::sexp_expect::expect_list; |
|
|
|
|
use crate::builtin::BuiltinOps; |
|
|
|
|
use crate::module::OpTrait; |
|
|
|
|
use std::cell::RefCell; |
|
|
|
|
use std::sync::Arc; |
|
|
|
|
use crate::runtime::run_thread::{ThreadInfo, ThreadToken, RunState}; |
|
|
|
|
use crate::runtime::program::Program; |
|
|
|
|
use crate::runtime::frame::REG_COUNT; |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn roundtrip() { |
|
|
|
@ -474,6 +556,8 @@ mod test { |
|
|
|
|
("(xch r0 r1)", "(xch r0 r1)"), |
|
|
|
|
("(xch32 r0 r1)", "(xch32 r0 r1)"), |
|
|
|
|
("(xch32 r0:8 r1:16)", "(xch32 r0:8 r1:16)"), |
|
|
|
|
("(cas r0 r1 r2)", "(cas r0 r1 r2)"), |
|
|
|
|
("(cas8 r0:8 r1:16 r2:24)", "(cas8 r0:8 r1:16 r2:24)"), |
|
|
|
|
("(ld r0 r0)", "(ld r0 r0)"), |
|
|
|
|
("(ld8 r0 r1)", "(ld8 r0 r1)"), |
|
|
|
|
("(ld16 r0 r1)", "(ld16 r0 r1)"), |
|
|
|
@ -492,17 +576,53 @@ mod test { |
|
|
|
|
("(far :label)", "(far :label)"), |
|
|
|
|
("(del @r5)", "(del @r5)"), |
|
|
|
|
("(sym cat r0)(del @cat)", "(del @r0)"), |
|
|
|
|
("(spawn r0 foo 1 2 3)", "(spawn r0 foo 1 2 3)"), |
|
|
|
|
("(yield)", "(yield)"), |
|
|
|
|
("(yield -1)", "(yield -1)"), |
|
|
|
|
("(yield r5)", "(yield r5)"), |
|
|
|
|
("(join @r5)", "(join @r5)"), |
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
let parser = BuiltinOps::new(); |
|
|
|
|
|
|
|
|
|
let parsers = &[parser]; |
|
|
|
|
let parsers = Arc::new(vec![parser]); |
|
|
|
|
|
|
|
|
|
let ti = Arc::new(ThreadInfo { |
|
|
|
|
id: ThreadToken(0), |
|
|
|
|
uniq: Default::default(), |
|
|
|
|
program: Program::new(vec![], parsers.clone()).unwrap(), |
|
|
|
|
cycle_time: Default::default(), |
|
|
|
|
scheduler_interval: Default::default(), |
|
|
|
|
extensions: parsers.clone(), |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
let pcx = ParserContext { |
|
|
|
|
parsers: &parsers, |
|
|
|
|
state: RefCell::new(ParserState { |
|
|
|
|
reg_aliases: Default::default(), |
|
|
|
|
reg_alias_stack: vec![], |
|
|
|
|
global_reg_aliases: Default::default(), |
|
|
|
|
constants: Default::default(), |
|
|
|
|
|
|
|
|
|
// This is a fake thread to pass to constant expressions when evaluating them.
|
|
|
|
|
// This allows to evaluate nearly all instructions at compile time.
|
|
|
|
|
const_eval: RunState { |
|
|
|
|
thread_info: ti.clone(), |
|
|
|
|
cr: Default::default(), |
|
|
|
|
parked: Default::default(), |
|
|
|
|
global_regs: [0; REG_COUNT], |
|
|
|
|
ext_data: Default::default(), |
|
|
|
|
cr_deadline: None, |
|
|
|
|
critical_section: 0, |
|
|
|
|
}, |
|
|
|
|
const_eval_ti: ti.clone(), |
|
|
|
|
parsing_expr: false, |
|
|
|
|
label_num: Default::default() |
|
|
|
|
}), |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
for (sample, expected) in samples { |
|
|
|
|
let pcx = ParserContext { |
|
|
|
|
parsers, |
|
|
|
|
state: Default::default(), |
|
|
|
|
}; |
|
|
|
|
pcx.state.borrow_mut().reset(); |
|
|
|
|
|
|
|
|
|
println!("Parse: {}", sample); |
|
|
|
|
|
|
|
|
@ -521,10 +641,7 @@ mod test { |
|
|
|
|
assert_eq!(expected, exported); |
|
|
|
|
|
|
|
|
|
println!(" - 2nd cycle"); |
|
|
|
|
let pcx = ParserContext { |
|
|
|
|
parsers, |
|
|
|
|
state: Default::default(), |
|
|
|
|
}; |
|
|
|
|
pcx.state.borrow_mut().reset(); |
|
|
|
|
|
|
|
|
|
/* second cycle, nothing should change */ |
|
|
|
|
let s = sexp::parse(&format!("({})", exported)) |
|
|
|
|