Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring Virtual Machine #7

Open
wants to merge 8 commits into
base: refactor-cleanup
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = [
"crates/vuur_lexer",
"crates/vuur_parse",
"crates/vuur_compile",
"crates/vuur_compiler",
# "crates/vuur_compiler",
"crates/vuur_vm",
"crates/vuur",
]
12 changes: 12 additions & 0 deletions crates/vuur_vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,21 @@ edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[[bench]]
name = "vm"
harness = false

[dev-dependencies]
criterion = "0.5"

[dependencies]
num = "0.4"
vuur_compile = { path = "../vuur_compile" }
vuur_parse = { path = "../vuur_parse" }

# Dynamic Objects
bytemuck = "1.13"

[features]
# Prints opcode instructions as they are interpreted.
trace_ops = []
101 changes: 101 additions & 0 deletions crates/vuur_vm/benches/vm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use std::rc::Rc;

use criterion::{black_box, criterion_group, criterion_main, Criterion};

use vuur_vm::handle::Handle;
use vuur_vm::instruction_set::{Arg24, Op};
use vuur_vm::value::{Closure, ConstantId, GlobalId, LocalId, Module, Program, ScriptFunc, Value};
use vuur_vm::vm_v2::VM;

/// Create a recursive fibonacci script function.
fn fibonacci(module: Handle<Module>) -> Rc<ScriptFunc> {
// func fib(n: Int) -> Int {
// if n <= 1 {
// return n
// } else {
// return fib(n - 1) + fib(n - 2)
// }
// }
let fib = GlobalId::new(0);
let n = LocalId::new(0);
let code = vec![
Op::Load_Local(n),
Op::I32_Const_Inline(Arg24::from_i32(1)),
Op::I32_LessEq,
Op::Jump_False {
addr: Arg24::from_u32(6),
},
Op::Load_Local(n),
Op::Return,
// Setup call to fib(n)
Op::Load_Global(fib),
// n - 1
Op::Load_Local(n),
Op::I32_Const_Inline(Arg24::from_i32(1)),
Op::I32_Sub,
Op::Call_Closure { arity: 1 },
// Setup call to fib(n)
Op::Load_Global(fib),
// n - 2
Op::Load_Local(n),
Op::I32_Const_Inline(Arg24::from_i32(2)),
Op::I32_Sub,
Op::Call_Closure { arity: 1 },
// fib(n - 1) + fib(n - 2)
Op::I32_Add,
Op::Return,
];

Rc::new(ScriptFunc {
constants: vec![],
code: code.into_boxed_slice(),
module: module.downgrade(),
})
}

pub fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("fib 20", |b| {
let fib_arg_1 = 10;

let module = Handle::new(Module::new("__main__"));

// Global variable slots would be determined by top-level `var` and `func` statements.
for _ in 0..1 {
module.borrow_mut().vars.push(Value::Nil);
}

let fib_func = fibonacci(module.clone());

let code = vec![
// func fib(n: Int) -> Int:
Op::Closure(ConstantId::new(0)), // create closure
Op::Store_Global(GlobalId::new(0)), // Store closure in variable
// fib(5)
Op::Load_Global(GlobalId::new(0)), // Load closure from variable
Op::I32_Const_Inline(Arg24::from_i32(fib_arg_1)),
Op::Call_Closure { arity: 1 },
Op::Return,
Op::End,
];

// Module top-level code.
let func = Rc::new(ScriptFunc {
constants: vec![
Value::Func(fib_func), // ConstantId(0)
],
code: code.into_boxed_slice(),
module: module.downgrade(),
});

let closure = Handle::new(Closure::new(func));
let program = Program::new(module, closure);

// ---------------------------------------------------------------------------------------------
let mut vm = VM::new();

b.iter(|| vm.run_program(black_box(&program)))
});
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
35 changes: 35 additions & 0 deletions crates/vuur_vm/src/core.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! Core module.
use crate::handle::Handle;
use crate::store::Store;
use crate::value::{Env, Module, NativeFunc, NativeFuncPtr, Value, Value::Int};

/// Initialize the built-in types and function of the core module.
pub fn init_core(store: &mut Store, module: &mut Module) {
bind_method(store, module, "static max(_,_)", int32_max);
bind_method(store, module, "static print(_)", system_print);
}

fn bind_method(store: &mut Store, module: &mut Module, sig: &str, ptr: NativeFuncPtr) {
// TODO: Get or insert function signature into store
// Arity from signature
let arity = sig.chars().filter(|c| *c == '_').count() as u8;
// Save native func as global variable
module.vars.push(Value::Native(Handle::new(NativeFunc { ptr, arity })));
store.methods.push(sig.to_string());
}

/// Returns the maximum integer.
fn int32_max(_env: Env, args: &[Value]) -> Result<Value, String> {
match args {
&[Int(a), Int(b)] => Ok(Int(a.max(b))),
_ => Err("unexpected types".to_string()),
}
}

// ----------------------------------------------------------------------------
// System

fn system_print(_env: Env, args: &[Value]) -> Result<Value, String> {
println!("{}", args[0].repr());
Ok(Value::Nil)
}
62 changes: 62 additions & 0 deletions crates/vuur_vm/src/handle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::cell::RefCell;
pub use std::cell::{Ref, RefMut};
use std::fmt;
use std::fmt::Formatter;
use std::rc::Rc;
pub use std::rc::Weak;

/// Shared reference counted handle
pub struct Handle<T: ?Sized>(Rc<RefCell<T>>);

impl<T> Handle<T> {
#[inline(always)]
pub fn new(value: T) -> Self {
Self(Rc::new(RefCell::new(value)))
}

#[inline(always)]
pub fn borrow(&self) -> Ref<'_, T> {
self.0.borrow()
}

#[inline(always)]
pub fn borrow_mut(&self) -> RefMut<'_, T> {
self.0.borrow_mut()
}

#[inline(always)]
pub fn try_borrow(&self) -> Option<Ref<'_, T>> {
self.0.try_borrow().ok()
}

#[inline(always)]
pub fn try_borrow_mut(&self) -> Option<RefMut<'_, T>> {
self.0.try_borrow_mut().ok()
}

#[inline(always)]
pub fn downgrade(&self) -> Weak<RefCell<T>> {
Rc::downgrade(&self.0)
}
}

impl<T: ?Sized> Clone for Handle<T> {
#[inline(always)]
fn clone(&self) -> Self {
Self(self.0.clone())
}
}

impl<T> fmt::Debug for Handle<T>
where
T: ?Sized + fmt::Debug,
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut debug = f.debug_tuple("Handle");

match self.0.try_borrow() {
Ok(value) => debug.field(&&*value).finish(),
Err(_) => debug.field(&"_").finish(),
}
}
}
Loading
Loading