From a89b927a80877d31bdaf7926238b96110f38a850 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 30 Jan 2025 20:48:17 -0500 Subject: [PATCH] Begin value overhaul --- Cargo.lock | 4 + dust-lang/Cargo.toml | 1 + dust-lang/src/chunk/mod.rs | 39 +- dust-lang/src/lib.rs | 8 +- dust-lang/src/value/mod.rs | 230 +-------- dust-lang/src/vm/{run_action.rs => action.rs} | 16 +- dust-lang/src/vm/call_frame.rs | 73 +++ dust-lang/src/vm/function_call.rs | 44 -- dust-lang/src/vm/mod.rs | 53 +- dust-lang/src/vm/stack.rs | 137 ----- dust-lang/src/vm/thread.rs | 470 ++++++++++-------- 11 files changed, 390 insertions(+), 685 deletions(-) rename dust-lang/src/vm/{run_action.rs => action.rs} (98%) create mode 100644 dust-lang/src/vm/call_frame.rs delete mode 100644 dust-lang/src/vm/function_call.rs delete mode 100644 dust-lang/src/vm/stack.rs diff --git a/Cargo.lock b/Cargo.lock index f62c495..b599be5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -349,6 +349,7 @@ dependencies = [ "rand", "serde", "serde_json", + "smallvec", "smartstring", "tracing", ] @@ -805,6 +806,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "smartstring" diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index 96fedb5..a1f819b 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -22,6 +22,7 @@ smartstring = { version = "1.0.1", features = [ ], default-features = false } tracing = "0.1.41" crossbeam-channel = "0.5.14" +smallvec = { version = "1.13.2", features = ["serde", "const_generics"] } [dev-dependencies] criterion = { version = "0.3.4", features = ["html_reports"] } diff --git a/dust-lang/src/chunk/mod.rs b/dust-lang/src/chunk/mod.rs index 2944767..d4fde8c 100644 --- a/dust-lang/src/chunk/mod.rs +++ b/dust-lang/src/chunk/mod.rs @@ -27,7 +27,7 @@ use std::sync::Arc; use serde::{Deserialize, Serialize}; -use crate::{DustString, Function, FunctionType, Instruction, Span, Value}; +use crate::{DustString, Function, FunctionType, Instruction, Span}; /// Representation of a Dust program or function. /// @@ -39,8 +39,14 @@ pub struct Chunk { pub(crate) instructions: Vec, pub(crate) positions: Vec, - pub(crate) constants: Vec, + + pub(crate) constant_characters: Vec, + pub(crate) constant_floats: Vec, + pub(crate) constant_integers: Vec, + pub(crate) constant_strings: Vec, + pub(crate) locals: Vec, + pub(crate) prototypes: Vec>, pub(crate) register_count: usize, @@ -48,29 +54,6 @@ pub struct Chunk { } impl Chunk { - #[cfg(any(test, debug_assertions))] - pub fn with_data( - name: Option, - r#type: FunctionType, - instructions: impl Into>, - positions: impl Into>, - constants: impl Into>, - locals: impl Into>, - prototypes: impl IntoIterator, - ) -> Self { - Self { - name, - r#type, - instructions: instructions.into(), - positions: positions.into(), - constants: constants.into(), - locals: locals.into(), - prototypes: prototypes.into_iter().map(Arc::new).collect(), - register_count: 0, - prototype_index: 0, - } - } - pub fn as_function(&self) -> Function { Function { name: self.name.clone(), @@ -125,7 +108,11 @@ impl PartialEq for Chunk { self.name == other.name && self.r#type == other.r#type && self.instructions == other.instructions - && self.constants == other.constants + && self.positions == other.positions + && self.constant_characters == other.constant_characters + && self.constant_floats == other.constant_floats + && self.constant_integers == other.constant_integers + && self.constant_strings == other.constant_strings && self.locals == other.locals && self.prototypes == other.prototypes } diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index bc6cb03..69cde3d 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -42,15 +42,13 @@ pub mod vm; pub use crate::chunk::{Chunk, Disassembler, Local, Scope}; pub use crate::compiler::{CompileError, Compiler, compile}; pub use crate::dust_error::{AnnotatedError, DustError}; -pub use crate::instruction::{Operand, Instruction, Operation}; +pub use crate::instruction::{Instruction, Operand, Operation}; pub use crate::lexer::{LexError, Lexer, lex}; pub use crate::native_function::{NativeFunction, NativeFunctionError}; pub use crate::token::{Token, TokenKind, TokenOwned}; pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict}; -pub use crate::value::{ - AbstractList, ConcreteValue, DustString, Function, RangeValue, Value, ValueError, -}; -pub use crate::vm::{Pointer, Vm, run}; +pub use crate::value::{DustString, Function, RangeValue, Value, ValueError}; +pub use crate::vm::{Vm, run}; use std::fmt::Display; diff --git a/dust-lang/src/value/mod.rs b/dust-lang/src/value/mod.rs index b9b7d91..95bb59e 100644 --- a/dust-lang/src/value/mod.rs +++ b/dust-lang/src/value/mod.rs @@ -1,238 +1,40 @@ //! Runtime values used by the VM. -mod abstract_list; -mod concrete_value; mod function; mod range_value; -pub use abstract_list::AbstractList; -pub use concrete_value::{ConcreteValue, DustString}; pub use function::Function; pub use range_value::RangeValue; use serde::{Deserialize, Serialize}; +use smartstring::{LazyCompact, SmartString}; use std::fmt::{self, Debug, Display, Formatter}; -use crate::{Type, vm::ThreadData}; +pub type DustString = SmartString; #[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)] pub enum Value { - Concrete(ConcreteValue), - - #[serde(skip)] - AbstractList(AbstractList), + Boolean(bool), + Byte(u8), + Character(char), + Float(f64), + Integer(i64), + String(DustString), + List(Vec), #[serde(skip)] Function(Function), } -impl Value { - pub fn boolean(boolean: bool) -> Self { - Value::Concrete(ConcreteValue::Boolean(boolean)) - } - - pub fn byte(byte: u8) -> Self { - Value::Concrete(ConcreteValue::Byte(byte)) - } - - pub fn character(character: char) -> Self { - Value::Concrete(ConcreteValue::Character(character)) - } - - pub fn float(float: f64) -> Self { - Value::Concrete(ConcreteValue::Float(float)) - } - - pub fn integer(integer: i64) -> Self { - Value::Concrete(ConcreteValue::Integer(integer)) - } - - pub fn string(string: impl Into) -> Self { - Value::Concrete(ConcreteValue::String(string.into())) - } - - pub fn as_boolean(&self) -> Option { - if let Value::Concrete(ConcreteValue::Boolean(boolean)) = self { - Some(*boolean) - } else { - None - } - } - - pub fn as_byte(&self) -> Option { - if let Value::Concrete(ConcreteValue::Byte(byte)) = self { - Some(*byte) - } else { - None - } - } - - pub fn as_character(&self) -> Option { - if let Value::Concrete(ConcreteValue::Character(character)) = self { - Some(*character) - } else { - None - } - } - - pub fn as_float(&self) -> Option { - if let Value::Concrete(ConcreteValue::Float(float)) = self { - Some(*float) - } else { - None - } - } - - pub fn as_function(&self) -> Option<&Function> { - if let Value::Function(function) = self { - Some(function) - } else { - None - } - } - - pub fn as_integer(&self) -> Option { - if let Value::Concrete(ConcreteValue::Integer(integer)) = self { - Some(*integer) - } else { - None - } - } - - pub fn as_string(&self) -> Option<&DustString> { - if let Value::Concrete(ConcreteValue::String(value)) = self { - Some(value) - } else { - None - } - } - - pub fn is_string(&self) -> bool { - matches!(self, Value::Concrete(ConcreteValue::String(_))) - } - - pub fn is_function(&self) -> bool { - matches!(self, Value::Function(_)) - } - - pub fn r#type(&self) -> Type { - match self { - Value::Concrete(concrete_value) => concrete_value.r#type(), - Value::AbstractList(AbstractList { item_type, .. }) => { - Type::List(Box::new(item_type.clone())) - } - Value::Function(Function { r#type, .. }) => Type::Function(Box::new(r#type.clone())), - } - } - - pub fn add(&self, other: &Value) -> Value { - let sum = match (self, other) { - (Value::Concrete(left), Value::Concrete(right)) => left.add(right), - _ => panic!("{}", ValueError::CannotAdd(self.clone(), other.clone())), - }; - - Value::Concrete(sum) - } - - pub fn subtract(&self, other: &Value) -> Value { - let difference = match (self, other) { - (Value::Concrete(left), Value::Concrete(right)) => left.subtract(right), - _ => panic!( - "{}", - ValueError::CannotSubtract(self.clone(), other.clone()) - ), - }; - - Value::Concrete(difference) - } - - pub fn multiply(&self, other: &Value) -> Result { - match (self, other) { - (Value::Concrete(left), Value::Concrete(right)) => { - left.multiply(right).map(Value::Concrete) - } - _ => Err(ValueError::CannotMultiply( - self.to_owned(), - other.to_owned(), - )), - } - } - - pub fn divide(&self, other: &Value) -> Result { - match (self, other) { - (Value::Concrete(left), Value::Concrete(right)) => { - left.divide(right).map(Value::Concrete) - } - _ => Err(ValueError::CannotDivide(self.to_owned(), other.to_owned())), - } - } - - pub fn modulo(&self, other: &Value) -> Result { - match (self, other) { - (Value::Concrete(left), Value::Concrete(right)) => { - left.modulo(right).map(Value::Concrete) - } - _ => Err(ValueError::CannotModulo(self.to_owned(), other.to_owned())), - } - } - - pub fn negate(&self) -> Value { - let concrete = match self { - Value::Concrete(concrete_value) => concrete_value.negate(), - _ => panic!("{}", ValueError::CannotNegate(self.clone())), - }; - - Value::Concrete(concrete) - } - - pub fn not(&self) -> Result { - match self { - Value::Concrete(concrete_value) => concrete_value.not().map(Value::Concrete), - _ => Err(ValueError::CannotNot(self.to_owned())), - } - } - - pub fn equals(&self, other: &Value) -> bool { - match (self, other) { - (Value::Concrete(left), Value::Concrete(right)) => left.equals(right), - _ => panic!( - "{}", - ValueError::CannotCompare(self.to_owned(), other.to_owned()) - ), - } - } - - pub fn less(&self, other: &Value) -> Result { - match (self, other) { - (Value::Concrete(left), Value::Concrete(right)) => { - left.less_than(right).map(Value::Concrete) - } - _ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())), - } - } - - pub fn less_than_or_equals(&self, other: &Value) -> Result { - match (self, other) { - (Value::Concrete(left), Value::Concrete(right)) => { - left.less_than_or_equals(right).map(Value::Concrete) - } - _ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())), - } - } - - pub fn display(&self, data: &ThreadData) -> DustString { - match self { - Value::AbstractList(list) => list.display(data), - Value::Concrete(concrete_value) => concrete_value.display(), - Value::Function(function) => DustString::from(function.to_string()), - } - } -} - impl Display for Value { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - Value::Concrete(concrete_value) => write!(f, "{concrete_value}"), - Value::AbstractList(list) => write!(f, "{list}"), + Value::Boolean(boolean) => write!(f, "{boolean}"), + Value::Byte(byte) => write!(f, "{byte}"), + Value::Character(character) => write!(f, "{character}"), + Value::Float(float) => write!(f, "{float}"), + Value::Integer(integer) => write!(f, "{integer}"), + Value::String(string) => write!(f, "{string}"), + Value::List(list) => write!(f, "{list:?}"), Value::Function(function) => write!(f, "{function}"), } } diff --git a/dust-lang/src/vm/run_action.rs b/dust-lang/src/vm/action.rs similarity index 98% rename from dust-lang/src/vm/run_action.rs rename to dust-lang/src/vm/action.rs index 167419a..0a99caf 100644 --- a/dust-lang/src/vm/run_action.rs +++ b/dust-lang/src/vm/action.rs @@ -7,27 +7,27 @@ use crate::{ LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point, Return, SetLocal, Subtract, Test, TestSet, TypeCode, }, - vm::FunctionCall, + vm::CallFrame, }; use super::{Pointer, Register, thread::ThreadData}; #[derive(Clone, Copy, Debug, PartialEq)] -pub struct RunAction { +pub struct Action { pub logic: RunnerLogic, pub instruction: Instruction, } -impl From for RunAction { +impl From for Action { fn from(instruction: Instruction) -> Self { let operation = instruction.operation(); let logic = RUNNER_LOGIC_TABLE[operation.0 as usize]; - RunAction { logic, instruction } + Action { logic, instruction } } } -pub type RunnerLogic = fn(Instruction, &mut ThreadData) -> bool; +pub type RunnerLogic = fn(Instruction, &mut Thread) -> bool; pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [ point, @@ -57,7 +57,7 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [ r#return, ]; -pub(crate) fn get_next_action(data: &mut ThreadData) -> RunAction { +pub(crate) fn get_next_action(data: &mut ThreadData) -> Action { let current_call = data.call_stack.last_mut_unchecked(); let instruction = current_call.chunk.instructions[current_call.ip]; let operation = instruction.operation(); @@ -65,7 +65,7 @@ pub(crate) fn get_next_action(data: &mut ThreadData) -> RunAction { current_call.ip += 1; - RunAction { logic, instruction } + Action { logic, instruction } } pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool { @@ -782,7 +782,7 @@ pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool { current_call.chunk.prototypes[function.prototype_index as usize].clone() }; - let mut next_call = FunctionCall::new(prototype, return_register); + let mut next_call = CallFrame::new(prototype, return_register); let mut argument_index = 0; for register_index in first_argument_register..return_register { diff --git a/dust-lang/src/vm/call_frame.rs b/dust-lang/src/vm/call_frame.rs new file mode 100644 index 0000000..a1ea6f0 --- /dev/null +++ b/dust-lang/src/vm/call_frame.rs @@ -0,0 +1,73 @@ +use std::{ + fmt::{self, Debug, Display, Formatter}, + sync::Arc, +}; + +use smallvec::{SmallVec, smallvec}; + +use crate::{Chunk, DustString}; + +#[derive(Debug)] +pub struct CallFrame { + pub chunk: Arc, + pub ip: usize, + pub return_register: u16, + pub registers: RegisterTable, +} + +impl CallFrame { + pub fn new(chunk: Arc, return_register: u16) -> Self { + let register_count = chunk.register_count; + + Self { + chunk, + ip: 0, + return_register, + registers: RegisterTable::new(), + } + } +} + +impl Display for CallFrame { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!( + f, + "FunctionCall: {} | IP: {}", + self.chunk + .name + .as_ref() + .unwrap_or(&DustString::from("anonymous")), + self.ip, + ) + } +} + +#[derive(Debug)] +pub struct RegisterTable { + pub booleans: SmallVec<[Register; 64]>, + pub bytes: SmallVec<[Register; 64]>, + pub characters: SmallVec<[Register; 64]>, + pub floats: SmallVec<[Register; 64]>, + pub integers: SmallVec<[Register; 64]>, + pub strings: SmallVec<[Register; 64]>, +} + +impl RegisterTable { + pub fn new() -> Self { + Self { + booleans: smallvec![Register::Empty; 64], + bytes: smallvec![Register::Empty; 64], + characters: smallvec![Register::Empty; 64], + floats: smallvec![Register::Empty; 64], + integers: smallvec![Register::Empty; 64], + strings: smallvec![Register::Empty; 64], + } + } +} + +#[derive(Debug, Clone)] +pub enum Register { + Empty, + Value(T), + Pointer(*const T), +} diff --git a/dust-lang/src/vm/function_call.rs b/dust-lang/src/vm/function_call.rs deleted file mode 100644 index 7db37aa..0000000 --- a/dust-lang/src/vm/function_call.rs +++ /dev/null @@ -1,44 +0,0 @@ -use std::{ - fmt::{self, Debug, Display, Formatter}, - sync::Arc, -}; - -use crate::{Chunk, DustString}; - -use super::Register; - -#[derive(Debug)] -pub struct FunctionCall { - pub chunk: Arc, - pub ip: usize, - pub return_register: u16, - pub registers: Vec, -} - -impl FunctionCall { - pub fn new(chunk: Arc, return_register: u16) -> Self { - let register_count = chunk.register_count; - - Self { - chunk, - ip: 0, - return_register, - registers: vec![Register::Empty; register_count], - } - } -} - -impl Display for FunctionCall { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!( - f, - "FunctionCall: {} | IP: {} | Registers: {}", - self.chunk - .name - .as_ref() - .unwrap_or(&DustString::from("anonymous")), - self.ip, - self.registers.len() - ) - } -} diff --git a/dust-lang/src/vm/mod.rs b/dust-lang/src/vm/mod.rs index 0e44c78..b2cf91d 100644 --- a/dust-lang/src/vm/mod.rs +++ b/dust-lang/src/vm/mod.rs @@ -1,7 +1,6 @@ //! Virtual machine and errors -mod function_call; -mod run_action; -mod stack; +mod action; +mod call_frame; mod thread; use std::{ @@ -10,11 +9,10 @@ use std::{ thread::Builder, }; -pub use function_call::FunctionCall; -pub use run_action::RunAction; -pub(crate) use run_action::get_next_action; -pub use stack::Stack; -pub use thread::{Thread, ThreadData}; +pub use action::Action; +pub(crate) use action::get_next_action; +pub use call_frame::{CallFrame, Register, RegisterTable}; +pub use thread::Thread; use crossbeam_channel::bounded; use tracing::{Level, span}; @@ -46,12 +44,13 @@ impl Vm { .as_ref() .map(|name| name.to_string()) .unwrap_or_else(|| "anonymous".to_string()); - let mut main_thread = Thread::new(Arc::new(self.main_chunk)); let (tx, rx) = bounded(1); + let main_chunk = Arc::new(self.main_chunk); Builder::new() .name(thread_name) .spawn(move || { + let mut main_thread = Thread::new(main_chunk); let value_option = main_thread.run(); let _ = tx.send(value_option); }) @@ -62,39 +61,3 @@ impl Vm { rx.recv().unwrap_or(None) } } - -#[derive(Clone, Debug, PartialEq)] -pub enum Register { - Empty, - Value(Value), - Pointer(Pointer), -} - -impl Display for Register { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::Empty => write!(f, "empty"), - Self::Value(value) => write!(f, "{}", value), - Self::Pointer(pointer) => write!(f, "{}", pointer), - } - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub enum Pointer { - Register(u16), - Constant(u16), - Stack(usize, u16), -} - -impl Display for Pointer { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::Register(index) => write!(f, "PR{}", index), - Self::Constant(index) => write!(f, "PC{}", index), - Self::Stack(call_index, register_index) => { - write!(f, "PS{}R{}", call_index, register_index) - } - } - } -} diff --git a/dust-lang/src/vm/stack.rs b/dust-lang/src/vm/stack.rs deleted file mode 100644 index dd43253..0000000 --- a/dust-lang/src/vm/stack.rs +++ /dev/null @@ -1,137 +0,0 @@ -use std::{ - fmt::{self, Debug, Display, Formatter}, - ops::{Index, IndexMut, Range}, -}; - -use super::FunctionCall; - -#[derive(Clone, PartialEq)] -pub struct Stack { - items: Vec, -} - -impl Stack { - pub fn new() -> Self { - Stack { - items: Vec::with_capacity(1), - } - } - - pub fn with_capacity(capacity: usize) -> Self { - Stack { - items: Vec::with_capacity(capacity), - } - } - - pub fn is_empty(&self) -> bool { - self.items.is_empty() - } - - pub fn len(&self) -> usize { - self.items.len() - } - - pub fn get_unchecked(&self, index: usize) -> &T { - if cfg!(debug_assertions) { - assert!(index < self.len(), "Stack underflow"); - - &self.items[index] - } else { - unsafe { self.items.get_unchecked(index) } - } - } - - pub fn get_unchecked_mut(&mut self, index: usize) -> &mut T { - if cfg!(debug_assertions) { - assert!(index < self.len(), "Stack underflow"); - - &mut self.items[index] - } else { - unsafe { self.items.get_unchecked_mut(index) } - } - } - - pub fn push(&mut self, item: T) { - self.items.push(item); - } - - pub fn pop(&mut self) -> Option { - self.items.pop() - } - - pub fn last(&self) -> Option<&T> { - self.items.last() - } - - pub fn last_mut(&mut self) -> Option<&mut T> { - self.items.last_mut() - } - - pub fn pop_unchecked(&mut self) -> T { - if cfg!(debug_assertions) { - assert!(!self.is_empty(), "Stack underflow"); - - self.items.pop().unwrap() - } else { - unsafe { self.items.pop().unwrap_unchecked() } - } - } - - pub fn last_unchecked(&self) -> &T { - if cfg!(debug_assertions) { - assert!(!self.is_empty(), "Stack underflow"); - - self.items.last().unwrap() - } else { - unsafe { self.items.last().unwrap_unchecked() } - } - } - - pub fn last_mut_unchecked(&mut self) -> &mut T { - if cfg!(debug_assertions) { - assert!(!self.is_empty(), "Stack underflow"); - - self.items.last_mut().unwrap() - } else { - unsafe { self.items.last_mut().unwrap_unchecked() } - } - } -} - -impl Default for Stack { - fn default() -> Self { - Self::new() - } -} - -impl Index> for Stack { - type Output = [T]; - - fn index(&self, index: Range) -> &Self::Output { - &self.items[index] - } -} - -impl IndexMut> for Stack { - fn index_mut(&mut self, index: Range) -> &mut Self::Output { - &mut self.items[index] - } -} - -impl Debug for Stack { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{:?}", self.items) - } -} - -impl Display for Stack { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - writeln!(f, "----- DUST CALL STACK -----")?; - - for (index, function_call) in self.items.iter().enumerate().rev() { - writeln!(f, "{index:02} | {function_call}")?; - } - - write!(f, "---------------------------") - } -} diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs index c605066..ff1b0f9 100644 --- a/dust-lang/src/vm/thread.rs +++ b/dust-lang/src/vm/thread.rs @@ -1,18 +1,36 @@ -use std::{mem::replace, sync::Arc, thread::JoinHandle}; +use std::{sync::Arc, thread::JoinHandle}; use tracing::{info, trace}; -use crate::{Chunk, DustString, Operand, Span, Value, vm::FunctionCall}; +use crate::{Chunk, DustString, Span, Value, vm::CallFrame}; -use super::{Pointer, Register, RunAction, Stack}; +use super::{Action, Register}; pub struct Thread { chunk: Arc, + call_stack: Vec, + next_action: Action, + return_value: Option, + spawned_threads: Vec>, } impl Thread { pub fn new(chunk: Arc) -> Self { - Thread { chunk } + let mut call_stack = Vec::with_capacity(chunk.prototypes.len() + 1); + let mut main_call = CallFrame::new(chunk.clone(), 0); + main_call.ip = 1; // The first action is already known + + call_stack.push(main_call); + + let first_action = Action::from(*chunk.instructions.first().unwrap()); + + Thread { + chunk, + call_stack, + next_action: first_action, + return_value: None, + spawned_threads: Vec::new(), + } } pub fn run(&mut self) -> Option { @@ -24,247 +42,287 @@ impl Thread { .unwrap_or_else(|| DustString::from("anonymous")) ); - let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1); - let mut main_call = FunctionCall::new(self.chunk.clone(), 0); - main_call.ip = 1; // The first action is already known - - call_stack.push(main_call); - - let first_action = RunAction::from(*self.chunk.instructions.first().unwrap()); - let mut thread_data = ThreadData { - call_stack, - next_action: first_action, - return_value_index: None, - spawned_threads: Vec::new(), - }; - loop { - trace!("Instruction: {}", thread_data.next_action.instruction); + trace!("Instruction: {}", self.next_action.instruction); - let should_end = (thread_data.next_action.logic)( - thread_data.next_action.instruction, - &mut thread_data, - ); + let should_end = (self.next_action.logic)(self.next_action.instruction, self); if should_end { - let value_option = if let Some(register_index) = thread_data.return_value_index { - let value = - thread_data.empty_register_or_clone_constant_unchecked(register_index); + self.spawned_threads.into_iter().for_each(|join_handle| { + let _ = join_handle.join(); + }); - Some(value) - } else { - None - }; - - thread_data - .spawned_threads - .into_iter() - .for_each(|join_handle| { - let _ = join_handle.join(); - }); - - return value_option; + return self.return_value.take(); } } } -} -#[derive(Debug)] -pub struct ThreadData { - pub call_stack: Stack, - pub next_action: RunAction, - pub return_value_index: Option, - pub spawned_threads: Vec>, -} - -impl ThreadData { pub fn current_position(&self) -> Span { - let current_call = self.call_stack.last_unchecked(); + let current_frame = self.current_frame(); - current_call.chunk.positions[current_call.ip] + current_frame.chunk.positions[current_frame.ip] } - pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value { - trace!("Follow {pointer}"); - - match pointer { - Pointer::Register(register_index) => self.open_register_unchecked(register_index), - Pointer::Constant(constant_index) => self.get_constant_unchecked(constant_index), - Pointer::Stack(stack_index, register_index) => unsafe { - let register = self - .call_stack - .get_unchecked(stack_index) - .registers - .get_unchecked(register_index as usize); - - match register { - Register::Value(value) => value, - Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer), - Register::Empty => panic!("VM Error: Register {register_index} is empty"), - } - }, - } - } - - pub fn get_register_unchecked(&self, register_index: u16) -> &Register { - trace!("Get R{register_index}"); - - let register_index = register_index as usize; - + pub fn current_frame(&self) -> &CallFrame { if cfg!(debug_assertions) { - &self.call_stack.last_unchecked().registers[register_index] + self.call_stack.last().unwrap() } else { - unsafe { - self.call_stack - .last_unchecked() - .registers - .get_unchecked(register_index) - } + unsafe { self.call_stack.last().unwrap_unchecked() } } } - pub fn set_register(&mut self, to_register: u16, register: Register) { - let to_register = to_register as usize; - - self.call_stack.last_mut_unchecked().registers[to_register] = register; + pub fn current_frame_mut(&mut self) -> &mut CallFrame { + if cfg!(debug_assertions) { + self.call_stack.last_mut().unwrap() + } else { + unsafe { self.call_stack.last_mut().unwrap_unchecked() } + } } - pub fn open_register_unchecked(&self, register_index: u16) -> &Value { - let register_index = register_index as usize; + pub fn get_frame(&self, index: usize) -> &CallFrame { + if cfg!(debug_assertions) { + self.call_stack.get(index).unwrap() + } else { + unsafe { self.call_stack.get_unchecked(index) } + } + } + pub fn get_boolean_register(&self, index: usize) -> bool { let register = if cfg!(debug_assertions) { - &self.call_stack.last_unchecked().registers[register_index] + self.current_frame() + .registers + .booleans + .get(index as usize) + .unwrap() } else { unsafe { - self.call_stack - .last_unchecked() + self.current_frame() .registers - .get_unchecked(register_index) + .booleans + .get_unchecked(index as usize) } }; - trace!("Open R{register_index} to {register}"); - match register { - Register::Value(value) => value, - Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer), - Register::Empty => panic!("VM Error: Register {register_index} is empty"), + Register::Value(boolean) => *boolean, + Register::Pointer(pointer) => unsafe { **pointer }, + Register::Empty => panic!("Attempted to get a boolean from an empty register"), } } - pub fn open_register_allow_empty_unchecked(&self, register_index: u16) -> Option<&Value> { - trace!("Open R{register_index}"); - - let register = self.get_register_unchecked(register_index); - - trace!("Open R{register_index} to {register}"); - - match register { - Register::Value(value) => Some(value), - Register::Pointer(pointer) => Some(self.follow_pointer_unchecked(*pointer)), - Register::Empty => None, - } - } - - pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u16) -> Value { - let register_index = register_index as usize; - let old_register = replace( - &mut self.call_stack.last_mut_unchecked().registers[register_index], - Register::Empty, - ); - - match old_register { - Register::Value(value) => value, - Register::Pointer(pointer) => match pointer { - Pointer::Register(register_index) => { - self.empty_register_or_clone_constant_unchecked(register_index) - } - Pointer::Constant(constant_index) => { - self.get_constant_unchecked(constant_index).clone() - } - Pointer::Stack(stack_index, register_index) => { - let call = self.call_stack.get_unchecked_mut(stack_index); - - let old_register = replace( - &mut call.registers[register_index as usize], - Register::Empty, - ); - - match old_register { - Register::Value(value) => value, - Register::Pointer(pointer) => { - self.follow_pointer_unchecked(pointer).clone() - } - Register::Empty => panic!("VM Error: Register {register_index} is empty"), - } - } - }, - Register::Empty => panic!("VM Error: Register {register_index} is empty"), - } - } - - pub fn clone_register_value_or_constant_unchecked(&self, register_index: u16) -> Value { - let register = self.get_register_unchecked(register_index); - - match register { - Register::Value(value) => value.clone(), - Register::Pointer(pointer) => match pointer { - Pointer::Register(register_index) => { - self.open_register_unchecked(*register_index).clone() - } - Pointer::Constant(constant_index) => { - self.get_constant_unchecked(*constant_index).clone() - } - Pointer::Stack(stack_index, register_index) => { - let call = self.call_stack.get_unchecked(*stack_index); - let register = &call.registers[*register_index as usize]; - - match register { - Register::Value(value) => value.clone(), - Register::Pointer(pointer) => { - self.follow_pointer_unchecked(*pointer).clone() - } - Register::Empty => panic!("VM Error: Register {register_index} is empty"), - } - } - }, - Register::Empty => panic!("VM Error: Register {register_index} is empty"), - } - } - - /// DRY helper to get a value from an Argument - pub fn get_argument_unchecked(&self, argument: Operand) -> &Value { - match argument { - Operand::Constant(constant_index) => self.get_constant_unchecked(constant_index), - Operand::Register(register_index) => self.open_register_unchecked(register_index), - } - } - - pub fn get_constant_unchecked(&self, constant_index: u16) -> &Value { - let constant_index = constant_index as usize; - - if cfg!(debug_assertions) { - &self.call_stack.last().unwrap().chunk.constants[constant_index] + pub fn set_boolean_register(&mut self, index: usize, new_register: Register) { + let old_register = if cfg!(debug_assertions) { + self.current_frame_mut() + .registers + .booleans + .get_mut(index as usize) + .unwrap() } else { unsafe { - self.call_stack - .last_unchecked() - .chunk - .constants - .get_unchecked(constant_index) + self.current_frame_mut() + .registers + .booleans + .get_unchecked_mut(index as usize) } + }; + + *old_register = new_register; + } + + pub fn get_byte_register(&self, index: usize) -> u8 { + let register = if cfg!(debug_assertions) { + self.current_frame() + .registers + .bytes + .get(index as usize) + .unwrap() + } else { + unsafe { + self.current_frame() + .registers + .bytes + .get_unchecked(index as usize) + } + }; + + match register { + Register::Value(byte) => *byte, + Register::Pointer(pointer) => unsafe { **pointer }, + Register::Empty => panic!("Attempted to get a byte from an empty register"), } } - pub fn get_local_register(&self, local_index: u16) -> u16 { - let local_index = local_index as usize; - let chunk = &self.call_stack.last_unchecked().chunk; + pub fn set_byte_register(&mut self, index: usize, new_register: Register) { + let old_register = if cfg!(debug_assertions) { + self.current_frame_mut() + .registers + .bytes + .get_mut(index as usize) + .unwrap() + } else { + unsafe { + self.current_frame_mut() + .registers + .bytes + .get_unchecked_mut(index as usize) + } + }; - assert!( - local_index < chunk.locals.len(), - "VM Error: Local index out of bounds" - ); + *old_register = new_register; + } - chunk.locals[local_index].register_index + pub fn get_character_register(&self, index: usize) -> char { + let register = if cfg!(debug_assertions) { + self.current_frame() + .registers + .characters + .get(index as usize) + .unwrap() + } else { + unsafe { + self.current_frame() + .registers + .characters + .get_unchecked(index as usize) + } + }; + + match register { + Register::Value(character) => *character, + Register::Pointer(pointer) => unsafe { **pointer }, + Register::Empty => panic!("Attempted to get a character from an empty register"), + } + } + + pub fn set_character_register(&mut self, index: usize, new_register: Register) { + let old_register = if cfg!(debug_assertions) { + self.current_frame_mut() + .registers + .characters + .get_mut(index as usize) + .unwrap() + } else { + unsafe { + self.current_frame_mut() + .registers + .characters + .get_unchecked_mut(index as usize) + } + }; + + *old_register = new_register; + } + + pub fn get_float_register(&self, index: usize) -> f64 { + let register = if cfg!(debug_assertions) { + self.current_frame() + .registers + .floats + .get(index as usize) + .unwrap() + } else { + unsafe { + self.current_frame() + .registers + .floats + .get_unchecked(index as usize) + } + }; + + match register { + Register::Value(float) => *float, + Register::Pointer(pointer) => unsafe { **pointer }, + Register::Empty => panic!("Attempted to get a float from an empty register"), + } + } + + pub fn get_integer_register(&self, index: usize) -> i64 { + let register = if cfg!(debug_assertions) { + self.current_frame() + .registers + .integers + .get(index as usize) + .unwrap() + } else { + unsafe { + self.current_frame() + .registers + .integers + .get_unchecked(index as usize) + } + }; + + match register { + Register::Value(integer) => *integer, + Register::Pointer(pointer) => unsafe { **pointer }, + Register::Empty => panic!("Attempted to get an integer from an empty register"), + } + } + + pub fn set_integer_register(&mut self, index: usize, new_register: Register) { + let old_register = if cfg!(debug_assertions) { + self.current_frame_mut() + .registers + .integers + .get_mut(index as usize) + .unwrap() + } else { + unsafe { + self.current_frame_mut() + .registers + .integers + .get_unchecked_mut(index as usize) + } + }; + + *old_register = new_register; + } + + pub fn get_string_register(&self, index: usize) -> &DustString { + let register = if cfg!(debug_assertions) { + self.current_frame() + .registers + .strings + .get(index as usize) + .unwrap() + } else { + unsafe { + self.current_frame() + .registers + .strings + .get_unchecked(index as usize) + } + }; + + match register { + Register::Value(string) => string, + Register::Pointer(pointer) => { + if cfg!(debug_assertions) { + unsafe { pointer.as_ref().unwrap() } + } else { + unsafe { pointer.as_ref().unwrap_unchecked() } + } + } + Register::Empty => panic!("Attempted to get a string from an empty register"), + } + } + + pub fn set_string_register(&mut self, index: usize, new_register: Register) { + let old_register = if cfg!(debug_assertions) { + self.current_frame_mut() + .registers + .strings + .get_mut(index as usize) + .unwrap() + } else { + unsafe { + self.current_frame_mut() + .registers + .strings + .get_unchecked_mut(index as usize) + } + }; + + *old_register = new_register; } }