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

View File

@ -23,7 +23,6 @@ smartstring = { version = "1.0.1", features = [
"serde", "serde",
], default-features = false } ], default-features = false }
bitflags = { version = "2.6.0", features = ["serde"] } bitflags = { version = "2.6.0", features = ["serde"] }
slab = "0.4.9"
[dev-dependencies] [dev-dependencies]
criterion = { version = "0.3.4", features = ["html_reports"] } criterion = { version = "0.3.4", features = ["html_reports"] }
@ -32,6 +31,10 @@ criterion = { version = "0.3.4", features = ["html_reports"] }
name = "addictive_addition" name = "addictive_addition"
harness = false harness = false
[[bench]]
name = "fibonacci"
harness = false
[[test]] [[test]]
name = "logic_and" name = "logic_and"
path = "tests/logic/and.rs" 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 criterion::{black_box, criterion_group, criterion_main, Criterion};
use dust_lang::run; use dust_lang::run;
const SOURCE: &str = " const SOURCE: &str = r"
let mut i = 0 let mut i = 0
while i < 5_000_000 { 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!( let info_line = format!(
"{} instructions, {} constants, {} locals, returns {}", "{} instructions, {} constants, {} locals, returns {}",
self.chunk.len(), self.chunk.instructions.len(),
self.chunk.constants().len(), self.chunk.constants.len(),
self.chunk.locals().len(), self.chunk.locals.len(),
self.chunk.r#type().return_type self.chunk.r#type.return_type
); );
self.write_centered_with_border_dim(&info_line)?; self.write_centered_with_border_dim(&info_line)?;

View File

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

View File

@ -60,6 +60,7 @@ pub struct Compiler<'src> {
instructions: SmallVec<[(Instruction, Type, Span); 32]>, instructions: SmallVec<[(Instruction, Type, Span); 32]>,
constants: SmallVec<[ConcreteValue; 16]>, constants: SmallVec<[ConcreteValue; 16]>,
locals: SmallVec<[(Local, Type); 8]>, locals: SmallVec<[(Local, Type); 8]>,
stack_size: usize,
lexer: Lexer<'src>, lexer: Lexer<'src>,
@ -89,6 +90,7 @@ impl<'src> Compiler<'src> {
instructions: SmallVec::new(), instructions: SmallVec::new(),
constants: SmallVec::new(), constants: SmallVec::new(),
locals: SmallVec::new(), locals: SmallVec::new(),
stack_size: 0,
lexer, lexer,
current_token, current_token,
current_position, current_position,
@ -124,7 +126,14 @@ impl<'src> Compiler<'src> {
.map(|(local, _)| local) .map(|(local, _)| local)
.collect::<SmallVec<[Local; 8]>>(); .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> { pub fn compile(&mut self) -> Result<(), CompileError> {
@ -151,7 +160,7 @@ impl<'src> Compiler<'src> {
.rev() .rev()
.find_map(|(instruction, _, _)| { .find_map(|(instruction, _, _)| {
if instruction.yields_value() { if instruction.yields_value() {
Some(instruction.a + 1) Some(instruction.a_field() + 1)
} else { } else {
None None
} }
@ -381,6 +390,12 @@ impl<'src> Compiler<'src> {
position.to_string() 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)); self.instructions.push((instruction, r#type, position));
} }
@ -597,9 +612,9 @@ impl<'src> Compiler<'src> {
instruction: &Instruction, instruction: &Instruction,
) -> Result<(Argument, bool), CompileError> { ) -> Result<(Argument, bool), CompileError> {
let (argument, push_back) = match instruction.operation() { let (argument, push_back) = match instruction.operation() {
Operation::LoadConstant => (Argument::Constant(instruction.b), false), Operation::LoadConstant => (Argument::Constant(instruction.b_field()), false),
Operation::GetLocal => { Operation::GetLocal => {
let local_index = instruction.b; let local_index = instruction.b_field();
let (local, _) = self.get_local(local_index)?; let (local, _) = self.get_local(local_index)?;
(Argument::Register(local.register_index), false) (Argument::Register(local.register_index), false)
@ -617,12 +632,12 @@ impl<'src> Compiler<'src> {
| Operation::LessEqual | Operation::LessEqual
| Operation::Negate | Operation::Negate
| Operation::Not | Operation::Not
| Operation::Call => (Argument::Register(instruction.a), true), | Operation::Call => (Argument::Register(instruction.a_field()), true),
Operation::CallNative => { Operation::CallNative => {
let function = NativeFunction::from(instruction.b); let function = NativeFunction::from(instruction.b_field());
if function.returns_value() { if function.returns_value() {
(Argument::Register(instruction.a), true) (Argument::Register(instruction.a_field()), true)
} else { } else {
return Err(CompileError::ExpectedExpression { return Err(CompileError::ExpectedExpression {
found: self.previous_token.to_owned(), found: self.previous_token.to_owned(),
@ -882,9 +897,9 @@ impl<'src> Compiler<'src> {
.iter() .iter()
.rev() .rev()
.nth(2) .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); let jump_index = self.instructions.len().saturating_sub(1);
@ -932,7 +947,7 @@ impl<'src> Compiler<'src> {
self.parse_sub_expression(&rule.precedence)?; self.parse_sub_expression(&rule.precedence)?;
let (mut right_instruction, _, _) = self.instructions.last_mut().unwrap(); 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 { if is_logic_chain {
let expression_length = self.instructions.len() - jump_index - 1; let expression_length = self.instructions.len() - jump_index - 1;
@ -1005,11 +1020,11 @@ impl<'src> Compiler<'src> {
if self if self
.instructions .instructions
.last() .last()
.map_or(false, |(instruction, _, _)| instruction.is_math()) .is_some_and(|(instruction, _, _)| instruction.is_math())
{ {
let (math_instruction, _, _) = self.instructions.last_mut().unwrap(); let (math_instruction, _, _) = self.instructions.last_mut().unwrap();
math_instruction.a = local_register_index; math_instruction.set_a_field(local_register_index);
} else { } else {
let register = self.next_register() - 1; let register = self.next_register() - 1;
let set_local = Instruction::from(SetLocal { let set_local = Instruction::from(SetLocal {
@ -1194,7 +1209,7 @@ impl<'src> Compiler<'src> {
{ {
let (mut loader, _, _) = self.instructions.last_mut().unwrap(); let (mut loader, _, _) = self.instructions.last_mut().unwrap();
loader.c = true as u8; loader.set_c_field(true as u8);
} else { } else {
if_block_distance += 1; if_block_distance += 1;
let jump = Instruction::from(Jump { 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 first_loader = compiler.instructions.iter().nth_back(1).unwrap();
let second_loader = compiler.instructions.last().unwrap(); let second_loader = compiler.instructions.last().unwrap();
let first_boolean = first_loader.0.b != 0; let first_boolean = first_loader.0.b_field() != 0;
let second_boolean = second_loader.0.b != 0; let second_boolean = second_loader.0.b_field() != 0;
if first_boolean && !second_boolean { if first_boolean && !second_boolean {
compiler.instructions.pop(); 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; 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; 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 { impl From<&Instruction> for Add {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Add { Add {
@ -23,10 +23,9 @@ impl From<Add> for Instruction {
fn from(add: Add) -> Self { fn from(add: Add) -> Self {
let operation = Operation::Add; let operation = Operation::Add;
let a = add.destination; let a = add.destination;
let (b, b_options) = add.left.as_index_and_b_options(); let (b, b_is_constant) = add.left.as_index_and_constant_flag();
let (c, c_options) = add.right.as_index_and_c_options(); let (c, c_is_constant) = add.right.as_index_and_constant_flag();
let metadata = operation as u8 | b_options.bits() | c_options.bits();
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 { impl From<&Instruction> for Call {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let function = instruction.b_as_argument(); let function = instruction.b_as_argument();
let argument_count = instruction.c; let argument_count = instruction.c_field();
Call { Call {
destination, destination,
@ -23,10 +23,9 @@ impl From<&Instruction> for Call {
impl From<Call> for Instruction { impl From<Call> for Instruction {
fn from(call: Call) -> Self { fn from(call: Call) -> Self {
let a = call.destination; 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 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 { impl From<&Instruction> for CallNative {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let function = NativeFunction::from(instruction.b); let function = NativeFunction::from(instruction.b_field());
CallNative { CallNative {
destination, destination,
function, function,
argument_count: instruction.c, argument_count: instruction.c_field(),
} }
} }
} }
impl From<CallNative> for Instruction { impl From<CallNative> for Instruction {
fn from(call_native: CallNative) -> Self { fn from(call_native: CallNative) -> Self {
let metadata = Operation::CallNative as u8; let operation = Operation::CallNative;
let a = call_native.destination; let a = call_native.destination;
let b = call_native.function as u8; let b = call_native.function as u8;
let c = call_native.argument_count; 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 { impl From<&Instruction> for Close {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
Close { Close {
from: instruction.b, from: instruction.b_field(),
to: instruction.c, to: instruction.c_field(),
} }
} }
} }
impl From<Close> for Instruction { impl From<Close> for Instruction {
fn from(close: Close) -> Self { fn from(close: Close) -> Self {
let metadata = Operation::Close as u8; let operation = Operation::Close;
let (a, b, c) = (0, close.from, close.to); 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 { impl From<&Instruction> for Divide {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Divide { Divide {
@ -21,11 +21,11 @@ impl From<&Instruction> for Divide {
impl From<Divide> for Instruction { impl From<Divide> for Instruction {
fn from(divide: Divide) -> Self { fn from(divide: Divide) -> Self {
let operation = Operation::Divide;
let a = divide.destination; let a = divide.destination;
let (b, b_options) = divide.left.as_index_and_b_options(); let (b, b_is_constant) = divide.left.as_index_and_constant_flag();
let (c, c_options) = divide.right.as_index_and_c_options(); let (c, c_is_constant) = divide.right.as_index_and_constant_flag();
let metadata = Operation::Divide as u8 | b_options.bits() | c_options.bits();
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 crate::{Argument, Instruction, Operation};
use super::InstructionOptions;
pub struct Equal { pub struct Equal {
pub destination: u8, pub destination: u8,
pub value: bool, pub value: bool,
@ -11,8 +9,8 @@ pub struct Equal {
impl From<&Instruction> for Equal { impl From<&Instruction> for Equal {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let value = instruction.d(); let value = instruction.d_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Equal { Equal {
@ -26,17 +24,12 @@ impl From<&Instruction> for Equal {
impl From<Equal> for Instruction { impl From<Equal> for Instruction {
fn from(equal: Equal) -> Self { fn from(equal: Equal) -> Self {
let operation = Operation::Equal;
let a = equal.destination; let a = equal.destination;
let (b, b_options) = equal.left.as_index_and_b_options(); let (b, b_is_constant) = equal.left.as_index_and_constant_flag();
let (c, c_options) = equal.right.as_index_and_c_options(); let (c, c_is_constant) = equal.right.as_index_and_constant_flag();
let d_options = if equal.value { let d = equal.value;
InstructionOptions::D_IS_TRUE
} else {
InstructionOptions::empty()
};
let metadata =
Operation::Equal as u8 | b_options.bits() | c_options.bits() | d_options.bits();
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 { impl From<&Instruction> for GetLocal {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let local_index = instruction.b; let local_index = instruction.b_field();
GetLocal { GetLocal {
destination, destination,
@ -19,11 +19,10 @@ impl From<&Instruction> for GetLocal {
impl From<GetLocal> for Instruction { impl From<GetLocal> for Instruction {
fn from(get_local: GetLocal) -> Self { fn from(get_local: GetLocal) -> Self {
let operation = Operation::GetLocal;
let a = get_local.destination; let a = get_local.destination;
let b = get_local.local_index; 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 { impl From<&Instruction> for Jump {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
Jump { Jump {
offset: instruction.b, offset: instruction.b_field(),
is_positive: instruction.c != 0, is_positive: instruction.c_field() != 0,
} }
} }
} }
impl From<Jump> for Instruction { impl From<Jump> for Instruction {
fn from(jump: Jump) -> Self { fn from(jump: Jump) -> Self {
let metadata = Operation::Jump as u8; let operation = Operation::Jump;
let a = 0;
let b = jump.offset; let b = jump.offset;
let c = jump.is_positive as u8; 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 crate::{Argument, Instruction, Operation};
use super::InstructionOptions;
pub struct Less { pub struct Less {
pub destination: u8, pub destination: u8,
pub value: bool, pub value: bool,
@ -11,8 +9,8 @@ pub struct Less {
impl From<&Instruction> for Less { impl From<&Instruction> for Less {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let value = instruction.d(); let value = instruction.d_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Less { Less {
@ -26,17 +24,12 @@ impl From<&Instruction> for Less {
impl From<Less> for Instruction { impl From<Less> for Instruction {
fn from(less: Less) -> Self { fn from(less: Less) -> Self {
let operation = Operation::Less;
let a = less.destination; let a = less.destination;
let (b, b_options) = less.left.as_index_and_b_options(); let (b, b_is_constant) = less.left.as_index_and_constant_flag();
let (c, c_options) = less.right.as_index_and_c_options(); let (c, c_is_constant) = less.right.as_index_and_constant_flag();
let d_options = if less.value { let d = less.value;
InstructionOptions::D_IS_TRUE
} else {
InstructionOptions::empty()
};
let metadata =
Operation::Less as u8 | b_options.bits() | c_options.bits() | d_options.bits();
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 crate::{Argument, Instruction, Operation};
use super::InstructionOptions;
pub struct LessEqual { pub struct LessEqual {
pub destination: u8, pub destination: u8,
pub value: bool, pub value: bool,
@ -11,8 +9,8 @@ pub struct LessEqual {
impl From<&Instruction> for LessEqual { impl From<&Instruction> for LessEqual {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let value = instruction.d(); let value = instruction.d_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
LessEqual { LessEqual {
@ -26,17 +24,12 @@ impl From<&Instruction> for LessEqual {
impl From<LessEqual> for Instruction { impl From<LessEqual> for Instruction {
fn from(less_equal: LessEqual) -> Self { fn from(less_equal: LessEqual) -> Self {
let operation = Operation::LessEqual;
let a = less_equal.destination; let a = less_equal.destination;
let (b, b_options) = less_equal.left.as_index_and_b_options(); let (b, b_options) = less_equal.left.as_index_and_constant_flag();
let (c, c_options) = less_equal.right.as_index_and_c_options(); let (c, c_options) = less_equal.right.as_index_and_constant_flag();
let d_options = if less_equal.value { let d = 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();
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 { impl From<&Instruction> for LoadBoolean {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let value = instruction.b != 0; let value = instruction.b_field() != 0;
let jump_next = instruction.c != 0; let jump_next = instruction.c_field() != 0;
LoadBoolean { LoadBoolean {
destination, destination,
@ -22,11 +22,11 @@ impl From<&Instruction> for LoadBoolean {
impl From<LoadBoolean> for Instruction { impl From<LoadBoolean> for Instruction {
fn from(load_boolean: LoadBoolean) -> Self { fn from(load_boolean: LoadBoolean) -> Self {
let metadata = Operation::LoadBoolean as u8; let operation = Operation::LoadBoolean;
let a = load_boolean.destination; let a = load_boolean.destination;
let b = load_boolean.value as u8; let b = load_boolean.value as u8;
let c = load_boolean.jump_next 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 { impl From<&Instruction> for LoadConstant {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let constant_index = instruction.b; let constant_index = instruction.b_field();
let jump_next = instruction.c != 0; let jump_next = instruction.c_field() != 0;
LoadConstant { LoadConstant {
destination, destination,
@ -22,11 +22,11 @@ impl From<&Instruction> for LoadConstant {
impl From<LoadConstant> for Instruction { impl From<LoadConstant> for Instruction {
fn from(load_constant: LoadConstant) -> Self { fn from(load_constant: LoadConstant) -> Self {
let metadata = Operation::LoadConstant as u8; let operation = Operation::LoadConstant;
let a = load_constant.destination; let a = load_constant.destination;
let b = load_constant.constant_index; let b = load_constant.constant_index;
let c = load_constant.jump_next as u8; 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 { impl From<&Instruction> for LoadList {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let start_register = instruction.b; let start_register = instruction.b_field();
LoadList { LoadList {
destination, destination,
@ -19,11 +19,10 @@ impl From<&Instruction> for LoadList {
impl From<LoadList> for Instruction { impl From<LoadList> for Instruction {
fn from(load_list: LoadList) -> Self { fn from(load_list: LoadList) -> Self {
let metadata = Operation::LoadList as u8; let operation = Operation::LoadList;
let a = load_list.destination; let a = load_list.destination;
let b = load_list.start_register; 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 { impl From<&Instruction> for LoadSelf {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
LoadSelf { destination } LoadSelf { destination }
} }
@ -14,11 +14,9 @@ impl From<&Instruction> for LoadSelf {
impl From<LoadSelf> for Instruction { impl From<LoadSelf> for Instruction {
fn from(load_self: LoadSelf) -> Self { fn from(load_self: LoadSelf) -> Self {
let metadata = Operation::LoadSelf as u8; let operation = Operation::LoadSelf;
let a = load_self.destination; 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 negate;
mod not; mod not;
mod operation; mod operation;
mod options;
mod r#return; mod r#return;
mod set_local; mod set_local;
mod subtract; mod subtract;
@ -135,7 +134,6 @@ pub use multiply::Multiply;
pub use negate::Negate; pub use negate::Negate;
pub use not::Not; pub use not::Not;
pub use operation::Operation; pub use operation::Operation;
pub use options::InstructionOptions;
pub use r#move::Move; pub use r#move::Move;
pub use r#return::Return; pub use r#return::Return;
pub use set_local::SetLocal; pub use set_local::SetLocal;
@ -152,22 +150,92 @@ use crate::NativeFunction;
/// ///
/// See the [module-level documentation](index.html) for more information. /// See the [module-level documentation](index.html) for more information.
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Instruction { pub struct Instruction(u32);
pub metadata: u8,
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct InstructionData {
pub operation: Operation,
pub a: u8, pub a: u8,
pub b: u8, pub b: u8,
pub c: u8, pub c: u8,
pub b_is_constant: bool,
pub c_is_constant: bool,
pub d: bool,
} }
impl Instruction { impl Instruction {
pub fn operation(&self) -> Operation { pub fn new(
let operation_bits = self.metadata & 0b0001_1111; 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 { pub fn operation(&self) -> Operation {
self.metadata & 0b1000_0000 != 0 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 { pub fn r#move(from: u8, to: u8) -> Instruction {
@ -373,7 +441,7 @@ impl Instruction {
pub fn as_argument(&self) -> Option<Argument> { pub fn as_argument(&self) -> Option<Argument> {
match self.operation() { match self.operation() {
Operation::LoadConstant => Some(Argument::Constant(self.b)), Operation::LoadConstant => Some(Argument::Constant(self.b_field())),
Operation::LoadBoolean Operation::LoadBoolean
| Operation::LoadList | Operation::LoadList
| Operation::LoadSelf | Operation::LoadSelf
@ -388,12 +456,12 @@ impl Instruction {
| Operation::LessEqual | Operation::LessEqual
| Operation::Negate | Operation::Negate
| Operation::Not | Operation::Not
| Operation::Call => Some(Argument::Register(self.a)), | Operation::Call => Some(Argument::Register(self.a_field())),
Operation::CallNative => { Operation::CallNative => {
let function = NativeFunction::from(self.b); let function = NativeFunction::from(self.b_field());
if function.returns_value() { if function.returns_value() {
Some(Argument::Register(self.a)) Some(Argument::Register(self.a_field()))
} else { } else {
None None
} }
@ -403,28 +471,23 @@ impl Instruction {
} }
pub fn b_as_argument(&self) -> Argument { pub fn b_as_argument(&self) -> Argument {
let b_is_constant = self.metadata & 0b0010_0000 != 0; if self.b_is_constant() {
Argument::Constant(self.b_field())
if b_is_constant {
Argument::Constant(self.b)
} else { } else {
Argument::Register(self.b) Argument::Register(self.b_field())
} }
} }
pub fn b_and_c_as_arguments(&self) -> (Argument, Argument) { pub fn b_and_c_as_arguments(&self) -> (Argument, Argument) {
let b_is_constant = self.metadata & 0b0010_0000 != 0; let left = if self.b_is_constant() {
let c_is_constant = self.metadata & 0b0100_0000 != 0; Argument::Constant(self.b_field())
let left = if b_is_constant {
Argument::Constant(self.b)
} else { } else {
Argument::Register(self.b) Argument::Register(self.b_field())
}; };
let right = if c_is_constant { let right = if self.c_is_constant() {
Argument::Constant(self.c) Argument::Constant(self.c_field())
} else { } else {
Argument::Register(self.c) Argument::Register(self.c_field())
}; };
(left, right) (left, right)
@ -451,7 +514,7 @@ impl Instruction {
| Operation::LessEqual | Operation::LessEqual
| Operation::Call => true, | Operation::Call => true,
Operation::CallNative => { Operation::CallNative => {
let function = NativeFunction::from(self.b); let function = NativeFunction::from(self.b_field());
function.returns_value() function.returns_value()
} }
@ -655,7 +718,7 @@ impl Instruction {
if is_positive { if is_positive {
format!("JUMP +{offset}") format!("JUMP +{offset}")
} else { } else {
format!("JUMP -{}", offset - 1) format!("JUMP -{offset}")
} }
} }
Operation::Call => { Operation::Call => {
@ -751,17 +814,10 @@ impl Argument {
matches!(self, Argument::Register(_)) 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 { match self {
Argument::Constant(index) => (*index, InstructionOptions::B_IS_CONSTANT), Argument::Constant(index) => (*index, true),
Argument::Register(index) => (*index, InstructionOptions::empty()), Argument::Register(index) => (*index, false),
}
}
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()),
} }
} }
} }
@ -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 { impl From<&Instruction> for Modulo {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Modulo { Modulo {
@ -21,11 +21,11 @@ impl From<&Instruction> for Modulo {
impl From<Modulo> for Instruction { impl From<Modulo> for Instruction {
fn from(modulo: Modulo) -> Self { fn from(modulo: Modulo) -> Self {
let operation = Operation::Modulo;
let a = modulo.destination; let a = modulo.destination;
let (b, b_options) = modulo.left.as_index_and_b_options(); let (b, b_is_constant) = modulo.left.as_index_and_constant_flag();
let (c, c_options) = modulo.right.as_index_and_c_options(); let (c, c_is_constant) = modulo.right.as_index_and_constant_flag();
let metadata = Operation::Modulo as u8 | b_options.bits() | c_options.bits();
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 { impl From<&Instruction> for Move {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
Move { Move {
from: instruction.b, from: instruction.b_field(),
to: instruction.c, to: instruction.c_field(),
} }
} }
} }
impl From<Move> for Instruction { impl From<Move> for Instruction {
fn from(r#move: Move) -> Self { fn from(r#move: Move) -> Self {
let metadata = Operation::Move as u8; let operation = Operation::Move;
let a = 0;
let b = r#move.from; let b = r#move.from;
let c = r#move.to; 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 { impl From<&Instruction> for Multiply {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Multiply { Multiply {
@ -21,11 +21,11 @@ impl From<&Instruction> for Multiply {
impl From<Multiply> for Instruction { impl From<Multiply> for Instruction {
fn from(multiply: Multiply) -> Self { fn from(multiply: Multiply) -> Self {
let operation = Operation::Multiply;
let a = multiply.destination; let a = multiply.destination;
let (b, b_options) = multiply.left.as_index_and_b_options(); let (b, b_options) = multiply.left.as_index_and_constant_flag();
let (c, c_options) = multiply.right.as_index_and_c_options(); let (c, c_options) = multiply.right.as_index_and_constant_flag();
let metadata = Operation::Multiply as u8 | b_options.bits() | c_options.bits();
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 { impl From<&Instruction> for Negate {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let argument = instruction.b_as_argument(); let argument = instruction.b_as_argument();
Negate { Negate {
@ -19,11 +19,11 @@ impl From<&Instruction> for Negate {
impl From<Negate> for Instruction { impl From<Negate> for Instruction {
fn from(negate: Negate) -> Self { fn from(negate: Negate) -> Self {
let operation = Operation::Negate;
let a = negate.destination; 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 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 { impl From<&Instruction> for Not {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let argument = instruction.b_as_argument(); let argument = instruction.b_as_argument();
Not { Not {
@ -19,11 +19,10 @@ impl From<&Instruction> for Not {
impl From<Not> for Instruction { impl From<Not> for Instruction {
fn from(not: Not) -> Self { fn from(not: Not) -> Self {
let operation = Operation::Not;
let a = not.destination; let a = not.destination;
let (b, b_options) = not.argument.as_index_and_b_options(); let (b, b_is_constant) = not.argument.as_index_and_constant_flag();
let c = 0;
let metadata = Operation::Not as u8 | b_options.bits();
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 { impl From<&Instruction> for Return {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let should_return_value = instruction.b != 0; let should_return_value = instruction.b_field() != 0;
Return { Return {
should_return_value, should_return_value,
@ -16,11 +16,9 @@ impl From<&Instruction> for Return {
impl From<Return> for Instruction { impl From<Return> for Instruction {
fn from(r#return: Return) -> Self { fn from(r#return: Return) -> Self {
let metadata = Operation::Return as u8; let operation = Operation::Return;
let a = 0;
let b = r#return.should_return_value as u8; 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 { impl From<&Instruction> for SetLocal {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let register_index = instruction.b; let register_index = instruction.b_field();
let local_index = instruction.c; let local_index = instruction.c_field();
SetLocal { SetLocal {
register_index, register_index,
@ -19,11 +19,10 @@ impl From<&Instruction> for SetLocal {
impl From<SetLocal> for Instruction { impl From<SetLocal> for Instruction {
fn from(set_local: SetLocal) -> Self { fn from(set_local: SetLocal) -> Self {
let metadata = Operation::SetLocal as u8; let operation = Operation::SetLocal;
let a = 0;
let b = set_local.register_index; let b = set_local.register_index;
let c = set_local.local_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 { impl From<&Instruction> for Subtract {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let (left, right) = instruction.b_and_c_as_arguments(); let (left, right) = instruction.b_and_c_as_arguments();
Subtract { Subtract {
@ -21,11 +21,11 @@ impl From<&Instruction> for Subtract {
impl From<Subtract> for Instruction { impl From<Subtract> for Instruction {
fn from(subtract: Subtract) -> Self { fn from(subtract: Subtract) -> Self {
let operation = Operation::Subtract;
let a = subtract.destination; let a = subtract.destination;
let (b, b_options) = subtract.left.as_index_and_b_options(); let (b, b_is_constant) = subtract.left.as_index_and_constant_flag();
let (c, c_options) = subtract.right.as_index_and_c_options(); let (c, c_is_constant) = subtract.right.as_index_and_constant_flag();
let metadata = Operation::Subtract as u8 | b_options.bits() | c_options.bits();
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 { impl From<&Instruction> for Test {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let argument = instruction.b_as_argument(); let argument = instruction.b_as_argument();
let test_value = instruction.c != 0; let test_value = instruction.c_field() != 0;
Test { Test {
argument, argument,
@ -19,11 +19,10 @@ impl From<&Instruction> for Test {
impl From<Test> for Instruction { impl From<Test> for Instruction {
fn from(test: Test) -> Self { fn from(test: Test) -> Self {
let a = 0; let operation = Operation::Test;
let (b, options) = test.argument.as_index_and_b_options(); let (b, b_is_constant) = test.argument.as_index_and_constant_flag();
let c = test.test_value as u8; 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 { impl From<&Instruction> for TestSet {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a; let destination = instruction.a_field();
let argument = instruction.b_as_argument(); let argument = instruction.b_as_argument();
let test_value = instruction.c != 0; let test_value = instruction.c_field() != 0;
TestSet { TestSet {
destination, destination,
@ -22,11 +22,11 @@ impl From<&Instruction> for TestSet {
impl From<TestSet> for Instruction { impl From<TestSet> for Instruction {
fn from(test_set: TestSet) -> Self { fn from(test_set: TestSet) -> Self {
let operation = Operation::Test;
let a = test_set.destination; 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 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>, lexer: LexerFn<'src>,
} }
impl<'src> From<&char> for LexRule<'src> { impl From<&char> for LexRule<'_> {
fn from(char: &char) -> Self { fn from(char: &char) -> Self {
match char { match char {
'0'..='9' => LexRule { '0'..='9' => LexRule {

View File

@ -42,7 +42,7 @@ pub mod vm;
pub use crate::chunk::{Chunk, Disassembler, Local, Scope}; pub use crate::chunk::{Chunk, Disassembler, Local, Scope};
pub use crate::compiler::{compile, CompileError, Compiler}; pub use crate::compiler::{compile, CompileError, Compiler};
pub use crate::dust_error::{AnnotatedError, DustError}; 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::lexer::{lex, LexError, Lexer};
pub use crate::native_function::{NativeFunction, NativeFunctionError}; pub use crate::native_function::{NativeFunction, NativeFunctionError};
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict}; pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};

View File

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

View File

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