Example extension: Stacks; fixes to allow module data storage in thread context

pull/21/head
Ondřej Hruška 4 years ago
parent 3e0aaa71e9
commit 0cd800653f
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 8
      Cargo.lock
  2. 1
      Cargo.toml
  3. 24
      crsn/src/asm/instr/op.rs
  4. 2
      crsn/src/asm/mod.rs
  5. 8
      crsn/src/asm/parse/parse_op.rs
  6. 2
      crsn/src/builtin/defs.rs
  7. 21
      crsn/src/builtin/exec.rs
  8. 10
      crsn/src/builtin/parse.rs
  9. 16
      crsn/src/runtime/exec.rs
  10. 3
      crsn/src/runtime/fault.rs
  11. 72
      crsn/src/runtime/run_thread.rs
  12. 8
      crsn_arith/src/exec.rs
  13. 2
      crsn_arith/src/lib.rs
  14. 6
      crsn_arith/src/parse.rs
  15. 10
      crsn_stacks/Cargo.toml
  16. 7
      crsn_stacks/src/defs.rs
  17. 71
      crsn_stacks/src/exec.rs
  18. 6
      crsn_stacks/src/lib.rs
  19. 48
      crsn_stacks/src/parse.rs
  20. 1
      launcher/Cargo.toml
  21. 33
      launcher/src/main.rs

8
Cargo.lock generated

@ -71,6 +71,13 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "crsn_stacks"
version = "0.1.0"
dependencies = [
"crsn",
]
[[package]] [[package]]
name = "dyn-clonable" name = "dyn-clonable"
version = "0.9.0" version = "0.9.0"
@ -114,6 +121,7 @@ dependencies = [
"anyhow", "anyhow",
"crsn", "crsn",
"crsn_arith", "crsn_arith",
"crsn_stacks",
"log", "log",
"simple_logger", "simple_logger",
"thiserror", "thiserror",

@ -3,4 +3,5 @@ members = [
"launcher", "launcher",
"crsn", "crsn",
"crsn_arith", "crsn_arith",
"crsn_stacks",
] ]

@ -1,19 +1,15 @@
use std::fmt::Debug; use std::fmt::Debug;
use crate::asm::data::{
literal::DebugMsg, literal::Label,
literal::RoutineName,
Rd,
Wr,
};
use crate::asm::data::literal::Addr;
use crate::asm::error::Error; use crate::asm::error::Error;
use crate::asm::instr::Cond;
use crate::asm::parse::arg_parser::ArgParser; use crate::asm::parse::arg_parser::ArgParser;
use crate::builtin::defs::BuiltinOp; use crate::builtin::defs::BuiltinOp;
use crate::runtime::fault::Fault; use crate::runtime::fault::Fault;
use crate::runtime::frame::{CallStack, StackFrame};
use crate::runtime::program::Program;
use crate::runtime::run_thread::{RunState, ThreadInfo};
/// A higher level simple opration /// A higher level simple opration
#[derive(Debug)] #[derive(Debug)]
@ -24,7 +20,7 @@ pub enum Op {
} }
pub trait OpTrait: Debug + Send + Sync + 'static { pub trait OpTrait: Debug + Send + Sync + 'static {
fn execute(&self, program: &Program, call_stack: &mut CallStack, frame: &mut StackFrame) -> Result<EvalRes, Fault>; fn execute(&self, ti : &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault>;
} }
pub enum ParseOpResult { pub enum ParseOpResult {
@ -64,13 +60,13 @@ impl Default for EvalRes {
impl OpTrait for Op { impl OpTrait for Op {
fn execute(&self, program: &Program, call_stack: &mut CallStack, frame: &mut StackFrame) -> Result<EvalRes, Fault> { fn execute(&self, ti : &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault> {
match self { match self {
Op::BuiltIn(op) => { Op::BuiltIn(op) => {
op.execute(&program, call_stack, frame) op.execute(ti, state)
} }
Op::Ext(op) => { Op::Ext(op) => {
op.execute(&program, call_stack, frame) op.execute(ti, state)
} }
} }
} }

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::asm::instr::Op;
use crate::asm::instr::op::AsmModule; use crate::asm::instr::op::AsmModule;
use crate::runtime::program::Program; use crate::runtime::program::Program;

@ -1,11 +1,11 @@
use crate::asm::data::literal::{Label, RoutineName};
use crate::asm::error::Error; use crate::asm::error::Error;
use crate::asm::instr::Op; use crate::asm::instr::Op;
use crate::asm::instr::cond::parse_cond;
use crate::asm::instr::op::{AsmModule, ParseOpResult}; use crate::asm::instr::op::{AsmModule, ParseOpResult};
use crate::asm::parse::arg_parser::ArgParser; use crate::asm::parse::arg_parser::ArgParser;
use crate::asm::parse::parse_data::{parse_label, parse_rd};
use crate::asm::parse::sexp_expect::expect_string_atom;
use crate::builtin::parse::BuiltinOpParser; use crate::builtin::parse::BuiltinOpParser;
pub fn parse_op(keyword: &str, mut arg_tokens: ArgParser, parsers: &[Box<dyn AsmModule>]) -> Result<Op, Error> { pub fn parse_op(keyword: &str, mut arg_tokens: ArgParser, parsers: &[Box<dyn AsmModule>]) -> Result<Op, Error> {

@ -6,6 +6,8 @@ use crate::asm::data::{Rd, Wr};
pub enum BuiltinOp { pub enum BuiltinOp {
/// Do nothing /// Do nothing
Nop, Nop,
/// Stop execution
Halt,
/// Mark a jump target. /// Mark a jump target.
Label(Label), Label(Label),
/// Jump to a label /// Jump to a label

@ -1,19 +1,26 @@
use std::ops::Rem;
use num_traits::PrimInt;
use crate::asm::instr::op::{OpTrait, EvalRes}; use crate::asm::instr::op::{OpTrait, EvalRes};
use crate::runtime::fault::Fault; use crate::runtime::fault::Fault;
use crate::runtime::frame::{CallStack, StackFrame}; use crate::runtime::frame::{StackFrame};
use crate::runtime::program::Program;
use crate::builtin::defs::BuiltinOp; use crate::builtin::defs::BuiltinOp;
use crate::asm::data::literal::Addr; use crate::asm::data::literal::Addr;
use crate::runtime::run_thread::{ThreadInfo, RunState};
impl OpTrait for BuiltinOp { impl OpTrait for BuiltinOp {
fn execute(&self, program: &Program, call_stack: &mut CallStack, frame: &mut StackFrame) -> Result<EvalRes, Fault> { fn execute(&self, info: &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault> {
let frame = &mut state.frame;
let program = &info.program;
let mut res = EvalRes::default(); let mut res = EvalRes::default();
match self { match self {
BuiltinOp::Nop => {} BuiltinOp::Nop => {}
BuiltinOp::Halt => {
trace!("HALT - stop execution");
return Err(Fault::Halt);
}
BuiltinOp::Label(_) | BuiltinOp::FarLabel(_) | BuiltinOp::Routine(_) => { BuiltinOp::Label(_) | BuiltinOp::FarLabel(_) | BuiltinOp::Routine(_) => {
/* this is nop, but without any cost - just markers */ /* this is nop, but without any cost - just markers */
res.cycles = 0; res.cycles = 0;
@ -82,7 +89,7 @@ impl OpTrait for BuiltinOp {
let mut frame2 = StackFrame::new(pos, &values); let mut frame2 = StackFrame::new(pos, &values);
std::mem::swap(frame, &mut frame2); std::mem::swap(frame, &mut frame2);
call_stack.push(frame2); state.call_stack.push(frame2);
res.advance = 0; res.advance = 0;
} }
Err(e) => { Err(e) => {
@ -91,7 +98,7 @@ impl OpTrait for BuiltinOp {
} }
} }
BuiltinOp::Ret(retvals) => { BuiltinOp::Ret(retvals) => {
match call_stack.pop() { match state.call_stack.pop() {
Some(previous) => { Some(previous) => {
let mut values = Vec::with_capacity(retvals.len()); let mut values = Vec::with_capacity(retvals.len());
for arg in retvals { for arg in retvals {

@ -1,4 +1,4 @@
use crate::asm::data::{Rd, Wr};
use crate::asm::error::{Error}; use crate::asm::error::{Error};
use crate::asm::instr::op::{AsmModule, ParseOpResult}; use crate::asm::instr::op::{AsmModule, ParseOpResult};
use crate::asm::parse::arg_parser::ArgParser; use crate::asm::parse::arg_parser::ArgParser;
@ -30,6 +30,14 @@ impl AsmModule for BuiltinOpParser {
fn parse_op(&self, keyword: &str, mut args: ArgParser) -> Result<ParseOpResult, Error> { fn parse_op(&self, keyword: &str, mut args: ArgParser) -> Result<ParseOpResult, Error> {
Ok(ParseOpResult::Parsed(Op::BuiltIn(match keyword { Ok(ParseOpResult::Parsed(Op::BuiltIn(match keyword {
"nop" => {
BuiltinOp::Nop
}
"halt" => {
BuiltinOp::Halt
}
"j" => { "j" => {
let dest = parse_label(args.next())?; let dest = parse_label(args.next())?;
BuiltinOp::Jump(dest) BuiltinOp::Jump(dest)

@ -1,23 +1,25 @@
use crate::asm::data::literal::Addr;
use crate::asm::instr::Op;
use crate::asm::instr::op::{EvalRes, OpTrait}; use crate::asm::instr::op::{EvalRes, OpTrait};
use crate::runtime::fault::Fault; use crate::runtime::fault::Fault;
use crate::runtime::frame::StackFrame;
use crate::runtime::run_thread::RunThread; use crate::runtime::run_thread::RunThread;
impl RunThread { impl RunThread {
// TODO unit tests // TODO unit tests
pub fn eval_op(&mut self) -> Result<EvalRes, Fault> { pub fn eval_op(&mut self) -> Result<EvalRes, Fault> {
let frame = &mut self.frame;
let op = self.program.read(frame.pc); let state = &mut self.state;
let info = &self.info;
let op = info.program.read(state.frame.pc);
trace!("### {:04} : {:?}", frame.pc.0, op); trace!("### {:04} : {:?}", state.frame.pc.0, op);
/* Operations can be given different execution times when run in slow mode. */ /* Operations can be given different execution times when run in slow mode. */
/* Presently, all that do anything use 1 cycle. */ /* Presently, all that do anything use 1 cycle. */
let rv = op.execute(&self.program, &mut self.call_stack, frame); let rv = op.execute(info, state);
trace!("-> {:?}", rv); trace!("-> {:?}", rv);
rv rv
} }

@ -21,6 +21,9 @@ pub enum Fault {
msg: DebugMsg, msg: DebugMsg,
}, },
#[error("Program ended.")]
Halt,
// #[error("Memory region {area:?} is locked by thread {owner:?}")] // #[error("Memory region {area:?} is locked by thread {owner:?}")]
// MemoryLocked { // MemoryLocked {
// area: MemorySpan, // area: MemorySpan,

@ -8,36 +8,58 @@ use crate::asm::data::literal::Addr;
use crate::asm::instr::op::EvalRes; use crate::asm::instr::op::EvalRes;
use crate::runtime::frame::{CallStack, StackFrame}; use crate::runtime::frame::{CallStack, StackFrame};
use crate::runtime::program::Program; use crate::runtime::program::Program;
use crate::runtime::fault::Fault;
const CYCLE_TIME: Duration = Duration::from_millis(0);
//const CYCLE_TIME : Duration = Duration::from_millis(100);
#[derive(Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd)] #[derive(Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd)]
pub struct ThreadToken(pub u32); pub struct ThreadToken(pub u32);
pub struct RunThread { pub struct RunThread {
pub(crate) info: ThreadInfo,
pub(crate) state: RunState,
}
pub struct ThreadInfo {
/// Thread ID /// Thread ID
pub id: ThreadToken, pub id: ThreadToken,
/// Program to run
pub program: Arc<Program>,
/// Program to run
pub cycle_time: Duration,
}
pub struct RunState {
/// Active stack frame /// Active stack frame
pub frame: StackFrame, pub frame: StackFrame,
/// Call stack /// Call stack
pub call_stack: CallStack, pub call_stack: CallStack,
/// Program to run
pub program: Arc<Program>,
/// Extension data /// Extension data
ext_data: HashMap<TypeId, Box<dyn Any + Send>>, ext_data: HashMap<TypeId, Box<dyn Any + Send>>,
} }
impl RunState {
pub fn ext_mut<T: Send + Default + 'static>(&mut self) -> &mut T {
if !self.ext_data.contains_key(&TypeId::of::<T>()) {
self.ext_data.insert(TypeId::of::<T>(), Box::new(T::default()));
}
self.ext_data.get_mut(&TypeId::of::<T>()).unwrap()
.downcast_mut().unwrap()
}
}
impl RunThread { impl RunThread {
pub fn new(id: ThreadToken, program: Arc<Program>, pc: Addr, args: &[u64]) -> Self { pub fn new(id: ThreadToken, program: Arc<Program>, pc: Addr, args: &[u64]) -> Self {
let sf = StackFrame::new(pc, args);
Self { Self {
id, info: ThreadInfo {
frame: sf, id,
call_stack: vec![], program,
program, cycle_time: Duration::default(),
ext_data: Default::default(), },
state: RunState {
frame: StackFrame::new(pc, args),
call_stack: vec![],
ext_data: Default::default(),
},
} }
} }
@ -50,12 +72,17 @@ impl RunThread {
fn run(mut self) { fn run(mut self) {
'run: loop { 'run: loop {
match self.eval_op() { match self.eval_op() {
Ok(EvalRes { Ok(EvalRes { cycles, advance }) => {
cycles, advance std::thread::sleep(self.info.cycle_time * (cycles as u32));
}) => { trace!("Step {}; Status = {}", advance, self.state.frame.status);
std::thread::sleep(CYCLE_TIME * (cycles as u32)); self.state.frame.pc.advance(advance);
trace!("Step {}; Status = {}", advance, self.frame.status); if self.state.frame.status.invalid {
self.frame.pc.advance(advance); warn!("Operation failed with INVALID status!");
}
}
Err(Fault::Halt) => {
info!("Program ended.");
break 'run;
} }
Err(e) => { Err(e) => {
error!("Fault: {:?}", e); error!("Fault: {:?}", e);
@ -64,13 +91,4 @@ impl RunThread {
} }
} }
} }
pub fn ext_mut<T: Send + Default + 'static>(&mut self) -> &mut T {
if !self.ext_data.contains_key(&TypeId::of::<T>()) {
self.ext_data.insert(TypeId::of::<T>(), Box::new(T::default()));
}
self.ext_data.get_mut(&TypeId::of::<T>()).unwrap()
.downcast_mut().unwrap()
}
} }

@ -4,13 +4,15 @@ use num_traits::PrimInt;
use crsn::asm::instr::op::{OpTrait, EvalRes}; use crsn::asm::instr::op::{OpTrait, EvalRes};
use crsn::runtime::fault::Fault; use crsn::runtime::fault::Fault;
use crsn::runtime::frame::{CallStack, StackFrame};
use crsn::runtime::program::Program;
use crate::defs::ArithOp; use crate::defs::ArithOp;
use crsn::runtime::run_thread::{ThreadInfo, RunState};
impl OpTrait for ArithOp { impl OpTrait for ArithOp {
fn execute(&self, _program: &Program, _call_stack: &mut CallStack, frame: &mut StackFrame) -> Result<EvalRes, Fault> { fn execute(&self, _ti : &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault> {
let frame = &mut state.frame;
let eres = EvalRes::default(); let eres = EvalRes::default();
match self { match self {
ArithOp::Test { a } => { ArithOp::Test { a } => {

@ -1,4 +1,4 @@
pub use parse::ArithOpParser; pub use parse::ArithOps;
mod defs; mod defs;
mod parse; mod parse;

@ -7,11 +7,11 @@ use crsn::asm::parse::arg_parser::ArgParser;
use crate::defs::ArithOp; use crate::defs::ArithOp;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ArithOpParser { pub struct ArithOps {
_internal: () _internal: ()
} }
impl ArithOpParser { impl ArithOps {
pub fn new() -> Box<dyn AsmModule> { pub fn new() -> Box<dyn AsmModule> {
Box::new(Self { Box::new(Self {
_internal: () _internal: ()
@ -19,7 +19,7 @@ impl ArithOpParser {
} }
} }
impl AsmModule for ArithOpParser { impl AsmModule for ArithOps {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"arith" "arith"
} }

@ -0,0 +1,10 @@
[package]
name = "crsn_stacks"
version = "0.1.0"
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
crsn = { path = "../crsn" }

@ -0,0 +1,7 @@
use crsn::asm::data::{Rd, Wr};
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum StackOp {
Push { num: Rd, src: Rd },
Pop { dst: Wr, num: Rd },
}

@ -0,0 +1,71 @@
use std::collections::VecDeque;
use crsn::asm::data::literal::Value;
use crsn::asm::instr::op::{EvalRes, OpTrait};
use crsn::runtime::fault::Fault;
use crsn::runtime::run_thread::{RunState, ThreadInfo};
use crate::defs::StackOp;
struct Stacks {
stacks: Vec<VecDeque<Value>>,
}
impl Default for Stacks {
fn default() -> Self {
Stacks {
stacks: vec![VecDeque::default(); 8],
}
}
}
impl OpTrait for StackOp {
fn execute(&self, _ti: &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault> {
let eres = EvalRes::default();
match self {
StackOp::Push { num, src } => {
state.frame.status.clear();
let stack_num = state.frame.read(*num)?;
let val = state.frame.read(*src)?;
if stack_num > 8 {
state.frame.status.invalid = true;
} else {
let obj: &mut Stacks = state.ext_mut();
obj.stacks[stack_num as usize].push_back(val);
}
}
StackOp::Pop { dst, num } => {
state.frame.status.clear();
let stack_num = state.frame.read(*num)?;
if stack_num > 8 {
state.frame.status.invalid = true;
} else {
let obj: &mut Stacks = state.ext_mut();
let val = obj.stacks[stack_num as usize].pop_back();
let val = match val {
None => {
state.frame.status.overflow = true;
0
}
Some(val) => {
val
}
};
state.frame.write(*dst, val)?;
}
// TODO
}
}
Ok(eres)
}
//
}

@ -0,0 +1,6 @@
pub use parse::StackOps;
mod defs;
mod parse;
mod exec;

@ -0,0 +1,48 @@
use crsn::asm::error::Error;
use crsn::asm::instr::op::{AsmModule, ParseOpResult};
use crsn::asm::instr::Op;
use crsn::asm::parse::arg_parser::ArgParser;
use crate::defs::StackOp;
#[derive(Debug, Clone)]
pub struct StackOps {
_internal: ()
}
impl StackOps {
pub fn new() -> Box<dyn AsmModule> {
Box::new(Self {
_internal: ()
})
}
}
impl AsmModule for StackOps {
fn name(&self) -> &'static str {
"stacks"
}
fn parse_op(&self, keyword: &str, mut args: ArgParser) -> Result<ParseOpResult, Error> {
Ok(ParseOpResult::Parsed(Op::Ext(Box::new(match keyword {
"push" => {
StackOp::Push {
num: args.next_rd()?,
src: args.next_rd()?,
}
}
"pop" => {
StackOp::Pop {
dst: args.next_wr()?,
num: args.next_rd()?,
}
}
_other => {
return Ok(ParseOpResult::Unknown(args));
}
}))))
}
}

@ -9,6 +9,7 @@ edition = "2018"
[dependencies] [dependencies]
crsn = { path = "../crsn" } crsn = { path = "../crsn" }
crsn_arith = { path = "../crsn_arith" } crsn_arith = { path = "../crsn_arith" }
crsn_stacks = { path = "../crsn_stacks" }
simple_logger = "1.9.0" simple_logger = "1.9.0"
log = "0.4.11" log = "0.4.11"

@ -4,9 +4,10 @@ extern crate log;
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use crsn::asm::data::literal::Addr; use crsn::asm::data::literal::Addr;
use crsn::runtime::program::Program;
use crsn::runtime::run_thread::{RunThread, ThreadToken}; use crsn::runtime::run_thread::{RunThread, ThreadToken};
use crsn_arith::ArithOpParser; use crsn_arith::ArithOps;
use crsn_stacks::StackOps;
fn main() { fn main() {
SimpleLogger::new().init().unwrap(); SimpleLogger::new().init().unwrap();
@ -42,7 +43,7 @@ fn main() {
) )
";*/ ";*/
let program = " /* let program = "
( (
(main (main
(j :lbl) (j :lbl)
@ -63,15 +64,35 @@ fn main() {
(ret r0) (ret r0)
) )
) )
";*/
let program = "
(
(main
(push 0 10)
(push 0 20)
(push 0 30)
(pop r0 0)
(pop r0 0)
(pop r0 0)
(halt)
)
)
"; ";
let parsers = vec![ let parsers = vec![
ArithOpParser::new() ArithOps::new(),
StackOps::new(),
]; ];
let parsed = crsn::asm::assemble(program, parsers.as_slice()).unwrap(); let parsed = crsn::asm::assemble(program, parsers.as_slice()).unwrap();
let thread = RunThread::new(ThreadToken(0), parsed, Addr(0), &[]); let thread1 = RunThread::new(ThreadToken(0), parsed.clone(), Addr(0), &[]);
let thread2 = RunThread::new(ThreadToken(1), parsed.clone(), Addr(0), &[]);
let a = thread1.start();
let b = thread2.start();
thread.start().join().unwrap(); a.join().unwrap();
b.join().unwrap();
} }

Loading…
Cancel
Save