From 20f451fe6c2832d3e765a9a741a87bce394d4c20 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 11 Dec 2024 06:49:43 -0500 Subject: [PATCH] Experiment with optimizations and benches --- Cargo.lock | 10 - dust-lang/Cargo.toml | 5 +- dust-lang/benches/addictive_addition.rs | 2 +- dust-lang/benches/fibonacci.rs | 32 +++ dust-lang/src/chunk/disassembler.rs | 8 +- dust-lang/src/chunk/mod.rs | 45 ++-- dust-lang/src/compiler/mod.rs | 41 +++- dust-lang/src/compiler/optimize.rs | 10 +- dust-lang/src/instruction/add.rs | 9 +- dust-lang/src/instruction/call.rs | 9 +- dust-lang/src/instruction/call_native.rs | 10 +- dust-lang/src/instruction/close.rs | 8 +- dust-lang/src/instruction/divide.rs | 10 +- dust-lang/src/instruction/equal.rs | 21 +- dust-lang/src/instruction/get_local.rs | 9 +- dust-lang/src/instruction/jump.rs | 9 +- dust-lang/src/instruction/less.rs | 21 +- dust-lang/src/instruction/less_equal.rs | 21 +- dust-lang/src/instruction/load_boolean.rs | 10 +- dust-lang/src/instruction/load_constant.rs | 10 +- dust-lang/src/instruction/load_list.rs | 9 +- dust-lang/src/instruction/load_self.rs | 8 +- dust-lang/src/instruction/mod.rs | 154 +++++++----- dust-lang/src/instruction/modulo.rs | 10 +- dust-lang/src/instruction/move.rs | 9 +- dust-lang/src/instruction/multiply.rs | 10 +- dust-lang/src/instruction/negate.rs | 8 +- dust-lang/src/instruction/not.rs | 9 +- dust-lang/src/instruction/options.rs | 34 --- dust-lang/src/instruction/return.rs | 8 +- dust-lang/src/instruction/set_local.rs | 9 +- dust-lang/src/instruction/subtract.rs | 10 +- dust-lang/src/instruction/test.rs | 9 +- dust-lang/src/instruction/test_set.rs | 10 +- dust-lang/src/lexer.rs | 2 +- dust-lang/src/lib.rs | 2 +- dust-lang/src/token.rs | 4 +- dust-lang/src/vm.rs | 273 ++++++++++----------- 38 files changed, 437 insertions(+), 441 deletions(-) create mode 100644 dust-lang/benches/fibonacci.rs delete mode 100644 dust-lang/src/instruction/options.rs diff --git a/Cargo.lock b/Cargo.lock index 0730842..5579061 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index ed11c85..0481312 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -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" diff --git a/dust-lang/benches/addictive_addition.rs b/dust-lang/benches/addictive_addition.rs index 964939f..5c82e32 100644 --- a/dust-lang/benches/addictive_addition.rs +++ b/dust-lang/benches/addictive_addition.rs @@ -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 { diff --git a/dust-lang/benches/fibonacci.rs b/dust-lang/benches/fibonacci.rs new file mode 100644 index 0000000..556be97 --- /dev/null +++ b/dust-lang/benches/fibonacci.rs @@ -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); diff --git a/dust-lang/src/chunk/disassembler.rs b/dust-lang/src/chunk/disassembler.rs index 0d21e71..3a73c62 100644 --- a/dust-lang/src/chunk/disassembler.rs +++ b/dust-lang/src/chunk/disassembler.rs @@ -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)?; diff --git a/dust-lang/src/chunk/mod.rs b/dust-lang/src/chunk/mod.rs index a988b5e..2096070 100644 --- a/dust-lang/src/chunk/mod.rs +++ b/dust-lang/src/chunk/mod.rs @@ -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) -> Self { + pub fn new( + name: Option, + 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, 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 { + pub fn disassembler<'a, W: Write>(&'a self, writer: &'a mut W) -> Disassembler<'a, W> { Disassembler::new(self, writer) } } diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index 6cda5c1..1ff5d79 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -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::>(); - 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 { diff --git a/dust-lang/src/compiler/optimize.rs b/dust-lang/src/compiler/optimize.rs index 4f64332..b2008ee 100644 --- a/dust-lang/src/compiler/optimize.rs +++ b/dust-lang/src/compiler/optimize.rs @@ -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); } diff --git a/dust-lang/src/instruction/add.rs b/dust-lang/src/instruction/add.rs index 2117fd8..dcd2646 100644 --- a/dust-lang/src/instruction/add.rs +++ b/dust-lang/src/instruction/add.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/call.rs b/dust-lang/src/instruction/call.rs index 2176dfa..cd5240a 100644 --- a/dust-lang/src/instruction/call.rs +++ b/dust-lang/src/instruction/call.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/call_native.rs b/dust-lang/src/instruction/call_native.rs index 0510475..12f8873 100644 --- a/dust-lang/src/instruction/call_native.rs +++ b/dust-lang/src/instruction/call_native.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/close.rs b/dust-lang/src/instruction/close.rs index fc7b39d..b7cbee9 100644 --- a/dust-lang/src/instruction/close.rs +++ b/dust-lang/src/instruction/close.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/divide.rs b/dust-lang/src/instruction/divide.rs index dd7d948..a4c8ab3 100644 --- a/dust-lang/src/instruction/divide.rs +++ b/dust-lang/src/instruction/divide.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/equal.rs b/dust-lang/src/instruction/equal.rs index f01a6ea..e8fc5ca 100644 --- a/dust-lang/src/instruction/equal.rs +++ b/dust-lang/src/instruction/equal.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/get_local.rs b/dust-lang/src/instruction/get_local.rs index 87ad587..6462624 100644 --- a/dust-lang/src/instruction/get_local.rs +++ b/dust-lang/src/instruction/get_local.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/jump.rs b/dust-lang/src/instruction/jump.rs index 557a44f..66ced41 100644 --- a/dust-lang/src/instruction/jump.rs +++ b/dust-lang/src/instruction/jump.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/less.rs b/dust-lang/src/instruction/less.rs index a62120c..4c03d60 100644 --- a/dust-lang/src/instruction/less.rs +++ b/dust-lang/src/instruction/less.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/less_equal.rs b/dust-lang/src/instruction/less_equal.rs index 28cea77..9cd8445 100644 --- a/dust-lang/src/instruction/less_equal.rs +++ b/dust-lang/src/instruction/less_equal.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/load_boolean.rs b/dust-lang/src/instruction/load_boolean.rs index e5f1dcb..b224cf9 100644 --- a/dust-lang/src/instruction/load_boolean.rs +++ b/dust-lang/src/instruction/load_boolean.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/load_constant.rs b/dust-lang/src/instruction/load_constant.rs index 8b8826a..7f5da34 100644 --- a/dust-lang/src/instruction/load_constant.rs +++ b/dust-lang/src/instruction/load_constant.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/load_list.rs b/dust-lang/src/instruction/load_list.rs index 43df68c..b3ca73d 100644 --- a/dust-lang/src/instruction/load_list.rs +++ b/dust-lang/src/instruction/load_list.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/load_self.rs b/dust-lang/src/instruction/load_self.rs index aa4293e..2c53c2d 100644 --- a/dust-lang/src/instruction/load_self.rs +++ b/dust-lang/src/instruction/load_self.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index b4ac282..a3db521 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -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 { 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::(), 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); - } -} diff --git a/dust-lang/src/instruction/modulo.rs b/dust-lang/src/instruction/modulo.rs index ac3b04f..6435047 100644 --- a/dust-lang/src/instruction/modulo.rs +++ b/dust-lang/src/instruction/modulo.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/move.rs b/dust-lang/src/instruction/move.rs index 8eed616..1625528 100644 --- a/dust-lang/src/instruction/move.rs +++ b/dust-lang/src/instruction/move.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/multiply.rs b/dust-lang/src/instruction/multiply.rs index 53af4a6..4919377 100644 --- a/dust-lang/src/instruction/multiply.rs +++ b/dust-lang/src/instruction/multiply.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/negate.rs b/dust-lang/src/instruction/negate.rs index b1db5d3..894bd52 100644 --- a/dust-lang/src/instruction/negate.rs +++ b/dust-lang/src/instruction/negate.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/not.rs b/dust-lang/src/instruction/not.rs index 156d059..79fb2ab 100644 --- a/dust-lang/src/instruction/not.rs +++ b/dust-lang/src/instruction/not.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/options.rs b/dust-lang/src/instruction/options.rs deleted file mode 100644 index 8c1f4d4..0000000 --- a/dust-lang/src/instruction/options.rs +++ /dev/null @@ -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) - } -} diff --git a/dust-lang/src/instruction/return.rs b/dust-lang/src/instruction/return.rs index 1c287d7..99427c3 100644 --- a/dust-lang/src/instruction/return.rs +++ b/dust-lang/src/instruction/return.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/set_local.rs b/dust-lang/src/instruction/set_local.rs index 317f63c..cfe1609 100644 --- a/dust-lang/src/instruction/set_local.rs +++ b/dust-lang/src/instruction/set_local.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/subtract.rs b/dust-lang/src/instruction/subtract.rs index 6fb32a8..5805c50 100644 --- a/dust-lang/src/instruction/subtract.rs +++ b/dust-lang/src/instruction/subtract.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/test.rs b/dust-lang/src/instruction/test.rs index 5871187..8cbb0ff 100644 --- a/dust-lang/src/instruction/test.rs +++ b/dust-lang/src/instruction/test.rs @@ -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 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) } } diff --git a/dust-lang/src/instruction/test_set.rs b/dust-lang/src/instruction/test_set.rs index 3b4914b..608306c 100644 --- a/dust-lang/src/instruction/test_set.rs +++ b/dust-lang/src/instruction/test_set.rs @@ -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 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) } } diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index 685f1fa..c66015c 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -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 { diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 10908fd..0c7f7cc 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -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}; diff --git a/dust-lang/src/token.rs b/dust-lang/src/token.rs index fa75942..2a74872 100644 --- a/dust-lang/src/token.rs +++ b/dust-lang/src/token.rs @@ -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, "->"), diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index ca8a945..8f5d131 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -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, DustError> { @@ -24,7 +23,7 @@ pub fn run(source: &str) -> Result, DustError> { /// See the [module-level documentation](index.html) for more information. #[derive(Debug)] pub struct Vm<'a> { - stack: Slab, + stack: Vec, 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, 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 { 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 { - let value_ref = match argument { - Argument::Constant(constant_index) => { - ValueRef::Concrete(self.get_constant(constant_index)) - } - Argument::Register(register_index) => self.open_register(register_index)?, - }; - - Ok(value_ref) + fn get_argument(&self, index: u8, is_constant: bool) -> Result { + if is_constant { + Ok(self.get_constant(index).to_value_ref()) + } else { + Ok(self.open_register(index)?) + } } 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]> {