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",
"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"

View File

@ -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"] }

View File

@ -42,7 +42,7 @@ pub struct Chunk {
pub(crate) locals: SmallVec<[Local; 8]>,
pub(crate) prototypes: Vec<Chunk>,
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,
}
}

View File

@ -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));

View File

@ -4,6 +4,7 @@ pub struct Call {
pub destination: u8,
pub function_register: u8,
pub argument_count: u8,
pub is_recursive: bool,
}
impl From<Instruction> for Call {
@ -11,11 +12,13 @@ impl From<Instruction> 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<Call> 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)
}
}

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 {
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);

View File

@ -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);

View File

@ -10,7 +10,7 @@ pub fn to_string(
destination: Option<u8>,
argument_range: Range<u8>,
) -> 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 destination = destination.unwrap();
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)]
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 } => {

View File

@ -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};

View File

@ -10,7 +10,7 @@ use super::{Pointer, Register};
pub struct Record<'a> {
pub ip: usize,
pub chunk: &'a Chunk,
stack: Vec<Register>,
registers: Vec<Register>,
}
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),
}
}

View File

@ -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<Instruction> for RunAction {
impl From<Instruction> 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<Value>;
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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<Value> {
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));
}
pub fn call_native(instruction: Instruction, record: &mut Record) -> ThreadSignal {
record.ip += 1;
data.call_stack.push(next_call);
data.records.push(next_record);
None
}
pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
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<Value> {
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)]

View File

@ -33,6 +33,16 @@ impl<T> Stack<T> {
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<T> Stack<T> {
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<T> Stack<T> {
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<T> Stack<T> {
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<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 {
write!(f, "{self}")
write!(f, "{:?}", self.items)
}
}

View File

@ -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,125 +25,41 @@ 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<FunctionCall>,
pub records: Stack<Record<'a>>,
}
#[derive(Debug)]