From cd4fa6bef54cc269c18b51de4ae235214e3357ec Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 14 Dec 2024 04:54:45 -0500 Subject: [PATCH] Optimize VM --- dust-cli/src/main.rs | 2 +- dust-lang/src/value/mod.rs | 1 - dust-lang/src/vm/mod.rs | 65 +++++------ dust-lang/src/vm/runners.rs | 217 +++++++++++++++++++++++++++++++----- 4 files changed, 225 insertions(+), 60 deletions(-) diff --git a/dust-cli/src/main.rs b/dust-cli/src/main.rs index 0cceb84..388b61c 100644 --- a/dust-cli/src/main.rs +++ b/dust-cli/src/main.rs @@ -199,7 +199,7 @@ fn main() { let chunk = compiler.finish(None, None); let compile_end = start_time.elapsed(); - let vm = Vm::new(&source, &chunk, None); + let vm = Vm::new(&source, &chunk, None, None); let return_value = vm.run(); let run_end = start_time.elapsed(); diff --git a/dust-lang/src/value/mod.rs b/dust-lang/src/value/mod.rs index d155fcb..a6f0761 100644 --- a/dust-lang/src/value/mod.rs +++ b/dust-lang/src/value/mod.rs @@ -153,7 +153,6 @@ impl ValueRef<'_> { } } - #[inline(always)] pub fn less(&self, other: ValueRef) -> Result { match (self, other) { (ValueRef::Concrete(left), ValueRef::Concrete(right)) => { diff --git a/dust-lang/src/vm/mod.rs b/dust-lang/src/vm/mod.rs index d3b0ae0..896bc32 100644 --- a/dust-lang/src/vm/mod.rs +++ b/dust-lang/src/vm/mod.rs @@ -3,10 +3,10 @@ mod runners; use std::{ fmt::{self, Display, Formatter}, - io, + io, iter, }; -use runners::{Runner, RUNNERS}; +use runners::Runner; use smallvec::SmallVec; use crate::{ @@ -16,7 +16,7 @@ use crate::{ pub fn run(source: &str) -> Result, DustError> { let chunk = compile(source)?; - let vm = Vm::new(source, &chunk, None); + let vm = Vm::new(source, &chunk, None, None); Ok(vm.run()) } @@ -28,6 +28,7 @@ pub fn run(source: &str) -> Result, DustError> { pub struct Vm<'a> { stack: Vec, + runners: Vec, chunk: &'a Chunk, parent: Option<&'a Vm<'a>>, @@ -38,11 +39,28 @@ pub struct Vm<'a> { } impl<'a> Vm<'a> { - pub fn new(source: &'a str, chunk: &'a Chunk, parent: Option<&'a Vm<'a>>) -> Self { + pub fn new( + source: &'a str, + chunk: &'a Chunk, + parent: Option<&'a Vm<'a>>, + runners: Option>, + ) -> Self { let stack = vec![Register::Empty; chunk.stack_size()]; + let runners = runners.unwrap_or_else(|| { + let mut runners = Vec::with_capacity(chunk.instructions().len()); + + for (instruction, _) in chunk.instructions() { + let runner = Runner::new(*instruction); + + runners.push(runner); + } + + runners + }); Self { chunk, + runners, stack, parent, ip: 0, @@ -68,27 +86,13 @@ impl<'a> Vm<'a> { } pub fn run(mut self) -> Option { - let runners = self - .chunk - .instructions() - .iter() - .map(|(instruction, _)| { - let (operation, data) = instruction.decode(); - let runner = RUNNERS[operation.0 as usize]; + while self.ip < self.runners.len() && self.return_value.is_none() { + let runner = self.runners[self.ip]; - (runner, data) - }) - .collect::>(); - - while self.ip < runners.len() && self.return_value.is_none() { - let (runner, data) = runners[self.ip]; - - self.ip += 1; - - runner(&mut self, data); + runner.run(&mut self); } - self.return_value.take() + self.return_value } pub(crate) fn follow_pointer(&self, pointer: Pointer) -> ValueRef { @@ -179,8 +183,6 @@ impl<'a> Vm<'a> { let register = &self.stack[register_index]; - log::trace!("Open R{register_index} to {register}"); - match register { Register::Value(value) => Some(value.to_ref()), Register::Pointer(pointer) => Some(self.follow_pointer(*pointer)), @@ -218,19 +220,18 @@ impl<'a> Vm<'a> { fn set_register(&mut self, to_register: u8, register: Register) { self.last_assigned_register = Some(to_register); let to_register = to_register as usize; - let stack = self.stack.as_mut_slice(); assert!( - to_register < stack.len(), + to_register < self.stack.len(), "VM Error: Register index out of bounds" ); - stack[to_register] = register; + self.stack[to_register] = register; } fn get_constant(&self, constant_index: u8) -> &ConcreteValue { let constant_index = constant_index as usize; - let constants = self.chunk.constants().as_slice(); + let constants = self.chunk.constants(); assert!( constant_index < constants.len(), @@ -242,7 +243,7 @@ impl<'a> Vm<'a> { fn get_local_register(&self, local_index: u8) -> u8 { let local_index = local_index as usize; - let locals = self.chunk.locals().as_slice(); + let locals = self.chunk.locals(); assert!( local_index < locals.len(), @@ -419,9 +420,11 @@ impl AnnotatedError for VmError { #[cfg(test)] mod tests { + use runners::{RunnerLogic, RUNNER_LOGIC_TABLE}; + use super::*; - const ALL_OPERATIONS: [(Operation, Runner); 24] = [ + const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [ (Operation::MOVE, runners::r#move), (Operation::CLOSE, runners::close), (Operation::LOAD_BOOLEAN, runners::load_boolean), @@ -451,7 +454,7 @@ mod tests { #[test] fn operations_map_to_the_correct_runner() { for (operation, expected_runner) in ALL_OPERATIONS { - let actual_runner = RUNNERS[operation.0 as usize]; + let actual_runner = RUNNER_LOGIC_TABLE[operation.0 as usize]; assert_eq!( expected_runner, actual_runner, diff --git a/dust-lang/src/vm/runners.rs b/dust-lang/src/vm/runners.rs index a2c7dff..9fb33ac 100644 --- a/dust-lang/src/vm/runners.rs +++ b/dust-lang/src/vm/runners.rs @@ -2,11 +2,34 @@ use smallvec::SmallVec; use crate::{AbstractValue, ConcreteValue, NativeFunction, Value, ValueRef}; -use super::{InstructionData, Pointer, Register, Vm}; +use super::{Instruction, InstructionData, Pointer, Register, Vm}; -pub type Runner = fn(&mut Vm, InstructionData); +#[derive(Clone, Copy, Debug)] +pub struct Runner { + logic: RunnerLogic, + data: InstructionData, +} -pub const RUNNERS: [Runner; 24] = [ +impl Runner { + pub fn new(instruction: Instruction) -> Self { + let (operation, data) = instruction.decode(); + let logic = RUNNER_LOGIC_TABLE[operation.0 as usize]; + + Self { logic, data } + } + + pub fn from_parts(logic: RunnerLogic, data: InstructionData) -> Self { + Self { logic, data } + } + + pub fn run(&self, vm: &mut Vm) { + (self.logic)(vm, self.data); + } +} + +pub type RunnerLogic = fn(&mut Vm, InstructionData); + +pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 24] = [ r#move, close, load_boolean, @@ -45,6 +68,12 @@ pub fn r#move<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { if from_register_has_value { vm.set_register(c, register); } + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -61,6 +90,12 @@ pub fn close<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { vm.stack[register_index as usize] = Register::Empty; } + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -72,8 +107,14 @@ pub fn load_boolean<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionDat vm.set_register(a, register); if c != 0 { - vm.jump_instructions(1, true); + vm.ip += 2; + } else { + vm.ip += 1; } + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -84,8 +125,14 @@ pub fn load_constant<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionDa vm.set_register(a, register); if c != 0 { - vm.jump_instructions(1, true); + vm.ip += 2; + } else { + vm.ip += 1; } + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -107,7 +154,13 @@ pub fn load_list<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) let list_value = AbstractValue::List { item_pointers }.to_value(); let register = Register::Value(list_value); - vm.set_register(a, register) + vm.set_register(a, register); + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -115,7 +168,13 @@ pub fn load_self<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) let InstructionData { a, .. } = instruction_data; let register = Register::Value(AbstractValue::FunctionSelf.to_value()); - vm.set_register(a, register) + vm.set_register(a, register); + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -124,7 +183,13 @@ pub fn get_local<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) let local_register_index = vm.get_local_register(b); let register = Register::Pointer(Pointer::Stack(local_register_index)); - vm.set_register(a, register) + vm.set_register(a, register); + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -133,7 +198,13 @@ pub fn set_local<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) let local_register_index = vm.get_local_register(c); let register = Register::Pointer(Pointer::Stack(b)); - vm.set_register(local_register_index, register) + vm.set_register(local_register_index, register); + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -159,7 +230,13 @@ pub fn add<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { }; let register = Register::Value(sum); - vm.set_register(a, register) + vm.set_register(a, register); + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -185,7 +262,13 @@ pub fn subtract<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { }; let register = Register::Value(difference); - vm.set_register(a, register) + vm.set_register(a, register); + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -211,7 +294,13 @@ pub fn multiply<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { }; let register = Register::Value(product); - vm.set_register(a, register) + vm.set_register(a, register); + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -237,7 +326,13 @@ pub fn divide<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { }; let register = Register::Value(quotient); - vm.set_register(a, register) + vm.set_register(a, register); + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -263,7 +358,13 @@ pub fn modulo<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { }; let register = Register::Value(remainder); - vm.set_register(a, register) + vm.set_register(a, register); + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -286,8 +387,14 @@ pub fn test<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { let test_value = c != 0; if boolean == test_value { - vm.jump_instructions(1, true); + vm.ip += 2; + } else { + vm.ip += 1; } + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -311,7 +418,7 @@ pub fn test_set<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { let test_value = c != 0; if boolean == test_value { - vm.jump_instructions(1, true); + vm.ip += 2; } else { let pointer = if b_is_constant { Pointer::Constant(b) @@ -321,7 +428,13 @@ pub fn test_set<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { let register = Register::Pointer(pointer); vm.set_register(a, register); + + vm.ip += 1; } + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -339,8 +452,14 @@ pub fn equal<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { let is_equal = left == right; if is_equal == d { - vm.jump_instructions(1, true); + vm.ip += 2; + } else { + vm.ip += 1; } + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -358,8 +477,14 @@ pub fn less<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { let is_less = left < right; if is_less == d { - vm.jump_instructions(1, true); + vm.ip += 2; + } else { + vm.ip += 1; } + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -377,8 +502,14 @@ pub fn less_equal<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) let is_less_or_equal = left <= right; if is_less_or_equal == d { - vm.jump_instructions(1, true); + vm.ip += 2; + } else { + vm.ip += 1; } + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -400,7 +531,13 @@ pub fn negate<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { }; let register = Register::Value(Value::Concrete(negated)); - vm.set_register(a, register) + vm.set_register(a, register); + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -418,15 +555,30 @@ pub fn not<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { }; let register = Register::Value(Value::Concrete(not)); - vm.set_register(a, register) + vm.set_register(a, register); + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] pub fn jump<'c>(vm: &mut Vm<'c>, instruction_data: InstructionData) { let InstructionData { b, c, .. } = instruction_data; + let offset = b as usize; let is_positive = c != 0; - vm.jump_instructions(b as usize, is_positive); + if is_positive { + vm.ip += offset + 1 + } else { + vm.ip -= offset + } + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -439,14 +591,13 @@ pub fn call<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { .. } = instruction_data; let function = vm.get_argument(b, b_is_constant); - let chunk = if let ValueRef::Concrete(ConcreteValue::Function(chunk)) = function { - chunk + let mut function_vm = if let ValueRef::Concrete(ConcreteValue::Function(chunk)) = function { + Vm::new(vm.source, chunk, Some(vm), None) } else if let ValueRef::Abstract(AbstractValue::FunctionSelf) = function { - vm.chunk + Vm::new(vm.source, vm.chunk, Some(vm), Some(vm.runners.clone())) } else { panic!("VM Error: Expected function") }; - let mut function_vm = Vm::new(vm.source, chunk, Some(vm)); let first_argument_index = a - c; let mut argument_index = 0; @@ -473,6 +624,12 @@ pub fn call<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { vm.set_register(a, register); } + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -508,6 +665,12 @@ pub fn call_native<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData vm.set_register(a, register); } + + vm.ip += 1; + + let next = vm.runners[vm.ip]; + + next.run(vm); } #[allow(clippy::needless_lifetimes)] @@ -515,7 +678,7 @@ pub fn r#return<'b, 'c>(vm: &'b mut Vm<'c>, instruction_data: InstructionData) { let should_return_value = instruction_data.b != 0; if !should_return_value { - return; + return vm.ip += 1; } if let Some(register_index) = &vm.last_assigned_register {