1
0

Consolidate local operations to point operations

This commit is contained in:
Jeff 2025-02-03 17:49:38 -05:00
parent c1fe54ccd5
commit 371a061b1c
23 changed files with 218 additions and 1246 deletions

4
Cargo.lock generated
View File

@ -349,6 +349,7 @@ dependencies = [
"rand",
"serde",
"serde_json",
"smallvec",
"smartstring",
"tracing",
]
@ -805,6 +806,9 @@ name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
dependencies = [
"serde",
]
[[package]]
name = "smartstring"

View File

@ -22,6 +22,7 @@ smartstring = { version = "1.0.1", features = [
], default-features = false }
tracing = "0.1.41"
crossbeam-channel = "0.5.14"
smallvec = { version = "1.13.2", features = ["serde", "const_generics"] }
[dev-dependencies]
criterion = { version = "0.3.4", features = ["html_reports"] }

View File

@ -35,7 +35,7 @@ use optimize::control_flow_register_consolidation;
use crate::{
Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type, Value,
instruction::{CallNative, Close, GetLocal, Jump, LoadList, Return, SetLocal, TypeCode},
instruction::{CallNative, Close, Jump, LoadList, Point, Return, TypeCode},
};
/// Compiles the input and returns a chunk.
@ -697,12 +697,7 @@ impl<'src> Compiler<'src> {
) -> Result<(Operand, bool), CompileError> {
let (argument, push_back) = match instruction.operation() {
Operation::LOAD_CONSTANT => (Operand::Constant(instruction.b_field()), false),
Operation::GET_LOCAL => {
let local_index = instruction.b_field();
let (local, _) = self.get_local(local_index)?;
(Operand::Register(local.register_index), false)
}
Operation::POINT => (Operand::Register(instruction.a_field()), false),
Operation::LOAD_BOOLEAN
| Operation::LOAD_LIST
| Operation::LOAD_SELF
@ -749,11 +744,11 @@ impl<'src> Compiler<'src> {
position: self.previous_position,
})?;
let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?;
let left_is_mutable_local = if let Operation::GET_LOCAL = left_instruction.operation() {
let GetLocal { local_index, .. } = GetLocal::from(left_instruction);
let left_is_mutable_local = if left_instruction.operation() == Operation::POINT {
let Point { to, .. } = Point::from(left_instruction);
self.locals
.get(local_index as usize)
.get(to.index() as usize)
.map(|(local, _)| local.is_mutable)
.unwrap_or(false)
} else {
@ -1008,8 +1003,9 @@ impl<'src> Compiler<'src> {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let operand_register = if last_instruction.operation() == Operation::GET_LOCAL {
let (local, _) = self.get_local(last_instruction.b_field())?;
let operand_register = if last_instruction.operation() == Operation::POINT {
let Point { to, .. } = Point::from(last_instruction);
let (local, _) = self.get_local(to.index())?;
local.register_index
} else if last_instruction.yields_value() {
@ -1138,24 +1134,18 @@ impl<'src> Compiler<'src> {
math_instruction.set_a_field(local_register_index);
} else {
let register = self.next_register() - 1;
let set_local = Instruction::from(SetLocal {
register_index: register,
local_index,
});
let point = Instruction::point(local_register_index, Operand::Register(register));
self.emit_instruction(set_local, Type::None, start_position);
self.emit_instruction(point, r#type, start_position);
}
return Ok(());
}
let destination = self.next_register();
let get_local = Instruction::from(GetLocal {
destination,
local_index,
});
let point = Instruction::point(destination, Operand::Register(local_register_index));
self.emit_instruction(get_local, r#type, self.previous_position);
self.emit_instruction(point, r#type, self.previous_position);
Ok(())
}

View File

@ -1,49 +0,0 @@
use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionBuilder;
pub struct GetLocal {
pub destination: u16,
pub local_index: u16,
}
impl From<Instruction> for GetLocal {
fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field();
let local_index = instruction.b_field();
GetLocal {
destination,
local_index,
}
}
}
impl From<GetLocal> for Instruction {
fn from(get_local: GetLocal) -> Self {
let operation = Operation::GET_LOCAL;
let a_field = get_local.destination;
let b_field = get_local.local_index;
InstructionBuilder {
operation,
a_field,
b_field,
..Default::default()
}
.build()
}
}
impl Display for GetLocal {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let GetLocal {
destination,
local_index,
} = self;
write!(f, "R{destination} = L{local_index}")
}
}

View File

@ -99,7 +99,6 @@ mod call_native;
mod close;
mod divide;
mod equal;
mod get_local;
mod jump;
mod less;
mod less_equal;
@ -115,7 +114,6 @@ mod not;
mod operation;
mod point;
mod r#return;
mod set_local;
mod subtract;
mod test;
mod test_set;
@ -127,7 +125,6 @@ pub use call_native::CallNative;
pub use close::Close;
pub use divide::Divide;
pub use equal::Equal;
pub use get_local::GetLocal;
pub use jump::Jump;
pub use less::Less;
pub use less_equal::LessEqual;
@ -143,7 +140,6 @@ pub use not::Not;
pub use operation::Operation;
pub use point::Point;
pub use r#return::Return;
pub use set_local::SetLocal;
pub use subtract::Subtract;
pub use test::Test;
pub use test_set::TestSet;
@ -276,8 +272,8 @@ impl Instruction {
self.0 = (bits as u64) << 63;
}
pub fn point(from: u16, to: u16) -> Instruction {
Instruction::from(Point { from, to })
pub fn point(destination: u16, to: Operand) -> Instruction {
Instruction::from(Point { destination, to })
}
pub fn close(from: u16, to: u16) -> Instruction {
@ -323,20 +319,6 @@ impl Instruction {
})
}
pub fn get_local(destination: u16, local_index: u16) -> Instruction {
Instruction::from(GetLocal {
destination,
local_index,
})
}
pub fn set_local(register: u16, local_index: u16) -> Instruction {
Instruction::from(SetLocal {
local_index,
register_index: register,
})
}
pub fn add(
destination: u16,
left: Operand,
@ -546,10 +528,10 @@ impl Instruction {
pub fn as_argument(&self) -> Option<Operand> {
match self.operation() {
Operation::LOAD_CONSTANT => Some(Operand::Constant(self.b_field())),
Operation::LOAD_BOOLEAN
Operation::POINT
| Operation::LOAD_BOOLEAN
| Operation::LOAD_LIST
| Operation::LOAD_SELF
| Operation::GET_LOCAL
| Operation::ADD
| Operation::SUBTRACT
| Operation::MULTIPLY
@ -574,7 +556,7 @@ impl Instruction {
}
}
pub fn b_as_argument(&self) -> Operand {
pub fn b_as_operand(&self) -> Operand {
if self.b_is_constant() {
Operand::Constant(self.b_field())
} else {
@ -605,7 +587,6 @@ impl Instruction {
| Operation::LOAD_FUNCTION
| Operation::LOAD_LIST
| Operation::LOAD_SELF
| Operation::GET_LOCAL
| Operation::ADD
| Operation::SUBTRACT
| Operation::MULTIPLY
@ -619,9 +600,7 @@ impl Instruction {
function.returns_value()
}
Operation::CLOSE
| Operation::SET_LOCAL
| Operation::EQUAL
Operation::EQUAL
| Operation::LESS
| Operation::LESS_EQUAL
| Operation::TEST
@ -637,14 +616,11 @@ impl Instruction {
match operation {
Operation::POINT => Point::from(*self).to_string(),
Operation::CLOSE => Close::from(*self).to_string(),
Operation::LOAD_BOOLEAN => LoadBoolean::from(*self).to_string(),
Operation::LOAD_CONSTANT => LoadConstant::from(*self).to_string(),
Operation::LOAD_FUNCTION => LoadFunction::from(*self).to_string(),
Operation::LOAD_LIST => LoadList::from(*self).to_string(),
Operation::LOAD_SELF => LoadSelf::from(*self).to_string(),
Operation::GET_LOCAL => GetLocal::from(*self).to_string(),
Operation::SET_LOCAL => SetLocal::from(*self).to_string(),
Operation::ADD => Add::from(*self).to_string(),
Operation::SUBTRACT => Subtract::from(*self).to_string(),
Operation::MULTIPLY => Multiply::from(*self).to_string(),

View File

@ -11,7 +11,7 @@ pub struct Negate {
impl From<Instruction> for Negate {
fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field();
let argument = instruction.b_as_argument();
let argument = instruction.b_as_operand();
let argument_type = instruction.b_type();
Negate {

View File

@ -12,7 +12,7 @@ pub struct Not {
impl From<Instruction> for Not {
fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field();
let argument = instruction.b_as_argument();
let argument = instruction.b_as_operand();
Not {
destination,

View File

@ -20,51 +20,44 @@ impl Operation {
pub const LOAD_LIST: Operation = Operation(5);
pub const LOAD_SELF: Operation = Operation(6);
// Locals
pub const GET_LOCAL: Operation = Operation(7);
pub const SET_LOCAL: Operation = Operation(8);
// Arithmetic
pub const ADD: Operation = Operation(9);
pub const SUBTRACT: Operation = Operation(10);
pub const MULTIPLY: Operation = Operation(11);
pub const DIVIDE: Operation = Operation(12);
pub const MODULO: Operation = Operation(13);
pub const ADD: Operation = Operation(7);
pub const SUBTRACT: Operation = Operation(8);
pub const MULTIPLY: Operation = Operation(9);
pub const DIVIDE: Operation = Operation(10);
pub const MODULO: Operation = Operation(11);
// Comparison
pub const EQUAL: Operation = Operation(14);
pub const LESS: Operation = Operation(15);
pub const LESS_EQUAL: Operation = Operation(16);
pub const EQUAL: Operation = Operation(12);
pub const LESS: Operation = Operation(13);
pub const LESS_EQUAL: Operation = Operation(14);
// Unary operations
pub const NEGATE: Operation = Operation(17);
pub const NOT: Operation = Operation(18);
pub const NEGATE: Operation = Operation(15);
pub const NOT: Operation = Operation(16);
// Logical operations
pub const TEST: Operation = Operation(19);
pub const TEST_SET: Operation = Operation(20);
pub const TEST: Operation = Operation(17);
pub const TEST_SET: Operation = Operation(18);
// Function calls
pub const CALL: Operation = Operation(21);
pub const CALL_NATIVE: Operation = Operation(22);
pub const CALL: Operation = Operation(19);
pub const CALL_NATIVE: Operation = Operation(20);
// Control flow
pub const JUMP: Operation = Operation(23);
pub const RETURN: Operation = Operation(24);
pub const JUMP: Operation = Operation(21);
pub const RETURN: Operation = Operation(22);
}
impl Operation {
pub fn name(&self) -> &'static str {
match *self {
Self::POINT => "POINT",
Self::CLOSE => "CLOSE",
Self::LOAD_BOOLEAN => "LOAD_BOOLEAN",
Self::LOAD_CONSTANT => "LOAD_CONSTANT",
Self::LOAD_FUNCTION => "LOAD_FUNCTION",
Self::LOAD_LIST => "LOAD_LIST",
Self::LOAD_SELF => "LOAD_SELF",
Self::GET_LOCAL => "GET_LOCAL",
Self::SET_LOCAL => "SET_LOCAL",
Self::ADD => "ADD",
Self::SUBTRACT => "SUBTRACT",
Self::MULTIPLY => "MULTIPLY",

View File

@ -2,18 +2,18 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionBuilder;
use super::{InstructionBuilder, Operand};
pub struct Point {
pub from: u16,
pub to: u16,
pub destination: u16,
pub to: Operand,
}
impl From<Instruction> for Point {
fn from(instruction: Instruction) -> Self {
Point {
from: instruction.b_field(),
to: instruction.c_field(),
destination: instruction.a_field(),
to: instruction.b_as_operand(),
}
}
}
@ -21,13 +21,14 @@ impl From<Instruction> for Point {
impl From<Point> for Instruction {
fn from(r#move: Point) -> Self {
let operation = Operation::POINT;
let b_field = r#move.from;
let c_field = r#move.to;
let a_field = r#move.destination;
let (b_field, b_is_constant) = r#move.to.as_index_and_constant_flag();
InstructionBuilder {
operation,
a_field,
b_field,
c_field,
b_is_constant,
..Default::default()
}
.build()
@ -36,8 +37,8 @@ impl From<Point> for Instruction {
impl Display for Point {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let Point { from, to } = self;
let Point { destination, to } = self;
write!(f, "{from} -> {to}")
write!(f, "R{destination} -> {to}")
}
}

View File

@ -1,49 +0,0 @@
use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionBuilder;
pub struct SetLocal {
pub register_index: u16,
pub local_index: u16,
}
impl From<Instruction> for SetLocal {
fn from(instruction: Instruction) -> Self {
let register_index = instruction.b_field();
let local_index = instruction.c_field();
SetLocal {
register_index,
local_index,
}
}
}
impl From<SetLocal> for Instruction {
fn from(set_local: SetLocal) -> Self {
let operation = Operation::SET_LOCAL;
let b_field = set_local.register_index;
let c_field = set_local.local_index;
InstructionBuilder {
operation,
b_field,
c_field,
..Default::default()
}
.build()
}
}
impl Display for SetLocal {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let SetLocal {
register_index,
local_index,
} = self;
write!(f, "L{local_index} = R{register_index}")
}
}

View File

@ -13,7 +13,7 @@ pub struct TestSet {
impl From<Instruction> for TestSet {
fn from(instruction: Instruction) -> Self {
let destination = instruction.a_field();
let argument = instruction.b_as_argument();
let argument = instruction.b_as_operand();
let test_value = instruction.c_field() != 0;
TestSet {

View File

@ -1,17 +1,13 @@
use std::{ops::Range, panic};
use crate::vm::ThreadData;
use crate::vm::Thread;
pub fn panic(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
pub fn panic(data: &mut Thread, _: usize, argument_range: Range<usize>) {
let position = data.current_position();
let mut message = format!("Dust panic at {position}!");
for register_index in argument_range {
let value_option = data.open_register_allow_empty_unchecked(register_index);
let value = match value_option {
Some(value) => value,
None => continue,
};
let value = data.get_register(register_index);
let string = value.display(data);
message.push_str(&string);

View File

@ -3,10 +3,10 @@ use std::ops::Range;
use crate::{
ConcreteValue, Value,
vm::{Register, ThreadData, get_next_action},
vm::{Register, Thread},
};
pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range<u16>) -> bool {
pub fn read_line(data: &mut Thread, destination: usize, _argument_range: Range<usize>) {
let mut buffer = String::new();
if stdin().read_line(&mut buffer).is_ok() {
@ -14,45 +14,32 @@ pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range
buffer.truncate(length.saturating_sub(1));
let register = Register::Value(Value::Concrete(ConcreteValue::string(buffer)));
let new_register = Register::Value(Value::Concrete(ConcreteValue::string(buffer)));
let old_register = data.get_register_mut(destination);
data.set_register(destination, register);
*old_register = new_register;
}
data.next_action = get_next_action(data);
false
}
pub fn write(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
pub fn write(data: &mut Thread, _: usize, argument_range: Range<usize>) {
let mut stdout = stdout();
for register_index in argument_range {
if let Some(value) = data.open_register_allow_empty_unchecked(register_index) {
let string = value.display(data);
let _ = stdout.write(string.as_bytes());
}
let value = data.get_register(register_index);
let _ = stdout.write(value.to_string().as_bytes());
}
let _ = stdout.flush();
data.next_action = get_next_action(data);
false
}
pub fn write_line(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
pub fn write_line(data: &mut Thread, _: usize, argument_range: Range<usize>) {
let mut stdout = stdout().lock();
for register_index in argument_range {
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 value = data.get_register(register_index);
let _ = stdout.write(value.to_string().as_bytes());
}
let _ = stdout.write(b"\n");
let _ = stdout.flush();
data.next_action = get_next_action(data);
false
}

View File

@ -17,7 +17,7 @@ use std::{
use serde::{Deserialize, Serialize};
use crate::{AnnotatedError, FunctionType, Span, Type, vm::ThreadData};
use crate::{AnnotatedError, FunctionType, Span, Type, vm::Thread};
macro_rules! define_native_function {
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
@ -34,13 +34,13 @@ macro_rules! define_native_function {
impl NativeFunction {
pub fn call(
&self,
data: &mut ThreadData,
destination: u16,
argument_range: Range<u16>,
) -> bool {
thread: &mut Thread,
destination: usize,
argument_range: Range<usize>,
) {
match self {
$(
NativeFunction::$name => $function(data, destination, argument_range),
NativeFunction::$name => $function(thread, destination, argument_range),
)*
}
}

View File

@ -4,10 +4,10 @@ use rand::Rng;
use crate::{
Value,
vm::{Register, ThreadData, get_next_action},
vm::{Register, Thread},
};
pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
pub fn random_int(data: &mut Thread, destination: usize, argument_range: Range<usize>) {
let mut argument_range_iter = argument_range.into_iter();
let (min, max) = {
let mut min = None;
@ -16,25 +16,21 @@ pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range
let register_index = argument_range_iter
.next()
.unwrap_or_else(|| panic!("No argument was passed to \"random_int\""));
let value_option = data.open_register_allow_empty_unchecked(register_index);
let argument = data.get_register(register_index);
if let Some(argument) = value_option {
if let Some(integer) = argument.as_integer() {
if min.is_none() {
min = Some(integer);
} else {
break (min, integer);
}
if let Some(integer) = argument.as_integer() {
if min.is_none() {
min = Some(integer);
} else {
break (min, integer);
}
}
}
};
let random_integer = rand::thread_rng().gen_range(min.unwrap()..max);
let new_register = Register::Value(Value::integer(random_integer));
let old_register = data.get_register_mut(destination);
data.set_register(destination, Register::Value(Value::integer(random_integer)));
data.next_action = get_next_action(data);
false
*old_register = new_register;
}

View File

@ -2,17 +2,14 @@ use std::ops::Range;
use crate::{
ConcreteValue, Value,
vm::{Register, ThreadData, get_next_action},
vm::{Register, Thread},
};
pub fn to_string(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
let argument_value = data.open_register_unchecked(argument_range.start);
let argument_string = argument_value.display(data);
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
pub fn to_string(thread: &mut Thread, destination: usize, argument_range: Range<usize>) {
let argument_value = thread.get_register(argument_range.start);
let argument_string = argument_value.display(thread);
let new_register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
let old_register = thread.get_register_mut(destination);
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
*old_register = new_register;
}

View File

@ -5,60 +5,12 @@ use std::{
use tracing::{Level, info, span};
use crate::{
DustString,
vm::{Thread, ThreadData, get_next_action},
};
use crate::{DustString, vm::Thread};
fn start_thread(data: &mut ThreadData, argument_range: Range<u16>) -> JoinHandle<()> {
let mut argument_range_iter = argument_range.into_iter();
let function_argument = {
loop {
let register_index = argument_range_iter
.next()
.unwrap_or_else(|| panic!("No argument was passed to \"spawn\""));
let value_option = data.open_register_allow_empty_unchecked(register_index);
if let Some(argument) = value_option {
break argument;
}
}
};
let function = function_argument.as_function().unwrap();
let prototype_index = function.prototype_index as usize;
let current_call = data.call_stack.last_unchecked();
let prototype = current_call.chunk.prototypes[prototype_index].clone();
info!(
"Spawning thread for \"{}\"",
function
.name
.as_ref()
.cloned()
.unwrap_or_else(|| DustString::from("anonymous"))
);
let thread_name = prototype
.name
.as_ref()
.map(|name| name.to_string())
.unwrap_or_else(|| "anonymous".to_string());
let mut thread = Thread::new(prototype);
Builder::new()
.name(thread_name)
.spawn(move || {
let span = span!(Level::INFO, "Spawned thread");
let _enter = span.enter();
thread.run();
})
.expect("Critical VM Error: Failed to spawn thread")
fn start_thread(thread: &mut Thread, argument_range: Range<usize>) -> JoinHandle<()> {
todo!();
}
pub fn spawn(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
pub fn spawn(data: &mut Thread, _: usize, argument_range: Range<usize>) {
let _ = start_thread(data, argument_range);
data.next_action = get_next_action(data);
false
}

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter};
use crate::{vm::ThreadData, Pointer, Type};
use crate::{Pointer, Type, vm::Thread};
use super::DustString;
@ -11,17 +11,17 @@ pub struct AbstractList {
}
impl AbstractList {
pub fn display(&self, data: &ThreadData) -> DustString {
pub fn display(&self, thread: &Thread) -> DustString {
let mut display = DustString::new();
display.push('[');
for (i, item) in self.item_pointers.iter().enumerate() {
for (i, pointer) in self.item_pointers.iter().enumerate() {
if i > 0 {
display.push_str(", ");
}
let item_display = data.follow_pointer_unchecked(*item).display(data);
let item_display = thread.get_pointer_value(pointer).to_string();
display.push_str(&item_display);
}

View File

@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
use std::fmt::{self, Debug, Display, Formatter};
use crate::{Type, vm::ThreadData};
use crate::{Type, vm::Thread};
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum Value {
@ -219,7 +219,7 @@ impl Value {
}
}
pub fn display(&self, data: &ThreadData) -> DustString {
pub fn display(&self, data: &Thread) -> DustString {
match self {
Value::AbstractList(list) => list.display(data),
Value::Concrete(concrete_value) => concrete_value.display(),

View File

@ -3,15 +3,16 @@ use tracing::trace;
use crate::{
AbstractList, ConcreteValue, Instruction, Operand, Type, Value,
instruction::{
Add, Call, CallNative, Close, Divide, Equal, GetLocal, InstructionBuilder, Jump, Less,
LessEqual, LoadBoolean, LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply,
Negate, Not, Point, Return, SetLocal, Subtract, Test, TestSet, TypeCode,
Add, Call, CallNative, Divide, Equal, InstructionBuilder, Jump, Less, LessEqual,
LoadBoolean, LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not,
Return, Subtract, Test, TestSet, TypeCode,
},
vm::CallFrame,
};
use super::{Pointer, Register, thread::Thread};
#[derive(Debug)]
pub struct ActionSequence {
pub actions: Vec<Action>,
}
@ -21,7 +22,7 @@ impl ActionSequence {
let mut actions = Vec::with_capacity(instructions.len());
for instruction in instructions {
let action = Action::from(*instruction);
let action = Action::from(instruction);
actions.push(action);
}
@ -46,18 +47,15 @@ impl From<&Instruction> for Action {
}
}
pub type RunnerLogic = fn(InstructionBuilder, &mut Thread) -> bool;
pub type RunnerLogic = fn(InstructionBuilder, &mut Thread);
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 22] = [
point,
close,
load_boolean,
load_constant,
load_function,
load_list,
load_self,
get_local,
set_local,
add,
subtract,
multiply,
@ -76,782 +74,66 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
r#return,
];
pub fn point(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Point { from, to } = instruction.into();
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::Register(to));
data.set_register(from, register);
}
data.next_action = get_next_action(data);
false
}
pub fn close(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Close { from, to } = instruction.into();
for register_index in from..to {
data.set_register(register_index, Register::Empty);
}
data.next_action = get_next_action(data);
false
}
pub fn load_boolean(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let LoadBoolean {
destination,
value,
jump_next,
} = instruction.into();
let boolean = Value::Concrete(ConcreteValue::Boolean(value));
let register = Register::Value(boolean);
data.set_register(destination, register);
if jump_next {
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(data);
false
}
pub fn load_constant(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let LoadConstant {
destination,
constant_index,
jump_next,
} = instruction.into();
let register = Register::Pointer(Pointer::Constant(constant_index));
trace!("Load constant {constant_index} into R{destination}");
data.set_register(destination, register);
if jump_next {
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(data);
false
}
pub fn load_list(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let LoadList {
destination,
start_register,
jump_next,
} = instruction.into();
let mut item_pointers = Vec::with_capacity((destination - start_register) as usize);
let mut item_type = Type::Any;
for register_index in start_register..destination {
match data.get_register_unchecked(register_index) {
Register::Empty => continue,
Register::Value(value) => {
if item_type == Type::Any {
item_type = value.r#type();
}
}
Register::Pointer(pointer) => {
if item_type == Type::Any {
item_type = data.follow_pointer_unchecked(*pointer).r#type();
}
}
}
let pointer = Pointer::Register(register_index);
item_pointers.push(pointer);
}
let list_value = Value::AbstractList(AbstractList {
item_type,
item_pointers,
});
let register = Register::Value(list_value);
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn load_function(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let LoadFunction {
destination,
prototype_index,
jump_next,
} = instruction.into();
let prototype_index = prototype_index as usize;
let current_call = data.call_stack.last_mut_unchecked();
let prototype = &current_call.chunk.prototypes[prototype_index];
let function = prototype.as_function();
let register = Register::Value(Value::Function(function));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn load_self(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let LoadSelf {
destination,
jump_next,
} = instruction.into();
let current_call = data.call_stack.last_mut_unchecked();
let prototype = &current_call.chunk;
let function = prototype.as_function();
let register = Register::Value(Value::Function(function));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn get_local(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let GetLocal {
destination,
local_index,
} = instruction.into();
let local_register_index = data.get_local_register(local_index);
let register = Register::Pointer(Pointer::Register(local_register_index));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn set_local(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let SetLocal {
register_index,
local_index,
} = instruction.into();
let local_register_index = data.get_local_register(local_index);
let register = Register::Pointer(Pointer::Register(register_index));
data.set_register(local_register_index, register);
data.next_action = get_next_action(data);
false
}
pub fn add(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Add {
destination,
left,
left_type,
right,
right_type,
} = instruction.into();
let sum = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
ConcreteValue::Integer(left + right)
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
ConcreteValue::Float(left + right)
}
_ => panic!("VM Error: Cannot add values"),
};
let register = Register::Value(Value::Concrete(sum));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn subtract(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Subtract {
destination,
left,
left_type,
right,
right_type,
} = instruction.into();
let difference = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
ConcreteValue::Integer(left - right)
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
ConcreteValue::Float(left - right)
}
_ => panic!("VM Error: Cannot subtract values"),
};
let register = Register::Value(Value::Concrete(difference));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn multiply(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Multiply {
destination,
left,
left_type,
right,
right_type,
} = instruction.into();
let product = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
ConcreteValue::Integer(left * right)
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
ConcreteValue::Float(left * right)
}
_ => panic!("VM Error: Cannot multiply values"),
};
let register = Register::Value(Value::Concrete(product));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn divide(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Divide {
destination,
left,
left_type,
right,
right_type,
} = instruction.into();
let quotient = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
ConcreteValue::Integer(left / right)
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
ConcreteValue::Float(left / right)
}
_ => panic!("VM Error: Cannot divide values"),
};
let register = Register::Value(Value::Concrete(quotient));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn modulo(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Modulo {
destination,
left,
left_type,
right,
right_type,
} = instruction.into();
let remainder = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
ConcreteValue::Integer(left % right)
}
_ => panic!("VM Error: Cannot modulo values"),
};
let register = Register::Value(Value::Concrete(remainder));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn test(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Test {
operand_register,
test_value,
} = instruction.into();
let value = data.open_register_unchecked(operand_register);
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
*boolean
pub fn point(instruction: InstructionBuilder, thread: &mut Thread) {
let destination = instruction.a_field as usize;
let to = instruction.b_field as usize;
let to_is_constant = instruction.b_is_constant;
let pointer = if to_is_constant {
Pointer::Constant(to)
} else {
panic!("VM Error: Expected boolean value for TEST operation",);
Pointer::Register(to)
};
let new_register = Register::Pointer(pointer);
let old_register = thread.get_register_mut(destination);
if boolean == test_value {
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(data);
false
*old_register = new_register;
}
pub fn test_set(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let TestSet {
destination,
argument,
test_value,
} = instruction.into();
let value = data.get_argument_unchecked(argument);
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
*boolean
} else {
panic!("VM Error: Expected boolean value for TEST_SET operation",);
};
pub fn load_boolean(instruction: InstructionBuilder, thread: &mut Thread) {}
if boolean == test_value {
} else {
let pointer = match argument {
Operand::Constant(constant_index) => Pointer::Constant(constant_index),
Operand::Register(register_index) => Pointer::Register(register_index),
};
let register = Register::Pointer(pointer);
pub fn load_constant(instruction: InstructionBuilder, thread: &mut Thread) {}
data.set_register(destination, register);
}
pub fn load_list(instruction: InstructionBuilder, thread: &mut Thread) {}
data.next_action = get_next_action(data);
pub fn load_function(instruction: InstructionBuilder, thread: &mut Thread) {}
false
}
pub fn load_self(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn equal(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Equal {
comparator,
left,
left_type,
right,
right_type,
} = instruction.into();
let is_equal = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
pub fn get_local(instruction: InstructionBuilder, thread: &mut Thread) {}
left == right
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
pub fn set_local(instruction: InstructionBuilder, thread: &mut Thread) {}
left == right
}
(TypeCode::BOOLEAN, TypeCode::BOOLEAN) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_boolean()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_boolean()
.unwrap_unchecked()
};
pub fn add(instruction: InstructionBuilder, thread: &mut Thread) {}
left == right
}
(TypeCode::STRING, TypeCode::STRING) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_string()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_string()
.unwrap_unchecked()
};
pub fn subtract(instruction: InstructionBuilder, thread: &mut Thread) {}
left == right
}
_ => panic!("VM Error: Cannot compare values"),
};
pub fn multiply(instruction: InstructionBuilder, thread: &mut Thread) {}
if is_equal == comparator {
let current_call = data.call_stack.last_mut_unchecked();
pub fn divide(instruction: InstructionBuilder, thread: &mut Thread) {}
current_call.ip += 1;
}
pub fn modulo(instruction: InstructionBuilder, thread: &mut Thread) {}
data.next_action = get_next_action(data);
pub fn test(instruction: InstructionBuilder, thread: &mut Thread) {}
false
}
pub fn test_set(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn less(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Less {
comparator,
left,
left_type,
right,
right_type,
} = instruction.into();
let is_less = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
pub fn equal(instruction: InstructionBuilder, thread: &mut Thread) {}
left < right
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
pub fn less(instruction: InstructionBuilder, thread: &mut Thread) {}
left < right
}
_ => panic!("VM Error: Cannot compare values"),
};
pub fn less_equal(instruction: InstructionBuilder, thread: &mut Thread) {}
if is_less == comparator {
let current_call = data.call_stack.last_mut_unchecked();
pub fn negate(instruction: InstructionBuilder, thread: &mut Thread) {}
current_call.ip += 1;
}
pub fn not(instruction: InstructionBuilder, thread: &mut Thread) {}
data.next_action = get_next_action(data);
pub fn jump(instruction: InstructionBuilder, thread: &mut Thread) {}
false
}
pub fn call(instruction: InstructionBuilder, thread: &mut Thread) {}
pub fn less_equal(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let LessEqual {
comparator,
left,
left_type,
right,
right_type,
} = instruction.into();
let is_less_or_equal = match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_integer()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_integer()
.unwrap_unchecked()
};
pub fn call_native(instruction: InstructionBuilder, thread: &mut Thread) {}
left <= right
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left = unsafe {
data.get_argument_unchecked(left)
.as_float()
.unwrap_unchecked()
};
let right = unsafe {
data.get_argument_unchecked(right)
.as_float()
.unwrap_unchecked()
};
left <= right
}
_ => panic!("VM Error: Cannot compare values"),
};
if is_less_or_equal == comparator {
let current_call = data.call_stack.last_mut_unchecked();
current_call.ip += 1;
}
data.next_action = get_next_action(data);
false
}
pub fn negate(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Negate {
destination,
argument,
argument_type,
} = instruction.into();
let argument = data.get_argument_unchecked(argument);
let negated = argument.negate();
let register = Register::Value(negated);
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn not(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Not {
destination,
argument,
} = instruction.into();
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));
data.set_register(destination, register);
data.next_action = get_next_action(data);
false
}
pub fn jump(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Jump {
offset,
is_positive,
} = instruction.into();
let offset = offset as usize;
let current_call = data.call_stack.last_mut_unchecked();
if is_positive {
current_call.ip += offset;
} else {
current_call.ip -= offset + 1;
}
data.next_action = get_next_action(data);
false
}
pub fn call(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Call {
destination: return_register,
function_register,
argument_count,
is_recursive,
} = instruction.into();
let current_call = data.call_stack.last_unchecked();
let first_argument_register = return_register - argument_count;
let prototype = if is_recursive {
current_call.chunk.clone()
} else {
let function = data
.open_register_unchecked(function_register)
.as_function()
.unwrap();
current_call.chunk.prototypes[function.prototype_index as usize].clone()
};
let mut next_call = CallFrame::new(prototype, return_register);
let mut argument_index = 0;
for register_index in first_argument_register..return_register {
let value_option = data.open_register_allow_empty_unchecked(register_index);
let argument = if let Some(value) = value_option {
value.clone()
} else {
continue;
};
next_call.registers[argument_index] = Register::Value(argument);
argument_index += 1;
}
data.call_stack.push(next_call);
data.next_action = get_next_action(data);
false
}
pub fn call_native(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let CallNative {
destination,
function,
argument_count,
} = instruction.into();
let first_argument_index = destination - argument_count;
let argument_range = first_argument_index..destination;
function.call(data, destination, argument_range)
}
pub fn r#return(instruction: InstructionBuilder, data: &mut Thread) -> bool {
trace!("Returning with call stack:\n{}", data.call_stack);
let Return {
should_return_value,
return_register,
} = instruction.into();
let (destination, return_value) = if data.call_stack.len() == 1 {
if should_return_value {
data.return_value_index = Some(return_register);
};
return true;
} else {
let return_value = data.empty_register_or_clone_constant_unchecked(return_register);
let destination = data.call_stack.pop_unchecked().return_register;
(destination, return_value)
};
if should_return_value {
data.set_register(destination, Register::Value(return_value));
}
data.next_action = get_next_action(data);
false
}
pub fn r#return(instruction: InstructionBuilder, thread: &mut Thread) {}
#[cfg(test)]
mod tests {
@ -860,15 +142,12 @@ mod tests {
use super::*;
const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [
const ALL_OPERATIONS: [(Operation, RunnerLogic); 21] = [
(Operation::POINT, point),
(Operation::CLOSE, close),
(Operation::LOAD_BOOLEAN, load_boolean),
(Operation::LOAD_CONSTANT, load_constant),
(Operation::LOAD_LIST, load_list),
(Operation::LOAD_SELF, load_self),
(Operation::GET_LOCAL, get_local),
(Operation::SET_LOCAL, set_local),
(Operation::ADD, add),
(Operation::SUBTRACT, subtract),
(Operation::MULTIPLY, multiply),

View File

@ -3,27 +3,32 @@ use std::{
sync::Arc,
};
use smallvec::{SmallVec, smallvec};
use crate::{Chunk, DustString};
use super::Register;
use super::{Register, action::ActionSequence};
#[derive(Debug)]
pub struct CallFrame {
pub chunk: Arc<Chunk>,
pub ip: usize,
pub return_register: u16,
pub registers: Vec<Register>,
pub registers: SmallVec<[Register; 64]>,
pub action_sequence: ActionSequence,
}
impl CallFrame {
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
let register_count = chunk.register_count;
let registers = smallvec![Register::Empty; chunk.register_count];
let action_sequence = ActionSequence::new(&chunk.instructions);
Self {
chunk,
ip: 0,
return_register,
registers: vec![Register::Empty; register_count],
registers,
action_sequence,
}
}
}

View File

@ -10,7 +10,6 @@ use std::{
};
pub use action::Action;
pub(crate) use action::get_next_action;
pub use call_frame::CallFrame;
pub use thread::Thread;
@ -73,17 +72,17 @@ impl Display for Register {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Empty => write!(f, "empty"),
Self::Value(value) => write!(f, "{}", value),
Self::Pointer(pointer) => write!(f, "{}", pointer),
Self::Value(value) => write!(f, "{value}"),
Self::Pointer(pointer) => write!(f, "{pointer}"),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Pointer {
Register(u16),
Constant(u16),
Stack(usize, u16),
Register(usize),
Constant(usize),
Stack(usize, usize),
}
impl Display for Pointer {

View File

@ -1,11 +1,8 @@
use std::{mem::replace, sync::Arc, thread::JoinHandle};
use std::{sync::Arc, thread::JoinHandle};
use tracing::{info, trace};
use crate::{
Chunk, DustString, Operand, Span, Value,
vm::{CallFrame, action::ActionSequence},
};
use crate::{Chunk, DustString, Span, Value, vm::CallFrame};
use super::{Pointer, Register};
@ -40,9 +37,6 @@ impl Thread {
.unwrap_or_else(|| DustString::from("anonymous"))
);
let main_call = self.current_frame();
let action_sequence = ActionSequence::new(&main_call.chunk.instructions);
loop {
let current_frame = self.current_frame_mut();
let ip = {
@ -52,18 +46,21 @@ impl Thread {
ip
};
let current_action = if cfg!(debug_assertions) {
action_sequence.actions.get(ip).unwrap()
current_frame.action_sequence.actions.get(ip).unwrap()
} else {
unsafe { action_sequence.actions.get_unchecked(ip) }
unsafe { current_frame.action_sequence.actions.get_unchecked(ip) }
};
trace!("Operation: {}", current_action.instruction.operation);
trace!(
"Instruction operation: {}",
current_action.instruction.operation
);
(current_action.logic)(current_action.instruction, &mut self);
if let Some(return_index_option) = self.return_value_index {
if let Some(return_index) = return_index_option {
let return_value = self.open_register_unchecked(return_index as u16).clone();
let return_value = self.get_register(return_index).clone();
return Some(return_value);
} else {
@ -95,187 +92,84 @@ impl Thread {
}
}
pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value {
trace!("Follow {pointer}");
match pointer {
Pointer::Register(register_index) => self.open_register_unchecked(register_index),
Pointer::Constant(constant_index) => self.get_constant_unchecked(constant_index),
Pointer::Stack(stack_index, register_index) => unsafe {
let register = self
.call_stack
.get_unchecked(stack_index)
.registers
.get_unchecked(register_index as usize);
match register {
Register::Value(value) => value,
Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer),
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
},
}
}
pub fn get_register_unchecked(&self, register_index: u16) -> &Register {
pub fn get_register(&self, register_index: usize) -> &Value {
trace!("Get 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: u16, 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: u16) -> &Value {
let register_index = register_index as usize;
let register = if cfg!(debug_assertions) {
&self.call_stack.last_unchecked().registers[register_index]
self.call_stack
.last()
.unwrap()
.registers
.get(register_index)
.unwrap()
} else {
unsafe {
self.call_stack
.last_unchecked()
.last()
.unwrap_unchecked()
.registers
.get_unchecked(register_index)
}
};
trace!("Open R{register_index} to {register}");
match register {
Register::Value(value) => value,
Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer),
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
Register::Pointer(pointer) => self.get_pointer_value(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
}
pub fn open_register_allow_empty_unchecked(&self, register_index: u16) -> Option<&Value> {
trace!("Open R{register_index}");
let register = self.get_register_unchecked(register_index);
trace!("Open R{register_index} to {register}");
match register {
Register::Value(value) => Some(value),
Register::Pointer(pointer) => Some(self.follow_pointer_unchecked(*pointer)),
Register::Empty => None,
}
}
pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u16) -> Value {
let register_index = register_index as usize;
let old_register = replace(
&mut self.call_stack.last_mut_unchecked().registers[register_index],
Register::Empty,
);
match old_register {
Register::Value(value) => value,
Register::Pointer(pointer) => match pointer {
Pointer::Register(register_index) => {
self.empty_register_or_clone_constant_unchecked(register_index)
}
Pointer::Constant(constant_index) => {
self.get_constant_unchecked(constant_index).clone()
}
Pointer::Stack(stack_index, register_index) => {
let call = self.call_stack.get_unchecked_mut(stack_index);
let old_register = replace(
&mut call.registers[register_index as usize],
Register::Empty,
);
match old_register {
Register::Value(value) => value,
Register::Pointer(pointer) => {
self.follow_pointer_unchecked(pointer).clone()
}
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
}
},
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
}
pub fn clone_register_value_or_constant_unchecked(&self, register_index: u16) -> Value {
let register = self.get_register_unchecked(register_index);
match register {
Register::Value(value) => value.clone(),
Register::Pointer(pointer) => match pointer {
Pointer::Register(register_index) => {
self.open_register_unchecked(*register_index).clone()
}
Pointer::Constant(constant_index) => {
self.get_constant_unchecked(*constant_index).clone()
}
Pointer::Stack(stack_index, register_index) => {
let call = self.call_stack.get_unchecked(*stack_index);
let register = &call.registers[*register_index as usize];
match register {
Register::Value(value) => value.clone(),
Register::Pointer(pointer) => {
self.follow_pointer_unchecked(*pointer).clone()
}
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
}
},
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
}
/// DRY helper to get a value from an Argument
pub fn get_argument_unchecked(&self, argument: Operand) -> &Value {
match argument {
Operand::Constant(constant_index) => self.get_constant_unchecked(constant_index),
Operand::Register(register_index) => self.open_register_unchecked(register_index),
}
}
pub fn get_constant_unchecked(&self, constant_index: u16) -> &Value {
let constant_index = constant_index as usize;
pub fn get_register_mut(&mut self, register_index: usize) -> &mut Register {
trace!("Get R{register_index}");
if cfg!(debug_assertions) {
&self.call_stack.last().unwrap().chunk.constants[constant_index]
self.call_stack
.last_mut()
.unwrap()
.registers
.get_mut(register_index)
.unwrap()
} else {
unsafe {
self.call_stack
.last_unchecked()
.chunk
.constants
.get_unchecked(constant_index)
.last_mut()
.unwrap_unchecked()
.registers
.get_unchecked_mut(register_index)
}
}
}
pub fn get_local_register(&self, local_index: u16) -> u16 {
let local_index = local_index as usize;
let chunk = &self.call_stack.last_unchecked().chunk;
pub fn get_constant(&self, constant_index: usize) -> &Value {
if cfg!(debug_assertions) {
self.chunk.constants.get(constant_index).unwrap()
} else {
unsafe { self.chunk.constants.get_unchecked(constant_index) }
}
}
assert!(
local_index < chunk.locals.len(),
"VM Error: Local index out of bounds"
);
pub fn get_pointer_value(&self, pointer: &Pointer) -> &Value {
match pointer {
Pointer::Register(register_index) => self.get_register(*register_index),
Pointer::Constant(constant_index) => self.get_constant(*constant_index),
Pointer::Stack(call_index, register_index) => {
let call_frame = if cfg!(debug_assertions) {
self.call_stack.get(*call_index).unwrap()
} else {
unsafe { self.call_stack.get_unchecked(*call_index) }
};
let register = if cfg!(debug_assertions) {
call_frame.registers.get(*register_index).unwrap()
} else {
unsafe { call_frame.registers.get_unchecked(*register_index) }
};
chunk.locals[local_index].register_index
match register {
Register::Value(value) => value,
Register::Pointer(pointer) => self.get_pointer_value(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
}
}
}
}