From 534812f54d759f3ca07db942f3cfa98bda13e2ba Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Thu, 17 Sep 2020 13:09:37 -0700 Subject: [PATCH] Add instruction dumper A vector of instructions and constants can now be decompiled as text on the terminal to give an idea of what the VM is doing. Signed-off-by: Alek Ratzloff --- src/bin/not.rs | 7 ++- src/compile/mod.rs | 14 +++++ src/compile/thunk.rs | 14 ++--- src/obj/sym.rs | 8 ++- src/vm/inst.rs | 127 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 155 insertions(+), 15 deletions(-) diff --git a/src/bin/not.rs b/src/bin/not.rs index dbb3934..284ddce 100644 --- a/src/bin/not.rs +++ b/src/bin/not.rs @@ -32,13 +32,14 @@ fn main() -> Result<()> { return Err("errors reported, exiting".into()); } }.unwrap(); - //println!("{:#?}", ast); + println!("{:#?}", ast); let mut compile = Compile::new(); let (inst, const_pool) = compile.compile(&ast)?; - println!("{:#?}", inst); - println!("{:#?}", const_pool); + //println!("{:#?}", inst); + //println!("{:#?}", const_pool); + not_python::vm::inst::dump_inst_body(&inst, &const_pool); Ok(()) } diff --git a/src/compile/mod.rs b/src/compile/mod.rs index 6f2cc21..22007e0 100644 --- a/src/compile/mod.rs +++ b/src/compile/mod.rs @@ -11,6 +11,7 @@ pub struct Compile { const_data: ConstData, globals: BTreeMap, locals: Vec>, + names: Vec, next_local: Local, } @@ -63,6 +64,15 @@ impl Compile { .copied() } + /// Looks up a variable name, or creates a global name if it doesn't exist. + pub fn lookup_scope_or_create_global(&mut self, sym: Sym) -> Local { + if let Some(local) = self.lookup_scope(sym) { + local + } else { + self.create_global(sym) + } + } + /// Creates a new local variable if it does not exist in the current local scope. pub(crate) fn create_local(&mut self, sym: Sym) -> Local { let locals = self.locals.last_mut().expect("scope"); @@ -73,6 +83,8 @@ impl Compile { let local = self.next_local; self.next_local = self.next_local.next(); locals.insert(sym, local); + self.names.push(sym); + assert_eq!(self.names.len(), self.next_local.index()); local } } @@ -85,6 +97,8 @@ impl Compile { let global = self.next_local; self.next_local = self.next_local.next(); self.globals.insert(sym, global); + self.names.push(sym); + assert_eq!(self.names.len(), self.next_local.index()); global } } diff --git a/src/compile/thunk.rs b/src/compile/thunk.rs index d83f625..367bbbd 100644 --- a/src/compile/thunk.rs +++ b/src/compile/thunk.rs @@ -264,8 +264,9 @@ impl Visit for CompileBody<'_> { let attr = global_sym(expr.access.to_string()); thunk.push(Inst::SetAttr(attr)); } - LhsExpr::Local(local) => { - let local = global_sym(local.to_string()); + LhsExpr::Local(local_name) => { + let sym = global_sym(local_name.to_string()); + let local = self.compile.lookup_scope_or_create_global(sym); thunk = Inst::Pop(Some(local)).into(); } } @@ -320,6 +321,7 @@ impl Visit for CompileBody<'_> { for arg in expr.args.iter() { thunk.push_thunk(self.visit_expr(&arg)?); } + thunk.push(Inst::Call(expr.args.len())); Ok(thunk) } @@ -347,13 +349,7 @@ impl Visit for CompileBody<'_> { let thunk = match atom { Atom::Ident(ident) => { let sym = global_sym(ident.to_string()); - let local = if let Some(local) = self.compile.lookup_scope(sym) { - local - } else { - // create a global that gets looked up instead, since nothing with this name - // has been declared/assigned in this scope - self.compile.create_global(sym) - }; + let local = self.compile.lookup_scope_or_create_global(sym); // get local Inst::PushLocal(local).into() } diff --git a/src/obj/sym.rs b/src/obj/sym.rs index 4a6d4ed..7c63187 100644 --- a/src/obj/sym.rs +++ b/src/obj/sym.rs @@ -1,6 +1,6 @@ use crate::obj::{intern::Interner, names::*, prelude::*}; use once_cell::sync::Lazy; -use std::{collections::BTreeMap, sync::Mutex}; +use std::{collections::BTreeMap, sync::{Arc, Mutex}}; // // struct Sym @@ -93,6 +93,12 @@ pub fn global_sym(s: String) -> Sym { table.insert(s) } +/// Gets the pointer to a name from a global symbol. +pub fn global_sym_lookup(sym: Sym) -> Option> { + let table = SYMS.lock().unwrap(); + table.lookup(sym) +} + // // SymTable interner types // diff --git a/src/vm/inst.rs b/src/vm/inst.rs index 15b9aa2..343f38f 100644 --- a/src/vm/inst.rs +++ b/src/vm/inst.rs @@ -1,4 +1,5 @@ -use crate::{obj::prelude::*, vm::consts::ConstHandle}; +use crate::{obj::prelude::*, vm::consts::*}; +use std::fmt::{self, Display, Formatter}; #[derive(Debug, PartialEq, Clone, Copy)] pub enum Inst { @@ -12,7 +13,7 @@ pub enum Inst { PushLocal(Local), /// Pop a value from the stack, possibly into a local symbol. - Pop(Option), + Pop(Option), /// Pops a symbol value and an object reference. /// @@ -109,3 +110,125 @@ pub enum Inst { /// pushing the result to the stack. BinOr, } + +// +// impl Inst +// +impl Inst { + pub fn name(&self) -> &'static str { + match self { + Inst::PushSym(_) => "PUSH_SYM", + Inst::PushConst(_) => "PUSH_CONST", + Inst::PushLocal(_) => "PUSH_LOCAL", + Inst::Pop(_) => "POP", + Inst::GetAttr(_) => "GET_ATTR", + Inst::SetAttr(_) => "SET_ATTR", + Inst::Jump(_) => "JUMP", + Inst::JumpTrue(_) => "JUMP_TRUE", + Inst::Call(_) => "CALL", + Inst::Index => "INDEX", + Inst::Return => "RETURN", + Inst::UnNeg => "UN_NEG", + Inst::UnPos => "UN_POS", + Inst::BinPlus => "BIN_PLUS", + Inst::BinMinus => "BIN_MINUS", + Inst::BinMul => "BIN_MUL", + Inst::BinDiv => "BIN_DIV", + Inst::BinEq => "BIN_EQ", + Inst::BinNeq => "BIN_NEQ", + Inst::BinLt => "BIN_LT", + Inst::BinLe => "BIN_LE", + Inst::BinGt => "BIN_GT", + Inst::BinGe => "BIN_GE", + Inst::BinAnd => "BIN_AND", + Inst::BinOr => "BIN_OR", + } + } +} + +pub fn dump_inst_body(insts: &Vec, const_pool: &ConstPool) { + println!("{}", InstFormatter::new(insts, const_pool)) +} + +// +// struct InstFormatter +// + +pub struct InstFormatter<'i, 'c> { + insts: &'i Vec, + const_pool: &'c ConstPool, +} + +impl<'i, 'c> InstFormatter<'i, 'c> { + pub fn new(insts: &'i Vec, const_pool: &'c ConstPool) -> Self { + Self { insts, const_pool, } + } +} + +impl Display for InstFormatter<'_, '_> { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { + // column widths + let addr_w = num_digits(self.insts.len(), 16).max(4); + let inst_col = 16 - addr_w; + let inst_w = 16; + + for (addr, inst) in self.insts.iter().enumerate() { + let (param_val, mut param_name) = match inst { + Inst::PushSym(sym) | Inst::GetAttr(sym) | Inst::SetAttr(sym) => ( + sym.index().to_string(), + global_sym_lookup(*sym).unwrap().to_string(), + ), + Inst::PushConst(hdl) => ( + hdl.index().to_string(), + { + let obj_ref = self.const_pool.get(*hdl); + read_obj!(let obj = obj_ref); + // XXX weirdness with coercion, can't deref as a &dyn Obj because + // RwReadLockGuard is not Obj - but using Deref::deref works + let obj: &dyn Obj = std::ops::Deref::deref(obj); + format!("{:?}", obj) + }, + ), + Inst::PushLocal(local) => ( + local.index().to_string(), + "TODO: local name".to_string(), + ), + Inst::Pop(local) => ( + format!("{:?}", local), + "TODO: local name".to_string(), + ), + Inst::Jump(addr) | Inst::JumpTrue(addr) => ( + addr.to_string(), + String::new(), + ), + Inst::Call(argc) => ( + argc.to_string(), + String::new(), + ), + _ => (String::new(), String::new()), + }; + + if !param_name.is_empty() { + param_name = format!("({})", param_name); + } + + writeln!( + fmt, + "{:0addr_w$x}{space: >inst_col$}{: usize { + ((n as f64) + 1.0).log(radix as f64).ceil() as usize +}