1
0

Optimize VM

This commit is contained in:
Jeff 2024-12-14 04:54:45 -05:00
parent 9ae923febd
commit cd4fa6bef5
4 changed files with 225 additions and 60 deletions

View File

@ -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();

View File

@ -153,7 +153,6 @@ impl ValueRef<'_> {
}
}
#[inline(always)]
pub fn less(&self, other: ValueRef) -> Result<Value, ValueError> {
match (self, other) {
(ValueRef::Concrete(left), ValueRef::Concrete(right)) => {

View File

@ -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<Option<ConcreteValue>, 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<Option<ConcreteValue>, DustError> {
pub struct Vm<'a> {
stack: Vec<Register>,
runners: Vec<Runner>,
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<Vec<Runner>>,
) -> 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<ConcreteValue> {
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::<Vec<(Runner, InstructionData)>>();
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,

View File

@ -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 {