From c1fe54ccd52566a85fd89a8068b0ba57ed7985d2 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 3 Feb 2025 15:05:32 -0500 Subject: [PATCH] Attempt alternative to total register overhaul --- dust-lang/src/instruction/add.rs | 9 +- dust-lang/src/instruction/less.rs | 9 +- dust-lang/src/instruction/less_equal.rs | 9 +- dust-lang/src/instruction/mod.rs | 17 +++ dust-lang/src/vm/{run_action.rs => action.rs} | 112 +++++++------- .../vm/{function_call.rs => call_frame.rs} | 6 +- dust-lang/src/vm/mod.rs | 17 +-- dust-lang/src/vm/stack.rs | 137 ------------------ dust-lang/src/vm/thread.rs | 111 +++++++------- 9 files changed, 158 insertions(+), 269 deletions(-) rename dust-lang/src/vm/{run_action.rs => action.rs} (88%) rename dust-lang/src/vm/{function_call.rs => call_frame.rs} (91%) delete mode 100644 dust-lang/src/vm/stack.rs diff --git a/dust-lang/src/instruction/add.rs b/dust-lang/src/instruction/add.rs index 94230bb..c00f643 100644 --- a/dust-lang/src/instruction/add.rs +++ b/dust-lang/src/instruction/add.rs @@ -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}",) } } diff --git a/dust-lang/src/instruction/less.rs b/dust-lang/src/instruction/less.rs index 67fa53b..025b204 100644 --- a/dust-lang/src/instruction/less.rs +++ b/dust-lang/src/instruction/less.rs @@ -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 }}") } } diff --git a/dust-lang/src/instruction/less_equal.rs b/dust-lang/src/instruction/less_equal.rs index fc3238f..d1dbf1a 100644 --- a/dust-lang/src/instruction/less_equal.rs +++ b/dust-lang/src/instruction/less_equal.rs @@ -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 }}") } } diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index 424819c..99c3f68 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -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 { diff --git a/dust-lang/src/vm/run_action.rs b/dust-lang/src/vm/action.rs similarity index 88% rename from dust-lang/src/vm/run_action.rs rename to dust-lang/src/vm/action.rs index 167419a..8225425 100644 --- a/dust-lang/src/vm/run_action.rs +++ b/dust-lang/src/vm/action.rs @@ -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, } -impl From 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 { diff --git a/dust-lang/src/vm/function_call.rs b/dust-lang/src/vm/call_frame.rs similarity index 91% rename from dust-lang/src/vm/function_call.rs rename to dust-lang/src/vm/call_frame.rs index 7db37aa..9495200 100644 --- a/dust-lang/src/vm/function_call.rs +++ b/dust-lang/src/vm/call_frame.rs @@ -8,14 +8,14 @@ use crate::{Chunk, DustString}; use super::Register; #[derive(Debug)] -pub struct FunctionCall { +pub struct CallFrame { pub chunk: Arc, pub ip: usize, pub return_register: u16, pub registers: Vec, } -impl FunctionCall { +impl CallFrame { pub fn new(chunk: Arc, 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, diff --git a/dust-lang/src/vm/mod.rs b/dust-lang/src/vm/mod.rs index 0e44c78..aa9fae3 100644 --- a/dust-lang/src/vm/mod.rs +++ b/dust-lang/src/vm/mod.rs @@ -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); }) diff --git a/dust-lang/src/vm/stack.rs b/dust-lang/src/vm/stack.rs deleted file mode 100644 index dd43253..0000000 --- a/dust-lang/src/vm/stack.rs +++ /dev/null @@ -1,137 +0,0 @@ -use std::{ - fmt::{self, Debug, Display, Formatter}, - ops::{Index, IndexMut, Range}, -}; - -use super::FunctionCall; - -#[derive(Clone, PartialEq)] -pub struct Stack { - items: Vec, -} - -impl Stack { - 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 { - 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 Default for Stack { - fn default() -> Self { - Self::new() - } -} - -impl Index> for Stack { - type Output = [T]; - - fn index(&self, index: Range) -> &Self::Output { - &self.items[index] - } -} - -impl IndexMut> for Stack { - fn index_mut(&mut self, index: Range) -> &mut Self::Output { - &mut self.items[index] - } -} - -impl Debug for Stack { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{:?}", self.items) - } -} - -impl Display for Stack { - 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, "---------------------------") - } -} diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs index c605066..3dad090 100644 --- a/dust-lang/src/vm/thread.rs +++ b/dust-lang/src/vm/thread.rs @@ -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, + call_stack: Vec, + return_value_index: Option>, + spawned_threads: Vec>, } impl Thread { pub fn new(chunk: Arc) -> 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 { + pub fn run(mut self) -> Option { 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, - pub next_action: RunAction, - pub return_value_index: Option, - pub spawned_threads: Vec>, -} - -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 {