From 0cd800653f0fb2da266fa8989d6b381f2ed83296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 26 Sep 2020 22:49:06 +0200 Subject: [PATCH] Example extension: Stacks; fixes to allow module data storage in thread context --- Cargo.lock | 8 ++++ Cargo.toml | 1 + crsn/src/asm/instr/op.rs | 24 +++++------- crsn/src/asm/mod.rs | 2 +- crsn/src/asm/parse/parse_op.rs | 8 ++-- crsn/src/builtin/defs.rs | 2 + crsn/src/builtin/exec.rs | 21 ++++++---- crsn/src/builtin/parse.rs | 10 ++++- crsn/src/runtime/exec.rs | 16 ++++---- crsn/src/runtime/fault.rs | 3 ++ crsn/src/runtime/run_thread.rs | 72 +++++++++++++++++++++------------- crsn_arith/src/exec.rs | 8 ++-- crsn_arith/src/lib.rs | 2 +- crsn_arith/src/parse.rs | 6 +-- crsn_stacks/Cargo.toml | 10 +++++ crsn_stacks/src/defs.rs | 7 ++++ crsn_stacks/src/exec.rs | 71 +++++++++++++++++++++++++++++++++ crsn_stacks/src/lib.rs | 6 +++ crsn_stacks/src/parse.rs | 48 +++++++++++++++++++++++ launcher/Cargo.toml | 1 + launcher/src/main.rs | 33 +++++++++++++--- 21 files changed, 285 insertions(+), 74 deletions(-) create mode 100644 crsn_stacks/Cargo.toml create mode 100644 crsn_stacks/src/defs.rs create mode 100644 crsn_stacks/src/exec.rs create mode 100644 crsn_stacks/src/lib.rs create mode 100644 crsn_stacks/src/parse.rs diff --git a/Cargo.lock b/Cargo.lock index 0040b5e..f7f6015 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,13 @@ dependencies = [ "num-traits", ] +[[package]] +name = "crsn_stacks" +version = "0.1.0" +dependencies = [ + "crsn", +] + [[package]] name = "dyn-clonable" version = "0.9.0" @@ -114,6 +121,7 @@ dependencies = [ "anyhow", "crsn", "crsn_arith", + "crsn_stacks", "log", "simple_logger", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 9299fd2..43dae3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,4 +3,5 @@ members = [ "launcher", "crsn", "crsn_arith", + "crsn_stacks", ] diff --git a/crsn/src/asm/instr/op.rs b/crsn/src/asm/instr/op.rs index b3bf321..6375caf 100644 --- a/crsn/src/asm/instr/op.rs +++ b/crsn/src/asm/instr/op.rs @@ -1,19 +1,15 @@ 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::instr::Cond; + use crate::asm::parse::arg_parser::ArgParser; use crate::builtin::defs::BuiltinOp; 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 #[derive(Debug)] @@ -24,7 +20,7 @@ pub enum Op { } pub trait OpTrait: Debug + Send + Sync + 'static { - fn execute(&self, program: &Program, call_stack: &mut CallStack, frame: &mut StackFrame) -> Result; + fn execute(&self, ti : &ThreadInfo, state: &mut RunState) -> Result; } pub enum ParseOpResult { @@ -64,13 +60,13 @@ impl Default for EvalRes { impl OpTrait for Op { - fn execute(&self, program: &Program, call_stack: &mut CallStack, frame: &mut StackFrame) -> Result { + fn execute(&self, ti : &ThreadInfo, state: &mut RunState) -> Result { match self { Op::BuiltIn(op) => { - op.execute(&program, call_stack, frame) + op.execute(ti, state) } Op::Ext(op) => { - op.execute(&program, call_stack, frame) + op.execute(ti, state) } } } diff --git a/crsn/src/asm/mod.rs b/crsn/src/asm/mod.rs index 0cb8966..0caf02b 100644 --- a/crsn/src/asm/mod.rs +++ b/crsn/src/asm/mod.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crate::asm::instr::Op; + use crate::asm::instr::op::AsmModule; use crate::runtime::program::Program; diff --git a/crsn/src/asm/parse/parse_op.rs b/crsn/src/asm/parse/parse_op.rs index 0788315..d211e70 100644 --- a/crsn/src/asm/parse/parse_op.rs +++ b/crsn/src/asm/parse/parse_op.rs @@ -1,11 +1,11 @@ -use crate::asm::data::literal::{Label, RoutineName}; + use crate::asm::error::Error; use crate::asm::instr::Op; -use crate::asm::instr::cond::parse_cond; + use crate::asm::instr::op::{AsmModule, ParseOpResult}; 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; pub fn parse_op(keyword: &str, mut arg_tokens: ArgParser, parsers: &[Box]) -> Result { diff --git a/crsn/src/builtin/defs.rs b/crsn/src/builtin/defs.rs index d2be813..b974808 100644 --- a/crsn/src/builtin/defs.rs +++ b/crsn/src/builtin/defs.rs @@ -6,6 +6,8 @@ use crate::asm::data::{Rd, Wr}; pub enum BuiltinOp { /// Do nothing Nop, + /// Stop execution + Halt, /// Mark a jump target. Label(Label), /// Jump to a label diff --git a/crsn/src/builtin/exec.rs b/crsn/src/builtin/exec.rs index 495fc22..168dde6 100644 --- a/crsn/src/builtin/exec.rs +++ b/crsn/src/builtin/exec.rs @@ -1,19 +1,26 @@ -use std::ops::Rem; -use num_traits::PrimInt; + + use crate::asm::instr::op::{OpTrait, EvalRes}; use crate::runtime::fault::Fault; -use crate::runtime::frame::{CallStack, StackFrame}; -use crate::runtime::program::Program; +use crate::runtime::frame::{StackFrame}; + use crate::builtin::defs::BuiltinOp; use crate::asm::data::literal::Addr; +use crate::runtime::run_thread::{ThreadInfo, RunState}; impl OpTrait for BuiltinOp { - fn execute(&self, program: &Program, call_stack: &mut CallStack, frame: &mut StackFrame) -> Result { + fn execute(&self, info: &ThreadInfo, state: &mut RunState) -> Result { + let frame = &mut state.frame; + let program = &info.program; let mut res = EvalRes::default(); match self { BuiltinOp::Nop => {} + BuiltinOp::Halt => { + trace!("HALT - stop execution"); + return Err(Fault::Halt); + } BuiltinOp::Label(_) | BuiltinOp::FarLabel(_) | BuiltinOp::Routine(_) => { /* this is nop, but without any cost - just markers */ res.cycles = 0; @@ -82,7 +89,7 @@ impl OpTrait for BuiltinOp { let mut frame2 = StackFrame::new(pos, &values); std::mem::swap(frame, &mut frame2); - call_stack.push(frame2); + state.call_stack.push(frame2); res.advance = 0; } Err(e) => { @@ -91,7 +98,7 @@ impl OpTrait for BuiltinOp { } } BuiltinOp::Ret(retvals) => { - match call_stack.pop() { + match state.call_stack.pop() { Some(previous) => { let mut values = Vec::with_capacity(retvals.len()); for arg in retvals { diff --git a/crsn/src/builtin/parse.rs b/crsn/src/builtin/parse.rs index 83360e1..41ee337 100644 --- a/crsn/src/builtin/parse.rs +++ b/crsn/src/builtin/parse.rs @@ -1,4 +1,4 @@ -use crate::asm::data::{Rd, Wr}; + use crate::asm::error::{Error}; use crate::asm::instr::op::{AsmModule, ParseOpResult}; 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 { Ok(ParseOpResult::Parsed(Op::BuiltIn(match keyword { + "nop" => { + BuiltinOp::Nop + } + + "halt" => { + BuiltinOp::Halt + } + "j" => { let dest = parse_label(args.next())?; BuiltinOp::Jump(dest) diff --git a/crsn/src/runtime/exec.rs b/crsn/src/runtime/exec.rs index bea9d3d..5bb0e9b 100644 --- a/crsn/src/runtime/exec.rs +++ b/crsn/src/runtime/exec.rs @@ -1,23 +1,25 @@ -use crate::asm::data::literal::Addr; -use crate::asm::instr::Op; + + use crate::asm::instr::op::{EvalRes, OpTrait}; use crate::runtime::fault::Fault; -use crate::runtime::frame::StackFrame; + use crate::runtime::run_thread::RunThread; impl RunThread { // TODO unit tests pub fn eval_op(&mut self) -> Result { - 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. */ /* 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); rv } diff --git a/crsn/src/runtime/fault.rs b/crsn/src/runtime/fault.rs index 951ce8e..b6e40ac 100644 --- a/crsn/src/runtime/fault.rs +++ b/crsn/src/runtime/fault.rs @@ -21,6 +21,9 @@ pub enum Fault { msg: DebugMsg, }, + #[error("Program ended.")] + Halt, + // #[error("Memory region {area:?} is locked by thread {owner:?}")] // MemoryLocked { // area: MemorySpan, diff --git a/crsn/src/runtime/run_thread.rs b/crsn/src/runtime/run_thread.rs index 8d50d49..be1e214 100644 --- a/crsn/src/runtime/run_thread.rs +++ b/crsn/src/runtime/run_thread.rs @@ -8,36 +8,58 @@ use crate::asm::data::literal::Addr; use crate::asm::instr::op::EvalRes; use crate::runtime::frame::{CallStack, StackFrame}; use crate::runtime::program::Program; - -const CYCLE_TIME: Duration = Duration::from_millis(0); -//const CYCLE_TIME : Duration = Duration::from_millis(100); +use crate::runtime::fault::Fault; #[derive(Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd)] pub struct ThreadToken(pub u32); pub struct RunThread { + pub(crate) info: ThreadInfo, + pub(crate) state: RunState, +} + +pub struct ThreadInfo { /// Thread ID pub id: ThreadToken, + /// Program to run + pub program: Arc, + /// Program to run + pub cycle_time: Duration, +} + +pub struct RunState { /// Active stack frame pub frame: StackFrame, /// Call stack pub call_stack: CallStack, - /// Program to run - pub program: Arc, /// Extension data ext_data: HashMap>, } +impl RunState { + pub fn ext_mut(&mut self) -> &mut T { + if !self.ext_data.contains_key(&TypeId::of::()) { + self.ext_data.insert(TypeId::of::(), Box::new(T::default())); + } + + self.ext_data.get_mut(&TypeId::of::()).unwrap() + .downcast_mut().unwrap() + } +} + impl RunThread { pub fn new(id: ThreadToken, program: Arc, pc: Addr, args: &[u64]) -> Self { - let sf = StackFrame::new(pc, args); - Self { - id, - frame: sf, - call_stack: vec![], - program, - ext_data: Default::default(), + info: ThreadInfo { + id, + program, + cycle_time: Duration::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) { 'run: loop { match self.eval_op() { - Ok(EvalRes { - cycles, advance - }) => { - std::thread::sleep(CYCLE_TIME * (cycles as u32)); - trace!("Step {}; Status = {}", advance, self.frame.status); - self.frame.pc.advance(advance); + Ok(EvalRes { cycles, advance }) => { + std::thread::sleep(self.info.cycle_time * (cycles as u32)); + trace!("Step {}; Status = {}", advance, self.state.frame.status); + self.state.frame.pc.advance(advance); + if self.state.frame.status.invalid { + warn!("Operation failed with INVALID status!"); + } + } + Err(Fault::Halt) => { + info!("Program ended."); + break 'run; } Err(e) => { error!("Fault: {:?}", e); @@ -64,13 +91,4 @@ impl RunThread { } } } - - pub fn ext_mut(&mut self) -> &mut T { - if !self.ext_data.contains_key(&TypeId::of::()) { - self.ext_data.insert(TypeId::of::(), Box::new(T::default())); - } - - self.ext_data.get_mut(&TypeId::of::()).unwrap() - .downcast_mut().unwrap() - } } diff --git a/crsn_arith/src/exec.rs b/crsn_arith/src/exec.rs index 5602829..de7925e 100644 --- a/crsn_arith/src/exec.rs +++ b/crsn_arith/src/exec.rs @@ -4,13 +4,15 @@ use num_traits::PrimInt; use crsn::asm::instr::op::{OpTrait, EvalRes}; use crsn::runtime::fault::Fault; -use crsn::runtime::frame::{CallStack, StackFrame}; -use crsn::runtime::program::Program; + + use crate::defs::ArithOp; +use crsn::runtime::run_thread::{ThreadInfo, RunState}; impl OpTrait for ArithOp { - fn execute(&self, _program: &Program, _call_stack: &mut CallStack, frame: &mut StackFrame) -> Result { + fn execute(&self, _ti : &ThreadInfo, state: &mut RunState) -> Result { + let frame = &mut state.frame; let eres = EvalRes::default(); match self { ArithOp::Test { a } => { diff --git a/crsn_arith/src/lib.rs b/crsn_arith/src/lib.rs index 3913fd1..e2f4f86 100644 --- a/crsn_arith/src/lib.rs +++ b/crsn_arith/src/lib.rs @@ -1,4 +1,4 @@ -pub use parse::ArithOpParser; +pub use parse::ArithOps; mod defs; mod parse; diff --git a/crsn_arith/src/parse.rs b/crsn_arith/src/parse.rs index b42404d..0381f4a 100644 --- a/crsn_arith/src/parse.rs +++ b/crsn_arith/src/parse.rs @@ -7,11 +7,11 @@ use crsn::asm::parse::arg_parser::ArgParser; use crate::defs::ArithOp; #[derive(Debug, Clone)] -pub struct ArithOpParser { +pub struct ArithOps { _internal: () } -impl ArithOpParser { +impl ArithOps { pub fn new() -> Box { Box::new(Self { _internal: () @@ -19,7 +19,7 @@ impl ArithOpParser { } } -impl AsmModule for ArithOpParser { +impl AsmModule for ArithOps { fn name(&self) -> &'static str { "arith" } diff --git a/crsn_stacks/Cargo.toml b/crsn_stacks/Cargo.toml new file mode 100644 index 0000000..dfb3421 --- /dev/null +++ b/crsn_stacks/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "crsn_stacks" +version = "0.1.0" +authors = ["Ondřej Hruška "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +crsn = { path = "../crsn" } diff --git a/crsn_stacks/src/defs.rs b/crsn_stacks/src/defs.rs new file mode 100644 index 0000000..a81d834 --- /dev/null +++ b/crsn_stacks/src/defs.rs @@ -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 }, +} diff --git a/crsn_stacks/src/exec.rs b/crsn_stacks/src/exec.rs new file mode 100644 index 0000000..ee22509 --- /dev/null +++ b/crsn_stacks/src/exec.rs @@ -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>, +} + +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 { + 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) + } + // +} diff --git a/crsn_stacks/src/lib.rs b/crsn_stacks/src/lib.rs new file mode 100644 index 0000000..e182b9f --- /dev/null +++ b/crsn_stacks/src/lib.rs @@ -0,0 +1,6 @@ +pub use parse::StackOps; + +mod defs; +mod parse; +mod exec; + diff --git a/crsn_stacks/src/parse.rs b/crsn_stacks/src/parse.rs new file mode 100644 index 0000000..c66be11 --- /dev/null +++ b/crsn_stacks/src/parse.rs @@ -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 { + Box::new(Self { + _internal: () + }) + } +} + +impl AsmModule for StackOps { + fn name(&self) -> &'static str { + "stacks" + } + + fn parse_op(&self, keyword: &str, mut args: ArgParser) -> Result { + 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)); + } + })))) + } +} diff --git a/launcher/Cargo.toml b/launcher/Cargo.toml index 2d29b17..7749f43 100644 --- a/launcher/Cargo.toml +++ b/launcher/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" [dependencies] crsn = { path = "../crsn" } crsn_arith = { path = "../crsn_arith" } +crsn_stacks = { path = "../crsn_stacks" } simple_logger = "1.9.0" log = "0.4.11" diff --git a/launcher/src/main.rs b/launcher/src/main.rs index 518e0c2..c90dc72 100644 --- a/launcher/src/main.rs +++ b/launcher/src/main.rs @@ -4,9 +4,10 @@ extern crate log; use simple_logger::SimpleLogger; use crsn::asm::data::literal::Addr; -use crsn::runtime::program::Program; + use crsn::runtime::run_thread::{RunThread, ThreadToken}; -use crsn_arith::ArithOpParser; +use crsn_arith::ArithOps; +use crsn_stacks::StackOps; fn main() { SimpleLogger::new().init().unwrap(); @@ -42,7 +43,7 @@ fn main() { ) ";*/ - let program = " +/* let program = " ( (main (j :lbl) @@ -63,15 +64,35 @@ fn main() { (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![ - ArithOpParser::new() + ArithOps::new(), + StackOps::new(), ]; 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(); }