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. 66
      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",
]
[[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",

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

@ -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<EvalRes, Fault>;
fn execute(&self, ti : &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault>;
}
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<EvalRes, Fault> {
fn execute(&self, ti : &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault> {
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)
}
}
}

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

@ -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<dyn AsmModule>]) -> Result<Op, Error> {

@ -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

@ -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<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();
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 {

@ -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<ParseOpResult, Error> {
Ok(ParseOpResult::Parsed(Op::BuiltIn(match keyword {
"nop" => {
BuiltinOp::Nop
}
"halt" => {
BuiltinOp::Halt
}
"j" => {
let dest = parse_label(args.next())?;
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::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<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. */
/* 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
}

@ -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,

@ -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>,
/// 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<Program>,
/// Extension data
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 {
pub fn new(id: ThreadToken, program: Arc<Program>, pc: Addr, args: &[u64]) -> Self {
let sf = StackFrame::new(pc, args);
Self {
info: ThreadInfo {
id,
frame: sf,
call_stack: vec![],
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<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::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<EvalRes, Fault> {
fn execute(&self, _ti : &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault> {
let frame = &mut state.frame;
let eres = EvalRes::default();
match self {
ArithOp::Test { a } => {

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

@ -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<dyn AsmModule> {
Box::new(Self {
_internal: ()
@ -19,7 +19,7 @@ impl ArithOpParser {
}
}
impl AsmModule for ArithOpParser {
impl AsmModule for ArithOps {
fn name(&self) -> &'static str {
"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]
crsn = { path = "../crsn" }
crsn_arith = { path = "../crsn_arith" }
crsn_stacks = { path = "../crsn_stacks" }
simple_logger = "1.9.0"
log = "0.4.11"

@ -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();
}

Loading…
Cancel
Save