Optimize and refine VM thread and record
This commit is contained in:
parent
6cfa0f58e3
commit
c03ec528b7
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -342,7 +342,6 @@ dependencies = [
|
||||
"smallvec",
|
||||
"smartstring",
|
||||
"tracing",
|
||||
"typed-arena",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -947,12 +946,6 @@ dependencies = [
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-arena"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
|
@ -22,7 +22,6 @@ smartstring = { version = "1.0.1", features = [
|
||||
"serde",
|
||||
], default-features = false }
|
||||
tracing = "0.1.41"
|
||||
typed-arena = "2.0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.3.4", features = ["html_reports"] }
|
||||
|
@ -28,6 +28,8 @@
|
||||
//! println!("{}", report);
|
||||
//! ```
|
||||
|
||||
#![feature(array_repeat)]
|
||||
|
||||
pub mod chunk;
|
||||
pub mod compiler;
|
||||
pub mod dust_error;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::io::{self, stdin, stdout, Write};
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::vm::{Register, ThreadSignal};
|
||||
use crate::vm::{get_next_action, Register, ThreadSignal};
|
||||
use crate::{vm::Record, ConcreteValue, NativeFunctionError, Value};
|
||||
|
||||
pub fn read_line(
|
||||
@ -30,7 +30,9 @@ pub fn read_line(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ThreadSignal::Continue)
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
Ok(ThreadSignal::Continue(next_action))
|
||||
}
|
||||
|
||||
pub fn write(
|
||||
@ -41,7 +43,7 @@ pub fn write(
|
||||
let mut stdout = stdout();
|
||||
|
||||
for register_index in argument_range {
|
||||
if let Some(value) = record.open_register_allow_empty(register_index) {
|
||||
if let Some(value) = record.open_register_allow_empty_unchecked(register_index) {
|
||||
let string = value.display(record);
|
||||
|
||||
stdout
|
||||
@ -58,7 +60,9 @@ pub fn write(
|
||||
position: record.current_position(),
|
||||
})?;
|
||||
|
||||
Ok(ThreadSignal::Continue)
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
Ok(ThreadSignal::Continue(next_action))
|
||||
}
|
||||
|
||||
pub fn write_line(
|
||||
@ -73,7 +77,7 @@ pub fn write_line(
|
||||
let mut stdout = stdout().lock();
|
||||
|
||||
for register_index in argument_range {
|
||||
if let Some(value) = record.open_register_allow_empty(register_index) {
|
||||
if let Some(value) = record.open_register_allow_empty_unchecked(register_index) {
|
||||
let string = value.display(record);
|
||||
|
||||
stdout.write(string.as_bytes()).map_err(map_err)?;
|
||||
@ -83,5 +87,7 @@ pub fn write_line(
|
||||
|
||||
stdout.flush().map_err(map_err)?;
|
||||
|
||||
Ok(ThreadSignal::Continue)
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
Ok(ThreadSignal::Continue(next_action))
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::{
|
||||
vm::{Record, Register, ThreadSignal},
|
||||
vm::{get_next_action, Record, Register, ThreadSignal},
|
||||
ConcreteValue, NativeFunctionError, Value,
|
||||
};
|
||||
|
||||
@ -17,5 +17,7 @@ pub fn to_string(
|
||||
|
||||
record.set_register(destination, register);
|
||||
|
||||
Ok(ThreadSignal::Continue)
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
Ok(ThreadSignal::Continue(next_action))
|
||||
}
|
||||
|
@ -13,9 +13,11 @@ use std::{
|
||||
|
||||
pub use error::VmError;
|
||||
pub use record::Record;
|
||||
pub use run_action::RecordAction;
|
||||
pub(crate) use run_action::get_next_action;
|
||||
pub use run_action::RunAction;
|
||||
pub use stack::{FunctionCall, Stack};
|
||||
pub use thread::{Thread, ThreadSignal};
|
||||
|
||||
use tracing::{span, Level};
|
||||
|
||||
use crate::{compile, Chunk, DustError, DustString, Value};
|
||||
|
@ -14,7 +14,6 @@ pub struct Record<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Record<'a> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(chunk: &'a Chunk) -> Self {
|
||||
Self {
|
||||
ip: 0,
|
||||
@ -44,7 +43,7 @@ impl<'a> Record<'a> {
|
||||
|
||||
match pointer {
|
||||
Pointer::Stack(register_index) => self.open_register_unchecked(register_index),
|
||||
Pointer::Constant(constant_index) => self.get_constant(constant_index),
|
||||
Pointer::Constant(constant_index) => self.get_constant_unchecked(constant_index),
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,17 +89,10 @@ impl<'a> Record<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_register_allow_empty(&self, register_index: u8) -> Option<&Value> {
|
||||
pub fn open_register_allow_empty_unchecked(&self, register_index: u8) -> Option<&Value> {
|
||||
trace!("Open register R{register_index}");
|
||||
|
||||
let register_index = register_index as usize;
|
||||
|
||||
assert!(
|
||||
register_index < self.registers.len(),
|
||||
"VM Error: Register index out of bounds"
|
||||
);
|
||||
|
||||
let register = &self.registers[register_index];
|
||||
let register = self.get_register_unchecked(register_index);
|
||||
|
||||
match register {
|
||||
Register::Value(value) => {
|
||||
@ -117,7 +109,7 @@ impl<'a> Record<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty_register_or_clone_constant(&mut self, register_index: u8) -> Value {
|
||||
pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u8) -> Value {
|
||||
let register_index = register_index as usize;
|
||||
|
||||
assert!(
|
||||
@ -131,21 +123,18 @@ impl<'a> Record<'a> {
|
||||
Register::Value(value) => value,
|
||||
Register::Pointer(pointer) => match pointer {
|
||||
Pointer::Stack(register_index) => {
|
||||
self.empty_register_or_clone_constant(register_index)
|
||||
self.empty_register_or_clone_constant_unchecked(register_index)
|
||||
}
|
||||
Pointer::Constant(constant_index) => {
|
||||
self.get_constant_unchecked(constant_index).clone()
|
||||
}
|
||||
Pointer::Constant(constant_index) => self.get_constant(constant_index).clone(),
|
||||
},
|
||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clone_register_value_or_constant(&self, register_index: u8) -> Value {
|
||||
assert!(
|
||||
(register_index as usize) < self.registers.len(),
|
||||
"VM Error: Register index out of bounds"
|
||||
);
|
||||
|
||||
let register = &self.registers[register_index as usize];
|
||||
pub fn clone_register_value_or_constant_unchecked(&self, register_index: u8) -> Value {
|
||||
let register = self.get_register_unchecked(register_index);
|
||||
|
||||
match register {
|
||||
Register::Value(value) => value.clone(),
|
||||
@ -153,29 +142,30 @@ impl<'a> Record<'a> {
|
||||
Pointer::Stack(register_index) => {
|
||||
self.open_register_unchecked(*register_index).clone()
|
||||
}
|
||||
Pointer::Constant(constant_index) => self.get_constant(*constant_index).clone(),
|
||||
Pointer::Constant(constant_index) => {
|
||||
self.get_constant_unchecked(*constant_index).clone()
|
||||
}
|
||||
},
|
||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
||||
}
|
||||
}
|
||||
|
||||
/// DRY helper to get a value from an Argument
|
||||
pub fn get_argument(&self, argument: Argument) -> &Value {
|
||||
pub fn get_argument_unchecked(&self, argument: Argument) -> &Value {
|
||||
match argument {
|
||||
Argument::Constant(constant_index) => self.get_constant(constant_index),
|
||||
Argument::Constant(constant_index) => self.get_constant_unchecked(constant_index),
|
||||
Argument::Register(register_index) => self.open_register_unchecked(register_index),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_constant(&self, constant_index: u8) -> &Value {
|
||||
pub fn get_constant_unchecked(&self, constant_index: u8) -> &Value {
|
||||
let constant_index = constant_index as usize;
|
||||
|
||||
assert!(
|
||||
constant_index < self.chunk.constants.len(),
|
||||
"VM Error: Constant index out of bounds"
|
||||
);
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
&self.chunk.constants[constant_index]
|
||||
} else {
|
||||
unsafe { self.chunk.constants.get_unchecked(constant_index) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_local_register(&self, local_index: u8) -> u8 {
|
||||
|
@ -10,24 +10,24 @@ use crate::{
|
||||
AbstractList, Argument, ConcreteValue, DustString, Instruction, Type, Value,
|
||||
};
|
||||
|
||||
use super::{thread::ThreadData, Pointer, Record, Register};
|
||||
use super::{thread::ThreadData, Pointer, Record, Register, ThreadSignal};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct RecordAction {
|
||||
pub struct RunAction {
|
||||
pub logic: RunnerLogic,
|
||||
pub instruction: Instruction,
|
||||
}
|
||||
|
||||
impl From<Instruction> for RecordAction {
|
||||
impl From<Instruction> for RunAction {
|
||||
fn from(instruction: Instruction) -> Self {
|
||||
let operation = instruction.operation();
|
||||
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
||||
|
||||
RecordAction { logic, instruction }
|
||||
RunAction { logic, instruction }
|
||||
}
|
||||
}
|
||||
|
||||
pub type RunnerLogic = fn(Instruction, &mut ThreadData) -> Option<Value>;
|
||||
pub type RunnerLogic = fn(Instruction, &mut ThreadData) -> ThreadSignal;
|
||||
|
||||
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
|
||||
point,
|
||||
@ -57,7 +57,15 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
|
||||
r#return,
|
||||
];
|
||||
|
||||
pub fn point(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub(crate) fn get_next_action(record: &Record) -> RunAction {
|
||||
let instruction = record.chunk.instructions[record.ip];
|
||||
let operation = instruction.operation();
|
||||
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
||||
|
||||
RunAction { logic, instruction }
|
||||
}
|
||||
|
||||
pub fn point(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let Point { from, to } = instruction.into();
|
||||
let from_register = record.get_register_unchecked(from);
|
||||
@ -71,30 +79,27 @@ pub fn point(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn close(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn close(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let Close { from, to } = instruction.into();
|
||||
|
||||
assert!(from < to, "Runtime Error: Malformed instruction");
|
||||
|
||||
for register_index in from..to {
|
||||
assert!(
|
||||
(register_index as usize) < record.stack_size(),
|
||||
"Runtime Error: Register index out of bounds"
|
||||
);
|
||||
|
||||
record.set_register(register_index, Register::Empty);
|
||||
}
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let LoadBoolean {
|
||||
destination,
|
||||
@ -112,10 +117,12 @@ pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> Option<V
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let LoadConstant {
|
||||
destination,
|
||||
@ -134,10 +141,12 @@ pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> Option<
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let LoadList {
|
||||
destination,
|
||||
@ -176,10 +185,12 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> Option<Valu
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let LoadFunction {
|
||||
destination,
|
||||
@ -193,10 +204,12 @@ pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> Option<
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let LoadSelf { destination } = instruction.into();
|
||||
let function = record.as_function();
|
||||
@ -206,10 +219,12 @@ pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> Option<Valu
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let GetLocal {
|
||||
destination,
|
||||
@ -222,10 +237,12 @@ pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> Option<Valu
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let SetLocal {
|
||||
register_index,
|
||||
@ -238,18 +255,20 @@ pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> Option<Valu
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn add(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn add(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let Add {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = instruction.into();
|
||||
let left = record.get_argument(left);
|
||||
let right = record.get_argument(right);
|
||||
let left = record.get_argument_unchecked(left);
|
||||
let right = record.get_argument_unchecked(right);
|
||||
let sum = left.add(right);
|
||||
let register = Register::Value(sum);
|
||||
|
||||
@ -257,18 +276,20 @@ pub fn add(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let Subtract {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = instruction.into();
|
||||
let left = record.get_argument(left);
|
||||
let right = record.get_argument(right);
|
||||
let left = record.get_argument_unchecked(left);
|
||||
let right = record.get_argument_unchecked(right);
|
||||
let difference = left.subtract(right);
|
||||
let register = Register::Value(difference);
|
||||
|
||||
@ -276,18 +297,20 @@ pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> Option<Value
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let Multiply {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = instruction.into();
|
||||
let left = record.get_argument(left);
|
||||
let right = record.get_argument(right);
|
||||
let left = record.get_argument_unchecked(left);
|
||||
let right = record.get_argument_unchecked(right);
|
||||
let product = match (left, right) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||
@ -303,18 +326,20 @@ pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> Option<Value
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn divide(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn divide(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let Divide {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = instruction.into();
|
||||
let left = record.get_argument(left);
|
||||
let right = record.get_argument(right);
|
||||
let left = record.get_argument_unchecked(left);
|
||||
let right = record.get_argument_unchecked(right);
|
||||
let quotient = match (left, right) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||
@ -330,18 +355,20 @@ pub fn divide(instruction: Instruction, data: &mut ThreadData) -> Option<Value>
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let Modulo {
|
||||
destination,
|
||||
left,
|
||||
right,
|
||||
} = instruction.into();
|
||||
let left = record.get_argument(left);
|
||||
let right = record.get_argument(right);
|
||||
let left = record.get_argument_unchecked(left);
|
||||
let right = record.get_argument_unchecked(right);
|
||||
let remainder = match (left, right) {
|
||||
(Value::Concrete(left), Value::Concrete(right)) => match (left, right) {
|
||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||
@ -357,10 +384,12 @@ pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> Option<Value>
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn test(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn test(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let Test {
|
||||
operand_register,
|
||||
@ -379,17 +408,19 @@ pub fn test(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let TestSet {
|
||||
destination,
|
||||
argument,
|
||||
test_value,
|
||||
} = instruction.into();
|
||||
let value = record.get_argument(argument);
|
||||
let value = record.get_argument_unchecked(argument);
|
||||
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
||||
*boolean
|
||||
} else {
|
||||
@ -410,14 +441,16 @@ pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> Option<Value
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn equal(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn equal(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let Equal { value, left, right } = instruction.into();
|
||||
let left = record.get_argument(left);
|
||||
let right = record.get_argument(right);
|
||||
let left = record.get_argument_unchecked(left);
|
||||
let right = record.get_argument_unchecked(right);
|
||||
let is_equal = left.equals(right);
|
||||
|
||||
if is_equal == value {
|
||||
@ -426,14 +459,16 @@ pub fn equal(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn less(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn less(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let Less { value, left, right } = instruction.into();
|
||||
let left = record.get_argument(left);
|
||||
let right = record.get_argument(right);
|
||||
let left = record.get_argument_unchecked(left);
|
||||
let right = record.get_argument_unchecked(right);
|
||||
let is_less = left < right;
|
||||
|
||||
if is_less == value {
|
||||
@ -442,14 +477,16 @@ pub fn less(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let LessEqual { value, left, right } = instruction.into();
|
||||
let left = record.get_argument(left);
|
||||
let right = record.get_argument(right);
|
||||
let left = record.get_argument_unchecked(left);
|
||||
let right = record.get_argument_unchecked(right);
|
||||
let is_less_or_equal = left <= right;
|
||||
|
||||
if is_less_or_equal == value {
|
||||
@ -458,16 +495,18 @@ pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> Option<Val
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn negate(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn negate(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let Negate {
|
||||
destination,
|
||||
argument,
|
||||
} = instruction.into();
|
||||
let argument = record.get_argument(argument);
|
||||
let argument = record.get_argument_unchecked(argument);
|
||||
let negated = argument.negate();
|
||||
let register = Register::Value(negated);
|
||||
|
||||
@ -475,16 +514,18 @@ pub fn negate(instruction: Instruction, data: &mut ThreadData) -> Option<Value>
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn not(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn not(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let Not {
|
||||
destination,
|
||||
argument,
|
||||
} = instruction.into();
|
||||
let argument = record.get_argument(argument);
|
||||
let argument = record.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"),
|
||||
@ -495,10 +536,12 @@ pub fn not(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn jump(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn jump(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let Jump {
|
||||
offset,
|
||||
@ -514,11 +557,13 @@ pub fn jump(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn call(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
pub fn call(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let mut current_record = data.records.pop_unchecked();
|
||||
let Call {
|
||||
destination: return_register,
|
||||
function_register,
|
||||
@ -526,25 +571,25 @@ pub fn call(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
is_recursive,
|
||||
} = instruction.into();
|
||||
|
||||
let function = record
|
||||
let function = current_record
|
||||
.open_register_unchecked(function_register)
|
||||
.as_function()
|
||||
.unwrap();
|
||||
let first_argument_register = return_register - argument_count;
|
||||
let prototype = if is_recursive {
|
||||
record.chunk
|
||||
current_record.chunk
|
||||
} else {
|
||||
&record.chunk.prototypes[function.prototype_index as usize]
|
||||
¤t_record.chunk.prototypes[function.prototype_index as usize]
|
||||
};
|
||||
let mut next_record = Record::new(prototype);
|
||||
let next_call = FunctionCall {
|
||||
name: next_record.name().cloned(),
|
||||
return_register,
|
||||
ip: record.ip,
|
||||
ip: current_record.ip,
|
||||
};
|
||||
|
||||
for (argument_index, register_index) in (first_argument_register..return_register).enumerate() {
|
||||
let argument = record.clone_register_value_or_constant(register_index);
|
||||
let argument = current_record.clone_register_value_or_constant_unchecked(register_index);
|
||||
|
||||
trace!(
|
||||
"Passing argument \"{argument}\" to {}",
|
||||
@ -557,15 +602,18 @@ pub fn call(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
next_record.set_register(argument_index as u8, Register::Value(argument));
|
||||
}
|
||||
|
||||
record.ip += 1;
|
||||
let next_action = get_next_action(&next_record);
|
||||
|
||||
current_record.ip += 1;
|
||||
|
||||
data.call_stack.push(next_call);
|
||||
data.records.push(current_record);
|
||||
data.records.push(next_record);
|
||||
|
||||
None
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
let CallNative {
|
||||
destination,
|
||||
@ -581,34 +629,31 @@ pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> Option<Va
|
||||
|
||||
record.ip += 1;
|
||||
|
||||
None
|
||||
let next_action = get_next_action(record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> Option<Value> {
|
||||
let record = data.records.last_mut_unchecked();
|
||||
pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> ThreadSignal {
|
||||
trace!("Returning with call stack:\n{}", data.call_stack);
|
||||
|
||||
let Return {
|
||||
should_return_value,
|
||||
return_register,
|
||||
} = instruction.into();
|
||||
|
||||
trace!("Returning with call stack:\n{}", data.call_stack);
|
||||
let current_call = data.call_stack.pop_unchecked();
|
||||
let mut current_record = data.records.pop_unchecked();
|
||||
|
||||
let return_value = if should_return_value {
|
||||
Some(record.empty_register_or_clone_constant(return_register))
|
||||
Some(current_record.empty_register_or_clone_constant_unchecked(return_register))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let current_call = data.call_stack.pop_unchecked();
|
||||
let _current_record = data.records.pop_unchecked();
|
||||
let destination = current_call.return_register;
|
||||
|
||||
if data.call_stack.is_empty() {
|
||||
return if should_return_value {
|
||||
return_value
|
||||
} else {
|
||||
None
|
||||
};
|
||||
return ThreadSignal::End(return_value);
|
||||
}
|
||||
|
||||
let outer_record = data.records.last_mut_unchecked();
|
||||
@ -617,7 +662,9 @@ pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> Option<Value
|
||||
outer_record.set_register(destination, Register::Value(return_value.unwrap()));
|
||||
}
|
||||
|
||||
None
|
||||
let next_action = get_next_action(outer_record);
|
||||
|
||||
ThreadSignal::Continue(next_action)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,10 +1,10 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use tracing::{info, trace};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{vm::FunctionCall, Chunk, DustString, Value};
|
||||
|
||||
use super::{record::Record, RecordAction, Stack};
|
||||
use super::{record::Record, RunAction, Stack};
|
||||
|
||||
pub struct Thread {
|
||||
chunk: Chunk,
|
||||
@ -41,19 +41,23 @@ impl Thread {
|
||||
records,
|
||||
};
|
||||
|
||||
loop {
|
||||
let active_record = thread_data.records.last_unchecked();
|
||||
let instruction = active_record.chunk.instructions[active_record.ip];
|
||||
let record_action = RecordAction::from(instruction);
|
||||
let value_option = (record_action.logic)(record_action.instruction, &mut thread_data);
|
||||
let mut next_action = RunAction::from(*self.chunk.instructions.first().unwrap());
|
||||
|
||||
if thread_data.call_stack.is_empty() {
|
||||
trace!("Returning {value_option:?} from function");
|
||||
loop {
|
||||
let signal = (next_action.logic)(next_action.instruction, &mut thread_data);
|
||||
|
||||
match signal {
|
||||
ThreadSignal::Continue(action) => {
|
||||
next_action = action;
|
||||
}
|
||||
ThreadSignal::End(value_option) => {
|
||||
info!("Thread ended");
|
||||
|
||||
return value_option;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -64,20 +68,8 @@ pub struct ThreadData<'a> {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ThreadSignal {
|
||||
Continue,
|
||||
Call {
|
||||
function_register: u8,
|
||||
return_register: u8,
|
||||
argument_count: u8,
|
||||
},
|
||||
Return {
|
||||
should_return_value: bool,
|
||||
return_register: u8,
|
||||
},
|
||||
LoadFunction {
|
||||
prototype_index: u8,
|
||||
destination: u8,
|
||||
},
|
||||
Continue(RunAction),
|
||||
End(Option<Value>),
|
||||
}
|
||||
|
||||
impl Display for ThreadSignal {
|
||||
|
Loading…
x
Reference in New Issue
Block a user