Replace VsPointer with cached decoding

This commit is contained in:
Andrew Morris
2023-05-26 17:50:35 +10:00
parent 4fb30d68be
commit ac868be23c
12 changed files with 113 additions and 224 deletions

View File

@@ -1,6 +1,5 @@
use std::{
collections::{HashMap, HashSet},
rc::Rc,
str::FromStr,
};
@@ -13,7 +12,7 @@ use crate::asm::{
Label, LabelRef, Lazy, Module, Object, Pointer, Register, Value,
};
pub fn assemble(module: &Module) -> Rc<Vec<u8>> {
pub fn assemble(module: &Module) -> Vec<u8> {
let mut assembler = Assembler {
output: Vec::new(),
fn_data: Default::default(),
@@ -25,8 +24,7 @@ pub fn assemble(module: &Module) -> Rc<Vec<u8>> {
assembler.module(module);
// TODO: Don't use Rc
return Rc::new(assembler.output);
assembler.output
}
struct Assembler {

View File

@@ -1,11 +1,11 @@
use std::{process::exit, rc::Rc};
use valuescript_vm::VirtualMachine;
use valuescript_vm::{Bytecode, VirtualMachine};
pub fn main() {
let mut vm = VirtualMachine::new();
let result = vm.run(
&Rc::new(vec![
Rc::new(Bytecode::new(vec![
//
// This is the compiled bytecode for inputs/passing/projEuler/p28.ts.
//
@@ -46,7 +46,7 @@ pub fn main() {
0x05, 0x02, 0x06, 0x06, 0x03, 0x0e, 0x04, 0x06, 0x05, 0x0e, 0x02, 0x0e, 0x06, 0x07, 0x04,
0x0e, 0x07, 0x0e, 0x03, 0x06, 0x01, 0x09, 0x0e, 0x04, 0x0e, 0x05, 0x0e, 0x06, 0x00, 0x00,
0x00, 0x0b, 0x05, 0x02, 0x04, 0x0e, 0x02, 0x0e, 0x03, 0x00, 0x00,
]),
])),
None,
&[],
);

View File

@@ -0,0 +1,44 @@
use std::{cell::RefCell, collections::HashMap, fmt, ops::Index, rc::Rc, slice::SliceIndex};
use crate::{bytecode_decoder::BytecodeDecoder, vs_value::Val};
pub struct Bytecode {
pub code: Vec<u8>,
pub cache: RefCell<HashMap<usize, Val>>,
}
impl<I: SliceIndex<[u8]>> Index<I> for Bytecode {
type Output = I::Output;
fn index(&self, index: I) -> &Self::Output {
&self.code[index]
}
}
impl fmt::Debug for Bytecode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Bytecode {{ code: {:?} }}", self.code)
}
}
impl Bytecode {
pub fn new(code: Vec<u8>) -> Bytecode {
Bytecode {
code,
cache: RefCell::new(HashMap::new()),
}
}
}
pub trait DecoderMaker {
fn decoder(&self, pos: usize) -> BytecodeDecoder;
}
impl DecoderMaker for Rc<Bytecode> {
fn decoder(&self, pos: usize) -> BytecodeDecoder {
BytecodeDecoder {
bytecode: self.clone(),
pos,
}
}
}

View File

@@ -6,17 +6,17 @@ use num_bigint::Sign;
use valuescript_common::InstructionByte;
use crate::builtins::BUILTIN_VALS;
use crate::bytecode::Bytecode;
use crate::vs_class::VsClass;
use crate::vs_function::VsFunction;
use crate::vs_object::VsObject;
use crate::vs_pointer::VsPointer;
use crate::vs_symbol::VsSymbol;
use crate::vs_value::ToVal;
use crate::vs_value::Val;
pub struct BytecodeDecoder {
// TODO: Enable borrow usage to avoid the rc overhead
pub data: Rc<Vec<u8>>,
pub bytecode: Rc<Bytecode>,
pub pos: usize,
}
@@ -74,13 +74,13 @@ impl BytecodeType {
impl BytecodeDecoder {
pub fn decode_byte(&mut self) -> u8 {
let byte = self.data[self.pos];
let byte = self.bytecode[self.pos];
self.pos += 1;
return byte;
}
pub fn peek_byte(&self) -> u8 {
return self.data[self.pos];
return self.bytecode[self.pos];
}
pub fn decode_type(&mut self) -> BytecodeType {
@@ -138,7 +138,7 @@ impl BytecodeDecoder {
.to_val()
}
BytecodeType::Function => self.decode_function_header(),
BytecodeType::Pointer => self.decode_pointer(),
BytecodeType::Pointer => self.decode_pointer(registers),
BytecodeType::Register => match registers[self.decode_register_index().unwrap()].clone() {
Val::Void => Val::Undefined,
val => val,
@@ -155,7 +155,7 @@ impl BytecodeDecoder {
}
pub fn decode_signed_byte(&mut self) -> i8 {
let res = self.data[self.pos] as i8;
let res = self.bytecode[self.pos] as i8;
self.pos += 1;
return res;
}
@@ -163,7 +163,7 @@ impl BytecodeDecoder {
pub fn decode_number(&mut self) -> f64 {
let mut buf = [0u8; 8];
let next_pos = self.pos + 8;
buf.clone_from_slice(&self.data[self.pos..next_pos]);
buf.clone_from_slice(&self.bytecode[self.pos..next_pos]);
self.pos = next_pos;
return f64::from_le_bytes(buf);
}
@@ -178,7 +178,7 @@ impl BytecodeDecoder {
};
let len = self.decode_varsize_uint();
let bytes = &self.data[self.pos..self.pos + len];
let bytes = &self.bytecode[self.pos..self.pos + len];
self.pos += len;
return BigInt::from_bytes_le(sign, bytes);
@@ -188,7 +188,7 @@ impl BytecodeDecoder {
let len = self.decode_varsize_uint();
let start = self.pos; // Start after decoding varsize
let end = self.pos + len;
let res = String::from_utf8_lossy(&self.data[start..end]).into_owned();
let res = String::from_utf8_lossy(&self.bytecode[start..end]).into_owned();
self.pos = end;
return res;
@@ -229,12 +229,12 @@ impl BytecodeDecoder {
pub fn clone_at(&self, pos: usize) -> BytecodeDecoder {
return BytecodeDecoder {
data: self.data.clone(),
pos: pos,
bytecode: self.bytecode.clone(),
pos,
};
}
pub fn decode_pointer(&mut self) -> Val {
pub fn decode_pointer(&mut self, registers: &Vec<Val>) -> Val {
let from_pos = self.pos;
let pos = self.decode_pos();
@@ -250,7 +250,22 @@ impl BytecodeDecoder {
}
}
VsPointer::new(&self.data, pos).to_val()
let cached_val = self
.bytecode
.cache
.borrow()
.get(&pos)
.map(|val| val.clone());
match cached_val {
Some(val) => val,
None => {
let val = self.clone_at(pos).decode_val(registers);
self.bytecode.cache.borrow_mut().insert(pos, val.clone());
val
}
}
}
pub fn decode_function_header(&mut self) -> Val {
@@ -259,7 +274,7 @@ impl BytecodeDecoder {
let parameter_count = self.decode_byte() as usize;
return VsFunction {
bytecode: self.data.clone(),
bytecode: self.bytecode.clone(),
register_count,
parameter_count,
start: self.pos,

View File

@@ -1,6 +1,7 @@
mod array_higher_functions;
mod bigint_methods;
mod builtins;
mod bytecode;
mod bytecode_decoder;
mod bytecode_stack_frame;
mod first_stack_frame;
@@ -17,9 +18,9 @@ pub mod vs_array;
mod vs_class;
mod vs_function;
pub mod vs_object;
mod vs_pointer;
mod vs_symbol;
pub mod vs_value;
pub use bytecode::Bytecode;
pub use virtual_machine::VirtualMachine;
pub use vs_value::{LoadFunctionResult, ValTrait};

View File

@@ -1,7 +1,8 @@
use std::rc::Rc;
use crate::builtins::error_builtin::ToError;
use crate::bytecode_decoder::BytecodeDecoder;
use crate::bytecode::Bytecode;
use crate::bytecode::DecoderMaker;
use crate::first_stack_frame::FirstStackFrame;
use crate::stack_frame::FrameStepOk;
use crate::stack_frame::StackFrame;
@@ -15,14 +16,11 @@ pub struct VirtualMachine {
impl VirtualMachine {
pub fn run(
&mut self,
bytecode: &Rc<Vec<u8>>,
bytecode: Rc<Bytecode>,
step_limit: Option<usize>,
params: &[Val],
) -> Result<Val, Val> {
let mut bd = BytecodeDecoder {
data: bytecode.clone(),
pos: 0,
};
let mut bd = bytecode.decoder(0);
let main_fn = bd.decode_val(&Vec::new());
@@ -122,12 +120,7 @@ impl VirtualMachine {
Err(exception)
}
pub fn read_default_export(bytecode: &Rc<Vec<u8>>) -> Val {
let mut bd = BytecodeDecoder {
data: bytecode.clone(),
pos: 0,
};
bd.decode_val(&Vec::new())
pub fn read_default_export(bytecode: Rc<Bytecode>) -> Val {
bytecode.decoder(0).decode_val(&Vec::new())
}
}

View File

@@ -1,5 +1,6 @@
use std::rc::Rc;
use crate::bytecode::Bytecode;
use crate::vs_value::ToVal;
use super::bytecode_decoder::BytecodeDecoder;
@@ -9,7 +10,7 @@ use super::vs_value::Val;
#[derive(Debug)]
pub struct VsFunction {
pub bytecode: Rc<Vec<u8>>,
pub bytecode: Rc<Bytecode>,
pub register_count: usize,
pub parameter_count: usize,
pub start: usize,
@@ -49,7 +50,7 @@ impl VsFunction {
return Box::new(BytecodeStackFrame {
decoder: BytecodeDecoder {
data: self.bytecode.clone(),
bytecode: self.bytecode.clone(),
pos: self.start,
},
registers,

View File

@@ -1,169 +0,0 @@
use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;
use num_bigint::BigInt;
use crate::builtins::error_builtin::ToError;
use crate::vs_value::ToVal;
use super::bytecode_decoder::{BytecodeDecoder, BytecodeType};
use super::vs_array::VsArray;
use super::vs_class::VsClass;
use super::vs_object::VsObject;
use super::vs_value::{LoadFunctionResult, Val, ValTrait, VsType};
pub struct VsPointer {
bytecode: Rc<Vec<u8>>,
pos: usize,
resolved: RefCell<Option<Val>>,
}
impl VsPointer {
pub fn new(bytecode: &Rc<Vec<u8>>, pos: usize) -> VsPointer {
VsPointer {
bytecode: bytecode.clone(),
pos,
resolved: RefCell::new(None),
}
}
fn resolve(&self) -> Val {
let mut resolved = self.resolved.borrow_mut();
if resolved.is_some() {
return resolved.clone().unwrap();
}
let mut bd = BytecodeDecoder {
data: self.bytecode.clone(),
pos: self.pos,
};
let val = bd.decode_val(&Vec::new());
// TODO: Check that this actually inserts into the cell and not just a copy
// somehow
*resolved = Some(val.clone());
return val;
}
}
impl ValTrait for VsPointer {
fn typeof_(&self) -> VsType {
let mut bd = BytecodeDecoder {
data: self.bytecode.clone(),
pos: self.pos,
};
return match bd.decode_type() {
BytecodeType::End => panic!("Invalid: pointer to end"),
BytecodeType::Void => panic!("Invalid: pointer to void"),
BytecodeType::Undefined => VsType::Undefined,
BytecodeType::Null => VsType::Null,
BytecodeType::False => VsType::Bool,
BytecodeType::True => VsType::Bool,
BytecodeType::SignedByte => VsType::Number,
BytecodeType::Number => VsType::Number,
BytecodeType::String => VsType::String,
BytecodeType::Array => VsType::Array,
BytecodeType::Object => VsType::Object,
BytecodeType::Function => VsType::Function,
BytecodeType::Pointer => panic!("Invalid: pointer to pointer"),
BytecodeType::Register => panic!("Invalid: pointer to register"),
BytecodeType::Builtin => panic!("Invalid: pointer to builtin"),
BytecodeType::Class => VsType::Class,
BytecodeType::BigInt => VsType::BigInt,
BytecodeType::Unrecognized => panic!("Unrecognized bytecode type at {}", self.pos - 1),
};
}
fn to_number(&self) -> f64 {
return self.resolve().to_number();
}
fn to_index(&self) -> Option<usize> {
return self.resolve().to_index();
}
fn is_primitive(&self) -> bool {
return match self.typeof_() {
VsType::Undefined => true,
VsType::Null => true,
VsType::Bool => true,
VsType::Number => true,
VsType::BigInt => true,
VsType::Symbol => true,
VsType::String => true,
VsType::Array => false,
VsType::Object => false,
VsType::Function => false,
VsType::Class => false,
};
}
fn bind(&self, params: Vec<Val>) -> Option<Val> {
return self.resolve().bind(params);
}
fn is_truthy(&self) -> bool {
return self.resolve().is_truthy();
}
fn is_nullish(&self) -> bool {
return self.resolve().is_nullish();
}
fn as_bigint_data(&self) -> Option<BigInt> {
return self.resolve().as_bigint_data();
}
fn as_array_data(&self) -> Option<Rc<VsArray>> {
return self.resolve().as_array_data();
}
fn as_object_data(&self) -> Option<Rc<VsObject>> {
return self.resolve().as_object_data();
}
fn as_class_data(&self) -> Option<Rc<VsClass>> {
return self.resolve().as_class_data();
}
fn load_function(&self) -> LoadFunctionResult {
return self.resolve().load_function();
}
fn sub(&self, subscript: Val) -> Result<Val, Val> {
self.resolve().sub(subscript)
}
fn submov(&mut self, _subscript: Val, _value: Val) -> Result<(), Val> {
Err("TODO: Probably an exception, but might be possible".to_error())
}
fn next(&mut self) -> LoadFunctionResult {
self.resolve().next()
}
fn pretty_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.resolve().pretty_fmt(f)
}
fn codify(&self) -> String {
self.resolve().codify()
}
}
impl fmt::Display for VsPointer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.resolve().fmt(f)
}
}
impl ToVal for VsPointer {
fn to_val(self) -> Val {
Val::Custom(Rc::new(self))
}
}

View File

@@ -244,6 +244,7 @@ impl ValTrait for Val {
return match self {
Function(f) => Some(f.bind(params).to_val()),
Static(val) => val.bind(params),
Custom(val) => val.bind(params),
_ => None,

View File

@@ -1,4 +1,7 @@
use std::collections::{BTreeMap, HashMap};
use std::{
collections::{BTreeMap, HashMap},
rc::Rc,
};
use wasm_bindgen::prelude::*;
@@ -9,7 +12,7 @@ use valuescript_compiler::{
use valuescript_vm::{
vs_object::VsObject,
vs_value::{ToVal, Val},
LoadFunctionResult, ValTrait, VirtualMachine,
Bytecode, LoadFunctionResult, ValTrait, VirtualMachine,
};
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
@@ -108,9 +111,9 @@ fn run_to_result(entry_point: &str, read_file: &js_sys::Function, args: &str) ->
}
};
let bytecode = assemble(&module);
let bytecode = Rc::new(Bytecode::new(assemble(&module)));
match VirtualMachine::read_default_export(&bytecode).load_function() {
match VirtualMachine::read_default_export(bytecode.clone()).load_function() {
LoadFunctionResult::NotAFunction => {
return RunResult {
diagnostics: HashMap::default(),
@@ -132,7 +135,7 @@ fn run_to_result(entry_point: &str, read_file: &js_sys::Function, args: &str) ->
}
};
let vm_result = vm.run(&bytecode, None, &val_args);
let vm_result = vm.run(bytecode, None, &val_args);
RunResult {
diagnostics: HashMap::default(),

View File

@@ -1,9 +1,10 @@
use std::fs;
use std::rc::Rc;
use std::{ffi::OsStr, path::Path, process::exit};
use valuescript_compiler::{assemble, compile, parse_module};
use valuescript_vm::vs_value::Val;
use valuescript_vm::VirtualMachine;
use valuescript_vm::{Bytecode, VirtualMachine};
use crate::resolve_entry_path::resolve_entry_path;
@@ -35,7 +36,7 @@ pub fn run_command(args: &Vec<String>) {
let file_path = &args[argpos];
argpos += 1;
let bytecode = to_bytecode(format, file_path);
let bytecode = Rc::new(to_bytecode(format, file_path));
let mut vm = VirtualMachine::new();
@@ -44,7 +45,7 @@ pub fn run_command(args: &Vec<String>) {
.map(|a| Val::String(Rc::new(a.into())))
.collect();
match vm.run(&bytecode, None, &val_args) {
match vm.run(bytecode, None, &val_args) {
Ok(result) => {
println!("{}", result.pretty());
}
@@ -87,8 +88,8 @@ fn format_from_path(file_path: &String) -> RunFormat {
};
}
fn to_bytecode(format: RunFormat, file_path: &String) -> Rc<Vec<u8>> {
match format {
fn to_bytecode(format: RunFormat, file_path: &String) -> Bytecode {
Bytecode::new(match format {
RunFormat::TypeScript => {
let resolved_entry_path = resolve_entry_path(file_path);
@@ -115,10 +116,10 @@ fn to_bytecode(format: RunFormat, file_path: &String) -> Rc<Vec<u8>> {
assemble(&module)
}
RunFormat::Bytecode => Rc::new(
std::fs::read(file_path).unwrap_or_else(|_| panic!("Failed to read file {}", file_path)),
),
}
RunFormat::Bytecode => {
fs::read(file_path).unwrap_or_else(|_| panic!("Failed to read file {}", file_path))
}
})
}
fn show_help() {

View File

@@ -3,11 +3,12 @@ mod tests {
use std::collections::HashSet;
use std::fs;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use valuescript_compiler::compile;
use valuescript_compiler::{assemble, parse_module};
use valuescript_vm::ValTrait;
use valuescript_vm::VirtualMachine;
use valuescript_vm::{Bytecode, ValTrait};
use crate::handle_diagnostics_cli::handle_diagnostics_cli;
use crate::resolve_entry_path::resolve_entry_path;
@@ -70,20 +71,20 @@ mod tests {
.module
.expect("Should have exited if module is None");
let bytecode = assemble(&module);
let bytecode = Rc::new(Bytecode::new(assemble(&module)));
let assembly = module.to_string();
let parsed_assembly = parse_module(&assembly);
let bytecode_via_assembly = assemble(&parsed_assembly);
if bytecode != bytecode_via_assembly {
if bytecode.code != bytecode_via_assembly {
println!(" Bytecode mismatch between original and parsed assembly");
failed_paths.insert(file_path.clone());
}
let mut vm = VirtualMachine::new();
let result = vm.run(&bytecode, Some(2_000_000), &[]);
let result = vm.run(bytecode, Some(2_000_000), &[]);
let result_str = match result {
Ok(val) => val.codify(),