1
0

Add new benchmarks; Experiment with VM optimizations

This commit is contained in:
Jeff 2025-02-13 10:37:35 -05:00
parent 4145499e0c
commit 720f006d8c
8 changed files with 380 additions and 673 deletions

View File

@ -0,0 +1,29 @@
use std::time::Duration;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use dust_lang::run;
const SOURCE: &str = r"
let mut i = 1.7976931348623157e308
while i > 1.0 {
i /= 1.00014196662;
}
";
fn addictive_division(source: &str) {
run(source).unwrap();
}
fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("addictive_division");
group.measurement_time(Duration::from_secs(15));
group.bench_function("addictive_division", |b| {
b.iter(|| addictive_division(black_box(SOURCE)))
});
group.finish();
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@ -0,0 +1,29 @@
use std::time::Duration;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use dust_lang::run;
const SOURCE: &str = r"
let mut i = 1.0
while i < 1.7976931348623157e308 {
i *= 1.00014196662
}
";
fn addictive_multiplication(source: &str) {
run(source).unwrap();
}
fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("addictive_multiplication");
group.measurement_time(Duration::from_secs(15));
group.bench_function("addictive_multiplication", |b| {
b.iter(|| addictive_multiplication(black_box(SOURCE)))
});
group.finish();
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@ -0,0 +1,29 @@
use std::time::Duration;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use dust_lang::run;
const SOURCE: &str = r"
let mut i = 5_000_000
while i > 0 {
i -= 1
}
";
fn addictive_subtraction(source: &str) {
run(source).unwrap();
}
fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("addictive_subtraction");
group.measurement_time(Duration::from_secs(15));
group.bench_function("addictive_subtraction", |b| {
b.iter(|| addictive_subtraction(black_box(SOURCE)))
});
group.finish();
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@ -1,217 +1,20 @@
use tracing::trace; use tracing::trace;
use crate::{ use crate::{
DustString,
instruction::{InstructionFields, TypeCode}, instruction::{InstructionFields, TypeCode},
vm::{Register, Thread, call_frame::PointerCache}, vm::{call_frame::PointerCache, Register, Thread},
DustString,
}; };
pub fn add( pub fn add_integers(ip: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
_: &mut usize,
instruction: InstructionFields,
thread: &mut Thread,
pointer_cache: &mut PointerCache,
) {
let destination = instruction.a_field as usize;
let left = instruction.b_field as usize;
let left_is_constant = instruction.b_is_constant;
let left_type = instruction.b_type;
let right = instruction.c_field as usize;
let right_is_constant = instruction.c_is_constant;
let right_type = instruction.c_type;
match (left_type, right_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => add_integers(instruction, thread, pointer_cache),
(TypeCode::BYTE, TypeCode::BYTE) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_byte().unwrap()
} else {
unsafe { thread.get_constant(left).as_byte().unwrap_unchecked() }
}
} else {
thread.get_byte_register(left)
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_byte().unwrap()
} else {
unsafe { thread.get_constant(right).as_byte().unwrap_unchecked() }
}
} else {
thread.get_byte_register(right)
};
let sum = left_value + right_value;
let register = Register::Value(sum);
thread.set_byte_register(destination, register);
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_float().unwrap()
} else {
unsafe { thread.get_constant(left).as_float().unwrap_unchecked() }
}
} else {
thread.get_float_register(left)
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_float().unwrap()
} else {
unsafe { thread.get_constant(right).as_float().unwrap_unchecked() }
}
} else {
thread.get_float_register(right)
};
let sum = left_value + right_value;
let register = Register::Value(sum);
thread.set_float_register(destination, register);
}
(TypeCode::STRING, TypeCode::STRING) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_string().unwrap().clone()
} else {
unsafe {
thread
.get_constant(left)
.as_string()
.unwrap_unchecked()
.clone()
}
}
} else {
thread.get_string_register(left).clone()
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_string().unwrap().clone()
} else {
unsafe {
thread
.get_constant(right)
.as_string()
.unwrap_unchecked()
.clone()
}
}
} else {
thread.get_string_register(right).clone()
};
let concatenated = left_value + &right_value;
let register = Register::Value(concatenated);
thread.set_string_register(destination, register);
}
(TypeCode::CHARACTER, TypeCode::CHARACTER) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_character().unwrap()
} else {
unsafe { thread.get_constant(left).as_character().unwrap_unchecked() }
}
} else {
thread.get_character_register(left)
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_character().unwrap()
} else {
unsafe { thread.get_constant(right).as_character().unwrap_unchecked() }
}
} else {
thread.get_character_register(right)
};
let mut sum = DustString::new();
sum.push(*left_value);
sum.push(*right_value);
let register = Register::Value(sum);
thread.set_string_register(destination, register);
}
(TypeCode::STRING, TypeCode::CHARACTER) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_string().unwrap().clone()
} else {
unsafe {
thread
.get_constant(left)
.as_string()
.unwrap_unchecked()
.clone()
}
}
} else {
thread.get_string_register(left).clone()
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_character().unwrap()
} else {
unsafe { thread.get_constant(right).as_character().unwrap_unchecked() }
}
} else {
thread.get_character_register(right)
};
let mut sum = left_value.clone();
sum.push(*right_value);
let register = Register::Value(sum);
thread.set_string_register(destination, register);
}
(TypeCode::CHARACTER, TypeCode::STRING) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_character().unwrap()
} else {
unsafe { thread.get_constant(left).as_character().unwrap_unchecked() }
}
} else {
thread.get_character_register(left)
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_string().unwrap()
} else {
unsafe { thread.get_constant(right).as_string().unwrap_unchecked() }
}
} else {
thread.get_string_register(right)
};
let mut sum = right_value.clone();
sum.insert(0, *left_value);
let register = Register::Value(sum);
thread.set_string_register(destination, register);
}
_ => unreachable!(),
}
}
pub fn add_integers(
instruction: InstructionFields,
thread: &mut Thread,
pointer_cache: &mut PointerCache,
) {
let destination = instruction.a_field as usize;
let left = instruction.b_field as usize;
let left_is_constant = instruction.b_is_constant;
let right = instruction.c_field as usize;
let right_is_constant = instruction.c_is_constant;
if pointer_cache.integer_mut.is_null() {
trace!("ADD: Run and cache pointers"); trace!("ADD: Run and cache pointers");
let destination = instruction.a_field as usize;
let left = instruction.b_field as usize;
let left_is_constant = instruction.b_is_constant;
let right = instruction.c_field as usize;
let right_is_constant = instruction.c_is_constant;
let left_value = if left_is_constant { let left_value = if left_is_constant {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
thread.get_constant(left).as_integer().unwrap() thread.get_constant(left).as_integer().unwrap()
@ -232,28 +35,5 @@ pub fn add_integers(
}; };
let sum = left_value.saturating_add(*right_value); let sum = left_value.saturating_add(*right_value);
pointer_cache.integer_left = left_value;
pointer_cache.integer_right = right_value;
pointer_cache.integer_mut = thread.get_integer_register_mut_allow_empty(destination);
thread.set_integer_register(destination, Register::Value(sum)); thread.set_integer_register(destination, Register::Value(sum));
} else {
trace!("ADD: Use cached pointers");
add_integer_pointers(
pointer_cache.integer_mut,
pointer_cache.integer_left,
pointer_cache.integer_right,
);
};
}
pub fn add_integer_pointers(destination: *mut i64, left: *const i64, right: *const i64) {
assert!(destination.is_aligned());
assert!(left.is_aligned());
assert!(right.is_aligned());
unsafe {
*destination = (*left).saturating_add(*right);
}
} }

View File

@ -2,15 +2,10 @@ use tracing::trace;
use crate::{ use crate::{
instruction::InstructionFields, instruction::InstructionFields,
vm::{Thread, call_frame::PointerCache}, vm::{call_frame::PointerCache, Thread},
}; };
pub fn jump( pub fn jump(ip: &mut usize, instruction: &InstructionFields, _: &mut Thread) {
ip: &mut usize,
instruction: InstructionFields,
_: &mut Thread,
_pointer_cache: &mut PointerCache,
) {
let offset = instruction.b_field as usize; let offset = instruction.b_field as usize;
let is_positive = instruction.c_field != 0; let is_positive = instruction.c_field != 0;

View File

@ -2,182 +2,17 @@ use tracing::trace;
use crate::{ use crate::{
instruction::{InstructionFields, TypeCode}, instruction::{InstructionFields, TypeCode},
vm::{Thread, call_frame::PointerCache}, vm::{call_frame::PointerCache, Thread},
}; };
pub fn less( pub fn less_integers(ip: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
ip: &mut usize, trace!("LESS unoptimized");
instruction: InstructionFields,
thread: &mut Thread,
pointer_cache: &mut PointerCache,
) {
let comparator = instruction.d_field;
let left = instruction.b_field as usize;
let left_type = instruction.b_type;
let left_is_constant = instruction.b_is_constant;
let right = instruction.c_field as usize;
let right_type = instruction.c_type;
let right_is_constant = instruction.c_is_constant;
match (left_type, right_type) {
(TypeCode::BOOLEAN, TypeCode::BOOLEAN) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_boolean().unwrap()
} else {
unsafe { thread.get_constant(left).as_boolean().unwrap_unchecked() }
}
} else {
thread.get_boolean_register(left)
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_boolean().unwrap()
} else {
unsafe { thread.get_constant(right).as_boolean().unwrap_unchecked() }
}
} else {
thread.get_boolean_register(right)
};
let result = left_value < right_value;
if result == comparator {
*ip += 1;
}
}
(TypeCode::BYTE, TypeCode::BYTE) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_byte().unwrap()
} else {
unsafe { thread.get_constant(left).as_byte().unwrap_unchecked() }
}
} else {
thread.get_byte_register(left)
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_byte().unwrap()
} else {
unsafe { thread.get_constant(right).as_byte().unwrap_unchecked() }
}
} else {
thread.get_byte_register(right)
};
let result = left_value < right_value;
if result == comparator {
*ip += 1;
}
}
(TypeCode::CHARACTER, TypeCode::CHARACTER) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_character().unwrap()
} else {
unsafe { thread.get_constant(left).as_character().unwrap_unchecked() }
}
} else {
thread.get_character_register(left)
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_character().unwrap()
} else {
unsafe { thread.get_constant(right).as_character().unwrap_unchecked() }
}
} else {
thread.get_character_register(right)
};
let result = left_value < right_value;
if result == comparator {
*ip += 1;
}
}
(TypeCode::FLOAT, TypeCode::FLOAT) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_float().unwrap()
} else {
unsafe { thread.get_constant(left).as_float().unwrap_unchecked() }
}
} else {
thread.get_float_register(left)
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_float().unwrap()
} else {
unsafe { thread.get_constant(right).as_float().unwrap_unchecked() }
}
} else {
thread.get_float_register(right)
};
let result = left_value < right_value;
if result == comparator {
*ip += 1;
}
}
(TypeCode::INTEGER, TypeCode::INTEGER) => {
less_integers(ip, instruction, thread, pointer_cache)
}
(TypeCode::STRING, TypeCode::STRING) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_string().unwrap().clone()
} else {
unsafe {
thread
.get_constant(left)
.as_string()
.unwrap_unchecked()
.clone()
}
}
} else {
thread.get_string_register(left).clone()
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_string().unwrap().clone()
} else {
unsafe {
thread
.get_constant(right)
.as_string()
.unwrap_unchecked()
.clone()
}
}
} else {
thread.get_string_register(right).clone()
};
let result = left_value < right_value;
if result == comparator {
*ip += 1;
}
}
_ => unreachable!(),
}
}
pub fn less_integers(
ip: &mut usize,
instruction: InstructionFields,
thread: &mut Thread,
pointer_cache: &mut PointerCache,
) {
if pointer_cache.integer_left.is_null() {
trace!("LESS: Run and cache pointers");
let left = instruction.b_field as usize; let left = instruction.b_field as usize;
let left_is_constant = instruction.b_is_constant; let left_is_constant = instruction.b_is_constant;
let right = instruction.c_field as usize; let right = instruction.c_field as usize;
let right_is_constant = instruction.c_is_constant; let right_is_constant = instruction.c_is_constant;
let left_pointer = if left_is_constant { let left_value = if left_is_constant {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
thread.get_constant(left).as_integer().unwrap() thread.get_constant(left).as_integer().unwrap()
} else { } else {
@ -185,8 +20,8 @@ pub fn less_integers(
} }
} else { } else {
thread.get_integer_register(left) thread.get_integer_register(left)
} as *const i64; };
let right_pointer = if right_is_constant { let right_value = if right_is_constant {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
thread.get_constant(right).as_integer().unwrap() thread.get_constant(right).as_integer().unwrap()
} else { } else {
@ -194,39 +29,11 @@ pub fn less_integers(
} }
} else { } else {
thread.get_integer_register(right) thread.get_integer_register(right)
} as *const i64;
pointer_cache.integer_left = left_pointer;
pointer_cache.integer_right = right_pointer;
less_integer_pointers(ip, left_pointer, right_pointer, instruction.d_field);
} else {
trace!("LESS: Use cached pointers");
less_integer_pointers(
ip,
pointer_cache.integer_left,
pointer_cache.integer_right,
instruction.d_field,
);
}; };
} let is_less_than = left_value < right_value;
let comparator = instruction.d_field;
pub fn less_integer_pointers(
ip: *mut usize,
left: *const i64,
right: *const i64,
comparator: bool,
) {
assert!(ip.is_aligned());
assert!(left.is_aligned());
assert!(right.is_aligned());
unsafe {
let is_less_than = *left < *right;
if is_less_than == comparator { if is_less_than == comparator {
*ip += 1; *ip += 1;
} }
}
} }

View File

@ -2,36 +2,35 @@ mod add;
mod jump; mod jump;
mod less; mod less;
use add::{add, add_integer_pointers}; use add::add_integers;
use jump::jump; use jump::jump;
use less::{less, less_integer_pointers}; use less::less_integers;
use tracing::trace; use tracing::trace;
use std::fmt::{self, Display, Formatter}; use std::{
fmt::{self, Display, Formatter},
use crate::{ ptr,
AbstractList, ConcreteValue, Operation, Value,
instruction::{InstructionFields, TypeCode},
vm::call_frame::PointerCache,
}; };
use super::{Pointer, Register, thread::Thread}; use crate::{
instruction::{InstructionFields, TypeCode},
vm::call_frame::PointerCache,
AbstractList, ConcreteValue, Operation, Value,
};
use super::{thread::Thread, Pointer, Register};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ActionSequence { pub struct ActionSequence {
pub actions: Vec<Action>, pub actions: Vec<(Action, InstructionFields)>,
} }
impl ActionSequence { impl ActionSequence {
#[allow(clippy::while_let_on_iterator)] #[allow(clippy::while_let_on_iterator)]
pub fn new<T>(instructions: T) -> Self pub fn new(instructions: Vec<InstructionFields>) -> Self {
where
T: ExactSizeIterator<Item = InstructionFields> + DoubleEndedIterator + Clone,
{
let mut actions = Vec::with_capacity(instructions.len()); let mut actions = Vec::with_capacity(instructions.len());
let mut instructions_reversed = instructions.rev(); let mut instructions_reversed = instructions.into_iter().rev();
let mut in_loop = false;
while let Some(instruction) = instructions_reversed.next() { while let Some(instruction) = instructions_reversed.next() {
if instruction.operation == Operation::JUMP { if instruction.operation == Operation::JUMP {
@ -39,49 +38,33 @@ impl ActionSequence {
let is_positive = instruction.c_field != 0; let is_positive = instruction.c_field != 0;
if !is_positive { if !is_positive {
let mut loop_instructions = Vec::new(); let mut loop_actions = Vec::with_capacity(backward_offset + 1);
let mut previous = instruction; let jump_action = Action::optimized(&instruction);
in_loop = true; loop_actions.push((jump_action, instruction));
loop_instructions.push(instruction); for _ in 0..backward_offset {
let instruction = instructions_reversed.next().unwrap();
let action = Action::optimized(&instruction);
while let Some(instruction) = instructions_reversed.next() { loop_actions.push((action, instruction));
loop_instructions.push(instruction);
if instruction.operation == Operation::LESS
&& previous.operation == Operation::JUMP
{
let forward_offset = previous.b_field as usize;
let is_positive = previous.c_field != 0;
if is_positive && forward_offset == backward_offset - 1 {
in_loop = false;
break;
}
} }
previous = instruction; loop_actions.reverse();
}
loop_instructions.reverse(); let r#loop = Action::r#loop(ActionSequence {
actions: loop_actions,
});
let loop_action = Action::optimized_loop(loop_instructions); actions.push((r#loop, instruction));
actions.push(loop_action);
continue; continue;
} }
} }
let action = if in_loop { let action = Action::unoptimized(instruction);
Action::optimized_inline(instruction)
} else {
Action::unoptimized(instruction)
};
actions.push(action); actions.push((action, instruction));
} }
actions.reverse(); actions.reverse();
@ -93,54 +76,101 @@ impl ActionSequence {
self.actions.len() self.actions.len()
} }
pub fn run(&self, thread: &mut Thread) { pub fn run(&mut self, thread: &mut Thread) {
let mut pointer_caches = vec![PointerCache::new(); self.actions.len()];
let mut local_ip = 0; let mut local_ip = 0;
while local_ip < self.actions.len() { while local_ip < self.actions.len() {
assert!(local_ip < self.actions.len()); let (action, instruction) = &mut self.actions[local_ip];
assert!(local_ip < pointer_caches.len());
let action = &self.actions[local_ip];
let cache = &mut pointer_caches[local_ip];
local_ip += 1; local_ip += 1;
trace!("Run {action}"); trace!("Run {action}");
if let Some(loop_actions) = &action.loop_actions { match action {
loop_actions.run(thread); Action::Unoptimized { logic, instruction } => {
} else if action.optimize_inline { logic(&mut local_ip, &*instruction, thread);
match action.instruction.operation { }
Operation::ADD => { Action::Loop { actions } => {
if cache.integer_mut.is_null() { actions.run(thread);
add(&mut local_ip, action.instruction, thread, cache); }
Action::OptimizedAddIntegers {
destination_pointer,
left_pointer,
right_pointer,
} => {
let left = if left_pointer.is_null() || !left_pointer.is_aligned() {
let left_index = instruction.b_field as usize;
let left_is_constant = instruction.b_is_constant;
let left_value = thread.get_integer(left_index, left_is_constant);
*left_pointer = left_value;
left_value
} else { } else {
add_integer_pointers( unsafe { &ptr::read(*left_pointer) }
cache.integer_mut, };
cache.integer_left, let right = if right_pointer.is_null() || !right_pointer.is_aligned() {
cache.integer_right, let right_index = instruction.c_field as usize;
); let right_is_constant = instruction.c_is_constant;
} let right_value = thread.get_integer(right_index, right_is_constant);
}
Operation::LESS => { *right_pointer = right_value;
if cache.integer_left.is_null() {
less(&mut local_ip, action.instruction, thread, cache); right_value
} else { } else {
less_integer_pointers( unsafe { &ptr::read(*right_pointer) }
&mut local_ip, };
cache.integer_left, let sum = left.saturating_add(*right);
cache.integer_right,
action.instruction.d_field, if destination_pointer.is_null() || !destination_pointer.is_aligned() {
); let destination = instruction.a_field as usize;
} let register = Register::Value(sum);
}
Operation::JUMP => jump(&mut local_ip, action.instruction, thread, cache), thread.set_integer_register(destination, register);
_ => {
(action.logic)(&mut local_ip, action.instruction, thread, cache);
}
}
} else { } else {
(action.logic)(&mut local_ip, action.instruction, thread, cache); unsafe {
**destination_pointer = sum;
}
}
}
Action::OptimizedLessIntegers {
left_pointer,
right_pointer,
} => {
let left = if left_pointer.is_null() || !left_pointer.is_aligned() {
let left_index = instruction.b_field as usize;
let left_is_constant = instruction.b_is_constant;
let left_value = thread.get_integer(left_index, left_is_constant);
*left_pointer = left_value;
left_value
} else {
unsafe { &ptr::read(*left_pointer) }
};
let right = if right_pointer.is_null() || !right_pointer.is_aligned() {
let right_index = instruction.c_field as usize;
let right_is_constant = instruction.c_is_constant;
let right_value = thread.get_integer(right_index, right_is_constant);
*right_pointer = right_value;
right_value
} else {
unsafe { &ptr::read(*right_pointer) }
};
let is_less_than = left < right;
let comparator = instruction.d_field;
if is_less_than == comparator {
local_ip += 1;
}
}
Action::OptimizedJumpForward { offset } => {
local_ip += *offset;
}
Action::OptimizedJumpBackward { offset } => {
local_ip -= *offset + 1;
}
} }
} }
} }
@ -150,12 +180,12 @@ impl Display for ActionSequence {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "[")?; write!(f, "[")?;
for (index, action) in self.actions.iter().enumerate() { for (index, (action, _)) in self.actions.iter().enumerate() {
write!(f, "{action}")?; if index > 0 {
if index < self.actions.len() - 1 {
write!(f, ", ")?; write!(f, ", ")?;
} }
write!(f, "{action}")?;
} }
write!(f, "]") write!(f, "]")
@ -163,43 +193,33 @@ impl Display for ActionSequence {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Action { pub enum Action {
pub logic: ActionLogic, Unoptimized {
pub instruction: InstructionFields, logic: ActionLogic,
pub optimize_inline: bool, instruction: InstructionFields,
pub loop_actions: Option<ActionSequence>, },
Loop {
actions: ActionSequence,
},
OptimizedAddIntegers {
destination_pointer: *mut i64,
left_pointer: *const i64,
right_pointer: *const i64,
},
OptimizedLessIntegers {
left_pointer: *const i64,
right_pointer: *const i64,
},
OptimizedJumpForward {
offset: usize,
},
OptimizedJumpBackward {
offset: usize,
},
} }
impl Action { impl Action {
fn optimized_loop(instructions: Vec<InstructionFields>) -> Self { pub fn unoptimized(instruction: InstructionFields) -> Self {
let mut loop_actions = Vec::with_capacity(instructions.len());
for instruction in instructions {
let action = Action::optimized_inline(instruction);
loop_actions.push(action);
}
Action {
logic: no_op,
instruction: InstructionFields::default(),
optimize_inline: false,
loop_actions: Some(ActionSequence {
actions: loop_actions,
}),
}
}
fn optimized_inline(instruction: InstructionFields) -> Self {
Action {
logic: no_op,
optimize_inline: true,
instruction,
loop_actions: None,
}
}
fn unoptimized(instruction: InstructionFields) -> Self {
let logic = match instruction.operation { let logic = match instruction.operation {
Operation::POINT => point, Operation::POINT => point,
Operation::CLOSE => close, Operation::CLOSE => close,
@ -208,7 +228,10 @@ impl Action {
Operation::LOAD_LIST => load_list, Operation::LOAD_LIST => load_list,
Operation::LOAD_FUNCTION => load_function, Operation::LOAD_FUNCTION => load_function,
Operation::LOAD_SELF => load_self, Operation::LOAD_SELF => load_self,
Operation::ADD => add, Operation::ADD => match (instruction.b_type, instruction.c_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => add_integers,
_ => todo!(),
},
Operation::SUBTRACT => subtract, Operation::SUBTRACT => subtract,
Operation::MULTIPLY => multiply, Operation::MULTIPLY => multiply,
Operation::DIVIDE => divide, Operation::DIVIDE => divide,
@ -216,7 +239,10 @@ impl Action {
Operation::NEGATE => negate, Operation::NEGATE => negate,
Operation::NOT => not, Operation::NOT => not,
Operation::EQUAL => equal, Operation::EQUAL => equal,
Operation::LESS => less, Operation::LESS => match (instruction.b_type, instruction.c_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => less_integers,
_ => todo!(),
},
Operation::LESS_EQUAL => less_equal, Operation::LESS_EQUAL => less_equal,
Operation::TEST => test, Operation::TEST => test,
Operation::TEST_SET => test_set, Operation::TEST_SET => test_set,
@ -227,30 +253,75 @@ impl Action {
_ => todo!(), _ => todo!(),
}; };
Action { Action::Unoptimized { logic, instruction }
logic,
instruction,
optimize_inline: false,
loop_actions: None,
} }
pub fn optimized(instruction: &InstructionFields) -> Self {
match instruction.operation {
Operation::ADD => match (instruction.b_type, instruction.c_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => Action::OptimizedAddIntegers {
destination_pointer: std::ptr::null_mut(),
left_pointer: std::ptr::null(),
right_pointer: std::ptr::null(),
},
_ => todo!(),
},
Operation::LESS => match (instruction.b_type, instruction.c_type) {
(TypeCode::INTEGER, TypeCode::INTEGER) => Action::OptimizedLessIntegers {
left_pointer: std::ptr::null(),
right_pointer: std::ptr::null(),
},
_ => todo!(),
},
Operation::JUMP => {
let offset = instruction.b_field as usize;
let is_positive = instruction.c_field != 0;
if is_positive {
Action::OptimizedJumpForward { offset }
} else {
Action::OptimizedJumpBackward { offset }
}
}
_ => todo!(),
}
}
pub fn r#loop(actions: ActionSequence) -> Self {
Action::Loop { actions }
} }
} }
impl Display for Action { impl Display for Action {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if let Some(action_sequence) = &self.loop_actions { match self {
write!(f, "LOOP: {action_sequence}") Action::Unoptimized { instruction, .. } => {
} else { write!(f, "{}", instruction.operation)
write!(f, "{}", self.instruction.operation) }
Action::Loop { actions } => {
write!(f, "LOOP: {actions}")
}
Action::OptimizedAddIntegers { .. } => {
write!(f, "ADD integers optimized")
}
Action::OptimizedLessIntegers { .. } => {
write!(f, "LESS integers optimized")
}
Action::OptimizedJumpForward { offset } => {
write!(f, "JUMP +{offset}")
}
Action::OptimizedJumpBackward { offset } => {
write!(f, "JUMP -{offset}")
}
} }
} }
} }
pub type ActionLogic = fn(&mut usize, InstructionFields, &mut Thread, &mut PointerCache); pub type ActionLogic = fn(&mut usize, &InstructionFields, &mut Thread);
fn no_op(_: &mut usize, _: InstructionFields, _: &mut Thread, _: &mut PointerCache) {} fn no_op(_: &mut usize, _: &InstructionFields, _: &mut Thread) {}
fn point(_: &mut usize, instruction: InstructionFields, thread: &mut Thread, _: &mut PointerCache) { fn point(_: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
let destination = instruction.a_field as usize; let destination = instruction.a_field as usize;
let to = instruction.b_field as usize; let to = instruction.b_field as usize;
let to_is_constant = instruction.b_is_constant; let to_is_constant = instruction.b_is_constant;
@ -357,7 +428,7 @@ fn point(_: &mut usize, instruction: InstructionFields, thread: &mut Thread, _:
} }
} }
fn close(_: &mut usize, instruction: InstructionFields, thread: &mut Thread, _: &mut PointerCache) { fn close(_: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
let from = instruction.b_field as usize; let from = instruction.b_field as usize;
let to = instruction.c_field as usize; let to = instruction.c_field as usize;
let r#type = instruction.b_type; let r#type = instruction.b_type;
@ -402,12 +473,7 @@ fn close(_: &mut usize, instruction: InstructionFields, thread: &mut Thread, _:
} }
} }
fn load_encoded( fn load_encoded(ip: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
ip: &mut usize,
instruction: InstructionFields,
thread: &mut Thread,
_: &mut PointerCache,
) {
let destination = instruction.a_field; let destination = instruction.a_field;
let value = instruction.b_field; let value = instruction.b_field;
let value_type = instruction.b_type; let value_type = instruction.b_type;
@ -432,12 +498,7 @@ fn load_encoded(
} }
} }
fn load_constant( fn load_constant(ip: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
ip: &mut usize,
instruction: InstructionFields,
thread: &mut Thread,
_: &mut PointerCache,
) {
let destination = instruction.a_field as usize; let destination = instruction.a_field as usize;
let constant_index = instruction.b_field as usize; let constant_index = instruction.b_field as usize;
let constant_type = instruction.b_type; let constant_type = instruction.b_type;
@ -475,12 +536,7 @@ fn load_constant(
} }
} }
fn load_list( fn load_list(ip: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
ip: &mut usize,
instruction: InstructionFields,
thread: &mut Thread,
_: &mut PointerCache,
) {
let destination = instruction.a_field; let destination = instruction.a_field;
let start_register = instruction.b_field; let start_register = instruction.b_field;
let item_type = instruction.b_type; let item_type = instruction.b_type;
@ -576,20 +632,15 @@ fn load_list(
} }
} }
fn load_function(_: &mut usize, _: InstructionFields, _: &mut Thread, _: &mut PointerCache) { fn load_function(_: &mut usize, _: &InstructionFields, _: &mut Thread) {
todo!() todo!()
} }
fn load_self(_: &mut usize, _: InstructionFields, _: &mut Thread, _: &mut PointerCache) { fn load_self(_: &mut usize, _: &InstructionFields, _: &mut Thread) {
todo!() todo!()
} }
fn subtract( fn subtract(_: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
_: &mut usize,
instruction: InstructionFields,
thread: &mut Thread,
_: &mut PointerCache,
) {
let destination = instruction.a_field as usize; let destination = instruction.a_field as usize;
let left = instruction.b_field as usize; let left = instruction.b_field as usize;
let left_type = instruction.b_type; let left_type = instruction.b_type;
@ -675,12 +726,7 @@ fn subtract(
} }
} }
fn multiply( fn multiply(_: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
_: &mut usize,
instruction: InstructionFields,
thread: &mut Thread,
_: &mut PointerCache,
) {
let destination = instruction.a_field as usize; let destination = instruction.a_field as usize;
let left = instruction.b_field as usize; let left = instruction.b_field as usize;
let left_type = instruction.b_type; let left_type = instruction.b_type;
@ -766,12 +812,7 @@ fn multiply(
} }
} }
fn divide( fn divide(_: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
_: &mut usize,
instruction: InstructionFields,
thread: &mut Thread,
_: &mut PointerCache,
) {
let destination = instruction.a_field as usize; let destination = instruction.a_field as usize;
let left = instruction.b_field as usize; let left = instruction.b_field as usize;
let left_type = instruction.b_type; let left_type = instruction.b_type;
@ -857,12 +898,7 @@ fn divide(
} }
} }
fn modulo( fn modulo(_: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
_: &mut usize,
instruction: InstructionFields,
thread: &mut Thread,
_: &mut PointerCache,
) {
let destination = instruction.a_field as usize; let destination = instruction.a_field as usize;
let left = instruction.b_field as usize; let left = instruction.b_field as usize;
let left_type = instruction.b_type; let left_type = instruction.b_type;
@ -948,7 +984,7 @@ fn modulo(
} }
} }
fn test(ip: &mut usize, instruction: InstructionFields, thread: &mut Thread, _: &mut PointerCache) { fn test(ip: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
let operand_register = instruction.b_field as usize; let operand_register = instruction.b_field as usize;
let test_value = instruction.c_field != 0; let test_value = instruction.c_field != 0;
let operand_boolean = thread.get_boolean_register(operand_register); let operand_boolean = thread.get_boolean_register(operand_register);
@ -958,16 +994,11 @@ fn test(ip: &mut usize, instruction: InstructionFields, thread: &mut Thread, _:
} }
} }
fn test_set(_: &mut usize, _: InstructionFields, _: &mut Thread, _: &mut PointerCache) { fn test_set(_: &mut usize, _: &InstructionFields, _: &mut Thread) {
todo!() todo!()
} }
fn equal( fn equal(ip: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
ip: &mut usize,
instruction: InstructionFields,
thread: &mut Thread,
_: &mut PointerCache,
) {
let comparator = instruction.d_field; let comparator = instruction.d_field;
let left = instruction.b_field as usize; let left = instruction.b_field as usize;
let left_type = instruction.b_type; let left_type = instruction.b_type;
@ -1143,12 +1174,7 @@ fn equal(
} }
} }
fn less_equal( fn less_equal(ip: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
ip: &mut usize,
instruction: InstructionFields,
thread: &mut Thread,
_: &mut PointerCache,
) {
let comparator = instruction.d_field; let comparator = instruction.d_field;
let left = instruction.b_field as usize; let left = instruction.b_field as usize;
let left_type = instruction.b_type; let left_type = instruction.b_type;
@ -1324,28 +1350,23 @@ fn less_equal(
} }
} }
fn negate(_: &mut usize, _: InstructionFields, _: &mut Thread, _: &mut PointerCache) { fn negate(_: &mut usize, _: &InstructionFields, _: &mut Thread) {
todo!() todo!()
} }
fn not(_: &mut usize, _: InstructionFields, _: &mut Thread, _: &mut PointerCache) { fn not(_: &mut usize, _: &InstructionFields, _: &mut Thread) {
todo!() todo!()
} }
fn call(_: &mut usize, _: InstructionFields, _: &mut Thread, _: &mut PointerCache) { fn call(_: &mut usize, _: &InstructionFields, _: &mut Thread) {
todo!() todo!()
} }
fn call_native(_: &mut usize, _: InstructionFields, _: &mut Thread, _: &mut PointerCache) { fn call_native(_: &mut usize, _: &InstructionFields, _: &mut Thread) {
todo!() todo!()
} }
fn r#return( fn r#return(_: &mut usize, instruction: &InstructionFields, thread: &mut Thread) {
_: &mut usize,
instruction: InstructionFields,
thread: &mut Thread,
_: &mut PointerCache,
) {
let should_return_value = instruction.b_field != 0; let should_return_value = instruction.b_field != 0;
let return_register = instruction.c_field as usize; let return_register = instruction.c_field as usize;
let return_type = instruction.b_type; let return_type = instruction.b_type;

View File

@ -3,9 +3,9 @@ use std::{rc::Rc, thread::JoinHandle};
use tracing::{info, trace}; use tracing::{info, trace};
use crate::{ use crate::{
AbstractList, Chunk, ConcreteValue, DustString, Span, Value,
instruction::InstructionFields, instruction::InstructionFields,
vm::{CallFrame, action::ActionSequence}, vm::{action::ActionSequence, Action, CallFrame},
AbstractList, Chunk, ConcreteValue, DustString, Span, Value,
}; };
use super::call_frame::{Pointer, Register}; use super::call_frame::{Pointer, Register};
@ -41,8 +41,13 @@ impl Thread {
.unwrap_or_else(|| DustString::from("anonymous")) .unwrap_or_else(|| DustString::from("anonymous"))
); );
let actions = let mut actions = ActionSequence::new(
ActionSequence::new(self.chunk.instructions.iter().map(InstructionFields::from)); self.chunk
.instructions
.iter()
.map(InstructionFields::from)
.collect(),
);
trace!("Thread actions: {}", actions); trace!("Thread actions: {}", actions);
@ -532,6 +537,18 @@ impl Thread {
}; };
} }
pub fn get_integer(&self, index: usize, is_constant: bool) -> &i64 {
if is_constant {
if cfg!(debug_assertions) {
self.get_constant(index).as_integer().unwrap()
} else {
unsafe { self.get_constant(index).as_integer().unwrap_unchecked() }
}
} else {
self.get_integer_register(index)
}
}
pub fn get_integer_register(&self, register_index: usize) -> &i64 { pub fn get_integer_register(&self, register_index: usize) -> &i64 {
let register = if cfg!(debug_assertions) { let register = if cfg!(debug_assertions) {
self.call_stack self.call_stack