1
0

Improve VM layout and performance

This commit is contained in:
Jeff 2025-01-08 10:29:53 -05:00
parent 1f88d77476
commit 6cfa0f58e3
14 changed files with 303 additions and 241 deletions

7
Cargo.lock generated
View File

@ -342,6 +342,7 @@ dependencies = [
"smallvec", "smallvec",
"smartstring", "smartstring",
"tracing", "tracing",
"typed-arena",
] ]
[[package]] [[package]]
@ -946,6 +947,12 @@ dependencies = [
"tracing-log", "tracing-log",
] ]
[[package]]
name = "typed-arena"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.13" version = "1.0.13"

View File

@ -22,6 +22,7 @@ smartstring = { version = "1.0.1", features = [
"serde", "serde",
], default-features = false } ], default-features = false }
tracing = "0.1.41" tracing = "0.1.41"
typed-arena = "2.0.2"
[dev-dependencies] [dev-dependencies]
criterion = { version = "0.3.4", features = ["html_reports"] } criterion = { version = "0.3.4", features = ["html_reports"] }

View File

@ -42,7 +42,7 @@ pub struct Chunk {
pub(crate) locals: SmallVec<[Local; 8]>, pub(crate) locals: SmallVec<[Local; 8]>,
pub(crate) prototypes: Vec<Chunk>, pub(crate) prototypes: Vec<Chunk>,
pub(crate) stack_size: usize, pub(crate) register_count: usize,
pub(crate) prototype_index: u8, pub(crate) prototype_index: u8,
} }
@ -65,7 +65,7 @@ impl Chunk {
constants: constants.into(), constants: constants.into(),
locals: locals.into(), locals: locals.into(),
prototypes, prototypes,
stack_size: 0, register_count: 0,
prototype_index: 0, prototype_index: 0,
} }
} }

View File

@ -214,7 +214,7 @@ impl<'src> Compiler<'src> {
constants: self.constants, constants: self.constants,
locals, locals,
prototypes: self.prototypes, prototypes: self.prototypes,
stack_size: self.stack_size, register_count: self.stack_size,
prototype_index: self.prototype_index, prototype_index: self.prototype_index,
} }
} }
@ -1000,9 +1000,9 @@ impl<'src> Compiler<'src> {
return self.parse_call_native(native_function); return self.parse_call_native(native_function);
} else if self.function_name.as_deref() == Some(identifier) { } else if self.function_name.as_deref() == Some(identifier) {
let destination = self.next_register(); 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(()); return Ok(());
} else { } else {
@ -1700,6 +1700,7 @@ impl<'src> Compiler<'src> {
}); });
} }
}; };
let is_recursive = last_instruction_type == &Type::SelfFunction;
let mut argument_count = 0; let mut argument_count = 0;
@ -1727,7 +1728,7 @@ impl<'src> Compiler<'src> {
let end = self.current_position.1; let end = self.current_position.1;
let destination = self.next_register(); 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)); self.emit_instruction(call, function_return_type, Span(start, end));

View File

@ -4,6 +4,7 @@ pub struct Call {
pub destination: u8, pub destination: u8,
pub function_register: u8, pub function_register: u8,
pub argument_count: u8, pub argument_count: u8,
pub is_recursive: bool,
} }
impl From<Instruction> for Call { impl From<Instruction> for Call {
@ -11,11 +12,13 @@ impl From<Instruction> for Call {
let destination = instruction.a_field(); let destination = instruction.a_field();
let function_register = instruction.b_field(); let function_register = instruction.b_field();
let argument_count = instruction.c_field(); let argument_count = instruction.c_field();
let is_recursive = instruction.d_field();
Call { Call {
destination, destination,
function_register, function_register,
argument_count, argument_count,
is_recursive,
} }
} }
} }
@ -25,7 +28,8 @@ impl From<Call> for Instruction {
let a = call.destination; let a = call.destination;
let b = call.function_register; let b = call.function_register;
let c = call.argument_count; 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)
} }
} }

View File

@ -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 { Instruction::from(Call {
destination, destination,
function_register, function_register,
argument_count, argument_count,
is_recursive,
}) })
} }
@ -691,6 +697,7 @@ impl Instruction {
destination, destination,
function_register, function_register,
argument_count, argument_count,
..
} = Call::from(*self); } = Call::from(*self);
let arguments_start = destination.saturating_sub(argument_count); let arguments_start = destination.saturating_sub(argument_count);

View File

@ -14,7 +14,7 @@ pub fn panic(
let mut message = format!("Dust panic at {position}!"); let mut message = format!("Dust panic at {position}!");
for register_index in argument_range { 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() { if let Some(string) = value.as_string() {
message.push_str(string); message.push_str(string);

View File

@ -10,7 +10,7 @@ pub fn to_string(
destination: Option<u8>, destination: Option<u8>,
argument_range: Range<u8>, argument_range: Range<u8>,
) -> Result<ThreadSignal, NativeFunctionError> { ) -> Result<ThreadSignal, NativeFunctionError> {
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 argument_string = argument_value.display(record);
let destination = destination.unwrap(); let destination = destination.unwrap();
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string))); let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));

View File

@ -6,7 +6,7 @@ use super::{stack::Stack, FunctionCall};
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum VmError { pub enum VmError {
CallStackUnderflow, StackUnderflow,
ExpectedFunction { ExpectedFunction {
value: Value, value: Value,
}, },
@ -22,7 +22,7 @@ pub enum VmError {
impl Display for VmError { impl Display for VmError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Self::CallStackUnderflow => { Self::StackUnderflow => {
write!(f, "Call stack underflow") write!(f, "Call stack underflow")
} }
Self::ExpectedFunction { value } => { Self::ExpectedFunction { value } => {

View File

@ -13,7 +13,7 @@ use std::{
pub use error::VmError; pub use error::VmError;
pub use record::Record; pub use record::Record;
pub use run_action::RunAction; pub use run_action::RecordAction;
pub use stack::{FunctionCall, Stack}; pub use stack::{FunctionCall, Stack};
pub use thread::{Thread, ThreadSignal}; pub use thread::{Thread, ThreadSignal};
use tracing::{span, Level}; use tracing::{span, Level};

View File

@ -10,7 +10,7 @@ use super::{Pointer, Register};
pub struct Record<'a> { pub struct Record<'a> {
pub ip: usize, pub ip: usize,
pub chunk: &'a Chunk, pub chunk: &'a Chunk,
stack: Vec<Register>, registers: Vec<Register>,
} }
impl<'a> Record<'a> { impl<'a> Record<'a> {
@ -18,7 +18,7 @@ impl<'a> Record<'a> {
pub fn new(chunk: &'a Chunk) -> Self { pub fn new(chunk: &'a Chunk) -> Self {
Self { Self {
ip: 0, ip: 0,
stack: vec![Register::Empty; chunk.stack_size], registers: vec![Register::Empty; chunk.register_count],
chunk, chunk,
} }
} }
@ -28,7 +28,7 @@ impl<'a> Record<'a> {
} }
pub fn stack_size(&self) -> usize { pub fn stack_size(&self) -> usize {
self.stack.len() self.registers.len()
} }
pub fn current_position(&self) -> Span { pub fn current_position(&self) -> Span {
@ -43,59 +43,46 @@ impl<'a> Record<'a> {
trace!("Follow pointer {pointer}"); trace!("Follow pointer {pointer}");
match 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), 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}"); trace!("Get register R{register_index}");
let register_index = register_index as usize; let register_index = register_index as usize;
assert!( if cfg!(debug_assertions) {
register_index < self.stack.len(), &self.registers[register_index]
"VM Error: Register index out of bounds" } else {
); unsafe { self.registers.get_unchecked(register_index) }
}
&self.stack[register_index]
} }
pub fn set_register(&mut self, to_register: u8, register: Register) { pub fn set_register(&mut self, to_register: u8, register: Register) {
let to_register = to_register as usize; let to_register = to_register as usize;
assert!( self.registers[to_register] = register;
to_register < self.stack.len(),
"VM Error: Register index out of bounds"
);
self.stack[to_register] = register;
} }
pub fn reserve_registers(&mut self, count: usize) { pub fn open_register_unchecked(&self, register_index: u8) -> &Value {
for _ in 0..count {
self.stack.push(Register::Empty);
}
}
pub fn open_register(&self, register_index: u8) -> &Value {
let register_index = register_index as usize; let register_index = register_index as usize;
assert!( let register = if cfg!(debug_assertions) {
register_index < self.stack.len(), &self.registers[register_index]
"VM Error: Register index out of bounds" } else {
); unsafe { self.registers.get_unchecked(register_index) }
};
let register = &self.stack[register_index];
match register { match register {
Register::Value(value) => { Register::Value(value) => {
trace!("Register R{register_index} openned to value {value}"); trace!("Register R{register_index} opened to value {value}");
value value
} }
Register::Pointer(pointer) => { 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) self.follow_pointer(*pointer)
} }
@ -109,11 +96,11 @@ impl<'a> Record<'a> {
let register_index = register_index as usize; let register_index = register_index as usize;
assert!( assert!(
register_index < self.stack.len(), register_index < self.registers.len(),
"VM Error: Register index out of bounds" "VM Error: Register index out of bounds"
); );
let register = &self.stack[register_index]; let register = &self.registers[register_index];
match register { match register {
Register::Value(value) => { Register::Value(value) => {
@ -130,24 +117,22 @@ impl<'a> Record<'a> {
} }
} }
pub fn empty_register_or_clone_constant( pub fn empty_register_or_clone_constant(&mut self, register_index: u8) -> Value {
&mut self,
register_index: u8,
new_register: Register,
) -> Value {
let register_index = register_index as usize; let register_index = register_index as usize;
assert!( assert!(
register_index < self.stack.len(), register_index < self.registers.len(),
"VM Error: Register index out of bounds" "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 { match old_register {
Register::Value(value) => value, Register::Value(value) => value,
Register::Pointer(pointer) => match pointer { 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(), Pointer::Constant(constant_index) => self.get_constant(constant_index).clone(),
}, },
Register::Empty => panic!("VM Error: Register {register_index} is empty"), 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 { pub fn clone_register_value_or_constant(&self, register_index: u8) -> Value {
assert!( assert!(
(register_index as usize) < self.stack.len(), (register_index as usize) < self.registers.len(),
"VM Error: Register index out of bounds" "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 { match register {
Register::Value(value) => value.clone(), Register::Value(value) => value.clone(),
Register::Pointer(pointer) => match pointer { 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(), Pointer::Constant(constant_index) => self.get_constant(*constant_index).clone(),
}, },
Register::Empty => panic!("VM Error: Register {register_index} is empty"), 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 { pub fn get_argument(&self, argument: Argument) -> &Value {
match argument { match argument {
Argument::Constant(constant_index) => self.get_constant(constant_index), 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),
} }
} }

View File

@ -6,27 +6,28 @@ use crate::{
LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point, LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
Return, SetLocal, Subtract, Test, TestSet, 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)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct RunAction { pub struct RecordAction {
pub logic: RunnerLogic, pub logic: RunnerLogic,
pub instruction: Instruction, pub instruction: Instruction,
} }
impl From<Instruction> for RunAction { impl From<Instruction> for RecordAction {
fn from(instruction: Instruction) -> Self { fn from(instruction: Instruction) -> Self {
let operation = instruction.operation(); let operation = instruction.operation();
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize]; 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<Value>;
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
point, point,
@ -56,9 +57,10 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
r#return, r#return,
]; ];
pub fn point(instruction: Instruction, record: &mut Record) -> ThreadSignal { pub fn point(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
let record = data.records.last_mut_unchecked();
let Point { from, to } = instruction.into(); 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); let from_register_is_empty = matches!(from_register, Register::Empty);
if !from_register_is_empty { if !from_register_is_empty {
@ -67,10 +69,13 @@ pub fn point(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.set_register(from, register); 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<Value> {
let record = data.records.last_mut_unchecked();
let Close { from, to } = instruction.into(); let Close { from, to } = instruction.into();
assert!(from < to, "Runtime Error: Malformed instruction"); 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); 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<Value> {
let record = data.records.last_mut_unchecked();
let LoadBoolean { let LoadBoolean {
destination, destination,
value, value,
@ -102,10 +110,13 @@ pub fn load_boolean(instruction: Instruction, record: &mut Record) -> ThreadSign
record.ip += 1; 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<Value> {
let record = data.records.last_mut_unchecked();
let LoadConstant { let LoadConstant {
destination, destination,
constant_index, constant_index,
@ -121,10 +132,13 @@ pub fn load_constant(instruction: Instruction, record: &mut Record) -> ThreadSig
record.ip += 1; 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<Value> {
let record = data.records.last_mut_unchecked();
let LoadList { let LoadList {
destination, destination,
start_register, start_register,
@ -133,7 +147,7 @@ pub fn load_list(instruction: Instruction, record: &mut Record) -> ThreadSignal
let mut item_type = Type::Any; let mut item_type = Type::Any;
for register_index in start_register..destination { for register_index in start_register..destination {
match record.get_register(register_index) { match record.get_register_unchecked(register_index) {
Register::Empty => continue, Register::Empty => continue,
Register::Value(value) => { Register::Value(value) => {
if item_type == Type::Any { if item_type == Type::Any {
@ -160,32 +174,43 @@ pub fn load_list(instruction: Instruction, record: &mut Record) -> ThreadSignal
record.set_register(destination, register); 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<Value> {
let record = data.records.last_mut_unchecked();
let LoadFunction { let LoadFunction {
destination, destination,
prototype_index, prototype_index,
} = instruction.into(); } = 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 { record.set_register(destination, register);
destination,
prototype_index, record.ip += 1;
}
None
} }
pub fn load_self(instruction: Instruction, record: &mut Record) -> ThreadSignal { pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
let record = data.records.last_mut_unchecked();
let LoadSelf { destination } = instruction.into(); let LoadSelf { destination } = instruction.into();
let function = record.as_function(); let function = record.as_function();
let register = Register::Value(Value::Function(function)); let register = Register::Value(Value::Function(function));
record.set_register(destination, register); 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<Value> {
let record = data.records.last_mut_unchecked();
let GetLocal { let GetLocal {
destination, destination,
local_index, local_index,
@ -195,10 +220,13 @@ pub fn get_local(instruction: Instruction, record: &mut Record) -> ThreadSignal
record.set_register(destination, register); 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<Value> {
let record = data.records.last_mut_unchecked();
let SetLocal { let SetLocal {
register_index, register_index,
local_index, local_index,
@ -208,10 +236,13 @@ pub fn set_local(instruction: Instruction, record: &mut Record) -> ThreadSignal
record.set_register(local_register_index, register); 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<Value> {
let record = data.records.last_mut_unchecked();
let Add { let Add {
destination, destination,
left, left,
@ -224,10 +255,13 @@ pub fn add(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.set_register(destination, register); 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<Value> {
let record = data.records.last_mut_unchecked();
let Subtract { let Subtract {
destination, destination,
left, left,
@ -240,10 +274,13 @@ pub fn subtract(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.set_register(destination, register); 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<Value> {
let record = data.records.last_mut_unchecked();
let Multiply { let Multiply {
destination, destination,
left, left,
@ -264,10 +301,13 @@ pub fn multiply(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.set_register(destination, register); 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<Value> {
let record = data.records.last_mut_unchecked();
let Divide { let Divide {
destination, destination,
left, left,
@ -288,10 +328,13 @@ pub fn divide(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.set_register(destination, register); 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<Value> {
let record = data.records.last_mut_unchecked();
let Modulo { let Modulo {
destination, destination,
left, left,
@ -312,15 +355,18 @@ pub fn modulo(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.set_register(destination, register); 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<Value> {
let record = data.records.last_mut_unchecked();
let Test { let Test {
operand_register, operand_register,
test_value, test_value,
} = instruction.into(); } = 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 { let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
*boolean *boolean
} else { } else {
@ -331,10 +377,13 @@ pub fn test(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.ip += 1; 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<Value> {
let record = data.records.last_mut_unchecked();
let TestSet { let TestSet {
destination, destination,
argument, argument,
@ -359,10 +408,13 @@ pub fn test_set(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.set_register(destination, register); 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<Value> {
let record = data.records.last_mut_unchecked();
let Equal { value, left, right } = instruction.into(); let Equal { value, left, right } = instruction.into();
let left = record.get_argument(left); let left = record.get_argument(left);
let right = record.get_argument(right); let right = record.get_argument(right);
@ -372,10 +424,13 @@ pub fn equal(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.ip += 1; 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<Value> {
let record = data.records.last_mut_unchecked();
let Less { value, left, right } = instruction.into(); let Less { value, left, right } = instruction.into();
let left = record.get_argument(left); let left = record.get_argument(left);
let right = record.get_argument(right); let right = record.get_argument(right);
@ -385,10 +440,13 @@ pub fn less(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.ip += 1; 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<Value> {
let record = data.records.last_mut_unchecked();
let LessEqual { value, left, right } = instruction.into(); let LessEqual { value, left, right } = instruction.into();
let left = record.get_argument(left); let left = record.get_argument(left);
let right = record.get_argument(right); let right = record.get_argument(right);
@ -398,10 +456,13 @@ pub fn less_equal(instruction: Instruction, record: &mut Record) -> ThreadSignal
record.ip += 1; 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<Value> {
let record = data.records.last_mut_unchecked();
let Negate { let Negate {
destination, destination,
argument, argument,
@ -412,10 +473,13 @@ pub fn negate(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.set_register(destination, register); 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<Value> {
let record = data.records.last_mut_unchecked();
let Not { let Not {
destination, destination,
argument, argument,
@ -429,10 +493,13 @@ pub fn not(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.set_register(destination, register); 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<Value> {
let record = data.records.last_mut_unchecked();
let Jump { let Jump {
offset, offset,
is_positive, is_positive,
@ -445,24 +512,61 @@ pub fn jump(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.ip -= offset + 1; 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<Value> {
let record = data.records.last_mut_unchecked();
let Call { let Call {
destination: return_register, destination: return_register,
function_register, function_register,
argument_count, argument_count,
is_recursive,
} = instruction.into(); } = instruction.into();
ThreadSignal::Call { let function = record
function_register, .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, 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<Value> {
let record = data.records.last_mut_unchecked();
let CallNative { let CallNative {
destination, destination,
function, function,
@ -473,19 +577,47 @@ pub fn call_native(instruction: Instruction, record: &mut Record) -> ThreadSigna
function function
.call(record, Some(destination), argument_range) .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<Value> {
let record = data.records.last_mut_unchecked();
let Return { let Return {
should_return_value, should_return_value,
return_register, return_register,
} = instruction.into(); } = instruction.into();
ThreadSignal::Return { trace!("Returning with call stack:\n{}", data.call_stack);
should_return_value,
return_register, 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)] #[cfg(test)]

View File

@ -33,6 +33,16 @@ impl<T> Stack<T> {
self.items.len() 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) { pub fn push(&mut self, item: T) {
self.items.push(item); self.items.push(item);
} }
@ -51,7 +61,7 @@ impl<T> Stack<T> {
pub fn pop_unchecked(&mut self) -> T { pub fn pop_unchecked(&mut self) -> T {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow); assert!(!self.is_empty(), "{}", VmError::StackUnderflow);
self.items.pop().unwrap() self.items.pop().unwrap()
} else { } else {
@ -61,7 +71,7 @@ impl<T> Stack<T> {
pub fn last_unchecked(&self) -> &T { pub fn last_unchecked(&self) -> &T {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow); assert!(!self.is_empty(), "{}", VmError::StackUnderflow);
self.items.last().unwrap() self.items.last().unwrap()
} else { } else {
@ -71,7 +81,7 @@ impl<T> Stack<T> {
pub fn last_mut_unchecked(&mut self) -> &mut T { pub fn last_mut_unchecked(&mut self) -> &mut T {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow); assert!(!self.is_empty(), "{}", VmError::StackUnderflow);
self.items.last_mut().unwrap() self.items.last_mut().unwrap()
} else { } else {
@ -100,9 +110,9 @@ impl<T> IndexMut<Range<usize>> for Stack<T> {
} }
} }
impl Debug for Stack<FunctionCall> { impl<T: Debug> Debug for Stack<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{self}") write!(f, "{:?}", self.items)
} }
} }

View File

@ -2,12 +2,9 @@ use std::fmt::{self, Display, Formatter};
use tracing::{info, trace}; use tracing::{info, trace};
use crate::{ use crate::{vm::FunctionCall, Chunk, DustString, Value};
vm::{FunctionCall, Register},
Chunk, DustString, Value,
};
use super::{record::Record, RunAction, Stack}; use super::{record::Record, RecordAction, Stack};
pub struct Thread { pub struct Thread {
chunk: Chunk, chunk: Chunk,
@ -28,127 +25,43 @@ impl Thread {
}; };
let main_record = Record::new(&self.chunk); 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!( info!(
"Starting thread with {}", "Starting thread with {}",
active_record self.chunk
.as_function()
.name .name
.clone()
.unwrap_or_else(|| DustString::from("anonymous")) .unwrap_or_else(|| DustString::from("anonymous"))
); );
call_stack.push(main_call);
records.push(main_record);
let mut thread_data = ThreadData {
call_stack,
records,
};
loop { loop {
trace!( let active_record = thread_data.records.last_unchecked();
"Run \"{}\" | IP = {}",
active_record
.name()
.cloned()
.unwrap_or_else(|| DustString::from("anonymous")),
active_record.ip
);
let instruction = active_record.chunk.instructions[active_record.ip]; let instruction = active_record.chunk.instructions[active_record.ip];
let action = RunAction::from(instruction); let record_action = RecordAction::from(instruction);
let signal = (action.logic)(action.instruction, active_record); 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; return value_option;
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;
}
} }
} }
} }
} }
#[derive(Debug)]
pub struct ThreadData<'a> {
pub call_stack: Stack<FunctionCall>,
pub records: Stack<Record<'a>>,
}
#[derive(Debug)] #[derive(Debug)]
pub enum ThreadSignal { pub enum ThreadSignal {
Continue, Continue,