Consolidate local operations to point operations
This commit is contained in:
parent
c1fe54ccd5
commit
371a061b1c
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"] }
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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}")
|
||||
}
|
||||
}
|
@ -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(),
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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}")
|
||||
}
|
||||
}
|
||||
|
@ -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}")
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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),
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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 = ¤t_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 = ¤t_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),
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user