From 2cb3eb4bec4cd32de09809b26d1350753e90dac9 Mon Sep 17 00:00:00 2001 From: Jasper Schulz Date: Thu, 23 Feb 2017 16:01:02 +0000 Subject: [PATCH 1/4] Fixed interpreted program. --- Cargo.toml | 3 +- src/conversions.rs | 93 +++++++++++++++ src/main.rs | 278 +++++++++++++++++++++++++++++---------------- src/recovery.rs | 42 +++++++ src/tracerunner.rs | 150 ++++++++++++++++++++++++ src/util.rs | 29 +++++ 6 files changed, 496 insertions(+), 99 deletions(-) create mode 100644 src/conversions.rs create mode 100644 src/recovery.rs create mode 100644 src/tracerunner.rs create mode 100644 src/util.rs diff --git a/Cargo.toml b/Cargo.toml index fab8b85..8a8cdbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,5 @@ version = "0.1.0" authors = ["Jasper Schulz "] [dependencies] -maplit = "*" \ No newline at end of file +maplit = "*" +kaktus = "0.1.2" diff --git a/src/conversions.rs b/src/conversions.rs new file mode 100644 index 0000000..f210020 --- /dev/null +++ b/src/conversions.rs @@ -0,0 +1,93 @@ + +use super::*; + +impl From for Value { + fn from(b: bool) -> Self { + Value::Bool(b) + } +} + +impl From for bool { + fn from(val: Value) -> Self { + match val { + Value::Bool(b) => b, + _ => panic!("unexpeted Array variant"), + } + } +} + +impl From> for Value { + fn from(xs: Vec) -> Self { + Value::Array(xs) + } +} + +impl From for Vec { + fn from(val: Value) -> Self { + match val { + Value::Array(xs) => xs, + _ => panic!("unexpeted Array variant"), + } + } +} + +impl AsMut> for Value { + fn as_mut(&mut self) -> &mut Vec { + match *self { + Value::Array(ref mut xs) => xs, + _ => panic!("unexpeted Array variant"), + } + } +} + + +// usize +impl From for usize { + fn from(val: Value) -> Self { + match val { + Value::Usize(n) => n, + _ => panic!("unexpeted Array variant"), + } + } +} + +impl From for Value { + fn from(n: usize) -> Self { + Value::Usize(n) + } +} + + +// impl<'a> From<&'a Instruction> for TraceInstruction { +impl From for TraceInstruction { + fn from(instr: Instruction) -> TraceInstruction { + use Instruction as I; + use TraceInstruction as TI; + + match instr { + I::Add => TI::Add, + I::Cmp(c) => TI::Cmp(c), + I::Const(c) => TI::Const(c), + + I::Len => TI::Len, + I::Print => TI::Print, + I::Clone => TI::Clone, + + I::Array(u) => TI::Array(u), + I::ArrayGet => TI::ArrayGet, + + _ => panic!("can not convert {:?}", instr), + } + } +} + + +impl<'a> From<&'a Func> for FuncInfo { + fn from(func: &Func) -> Self { + FuncInfo { + name: func.name.clone(), + args: func.args, + locals: func.locals, + } + } +} diff --git a/src/main.rs b/src/main.rs index 19db0e3..6acbc7b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,37 @@ -#[macro_use] extern crate maplit; +#[macro_use] +extern crate maplit; + +extern crate kaktus; + +use kaktus::{PushPop, Stack}; use std::collections::BTreeMap; +use std::cmp::max; + +use std::rc::Rc; + +// impl From for Value and vice versa +mod conversions; +mod recovery; +mod tracerunner; +mod util; + +use recovery::{Guard, FrameInfo}; +use tracerunner::Runner; + pub struct Module { - funcs: BTreeMap, + funcs: BTreeMap>, } +#[derive(Debug)] +pub struct Trace { + pub trace: Vec, + pub locals: usize, +} + +#[derive(Debug, Clone)] pub struct Func { name: String, args: usize, @@ -14,6 +39,14 @@ pub struct Func { instr: Vec, } +#[derive(Debug, Clone)] +pub struct FuncInfo { + name: String, + args: usize, + locals: usize, +} + + #[derive(Debug, Clone, Copy)] pub enum Comp { Eq, @@ -51,93 +84,62 @@ pub enum Instruction { Len, Print, Clone, - - Guard(bool), } + #[derive(Debug, Clone)] -pub enum Value { - Null, - Bool(bool), - Usize(usize), - Array(Vec), -} +pub enum TraceInstruction { + Add, + Cmp(Comp), -impl From for Value { - fn from(b: bool) -> Self { - Value::Bool(b) - } -} + Load(usize), + Store(usize), -impl From for bool { - fn from(val: Value) -> Self { - match val { - Value::Bool(b) => b, - _ => panic!("unexpeted Array variant"), - } - } -} + Const(usize), -impl From> for Value { - fn from(xs: Vec) -> Self { - Value::Array(xs) - } -} + Array(usize), + ArrayGet, + Push, -impl From for Vec { - fn from(val: Value) -> Self { - match val { - Value::Array(xs) => xs, - _ => panic!("unexpeted Array variant"), - } - } -} + // intrinsics + Len, + Print, + Clone, -impl AsMut> for Value { - fn as_mut(&mut self) -> &mut Vec { - match *self { - Value::Array(ref mut xs) => xs, - _ => panic!("unexpeted Array variant"), - } - } + Guard(Guard), } - -// usize -impl From for usize { - fn from(val: Value) -> Self { - match val { - Value::Usize(n) => n, - _ => panic!("unexpeted Array variant"), - } - } +#[derive(Debug, Clone)] +pub enum Value { + Null, + Bool(bool), + Usize(usize), + Array(Vec), } -impl From for Value { - fn from(n: usize) -> Self { - Value::Usize(n) - } -} -pub struct CallFrame<'a> { - back_ref: (&'a Func, usize), +pub struct CallFrame { + back_ref: (Rc, usize), args: usize, locals: Vec, } -impl<'a> CallFrame<'a> { - fn for_fn(func: &Func, back_ref: (&'a Func, usize)) -> Self { +impl CallFrame { + fn for_fn(func: &Func, back_ref: (Rc, usize)) -> Self { CallFrame { - back_ref: back_ref, + back_ref: back_ref.clone(), args: func.args, - locals: vec![Value::Null; func.args+func.locals] } + locals: vec![Value::Null; func.args+func.locals], + } } } + + pub struct Interpreter<'a> { module: &'a Module, stack: Vec, - frames: Vec>, + frames: Vec, } @@ -150,27 +152,41 @@ impl<'a> Interpreter<'a> { } } - fn get_fn(&self, name: &str) -> &'a Func { - self.module.funcs.get(name).unwrap() + fn get_fn(&self, name: &str) -> Rc { + self.module.funcs.get(name).unwrap().clone() } - fn trace(&mut self, o_func: &'a Func, o_pc: usize) -> (&'a Func, usize) { + // XXX: why do I return func, pc? shouldn't that be the same as the input? + fn trace(&mut self, o_func: Rc, o_pc: usize) -> (Rc, usize, Trace) { use Instruction::*; let mut pc = o_pc; - let mut func = o_func; + let mut func = o_func.clone(); let mut trace = Vec::new(); - let mut stack_size = 0; + + // offset from where we can add new locals + let mut stack_offset = func.args + func.locals; + + let mut call_tree = Stack::root(FrameInfo { + func: o_func.clone(), + back_ref: self.frames.last().unwrap().back_ref.clone(), + offset: 0, + }); + + let mut stack_prefix = 0; + let mut stack_prefixes = vec![0]; + let mut max_local = 0; + loop { - let instr = &func.instr[pc]; - // println!("{:?}", instr); + let instr = func.instr[pc].clone(); + // println!(" DO: {:?}", instr); pc += 1; - match *instr { + match instr { Loop => { break; - }, + } Break => (), @@ -179,8 +195,19 @@ impl<'a> Interpreter<'a> { Const(n) => self.do_const(n), Add => self.do_add(), - Load(idx) => self.do_load(idx), - Store(idx) => self.do_store(idx), + Load(idx) => { + self.do_load(idx); + trace.push(TraceInstruction::Load(stack_prefix + idx)); + continue; + } + + Store(idx) => { + self.do_store(idx); + trace.push(TraceInstruction::Store(stack_prefix + idx)); + max_local = max(max_local, stack_prefix + idx); + continue; + + } Print => self.do_print(), @@ -192,32 +219,56 @@ impl<'a> Interpreter<'a> { ArrayGet => self.do_array_get(), Call(ref target) => { - let new_func = self.module.funcs.get(target).unwrap(); - let mut frame = CallFrame::for_fn(new_func, (func, pc)); + let new_func = self.module.funcs.get(target).unwrap().clone(); + let mut frame = CallFrame::for_fn(&*new_func, (func.clone(), pc)); + + // guard = guard.push(TraceGuard::new( + // stack_offset, + // new_func.into())); + + stack_prefixes.push(stack_prefix); + stack_prefix = stack_offset; + + stack_offset += frame.locals.len(); + // trace.push(Call("xxx".into())); for idx in 0..frame.args { frame.locals[idx] = self.stack.pop().unwrap(); + trace.push(TraceInstruction::Store(stack_prefix + idx)); + max_local = max(max_local, stack_prefix + idx); } + // XXX: push frame instead? + call_tree = call_tree.push(FrameInfo { + func: new_func.clone(), + back_ref: frame.back_ref.clone(), + offset: stack_prefix, + }); + self.frames.push(frame); + func = new_func; pc = 0; continue; - }, + } Return => { - let frame = self.frames.pop(); + stack_prefix = stack_prefixes.pop().unwrap(); + let frame = self.frames.pop(); if self.frames.is_empty() { break; } + // trace.push(Return); + + call_tree = call_tree.pop().unwrap(); let (f, rpc) = frame.unwrap().back_ref; func = f; pc = rpc; continue; - }, + } Cmp(how) => self.do_cmp(how), @@ -233,7 +284,13 @@ impl<'a> Interpreter<'a> { pc = target; } - trace.push(Guard(b)); + let guard = Guard { + condition: b, + frame: call_tree.clone(), + // reverse pc +1 above + pc: pc - 1, + }; + trace.push(TraceInstruction::Guard(guard)); continue; } @@ -241,14 +298,20 @@ impl<'a> Interpreter<'a> { _ => panic!("TODO: {:?}", instr), } - trace.push(instr.clone()); + trace.push(TraceInstruction::from(instr)); } - println!("{:?}", trace); + // println!("{:?}", trace); + + let t = Trace { + trace: trace, + locals: max_local + 1, + }; - (func, pc) + (func, pc, t) } + fn run(&mut self) { use Instruction::*; @@ -256,21 +319,39 @@ impl<'a> Interpreter<'a> { let mut pc = 0; - self.frames.push(CallFrame::for_fn(&main, (&main, 0))); + self.frames.push(CallFrame::for_fn(&*main, (main.clone(), 0))); let mut func = main; + let mut traces: BTreeMap = BTreeMap::new(); + loop { - let instr = &func.instr[pc]; - // println!("{:?}", instr); + let instr = func.instr[pc].clone(); + // println!("I: {:?}", instr); pc += 1; - match *instr { + match instr { Loop => { + if let Some(trace) = traces.get(&pc) { + // println!("{:?}", trace); + { + let mut runner = Runner::new(self, &trace.trace, trace.locals); + let res = runner.run(); + func = res.0; + pc = res.1; + } + + // println!("return from trace to func {:?} pc {:?}", func.name, pc); + // println!("STACK: {:?}", self.stack); + // println!("FRAME: {:?}", self.frames.last().unwrap().locals); + continue; + } + let res = self.trace(func, pc); func = res.0; pc = res.1; - }, + traces.insert(pc, res.2); + } Break => (), @@ -301,9 +382,9 @@ impl<'a> Interpreter<'a> { self.frames.push(frame); - func = new_func; + func = new_func.clone(); pc = 0; - }, + } Return => { let frame = self.frames.pop(); @@ -315,7 +396,7 @@ impl<'a> Interpreter<'a> { let (f, rpc) = frame.unwrap().back_ref; func = f; pc = rpc; - }, + } Cmp(how) => self.do_cmp(how), @@ -340,7 +421,8 @@ impl<'a> Interpreter<'a> { } fn pop(&mut self) -> T - where T: From { + where T: From + { self.stack.pop().unwrap().into() } @@ -413,21 +495,21 @@ fn main() { args: 0, locals: 0, instr: vec![Array(8), Const(9), Push, Const(3), Push, Const(4), Push, Const(5), Push, Const(6), Push, Const(1), Push, Const(3), Push, Const(2), Push, Const(4), Push, Call(String::from("min_list")), Return], - }, + }.into(), "min".into() => Func { name: "min".into(), args: 2, locals: 0, instr: vec![Load(1), Load(0), Cmp(self::Comp::Le), JumpIfFalse(6), Load(0), Jump(8), Load(1), Jump(8), Clone, Return] - }, + }.into(), "min_list".into() => Func { - name: "print".into(), + name: "min_list".into(), args: 1, locals: 3, - instr: vec![Load(0), Const(0), ArrayGet, Store(1), Load(0), Len, Store(2), Const(0), Store(3), Loop, Load(2), Load(3), Cmp(Comp::Le), JumpIfFalse(25), Load(0), Load(3), ArrayGet, Load(1), Call(String::from("min")), Store(1), Load(3), Const(1), Add, Store(3), Jump(9), Break, Load(1), Print, Return], - } + instr: vec![Load(0), Const(0), ArrayGet, Store(1), Load(0), Len, Store(2), Const(0), Store(3), Loop, Load(2), Load(3), Cmp(Comp::Lt), JumpIfFalse(25), Load(0), Load(3), ArrayGet, Load(1), Call(String::from("min")), Store(1), Load(3), Const(1), Add, Store(3), Jump(9), Break, Load(1), Print, Return], + }.into(), } }; diff --git a/src/recovery.rs b/src/recovery.rs new file mode 100644 index 0000000..09b5a24 --- /dev/null +++ b/src/recovery.rs @@ -0,0 +1,42 @@ + +use std::rc::Rc; + +use kaktus::Stack; +use super::Func; + +use std::fmt; + +// #[derive(Debug, Clone)] +// pub struct Func { +// pub name: String, +// // number of arguments +// pub args: usize, +// // number of local variables (excluding args) +// pub locals: usize, +// pub instr: Vec, +// } + + +pub struct FrameInfo { + pub func: Rc, + pub back_ref: (Rc, usize), + pub offset: usize, +} + +#[derive(Clone)] +pub struct Guard { + // condition guard protects + pub condition: bool, + + pub frame: Stack, + // instruction in frame.instructions + pub pc: usize, /* offset of locals to trace vars + * pub locals_offset: usize, */ +} + + +impl fmt::Debug for Guard { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "-{:?}-", self.condition) + } +} diff --git a/src/tracerunner.rs b/src/tracerunner.rs new file mode 100644 index 0000000..06add71 --- /dev/null +++ b/src/tracerunner.rs @@ -0,0 +1,150 @@ + +use std::rc::Rc; +use super::{TraceInstruction, Comp, Value, Interpreter, CallFrame, Func}; +use util::Stack; +use recovery::Guard; +use kaktus::PushPop; + +pub struct Runner<'a, 'b: 'a> { + pub trace: &'a [TraceInstruction], + pub stack: Stack, + pub locals: Vec, + pub interp: &'a mut Interpreter<'b>, +} + +impl<'a, 'b> Runner<'a, 'b> { + pub fn new(interp: &'a mut Interpreter<'b>, + trace: &'a [TraceInstruction], + n_locals: usize) + -> Self { + let mut locals = vec![Value::Null; n_locals]; + { + let interp_locals = &interp.frames.last().unwrap().locals; + for idx in 0..interp_locals.len() { + locals[idx] = interp_locals[idx].clone(); + } + } + + Runner { + interp: interp, + trace: trace, + stack: Stack::new(), + locals: locals, + } + } + + pub fn run(&mut self) -> (Rc, usize) { + use TraceInstruction::*; + + let mut pc = 0; + loop { + let instr = &self.trace[pc]; + pc = (pc + 1) % self.trace.len(); + + // println!(" RUN: {:?}", instr); + + match *instr { + Add => self.add(), + Cmp(how) => self.cmp(how), + + Load(idx) => self.load(idx), + Store(idx) => self.store(idx), + Const(val) => self.stack.push(val), + + ArrayGet => self.array_get(), + + Clone => (), + + Guard(ref guard) => { + match self.guard(guard) { + Some(pc) => return pc, + None => (), + } + }, + + _ => unimplemented!(), + // NOT needed + // Array(usize), + // Push, + // Print, + // Len, + } + + } + } + + + fn add(&mut self) { + let (a, b) = self.stack.pop_2::(); + self.stack.push(a + b) + } + + fn cmp(&mut self, how: Comp) { + let (left, right) = self.stack.pop_2::(); + + let b = match how { + Comp::Lt => left < right, + Comp::Le => left <= right, + _ => panic!("TODO"), + }; + + self.stack.push(b); + } + + fn load(&mut self, idx: usize) { + let val = self.locals[idx].clone(); + self.stack.push(val); + + // println!("STACK: {:?}", self.stack.stack); + } + + fn store(&mut self, idx: usize) { + self.locals[idx] = self.stack.pop(); + } + + fn array_get(&mut self) { + let index: usize = self.stack.pop(); + let xs: Vec = self.stack.pop(); + self.stack.push(xs[index]); + } + + fn guard(&mut self, guard: &Guard) -> Option<(Rc, usize)> { + let got = self.stack.pop::(); + if got == guard.condition { + None + } else { + self.recover(guard); + Some((guard.frame.func.clone(), guard.pc)) + } + } + + fn recover(&mut self, guard: &Guard) { + // self.stack + // stack frames + // let mut frames = Vec::new(); + let chain = guard.frame.walk().collect::>(); + + // let mut last = &chain[0];//(*guard.frame).clone(); + + // remove the last call frame of the Interpreter + // it gets replaced with our updated version + self.interp.frames.pop().unwrap(); + + for info in chain.iter().rev() { + // first get an empty call frame + let mut frame = CallFrame::for_fn(&*info.func, info.back_ref.clone()); + + // fill it up with values + for idx in 0..frame.locals.len() { + frame.locals[idx] = self.locals[info.offset + idx].clone(); + } + // println!("{:?}", frame.locals); + + // and back to the Interpreter + self.interp.frames.push(frame); + } + + // push + self.interp.push_stack(!guard.condition); + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..3f4bcb2 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,29 @@ + + +use super::Value; + +pub struct Stack { + pub stack: Vec, +} + +impl Stack { + pub fn new() -> Self { + Stack { stack: Vec::new() } + } + + pub fn pop(&mut self) -> T + where T: From + { + self.stack.pop().unwrap().into() + } + + pub fn pop_2(&mut self) -> (T, T) + where T: From + { + (self.stack.pop().unwrap().into(), self.stack.pop().unwrap().into()) + } + + pub fn push>(&mut self, val: T) { + self.stack.push(val.into()); + } +} From b159c64e49dff9c0ae04d8e6ce92c615fed04c03 Mon Sep 17 00:00:00 2001 From: Jasper Schulz Date: Thu, 23 Mar 2017 22:04:01 +0100 Subject: [PATCH 2/4] Added From conversions to stack push/pop. Addressed comments. --- Cargo.toml | 2 + README.md | 24 +++++++ src/conversions.rs | 2 + src/main.rs | 162 ++++++++++++++++++++------------------------- src/recovery.rs | 24 ++----- src/tracerunner.rs | 123 +++++++++++++++++----------------- src/traits.rs | 29 ++++++++ src/util.rs | 29 -------- 8 files changed, 200 insertions(+), 195 deletions(-) create mode 100644 README.md create mode 100644 src/traits.rs delete mode 100644 src/util.rs diff --git a/Cargo.toml b/Cargo.toml index 8a8cdbd..b00ea77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,5 @@ authors = ["Jasper Schulz "] [dependencies] maplit = "*" kaktus = "0.1.2" +log = "0.3" +env_logger = "0.3" diff --git a/README.md b/README.md new file mode 100644 index 0000000..9955f01 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# daly +Simple VM for a Dyon--subset + +To run use: + + cargo run + +To see what is happening internally, you can enable logging: + + RUST_LOG=daly cargo run + +## Current state + +* `main.rs` implements a simple interpreter for some dyon-bytecode. `main()` contains the bytecode of [this](https://github.com/greenMT/example-programs/blob/master/example-programs/dyon/min_loop.dyon) program. + +* the interpreter traces execution of loops + +* `tracerunner.rs` contains an independent execution engine for generated traces + + +### Optimisations + +**Inlining** +Inlining of function calls is performed. Necessary steps for deoptimisation can be found in `tracerunner::Runner::recover`. diff --git a/src/conversions.rs b/src/conversions.rs index f210020..a1a6c80 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -1,4 +1,6 @@ +// XXX: there might be a macro which implements From/Into for enums + use super::*; impl From for Value { diff --git a/src/main.rs b/src/main.rs index 6acbc7b..d49350d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,36 +1,44 @@ +// btreemap! macro #[macro_use] extern crate maplit; +#[macro_use] +extern crate log; +extern crate env_logger; + extern crate kaktus; -use kaktus::{PushPop, Stack}; use std::collections::BTreeMap; use std::cmp::max; - use std::rc::Rc; -// impl From for Value and vice versa -mod conversions; -mod recovery; -mod tracerunner; -mod util; +use kaktus::{PushPop, Stack}; use recovery::{Guard, FrameInfo}; +use traits::vec::ConvertingStack; use tracerunner::Runner; +mod conversions; +mod recovery; +mod tracerunner; +mod traits; + + pub struct Module { funcs: BTreeMap>, } + #[derive(Debug)] pub struct Trace { pub trace: Vec, pub locals: usize, } + #[derive(Debug, Clone)] pub struct Func { name: String, @@ -39,6 +47,7 @@ pub struct Func { instr: Vec, } + #[derive(Debug, Clone)] pub struct FuncInfo { name: String, @@ -56,13 +65,13 @@ pub enum Comp { Ge, } + #[derive(Debug, Clone)] pub enum Instruction { Call(String), Return, Add, - Cmp(Comp), Jump(usize), @@ -71,7 +80,6 @@ pub enum Instruction { Load(usize), Store(usize), - Const(usize), Array(usize), @@ -80,6 +88,7 @@ pub enum Instruction { Loop, Break, + // intrinsics Len, Print, @@ -94,7 +103,6 @@ pub enum TraceInstruction { Load(usize), Store(usize), - Const(usize), Array(usize), @@ -109,6 +117,7 @@ pub enum TraceInstruction { Guard(Guard), } + #[derive(Debug, Clone)] pub enum Value { Null, @@ -135,14 +144,12 @@ impl CallFrame { } - pub struct Interpreter<'a> { module: &'a Module, stack: Vec, frames: Vec, } - impl<'a> Interpreter<'a> { fn new(module: &'a Module) -> Self { Interpreter { @@ -153,7 +160,7 @@ impl<'a> Interpreter<'a> { } fn get_fn(&self, name: &str) -> Rc { - self.module.funcs.get(name).unwrap().clone() + self.module.funcs[name].clone() } // XXX: why do I return func, pc? shouldn't that be the same as the input? @@ -176,19 +183,21 @@ impl<'a> Interpreter<'a> { let mut stack_prefix = 0; let mut stack_prefixes = vec![0]; + + // XXX: does this brake, if there are no local variables? + // eg: fn noop() {} let mut max_local = 0; loop { let instr = func.instr[pc].clone(); - // println!(" DO: {:?}", instr); - pc += 1; + + info!(target: "exec","TRACE: {:?}", instr); + match instr { - Loop => { - break; - } + Loop => break, - Break => (), + Break => unimplemented!(), Clone => (), @@ -206,7 +215,6 @@ impl<'a> Interpreter<'a> { trace.push(TraceInstruction::Store(stack_prefix + idx)); max_local = max(max_local, stack_prefix + idx); continue; - } Print => self.do_print(), @@ -219,26 +227,19 @@ impl<'a> Interpreter<'a> { ArrayGet => self.do_array_get(), Call(ref target) => { - let new_func = self.module.funcs.get(target).unwrap().clone(); + let new_func = self.module.funcs[target].clone(); let mut frame = CallFrame::for_fn(&*new_func, (func.clone(), pc)); - // guard = guard.push(TraceGuard::new( - // stack_offset, - // new_func.into())); - stack_prefixes.push(stack_prefix); stack_prefix = stack_offset; - stack_offset += frame.locals.len(); - // trace.push(Call("xxx".into())); for idx in 0..frame.args { frame.locals[idx] = self.stack.pop().unwrap(); trace.push(TraceInstruction::Store(stack_prefix + idx)); max_local = max(max_local, stack_prefix + idx); } - // XXX: push frame instead? call_tree = call_tree.push(FrameInfo { func: new_func.clone(), back_ref: frame.back_ref.clone(), @@ -247,7 +248,6 @@ impl<'a> Interpreter<'a> { self.frames.push(frame); - func = new_func; pc = 0; continue; @@ -260,7 +260,6 @@ impl<'a> Interpreter<'a> { if self.frames.is_empty() { break; } - // trace.push(Return); call_tree = call_tree.pop().unwrap(); @@ -274,12 +273,12 @@ impl<'a> Interpreter<'a> { Jump(target) => { pc = target; - // don't trace + // skip trace continue; } JumpIfFalse(target) => { - let b: bool = self.pop(); + let b: bool = self.stack.pop_into(); if !bool::from(b) { pc = target; } @@ -287,12 +286,11 @@ impl<'a> Interpreter<'a> { let guard = Guard { condition: b, frame: call_tree.clone(), - // reverse pc +1 above + // reverse `pc+=1` above pc: pc - 1, }; trace.push(TraceInstruction::Guard(guard)); continue; - } _ => panic!("TODO: {:?}", instr), @@ -301,79 +299,78 @@ impl<'a> Interpreter<'a> { trace.push(TraceInstruction::from(instr)); } - // println!("{:?}", trace); + info!(target: "trace", "{:?}", trace); - let t = Trace { + (func, + pc, + Trace { trace: trace, locals: max_local + 1, - }; - - (func, pc, t) + }) } - fn run(&mut self) { use Instruction::*; + // `main` function has to exist let main = self.get_fn("main"); - let mut pc = 0; - + // a bit awkward, main would return to main + // maybe it would be better to have Option as back_ref self.frames.push(CallFrame::for_fn(&*main, (main.clone(), 0))); let mut func = main; - + let mut pc = 0; let mut traces: BTreeMap = BTreeMap::new(); loop { - let instr = func.instr[pc].clone(); - // println!("I: {:?}", instr); - + let instr = &func.clone().instr[pc]; pc += 1; - match instr { + + info!("E: {:?}", instr); + + match *instr { + // currently there is no threshhold value when to start tracing Loop => { + // do we already have a trace for this position? if let Some(trace) = traces.get(&pc) { - // println!("{:?}", trace); { + info!("T: running trace @{:}[{:}]", func.name, pc); + let mut runner = Runner::new(self, &trace.trace, trace.locals); let res = runner.run(); func = res.0; pc = res.1; } - // println!("return from trace to func {:?} pc {:?}", func.name, pc); - // println!("STACK: {:?}", self.stack); - // println!("FRAME: {:?}", self.frames.last().unwrap().locals); + info!("T: return from trace to func {:?} pc {:?}", func.name, pc); + info!("T: STACK: {:?}", self.stack); + info!("T: FRAME: {:?}", self.frames.last().unwrap().locals); continue; } + // start tracing let res = self.trace(func, pc); func = res.0; pc = res.1; traces.insert(pc, res.2); } - Break => (), - - Clone => (), + // XXX + Break | Clone => (), Const(n) => self.do_const(n), Add => self.do_add(), - Load(idx) => self.do_load(idx), Store(idx) => self.do_store(idx), - Print => self.do_print(), - Array(size) => self.do_array(size), - Len => self.do_len(), Push => self.do_push(), - ArrayGet => self.do_array_get(), Call(ref target) => { - let new_func = self.module.funcs.get(target).unwrap(); + let new_func = &self.module.funcs[target]; let mut frame = CallFrame::for_fn(new_func, (func, pc)); for idx in 0..frame.args { @@ -416,25 +413,13 @@ impl<'a> Interpreter<'a> { } } - fn push_stack>(&mut self, val: T) { - self.stack.push(val.into()); - } - - fn pop(&mut self) -> T - where T: From - { - self.stack.pop().unwrap().into() - } - fn do_add(&mut self) { - let left = self.pop::(); - let right = self.pop::(); - - self.push_stack(left + right); + let (left, right) = self.stack.pop_2_into::(); + self.stack.push_from(left + right); } fn do_push(&mut self) { - let val = self.pop(); + let val = self.stack.pop_into(); self.stack.last_mut().unwrap().as_mut().push(val); } @@ -451,8 +436,8 @@ impl<'a> Interpreter<'a> { } fn do_len(&mut self) { - let v: Vec = self.pop(); - self.stack.push(v.len().into()); + let v: Vec = self.stack.pop_into(); + self.stack.push_from(v.len()); } fn do_print(&mut self) { @@ -462,32 +447,32 @@ impl<'a> Interpreter<'a> { } fn do_array(&mut self, capacity: usize) { - self.stack.push(Vec::with_capacity(capacity).into()); + self.stack.push_from(Vec::with_capacity(capacity)); } fn do_array_get(&mut self) { - let index: usize = self.pop(); - let xs: Vec = self.pop(); - self.stack.push(xs[index].into()); + let index: usize = self.stack.pop_into(); + let xs: Vec = self.stack.pop_into(); + self.stack.push_from(xs[index]); } fn do_cmp(&mut self, how: Comp) { - let left: usize = self.pop(); - let right: usize = self.pop(); - - let b = match how { + let (left, right) = self.stack.pop_2_into::(); + self.stack.push_from(match how { Comp::Lt => left < right, Comp::Le => left <= right, _ => panic!("TODO"), - }; - - self.stack.push(b.into()); + }); } } + fn main() { use Instruction::*; + + env_logger::init().unwrap(); + let prog = Module { funcs: btreemap!{ "main".into() => Func { @@ -514,6 +499,5 @@ fn main() { }; let mut interpreter = Interpreter::new(&prog); - interpreter.run(); } diff --git a/src/recovery.rs b/src/recovery.rs index 09b5a24..b1f22ff 100644 --- a/src/recovery.rs +++ b/src/recovery.rs @@ -1,40 +1,30 @@ use std::rc::Rc; +use std::fmt; use kaktus::Stack; -use super::Func; - -use std::fmt; -// #[derive(Debug, Clone)] -// pub struct Func { -// pub name: String, -// // number of arguments -// pub args: usize, -// // number of local variables (excluding args) -// pub locals: usize, -// pub instr: Vec, -// } +use super::Func; pub struct FrameInfo { pub func: Rc, pub back_ref: (Rc, usize), + // offset of inlined values pub offset: usize, } + #[derive(Clone)] pub struct Guard { // condition guard protects pub condition: bool, - + // frame information to recover from pub frame: Stack, - // instruction in frame.instructions - pub pc: usize, /* offset of locals to trace vars - * pub locals_offset: usize, */ + // pc position where execution can continue + pub pc: usize, } - impl fmt::Debug for Guard { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "-{:?}-", self.condition) diff --git a/src/tracerunner.rs b/src/tracerunner.rs index 06add71..1a4eaa0 100644 --- a/src/tracerunner.rs +++ b/src/tracerunner.rs @@ -1,13 +1,16 @@ use std::rc::Rc; + +use kaktus::PushPop; + use super::{TraceInstruction, Comp, Value, Interpreter, CallFrame, Func}; -use util::Stack; use recovery::Guard; -use kaktus::PushPop; +use traits::vec::ConvertingStack; + pub struct Runner<'a, 'b: 'a> { pub trace: &'a [TraceInstruction], - pub stack: Stack, + pub stack: Vec, pub locals: Vec, pub interp: &'a mut Interpreter<'b>, } @@ -17,6 +20,7 @@ impl<'a, 'b> Runner<'a, 'b> { trace: &'a [TraceInstruction], n_locals: usize) -> Self { + // we have to copy over current stack frame from interpreter let mut locals = vec![Value::Null; n_locals]; { let interp_locals = &interp.frames.last().unwrap().locals; @@ -28,7 +32,7 @@ impl<'a, 'b> Runner<'a, 'b> { Runner { interp: interp, trace: trace, - stack: Stack::new(), + stack: Vec::new(), locals: locals, } } @@ -41,7 +45,7 @@ impl<'a, 'b> Runner<'a, 'b> { let instr = &self.trace[pc]; pc = (pc + 1) % self.trace.len(); - // println!(" RUN: {:?}", instr); + info!("TEXEC: {:?}", instr); match *instr { Add => self.add(), @@ -49,7 +53,7 @@ impl<'a, 'b> Runner<'a, 'b> { Load(idx) => self.load(idx), Store(idx) => self.store(idx), - Const(val) => self.stack.push(val), + Const(val) => self.stack.push_from(val), ArrayGet => self.array_get(), @@ -57,13 +61,12 @@ impl<'a, 'b> Runner<'a, 'b> { Guard(ref guard) => { match self.guard(guard) { - Some(pc) => return pc, - None => (), + Ok(_) => (), + Err(recovery) => return recovery, } - }, + } _ => unimplemented!(), - // NOT needed // Array(usize), // Push, // Print, @@ -73,14 +76,56 @@ impl<'a, 'b> Runner<'a, 'b> { } } + // XXX: return None guard succeeds + fn guard(&mut self, guard: &Guard) -> Result<(), (Rc, usize)> { + let got = self.stack.pop_into::(); + if got == guard.condition { + Ok(()) + } else { + self.recover(guard); + Err((guard.frame.func.clone(), guard.pc)) + } + } + + /// the following things have to be recovered + /// * stack-frames (call-frames) + /// * value stack (essentially bool which caused guard to fail) + fn recover(&mut self, guard: &Guard) { + // remove the last callframe of the Interpreter + // it gets replaced with our updated version + self.interp.frames.pop().unwrap(); + + // recover callframes + // since callframes depend on each other, we start with the one which + // was created first + let frames = guard.frame.walk().collect::>(); + for frame_info in frames.iter().rev() { + // 1. create a new callframe + let mut frame = CallFrame::for_fn(&*frame_info.func, frame_info.back_ref.clone()); + + // 2. fill it up with locals + for idx in 0..frame.locals.len() { + frame.locals[idx] = self.locals[frame_info.offset + idx].clone(); + } + + // 3. add frame to interpreter callframes + self.interp.frames.push(frame); + } + + // recover value stack + self.interp.stack.push_from(!guard.condition); + } +} +/// normal interpreter functions +impl<'a, 'b> Runner<'a, 'b> { fn add(&mut self) { - let (a, b) = self.stack.pop_2::(); - self.stack.push(a + b) + let (a, b) = self.stack.pop_2_into::(); + self.stack.push_from(a + b) } fn cmp(&mut self, how: Comp) { - let (left, right) = self.stack.pop_2::(); + let (left, right) = self.stack.pop_2_into::(); let b = match how { Comp::Lt => left < right, @@ -88,63 +133,21 @@ impl<'a, 'b> Runner<'a, 'b> { _ => panic!("TODO"), }; - self.stack.push(b); + self.stack.push_from(b); } fn load(&mut self, idx: usize) { let val = self.locals[idx].clone(); self.stack.push(val); - - // println!("STACK: {:?}", self.stack.stack); } fn store(&mut self, idx: usize) { - self.locals[idx] = self.stack.pop(); + self.locals[idx] = self.stack.pop_into(); } fn array_get(&mut self) { - let index: usize = self.stack.pop(); - let xs: Vec = self.stack.pop(); - self.stack.push(xs[index]); - } - - fn guard(&mut self, guard: &Guard) -> Option<(Rc, usize)> { - let got = self.stack.pop::(); - if got == guard.condition { - None - } else { - self.recover(guard); - Some((guard.frame.func.clone(), guard.pc)) - } - } - - fn recover(&mut self, guard: &Guard) { - // self.stack - // stack frames - // let mut frames = Vec::new(); - let chain = guard.frame.walk().collect::>(); - - // let mut last = &chain[0];//(*guard.frame).clone(); - - // remove the last call frame of the Interpreter - // it gets replaced with our updated version - self.interp.frames.pop().unwrap(); - - for info in chain.iter().rev() { - // first get an empty call frame - let mut frame = CallFrame::for_fn(&*info.func, info.back_ref.clone()); - - // fill it up with values - for idx in 0..frame.locals.len() { - frame.locals[idx] = self.locals[info.offset + idx].clone(); - } - // println!("{:?}", frame.locals); - - // and back to the Interpreter - self.interp.frames.push(frame); - } - - // push - self.interp.push_stack(!guard.condition); + let index: usize = self.stack.pop_into(); + let xs: Vec = self.stack.pop_into(); + self.stack.push_from(xs[index]); } } diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 0000000..99cdede --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,29 @@ + +pub mod vec { + + pub trait ConvertingStack { + fn pop_into(&mut self) -> U where U: From; + + fn pop_2_into(&mut self) -> (U, U) where U: From; + + fn push_from>(&mut self, val: U); + } + + impl ConvertingStack for Vec { + fn pop_into(&mut self) -> U + where U: From + { + self.pop().unwrap().into() + } + + fn pop_2_into(&mut self) -> (U, U) + where U: From + { + (self.pop().unwrap().into(), self.pop().unwrap().into()) + } + + fn push_from>(&mut self, val: U) { + self.push(val.into()); + } + } +} diff --git a/src/util.rs b/src/util.rs deleted file mode 100644 index 3f4bcb2..0000000 --- a/src/util.rs +++ /dev/null @@ -1,29 +0,0 @@ - - -use super::Value; - -pub struct Stack { - pub stack: Vec, -} - -impl Stack { - pub fn new() -> Self { - Stack { stack: Vec::new() } - } - - pub fn pop(&mut self) -> T - where T: From - { - self.stack.pop().unwrap().into() - } - - pub fn pop_2(&mut self) -> (T, T) - where T: From - { - (self.stack.pop().unwrap().into(), self.stack.pop().unwrap().into()) - } - - pub fn push>(&mut self, val: T) { - self.stack.push(val.into()); - } -} From 5cab23dc5332ff220b388367815e2ac105ee208b Mon Sep 17 00:00:00 2001 From: Jasper Schulz Date: Thu, 30 Mar 2017 14:54:39 +0200 Subject: [PATCH 3/4] Simplified trace-locals "allocation". --- src/main.rs | 74 ++++++++++++++++++++++++++++++---------------- src/tracerunner.rs | 9 +++--- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/src/main.rs b/src/main.rs index d49350d..c456e9d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,6 @@ extern crate kaktus; use std::collections::BTreeMap; -use std::cmp::max; use std::rc::Rc; use kaktus::{PushPop, Stack}; @@ -35,7 +34,13 @@ pub struct Module { #[derive(Debug)] pub struct Trace { pub trace: Vec, - pub locals: usize, + pub locals_count: usize, +} + +impl Trace { + fn new(trace: Vec, locals_count: usize) -> Self { + Trace { trace: trace, locals_count: locals_count } + } } @@ -144,6 +149,36 @@ impl CallFrame { } +struct TraceDataAllocator { + total_size: usize, + offsets: Vec, +} + +impl TraceDataAllocator { + fn new() -> Self { + TraceDataAllocator { total_size: 0, offsets: Vec::new() } + } + + fn alloc(&mut self, to_allocate: usize) { + self.offsets.push(self.total_size); + // reserve space at the end + self.total_size += to_allocate; + } + + fn pop(&mut self) { + self.offsets.pop().unwrap(); + } + + fn current(&self) -> usize { + *self.offsets.last().unwrap() + } + + fn at(&self, idx: usize) -> usize { + self.current() + idx + } +} + + pub struct Interpreter<'a> { module: &'a Module, stack: Vec, @@ -172,21 +207,14 @@ impl<'a> Interpreter<'a> { let mut trace = Vec::new(); - // offset from where we can add new locals - let mut stack_offset = func.args + func.locals; - let mut call_tree = Stack::root(FrameInfo { func: o_func.clone(), back_ref: self.frames.last().unwrap().back_ref.clone(), offset: 0, }); - let mut stack_prefix = 0; - let mut stack_prefixes = vec![0]; - - // XXX: does this brake, if there are no local variables? - // eg: fn noop() {} - let mut max_local = 0; + let mut locals = TraceDataAllocator::new(); + locals.alloc(func.args + func.locals); loop { let instr = func.instr[pc].clone(); @@ -206,14 +234,13 @@ impl<'a> Interpreter<'a> { Load(idx) => { self.do_load(idx); - trace.push(TraceInstruction::Load(stack_prefix + idx)); + trace.push(TraceInstruction::Load(locals.at(idx))); continue; } Store(idx) => { self.do_store(idx); - trace.push(TraceInstruction::Store(stack_prefix + idx)); - max_local = max(max_local, stack_prefix + idx); + trace.push(TraceInstruction::Store(locals.at(idx))); continue; } @@ -230,20 +257,17 @@ impl<'a> Interpreter<'a> { let new_func = self.module.funcs[target].clone(); let mut frame = CallFrame::for_fn(&*new_func, (func.clone(), pc)); - stack_prefixes.push(stack_prefix); - stack_prefix = stack_offset; - stack_offset += frame.locals.len(); + locals.alloc(frame.locals.len()); for idx in 0..frame.args { frame.locals[idx] = self.stack.pop().unwrap(); - trace.push(TraceInstruction::Store(stack_prefix + idx)); - max_local = max(max_local, stack_prefix + idx); + trace.push(TraceInstruction::Store(locals.at(idx))); } call_tree = call_tree.push(FrameInfo { func: new_func.clone(), back_ref: frame.back_ref.clone(), - offset: stack_prefix, + offset: locals.current(), }); self.frames.push(frame); @@ -254,7 +278,7 @@ impl<'a> Interpreter<'a> { } Return => { - stack_prefix = stack_prefixes.pop().unwrap(); + locals.pop(); let frame = self.frames.pop(); if self.frames.is_empty() { @@ -303,10 +327,8 @@ impl<'a> Interpreter<'a> { (func, pc, - Trace { - trace: trace, - locals: max_local + 1, - }) + Trace::new(trace, locals.total_size) + ) } fn run(&mut self) { @@ -337,7 +359,7 @@ impl<'a> Interpreter<'a> { { info!("T: running trace @{:}[{:}]", func.name, pc); - let mut runner = Runner::new(self, &trace.trace, trace.locals); + let mut runner = Runner::new(self, trace); let res = runner.run(); func = res.0; pc = res.1; diff --git a/src/tracerunner.rs b/src/tracerunner.rs index 1a4eaa0..1420b6d 100644 --- a/src/tracerunner.rs +++ b/src/tracerunner.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use kaktus::PushPop; -use super::{TraceInstruction, Comp, Value, Interpreter, CallFrame, Func}; +use super::{TraceInstruction, Comp, Value, Interpreter, CallFrame, Func, Trace}; use recovery::Guard; use traits::vec::ConvertingStack; @@ -17,11 +17,10 @@ pub struct Runner<'a, 'b: 'a> { impl<'a, 'b> Runner<'a, 'b> { pub fn new(interp: &'a mut Interpreter<'b>, - trace: &'a [TraceInstruction], - n_locals: usize) + trace: &'a Trace) -> Self { // we have to copy over current stack frame from interpreter - let mut locals = vec![Value::Null; n_locals]; + let mut locals = vec![Value::Null; trace.locals_count]; { let interp_locals = &interp.frames.last().unwrap().locals; for idx in 0..interp_locals.len() { @@ -31,7 +30,7 @@ impl<'a, 'b> Runner<'a, 'b> { Runner { interp: interp, - trace: trace, + trace: &trace.trace, stack: Vec::new(), locals: locals, } From 683592108abb49e52cb87afe2f7bc8eb68fb031f Mon Sep 17 00:00:00 2001 From: Jasper Schulz Date: Thu, 30 Mar 2017 15:05:31 +0200 Subject: [PATCH 4/4] Refactoring / Addressing comments. --- Cargo.lock | 197 ++++++++++++++++++++++++++++++++ Cargo.toml | 3 + src/bytecode.rs | 40 +++++++ src/conversions.rs | 21 +--- src/main.rs | 275 +++++++++++++++++---------------------------- src/recovery.rs | 3 +- src/repr.rs | 78 +++++++++++++ src/tracerunner.rs | 89 ++++++++------- src/traits.rs | 1 - 9 files changed, 471 insertions(+), 236 deletions(-) create mode 100644 Cargo.lock create mode 100644 src/bytecode.rs create mode 100644 src/repr.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2261ccc --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,197 @@ +[root] +name = "daly" +version = "0.1.0" +dependencies = [ + "boolinator 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "frunk 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "frunk_core 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "kaktus 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "maplit 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aho-corasick" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "boolinator" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "env_logger" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "frunk" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "frunk_core 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "frunk_derives 0.0.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "frunk_core" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "frunk_derives" +version = "0.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "frunk_core 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kaktus" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "maplit" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "regex" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread-id" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "utf8-ranges" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" +"checksum boolinator 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" +"checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" +"checksum frunk 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "42b2bd259125dfaba5473097510511f5a1b8207d4b57c452d3c15f0d10e60e65" +"checksum frunk_core 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cd6072440020a11d45de0d90b9262e5c3ef97a15715b6967882929f268e0ae61" +"checksum frunk_derives 0.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0c8eeb7faaf9ce942ed75d2bc700b679fcf2bfa59d2c2e53fbc1a792f109ed" +"checksum kaktus 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee011eaf002da2fffa16feb71bfc9c221aed5f630cae0787d84430b1db13759" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135" +"checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad" +"checksum maplit 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "be384c560e0c3ad868b590ffb88d2c0a1effde6f59885234e4ea811c1202bfea" +"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" +"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" +"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" +"checksum syn 0.11.9 (registry+https://github.com/rust-lang/crates.io-index)" = "480c834701caba3548aa991e54677281be3a5414a9d09ddbdf4ed74a569a9d19" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" +"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" +"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/Cargo.toml b/Cargo.toml index b00ea77..18a9985 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,6 @@ maplit = "*" kaktus = "0.1.2" log = "0.3" env_logger = "0.3" +frunk = "0.1.22" +frunk_core = "0.0.11" +boolinator = "2.4.0" diff --git a/src/bytecode.rs b/src/bytecode.rs new file mode 100644 index 0000000..85fedab --- /dev/null +++ b/src/bytecode.rs @@ -0,0 +1,40 @@ + + +#[derive(Debug, Clone, Copy)] +pub enum Comp { + Eq, + Lt, + Le, + Gt, + Ge, +} + + +#[derive(Debug, Clone)] +pub enum Instruction { + Call(String), + Return, + + Add, + Cmp(Comp), + + Jump(usize), + JumpIfTrue(usize), + JumpIfFalse(usize), + + Load(usize), + Store(usize), + Const(usize), + + Array(usize), + ArrayGet, + Push, + + Loop, + Break, + + // intrinsics + Len, + Print, + Clone, +} diff --git a/src/conversions.rs b/src/conversions.rs index a1a6c80..6390a57 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -43,7 +43,6 @@ impl AsMut> for Value { } -// usize impl From for usize { fn from(val: Value) -> Self { match val { @@ -60,21 +59,18 @@ impl From for Value { } -// impl<'a> From<&'a Instruction> for TraceInstruction { -impl From for TraceInstruction { - fn from(instr: Instruction) -> TraceInstruction { +impl<'a> From<&'a Instruction> for TraceInstruction { + fn from(instr: &Instruction) -> TraceInstruction { use Instruction as I; use TraceInstruction as TI; - match instr { + match *instr { I::Add => TI::Add, I::Cmp(c) => TI::Cmp(c), I::Const(c) => TI::Const(c), - I::Len => TI::Len, I::Print => TI::Print, I::Clone => TI::Clone, - I::Array(u) => TI::Array(u), I::ArrayGet => TI::ArrayGet, @@ -82,14 +78,3 @@ impl From for TraceInstruction { } } } - - -impl<'a> From<&'a Func> for FuncInfo { - fn from(func: &Func) -> Self { - FuncInfo { - name: func.name.clone(), - args: func.args, - locals: func.locals, - } - } -} diff --git a/src/main.rs b/src/main.rs index c456e9d..c94b8ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,27 +7,35 @@ extern crate maplit; extern crate log; extern crate env_logger; +extern crate boolinator; extern crate kaktus; - use std::collections::BTreeMap; use std::rc::Rc; use kaktus::{PushPop, Stack}; +use bytecode::{Instruction, Comp}; use recovery::{Guard, FrameInfo}; -use traits::vec::ConvertingStack; use tracerunner::Runner; +use repr::{CallFrame, Func, InstrPtr, Value}; +use traits::vec::ConvertingStack; +mod bytecode; mod conversions; mod recovery; mod tracerunner; mod traits; +mod repr; + + +pub type TraceMap = BTreeMap; +pub type ModuleMap = BTreeMap>; pub struct Module { - funcs: BTreeMap>, + funcs: ModuleMap, } @@ -39,68 +47,14 @@ pub struct Trace { impl Trace { fn new(trace: Vec, locals_count: usize) -> Self { - Trace { trace: trace, locals_count: locals_count } + Trace { + trace: trace, + locals_count: locals_count, + } } } -#[derive(Debug, Clone)] -pub struct Func { - name: String, - args: usize, - locals: usize, - instr: Vec, -} - - -#[derive(Debug, Clone)] -pub struct FuncInfo { - name: String, - args: usize, - locals: usize, -} - - -#[derive(Debug, Clone, Copy)] -pub enum Comp { - Eq, - Lt, - Le, - Gt, - Ge, -} - - -#[derive(Debug, Clone)] -pub enum Instruction { - Call(String), - Return, - - Add, - Cmp(Comp), - - Jump(usize), - JumpIfTrue(usize), - JumpIfFalse(usize), - - Load(usize), - Store(usize), - Const(usize), - - Array(usize), - ArrayGet, - Push, - - Loop, - Break, - - // intrinsics - Len, - Print, - Clone, -} - - #[derive(Debug, Clone)] pub enum TraceInstruction { Add, @@ -123,31 +77,6 @@ pub enum TraceInstruction { } -#[derive(Debug, Clone)] -pub enum Value { - Null, - Bool(bool), - Usize(usize), - Array(Vec), -} - - -pub struct CallFrame { - back_ref: (Rc, usize), - args: usize, - locals: Vec, -} - -impl CallFrame { - fn for_fn(func: &Func, back_ref: (Rc, usize)) -> Self { - CallFrame { - back_ref: back_ref.clone(), - args: func.args, - locals: vec![Value::Null; func.args+func.locals], - } - } -} - struct TraceDataAllocator { total_size: usize, @@ -156,7 +85,10 @@ struct TraceDataAllocator { impl TraceDataAllocator { fn new() -> Self { - TraceDataAllocator { total_size: 0, offsets: Vec::new() } + TraceDataAllocator { + total_size: 0, + offsets: Vec::new(), + } } fn alloc(&mut self, to_allocate: usize) { @@ -199,30 +131,29 @@ impl<'a> Interpreter<'a> { } // XXX: why do I return func, pc? shouldn't that be the same as the input? - fn trace(&mut self, o_func: Rc, o_pc: usize) -> (Rc, usize, Trace) { + fn trace(&mut self, instr: &InstrPtr) -> (InstrPtr, Trace) { use Instruction::*; - let mut pc = o_pc; - let mut func = o_func.clone(); - let mut trace = Vec::new(); let mut call_tree = Stack::root(FrameInfo { - func: o_func.clone(), + func: instr.func.clone(), back_ref: self.frames.last().unwrap().back_ref.clone(), offset: 0, }); let mut locals = TraceDataAllocator::new(); - locals.alloc(func.args + func.locals); + locals.alloc(instr.func.args_count + instr.func.locals_count); + + let mut next = instr.clone(); loop { - let instr = func.instr[pc].clone(); - pc += 1; + let instr = next; + next = instr.next(); info!(target: "exec","TRACE: {:?}", instr); - match instr { + match *instr { Loop => break, Break => unimplemented!(), @@ -254,12 +185,12 @@ impl<'a> Interpreter<'a> { ArrayGet => self.do_array_get(), Call(ref target) => { - let new_func = self.module.funcs[target].clone(); - let mut frame = CallFrame::for_fn(&*new_func, (func.clone(), pc)); + let new_func = &self.module.funcs[target]; + let mut frame = CallFrame::for_fn(new_func, next); locals.alloc(frame.locals.len()); - for idx in 0..frame.args { + for idx in 0..frame.args_count { frame.locals[idx] = self.stack.pop().unwrap(); trace.push(TraceInstruction::Store(locals.at(idx))); } @@ -271,9 +202,9 @@ impl<'a> Interpreter<'a> { }); self.frames.push(frame); + next = InstrPtr::for_fn(new_func.clone()); - func = new_func; - pc = 0; + // don't add Call to trace continue; } @@ -287,16 +218,16 @@ impl<'a> Interpreter<'a> { call_tree = call_tree.pop().unwrap(); - let (f, rpc) = frame.unwrap().back_ref; - func = f; - pc = rpc; + next = frame.unwrap().back_ref; + + // don't add Return to trace continue; } Cmp(how) => self.do_cmp(how), Jump(target) => { - pc = target; + next = next.jump(target); // skip trace continue; } @@ -304,14 +235,13 @@ impl<'a> Interpreter<'a> { JumpIfFalse(target) => { let b: bool = self.stack.pop_into(); if !bool::from(b) { - pc = target; + next = next.jump(target); } let guard = Guard { condition: b, frame: call_tree.clone(), - // reverse `pc+=1` above - pc: pc - 1, + pc: instr.pc, }; trace.push(TraceInstruction::Guard(guard)); continue; @@ -320,15 +250,12 @@ impl<'a> Interpreter<'a> { _ => panic!("TODO: {:?}", instr), } - trace.push(TraceInstruction::from(instr)); + trace.push(TraceInstruction::from(&*instr)); } info!(target: "trace", "{:?}", trace); - (func, - pc, - Trace::new(trace, locals.total_size) - ) + (instr.clone(), Trace::new(trace, locals.total_size)) } fn run(&mut self) { @@ -339,98 +266,98 @@ impl<'a> Interpreter<'a> { // a bit awkward, main would return to main // maybe it would be better to have Option as back_ref - self.frames.push(CallFrame::for_fn(&*main, (main.clone(), 0))); + self.frames.push(CallFrame::for_fn(&main, InstrPtr::new(main.clone(), 0))); - let mut func = main; - let mut pc = 0; - let mut traces: BTreeMap = BTreeMap::new(); + let mut traces = TraceMap::new(); + let mut next = InstrPtr::for_fn(main.clone()); loop { - let instr = &func.clone().instr[pc]; - pc += 1; + // get next instruction + let instr = next; + // pre-set next instruction + next = instr.next(); - info!("E: {:?}", instr); + info!("E: {:?}", *instr); match *instr { - // currently there is no threshhold value when to start tracing + // XXX: do I care about break here? + Break | Clone => (), + + // simple dispatch of opcodes to callbacks + Const(n) => self.do_const(n), + Add => self.do_add(), + Load(idx) => self.do_load(idx), + Store(idx) => self.do_store(idx), + Print => self.do_print(), + Array(size) => self.do_array(size), + Len => self.do_len(), + Push => self.do_push(), + ArrayGet => self.do_array_get(), + Cmp(how) => self.do_cmp(how), + + // XXX: currently there is no threshhold value when to start tracing + // meaning that tracing starts immediately Loop => { // do we already have a trace for this position? - if let Some(trace) = traces.get(&pc) { + if let Some(trace) = traces.get(&instr.pc) { + // we need this block, since Runner takes self as &mut { - info!("T: running trace @{:}[{:}]", func.name, pc); - + info!("T: running trace @{:}[{:}]", instr.func.name, instr.pc); let mut runner = Runner::new(self, trace); - let res = runner.run(); - func = res.0; - pc = res.1; + next = runner.run(); } - - info!("T: return from trace to func {:?} pc {:?}", func.name, pc); + info!("T: return from trace to func {:?} pc {:?}", next.func.name, next.pc); info!("T: STACK: {:?}", self.stack); info!("T: FRAME: {:?}", self.frames.last().unwrap().locals); continue; } - // start tracing - let res = self.trace(func, pc); - func = res.0; - pc = res.1; - traces.insert(pc, res.2); + // no trace found => start tracing (with next instr) + let res = self.trace(&next); + next = res.0; + traces.insert(instr.pc, res.1); } - // XXX - Break | Clone => (), - - Const(n) => self.do_const(n), - Add => self.do_add(), - Load(idx) => self.do_load(idx), - Store(idx) => self.do_store(idx), - Print => self.do_print(), - Array(size) => self.do_array(size), - Len => self.do_len(), - Push => self.do_push(), - ArrayGet => self.do_array_get(), - Call(ref target) => { let new_func = &self.module.funcs[target]; - let mut frame = CallFrame::for_fn(new_func, (func, pc)); + let mut frame = CallFrame::for_fn(new_func, next); - for idx in 0..frame.args { - frame.locals[idx] = self.stack.pop().unwrap(); + // pass arguments to function locals + for idx in 0..frame.args_count { + frame.locals[idx] = self.stack + .pop() + .expect("Not enough arguments passed"); } self.frames.push(frame); - - func = new_func.clone(); - pc = 0; + next = InstrPtr::for_fn(new_func.clone()); } Return => { - let frame = self.frames.pop(); + // remove latest callframe + let old_frame = self.frames + .pop() + .expect("Return from non existing frame."); + // did we return from main function? if self.frames.is_empty() { break; + } else { + next = old_frame.back_ref; } - - let (f, rpc) = frame.unwrap().back_ref; - func = f; - pc = rpc; } - Cmp(how) => self.do_cmp(how), - Jump(target) => { - pc = target; + next = instr.jump(target); } JumpIfFalse(target) => { - if !bool::from(self.stack.pop().unwrap()) { - pc = target; + if let false = self.stack.pop_into::() { + next = instr.jump(target); } } _ => panic!("TODO: {:?}", instr), - } } } @@ -446,7 +373,7 @@ impl<'a> Interpreter<'a> { } fn do_const(&mut self, n: usize) { - self.stack.push(n.into()); + self.stack.push_from(n); } fn do_load(&mut self, idx: usize) { @@ -499,23 +426,23 @@ fn main() { funcs: btreemap!{ "main".into() => Func { name: "main".into(), - args: 0, - locals: 0, - instr: vec![Array(8), Const(9), Push, Const(3), Push, Const(4), Push, Const(5), Push, Const(6), Push, Const(1), Push, Const(3), Push, Const(2), Push, Const(4), Push, Call(String::from("min_list")), Return], + args_count: 0, + locals_count: 0, + instrs: vec![Array(8), Const(9), Push, Const(3), Push, Const(4), Push, Const(5), Push, Const(6), Push, Const(1), Push, Const(3), Push, Const(2), Push, Const(4), Push, Call(String::from("min_list")), Return], }.into(), "min".into() => Func { name: "min".into(), - args: 2, - locals: 0, - instr: vec![Load(1), Load(0), Cmp(self::Comp::Le), JumpIfFalse(6), Load(0), Jump(8), Load(1), Jump(8), Clone, Return] + args_count: 2, + locals_count: 0, + instrs: vec![Load(1), Load(0), Cmp(self::Comp::Le), JumpIfFalse(6), Load(0), Jump(8), Load(1), Jump(8), Clone, Return] }.into(), "min_list".into() => Func { name: "min_list".into(), - args: 1, - locals: 3, - instr: vec![Load(0), Const(0), ArrayGet, Store(1), Load(0), Len, Store(2), Const(0), Store(3), Loop, Load(2), Load(3), Cmp(Comp::Lt), JumpIfFalse(25), Load(0), Load(3), ArrayGet, Load(1), Call(String::from("min")), Store(1), Load(3), Const(1), Add, Store(3), Jump(9), Break, Load(1), Print, Return], + args_count: 1, + locals_count: 3, + instrs: vec![Load(0), Const(0), ArrayGet, Store(1), Load(0), Len, Store(2), Const(0), Store(3), Loop, Load(2), Load(3), Cmp(Comp::Lt), JumpIfFalse(25), Load(0), Load(3), ArrayGet, Load(1), Call(String::from("min")), Store(1), Load(3), Const(1), Add, Store(3), Jump(9), Break, Load(1), Print, Return], }.into(), } }; diff --git a/src/recovery.rs b/src/recovery.rs index b1f22ff..581feaa 100644 --- a/src/recovery.rs +++ b/src/recovery.rs @@ -5,11 +5,12 @@ use std::fmt; use kaktus::Stack; use super::Func; +use repr::InstrPtr; pub struct FrameInfo { pub func: Rc, - pub back_ref: (Rc, usize), + pub back_ref: InstrPtr, // offset of inlined values pub offset: usize, } diff --git a/src/repr.rs b/src/repr.rs new file mode 100644 index 0000000..abc4004 --- /dev/null +++ b/src/repr.rs @@ -0,0 +1,78 @@ + +use std::rc::Rc; +use std::ops::Deref; + +use bytecode::Instruction; + +#[derive(Debug, Clone)] +pub struct Func { + pub name: String, + pub args_count: usize, + pub locals_count: usize, + pub instrs: Vec, +} + +#[derive(Debug, Clone)] +pub enum Value { + Null, + Bool(bool), + Usize(usize), + Array(Vec), +} + + +pub struct CallFrame { + pub back_ref: InstrPtr, + pub args_count: usize, + pub locals: Vec, +} + +impl CallFrame { + pub fn for_fn(func: &Func, back_ref: InstrPtr) -> Self { + CallFrame { + back_ref: back_ref, + args_count: func.args_count, + locals: vec![Value::Null; func.args_count + func.locals_count], + } + } +} + + +#[derive(Debug, Clone)] +pub struct InstrPtr { + pub func: Rc, + pub pc: usize, +} + +impl InstrPtr { + pub fn new(func: Rc, pc: usize) -> Self { + InstrPtr { + func: func, + pc: pc, + } + } + + pub fn for_fn(func: Rc) -> Self { + InstrPtr::new(func, 0) + } + + pub fn next(&self) -> Self { + InstrPtr::new(self.func.clone(), self.pc + 1) + } + + pub fn jump(&self, target: usize) -> Self { + InstrPtr::new(self.func.clone(), target) + } + + pub fn pc(&self) -> usize { + self.pc + } +} + +impl Deref for InstrPtr { + type Target = Instruction; + + fn deref(&self) -> &Instruction { + &self.func.instrs[self.pc] + } +} diff --git a/src/tracerunner.rs b/src/tracerunner.rs index 1420b6d..a0cd03c 100644 --- a/src/tracerunner.rs +++ b/src/tracerunner.rs @@ -1,12 +1,11 @@ -use std::rc::Rc; - +use boolinator::Boolinator; use kaktus::PushPop; -use super::{TraceInstruction, Comp, Value, Interpreter, CallFrame, Func, Trace}; +use super::{TraceInstruction, Comp, Value, Interpreter, CallFrame, Trace}; use recovery::Guard; use traits::vec::ConvertingStack; - +use repr::InstrPtr; pub struct Runner<'a, 'b: 'a> { pub trace: &'a [TraceInstruction], @@ -16,9 +15,7 @@ pub struct Runner<'a, 'b: 'a> { } impl<'a, 'b> Runner<'a, 'b> { - pub fn new(interp: &'a mut Interpreter<'b>, - trace: &'a Trace) - -> Self { + pub fn new(interp: &'a mut Interpreter<'b>, trace: &'a Trace) -> Self { // we have to copy over current stack frame from interpreter let mut locals = vec![Value::Null; trace.locals_count]; { @@ -36,7 +33,7 @@ impl<'a, 'b> Runner<'a, 'b> { } } - pub fn run(&mut self) -> (Rc, usize) { + pub fn run(&mut self) -> InstrPtr { use TraceInstruction::*; let mut pc = 0; @@ -47,60 +44,68 @@ impl<'a, 'b> Runner<'a, 'b> { info!("TEXEC: {:?}", instr); match *instr { - Add => self.add(), - Cmp(how) => self.cmp(how), - - Load(idx) => self.load(idx), + Add => self.add(), + Cmp(how) => self.cmp(how), + Load(idx) => self.load(idx), Store(idx) => self.store(idx), + ArrayGet => self.array_get(), Const(val) => self.stack.push_from(val), - - ArrayGet => self.array_get(), - - Clone => (), + Clone => {} Guard(ref guard) => { - match self.guard(guard) { - Ok(_) => (), - Err(recovery) => return recovery, + if let Err(recovery) = self.check_guard(guard) { + return recovery; } } - _ => unimplemented!(), - // Array(usize), - // Push, - // Print, - // Len, + // these opcodes are not needed for example + Array(_) | Push | Print | Len => unimplemented!(), } - } } - // XXX: return None guard succeeds - fn guard(&mut self, guard: &Guard) -> Result<(), (Rc, usize)> { - let got = self.stack.pop_into::(); - if got == guard.condition { - Ok(()) - } else { - self.recover(guard); - Err((guard.frame.func.clone(), guard.pc)) - } + fn check_guard(&mut self, guard: &Guard) -> Result<(), InstrPtr> { + let check = self.stack.pop_into::() == guard.condition; + check.ok_or_else(||{ + self.recover(guard); + InstrPtr::new(guard.frame.func.clone(), guard.pc) + }) } - /// the following things have to be recovered - /// * stack-frames (call-frames) - /// * value stack (essentially bool which caused guard to fail) + /// Recovery (aka Blackholing) + /// + /// Execution has reached a point, where the trace isn't valid anymore. + /// The goal is to return to the interpreter, but the state has to be + /// recovered first. + /// + /// The following states have to be recovered: + /// * stack-frames (call-frames) + /// The failed guard might have failed within an inlined function call. + /// Thus, we have to reconstruct all missing callframes, before the + /// the interpreter can gain back control. + /// Second, we also have to consider the frame where the loop resides + /// in, since state might have also has changed there. + /// + /// * value stack + /// Also the operand stack has to be recovered. + /// Foremost, the condition, which caused the guard to fail, has to be + /// restored. + /// TODO: Are there other values which might have to be recovered? fn recover(&mut self, guard: &Guard) { // remove the last callframe of the Interpreter // it gets replaced with our updated version self.interp.frames.pop().unwrap(); // recover callframes - // since callframes depend on each other, we start with the one which - // was created first let frames = guard.frame.walk().collect::>(); + + // since callframes depend on each other, we start with the one which + // was created first (least-recent frame) `.rev()` ensures that for frame_info in frames.iter().rev() { - // 1. create a new callframe - let mut frame = CallFrame::for_fn(&*frame_info.func, frame_info.back_ref.clone()); + // 1. create a new callframe to push + let mut frame = CallFrame::for_fn( + &frame_info.func, + frame_info.back_ref.clone()); // 2. fill it up with locals for idx in 0..frame.locals.len() { @@ -116,7 +121,7 @@ impl<'a, 'b> Runner<'a, 'b> { } } -/// normal interpreter functions +// normal interpreter functions impl<'a, 'b> Runner<'a, 'b> { fn add(&mut self) { let (a, b) = self.stack.pop_2_into::(); diff --git a/src/traits.rs b/src/traits.rs index 99cdede..18db342 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,6 +1,5 @@ pub mod vec { - pub trait ConvertingStack { fn pop_into(&mut self) -> U where U: From;