Experiment with more VM optimizations
This commit is contained in:
parent
6a61947476
commit
69ef1b3b06
@ -1,13 +1,14 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
use std::{
|
||||||
|
arch::asm,
|
||||||
use tracing::{Level, span, trace};
|
fmt::{self, Display, Formatter},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AbstractList, ConcreteValue, DustString, Instruction, Operation, Value,
|
AbstractList, ConcreteValue, DustString, Instruction, Operation, Value,
|
||||||
instruction::{InstructionFields, Jump, TypeCode},
|
instruction::{InstructionFields, Jump, TypeCode},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Pointer, Register, thread::Thread};
|
use super::{Pointer, Register, call_frame::PointerCache, thread::Thread};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ActionSequence {
|
pub struct ActionSequence {
|
||||||
@ -28,15 +29,12 @@ impl ActionSequence {
|
|||||||
} = Jump::from(instruction);
|
} = Jump::from(instruction);
|
||||||
|
|
||||||
if !is_positive {
|
if !is_positive {
|
||||||
let mut loop_instructions = Vec::new();
|
|
||||||
let mut previous = instruction;
|
let mut previous = instruction;
|
||||||
|
|
||||||
loop_instructions
|
actions.push(Action::optimized(instruction));
|
||||||
.push((InstructionFields::from(instruction), PointerCache::new()));
|
|
||||||
|
|
||||||
while let Some(instruction) = instructions.next() {
|
while let Some(instruction) = instructions.next() {
|
||||||
loop_instructions
|
actions.push(Action::optimized(instruction));
|
||||||
.push((InstructionFields::from(instruction), PointerCache::new()));
|
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
instruction.operation(),
|
instruction.operation(),
|
||||||
@ -56,22 +54,11 @@ impl ActionSequence {
|
|||||||
previous = instruction;
|
previous = instruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
loop_instructions.reverse();
|
|
||||||
|
|
||||||
let loop_action = Action {
|
|
||||||
logic: ACTION_LOGIC_TABLE[0],
|
|
||||||
instruction: InstructionFields::default(),
|
|
||||||
optimized_logic: Some(optimized_loop),
|
|
||||||
loop_instructions: Some(loop_instructions),
|
|
||||||
};
|
|
||||||
|
|
||||||
actions.push(loop_action);
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let action = Action::from(instruction);
|
let action = Action::unoptimized(instruction);
|
||||||
|
|
||||||
actions.push(action);
|
actions.push(action);
|
||||||
}
|
}
|
||||||
@ -91,7 +78,7 @@ impl Display for ActionSequence {
|
|||||||
write!(f, "[")?;
|
write!(f, "[")?;
|
||||||
|
|
||||||
for (index, action) in self.actions.iter().enumerate() {
|
for (index, action) in self.actions.iter().enumerate() {
|
||||||
write!(f, "{}", action)?;
|
write!(f, "{}", action.name)?;
|
||||||
|
|
||||||
if index < self.actions.len() - 1 {
|
if index < self.actions.len() - 1 {
|
||||||
write!(f, ", ")?;
|
write!(f, ", ")?;
|
||||||
@ -102,77 +89,71 @@ impl Display for ActionSequence {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Action {
|
pub struct Action {
|
||||||
|
pub name: &'static str,
|
||||||
pub logic: ActionLogic,
|
pub logic: ActionLogic,
|
||||||
pub instruction: InstructionFields,
|
pub instruction: InstructionFields,
|
||||||
pub optimized_logic: Option<OptimizedActionLogic>,
|
|
||||||
pub loop_instructions: Option<Vec<(InstructionFields, PointerCache)>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Instruction> for Action {
|
impl Action {
|
||||||
fn from(instruction: &Instruction) -> Self {
|
fn optimized(instruction: &Instruction) -> Self {
|
||||||
let operation = instruction.operation();
|
|
||||||
let logic = ACTION_LOGIC_TABLE[operation.0 as usize];
|
|
||||||
let instruction = InstructionFields::from(instruction);
|
let instruction = InstructionFields::from(instruction);
|
||||||
|
let (name, logic): (&'static str, ActionLogic) = match (
|
||||||
|
instruction.operation,
|
||||||
|
instruction.b_type,
|
||||||
|
instruction.c_type,
|
||||||
|
) {
|
||||||
|
(Operation::ADD, TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||||
|
("ADD_INTEGERS", add_integers_optimized)
|
||||||
|
}
|
||||||
|
(Operation::LESS, TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||||
|
("LESS_INTEGERS", less_integers_optimized)
|
||||||
|
}
|
||||||
|
(Operation::JUMP, _, _) => ("JUMP", jump),
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
Action {
|
Action {
|
||||||
|
name,
|
||||||
logic,
|
logic,
|
||||||
instruction,
|
instruction,
|
||||||
optimized_logic: None,
|
|
||||||
loop_instructions: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Action {
|
fn unoptimized(instruction: &Instruction) -> Self {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
let instruction = InstructionFields::from(instruction);
|
||||||
if let Some(loop_instructions) = &self.loop_instructions {
|
let logic = match instruction.operation {
|
||||||
write!(f, "LOOP(")?;
|
Operation::POINT => point,
|
||||||
|
Operation::CLOSE => close,
|
||||||
|
Operation::LOAD_ENCODED => load_encoded,
|
||||||
|
Operation::LOAD_CONSTANT => load_constant,
|
||||||
|
Operation::LOAD_LIST => load_list,
|
||||||
|
Operation::LOAD_FUNCTION => load_function,
|
||||||
|
Operation::LOAD_SELF => load_self,
|
||||||
|
Operation::ADD => add,
|
||||||
|
Operation::SUBTRACT => subtract,
|
||||||
|
Operation::MULTIPLY => multiply,
|
||||||
|
Operation::DIVIDE => divide,
|
||||||
|
Operation::MODULO => modulo,
|
||||||
|
Operation::EQUAL => equal,
|
||||||
|
Operation::LESS => less,
|
||||||
|
Operation::LESS_EQUAL => less_equal,
|
||||||
|
Operation::TEST => test,
|
||||||
|
Operation::TEST_SET => test_set,
|
||||||
|
Operation::RETURN => r#return,
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
for (index, (instruction, _)) in loop_instructions.iter().enumerate() {
|
Action {
|
||||||
write!(f, "{}", instruction.operation)?;
|
name: instruction.operation.name(),
|
||||||
|
logic,
|
||||||
if index < loop_instructions.len() - 1 {
|
instruction,
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ")")
|
|
||||||
} else {
|
|
||||||
write!(f, "{}", self.instruction.operation)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ActionLogic = fn(InstructionFields, &mut Thread);
|
pub type ActionLogic = fn(InstructionFields, &mut Thread);
|
||||||
pub type OptimizedActionLogic = fn(Vec<(InstructionFields, PointerCache)>, &mut Thread);
|
|
||||||
|
|
||||||
pub const ACTION_LOGIC_TABLE: [ActionLogic; 23] = [
|
|
||||||
point,
|
|
||||||
close,
|
|
||||||
load_encoded,
|
|
||||||
load_constant,
|
|
||||||
load_function,
|
|
||||||
load_list,
|
|
||||||
load_self,
|
|
||||||
add,
|
|
||||||
subtract,
|
|
||||||
multiply,
|
|
||||||
divide,
|
|
||||||
modulo,
|
|
||||||
equal,
|
|
||||||
less,
|
|
||||||
less_equal,
|
|
||||||
negate,
|
|
||||||
not,
|
|
||||||
test,
|
|
||||||
test_set,
|
|
||||||
call,
|
|
||||||
call_native,
|
|
||||||
jump,
|
|
||||||
r#return,
|
|
||||||
];
|
|
||||||
|
|
||||||
pub fn point(instruction: InstructionFields, thread: &mut Thread) {
|
pub fn point(instruction: InstructionFields, thread: &mut Thread) {
|
||||||
let destination = instruction.a_field as usize;
|
let destination = instruction.a_field as usize;
|
||||||
@ -703,6 +684,60 @@ pub fn add(instruction: InstructionFields, thread: &mut Thread) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_integers_optimized(instruction: InstructionFields, thread: &mut Thread) {
|
||||||
|
let current_frame = thread.current_frame_mut();
|
||||||
|
let pointer_cache = current_frame.pointer_caches[current_frame.ip];
|
||||||
|
|
||||||
|
if let PointerCache::Integers { left, right } = pointer_cache {
|
||||||
|
let destination = instruction.a_field as usize;
|
||||||
|
let destination_pointer = thread.get_integer_register_mut(destination) as *mut i64;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
asm!(
|
||||||
|
"add {}, {}",
|
||||||
|
inout(reg) *left => *destination_pointer,
|
||||||
|
in(reg) *right,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if let PointerCache::Empty = pointer_cache {
|
||||||
|
let left = instruction.b_field as usize;
|
||||||
|
let right = instruction.c_field as usize;
|
||||||
|
let left_is_constant = instruction.b_is_constant;
|
||||||
|
let right_is_constant = instruction.c_is_constant;
|
||||||
|
|
||||||
|
let left_value = if left_is_constant {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
thread.get_constant(left).as_integer().unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { thread.get_constant(left).as_integer().unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
thread.get_integer_register(left)
|
||||||
|
};
|
||||||
|
let right_value = if right_is_constant {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
thread.get_constant(right).as_integer().unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { thread.get_constant(right).as_integer().unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
thread.get_integer_register(right)
|
||||||
|
};
|
||||||
|
let cache = PointerCache::Integers {
|
||||||
|
left: left_value,
|
||||||
|
right: right_value,
|
||||||
|
};
|
||||||
|
let destination = instruction.a_field as usize;
|
||||||
|
let sum = left_value.saturating_add(*right_value);
|
||||||
|
let register = Register::Value(sum);
|
||||||
|
|
||||||
|
thread.set_integer_register(destination, register);
|
||||||
|
thread.set_current_pointer_cache(cache);
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn subtract(instruction: InstructionFields, thread: &mut Thread) {
|
pub fn subtract(instruction: InstructionFields, thread: &mut Thread) {
|
||||||
let destination = instruction.a_field as usize;
|
let destination = instruction.a_field as usize;
|
||||||
let left = instruction.b_field as usize;
|
let left = instruction.b_field as usize;
|
||||||
@ -1413,6 +1448,57 @@ pub fn less(instruction: InstructionFields, thread: &mut Thread) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn less_integers_optimized(instruction: InstructionFields, thread: &mut Thread) {
|
||||||
|
let current_frame = thread.current_frame_mut();
|
||||||
|
let pointer_cache = current_frame.pointer_caches[current_frame.ip];
|
||||||
|
|
||||||
|
let is_less = if let PointerCache::Integers { left, right } = pointer_cache {
|
||||||
|
let left_value = unsafe { *left };
|
||||||
|
let right_value = unsafe { *right };
|
||||||
|
|
||||||
|
left_value < right_value
|
||||||
|
} else if let PointerCache::Empty = pointer_cache {
|
||||||
|
let left = instruction.b_field as usize;
|
||||||
|
let right = instruction.c_field as usize;
|
||||||
|
let left_is_constant = instruction.b_is_constant;
|
||||||
|
let right_is_constant = instruction.c_is_constant;
|
||||||
|
|
||||||
|
let left_value = if left_is_constant {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
thread.get_constant(left).as_integer().unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { thread.get_constant(left).as_integer().unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
thread.get_integer_register(left)
|
||||||
|
};
|
||||||
|
let right_value = if right_is_constant {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
thread.get_constant(right).as_integer().unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { thread.get_constant(right).as_integer().unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
thread.get_integer_register(right)
|
||||||
|
};
|
||||||
|
let cache = PointerCache::Integers {
|
||||||
|
left: left_value,
|
||||||
|
right: right_value,
|
||||||
|
};
|
||||||
|
let is_less = left_value < right_value;
|
||||||
|
|
||||||
|
thread.set_current_pointer_cache(cache);
|
||||||
|
|
||||||
|
is_less
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_less {
|
||||||
|
thread.current_frame_mut().ip += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn less_equal(instruction: InstructionFields, thread: &mut Thread) {
|
pub fn less_equal(instruction: InstructionFields, thread: &mut Thread) {
|
||||||
let comparator = instruction.d_field;
|
let comparator = instruction.d_field;
|
||||||
let left = instruction.b_field as usize;
|
let left = instruction.b_field as usize;
|
||||||
@ -1668,224 +1754,3 @@ pub fn r#return(instruction: InstructionFields, thread: &mut Thread) {
|
|||||||
thread.return_value = Some(None);
|
thread.return_value = Some(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn optimized_loop(instructions: Vec<(InstructionFields, PointerCache)>, thread: &mut Thread) {
|
|
||||||
let span = span!(Level::TRACE, "Optimized Loop");
|
|
||||||
let _ = span.enter();
|
|
||||||
|
|
||||||
let mut loop_ip = 0;
|
|
||||||
|
|
||||||
while loop_ip < instructions.len() {
|
|
||||||
let (instruction, mut pointer_cache) = instructions[loop_ip];
|
|
||||||
|
|
||||||
loop_ip += 1;
|
|
||||||
|
|
||||||
match instruction.operation {
|
|
||||||
Operation::ADD => {
|
|
||||||
trace!("Running loop-optimized ADD instruction");
|
|
||||||
|
|
||||||
let destination = instruction.a_field as usize;
|
|
||||||
let sum = if pointer_cache.integers[0].is_null() {
|
|
||||||
let left = instruction.b_field as usize;
|
|
||||||
let left_is_constant = instruction.b_is_constant;
|
|
||||||
let right = instruction.c_field as usize;
|
|
||||||
let right_is_constant = instruction.c_is_constant;
|
|
||||||
|
|
||||||
let left_value = if left_is_constant {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
thread.get_constant(left).as_integer().unwrap()
|
|
||||||
} else {
|
|
||||||
unsafe { thread.get_constant(left).as_integer().unwrap_unchecked() }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
thread.get_integer_register(left)
|
|
||||||
};
|
|
||||||
let right_value = if right_is_constant {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
thread.get_constant(right).as_integer().unwrap()
|
|
||||||
} else {
|
|
||||||
unsafe { thread.get_constant(right).as_integer().unwrap_unchecked() }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
thread.get_integer_register(right)
|
|
||||||
};
|
|
||||||
|
|
||||||
pointer_cache.integers[0] = left_value;
|
|
||||||
pointer_cache.integers[1] = right_value;
|
|
||||||
|
|
||||||
left_value.saturating_add(*right_value)
|
|
||||||
} else {
|
|
||||||
let left_value = unsafe { *pointer_cache.integers[0] };
|
|
||||||
let right_value = unsafe { *pointer_cache.integers[1] };
|
|
||||||
|
|
||||||
left_value.saturating_add(right_value)
|
|
||||||
};
|
|
||||||
|
|
||||||
let register = Register::Value(sum);
|
|
||||||
|
|
||||||
thread.set_integer_register(destination, register);
|
|
||||||
}
|
|
||||||
Operation::LESS => {
|
|
||||||
trace!("Running loop-optimized LESS instruction");
|
|
||||||
|
|
||||||
let comparator = instruction.d_field;
|
|
||||||
let result = if pointer_cache.integers[0].is_null() {
|
|
||||||
let left = instruction.b_field as usize;
|
|
||||||
let left_is_constant = instruction.b_is_constant;
|
|
||||||
let right = instruction.c_field as usize;
|
|
||||||
let right_is_constant = instruction.c_is_constant;
|
|
||||||
let left_value = if left_is_constant {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
thread.get_constant(left).as_integer().unwrap()
|
|
||||||
} else {
|
|
||||||
unsafe { thread.get_constant(left).as_integer().unwrap_unchecked() }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
thread.get_integer_register(left)
|
|
||||||
};
|
|
||||||
let right_value = if right_is_constant {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
thread.get_constant(right).as_integer().unwrap()
|
|
||||||
} else {
|
|
||||||
unsafe { thread.get_constant(right).as_integer().unwrap_unchecked() }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
thread.get_integer_register(right)
|
|
||||||
};
|
|
||||||
|
|
||||||
pointer_cache.integers[0] = left_value;
|
|
||||||
pointer_cache.integers[1] = right_value;
|
|
||||||
|
|
||||||
left_value < right_value
|
|
||||||
} else {
|
|
||||||
let left_value = unsafe { *pointer_cache.integers[0] };
|
|
||||||
let right_value = unsafe { *pointer_cache.integers[1] };
|
|
||||||
|
|
||||||
left_value < right_value
|
|
||||||
};
|
|
||||||
|
|
||||||
if result == comparator {
|
|
||||||
loop_ip += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Operation::LESS_EQUAL => {
|
|
||||||
trace!("Running loop-optimized LESS_EQUAL instruction");
|
|
||||||
|
|
||||||
let comparator = instruction.d_field;
|
|
||||||
let result = if pointer_cache.integers[0].is_null() {
|
|
||||||
let left = instruction.b_field as usize;
|
|
||||||
let left_is_constant = instruction.b_is_constant;
|
|
||||||
let right = instruction.c_field as usize;
|
|
||||||
let right_is_constant = instruction.c_is_constant;
|
|
||||||
let left_value = if left_is_constant {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
thread.get_constant(left).as_integer().unwrap()
|
|
||||||
} else {
|
|
||||||
unsafe { thread.get_constant(left).as_integer().unwrap_unchecked() }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
thread.get_integer_register(left)
|
|
||||||
};
|
|
||||||
let right_value = if right_is_constant {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
thread.get_constant(right).as_integer().unwrap()
|
|
||||||
} else {
|
|
||||||
unsafe { thread.get_constant(right).as_integer().unwrap_unchecked() }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
thread.get_integer_register(right)
|
|
||||||
};
|
|
||||||
|
|
||||||
pointer_cache.integers[0] = left_value;
|
|
||||||
pointer_cache.integers[1] = right_value;
|
|
||||||
|
|
||||||
left_value <= right_value
|
|
||||||
} else {
|
|
||||||
let left_value = unsafe { *pointer_cache.integers[0] };
|
|
||||||
let right_value = unsafe { *pointer_cache.integers[1] };
|
|
||||||
|
|
||||||
left_value <= right_value
|
|
||||||
};
|
|
||||||
|
|
||||||
if result == comparator {
|
|
||||||
loop_ip += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Operation::JUMP => {
|
|
||||||
trace!("Running loop-optimized JUMP instruction");
|
|
||||||
|
|
||||||
let offset = instruction.b_field as usize;
|
|
||||||
let is_positive = instruction.c_field != 0;
|
|
||||||
|
|
||||||
if is_positive {
|
|
||||||
loop_ip += offset;
|
|
||||||
} else {
|
|
||||||
loop_ip -= offset + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let runner = ACTION_LOGIC_TABLE[instruction.operation.0 as usize];
|
|
||||||
runner(instruction, thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct PointerCache {
|
|
||||||
integers: [*const i64; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PointerCache {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
integers: [std::ptr::null(); 2],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use crate::Operation;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
const ALL_OPERATIONS: [(Operation, ActionLogic); 23] = [
|
|
||||||
(Operation::POINT, point),
|
|
||||||
(Operation::CLOSE, close),
|
|
||||||
(Operation::LOAD_ENCODED, load_encoded),
|
|
||||||
(Operation::LOAD_CONSTANT, load_constant),
|
|
||||||
(Operation::LOAD_FUNCTION, load_function),
|
|
||||||
(Operation::LOAD_LIST, load_list),
|
|
||||||
(Operation::LOAD_SELF, load_self),
|
|
||||||
(Operation::ADD, add),
|
|
||||||
(Operation::SUBTRACT, subtract),
|
|
||||||
(Operation::MULTIPLY, multiply),
|
|
||||||
(Operation::DIVIDE, divide),
|
|
||||||
(Operation::MODULO, modulo),
|
|
||||||
(Operation::TEST, test),
|
|
||||||
(Operation::TEST_SET, test_set),
|
|
||||||
(Operation::EQUAL, equal),
|
|
||||||
(Operation::LESS, less),
|
|
||||||
(Operation::LESS_EQUAL, less_equal),
|
|
||||||
(Operation::NEGATE, negate),
|
|
||||||
(Operation::NOT, not),
|
|
||||||
(Operation::CALL, call),
|
|
||||||
(Operation::CALL_NATIVE, call_native),
|
|
||||||
(Operation::JUMP, jump),
|
|
||||||
(Operation::RETURN, r#return),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn operations_map_to_the_correct_runner() {
|
|
||||||
for (operation, expected_runner) in ALL_OPERATIONS {
|
|
||||||
let actual_runner = ACTION_LOGIC_TABLE[operation.0 as usize];
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
expected_runner, actual_runner,
|
|
||||||
"{operation} runner is incorrect"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -16,12 +16,14 @@ pub struct CallFrame {
|
|||||||
pub return_register: u16,
|
pub return_register: u16,
|
||||||
pub registers: RegisterTable,
|
pub registers: RegisterTable,
|
||||||
pub action_sequence: ActionSequence,
|
pub action_sequence: ActionSequence,
|
||||||
|
pub pointer_caches: Vec<PointerCache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CallFrame {
|
impl CallFrame {
|
||||||
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
|
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
|
||||||
let registers = RegisterTable::new();
|
let registers = RegisterTable::new();
|
||||||
let action_sequence = ActionSequence::new(&chunk.instructions);
|
let action_sequence = ActionSequence::new(&chunk.instructions);
|
||||||
|
let optimization_data = vec![PointerCache::default(); chunk.instructions.len()];
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
chunk,
|
chunk,
|
||||||
@ -29,6 +31,7 @@ impl CallFrame {
|
|||||||
return_register,
|
return_register,
|
||||||
registers,
|
registers,
|
||||||
action_sequence,
|
action_sequence,
|
||||||
|
pointer_caches: optimization_data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,6 +91,16 @@ pub enum Register<T> {
|
|||||||
Pointer(Pointer),
|
Pointer(Pointer),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Register<T> {
|
||||||
|
pub fn contained_value_mut(&mut self) -> Option<&mut T> {
|
||||||
|
match self {
|
||||||
|
Self::Value(value) => Some(value),
|
||||||
|
Self::Closed(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Display> Display for Register<T> {
|
impl<T: Display> Display for Register<T> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
@ -133,3 +146,13 @@ impl Display for Pointer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub enum PointerCache {
|
||||||
|
#[default]
|
||||||
|
Empty,
|
||||||
|
Integers {
|
||||||
|
left: *const i64,
|
||||||
|
right: *const i64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
use std::{collections::HashMap, sync::Arc, thread::JoinHandle};
|
use std::{sync::Arc, thread::JoinHandle};
|
||||||
|
|
||||||
use tracing::{info, trace};
|
use tracing::{info, trace};
|
||||||
|
|
||||||
use crate::{AbstractList, Chunk, ConcreteValue, DustString, Span, Value, vm::CallFrame};
|
use crate::{AbstractList, Chunk, ConcreteValue, DustString, Span, Value, vm::CallFrame};
|
||||||
|
|
||||||
use super::call_frame::{Pointer, Register};
|
use super::call_frame::{Pointer, PointerCache, Register};
|
||||||
|
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
chunk: Arc<Chunk>,
|
chunk: Arc<Chunk>,
|
||||||
call_stack: Vec<CallFrame>,
|
call_stack: Vec<CallFrame>,
|
||||||
pub return_value: Option<Option<Value>>,
|
pub return_value: Option<Option<Value>>,
|
||||||
pub integer_cache: HashMap<usize, *const i64>,
|
|
||||||
_spawned_threads: Vec<JoinHandle<()>>,
|
_spawned_threads: Vec<JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +24,6 @@ impl Thread {
|
|||||||
chunk,
|
chunk,
|
||||||
call_stack,
|
call_stack,
|
||||||
return_value: None,
|
return_value: None,
|
||||||
integer_cache: HashMap::new(),
|
|
||||||
_spawned_threads: Vec::new(),
|
_spawned_threads: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,6 +39,10 @@ impl Thread {
|
|||||||
trace!("Thread actions: {}", self.current_frame().action_sequence);
|
trace!("Thread actions: {}", self.current_frame().action_sequence);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
if let Some(return_value_option) = self.return_value {
|
||||||
|
return return_value_option;
|
||||||
|
}
|
||||||
|
|
||||||
let current_frame = self.current_frame_mut();
|
let current_frame = self.current_frame_mut();
|
||||||
let ip = {
|
let ip = {
|
||||||
let ip = current_frame.ip;
|
let ip = current_frame.ip;
|
||||||
@ -54,22 +56,7 @@ impl Thread {
|
|||||||
unsafe { current_frame.action_sequence.actions.get_unchecked_mut(ip) }
|
unsafe { current_frame.action_sequence.actions.get_unchecked_mut(ip) }
|
||||||
};
|
};
|
||||||
|
|
||||||
if let (Some(optimized_logic), Some(loop_instructions)) = (
|
(current_action.logic)(current_action.instruction, &mut self);
|
||||||
¤t_action.optimized_logic,
|
|
||||||
¤t_action.loop_instructions,
|
|
||||||
) {
|
|
||||||
let loop_instructions = loop_instructions.clone();
|
|
||||||
|
|
||||||
(optimized_logic)(loop_instructions, &mut self);
|
|
||||||
} else {
|
|
||||||
trace!("Instruction: {}", current_action.instruction.operation);
|
|
||||||
|
|
||||||
(current_action.logic)(current_action.instruction, &mut self);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(return_value_option) = self.return_value {
|
|
||||||
return return_value_option;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,6 +82,29 @@ impl Thread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_current_pointer_cache(&mut self, new_cache: PointerCache) {
|
||||||
|
let ip = self.current_frame().ip;
|
||||||
|
|
||||||
|
let old_cache = if cfg!(debug_assertions) {
|
||||||
|
self.call_stack
|
||||||
|
.last_mut()
|
||||||
|
.unwrap()
|
||||||
|
.pointer_caches
|
||||||
|
.get_mut(ip - 1)
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
self.call_stack
|
||||||
|
.last_mut()
|
||||||
|
.unwrap_unchecked()
|
||||||
|
.pointer_caches
|
||||||
|
.get_unchecked_mut(ip - 1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
*old_cache = new_cache;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_value_from_pointer(&self, pointer: &Pointer) -> ConcreteValue {
|
pub fn get_value_from_pointer(&self, pointer: &Pointer) -> ConcreteValue {
|
||||||
match pointer {
|
match pointer {
|
||||||
Pointer::RegisterBoolean(register_index) => {
|
Pointer::RegisterBoolean(register_index) => {
|
||||||
@ -586,6 +596,31 @@ impl Thread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_integer_register_mut(&mut self, register_index: usize) -> &mut i64 {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.call_stack
|
||||||
|
.last_mut()
|
||||||
|
.unwrap()
|
||||||
|
.registers
|
||||||
|
.integers
|
||||||
|
.get_mut(register_index)
|
||||||
|
.unwrap()
|
||||||
|
.contained_value_mut()
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
self.call_stack
|
||||||
|
.last_mut()
|
||||||
|
.unwrap_unchecked()
|
||||||
|
.registers
|
||||||
|
.integers
|
||||||
|
.get_unchecked_mut(register_index)
|
||||||
|
.contained_value_mut()
|
||||||
|
.unwrap_unchecked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_pointer_to_integer(&self, pointer: &Pointer) -> &i64 {
|
pub fn get_pointer_to_integer(&self, pointer: &Pointer) -> &i64 {
|
||||||
match pointer {
|
match pointer {
|
||||||
Pointer::RegisterInteger(register_index) => self.get_integer_register(*register_index),
|
Pointer::RegisterInteger(register_index) => self.get_integer_register(*register_index),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user