diff --git a/Cargo.lock b/Cargo.lock index eecbb0c..8f82233 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -342,6 +342,7 @@ dependencies = [ "smallvec", "smartstring", "tracing", + "typed-arena", ] [[package]] @@ -946,6 +947,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "unicode-ident" version = "1.0.13" diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index 28c86ac..1de72f1 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -22,6 +22,7 @@ smartstring = { version = "1.0.1", features = [ "serde", ], default-features = false } tracing = "0.1.41" +typed-arena = "2.0.2" [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 277edbf..05eb799 100644 --- a/dust-lang/src/chunk/mod.rs +++ b/dust-lang/src/chunk/mod.rs @@ -42,7 +42,7 @@ pub struct Chunk { pub(crate) locals: SmallVec<[Local; 8]>, pub(crate) prototypes: Vec, - pub(crate) stack_size: usize, + pub(crate) register_count: usize, pub(crate) prototype_index: u8, } @@ -65,7 +65,7 @@ impl Chunk { constants: constants.into(), locals: locals.into(), prototypes, - stack_size: 0, + register_count: 0, prototype_index: 0, } } diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index 0fbbb77..2e19050 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -214,7 +214,7 @@ impl<'src> Compiler<'src> { constants: self.constants, locals, prototypes: self.prototypes, - stack_size: self.stack_size, + register_count: self.stack_size, prototype_index: self.prototype_index, } } @@ -1000,9 +1000,9 @@ impl<'src> Compiler<'src> { return self.parse_call_native(native_function); } else if self.function_name.as_deref() == Some(identifier) { let destination = self.next_register(); - let load_function = Instruction::load_function(destination, self.prototype_index); + let load_self = Instruction::load_self(destination); - self.emit_instruction(load_function, Type::SelfFunction, start_position); + self.emit_instruction(load_self, Type::SelfFunction, start_position); return Ok(()); } else { @@ -1700,6 +1700,7 @@ impl<'src> Compiler<'src> { }); } }; + let is_recursive = last_instruction_type == &Type::SelfFunction; let mut argument_count = 0; @@ -1727,7 +1728,7 @@ impl<'src> Compiler<'src> { let end = self.current_position.1; let destination = self.next_register(); - let call = Instruction::call(destination, function_register, argument_count); + let call = Instruction::call(destination, function_register, argument_count, is_recursive); self.emit_instruction(call, function_return_type, Span(start, end)); diff --git a/dust-lang/src/instruction/call.rs b/dust-lang/src/instruction/call.rs index 1a8213e..dff0679 100644 --- a/dust-lang/src/instruction/call.rs +++ b/dust-lang/src/instruction/call.rs @@ -4,6 +4,7 @@ pub struct Call { pub destination: u8, pub function_register: u8, pub argument_count: u8, + pub is_recursive: bool, } impl From for Call { @@ -11,11 +12,13 @@ impl From for Call { let destination = instruction.a_field(); let function_register = instruction.b_field(); let argument_count = instruction.c_field(); + let is_recursive = instruction.d_field(); Call { destination, function_register, argument_count, + is_recursive, } } } @@ -25,7 +28,8 @@ impl From for Instruction { let a = call.destination; let b = call.function_register; let c = call.argument_count; + let d = call.is_recursive; - Instruction::new(Operation::CALL, a, b, c, false, false, false) + Instruction::new(Operation::CALL, a, b, c, false, false, d) } } diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index 73aa3a1..e0a32e7 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -375,11 +375,17 @@ impl Instruction { }) } - pub fn call(destination: u8, function_register: u8, argument_count: u8) -> Instruction { + pub fn call( + destination: u8, + function_register: u8, + argument_count: u8, + is_recursive: bool, + ) -> Instruction { Instruction::from(Call { destination, function_register, argument_count, + is_recursive, }) } @@ -691,6 +697,7 @@ impl Instruction { destination, function_register, argument_count, + .. } = Call::from(*self); let arguments_start = destination.saturating_sub(argument_count); diff --git a/dust-lang/src/native_function/assertion.rs b/dust-lang/src/native_function/assertion.rs index 2edc9be..c0d4b45 100644 --- a/dust-lang/src/native_function/assertion.rs +++ b/dust-lang/src/native_function/assertion.rs @@ -14,7 +14,7 @@ pub fn panic( let mut message = format!("Dust panic at {position}!"); for register_index in argument_range { - let value = record.open_register(register_index); + let value = record.open_register_unchecked(register_index); if let Some(string) = value.as_string() { message.push_str(string); diff --git a/dust-lang/src/native_function/string.rs b/dust-lang/src/native_function/string.rs index a4db8dd..2d48ce7 100644 --- a/dust-lang/src/native_function/string.rs +++ b/dust-lang/src/native_function/string.rs @@ -10,7 +10,7 @@ pub fn to_string( destination: Option, argument_range: Range, ) -> Result { - let argument_value = record.open_register(argument_range.start); + let argument_value = record.open_register_unchecked(argument_range.start); let argument_string = argument_value.display(record); let destination = destination.unwrap(); let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string))); diff --git a/dust-lang/src/vm/error.rs b/dust-lang/src/vm/error.rs index a437edf..1c71da4 100644 --- a/dust-lang/src/vm/error.rs +++ b/dust-lang/src/vm/error.rs @@ -6,7 +6,7 @@ use super::{stack::Stack, FunctionCall}; #[derive(Clone, Debug, PartialEq)] pub enum VmError { - CallStackUnderflow, + StackUnderflow, ExpectedFunction { value: Value, }, @@ -22,7 +22,7 @@ pub enum VmError { impl Display for VmError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Self::CallStackUnderflow => { + Self::StackUnderflow => { write!(f, "Call stack underflow") } Self::ExpectedFunction { value } => { diff --git a/dust-lang/src/vm/mod.rs b/dust-lang/src/vm/mod.rs index 147a825..dc52e98 100644 --- a/dust-lang/src/vm/mod.rs +++ b/dust-lang/src/vm/mod.rs @@ -13,7 +13,7 @@ use std::{ pub use error::VmError; pub use record::Record; -pub use run_action::RunAction; +pub use run_action::RecordAction; pub use stack::{FunctionCall, Stack}; pub use thread::{Thread, ThreadSignal}; use tracing::{span, Level}; diff --git a/dust-lang/src/vm/record.rs b/dust-lang/src/vm/record.rs index 1920fca..8c709a7 100644 --- a/dust-lang/src/vm/record.rs +++ b/dust-lang/src/vm/record.rs @@ -10,7 +10,7 @@ use super::{Pointer, Register}; pub struct Record<'a> { pub ip: usize, pub chunk: &'a Chunk, - stack: Vec, + registers: Vec, } impl<'a> Record<'a> { @@ -18,7 +18,7 @@ impl<'a> Record<'a> { pub fn new(chunk: &'a Chunk) -> Self { Self { ip: 0, - stack: vec![Register::Empty; chunk.stack_size], + registers: vec![Register::Empty; chunk.register_count], chunk, } } @@ -28,7 +28,7 @@ impl<'a> Record<'a> { } pub fn stack_size(&self) -> usize { - self.stack.len() + self.registers.len() } pub fn current_position(&self) -> Span { @@ -43,59 +43,46 @@ impl<'a> Record<'a> { trace!("Follow pointer {pointer}"); match pointer { - Pointer::Stack(register_index) => self.open_register(register_index), + Pointer::Stack(register_index) => self.open_register_unchecked(register_index), Pointer::Constant(constant_index) => self.get_constant(constant_index), } } - pub fn get_register(&self, register_index: u8) -> &Register { + pub fn get_register_unchecked(&self, register_index: u8) -> &Register { trace!("Get register R{register_index}"); let register_index = register_index as usize; - assert!( - register_index < self.stack.len(), - "VM Error: Register index out of bounds" - ); - - &self.stack[register_index] + if cfg!(debug_assertions) { + &self.registers[register_index] + } else { + unsafe { self.registers.get_unchecked(register_index) } + } } pub fn set_register(&mut self, to_register: u8, register: Register) { let to_register = to_register as usize; - assert!( - to_register < self.stack.len(), - "VM Error: Register index out of bounds" - ); - - self.stack[to_register] = register; + self.registers[to_register] = register; } - pub fn reserve_registers(&mut self, count: usize) { - for _ in 0..count { - self.stack.push(Register::Empty); - } - } - - pub fn open_register(&self, register_index: u8) -> &Value { + pub fn open_register_unchecked(&self, register_index: u8) -> &Value { let register_index = register_index as usize; - assert!( - register_index < self.stack.len(), - "VM Error: Register index out of bounds" - ); - - let register = &self.stack[register_index]; + let register = if cfg!(debug_assertions) { + &self.registers[register_index] + } else { + unsafe { self.registers.get_unchecked(register_index) } + }; match register { Register::Value(value) => { - trace!("Register R{register_index} openned to value {value}"); + trace!("Register R{register_index} opened to value {value}"); value } Register::Pointer(pointer) => { - trace!("Open register R{register_index} openned to pointer {pointer}"); + trace!("Open register R{register_index} opened to pointer {pointer}"); self.follow_pointer(*pointer) } @@ -109,11 +96,11 @@ impl<'a> Record<'a> { let register_index = register_index as usize; assert!( - register_index < self.stack.len(), + register_index < self.registers.len(), "VM Error: Register index out of bounds" ); - let register = &self.stack[register_index]; + let register = &self.registers[register_index]; match register { Register::Value(value) => { @@ -130,24 +117,22 @@ impl<'a> Record<'a> { } } - pub fn empty_register_or_clone_constant( - &mut self, - register_index: u8, - new_register: Register, - ) -> Value { + pub fn empty_register_or_clone_constant(&mut self, register_index: u8) -> Value { let register_index = register_index as usize; assert!( - register_index < self.stack.len(), + register_index < self.registers.len(), "VM Error: Register index out of bounds" ); - let old_register = replace(&mut self.stack[register_index], new_register); + let old_register = replace(&mut self.registers[register_index], Register::Empty); match old_register { Register::Value(value) => value, Register::Pointer(pointer) => match pointer { - Pointer::Stack(register_index) => self.open_register(register_index).clone(), + Pointer::Stack(register_index) => { + self.empty_register_or_clone_constant(register_index) + } Pointer::Constant(constant_index) => self.get_constant(constant_index).clone(), }, Register::Empty => panic!("VM Error: Register {register_index} is empty"), @@ -156,16 +141,18 @@ impl<'a> Record<'a> { pub fn clone_register_value_or_constant(&self, register_index: u8) -> Value { assert!( - (register_index as usize) < self.stack.len(), + (register_index as usize) < self.registers.len(), "VM Error: Register index out of bounds" ); - let register = &self.stack[register_index as usize]; + let register = &self.registers[register_index as usize]; match register { Register::Value(value) => value.clone(), Register::Pointer(pointer) => match pointer { - Pointer::Stack(register_index) => self.open_register(*register_index).clone(), + Pointer::Stack(register_index) => { + self.open_register_unchecked(*register_index).clone() + } Pointer::Constant(constant_index) => self.get_constant(*constant_index).clone(), }, Register::Empty => panic!("VM Error: Register {register_index} is empty"), @@ -176,7 +163,7 @@ impl<'a> Record<'a> { pub fn get_argument(&self, argument: Argument) -> &Value { match argument { Argument::Constant(constant_index) => self.get_constant(constant_index), - Argument::Register(register_index) => self.open_register(register_index), + Argument::Register(register_index) => self.open_register_unchecked(register_index), } } diff --git a/dust-lang/src/vm/run_action.rs b/dust-lang/src/vm/run_action.rs index 3ea79a0..f520c23 100644 --- a/dust-lang/src/vm/run_action.rs +++ b/dust-lang/src/vm/run_action.rs @@ -6,27 +6,28 @@ use crate::{ LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point, Return, SetLocal, Subtract, Test, TestSet, }, - AbstractList, Argument, ConcreteValue, Instruction, Type, Value, + vm::FunctionCall, + AbstractList, Argument, ConcreteValue, DustString, Instruction, Type, Value, }; -use super::{thread::ThreadSignal, Pointer, Record, Register}; +use super::{thread::ThreadData, Pointer, Record, Register}; #[derive(Clone, Copy, Debug, PartialEq)] -pub struct RunAction { +pub struct RecordAction { pub logic: RunnerLogic, pub instruction: Instruction, } -impl From for RunAction { +impl From for RecordAction { fn from(instruction: Instruction) -> Self { let operation = instruction.operation(); let logic = RUNNER_LOGIC_TABLE[operation.0 as usize]; - RunAction { logic, instruction } + RecordAction { logic, instruction } } } -pub type RunnerLogic = fn(Instruction, &mut Record) -> ThreadSignal; +pub type RunnerLogic = fn(Instruction, &mut ThreadData) -> Option; pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [ point, @@ -56,9 +57,10 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [ r#return, ]; -pub fn point(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn point(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Point { from, to } = instruction.into(); - let from_register = record.get_register(from); + let from_register = record.get_register_unchecked(from); let from_register_is_empty = matches!(from_register, Register::Empty); if !from_register_is_empty { @@ -67,10 +69,13 @@ pub fn point(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.set_register(from, register); } - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn close(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn close(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Close { from, to } = instruction.into(); assert!(from < to, "Runtime Error: Malformed instruction"); @@ -84,10 +89,13 @@ pub fn close(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.set_register(register_index, Register::Empty); } - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn load_boolean(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let LoadBoolean { destination, value, @@ -102,10 +110,13 @@ pub fn load_boolean(instruction: Instruction, record: &mut Record) -> ThreadSign record.ip += 1; } - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn load_constant(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let LoadConstant { destination, constant_index, @@ -121,10 +132,13 @@ pub fn load_constant(instruction: Instruction, record: &mut Record) -> ThreadSig record.ip += 1; } - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn load_list(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let LoadList { destination, start_register, @@ -133,7 +147,7 @@ pub fn load_list(instruction: Instruction, record: &mut Record) -> ThreadSignal let mut item_type = Type::Any; for register_index in start_register..destination { - match record.get_register(register_index) { + match record.get_register_unchecked(register_index) { Register::Empty => continue, Register::Value(value) => { if item_type == Type::Any { @@ -160,32 +174,43 @@ pub fn load_list(instruction: Instruction, record: &mut Record) -> ThreadSignal record.set_register(destination, register); - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn load_function(instruction: Instruction, _: &mut Record) -> ThreadSignal { +pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let LoadFunction { destination, prototype_index, } = instruction.into(); + let prototype_index = prototype_index as usize; + let function = record.chunk.prototypes[prototype_index].as_function(); + let register = Register::Value(Value::Function(function)); - ThreadSignal::LoadFunction { - destination, - prototype_index, - } + record.set_register(destination, register); + + record.ip += 1; + + None } -pub fn load_self(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let LoadSelf { destination } = instruction.into(); let function = record.as_function(); let register = Register::Value(Value::Function(function)); record.set_register(destination, register); - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn get_local(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let GetLocal { destination, local_index, @@ -195,10 +220,13 @@ pub fn get_local(instruction: Instruction, record: &mut Record) -> ThreadSignal record.set_register(destination, register); - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn set_local(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let SetLocal { register_index, local_index, @@ -208,10 +236,13 @@ pub fn set_local(instruction: Instruction, record: &mut Record) -> ThreadSignal record.set_register(local_register_index, register); - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn add(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn add(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Add { destination, left, @@ -224,10 +255,13 @@ pub fn add(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.set_register(destination, register); - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn subtract(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Subtract { destination, left, @@ -240,10 +274,13 @@ pub fn subtract(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.set_register(destination, register); - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn multiply(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Multiply { destination, left, @@ -264,10 +301,13 @@ pub fn multiply(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.set_register(destination, register); - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn divide(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn divide(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Divide { destination, left, @@ -288,10 +328,13 @@ pub fn divide(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.set_register(destination, register); - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn modulo(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Modulo { destination, left, @@ -312,15 +355,18 @@ pub fn modulo(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.set_register(destination, register); - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn test(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn test(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Test { operand_register, test_value, } = instruction.into(); - let value = record.open_register(operand_register); + let value = record.open_register_unchecked(operand_register); let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value { *boolean } else { @@ -331,10 +377,13 @@ pub fn test(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.ip += 1; } - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn test_set(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let TestSet { destination, argument, @@ -359,10 +408,13 @@ pub fn test_set(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.set_register(destination, register); } - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn equal(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn equal(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Equal { value, left, right } = instruction.into(); let left = record.get_argument(left); let right = record.get_argument(right); @@ -372,10 +424,13 @@ pub fn equal(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.ip += 1; } - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn less(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn less(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Less { value, left, right } = instruction.into(); let left = record.get_argument(left); let right = record.get_argument(right); @@ -385,10 +440,13 @@ pub fn less(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.ip += 1; } - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn less_equal(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let LessEqual { value, left, right } = instruction.into(); let left = record.get_argument(left); let right = record.get_argument(right); @@ -398,10 +456,13 @@ pub fn less_equal(instruction: Instruction, record: &mut Record) -> ThreadSignal record.ip += 1; } - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn negate(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn negate(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Negate { destination, argument, @@ -412,10 +473,13 @@ pub fn negate(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.set_register(destination, register); - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn not(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn not(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Not { destination, argument, @@ -429,10 +493,13 @@ pub fn not(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.set_register(destination, register); - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn jump(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn jump(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Jump { offset, is_positive, @@ -445,24 +512,61 @@ pub fn jump(instruction: Instruction, record: &mut Record) -> ThreadSignal { record.ip -= offset + 1; } - ThreadSignal::Continue + record.ip += 1; + + None } -pub fn call(instruction: Instruction, _: &mut Record) -> ThreadSignal { +pub fn call(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Call { destination: return_register, function_register, argument_count, + is_recursive, } = instruction.into(); - ThreadSignal::Call { - function_register, + let function = record + .open_register_unchecked(function_register) + .as_function() + .unwrap(); + let first_argument_register = return_register - argument_count; + let prototype = if is_recursive { + record.chunk + } else { + &record.chunk.prototypes[function.prototype_index as usize] + }; + let mut next_record = Record::new(prototype); + let next_call = FunctionCall { + name: next_record.name().cloned(), return_register, - argument_count, + ip: record.ip, + }; + + for (argument_index, register_index) in (first_argument_register..return_register).enumerate() { + let argument = record.clone_register_value_or_constant(register_index); + + trace!( + "Passing argument \"{argument}\" to {}", + function + .name + .clone() + .unwrap_or_else(|| DustString::from("anonymous")) + ); + + next_record.set_register(argument_index as u8, Register::Value(argument)); } + + record.ip += 1; + + data.call_stack.push(next_call); + data.records.push(next_record); + + None } -pub fn call_native(instruction: Instruction, record: &mut Record) -> ThreadSignal { +pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let CallNative { destination, function, @@ -473,19 +577,47 @@ pub fn call_native(instruction: Instruction, record: &mut Record) -> ThreadSigna function .call(record, Some(destination), argument_range) - .unwrap_or_else(|error| panic!("{error:?}")) + .unwrap_or_else(|error| panic!("{error:?}")); + + record.ip += 1; + + None } -pub fn r#return(instruction: Instruction, _: &mut Record) -> ThreadSignal { +pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> Option { + let record = data.records.last_mut_unchecked(); let Return { should_return_value, return_register, } = instruction.into(); - ThreadSignal::Return { - should_return_value, - return_register, + trace!("Returning with call stack:\n{}", data.call_stack); + + let return_value = if should_return_value { + Some(record.empty_register_or_clone_constant(return_register)) + } else { + None + }; + + let current_call = data.call_stack.pop_unchecked(); + let _current_record = data.records.pop_unchecked(); + let destination = current_call.return_register; + + if data.call_stack.is_empty() { + return if should_return_value { + return_value + } else { + None + }; } + + let outer_record = data.records.last_mut_unchecked(); + + if should_return_value { + outer_record.set_register(destination, Register::Value(return_value.unwrap())); + } + + None } #[cfg(test)] diff --git a/dust-lang/src/vm/stack.rs b/dust-lang/src/vm/stack.rs index f04a12f..82e8562 100644 --- a/dust-lang/src/vm/stack.rs +++ b/dust-lang/src/vm/stack.rs @@ -33,6 +33,16 @@ impl Stack { self.items.len() } + pub fn get_unchecked(&self, index: usize) -> &T { + if cfg!(debug_assertions) { + assert!(index < self.len(), "{}", VmError::StackUnderflow); + + &self.items[index] + } else { + unsafe { self.items.get_unchecked(index) } + } + } + pub fn push(&mut self, item: T) { self.items.push(item); } @@ -51,7 +61,7 @@ impl Stack { pub fn pop_unchecked(&mut self) -> T { if cfg!(debug_assertions) { - assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow); + assert!(!self.is_empty(), "{}", VmError::StackUnderflow); self.items.pop().unwrap() } else { @@ -61,7 +71,7 @@ impl Stack { pub fn last_unchecked(&self) -> &T { if cfg!(debug_assertions) { - assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow); + assert!(!self.is_empty(), "{}", VmError::StackUnderflow); self.items.last().unwrap() } else { @@ -71,7 +81,7 @@ impl Stack { pub fn last_mut_unchecked(&mut self) -> &mut T { if cfg!(debug_assertions) { - assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow); + assert!(!self.is_empty(), "{}", VmError::StackUnderflow); self.items.last_mut().unwrap() } else { @@ -100,9 +110,9 @@ impl IndexMut> for Stack { } } -impl Debug for Stack { +impl Debug for Stack { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{self}") + write!(f, "{:?}", self.items) } } diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs index 287ceec..1dfad2e 100644 --- a/dust-lang/src/vm/thread.rs +++ b/dust-lang/src/vm/thread.rs @@ -2,12 +2,9 @@ use std::fmt::{self, Display, Formatter}; use tracing::{info, trace}; -use crate::{ - vm::{FunctionCall, Register}, - Chunk, DustString, Value, -}; +use crate::{vm::FunctionCall, Chunk, DustString, Value}; -use super::{record::Record, RunAction, Stack}; +use super::{record::Record, RecordAction, Stack}; pub struct Thread { chunk: Chunk, @@ -28,127 +25,43 @@ impl Thread { }; let main_record = Record::new(&self.chunk); - call_stack.push(main_call); - records.push(main_record); - - let mut active_record = records.last_mut_unchecked(); - info!( "Starting thread with {}", - active_record - .as_function() + self.chunk .name + .clone() .unwrap_or_else(|| DustString::from("anonymous")) ); + call_stack.push(main_call); + records.push(main_record); + + let mut thread_data = ThreadData { + call_stack, + records, + }; + loop { - trace!( - "Run \"{}\" | IP = {}", - active_record - .name() - .cloned() - .unwrap_or_else(|| DustString::from("anonymous")), - active_record.ip - ); - + let active_record = thread_data.records.last_unchecked(); let instruction = active_record.chunk.instructions[active_record.ip]; - let action = RunAction::from(instruction); - let signal = (action.logic)(action.instruction, active_record); + let record_action = RecordAction::from(instruction); + let value_option = (record_action.logic)(record_action.instruction, &mut thread_data); - trace!("Thread Signal: {}", signal); + if thread_data.call_stack.is_empty() { + trace!("Returning {value_option:?} from function"); - active_record.ip += 1; - - match signal { - ThreadSignal::Continue => {} - ThreadSignal::LoadFunction { - destination, - prototype_index, - } => { - let function_record_index = prototype_index as usize; - let function = self.chunk.prototypes[function_record_index].as_function(); - let register = Register::Value(Value::Function(function)); - - active_record.set_register(destination, register); - } - ThreadSignal::Call { - function_register, - return_register, - argument_count, - } => { - let function = active_record - .open_register(function_register) - .as_function() - .unwrap(); - let first_argument_register = return_register - argument_count; - let mut arguments = Vec::with_capacity(argument_count as usize); - - for register_index in first_argument_register..return_register { - let value = active_record.clone_register_value_or_constant(register_index); - - arguments.push(value); - } - - trace!("Passing arguments: {arguments:?}"); - - let prototype = &self.chunk.prototypes[function.prototype_index as usize]; - let next_record = Record::new(prototype); - let next_call = FunctionCall { - name: next_record.name().cloned(), - return_register, - ip: active_record.ip, - }; - - call_stack.push(next_call); - records.push(next_record); - - active_record = records.last_mut_unchecked(); - - for (index, argument) in arguments.into_iter().enumerate() { - active_record.set_register(index as u8, Register::Value(argument)); - } - } - ThreadSignal::Return { - should_return_value, - return_register, - } => { - trace!("Returning with call stack:\n{call_stack}"); - - let return_value = if should_return_value { - Some( - active_record - .empty_register_or_clone_constant(return_register, Register::Empty), - ) - } else { - None - }; - - let current_call = call_stack.pop_unchecked(); - let _current_record = records.pop_unchecked(); - let destination = current_call.return_register; - - if call_stack.is_empty() { - return if should_return_value { - Some(return_value.unwrap()) - } else { - None - }; - } - - let outer_record = records.last_mut_unchecked(); - - if should_return_value { - outer_record - .set_register(destination, Register::Value(return_value.unwrap())); - } - - active_record = outer_record; - } + return value_option; } } } } +#[derive(Debug)] +pub struct ThreadData<'a> { + pub call_stack: Stack, + pub records: Stack>, +} + #[derive(Debug)] pub enum ThreadSignal { Continue,