Optimize VM
This commit is contained in:
parent
9ae923febd
commit
cd4fa6bef5
@ -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();
|
||||
|
||||
|
@ -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)) => {
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user