diff --git a/dust-lang/src/native_function/assert.rs b/dust-lang/src/native_function/assert.rs index d2e3cef..b2704a4 100644 --- a/dust-lang/src/native_function/assert.rs +++ b/dust-lang/src/native_function/assert.rs @@ -3,17 +3,19 @@ use std::{ops::Range, panic}; use crate::vm::ThreadData; pub fn panic(data: &mut ThreadData, _: Option, argument_range: Range) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; - let position = record.current_position(); + let position = data.current_position(); let mut message = format!("Dust panic at {position}!"); for register_index in argument_range { - let value = record.open_register_unchecked(register_index); + let value_option = data.open_register_allow_empty_unchecked(register_index); + let value = match value_option { + Some(value) => value, + None => continue, + }; + let string = value.display(data); - if let Some(string) = value.as_string() { - message.push_str(string); - message.push('\n'); - } + message.push_str(&string); + message.push('\n'); } panic!("{}", message) diff --git a/dust-lang/src/native_function/io.rs b/dust-lang/src/native_function/io.rs index d4e571b..0267009 100644 --- a/dust-lang/src/native_function/io.rs +++ b/dust-lang/src/native_function/io.rs @@ -11,7 +11,6 @@ pub fn read_line( destination: Option, _argument_range: Range, ) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let destination = destination.unwrap(); let mut buffer = String::new(); @@ -22,27 +21,26 @@ pub fn read_line( let register = Register::Value(Value::Concrete(ConcreteValue::string(buffer))); - record.set_register(destination, register); + data.set_register(destination, register); } - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn write(data: &mut ThreadData, _destination: Option, argument_range: Range) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let mut stdout = stdout(); for register_index in argument_range { - if let Some(value) = record.open_register_allow_empty_unchecked(register_index) { - let string = value.display(record); + if let Some(value) = data.open_register_allow_empty_unchecked(register_index) { + let string = value.display(data); let _ = stdout.write(string.as_bytes()); } } let _ = stdout.flush(); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } @@ -52,19 +50,18 @@ pub fn write_line( _destination: Option, argument_range: Range, ) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let mut stdout = stdout().lock(); for register_index in argument_range { - if let Some(value) = record.open_register_allow_empty_unchecked(register_index) { - let string = value.display(record); + if let Some(value) = data.open_register_allow_empty_unchecked(register_index) { + let string = value.display(data); let _ = stdout.write(string.as_bytes()); let _ = stdout.write(b"\n"); } } let _ = stdout.flush(); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } diff --git a/dust-lang/src/native_function/string.rs b/dust-lang/src/native_function/string.rs index 48cd8b9..f368185 100644 --- a/dust-lang/src/native_function/string.rs +++ b/dust-lang/src/native_function/string.rs @@ -10,15 +10,14 @@ pub fn to_string( destination: Option, argument_range: Range, ) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; - let argument_value = record.open_register_unchecked(argument_range.start); - let argument_string = argument_value.display(record); + let argument_value = data.open_register_unchecked(argument_range.start); + let argument_string = argument_value.display(data); let destination = destination.unwrap(); let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string))); - record.set_register(destination, register); + data.set_register(destination, register); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } diff --git a/dust-lang/src/value/abstract_list.rs b/dust-lang/src/value/abstract_list.rs index 1b168f1..f678d77 100644 --- a/dust-lang/src/value/abstract_list.rs +++ b/dust-lang/src/value/abstract_list.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use crate::{vm::Record, Pointer, Type}; +use crate::{vm::ThreadData, Pointer, Type}; use super::DustString; @@ -11,7 +11,7 @@ pub struct AbstractList { } impl AbstractList { - pub fn display(&self, record: &Record) -> DustString { + pub fn display(&self, data: &ThreadData) -> DustString { let mut display = DustString::new(); display.push('['); @@ -21,7 +21,7 @@ impl AbstractList { display.push_str(", "); } - let item_display = record.follow_pointer(*item).display(record); + let item_display = data.follow_pointer_unchecked(*item).display(data); display.push_str(&item_display); } diff --git a/dust-lang/src/value/mod.rs b/dust-lang/src/value/mod.rs index 3664454..6da63fb 100644 --- a/dust-lang/src/value/mod.rs +++ b/dust-lang/src/value/mod.rs @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; use std::fmt::{self, Debug, Display, Formatter}; -use crate::{vm::Record, Type}; +use crate::{vm::ThreadData, Type}; #[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)] pub enum Value { @@ -179,9 +179,9 @@ impl Value { } } - pub fn display(&self, record: &Record) -> DustString { + pub fn display(&self, data: &ThreadData) -> DustString { match self { - Value::AbstractList(list) => list.display(record), + Value::AbstractList(list) => list.display(data), Value::Concrete(concrete_value) => concrete_value.display(), Value::Function(function) => DustString::from(function.to_string()), } diff --git a/dust-lang/src/vm/function_call.rs b/dust-lang/src/vm/function_call.rs new file mode 100644 index 0000000..86a0991 --- /dev/null +++ b/dust-lang/src/vm/function_call.rs @@ -0,0 +1,39 @@ +use std::fmt::{self, Debug, Display, Formatter}; + +use crate::{Chunk, DustString}; + +use super::Register; + +#[derive(Debug)] +pub struct FunctionCall<'a> { + pub chunk: &'a Chunk, + pub ip: usize, + pub return_register: u8, + pub registers: Vec, +} + +impl<'a> FunctionCall<'a> { + pub fn new(chunk: &'a Chunk, return_register: u8) -> Self { + Self { + chunk, + ip: 0, + return_register, + registers: vec![Register::Empty; chunk.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 fec393c..4f16b3e 100644 --- a/dust-lang/src/vm/mod.rs +++ b/dust-lang/src/vm/mod.rs @@ -1,5 +1,5 @@ //! Virtual machine and errors -mod record; +mod function_call; mod run_action; mod stack; mod thread; @@ -10,10 +10,10 @@ use std::{ thread::spawn, }; -pub use record::Record; +pub use function_call::FunctionCall; pub(crate) use run_action::get_next_action; pub use run_action::RunAction; -pub use stack::{FunctionCall, Stack}; +pub use stack::Stack; pub use thread::{Thread, ThreadData}; use tracing::{span, Level}; @@ -85,15 +85,19 @@ impl Display for Register { #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Pointer { - Stack(u8), + Register(u8), Constant(u8), + Stack(usize, u8), } impl Display for Pointer { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Self::Stack(index) => write!(f, "PR{}", index), + 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/record.rs b/dust-lang/src/vm/record.rs deleted file mode 100644 index 1a3fa33..0000000 --- a/dust-lang/src/vm/record.rs +++ /dev/null @@ -1,181 +0,0 @@ -use std::mem::replace; - -use tracing::trace; - -use crate::{Argument, Chunk, DustString, Function, Span, Value}; - -use super::{Pointer, Register}; - -#[derive(Debug)] -pub struct Record<'a> { - pub ip: usize, - pub chunk: &'a Chunk, - registers: Vec, -} - -impl<'a> Record<'a> { - pub fn new(chunk: &'a Chunk) -> Self { - Self { - ip: 0, - registers: vec![Register::Empty; chunk.register_count], - chunk, - } - } - - pub fn name(&self) -> Option<&DustString> { - self.chunk.name.as_ref() - } - - pub fn stack_size(&self) -> usize { - self.registers.len() - } - - pub fn current_position(&self) -> Span { - self.chunk.positions[self.ip] - } - - pub fn as_function(&self) -> Function { - self.chunk.as_function() - } - - pub(crate) fn follow_pointer(&self, pointer: Pointer) -> &Value { - trace!("Follow pointer {pointer}"); - - match pointer { - Pointer::Stack(register_index) => self.open_register_unchecked(register_index), - Pointer::Constant(constant_index) => self.get_constant_unchecked(constant_index), - } - } - - pub fn get_register_unchecked(&self, register_index: u8) -> &Register { - trace!("Get register R{register_index}"); - - let register_index = register_index as usize; - - 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; - - self.registers[to_register] = register; - } - - pub fn open_register_unchecked(&self, register_index: u8) -> &Value { - let register_index = register_index as usize; - - 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} opened to value {value}"); - - value - } - Register::Pointer(pointer) => { - trace!("Open register R{register_index} opened to pointer {pointer}"); - - self.follow_pointer(*pointer) - } - Register::Empty => panic!("VM Error: Register {register_index} is empty"), - } - } - - pub fn open_register_allow_empty_unchecked(&self, register_index: u8) -> Option<&Value> { - trace!("Open register R{register_index}"); - - let register = self.get_register_unchecked(register_index); - - match register { - Register::Value(value) => { - trace!("Register R{register_index} openned to value {value}"); - - Some(value) - } - Register::Pointer(pointer) => { - trace!("Open register R{register_index} openned to pointer {pointer}"); - - Some(self.follow_pointer(*pointer)) - } - Register::Empty => None, - } - } - - pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u8) -> Value { - let register_index = register_index as usize; - - assert!( - register_index < self.registers.len(), - "VM Error: Register index out of bounds" - ); - - 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.empty_register_or_clone_constant_unchecked(register_index) - } - Pointer::Constant(constant_index) => { - self.get_constant_unchecked(constant_index).clone() - } - }, - Register::Empty => panic!("VM Error: Register {register_index} is empty"), - } - } - - pub fn clone_register_value_or_constant_unchecked(&self, register_index: u8) -> Value { - let register = self.get_register_unchecked(register_index); - - match register { - Register::Value(value) => value.clone(), - Register::Pointer(pointer) => match pointer { - Pointer::Stack(register_index) => { - self.open_register_unchecked(*register_index).clone() - } - Pointer::Constant(constant_index) => { - self.get_constant_unchecked(*constant_index).clone() - } - }, - 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: Argument) -> &Value { - match argument { - Argument::Constant(constant_index) => self.get_constant_unchecked(constant_index), - Argument::Register(register_index) => self.open_register_unchecked(register_index), - } - } - - pub fn get_constant_unchecked(&self, constant_index: u8) -> &Value { - let constant_index = constant_index as usize; - - if cfg!(debug_assertions) { - &self.chunk.constants[constant_index] - } else { - unsafe { self.chunk.constants.get_unchecked(constant_index) } - } - } - - pub fn get_local_register(&self, local_index: u8) -> u8 { - let local_index = local_index as usize; - - assert!( - local_index < self.chunk.locals.len(), - "VM Error: Local index out of bounds" - ); - - self.chunk.locals[local_index].register_index - } -} diff --git a/dust-lang/src/vm/run_action.rs b/dust-lang/src/vm/run_action.rs index be1ae38..3af2630 100644 --- a/dust-lang/src/vm/run_action.rs +++ b/dust-lang/src/vm/run_action.rs @@ -7,10 +7,10 @@ use crate::{ Return, SetLocal, Subtract, Test, TestSet, }, vm::FunctionCall, - AbstractList, Argument, ConcreteValue, DustString, Instruction, Type, Value, + AbstractList, Argument, ConcreteValue, Instruction, Type, Value, }; -use super::{thread::ThreadData, Pointer, Record, Register}; +use super::{thread::ThreadData, Pointer, Register}; #[derive(Clone, Copy, Debug, PartialEq)] pub struct RunAction { @@ -57,48 +57,46 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [ r#return, ]; -pub(crate) fn get_next_action(record: &mut Record) -> RunAction { - let instruction = record.chunk.instructions[record.ip]; +pub(crate) fn get_next_action(data: &mut ThreadData) -> RunAction { + let current_call = data.call_stack.last_mut_unchecked(); + let instruction = current_call.chunk.instructions[current_call.ip]; let operation = instruction.operation(); let logic = RUNNER_LOGIC_TABLE[operation.0 as usize]; - record.ip += 1; + current_call.ip += 1; RunAction { logic, instruction } } pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let Point { from, to } = instruction.into(); - let from_register = record.get_register_unchecked(from); + let from_register = data.get_register_unchecked(from); let from_register_is_empty = matches!(from_register, Register::Empty); if !from_register_is_empty { - let register = Register::Pointer(Pointer::Stack(to)); + let register = Register::Pointer(Pointer::Register(to)); - record.set_register(from, register); + data.set_register(from, register); } - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let Close { from, to } = instruction.into(); for register_index in from..to { - record.set_register(register_index, Register::Empty); + data.set_register(register_index, Register::Empty); } - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let LoadBoolean { destination, value, @@ -107,19 +105,20 @@ pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool { let boolean = Value::Concrete(ConcreteValue::Boolean(value)); let register = Register::Value(boolean); - record.set_register(destination, register); + data.set_register(destination, register); if jump_next { - record.ip += 1; + let current_call = data.call_stack.last_mut_unchecked(); + + current_call.ip += 1; } - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let LoadConstant { destination, constant_index, @@ -129,19 +128,20 @@ pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool { trace!("Load constant {constant_index} into R{destination}"); - record.set_register(destination, register); + data.set_register(destination, register); if jump_next { - record.ip += 1; + let current_call = data.call_stack.last_mut_unchecked(); + + current_call.ip += 1; } - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let LoadList { destination, start_register, @@ -150,7 +150,7 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool { let mut item_type = Type::Any; for register_index in start_register..destination { - match record.get_register_unchecked(register_index) { + match data.get_register_unchecked(register_index) { Register::Empty => continue, Register::Value(value) => { if item_type == Type::Any { @@ -159,12 +159,12 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool { } Register::Pointer(pointer) => { if item_type == Type::Any { - item_type = record.follow_pointer(*pointer).r#type(); + item_type = data.follow_pointer_unchecked(*pointer).r#type(); } } } - let pointer = Pointer::Stack(register_index); + let pointer = Pointer::Register(register_index); item_pointers.push(pointer); } @@ -175,122 +175,119 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool { }); let register = Register::Value(list_value); - record.set_register(destination, register); + data.set_register(destination, register); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let LoadFunction { destination, prototype_index, } = instruction.into(); let prototype_index = prototype_index as usize; - let function = record.chunk.prototypes[prototype_index].as_function(); + let current_call = data.call_stack.last_mut_unchecked(); + let prototype = ¤t_call.chunk.prototypes[prototype_index]; + let function = prototype.as_function(); let register = Register::Value(Value::Function(function)); - record.set_register(destination, register); + data.set_register(destination, register); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let LoadSelf { destination } = instruction.into(); - let function = record.as_function(); + let current_call = data.call_stack.last_mut_unchecked(); + let prototype = ¤t_call.chunk; + let function = prototype.as_function(); let register = Register::Value(Value::Function(function)); - record.set_register(destination, register); + data.set_register(destination, register); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let GetLocal { destination, local_index, } = instruction.into(); - let local_register_index = record.get_local_register(local_index); - let register = Register::Pointer(Pointer::Stack(local_register_index)); + let local_register_index = data.get_local_register(local_index); + let register = Register::Pointer(Pointer::Register(local_register_index)); - record.set_register(destination, register); + data.set_register(destination, register); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let SetLocal { register_index, local_index, } = instruction.into(); - let local_register_index = record.get_local_register(local_index); - let register = Register::Pointer(Pointer::Stack(register_index)); + let local_register_index = data.get_local_register(local_index); + let register = Register::Pointer(Pointer::Register(register_index)); - record.set_register(local_register_index, register); + data.set_register(local_register_index, register); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let Add { destination, left, right, } = instruction.into(); - let left = record.get_argument_unchecked(left); - let right = record.get_argument_unchecked(right); + let left = data.get_argument_unchecked(left); + let right = data.get_argument_unchecked(right); let sum = left.add(right); let register = Register::Value(sum); - record.set_register(destination, register); + data.set_register(destination, register); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let Subtract { destination, left, right, } = instruction.into(); - let left = record.get_argument_unchecked(left); - let right = record.get_argument_unchecked(right); + let left = data.get_argument_unchecked(left); + let right = data.get_argument_unchecked(right); let difference = left.subtract(right); let register = Register::Value(difference); - record.set_register(destination, register); + data.set_register(destination, register); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let Multiply { destination, left, right, } = instruction.into(); - let left = record.get_argument_unchecked(left); - let right = record.get_argument_unchecked(right); + let left = data.get_argument_unchecked(left); + let right = data.get_argument_unchecked(right); let product = match (left, right) { (Value::Concrete(left), Value::Concrete(right)) => match (left, right) { (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { @@ -302,22 +299,21 @@ pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool { }; let register = Register::Value(product); - record.set_register(destination, register); + data.set_register(destination, register); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let Divide { destination, left, right, } = instruction.into(); - let left = record.get_argument_unchecked(left); - let right = record.get_argument_unchecked(right); + let left = data.get_argument_unchecked(left); + let right = data.get_argument_unchecked(right); let quotient = match (left, right) { (Value::Concrete(left), Value::Concrete(right)) => match (left, right) { (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { @@ -329,22 +325,21 @@ pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool { }; let register = Register::Value(quotient); - record.set_register(destination, register); + data.set_register(destination, register); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let Modulo { destination, left, right, } = instruction.into(); - let left = record.get_argument_unchecked(left); - let right = record.get_argument_unchecked(right); + let left = data.get_argument_unchecked(left); + let right = data.get_argument_unchecked(right); let remainder = match (left, right) { (Value::Concrete(left), Value::Concrete(right)) => match (left, right) { (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { @@ -356,20 +351,19 @@ pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool { }; let register = Register::Value(remainder); - record.set_register(destination, register); + data.set_register(destination, register); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let Test { operand_register, test_value, } = instruction.into(); - let value = record.open_register_unchecked(operand_register); + let value = data.open_register_unchecked(operand_register); let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value { *boolean } else { @@ -377,22 +371,23 @@ pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool { }; if boolean == test_value { - record.ip += 1; + let current_call = data.call_stack.last_mut_unchecked(); + + current_call.ip += 1; } - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let TestSet { destination, argument, test_value, } = instruction.into(); - let value = record.get_argument_unchecked(argument); + let value = data.get_argument_unchecked(argument); let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value { *boolean } else { @@ -403,179 +398,161 @@ pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool { } else { let pointer = match argument { Argument::Constant(constant_index) => Pointer::Constant(constant_index), - Argument::Register(register_index) => Pointer::Stack(register_index), + Argument::Register(register_index) => Pointer::Register(register_index), }; let register = Register::Pointer(pointer); - record.set_register(destination, register); + data.set_register(destination, register); } - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let Equal { value, left, right } = instruction.into(); - let left = record.get_argument_unchecked(left); - let right = record.get_argument_unchecked(right); + let left = data.get_argument_unchecked(left); + let right = data.get_argument_unchecked(right); let is_equal = left.equals(right); if is_equal == value { - record.ip += 1; + let current_call = data.call_stack.last_mut_unchecked(); + + current_call.ip += 1; } - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let Less { value, left, right } = instruction.into(); - let left = record.get_argument_unchecked(left); - let right = record.get_argument_unchecked(right); + let left = data.get_argument_unchecked(left); + let right = data.get_argument_unchecked(right); let is_less = left < right; if is_less == value { - record.ip += 1; + let current_call = data.call_stack.last_mut_unchecked(); + + current_call.ip += 1; } - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let LessEqual { value, left, right } = instruction.into(); - let left = record.get_argument_unchecked(left); - let right = record.get_argument_unchecked(right); + let left = data.get_argument_unchecked(left); + let right = data.get_argument_unchecked(right); let is_less_or_equal = left <= right; if is_less_or_equal == value { - record.ip += 1; + let current_call = data.call_stack.last_mut_unchecked(); + + current_call.ip += 1; } - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let Negate { destination, argument, } = instruction.into(); - let argument = record.get_argument_unchecked(argument); + let argument = data.get_argument_unchecked(argument); let negated = argument.negate(); let register = Register::Value(negated); - record.set_register(destination, register); + data.set_register(destination, register); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn not(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let Not { destination, argument, } = instruction.into(); - let argument = record.get_argument_unchecked(argument); + let argument = data.get_argument_unchecked(argument); let not = match argument { Value::Concrete(ConcreteValue::Boolean(boolean)) => ConcreteValue::Boolean(!boolean), _ => panic!("VM Error: Expected boolean value for NOT operation"), }; let register = Register::Value(Value::Concrete(not)); - record.set_register(destination, register); + data.set_register(destination, register); - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn jump(instruction: Instruction, data: &mut ThreadData) -> bool { - let record = &mut data.call_stack.last_mut_unchecked().record; let Jump { offset, is_positive, } = instruction.into(); let offset = offset as usize; + let current_call = data.call_stack.last_mut_unchecked(); if is_positive { - record.ip += offset; + current_call.ip += offset; } else { - record.ip -= offset + 1; + current_call.ip -= offset + 1; } - data.next_action = get_next_action(record); + data.next_action = get_next_action(data); false } pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool { - let current_call = data.call_stack.pop_unchecked(); let Call { destination: return_register, function_register, argument_count, is_recursive, } = instruction.into(); - let function = current_call - .record - .open_register_unchecked(function_register) - .as_function() - .unwrap(); + let current_call = data.call_stack.last_unchecked(); let first_argument_register = return_register - argument_count; let prototype = if is_recursive { - current_call.record.chunk + current_call.chunk } else { - ¤t_call.record.chunk.prototypes[function.prototype_index as usize] - }; - let mut next_call = FunctionCall { - name: prototype.name.clone(), - return_register, - ip: current_call.ip, - record: Record::new(prototype), + let function = data + .open_register_unchecked(function_register) + .as_function() + .unwrap(); + + ¤t_call.chunk.prototypes[function.prototype_index as usize] }; + let mut next_call = FunctionCall::new(prototype, return_register); let mut argument_index = 0; for register_index in first_argument_register..return_register { - let value_option = current_call - .record - .open_register_allow_empty_unchecked(register_index); + let value_option = data.open_register_allow_empty_unchecked(register_index); let argument = if let Some(value) = value_option { value.clone() } else { continue; }; - trace!( - "Passing argument \"{argument}\" to {}", - function - .name - .clone() - .unwrap_or_else(|| DustString::from("anonymous")) - ); - - next_call - .record - .set_register(argument_index, Register::Value(argument)); - + next_call.registers[argument_index] = Register::Value(argument); argument_index += 1; } - data.next_action = get_next_action(&mut next_call.record); - - data.call_stack.push(current_call); data.call_stack.push(next_call); + data.next_action = get_next_action(data); + false } @@ -598,30 +575,24 @@ pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool { should_return_value, return_register, } = instruction.into(); - let mut current_call = if data.call_stack.len() == 1 { + let (destination, return_value) = if data.call_stack.len() == 1 { if should_return_value { data.return_value_index = Some(return_register); }; return true; } else { - data.call_stack.pop_unchecked() + let return_value = data.empty_register_or_clone_constant_unchecked(return_register); + let destination = data.call_stack.pop_unchecked().return_register; + + (destination, return_value) }; - let outer_call = data.call_stack.last_mut_unchecked(); - if should_return_value { - let return_value = current_call - .record - .empty_register_or_clone_constant_unchecked(return_register); - let destination = current_call.return_register; - - outer_call - .record - .set_register(destination, Register::Value(return_value)); + data.set_register(destination, Register::Value(return_value)); } - data.next_action = get_next_action(&mut outer_call.record); + data.next_action = get_next_action(data); false } diff --git a/dust-lang/src/vm/stack.rs b/dust-lang/src/vm/stack.rs index 752dcd8..ac8d2e9 100644 --- a/dust-lang/src/vm/stack.rs +++ b/dust-lang/src/vm/stack.rs @@ -3,9 +3,7 @@ use std::{ ops::{Index, IndexMut, Range}, }; -use crate::DustString; - -use super::Record; +use super::FunctionCall; #[derive(Clone, PartialEq)] pub struct Stack { @@ -43,6 +41,16 @@ impl Stack { } } + 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); } @@ -118,36 +126,12 @@ impl Debug for Stack { impl Display for Stack> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - writeln!(f, "-- DUST CALL STACK --")?; + writeln!(f, "----- DUST CALL STACK -----")?; - for function_call in self.items.iter().rev() { - writeln!(f, "{function_call}")?; + for (index, function_call) in self.items.iter().enumerate().rev() { + writeln!(f, "{index:02} | {function_call}")?; } - writeln!(f, "--") - } -} - -#[derive(Debug)] -pub struct FunctionCall<'a> { - pub name: Option, - pub return_register: u8, - pub ip: usize, - pub record: Record<'a>, -} - -impl Display for FunctionCall<'_> { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let FunctionCall { - name, - return_register, - .. - } = self; - let name = name - .as_ref() - .map(|name| name.as_str()) - .unwrap_or("anonymous"); - - write!(f, "{name} (Return register: {return_register})") + write!(f, "---------------------------") } } diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs index 462479c..8152a5b 100644 --- a/dust-lang/src/vm/thread.rs +++ b/dust-lang/src/vm/thread.rs @@ -1,8 +1,10 @@ +use std::mem::replace; + use tracing::{info, trace}; -use crate::{vm::FunctionCall, Chunk, DustString, Value}; +use crate::{vm::FunctionCall, Argument, Chunk, DustString, Span, Value}; -use super::{record::Record, RunAction, Stack}; +use super::{Pointer, Register, RunAction, Stack}; pub struct Thread { chunk: Chunk, @@ -23,12 +25,7 @@ impl Thread { ); let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1); - let main_call = FunctionCall { - name: self.chunk.name.clone(), - return_register: 0, // Never used, the main function's return is the thread's return - ip: 0, - record: Record::new(&self.chunk), - }; + let main_call = FunctionCall::new(&self.chunk, 0); call_stack.push(main_call); @@ -49,11 +46,8 @@ impl Thread { if should_end { let return_value = if let Some(register_index) = thread_data.return_value_index { - let value = thread_data - .call_stack - .last_mut_unchecked() - .record - .empty_register_or_clone_constant_unchecked(register_index); + let value = + thread_data.empty_register_or_clone_constant_unchecked(register_index); Some(value) } else { @@ -72,3 +66,207 @@ pub struct ThreadData<'a> { pub next_action: RunAction, pub return_value_index: Option, } + +impl ThreadData<'_> { + pub fn current_position(&self) -> Span { + let current_call = self.call_stack.last_unchecked(); + + current_call.chunk.positions[current_call.ip] + } + + pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value { + trace!("Follow pointer {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: u8) -> &Register { + trace!("Get register R{register_index}"); + + let register_index = register_index as usize; + + if cfg!(debug_assertions) { + &self.call_stack.last_unchecked().registers[register_index] + } else { + unsafe { + self.call_stack + .last_unchecked() + .registers + .get_unchecked(register_index) + } + } + } + + pub fn set_register(&mut self, to_register: u8, register: Register) { + let to_register = to_register as usize; + + self.call_stack.last_mut_unchecked().registers[to_register] = register; + } + + pub fn open_register_unchecked(&self, register_index: u8) -> &Value { + let register_index = register_index as usize; + + let register = if cfg!(debug_assertions) { + &self.call_stack.last_unchecked().registers[register_index] + } else { + unsafe { + self.call_stack + .last_unchecked() + .registers + .get_unchecked(register_index) + } + }; + + match register { + Register::Value(value) => { + trace!("Register R{register_index} opened to value {value}"); + + value + } + Register::Pointer(pointer) => { + trace!("Open register R{register_index} opened to pointer {pointer}"); + + self.follow_pointer_unchecked(*pointer) + } + Register::Empty => panic!("VM Error: Register {register_index} is empty"), + } + } + + pub fn open_register_allow_empty_unchecked(&self, register_index: u8) -> Option<&Value> { + trace!("Open register R{register_index}"); + + let register = self.get_register_unchecked(register_index); + + match register { + Register::Value(value) => { + trace!("Register R{register_index} openned to value {value}"); + + Some(value) + } + Register::Pointer(pointer) => { + trace!("Open register R{register_index} openned to pointer {pointer}"); + + Some(self.follow_pointer_unchecked(*pointer)) + } + Register::Empty => None, + } + } + + pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u8) -> 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: u8) -> 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: Argument) -> &Value { + match argument { + Argument::Constant(constant_index) => self.get_constant_unchecked(constant_index), + Argument::Register(register_index) => self.open_register_unchecked(register_index), + } + } + + pub fn get_constant_unchecked(&self, constant_index: u8) -> &Value { + let constant_index = constant_index as usize; + + if cfg!(debug_assertions) { + &self.call_stack.last().unwrap().chunk.constants[constant_index] + } else { + unsafe { + self.call_stack + .last_unchecked() + .chunk + .constants + .get_unchecked(constant_index) + } + } + } + + pub fn get_local_register(&self, local_index: u8) -> u8 { + let local_index = local_index as usize; + let chunk = self.call_stack.last_unchecked().chunk; + + assert!( + local_index < chunk.locals.len(), + "VM Error: Local index out of bounds" + ); + + chunk.locals[local_index].register_index + } +}