From d7548f3d1b4d164079661688d2dd58e47a6f2044 Mon Sep 17 00:00:00 2001 From: Jasper Schulz Date: Thu, 30 Mar 2017 15:05:31 +0200 Subject: [PATCH] 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;