1
0

Experiment with optimizations and benches

This commit is contained in:
Jeff 2024-12-11 06:49:43 -05:00
parent 85274bfd8d
commit 20f451fe6c
38 changed files with 437 additions and 441 deletions

10
Cargo.lock generated
View File

@ -322,7 +322,6 @@ dependencies = [
"rand",
"serde",
"serde_json",
"slab",
"smallvec",
"smartstring",
]
@ -727,15 +726,6 @@ dependencies = [
"serde",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.13.2"

View File

@ -23,7 +23,6 @@ smartstring = { version = "1.0.1", features = [
"serde",
], default-features = false }
bitflags = { version = "2.6.0", features = ["serde"] }
slab = "0.4.9"
[dev-dependencies]
criterion = { version = "0.3.4", features = ["html_reports"] }
@ -32,6 +31,10 @@ criterion = { version = "0.3.4", features = ["html_reports"] }
name = "addictive_addition"
harness = false
[[bench]]
name = "fibonacci"
harness = false
[[test]]
name = "logic_and"
path = "tests/logic/and.rs"

View File

@ -3,7 +3,7 @@ use std::time::Duration;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use dust_lang::run;
const SOURCE: &str = "
const SOURCE: &str = r"
let mut i = 0
while i < 5_000_000 {

View File

@ -0,0 +1,32 @@
use std::time::Duration;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use dust_lang::run;
const SOURCE: &str = r"
fn fib (n: int) -> int {
if n <= 0 { return 0 }
if n == 1 { return 1 }
fib(n - 1) + fib(n - 2)
}
fib(25)
";
fn addictive_addition(source: &str) {
let _ = run(source).unwrap();
}
fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("fibonacci");
group.measurement_time(Duration::from_secs(15));
group.bench_function("fibonacci", |b| {
b.iter(|| addictive_addition(black_box(SOURCE)))
});
group.finish();
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@ -407,10 +407,10 @@ impl<'a, W: Write> Disassembler<'a, W> {
let info_line = format!(
"{} instructions, {} constants, {} locals, returns {}",
self.chunk.len(),
self.chunk.constants().len(),
self.chunk.locals().len(),
self.chunk.r#type().return_type
self.chunk.instructions.len(),
self.chunk.constants.len(),
self.chunk.locals.len(),
self.chunk.r#type.return_type
);
self.write_centered_with_border_dim(&info_line)?;

View File

@ -30,22 +30,29 @@ pub struct Chunk {
instructions: SmallVec<[(Instruction, Span); 32]>,
constants: SmallVec<[ConcreteValue; 16]>,
locals: SmallVec<[Local; 8]>,
stack_size: usize,
}
impl Chunk {
pub fn new(name: Option<DustString>) -> Self {
pub fn new(
name: Option<DustString>,
r#type: FunctionType,
instructions: SmallVec<[(Instruction, Span); 32]>,
constants: SmallVec<[ConcreteValue; 16]>,
locals: SmallVec<[Local; 8]>,
stack_size: usize,
) -> Self {
Self {
name,
instructions: SmallVec::new(),
constants: SmallVec::new(),
locals: SmallVec::new(),
r#type: FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None,
},
r#type,
instructions,
constants,
locals,
stack_size,
}
}
pub fn with_data(
name: Option<DustString>,
r#type: FunctionType,
@ -59,6 +66,7 @@ impl Chunk {
instructions: instructions.into(),
constants: constants.into(),
locals: locals.into(),
stack_size: 0,
}
}
@ -70,10 +78,6 @@ impl Chunk {
&self.r#type
}
pub fn len(&self) -> usize {
self.instructions.len()
}
pub fn is_empty(&self) -> bool {
self.instructions.is_empty()
}
@ -91,21 +95,10 @@ impl Chunk {
}
pub fn stack_size(&self) -> usize {
self.instructions()
.iter()
.rev()
.filter_map(|(instruction, _)| {
if instruction.yields_value() {
Some(instruction.a as usize + 1)
} else {
None
}
})
.max()
.unwrap_or(0)
self.stack_size
}
pub fn disassembler<'a, W: Write>(&'a self, writer: &'a mut W) -> Disassembler<W> {
pub fn disassembler<'a, W: Write>(&'a self, writer: &'a mut W) -> Disassembler<'a, W> {
Disassembler::new(self, writer)
}
}

View File

@ -60,6 +60,7 @@ pub struct Compiler<'src> {
instructions: SmallVec<[(Instruction, Type, Span); 32]>,
constants: SmallVec<[ConcreteValue; 16]>,
locals: SmallVec<[(Local, Type); 8]>,
stack_size: usize,
lexer: Lexer<'src>,
@ -89,6 +90,7 @@ impl<'src> Compiler<'src> {
instructions: SmallVec::new(),
constants: SmallVec::new(),
locals: SmallVec::new(),
stack_size: 0,
lexer,
current_token,
current_position,
@ -124,7 +126,14 @@ impl<'src> Compiler<'src> {
.map(|(local, _)| local)
.collect::<SmallVec<[Local; 8]>>();
Chunk::with_data(self.self_name, r#type, instructions, self.constants, locals)
Chunk::new(
self.self_name,
r#type,
instructions,
self.constants,
locals,
self.stack_size,
)
}
pub fn compile(&mut self) -> Result<(), CompileError> {
@ -151,7 +160,7 @@ impl<'src> Compiler<'src> {
.rev()
.find_map(|(instruction, _, _)| {
if instruction.yields_value() {
Some(instruction.a + 1)
Some(instruction.a_field() + 1)
} else {
None
}
@ -381,6 +390,12 @@ impl<'src> Compiler<'src> {
position.to_string()
);
if instruction.yields_value() {
let destination = instruction.a_field() as usize;
self.stack_size = (destination + 1).max(self.stack_size);
}
self.instructions.push((instruction, r#type, position));
}
@ -597,9 +612,9 @@ impl<'src> Compiler<'src> {
instruction: &Instruction,
) -> Result<(Argument, bool), CompileError> {
let (argument, push_back) = match instruction.operation() {
Operation::LoadConstant => (Argument::Constant(instruction.b), false),
Operation::LoadConstant => (Argument::Constant(instruction.b_field()), false),
Operation::GetLocal => {
let local_index = instruction.b;
let local_index = instruction.b_field();
let (local, _) = self.get_local(local_index)?;
(Argument::Register(local.register_index), false)
@ -617,12 +632,12 @@ impl<'src> Compiler<'src> {
| Operation::LessEqual
| Operation::Negate
| Operation::Not
| Operation::Call => (Argument::Register(instruction.a), true),
| Operation::Call => (Argument::Register(instruction.a_field()), true),
Operation::CallNative => {
let function = NativeFunction::from(instruction.b);
let function = NativeFunction::from(instruction.b_field());
if function.returns_value() {
(Argument::Register(instruction.a), true)
(Argument::Register(instruction.a_field()), true)
} else {
return Err(CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
@ -882,9 +897,9 @@ impl<'src> Compiler<'src> {
.iter()
.rev()
.nth(2)
.map_or(0, |(instruction, _, _)| instruction.a);
.map_or(0, |(instruction, _, _)| instruction.a_field());
left_instruction.a = destination;
left_instruction.set_a_field(destination);
}
let jump_index = self.instructions.len().saturating_sub(1);
@ -932,7 +947,7 @@ impl<'src> Compiler<'src> {
self.parse_sub_expression(&rule.precedence)?;
let (mut right_instruction, _, _) = self.instructions.last_mut().unwrap();
right_instruction.a = left_instruction.a;
right_instruction.set_a_field(left_instruction.a_field());
if is_logic_chain {
let expression_length = self.instructions.len() - jump_index - 1;
@ -1005,11 +1020,11 @@ impl<'src> Compiler<'src> {
if self
.instructions
.last()
.map_or(false, |(instruction, _, _)| instruction.is_math())
.is_some_and(|(instruction, _, _)| instruction.is_math())
{
let (math_instruction, _, _) = self.instructions.last_mut().unwrap();
math_instruction.a = local_register_index;
math_instruction.set_a_field(local_register_index);
} else {
let register = self.next_register() - 1;
let set_local = Instruction::from(SetLocal {
@ -1194,7 +1209,7 @@ impl<'src> Compiler<'src> {
{
let (mut loader, _, _) = self.instructions.last_mut().unwrap();
loader.c = true as u8;
loader.set_c_field(true as u8);
} else {
if_block_distance += 1;
let jump = Instruction::from(Jump {

View File

@ -40,8 +40,8 @@ pub fn optimize_test_with_explicit_booleans(compiler: &mut Compiler) {
let first_loader = compiler.instructions.iter().nth_back(1).unwrap();
let second_loader = compiler.instructions.last().unwrap();
let first_boolean = first_loader.0.b != 0;
let second_boolean = second_loader.0.b != 0;
let first_boolean = first_loader.0.b_field() != 0;
let second_boolean = second_loader.0.b_field() != 0;
if first_boolean && !second_boolean {
compiler.instructions.pop();
@ -83,10 +83,10 @@ pub fn optimize_test_with_loader_arguments(compiler: &mut Compiler) {
let first_loader = &mut compiler.instructions.iter_mut().nth_back(1).unwrap().0;
first_loader.c = true as u8;
first_loader.set_c_field(true as u8);
let first_loader_destination = first_loader.a;
let first_loader_destination = first_loader.a_field();
let second_loader = &mut compiler.instructions.last_mut().unwrap().0;
second_loader.a = first_loader_destination;
second_loader.set_a_field(first_loader_destination);
}

View File

@ -8,7 +8,7 @@ pub struct Add {
impl From<&Instruction> for Add {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments();
Add {
@ -23,10 +23,9 @@ impl From<Add> for Instruction {
fn from(add: Add) -> Self {
let operation = Operation::Add;
let a = add.destination;
let (b, b_options) = add.left.as_index_and_b_options();
let (c, c_options) = add.right.as_index_and_c_options();
let metadata = operation as u8 | b_options.bits() | c_options.bits();
let (b, b_is_constant) = add.left.as_index_and_constant_flag();
let (c, c_is_constant) = add.right.as_index_and_constant_flag();
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false)
}
}

View File

@ -8,9 +8,9 @@ pub struct Call {
impl From<&Instruction> for Call {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let destination = instruction.a_field();
let function = instruction.b_as_argument();
let argument_count = instruction.c;
let argument_count = instruction.c_field();
Call {
destination,
@ -23,10 +23,9 @@ impl From<&Instruction> for Call {
impl From<Call> for Instruction {
fn from(call: Call) -> Self {
let a = call.destination;
let (b, b_options) = call.function.as_index_and_b_options();
let (b, b_is_constant) = call.function.as_index_and_constant_flag();
let c = call.argument_count;
let metadata = Operation::Call as u8 | b_options.bits();
Instruction { metadata, a, b, c }
Instruction::new(Operation::Call, a, b, c, b_is_constant, false, false)
}
}

View File

@ -8,24 +8,24 @@ pub struct CallNative {
impl From<&Instruction> for CallNative {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let function = NativeFunction::from(instruction.b);
let destination = instruction.a_field();
let function = NativeFunction::from(instruction.b_field());
CallNative {
destination,
function,
argument_count: instruction.c,
argument_count: instruction.c_field(),
}
}
}
impl From<CallNative> for Instruction {
fn from(call_native: CallNative) -> Self {
let metadata = Operation::CallNative as u8;
let operation = Operation::CallNative;
let a = call_native.destination;
let b = call_native.function as u8;
let c = call_native.argument_count;
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, false, false, false)
}
}

View File

@ -8,17 +8,17 @@ pub struct Close {
impl From<&Instruction> for Close {
fn from(instruction: &Instruction) -> Self {
Close {
from: instruction.b,
to: instruction.c,
from: instruction.b_field(),
to: instruction.c_field(),
}
}
}
impl From<Close> for Instruction {
fn from(close: Close) -> Self {
let metadata = Operation::Close as u8;
let operation = Operation::Close;
let (a, b, c) = (0, close.from, close.to);
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, false, false, false)
}
}

View File

@ -8,7 +8,7 @@ pub struct Divide {
impl From<&Instruction> for Divide {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments();
Divide {
@ -21,11 +21,11 @@ impl From<&Instruction> for Divide {
impl From<Divide> for Instruction {
fn from(divide: Divide) -> Self {
let operation = Operation::Divide;
let a = divide.destination;
let (b, b_options) = divide.left.as_index_and_b_options();
let (c, c_options) = divide.right.as_index_and_c_options();
let metadata = Operation::Divide as u8 | b_options.bits() | c_options.bits();
let (b, b_is_constant) = divide.left.as_index_and_constant_flag();
let (c, c_is_constant) = divide.right.as_index_and_constant_flag();
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false)
}
}

View File

@ -1,7 +1,5 @@
use crate::{Argument, Instruction, Operation};
use super::InstructionOptions;
pub struct Equal {
pub destination: u8,
pub value: bool,
@ -11,8 +9,8 @@ pub struct Equal {
impl From<&Instruction> for Equal {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let value = instruction.d();
let destination = instruction.a_field();
let value = instruction.d_field();
let (left, right) = instruction.b_and_c_as_arguments();
Equal {
@ -26,17 +24,12 @@ impl From<&Instruction> for Equal {
impl From<Equal> for Instruction {
fn from(equal: Equal) -> Self {
let operation = Operation::Equal;
let a = equal.destination;
let (b, b_options) = equal.left.as_index_and_b_options();
let (c, c_options) = equal.right.as_index_and_c_options();
let d_options = if equal.value {
InstructionOptions::D_IS_TRUE
} else {
InstructionOptions::empty()
};
let metadata =
Operation::Equal as u8 | b_options.bits() | c_options.bits() | d_options.bits();
let (b, b_is_constant) = equal.left.as_index_and_constant_flag();
let (c, c_is_constant) = equal.right.as_index_and_constant_flag();
let d = equal.value;
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, d)
}
}

View File

@ -7,8 +7,8 @@ pub struct GetLocal {
impl From<&Instruction> for GetLocal {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let local_index = instruction.b;
let destination = instruction.a_field();
let local_index = instruction.b_field();
GetLocal {
destination,
@ -19,11 +19,10 @@ impl From<&Instruction> for GetLocal {
impl From<GetLocal> for Instruction {
fn from(get_local: GetLocal) -> Self {
let operation = Operation::GetLocal;
let a = get_local.destination;
let b = get_local.local_index;
let c = 0;
let metadata = Operation::GetLocal as u8;
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, 0, false, false, false)
}
}

View File

@ -8,19 +8,18 @@ pub struct Jump {
impl From<&Instruction> for Jump {
fn from(instruction: &Instruction) -> Self {
Jump {
offset: instruction.b,
is_positive: instruction.c != 0,
offset: instruction.b_field(),
is_positive: instruction.c_field() != 0,
}
}
}
impl From<Jump> for Instruction {
fn from(jump: Jump) -> Self {
let metadata = Operation::Jump as u8;
let a = 0;
let operation = Operation::Jump;
let b = jump.offset;
let c = jump.is_positive as u8;
Instruction { metadata, a, b, c }
Instruction::new(operation, 0, b, c, false, false, false)
}
}

View File

@ -1,7 +1,5 @@
use crate::{Argument, Instruction, Operation};
use super::InstructionOptions;
pub struct Less {
pub destination: u8,
pub value: bool,
@ -11,8 +9,8 @@ pub struct Less {
impl From<&Instruction> for Less {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let value = instruction.d();
let destination = instruction.a_field();
let value = instruction.d_field();
let (left, right) = instruction.b_and_c_as_arguments();
Less {
@ -26,17 +24,12 @@ impl From<&Instruction> for Less {
impl From<Less> for Instruction {
fn from(less: Less) -> Self {
let operation = Operation::Less;
let a = less.destination;
let (b, b_options) = less.left.as_index_and_b_options();
let (c, c_options) = less.right.as_index_and_c_options();
let d_options = if less.value {
InstructionOptions::D_IS_TRUE
} else {
InstructionOptions::empty()
};
let metadata =
Operation::Less as u8 | b_options.bits() | c_options.bits() | d_options.bits();
let (b, b_is_constant) = less.left.as_index_and_constant_flag();
let (c, c_is_constant) = less.right.as_index_and_constant_flag();
let d = less.value;
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, d)
}
}

View File

@ -1,7 +1,5 @@
use crate::{Argument, Instruction, Operation};
use super::InstructionOptions;
pub struct LessEqual {
pub destination: u8,
pub value: bool,
@ -11,8 +9,8 @@ pub struct LessEqual {
impl From<&Instruction> for LessEqual {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let value = instruction.d();
let destination = instruction.a_field();
let value = instruction.d_field();
let (left, right) = instruction.b_and_c_as_arguments();
LessEqual {
@ -26,17 +24,12 @@ impl From<&Instruction> for LessEqual {
impl From<LessEqual> for Instruction {
fn from(less_equal: LessEqual) -> Self {
let operation = Operation::LessEqual;
let a = less_equal.destination;
let (b, b_options) = less_equal.left.as_index_and_b_options();
let (c, c_options) = less_equal.right.as_index_and_c_options();
let d_options = if less_equal.value {
InstructionOptions::D_IS_TRUE
} else {
InstructionOptions::empty()
};
let metadata =
Operation::LessEqual as u8 | b_options.bits() | c_options.bits() | d_options.bits();
let (b, b_options) = less_equal.left.as_index_and_constant_flag();
let (c, c_options) = less_equal.right.as_index_and_constant_flag();
let d = less_equal.value;
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, b_options, c_options, d)
}
}

View File

@ -8,9 +8,9 @@ pub struct LoadBoolean {
impl From<&Instruction> for LoadBoolean {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let value = instruction.b != 0;
let jump_next = instruction.c != 0;
let destination = instruction.a_field();
let value = instruction.b_field() != 0;
let jump_next = instruction.c_field() != 0;
LoadBoolean {
destination,
@ -22,11 +22,11 @@ impl From<&Instruction> for LoadBoolean {
impl From<LoadBoolean> for Instruction {
fn from(load_boolean: LoadBoolean) -> Self {
let metadata = Operation::LoadBoolean as u8;
let operation = Operation::LoadBoolean;
let a = load_boolean.destination;
let b = load_boolean.value as u8;
let c = load_boolean.jump_next as u8;
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, false, false, false)
}
}

View File

@ -8,9 +8,9 @@ pub struct LoadConstant {
impl From<&Instruction> for LoadConstant {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let constant_index = instruction.b;
let jump_next = instruction.c != 0;
let destination = instruction.a_field();
let constant_index = instruction.b_field();
let jump_next = instruction.c_field() != 0;
LoadConstant {
destination,
@ -22,11 +22,11 @@ impl From<&Instruction> for LoadConstant {
impl From<LoadConstant> for Instruction {
fn from(load_constant: LoadConstant) -> Self {
let metadata = Operation::LoadConstant as u8;
let operation = Operation::LoadConstant;
let a = load_constant.destination;
let b = load_constant.constant_index;
let c = load_constant.jump_next as u8;
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, false, false, false)
}
}

View File

@ -7,8 +7,8 @@ pub struct LoadList {
impl From<&Instruction> for LoadList {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let start_register = instruction.b;
let destination = instruction.a_field();
let start_register = instruction.b_field();
LoadList {
destination,
@ -19,11 +19,10 @@ impl From<&Instruction> for LoadList {
impl From<LoadList> for Instruction {
fn from(load_list: LoadList) -> Self {
let metadata = Operation::LoadList as u8;
let operation = Operation::LoadList;
let a = load_list.destination;
let b = load_list.start_register;
let c = 0;
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, 0, false, false, false)
}
}

View File

@ -6,7 +6,7 @@ pub struct LoadSelf {
impl From<&Instruction> for LoadSelf {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let destination = instruction.a_field();
LoadSelf { destination }
}
@ -14,11 +14,9 @@ impl From<&Instruction> for LoadSelf {
impl From<LoadSelf> for Instruction {
fn from(load_self: LoadSelf) -> Self {
let metadata = Operation::LoadSelf as u8;
let operation = Operation::LoadSelf;
let a = load_self.destination;
let b = 0;
let c = 0;
Instruction { metadata, a, b, c }
Instruction::new(operation, a, 0, 0, false, false, false)
}
}

View File

@ -109,7 +109,6 @@ mod multiply;
mod negate;
mod not;
mod operation;
mod options;
mod r#return;
mod set_local;
mod subtract;
@ -135,7 +134,6 @@ pub use multiply::Multiply;
pub use negate::Negate;
pub use not::Not;
pub use operation::Operation;
pub use options::InstructionOptions;
pub use r#move::Move;
pub use r#return::Return;
pub use set_local::SetLocal;
@ -152,22 +150,92 @@ use crate::NativeFunction;
///
/// See the [module-level documentation](index.html) for more information.
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Instruction {
pub metadata: u8,
pub struct Instruction(u32);
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct InstructionData {
pub operation: Operation,
pub a: u8,
pub b: u8,
pub c: u8,
pub b_is_constant: bool,
pub c_is_constant: bool,
pub d: bool,
}
impl Instruction {
pub fn operation(&self) -> Operation {
let operation_bits = self.metadata & 0b0001_1111;
pub fn new(
operation: Operation,
a: u8,
b: u8,
c: u8,
b_is_constant: bool,
c_is_constant: bool,
d: bool,
) -> Instruction {
let bits = operation as u32
| ((b_is_constant as u32) << 5)
| ((c_is_constant as u32) << 6)
| ((d as u32) << 7)
| ((a as u32) << 8)
| ((b as u32) << 16)
| ((c as u32) << 24);
Operation::from(operation_bits)
Instruction(bits)
}
pub fn d(&self) -> bool {
self.metadata & 0b1000_0000 != 0
pub fn operation(&self) -> Operation {
let operation_bits = self.0 & 0b0001_1111;
Operation::from(operation_bits as u8)
}
pub fn b_is_constant(&self) -> bool {
(self.0 >> 5) & 1 == 1
}
pub fn c_is_constant(&self) -> bool {
(self.0 >> 6) & 1 == 1
}
pub fn d_field(&self) -> bool {
(self.0 >> 7) & 1 == 1
}
pub fn a_field(&self) -> u8 {
(self.0 >> 8) as u8
}
pub fn b_field(&self) -> u8 {
(self.0 >> 16) as u8
}
pub fn c_field(&self) -> u8 {
(self.0 >> 24) as u8
}
pub fn set_a_field(&mut self, bits: u8) {
self.0 = (self.0 & 0xFFFF00FF) | ((bits as u32) << 8);
}
pub fn set_b_field(&mut self, bits: u8) {
self.0 = (self.0 & 0xFFFF00FF) | ((bits as u32) << 16);
}
pub fn set_c_field(&mut self, bits: u8) {
self.0 = (self.0 & 0xFF00FFFF) | ((bits as u32) << 24);
}
pub fn decode(self) -> InstructionData {
InstructionData {
operation: self.operation(),
a: self.a_field(),
b: self.b_field(),
c: self.c_field(),
b_is_constant: self.b_is_constant(),
c_is_constant: self.c_is_constant(),
d: self.d_field(),
}
}
pub fn r#move(from: u8, to: u8) -> Instruction {
@ -373,7 +441,7 @@ impl Instruction {
pub fn as_argument(&self) -> Option<Argument> {
match self.operation() {
Operation::LoadConstant => Some(Argument::Constant(self.b)),
Operation::LoadConstant => Some(Argument::Constant(self.b_field())),
Operation::LoadBoolean
| Operation::LoadList
| Operation::LoadSelf
@ -388,12 +456,12 @@ impl Instruction {
| Operation::LessEqual
| Operation::Negate
| Operation::Not
| Operation::Call => Some(Argument::Register(self.a)),
| Operation::Call => Some(Argument::Register(self.a_field())),
Operation::CallNative => {
let function = NativeFunction::from(self.b);
let function = NativeFunction::from(self.b_field());
if function.returns_value() {
Some(Argument::Register(self.a))
Some(Argument::Register(self.a_field()))
} else {
None
}
@ -403,28 +471,23 @@ impl Instruction {
}
pub fn b_as_argument(&self) -> Argument {
let b_is_constant = self.metadata & 0b0010_0000 != 0;
if b_is_constant {
Argument::Constant(self.b)
if self.b_is_constant() {
Argument::Constant(self.b_field())
} else {
Argument::Register(self.b)
Argument::Register(self.b_field())
}
}
pub fn b_and_c_as_arguments(&self) -> (Argument, Argument) {
let b_is_constant = self.metadata & 0b0010_0000 != 0;
let c_is_constant = self.metadata & 0b0100_0000 != 0;
let left = if b_is_constant {
Argument::Constant(self.b)
let left = if self.b_is_constant() {
Argument::Constant(self.b_field())
} else {
Argument::Register(self.b)
Argument::Register(self.b_field())
};
let right = if c_is_constant {
Argument::Constant(self.c)
let right = if self.c_is_constant() {
Argument::Constant(self.c_field())
} else {
Argument::Register(self.c)
Argument::Register(self.c_field())
};
(left, right)
@ -451,7 +514,7 @@ impl Instruction {
| Operation::LessEqual
| Operation::Call => true,
Operation::CallNative => {
let function = NativeFunction::from(self.b);
let function = NativeFunction::from(self.b_field());
function.returns_value()
}
@ -655,7 +718,7 @@ impl Instruction {
if is_positive {
format!("JUMP +{offset}")
} else {
format!("JUMP -{}", offset - 1)
format!("JUMP -{offset}")
}
}
Operation::Call => {
@ -751,17 +814,10 @@ impl Argument {
matches!(self, Argument::Register(_))
}
pub fn as_index_and_b_options(&self) -> (u8, InstructionOptions) {
pub fn as_index_and_constant_flag(&self) -> (u8, bool) {
match self {
Argument::Constant(index) => (*index, InstructionOptions::B_IS_CONSTANT),
Argument::Register(index) => (*index, InstructionOptions::empty()),
}
}
pub fn as_index_and_c_options(&self) -> (u8, InstructionOptions) {
match self {
Argument::Constant(index) => (*index, InstructionOptions::C_IS_CONSTANT),
Argument::Register(index) => (*index, InstructionOptions::empty()),
Argument::Constant(index) => (*index, true),
Argument::Register(index) => (*index, false),
}
}
}
@ -774,23 +830,3 @@ impl Display for Argument {
}
}
}
#[cfg(test)]
mod tests {
use std::mem::offset_of;
use super::*;
#[test]
fn instruction_is_4_bytes() {
assert_eq!(size_of::<Instruction>(), 4);
}
#[test]
fn instruction_layout() {
assert_eq!(offset_of!(Instruction, metadata), 0);
assert_eq!(offset_of!(Instruction, a), 1);
assert_eq!(offset_of!(Instruction, b), 2);
assert_eq!(offset_of!(Instruction, c), 3);
}
}

View File

@ -8,7 +8,7 @@ pub struct Modulo {
impl From<&Instruction> for Modulo {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments();
Modulo {
@ -21,11 +21,11 @@ impl From<&Instruction> for Modulo {
impl From<Modulo> for Instruction {
fn from(modulo: Modulo) -> Self {
let operation = Operation::Modulo;
let a = modulo.destination;
let (b, b_options) = modulo.left.as_index_and_b_options();
let (c, c_options) = modulo.right.as_index_and_c_options();
let metadata = Operation::Modulo as u8 | b_options.bits() | c_options.bits();
let (b, b_is_constant) = modulo.left.as_index_and_constant_flag();
let (c, c_is_constant) = modulo.right.as_index_and_constant_flag();
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false)
}
}

View File

@ -8,19 +8,18 @@ pub struct Move {
impl From<&Instruction> for Move {
fn from(instruction: &Instruction) -> Self {
Move {
from: instruction.b,
to: instruction.c,
from: instruction.b_field(),
to: instruction.c_field(),
}
}
}
impl From<Move> for Instruction {
fn from(r#move: Move) -> Self {
let metadata = Operation::Move as u8;
let a = 0;
let operation = Operation::Move;
let b = r#move.from;
let c = r#move.to;
Instruction { metadata, a, b, c }
Instruction::new(operation, 0, b, c, false, false, false)
}
}

View File

@ -8,7 +8,7 @@ pub struct Multiply {
impl From<&Instruction> for Multiply {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments();
Multiply {
@ -21,11 +21,11 @@ impl From<&Instruction> for Multiply {
impl From<Multiply> for Instruction {
fn from(multiply: Multiply) -> Self {
let operation = Operation::Multiply;
let a = multiply.destination;
let (b, b_options) = multiply.left.as_index_and_b_options();
let (c, c_options) = multiply.right.as_index_and_c_options();
let metadata = Operation::Multiply as u8 | b_options.bits() | c_options.bits();
let (b, b_options) = multiply.left.as_index_and_constant_flag();
let (c, c_options) = multiply.right.as_index_and_constant_flag();
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, b_options, c_options, false)
}
}

View File

@ -7,7 +7,7 @@ pub struct Negate {
impl From<&Instruction> for Negate {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let destination = instruction.a_field();
let argument = instruction.b_as_argument();
Negate {
@ -19,11 +19,11 @@ impl From<&Instruction> for Negate {
impl From<Negate> for Instruction {
fn from(negate: Negate) -> Self {
let operation = Operation::Negate;
let a = negate.destination;
let (b, b_options) = negate.argument.as_index_and_b_options();
let (b, b_is_constant) = negate.argument.as_index_and_constant_flag();
let c = 0;
let metadata = Operation::Negate as u8 | b_options.bits();
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, b_is_constant, false, false)
}
}

View File

@ -7,7 +7,7 @@ pub struct Not {
impl From<&Instruction> for Not {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let destination = instruction.a_field();
let argument = instruction.b_as_argument();
Not {
@ -19,11 +19,10 @@ impl From<&Instruction> for Not {
impl From<Not> for Instruction {
fn from(not: Not) -> Self {
let operation = Operation::Not;
let a = not.destination;
let (b, b_options) = not.argument.as_index_and_b_options();
let c = 0;
let metadata = Operation::Not as u8 | b_options.bits();
let (b, b_is_constant) = not.argument.as_index_and_constant_flag();
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, 0, b_is_constant, false, false)
}
}

View File

@ -1,34 +0,0 @@
//! Byte that uses its bits as boolean flags to represent information about an instruction's
//! arguments. Additionally, one bit is used as the instruction's `D` field.
//!
//! See the [instruction documentation](crate::instruction) for more information.
use bitflags::bitflags;
use serde::{Deserialize, Serialize};
bitflags! {
/// Byte that uses its bits as boolean flags to represent an instruction's options and D field.
///
/// See the [instruction documentation](crate::instruction) for more information.
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct InstructionOptions: u8 {
const B_IS_CONSTANT = 0b0010_0000;
const C_IS_CONSTANT = 0b0100_0000;
const D_IS_TRUE = 0b1000_0000;
}
}
impl InstructionOptions {
pub fn b_is_constant(self) -> bool {
self.contains(Self::B_IS_CONSTANT)
}
pub fn c_is_constant(self) -> bool {
self.contains(Self::C_IS_CONSTANT)
}
pub fn d(self) -> bool {
self.contains(Self::D_IS_TRUE)
}
}

View File

@ -6,7 +6,7 @@ pub struct Return {
impl From<&Instruction> for Return {
fn from(instruction: &Instruction) -> Self {
let should_return_value = instruction.b != 0;
let should_return_value = instruction.b_field() != 0;
Return {
should_return_value,
@ -16,11 +16,9 @@ impl From<&Instruction> for Return {
impl From<Return> for Instruction {
fn from(r#return: Return) -> Self {
let metadata = Operation::Return as u8;
let a = 0;
let operation = Operation::Return;
let b = r#return.should_return_value as u8;
let c = 0;
Instruction { metadata, a, b, c }
Instruction::new(operation, 0, b, 0, false, false, false)
}
}

View File

@ -7,8 +7,8 @@ pub struct SetLocal {
impl From<&Instruction> for SetLocal {
fn from(instruction: &Instruction) -> Self {
let register_index = instruction.b;
let local_index = instruction.c;
let register_index = instruction.b_field();
let local_index = instruction.c_field();
SetLocal {
register_index,
@ -19,11 +19,10 @@ impl From<&Instruction> for SetLocal {
impl From<SetLocal> for Instruction {
fn from(set_local: SetLocal) -> Self {
let metadata = Operation::SetLocal as u8;
let a = 0;
let operation = Operation::SetLocal;
let b = set_local.register_index;
let c = set_local.local_index;
Instruction { metadata, a, b, c }
Instruction::new(operation, 0, b, c, false, false, false)
}
}

View File

@ -8,7 +8,7 @@ pub struct Subtract {
impl From<&Instruction> for Subtract {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments();
Subtract {
@ -21,11 +21,11 @@ impl From<&Instruction> for Subtract {
impl From<Subtract> for Instruction {
fn from(subtract: Subtract) -> Self {
let operation = Operation::Subtract;
let a = subtract.destination;
let (b, b_options) = subtract.left.as_index_and_b_options();
let (c, c_options) = subtract.right.as_index_and_c_options();
let metadata = Operation::Subtract as u8 | b_options.bits() | c_options.bits();
let (b, b_is_constant) = subtract.left.as_index_and_constant_flag();
let (c, c_is_constant) = subtract.right.as_index_and_constant_flag();
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, b_is_constant, c_is_constant, false)
}
}

View File

@ -8,7 +8,7 @@ pub struct Test {
impl From<&Instruction> for Test {
fn from(instruction: &Instruction) -> Self {
let argument = instruction.b_as_argument();
let test_value = instruction.c != 0;
let test_value = instruction.c_field() != 0;
Test {
argument,
@ -19,11 +19,10 @@ impl From<&Instruction> for Test {
impl From<Test> for Instruction {
fn from(test: Test) -> Self {
let a = 0;
let (b, options) = test.argument.as_index_and_b_options();
let operation = Operation::Test;
let (b, b_is_constant) = test.argument.as_index_and_constant_flag();
let c = test.test_value as u8;
let metadata = Operation::Test as u8 | options.bits();
Instruction { metadata, a, b, c }
Instruction::new(operation, 0, b, c, b_is_constant, false, false)
}
}

View File

@ -8,9 +8,9 @@ pub struct TestSet {
impl From<&Instruction> for TestSet {
fn from(instruction: &Instruction) -> Self {
let destination = instruction.a;
let destination = instruction.a_field();
let argument = instruction.b_as_argument();
let test_value = instruction.c != 0;
let test_value = instruction.c_field() != 0;
TestSet {
destination,
@ -22,11 +22,11 @@ impl From<&Instruction> for TestSet {
impl From<TestSet> for Instruction {
fn from(test_set: TestSet) -> Self {
let operation = Operation::Test;
let a = test_set.destination;
let (b, b_options) = test_set.argument.as_index_and_b_options();
let (b, b_is_constant) = test_set.argument.as_index_and_constant_flag();
let c = test_set.test_value as u8;
let metadata = Operation::TestSet as u8 | b_options.bits();
Instruction { metadata, a, b, c }
Instruction::new(operation, a, b, c, b_is_constant, false, false)
}
}

View File

@ -619,7 +619,7 @@ pub struct LexRule<'src> {
lexer: LexerFn<'src>,
}
impl<'src> From<&char> for LexRule<'src> {
impl From<&char> for LexRule<'_> {
fn from(char: &char) -> Self {
match char {
'0'..='9' => LexRule {

View File

@ -42,7 +42,7 @@ pub mod vm;
pub use crate::chunk::{Chunk, Disassembler, Local, Scope};
pub use crate::compiler::{compile, CompileError, Compiler};
pub use crate::dust_error::{AnnotatedError, DustError};
pub use crate::instruction::{Argument, Instruction, Operation};
pub use crate::instruction::{Argument, Instruction, InstructionData, Operation};
pub use crate::lexer::{lex, LexError, Lexer};
pub use crate::native_function::{NativeFunction, NativeFunctionError};
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};

View File

@ -93,7 +93,7 @@ define_tokens! {
StarEqual,
}
impl<'src> Token<'src> {
impl Token<'_> {
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
match self {
@ -382,7 +382,7 @@ impl<'src> Token<'src> {
}
}
impl<'src> Display for Token<'src> {
impl Display for Token<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Token::ArrowThin => write!(f, "->"),

View File

@ -4,12 +4,11 @@ use std::{
io,
};
use slab::Slab;
use smallvec::SmallVec;
use crate::{
compile, instruction::*, AbstractValue, AnnotatedError, Argument, Chunk, ConcreteValue,
DustError, Instruction, NativeFunctionError, Operation, Span, Value, ValueError, ValueRef,
compile, instruction::*, AbstractValue, AnnotatedError, Chunk, ConcreteValue, DustError,
Instruction, NativeFunctionError, Operation, Span, Value, ValueError, ValueRef,
};
pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
@ -24,7 +23,7 @@ pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
/// See the [module-level documentation](index.html) for more information.
#[derive(Debug)]
pub struct Vm<'a> {
stack: Slab<Register>,
stack: Vec<Register>,
chunk: &'a Chunk,
parent: Option<&'a Vm<'a>>,
@ -36,11 +35,7 @@ pub struct Vm<'a> {
impl<'a> Vm<'a> {
pub fn new(source: &'a str, chunk: &'a Chunk, parent: Option<&'a Vm<'a>>) -> Self {
let mut stack = Slab::with_capacity(chunk.stack_size());
for _ in 0..chunk.stack_size() {
stack.insert(Register::Empty);
}
let stack = vec![Register::Empty; chunk.stack_size()];
Self {
chunk,
@ -70,6 +65,15 @@ impl<'a> Vm<'a> {
pub fn run(&mut self) -> Result<Option<ConcreteValue>, VmError> {
loop {
let instruction = self.read();
let InstructionData {
operation,
a,
b,
c,
b_is_constant,
c_is_constant,
d,
} = instruction.decode();
log::info!(
"{} | {} | {} | {}",
@ -79,7 +83,7 @@ impl<'a> Vm<'a> {
instruction.disassembly_info()
);
match instruction.operation() {
match operation {
Operation::Move => {
let Move { from, to } = Move::from(&instruction);
let from_register_has_value = self
@ -121,16 +125,11 @@ impl<'a> Vm<'a> {
}
}
Operation::LoadConstant => {
let LoadConstant {
destination,
constant_index,
jump_next,
} = LoadConstant::from(&instruction);
let register = Register::Pointer(Pointer::Constant(constant_index));
let register = Register::Pointer(Pointer::Constant(b));
self.set_register(destination, register)?;
self.set_register(a, register)?;
if jump_next {
if c != 0 {
self.jump(1, true);
}
}
@ -187,13 +186,16 @@ impl<'a> Vm<'a> {
self.set_register(local_register_index, register)?;
}
Operation::Add => {
let Add {
destination,
left,
right,
} = Add::from(&instruction);
let left = self.get_argument(left)?;
let right = self.get_argument(right)?;
let left = if b_is_constant {
self.get_constant(b).to_value_ref()
} else {
self.open_register(b)?
};
let right = if c_is_constant {
self.get_constant(c).to_value_ref()
} else {
self.open_register(c)?
};
let sum_result = left.add(right);
let sum = match sum_result {
Ok(sum) => sum,
@ -205,16 +207,11 @@ impl<'a> Vm<'a> {
}
};
self.set_register(destination, Register::Value(sum))?;
self.set_register(a, Register::Value(sum))?;
}
Operation::Subtract => {
let Subtract {
destination,
left,
right,
} = Subtract::from(&instruction);
let left = self.get_argument(left)?;
let right = self.get_argument(right)?;
let left = self.get_argument(b, b_is_constant)?;
let right = self.get_argument(c, c_is_constant)?;
let subtraction_result = left.subtract(right);
let difference = match subtraction_result {
Ok(difference) => difference,
@ -226,16 +223,11 @@ impl<'a> Vm<'a> {
}
};
self.set_register(destination, Register::Value(difference))?;
self.set_register(a, Register::Value(difference))?;
}
Operation::Multiply => {
let Multiply {
destination,
left,
right,
} = Multiply::from(&instruction);
let left = self.get_argument(left)?;
let right = self.get_argument(right)?;
let left = self.get_argument(b, b_is_constant)?;
let right = self.get_argument(c, c_is_constant)?;
let multiplication_result = left.multiply(right);
let product = match multiplication_result {
Ok(product) => product,
@ -247,7 +239,7 @@ impl<'a> Vm<'a> {
}
};
self.set_register(destination, Register::Value(product))?;
self.set_register(a, Register::Value(product))?;
}
Operation::Divide => {
let Divide {
@ -255,8 +247,8 @@ impl<'a> Vm<'a> {
left,
right,
} = Divide::from(&instruction);
let left = self.get_argument(left)?;
let right = self.get_argument(right)?;
let left = self.get_argument(b, b_is_constant)?;
let right = self.get_argument(c, c_is_constant)?;
let division_result = left.divide(right);
let quotient = match division_result {
Ok(quotient) => quotient,
@ -276,8 +268,8 @@ impl<'a> Vm<'a> {
left,
right,
} = Modulo::from(&instruction);
let left = self.get_argument(left)?;
let right = self.get_argument(right)?;
let left = self.get_argument(b, b_is_constant)?;
let right = self.get_argument(c, c_is_constant)?;
let modulo_result = left.modulo(right);
let remainder = match modulo_result {
Ok(remainder) => remainder,
@ -292,11 +284,11 @@ impl<'a> Vm<'a> {
self.set_register(destination, Register::Value(remainder))?;
}
Operation::Test => {
let Test {
argument,
test_value,
} = Test::from(&instruction);
let value = self.get_argument(argument)?;
let value = if b_is_constant {
self.get_constant(b).to_value_ref()
} else {
self.open_register(b)?
};
let boolean = if let ValueRef::Concrete(ConcreteValue::Boolean(boolean)) = value
{
*boolean
@ -307,17 +299,12 @@ impl<'a> Vm<'a> {
});
};
if boolean == test_value {
if boolean == (c != 0) {
self.jump(1, true);
}
}
Operation::TestSet => {
let TestSet {
destination,
argument,
test_value,
} = TestSet::from(&instruction);
let value = self.get_argument(argument)?;
let value = self.get_argument(b, b_is_constant)?;
let boolean = if let ValueRef::Concrete(ConcreteValue::Boolean(boolean)) = value
{
*boolean
@ -327,28 +314,24 @@ impl<'a> Vm<'a> {
position: self.current_position(),
});
};
let test_value = c != 0;
if boolean == test_value {
self.jump(1, true);
} else {
let pointer = match argument {
Argument::Constant(constant_index) => Pointer::Constant(constant_index),
Argument::Register(register_index) => Pointer::Stack(register_index),
let pointer = if b_is_constant {
Pointer::Constant(b)
} else {
Pointer::Stack(b)
};
let register = Register::Pointer(pointer);
self.set_register(destination, register)?;
self.set_register(a, register)?;
}
}
Operation::Equal => {
let Equal {
destination,
value,
left,
right,
} = Equal::from(&instruction);
let left = self.get_argument(left)?;
let right = self.get_argument(right)?;
let left = self.get_argument(b, b_is_constant)?;
let right = self.get_argument(c, c_is_constant)?;
let equal_result = left.equal(right).map_err(|error| VmError::Value {
error,
position: self.current_position(),
@ -362,21 +345,23 @@ impl<'a> Vm<'a> {
position: self.current_position(),
});
};
let comparison = is_equal == value;
let comparison = is_equal == d;
let register =
Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
self.set_register(destination, register)?;
self.set_register(a, register)?;
}
Operation::Less => {
let Less {
destination,
value,
left,
right,
} = Less::from(&instruction);
let left = self.get_argument(left)?;
let right = self.get_argument(right)?;
let left = if b_is_constant {
self.get_constant(b).to_value_ref()
} else {
self.open_register(b)?
};
let right = if c_is_constant {
self.get_constant(c).to_value_ref()
} else {
self.open_register(c)?
};
let less_result = left.less_than(right);
let less_than_value = match less_result {
Ok(value) => value,
@ -396,21 +381,23 @@ impl<'a> Vm<'a> {
});
}
};
let comparison = is_less_than == value;
let comparison = is_less_than == d;
let register =
Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
self.set_register(destination, register)?;
self.set_register(a, register)?;
}
Operation::LessEqual => {
let LessEqual {
destination,
value,
left,
right,
} = LessEqual::from(&instruction);
let left = self.get_argument(left)?;
let right = self.get_argument(right)?;
let left = if b_is_constant {
self.get_constant(b).to_value_ref()
} else {
self.open_register(b)?
};
let right = if c_is_constant {
self.get_constant(c).to_value_ref()
} else {
self.open_register(c)?
};
let less_or_equal_result = left.less_than_or_equal(right);
let less_or_equal_value = match less_or_equal_result {
Ok(value) => value,
@ -430,55 +417,37 @@ impl<'a> Vm<'a> {
});
}
};
let comparison = is_less_than_or_equal == value;
let comparison = is_less_than_or_equal == d;
let register =
Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison)));
self.set_register(destination, register)?;
self.set_register(a, register)?;
}
Operation::Negate => {
let Negate {
destination,
argument,
} = Negate::from(&instruction);
let value = self.get_argument(argument)?;
let value = self.get_argument(b, b_is_constant)?;
let negated = value.negate().map_err(|error| VmError::Value {
error,
position: self.current_position(),
})?;
let register = Register::Value(negated);
self.set_register(destination, register)?;
self.set_register(a, register)?;
}
Operation::Not => {
let Not {
destination,
argument,
} = Not::from(&instruction);
let value = self.get_argument(argument)?;
let value = self.get_argument(b, b_is_constant)?;
let not = value.not().map_err(|error| VmError::Value {
error,
position: self.current_position(),
})?;
let register = Register::Value(not);
self.set_register(destination, register)?;
self.set_register(a, register)?;
}
Operation::Jump => {
let Jump {
offset,
is_positive,
} = Jump::from(&instruction);
self.jump(offset as usize, is_positive);
self.jump(b as usize, c != 0);
}
Operation::Call => {
let Call {
destination,
function,
argument_count,
} = Call::from(&instruction);
let function = self.get_argument(function)?;
let function = self.get_argument(b, b_is_constant)?;
let chunk = if let ValueRef::Concrete(ConcreteValue::Function(chunk)) = function
{
chunk
@ -491,15 +460,25 @@ impl<'a> Vm<'a> {
});
};
let mut function_vm = Vm::new(self.source, chunk, Some(self));
let first_argument_index = destination - argument_count;
let first_argument_index = a - c;
let mut argument_index = 0;
for argument_register_index in first_argument_index..a {
let target_register_is_empty = matches!(
self.stack[argument_register_index as usize],
Register::Empty
);
if target_register_is_empty {
continue;
}
for (argument_index, argument_register_index) in
(first_argument_index..destination).enumerate()
{
function_vm.set_register(
argument_index as u8,
Register::Pointer(Pointer::ParentStack(argument_register_index)),
)?;
argument_index += 1;
}
let return_value = function_vm.run()?;
@ -507,7 +486,7 @@ impl<'a> Vm<'a> {
if let Some(concrete_value) = return_value {
let register = Register::Value(concrete_value.to_value());
self.set_register(destination, register)?;
self.set_register(a, register)?;
}
}
Operation::CallNative => {
@ -645,17 +624,15 @@ impl<'a> Vm<'a> {
fn open_register(&self, register_index: u8) -> Result<ValueRef, VmError> {
let register_index = register_index as usize;
let stack = self.stack.as_slice();
if register_index < self.stack.len() {
let register = &self.stack[register_index];
if register_index < stack.len() {
let register = &stack[register_index];
return match register {
Register::Value(value) => Ok(value.to_ref()),
Register::Pointer(pointer) => self.follow_pointer(*pointer),
Register::Empty => Err(VmError::EmptyRegister {
index: register_index,
position: self.current_position(),
}),
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
};
}
@ -700,23 +677,21 @@ impl<'a> Vm<'a> {
}
/// DRY helper to get a value from an Argument
fn get_argument(&self, argument: Argument) -> Result<ValueRef, VmError> {
let value_ref = match argument {
Argument::Constant(constant_index) => {
ValueRef::Concrete(self.get_constant(constant_index))
fn get_argument(&self, index: u8, is_constant: bool) -> Result<ValueRef, VmError> {
if is_constant {
Ok(self.get_constant(index).to_value_ref())
} else {
Ok(self.open_register(index)?)
}
Argument::Register(register_index) => self.open_register(register_index)?,
};
Ok(value_ref)
}
fn set_register(&mut self, to_register: u8, register: Register) -> Result<(), VmError> {
self.last_assigned_register = Some(to_register);
let to_register = to_register as usize;
let stack = self.stack.as_mut_slice();
if to_register < self.stack.len() {
self.stack[to_register] = register;
if to_register < stack.len() {
stack[to_register] = register;
return Ok(());
}
@ -899,7 +874,27 @@ impl AnnotatedError for VmError {
}
fn detail_snippets(&self) -> SmallVec<[(String, Span); 2]> {
todo!()
match self {
VmError::StackOverflow { position } => todo!(),
VmError::StackUnderflow { position } => todo!(),
VmError::EmptyRegister { index, position } => todo!(),
VmError::ExpectedConcreteValue { found, position } => todo!(),
VmError::ExpectedValue { found, position } => todo!(),
VmError::RegisterIndexOutOfBounds { index, position } => todo!(),
VmError::UndefinedLocal {
local_index,
position,
} => todo!(),
VmError::ExpectedBoolean { found, position } => todo!(),
VmError::ExpectedFunction { found, position } => todo!(),
VmError::ExpectedParent { position } => todo!(),
VmError::ValueDisplay { error, position } => todo!(),
VmError::ConstantIndexOutOfBounds { index, position } => todo!(),
VmError::InstructionIndexOutOfBounds { index, position } => todo!(),
VmError::LocalIndexOutOfBounds { index, position } => todo!(),
VmError::NativeFunction(native_function_error) => todo!(),
VmError::Value { error, position } => todo!(),
}
}
fn help_snippets(&self) -> SmallVec<[(String, Span); 2]> {