1
0

Attempt alternative to total register overhaul

This commit is contained in:
Jeff 2025-02-03 15:05:32 -05:00
parent 3fcbde59e5
commit c1fe54ccd5
9 changed files with 158 additions and 269 deletions

View File

@ -56,14 +56,11 @@ impl Display for Add {
let Add {
destination,
left,
left_type,
left_type: _,
right,
right_type,
right_type: _,
} = self;
write!(
f,
"R{destination} = {left}({left_type}) + {right}({right_type})",
)
write!(f, "R{destination} = {left} + {right}",)
}
}

View File

@ -56,15 +56,12 @@ impl Display for Less {
let Less {
comparator,
left,
left_type,
left_type: _,
right,
right_type,
right_type: _,
} = self;
let operator = if *comparator { "<" } else { "" };
write!(
f,
"if {left_type}({left}) {operator} {right_type}({right}) {{ JUMP +1 }}"
)
write!(f, "if {left} {operator} {right} {{ JUMP +1 }}")
}
}

View File

@ -56,15 +56,12 @@ impl Display for LessEqual {
let LessEqual {
comparator,
left,
left_type,
left_type: _,
right,
right_type,
right_type: _,
} = self;
let operator = if *comparator { "" } else { ">" };
write!(
f,
"if {left_type}({left}) {operator} {right_type}({right}) {{ JUMP +1 }}"
)
write!(f, "if {left} {operator} {right} {{ JUMP +1 }}")
}
}

View File

@ -154,6 +154,7 @@ pub use type_code::TypeCode;
use crate::NativeFunction;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct InstructionBuilder {
pub operation: Operation,
pub a_field: u16,
@ -182,6 +183,22 @@ impl InstructionBuilder {
}
}
impl From<&Instruction> for InstructionBuilder {
fn from(instruction: &Instruction) -> Self {
InstructionBuilder {
operation: instruction.operation(),
a_field: instruction.a_field(),
b_field: instruction.b_field(),
c_field: instruction.c_field(),
d_field: instruction.d_field(),
b_is_constant: instruction.b_is_constant(),
c_is_constant: instruction.c_is_constant(),
b_type: instruction.b_type(),
c_type: instruction.c_type(),
}
}
}
impl Default for InstructionBuilder {
fn default() -> Self {
InstructionBuilder {

View File

@ -3,31 +3,50 @@ use tracing::trace;
use crate::{
AbstractList, ConcreteValue, Instruction, Operand, Type, Value,
instruction::{
Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean,
LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
Return, SetLocal, Subtract, Test, TestSet, TypeCode,
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,
},
vm::FunctionCall,
vm::CallFrame,
};
use super::{Pointer, Register, thread::ThreadData};
use super::{Pointer, Register, thread::Thread};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RunAction {
pub logic: RunnerLogic,
pub instruction: Instruction,
pub struct ActionSequence {
pub actions: Vec<Action>,
}
impl From<Instruction> for RunAction {
fn from(instruction: Instruction) -> Self {
let operation = instruction.operation();
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
impl ActionSequence {
pub fn new(instructions: &[Instruction]) -> Self {
let mut actions = Vec::with_capacity(instructions.len());
RunAction { logic, instruction }
for instruction in instructions {
let action = Action::from(*instruction);
actions.push(action);
}
ActionSequence { actions }
}
}
pub type RunnerLogic = fn(Instruction, &mut ThreadData) -> bool;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Action {
pub logic: RunnerLogic,
pub instruction: InstructionBuilder,
}
impl From<&Instruction> for Action {
fn from(instruction: &Instruction) -> Self {
let operation = instruction.operation();
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
let instruction = InstructionBuilder::from(instruction);
Action { logic, instruction }
}
}
pub type RunnerLogic = fn(InstructionBuilder, &mut Thread) -> bool;
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
point,
@ -57,18 +76,7 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
r#return,
];
pub(crate) fn get_next_action(data: &mut ThreadData) -> RunAction {
let current_call = data.call_stack.last_mut_unchecked();
let instruction = current_call.chunk.instructions[current_call.ip];
let operation = instruction.operation();
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
current_call.ip += 1;
RunAction { logic, instruction }
}
pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
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);
@ -84,7 +92,7 @@ pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn close(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Close { from, to } = instruction.into();
for register_index in from..to {
@ -96,7 +104,7 @@ pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn load_boolean(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let LoadBoolean {
destination,
value,
@ -118,7 +126,7 @@ pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn load_constant(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let LoadConstant {
destination,
constant_index,
@ -141,7 +149,7 @@ pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn load_list(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let LoadList {
destination,
start_register,
@ -183,7 +191,7 @@ pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn load_function(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let LoadFunction {
destination,
prototype_index,
@ -202,7 +210,7 @@ pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn load_self(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let LoadSelf {
destination,
jump_next,
@ -219,7 +227,7 @@ pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn get_local(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let GetLocal {
destination,
local_index,
@ -234,7 +242,7 @@ pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn set_local(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let SetLocal {
register_index,
local_index,
@ -249,7 +257,7 @@ pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn add(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Add {
destination,
left,
@ -297,7 +305,7 @@ pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn subtract(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Subtract {
destination,
left,
@ -345,7 +353,7 @@ pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn multiply(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Multiply {
destination,
left,
@ -393,7 +401,7 @@ pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn divide(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Divide {
destination,
left,
@ -441,7 +449,7 @@ pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn modulo(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Modulo {
destination,
left,
@ -475,7 +483,7 @@ pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn test(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Test {
operand_register,
test_value,
@ -498,7 +506,7 @@ pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn test_set(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let TestSet {
destination,
argument,
@ -527,7 +535,7 @@ pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn equal(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Equal {
comparator,
left,
@ -606,7 +614,7 @@ pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn less(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Less {
comparator,
left,
@ -657,7 +665,7 @@ pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn less_equal(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let LessEqual {
comparator,
left,
@ -708,7 +716,7 @@ pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn negate(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Negate {
destination,
argument,
@ -725,7 +733,7 @@ pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn not(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn not(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Not {
destination,
argument,
@ -744,7 +752,7 @@ pub fn not(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn jump(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn jump(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Jump {
offset,
is_positive,
@ -763,7 +771,7 @@ pub fn jump(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn call(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let Call {
destination: return_register,
function_register,
@ -782,7 +790,7 @@ pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
current_call.chunk.prototypes[function.prototype_index as usize].clone()
};
let mut next_call = FunctionCall::new(prototype, return_register);
let mut next_call = CallFrame::new(prototype, return_register);
let mut argument_index = 0;
for register_index in first_argument_register..return_register {
@ -804,7 +812,7 @@ pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
false
}
pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn call_native(instruction: InstructionBuilder, data: &mut Thread) -> bool {
let CallNative {
destination,
function,
@ -816,7 +824,7 @@ pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> bool {
function.call(data, destination, argument_range)
}
pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool {
pub fn r#return(instruction: InstructionBuilder, data: &mut Thread) -> bool {
trace!("Returning with call stack:\n{}", data.call_stack);
let Return {

View File

@ -8,14 +8,14 @@ use crate::{Chunk, DustString};
use super::Register;
#[derive(Debug)]
pub struct FunctionCall {
pub struct CallFrame {
pub chunk: Arc<Chunk>,
pub ip: usize,
pub return_register: u16,
pub registers: Vec<Register>,
}
impl FunctionCall {
impl CallFrame {
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
let register_count = chunk.register_count;
@ -28,7 +28,7 @@ impl FunctionCall {
}
}
impl Display for FunctionCall {
impl Display for CallFrame {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,

View File

@ -1,7 +1,6 @@
//! Virtual machine and errors
mod function_call;
mod run_action;
mod stack;
mod action;
mod call_frame;
mod thread;
use std::{
@ -10,11 +9,10 @@ use std::{
thread::Builder,
};
pub use function_call::FunctionCall;
pub use run_action::RunAction;
pub(crate) use run_action::get_next_action;
pub use stack::Stack;
pub use thread::{Thread, ThreadData};
pub use action::Action;
pub(crate) use action::get_next_action;
pub use call_frame::CallFrame;
pub use thread::Thread;
use crossbeam_channel::bounded;
use tracing::{Level, span};
@ -46,12 +44,13 @@ impl Vm {
.as_ref()
.map(|name| name.to_string())
.unwrap_or_else(|| "anonymous".to_string());
let mut main_thread = Thread::new(Arc::new(self.main_chunk));
let (tx, rx) = bounded(1);
let main_chunk = Arc::new(self.main_chunk);
Builder::new()
.name(thread_name)
.spawn(move || {
let main_thread = Thread::new(main_chunk);
let value_option = main_thread.run();
let _ = tx.send(value_option);
})

View File

@ -1,137 +0,0 @@
use std::{
fmt::{self, Debug, Display, Formatter},
ops::{Index, IndexMut, Range},
};
use super::FunctionCall;
#[derive(Clone, PartialEq)]
pub struct Stack<T> {
items: Vec<T>,
}
impl<T> Stack<T> {
pub fn new() -> Self {
Stack {
items: Vec::with_capacity(1),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Stack {
items: Vec::with_capacity(capacity),
}
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn get_unchecked(&self, index: usize) -> &T {
if cfg!(debug_assertions) {
assert!(index < self.len(), "Stack underflow");
&self.items[index]
} else {
unsafe { self.items.get_unchecked(index) }
}
}
pub fn get_unchecked_mut(&mut self, index: usize) -> &mut T {
if cfg!(debug_assertions) {
assert!(index < self.len(), "Stack underflow");
&mut self.items[index]
} else {
unsafe { self.items.get_unchecked_mut(index) }
}
}
pub fn push(&mut self, item: T) {
self.items.push(item);
}
pub fn pop(&mut self) -> Option<T> {
self.items.pop()
}
pub fn last(&self) -> Option<&T> {
self.items.last()
}
pub fn last_mut(&mut self) -> Option<&mut T> {
self.items.last_mut()
}
pub fn pop_unchecked(&mut self) -> T {
if cfg!(debug_assertions) {
assert!(!self.is_empty(), "Stack underflow");
self.items.pop().unwrap()
} else {
unsafe { self.items.pop().unwrap_unchecked() }
}
}
pub fn last_unchecked(&self) -> &T {
if cfg!(debug_assertions) {
assert!(!self.is_empty(), "Stack underflow");
self.items.last().unwrap()
} else {
unsafe { self.items.last().unwrap_unchecked() }
}
}
pub fn last_mut_unchecked(&mut self) -> &mut T {
if cfg!(debug_assertions) {
assert!(!self.is_empty(), "Stack underflow");
self.items.last_mut().unwrap()
} else {
unsafe { self.items.last_mut().unwrap_unchecked() }
}
}
}
impl<T> Default for Stack<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Index<Range<usize>> for Stack<T> {
type Output = [T];
fn index(&self, index: Range<usize>) -> &Self::Output {
&self.items[index]
}
}
impl<T> IndexMut<Range<usize>> for Stack<T> {
fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {
&mut self.items[index]
}
}
impl<T: Debug> Debug for Stack<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{:?}", self.items)
}
}
impl Display for Stack<FunctionCall> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
writeln!(f, "----- DUST CALL STACK -----")?;
for (index, function_call) in self.items.iter().enumerate().rev() {
writeln!(f, "{index:02} | {function_call}")?;
}
write!(f, "---------------------------")
}
}

View File

@ -2,20 +2,36 @@ use std::{mem::replace, sync::Arc, thread::JoinHandle};
use tracing::{info, trace};
use crate::{Chunk, DustString, Operand, Span, Value, vm::FunctionCall};
use crate::{
Chunk, DustString, Operand, Span, Value,
vm::{CallFrame, action::ActionSequence},
};
use super::{Pointer, Register, RunAction, Stack};
use super::{Pointer, Register};
pub struct Thread {
chunk: Arc<Chunk>,
call_stack: Vec<CallFrame>,
return_value_index: Option<Option<usize>>,
spawned_threads: Vec<JoinHandle<()>>,
}
impl Thread {
pub fn new(chunk: Arc<Chunk>) -> Self {
Thread { chunk }
let mut call_stack = Vec::with_capacity(chunk.prototypes.len() + 1);
let main_call = CallFrame::new(Arc::clone(&chunk), 0);
call_stack.push(main_call);
Thread {
chunk,
call_stack,
return_value_index: None,
spawned_threads: Vec::new(),
}
}
pub fn run(&mut self) -> Option<Value> {
pub fn run(mut self) -> Option<Value> {
info!(
"Starting thread with {}",
self.chunk
@ -24,64 +40,59 @@ impl Thread {
.unwrap_or_else(|| DustString::from("anonymous"))
);
let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1);
let mut main_call = FunctionCall::new(self.chunk.clone(), 0);
main_call.ip = 1; // The first action is already known
call_stack.push(main_call);
let first_action = RunAction::from(*self.chunk.instructions.first().unwrap());
let mut thread_data = ThreadData {
call_stack,
next_action: first_action,
return_value_index: None,
spawned_threads: Vec::new(),
};
let main_call = self.current_frame();
let action_sequence = ActionSequence::new(&main_call.chunk.instructions);
loop {
trace!("Instruction: {}", thread_data.next_action.instruction);
let current_frame = self.current_frame_mut();
let ip = {
let ip = current_frame.ip;
current_frame.ip += 1;
let should_end = (thread_data.next_action.logic)(
thread_data.next_action.instruction,
&mut thread_data,
);
ip
};
let current_action = if cfg!(debug_assertions) {
action_sequence.actions.get(ip).unwrap()
} else {
unsafe { action_sequence.actions.get_unchecked(ip) }
};
if should_end {
let value_option = if let Some(register_index) = thread_data.return_value_index {
let value =
thread_data.empty_register_or_clone_constant_unchecked(register_index);
trace!("Operation: {}", current_action.instruction.operation);
Some(value)
(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();
return Some(return_value);
} else {
None
};
thread_data
.spawned_threads
.into_iter()
.for_each(|join_handle| {
let _ = join_handle.join();
});
return value_option;
return None;
}
}
}
}
}
#[derive(Debug)]
pub struct ThreadData {
pub call_stack: Stack<FunctionCall>,
pub next_action: RunAction,
pub return_value_index: Option<u16>,
pub spawned_threads: Vec<JoinHandle<()>>,
}
impl ThreadData {
pub fn current_position(&self) -> Span {
let current_call = self.call_stack.last_unchecked();
let current_frame = self.current_frame();
current_call.chunk.positions[current_call.ip]
current_frame.chunk.positions[current_frame.ip]
}
pub fn current_frame(&self) -> &CallFrame {
if cfg!(debug_assertions) {
self.call_stack.last().unwrap()
} else {
unsafe { self.call_stack.last().unwrap_unchecked() }
}
}
pub fn current_frame_mut(&mut self) -> &mut CallFrame {
if cfg!(debug_assertions) {
self.call_stack.last_mut().unwrap()
} else {
unsafe { self.call_stack.last_mut().unwrap_unchecked() }
}
}
pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value {