Add vm:📦:Package, instruction disassembly
* Code is compiled into a vm:📦:Package which contains the executable code, the constants, and the local name mappings. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -32,14 +32,16 @@ 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 package = Compile::new().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);
|
{
|
||||||
|
let mut stdout = std::io::stdout();
|
||||||
|
package.dump(&mut stdout)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ pub mod error;
|
|||||||
mod locals;
|
mod locals;
|
||||||
pub mod thunk;
|
pub mod thunk;
|
||||||
|
|
||||||
use crate::{syn::ast::Body, obj::prelude::*, vm::{consts::*, inst::Inst}};
|
use crate::{syn::ast::Body, obj::prelude::*, vm::{consts::*, package::Package}};
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -22,12 +22,12 @@ impl Compile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compiles the given AST body.
|
/// Compiles the given AST body.
|
||||||
pub fn compile<'c>(&'c mut self, body: &Body) -> error::Result<(Vec<Inst>, &'c ConstPool)> {
|
pub fn compile(mut self, body: &Body) -> error::Result<Package> {
|
||||||
let main = thunk::CompileBody::new(self)
|
let main = thunk::CompileBody::new(&mut self)
|
||||||
.compile(body)?
|
.compile(body)?
|
||||||
.flatten()
|
.flatten()
|
||||||
.to_vec();
|
.to_vec();
|
||||||
Ok((main, &self.const_data.const_pool))
|
Ok(Package::new(self.names, self.const_data.const_pool, main))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the constant data that is interned in this compile session.
|
/// Gets the constant data that is interned in this compile session.
|
||||||
@@ -119,6 +119,9 @@ impl Compile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constant data pool used for building the const pool.
|
||||||
|
///
|
||||||
|
/// This distinguishes between the different types while building a const pool to avoid duplicates.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct ConstData {
|
pub struct ConstData {
|
||||||
ints: HashMap<i64, (ConstHandle, IntRef)>,
|
ints: HashMap<i64, (ConstHandle, IntRef)>,
|
||||||
|
|||||||
@@ -25,5 +25,5 @@
|
|||||||
\. "."
|
\. "."
|
||||||
, ","
|
, ","
|
||||||
|
|
||||||
#.+$ ;
|
#[^\n]*$ ;
|
||||||
[\t ]+ ;
|
[\t ]+ ;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::{obj::prelude::*, vm::consts::*};
|
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 {
|
||||||
@@ -145,90 +144,3 @@ impl Inst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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::LoadName(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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,20 +2,21 @@ pub mod consts;
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod frame;
|
pub mod frame;
|
||||||
pub mod inst;
|
pub mod inst;
|
||||||
|
pub mod package;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
obj::{reserved::*, prelude::*},
|
obj::{reserved::*, prelude::*},
|
||||||
vm::{consts::*, error::*, frame::*, inst::*},
|
vm::{error::*, frame::*, inst::*, package::*},
|
||||||
};
|
};
|
||||||
use shredder::{GcSafe, Scanner, Scan};
|
use shredder::{GcSafe, Scanner, Scan};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Vm<'c> {
|
pub struct Vm<'p> {
|
||||||
stack: Vec<ObjRef>,
|
stack: Vec<ObjRef>,
|
||||||
frames: Vec<Frame>,
|
frames: Vec<Frame>,
|
||||||
pc: usize,
|
pc: usize,
|
||||||
condition: bool,
|
condition: bool,
|
||||||
const_pool: &'c ConstPool,
|
package: &'p Package,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Scan for Vm<'_> {
|
unsafe impl Scan for Vm<'_> {
|
||||||
@@ -27,17 +28,22 @@ unsafe impl Scan for Vm<'_> {
|
|||||||
|
|
||||||
unsafe impl GcSafe for Vm<'_> {}
|
unsafe impl GcSafe for Vm<'_> {}
|
||||||
|
|
||||||
impl<'c> Vm<'c> {
|
impl<'p> Vm<'p> {
|
||||||
pub fn new(const_pool: &'c ConstPool) -> Self {
|
pub fn new(package: &'p Package) -> Self {
|
||||||
Self {
|
Self {
|
||||||
stack: Default::default(),
|
stack: Default::default(),
|
||||||
frames: vec![],
|
frames: vec![],
|
||||||
pc: 0,
|
pc: 0,
|
||||||
condition: false,
|
condition: false,
|
||||||
const_pool,
|
package,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the package that is currently loaded by this VM.
|
||||||
|
pub fn package(&self) -> &'p Package {
|
||||||
|
self.package
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the current stack frame, if any.
|
/// Gets the current stack frame, if any.
|
||||||
pub fn frame(&self) -> Option<&Frame> {
|
pub fn frame(&self) -> Option<&Frame> {
|
||||||
self.frames.last()
|
self.frames.last()
|
||||||
@@ -126,7 +132,10 @@ impl<'c> Vm<'c> {
|
|||||||
self.push(sym_ref);
|
self.push(sym_ref);
|
||||||
}
|
}
|
||||||
Inst::PushConst(hdl) => {
|
Inst::PushConst(hdl) => {
|
||||||
let obj_ref = self.const_pool.get(hdl).clone();
|
let obj_ref = self.package()
|
||||||
|
.const_pool()
|
||||||
|
.get(hdl)
|
||||||
|
.clone();
|
||||||
self.push(obj_ref);
|
self.push(obj_ref);
|
||||||
}
|
}
|
||||||
Inst::LoadName(_sym) => todo!(),
|
Inst::LoadName(_sym) => todo!(),
|
||||||
|
|||||||
105
src/vm/package.rs
Normal file
105
src/vm/package.rs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
use crate::{obj::prelude::*, vm::{consts::ConstPool, inst::Inst}};
|
||||||
|
use std::io::{self, Write};
|
||||||
|
|
||||||
|
/// A compiled package that can be executed by a VM.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Package {
|
||||||
|
names: Vec<Sym>, // local names mappings
|
||||||
|
const_pool: ConstPool,
|
||||||
|
code: Vec<Inst>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package {
|
||||||
|
/// Creates a new VM executable package with the given local name mappings, constant pool, and
|
||||||
|
/// executable code.
|
||||||
|
pub fn new(names: Vec<Sym>, const_pool: ConstPool, code: Vec<Inst>) -> Self {
|
||||||
|
Self { names, const_pool, code, }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the local name mappings for this package.
|
||||||
|
pub fn names(&self) -> &Vec<Sym> {
|
||||||
|
&self.names
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the constant pool that is used by this package.
|
||||||
|
pub fn const_pool(&self) -> &ConstPool {
|
||||||
|
&self.const_pool
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the executable code that was compiled for this package.
|
||||||
|
pub fn code(&self) -> &Vec<Inst> {
|
||||||
|
&self.code
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dump(&self, writer: &mut dyn Write) -> io::Result<()> {
|
||||||
|
// column widths
|
||||||
|
let addr_w = num_digits(self.code().len(), 16).max(4);
|
||||||
|
let inst_col = 16 - addr_w;
|
||||||
|
let inst_w = 16;
|
||||||
|
|
||||||
|
for (addr, inst) in self.code().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::LoadName(local) => (
|
||||||
|
local.index().to_string(),
|
||||||
|
global_sym_lookup(self.names()[local.index()]).unwrap().to_string(),
|
||||||
|
),
|
||||||
|
Inst::Pop(local) => {
|
||||||
|
if let Some(local) = local {
|
||||||
|
let index = local.index();
|
||||||
|
let sym = self.names()[index];
|
||||||
|
let name = global_sym_lookup(sym).unwrap().to_string();
|
||||||
|
(index.to_string(), name)
|
||||||
|
} else {
|
||||||
|
("(discarded)".to_string(), String::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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!(
|
||||||
|
writer,
|
||||||
|
"{:0addr_w$x}{space: >inst_col$}{: <inst_w$}{: <4} {}",
|
||||||
|
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