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 <alekratz@gmail.com>
This commit is contained in:
@@ -32,13 +32,14 @@ fn main() -> Result<()> {
|
|||||||
return Err("errors reported, exiting".into());
|
return Err("errors reported, exiting".into());
|
||||||
}
|
}
|
||||||
}.unwrap();
|
}.unwrap();
|
||||||
//println!("{:#?}", ast);
|
println!("{:#?}", ast);
|
||||||
|
|
||||||
let mut compile = Compile::new();
|
let mut compile = Compile::new();
|
||||||
let (inst, const_pool) = compile.compile(&ast)?;
|
let (inst, const_pool) = compile.compile(&ast)?;
|
||||||
|
|
||||||
println!("{:#?}", inst);
|
//println!("{:#?}", inst);
|
||||||
println!("{:#?}", const_pool);
|
//println!("{:#?}", const_pool);
|
||||||
|
not_python::vm::inst::dump_inst_body(&inst, &const_pool);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ pub struct Compile {
|
|||||||
const_data: ConstData,
|
const_data: ConstData,
|
||||||
globals: BTreeMap<Sym, Local>,
|
globals: BTreeMap<Sym, Local>,
|
||||||
locals: Vec<BTreeMap<Sym, Local>>,
|
locals: Vec<BTreeMap<Sym, Local>>,
|
||||||
|
names: Vec<Sym>,
|
||||||
next_local: Local,
|
next_local: Local,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +64,15 @@ impl Compile {
|
|||||||
.copied()
|
.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.
|
/// 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 {
|
pub(crate) fn create_local(&mut self, sym: Sym) -> Local {
|
||||||
let locals = self.locals.last_mut().expect("scope");
|
let locals = self.locals.last_mut().expect("scope");
|
||||||
@@ -73,6 +83,8 @@ impl Compile {
|
|||||||
let local = self.next_local;
|
let local = self.next_local;
|
||||||
self.next_local = self.next_local.next();
|
self.next_local = self.next_local.next();
|
||||||
locals.insert(sym, local);
|
locals.insert(sym, local);
|
||||||
|
self.names.push(sym);
|
||||||
|
assert_eq!(self.names.len(), self.next_local.index());
|
||||||
local
|
local
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,6 +97,8 @@ impl Compile {
|
|||||||
let global = self.next_local;
|
let global = self.next_local;
|
||||||
self.next_local = self.next_local.next();
|
self.next_local = self.next_local.next();
|
||||||
self.globals.insert(sym, global);
|
self.globals.insert(sym, global);
|
||||||
|
self.names.push(sym);
|
||||||
|
assert_eq!(self.names.len(), self.next_local.index());
|
||||||
global
|
global
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -264,8 +264,9 @@ impl Visit for CompileBody<'_> {
|
|||||||
let attr = global_sym(expr.access.to_string());
|
let attr = global_sym(expr.access.to_string());
|
||||||
thunk.push(Inst::SetAttr(attr));
|
thunk.push(Inst::SetAttr(attr));
|
||||||
}
|
}
|
||||||
LhsExpr::Local(local) => {
|
LhsExpr::Local(local_name) => {
|
||||||
let local = global_sym(local.to_string());
|
let sym = global_sym(local_name.to_string());
|
||||||
|
let local = self.compile.lookup_scope_or_create_global(sym);
|
||||||
thunk = Inst::Pop(Some(local)).into();
|
thunk = Inst::Pop(Some(local)).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -320,6 +321,7 @@ impl Visit for CompileBody<'_> {
|
|||||||
for arg in expr.args.iter() {
|
for arg in expr.args.iter() {
|
||||||
thunk.push_thunk(self.visit_expr(&arg)?);
|
thunk.push_thunk(self.visit_expr(&arg)?);
|
||||||
}
|
}
|
||||||
|
thunk.push(Inst::Call(expr.args.len()));
|
||||||
Ok(thunk)
|
Ok(thunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,13 +349,7 @@ impl Visit for CompileBody<'_> {
|
|||||||
let thunk = match atom {
|
let thunk = match atom {
|
||||||
Atom::Ident(ident) => {
|
Atom::Ident(ident) => {
|
||||||
let sym = global_sym(ident.to_string());
|
let sym = global_sym(ident.to_string());
|
||||||
let local = if let Some(local) = self.compile.lookup_scope(sym) {
|
let local = self.compile.lookup_scope_or_create_global(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)
|
|
||||||
};
|
|
||||||
// get local
|
// get local
|
||||||
Inst::PushLocal(local).into()
|
Inst::PushLocal(local).into()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::obj::{intern::Interner, names::*, prelude::*};
|
use crate::obj::{intern::Interner, names::*, prelude::*};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::{collections::BTreeMap, sync::Mutex};
|
use std::{collections::BTreeMap, sync::{Arc, Mutex}};
|
||||||
|
|
||||||
//
|
//
|
||||||
// struct Sym
|
// struct Sym
|
||||||
@@ -93,6 +93,12 @@ pub fn global_sym(s: String) -> Sym {
|
|||||||
table.insert(s)
|
table.insert(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the pointer to a name from a global symbol.
|
||||||
|
pub fn global_sym_lookup(sym: Sym) -> Option<Arc<String>> {
|
||||||
|
let table = SYMS.lock().unwrap();
|
||||||
|
table.lookup(sym)
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// SymTable interner types
|
// SymTable interner types
|
||||||
//
|
//
|
||||||
|
|||||||
127
src/vm/inst.rs
127
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)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
pub enum Inst {
|
pub enum Inst {
|
||||||
@@ -12,7 +13,7 @@ pub enum Inst {
|
|||||||
PushLocal(Local),
|
PushLocal(Local),
|
||||||
|
|
||||||
/// Pop a value from the stack, possibly into a local symbol.
|
/// Pop a value from the stack, possibly into a local symbol.
|
||||||
Pop(Option<Sym>),
|
Pop(Option<Local>),
|
||||||
|
|
||||||
/// Pops a symbol value and an object reference.
|
/// Pops a symbol value and an object reference.
|
||||||
///
|
///
|
||||||
@@ -109,3 +110,125 @@ pub enum Inst {
|
|||||||
/// pushing the result to the stack.
|
/// pushing the result to the stack.
|
||||||
BinOr,
|
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<Inst>, const_pool: &ConstPool) {
|
||||||
|
println!("{}", InstFormatter::new(insts, const_pool))
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// struct InstFormatter
|
||||||
|
//
|
||||||
|
|
||||||
|
pub struct InstFormatter<'i, 'c> {
|
||||||
|
insts: &'i Vec<Inst>,
|
||||||
|
const_pool: &'c ConstPool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, 'c> InstFormatter<'i, 'c> {
|
||||||
|
pub fn new(insts: &'i Vec<Inst>, 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$}{: <inst_w$}{} {}",
|
||||||
|
addr,
|
||||||
|
inst.name(),
|
||||||
|
param_val,
|
||||||
|
param_name,
|
||||||
|
space = "",
|
||||||
|
addr_w = addr_w,
|
||||||
|
inst_w = inst_w,
|
||||||
|
inst_col = inst_col,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_digits(n: usize, radix: usize) -> usize {
|
||||||
|
((n as f64) + 1.0).log(radix as f64).ceil() as usize
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user