diff --git a/README.md b/README.md index ea6ad90..cbc25e5 100644 --- a/README.md +++ b/README.md @@ -137,20 +137,23 @@ The compiler always checks types on the fly, so there is no need for a separate ### Instructions -Dust's virtual machine is register-based and uses 64-bit instructions, which encode nine pieces of +Dust's virtual machine is register-based and uses 64-bit instructions, which encode ten pieces of information: Bit | Description ----- | ----------- -0-8 | The operation code. -9 | Boolean flag indicating whether the second argument is a constant -10 | Boolean flag indicating whether the third argument is a constant -11 | Boolean flag indicating whether the first argument is a local -12 | Boolean flag indicating whether the second argument is a local -13 | Boolean flag indicating whether the third argument is a local -17-32 | First argument, usually the destination register or local where a value is stored -33-48 | Second argument, a register, local, constant or boolean flag -49-63 | Third argument, a register, local, constant or boolean flag +0-5 | Operation code +6-8 | Unused, reserved in case more operation codes are needed +9 | Flag indicating that A is a local +10 | Flag indicating that B is a constant +11 | Flag indicating that B is a local +12 | Flag indicating that C is a constant +13 | Flag indicating that C is a local +14 | D Argument (boolean value) +15-16 | Unused +17-32 | A argument (unsigned 16-bit integer) +33-48 | B argument (unsigned 16-bit integer) +49-63 | C argument (unsigned 16-bit integer) Because the instructions are 64 bits, the maximum number of registers is 2^16, which is more than enough, even for programs that are very large. This also means that chunks can store up to 2^16 diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 3a113fc..9107b12 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -20,8 +20,7 @@ pub struct Chunk { name: Option, r#type: FunctionType, - instructions: SmallVec<[Instruction; 32]>, - positions: SmallVec<[Span; 32]>, + instructions: SmallVec<[(Instruction, Span); 32]>, constants: SmallVec<[ConcreteValue; 16]>, locals: SmallVec<[Local; 8]>, } @@ -31,7 +30,6 @@ impl Chunk { Self { name, instructions: SmallVec::new(), - positions: SmallVec::new(), constants: SmallVec::new(), locals: SmallVec::new(), r#type: FunctionType { @@ -45,8 +43,7 @@ impl Chunk { pub fn with_data( name: Option, r#type: FunctionType, - instructions: SmallVec<[Instruction; 32]>, - positions: SmallVec<[Span; 32]>, + instructions: SmallVec<[(Instruction, Span); 32]>, constants: SmallVec<[ConcreteValue; 16]>, locals: SmallVec<[Local; 8]>, ) -> Self { @@ -54,7 +51,6 @@ impl Chunk { name, r#type, instructions, - positions, constants, locals, } @@ -80,14 +76,10 @@ impl Chunk { &self.constants } - pub fn instructions(&self) -> &SmallVec<[Instruction; 32]> { + pub fn instructions(&self) -> &SmallVec<[(Instruction, Span); 32]> { &self.instructions } - pub fn positions(&self) -> &SmallVec<[Span; 32]> { - &self.positions - } - pub fn locals(&self) -> &SmallVec<[Local; 8]> { &self.locals } @@ -96,9 +88,9 @@ impl Chunk { self.instructions() .iter() .rev() - .find_map(|instruction| { + .find_map(|(instruction, _)| { if instruction.yields_value() { - Some(instruction.a() as usize + 1) + Some(instruction.a as usize + 1) } else { None } @@ -145,10 +137,10 @@ impl PartialEq for Chunk { #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Local { /// The index of the identifier in the constants table. - pub identifier_index: u16, + pub identifier_index: u8, - /// The expected type of the local's value. - pub r#type: Type, + /// Stack index where the local's value is stored. + pub register_index: u8, /// Whether the local is mutable. pub is_mutable: bool, @@ -159,11 +151,11 @@ pub struct Local { impl Local { /// Creates a new Local instance. - pub fn new(identifier_index: u16, r#type: Type, mutable: bool, scope: Scope) -> Self { + pub fn new(identifier_index: u8, register_index: u8, is_mutable: bool, scope: Scope) -> Self { Self { identifier_index, - r#type, - is_mutable: mutable, + register_index, + is_mutable, scope, } } diff --git a/dust-lang/src/compiler.rs b/dust-lang/src/compiler/mod.rs similarity index 91% rename from dust-lang/src/compiler.rs rename to dust-lang/src/compiler/mod.rs index 57ece13..2434839 100644 --- a/dust-lang/src/compiler.rs +++ b/dust-lang/src/compiler/mod.rs @@ -4,6 +4,8 @@ //! - [`compile`] borrows a string and returns a chunk, handling the entire compilation process and //! turning any resulting [`ComplileError`] into a [`DustError`]. //! - [`Compiler`] uses a lexer to get tokens and assembles a chunk. +mod optimize; + use std::{ fmt::{self, Display, Formatter}, mem::replace, @@ -11,16 +13,17 @@ use std::{ }; use colored::Colorize; +use optimize::{optimize_control_flow, optimize_set_local}; use smallvec::{smallvec, SmallVec}; use crate::{ instruction::{ - Call, CallNative, Close, DefineLocal, GetLocal, Jump, LoadBoolean, LoadConstant, LoadList, - LoadSelf, Move, Negate, Not, Return, SetLocal, Test, + Call, CallNative, Close, GetLocal, Jump, LoadConstant, LoadList, LoadSelf, Move, Negate, + Not, Return, SetLocal, Test, }, - optimize_control_flow, optimize_set_local, AnnotatedError, Argument, Chunk, ConcreteValue, - Destination, DustError, DustString, FunctionType, Instruction, LexError, Lexer, Local, - NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type, TypeConflict, + AnnotatedError, Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType, + Instruction, LexError, Lexer, Local, NativeFunction, Operation, Scope, Span, Token, TokenKind, + TokenOwned, Type, TypeConflict, }; /// Compiles the input and returns a chunk. @@ -32,7 +35,7 @@ use crate::{ /// let source = "40 + 2 == 42"; /// let chunk = compile(source).unwrap(); /// -/// assert_eq!(chunk.len(), 6); +/// assert_eq!(chunk.len(), 3); /// ``` pub fn compile(source: &str) -> Result { let lexer = Lexer::new(source); @@ -55,7 +58,7 @@ pub struct Compiler<'src> { self_name: Option, instructions: SmallVec<[(Instruction, Type, Span); 32]>, constants: SmallVec<[ConcreteValue; 16]>, - locals: SmallVec<[Local; 8]>, + locals: SmallVec<[(Local, Type); 8]>, lexer: Lexer<'src>, @@ -65,7 +68,7 @@ pub struct Compiler<'src> { previous_position: Span, return_type: Option, - minimum_register: u16, + minimum_register: u8, block_index: u8, current_scope: Scope, } @@ -99,8 +102,8 @@ impl<'src> Compiler<'src> { pub fn finish( self, - type_parameters: Option>, - value_parameters: Option>, + type_parameters: Option>, + value_parameters: Option>, ) -> Chunk { log::info!("End chunk"); @@ -109,20 +112,14 @@ impl<'src> Compiler<'src> { value_parameters, return_type: self.return_type.unwrap_or(Type::None), }; - let (instructions, positions) = self + let instructions = self .instructions .into_iter() .map(|(instruction, _, position)| (instruction, position)) - .unzip(); + .collect(); + let locals = self.locals.into_iter().map(|(local, _)| local).collect(); - Chunk::with_data( - self.self_name, - r#type, - instructions, - positions, - self.constants, - self.locals, - ) + Chunk::with_data(self.self_name, r#type, instructions, self.constants, locals) } pub fn compile(&mut self) -> Result<(), CompileError> { @@ -143,13 +140,13 @@ impl<'src> Compiler<'src> { matches!(self.current_token, Token::Eof) } - fn next_register(&self) -> u16 { + fn next_register(&self) -> u8 { self.instructions .iter() .rev() .find_map(|(instruction, _, _)| { if instruction.yields_value() { - Some(instruction.a() + 1) + Some(instruction.a + 1) } else { None } @@ -176,7 +173,7 @@ impl<'src> Compiler<'src> { Ok(()) } - fn get_local(&self, index: u16) -> Result<&Local, CompileError> { + fn get_local(&self, index: u8) -> Result<&(Local, Type), CompileError> { self.locals .get(index as usize) .ok_or(CompileError::UndeclaredVariable { @@ -185,12 +182,12 @@ impl<'src> Compiler<'src> { }) } - fn get_local_index(&self, identifier_text: &str) -> Result { + fn get_local_index(&self, identifier_text: &str) -> Result { self.locals .iter() .enumerate() .rev() - .find_map(|(index, local)| { + .find_map(|(index, (local, _))| { let constant = self.constants.get(local.identifier_index as usize)?; let identifier = if let ConcreteValue::String(identifier) = constant { identifier @@ -199,7 +196,7 @@ impl<'src> Compiler<'src> { }; if identifier == identifier_text { - Some(index as u16) + Some(index as u8) } else { None } @@ -213,39 +210,44 @@ impl<'src> Compiler<'src> { fn declare_local( &mut self, identifier: &str, + register_index: u8, r#type: Type, is_mutable: bool, scope: Scope, - ) -> (u16, u16) { + ) -> (u8, u8) { log::info!("Declaring local {identifier}"); let identifier = ConcreteValue::string(identifier); let identifier_index = self.push_or_get_constant(identifier); - let local_index = self.locals.len() as u16; + let local_index = self.locals.len() as u8; - self.locals - .push(Local::new(identifier_index, r#type, is_mutable, scope)); + self.locals.push(( + Local::new(identifier_index, register_index, is_mutable, scope), + r#type, + )); (local_index, identifier_index) } - fn get_identifier(&self, local_index: u16) -> Option { - self.locals.get(local_index as usize).and_then(|local| { - self.constants - .get(local.identifier_index as usize) - .map(|value| value.to_string()) - }) + fn get_identifier(&self, local_index: u8) -> Option { + self.locals + .get(local_index as usize) + .and_then(|(local, _)| { + self.constants + .get(local.identifier_index as usize) + .map(|value| value.to_string()) + }) } - fn push_or_get_constant(&mut self, value: ConcreteValue) -> u16 { + fn push_or_get_constant(&mut self, value: ConcreteValue) -> u8 { if let Some(index) = self .constants .iter() .position(|constant| constant == &value) { - index as u16 + index as u8 } else { - let index = self.constants.len() as u16; + let index = self.constants.len() as u8; self.constants.push(value); @@ -325,23 +327,35 @@ impl<'src> Compiler<'src> { .unwrap_or(Type::None) } - fn get_register_type(&self, register_index: u16) -> Result { + fn get_register_type(&self, register_index: u8) -> Result { + if let Some((_, r#type)) = self + .locals + .iter() + .find(|(local, _)| local.register_index == register_index) + { + return Ok(r#type.clone()); + } + for (instruction, r#type, _) in &self.instructions { - if instruction.a() == register_index { - if let Operation::LoadList = instruction.operation() { - let LoadList { start_register, .. } = LoadList::from(instruction); - let item_type = self.get_register_type(start_register)?; + if !instruction.yields_value() { + continue; + } - return Ok(Type::List(Box::new(item_type))); - } + let operation = instruction.operation(); - if let Operation::LoadSelf = instruction.operation() { - return Ok(Type::SelfChunk); - } + if let Operation::LoadList = operation { + let LoadList { start_register, .. } = LoadList::from(instruction); + let item_type = self.get_register_type(start_register)?; - if instruction.yields_value() { - return Ok(r#type.clone()); - } + return Ok(Type::List(Box::new(item_type))); + } + + if let Operation::LoadSelf = operation { + return Ok(Type::SelfChunk); + } + + if instruction.yields_value() { + return Ok(r#type.clone()); } } @@ -391,14 +405,10 @@ impl<'src> Compiler<'src> { ) -> Result<(), CompileError> { let r#type = constant.r#type(); let constant_index = self.push_or_get_constant(constant); - let destination = Destination::Register(self.next_register()); - let instruction = Instruction::from(LoadConstant { - destination, - constant_index, - jump_next: false, - }); + let destination = self.next_register(); + let load_constant = Instruction::load_constant(destination, constant_index, false); - self.emit_instruction(instruction, r#type, position); + self.emit_instruction(load_constant, r#type, position); Ok(()) } @@ -410,14 +420,10 @@ impl<'src> Compiler<'src> { self.advance()?; let boolean = text.parse::().unwrap(); - let destination = Destination::Register(self.next_register()); - let instruction = Instruction::from(LoadBoolean { - destination, - value: boolean, - jump_next: false, - }); + let destination = self.next_register(); + let load_boolean = Instruction::load_boolean(destination, boolean, false); - self.emit_instruction(instruction, Type::Boolean, position); + self.emit_instruction(load_boolean, Type::Boolean, position); Ok(()) } else { @@ -576,7 +582,7 @@ impl<'src> Compiler<'src> { )) } - let destination = Destination::Register(self.next_register()); + let destination = self.next_register(); let instruction = match operator.kind() { TokenKind::Bang => Instruction::from(Not { destination, @@ -624,8 +630,13 @@ impl<'src> Compiler<'src> { position: self.previous_position, })?; let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?; - let left_is_mutable_local = if let Argument::Local(local_index) = left { - self.get_local(local_index)?.is_mutable + let left_is_mutable_local = if let Operation::GetLocal = left_instruction.operation() { + let GetLocal { local_index, .. } = GetLocal::from(&left_instruction); + + self.locals + .get(local_index as usize) + .map(|(local, _)| local.is_mutable) + .unwrap_or(false) } else { false }; @@ -742,12 +753,11 @@ impl<'src> Compiler<'src> { let destination = if is_assignment { match left { - Argument::Register(register) => Destination::Register(register), - Argument::Local(local_index) => Destination::Local(local_index), - Argument::Constant(_) => Destination::Register(self.next_register()), + Argument::Register(register) => register, + Argument::Constant(_) => self.next_register(), } } else { - Destination::Register(self.next_register()) + self.next_register() }; let instruction = match operator { Token::Plus | Token::PlusEqual => Instruction::add(destination, left, right), @@ -823,13 +833,14 @@ impl<'src> Compiler<'src> { .push((right_instruction, right_type, right_position)); } + let destination = self.next_register(); let comparison = match operator { - Token::DoubleEqual => Instruction::equal(true, left, right), - Token::BangEqual => Instruction::equal(false, left, right), - Token::Less => Instruction::less(true, left, right), - Token::LessEqual => Instruction::less_equal(true, left, right), - Token::Greater => Instruction::less_equal(false, left, right), - Token::GreaterEqual => Instruction::less(false, left, right), + Token::DoubleEqual => Instruction::equal(destination, true, left, right), + Token::BangEqual => Instruction::equal(destination, false, left, right), + Token::Less => Instruction::less(destination, true, left, right), + Token::LessEqual => Instruction::less_equal(destination, true, left, right), + Token::Greater => Instruction::less_equal(destination, false, left, right), + Token::GreaterEqual => Instruction::less(destination, false, left, right), _ => { return Err(CompileError::ExpectedTokenMultiple { expected: &[ @@ -845,26 +856,8 @@ impl<'src> Compiler<'src> { }) } }; - let destination = Destination::Register(self.next_register()); - let jump = Instruction::from(Jump { - offset: 1, - is_positive: true, - }); - let load_true = Instruction::from(LoadBoolean { - destination, - value: true, - jump_next: true, - }); - let load_false = Instruction::from(LoadBoolean { - destination, - value: false, - jump_next: false, - }); - self.emit_instruction(comparison, Type::None, operator_position); - self.emit_instruction(jump, Type::None, operator_position); - self.emit_instruction(load_true, Type::Boolean, operator_position); - self.emit_instruction(load_false, Type::Boolean, operator_position); + self.emit_instruction(comparison, Type::Boolean, operator_position); Ok(()) } @@ -893,10 +886,8 @@ impl<'src> Compiler<'src> { }); } - if let Some([Operation::Test, Operation::Jump]) = self.get_last_operations() {} - let (argument, push_back) = self.handle_binary_argument(&left_instruction)?; - let is_local = matches!(argument, Argument::Local(_)); + let is_local = left_instruction.operation() == Operation::GetLocal; if push_back || is_local { self.instructions @@ -929,7 +920,7 @@ impl<'src> Compiler<'src> { if is_logic_chain { let expression_length = self.instructions.len() - jump_index - 1; - jump_distance += expression_length as u16; + jump_distance += expression_length as u8; let jump = Instruction::jump(jump_distance, true); self.instructions @@ -957,7 +948,7 @@ impl<'src> Compiler<'src> { } else if let Some(native_function) = NativeFunction::from_str(identifier) { return self.parse_native_call(native_function); } else if self.self_name.as_deref() == Some(identifier) { - let destination = Destination::Register(self.next_register()); + let destination = self.next_register(); let load_self = Instruction::from(LoadSelf { destination }); self.emit_instruction(load_self, Type::SelfChunk, start_position); @@ -970,7 +961,9 @@ impl<'src> Compiler<'src> { }); }; - let local = self.get_local(local_index)?; + let (local, r#type) = self + .get_local(local_index) + .map(|(local, r#type)| (local, r#type.clone()))?; let is_mutable = local.is_mutable; if !self.current_scope.contains(&local.scope) { @@ -999,17 +992,16 @@ impl<'src> Compiler<'src> { }); self.emit_instruction(set_local, Type::None, start_position); - optimize_set_local(&mut self.instructions); + optimize_set_local(self); return Ok(()); } - let destination = Destination::Register(self.next_register()); + let destination = self.next_register(); let get_local = Instruction::from(GetLocal { destination, local_index, }); - let r#type = self.get_local(local_index)?.r#type.clone(); self.emit_instruction(get_local, r#type, self.previous_position); @@ -1083,7 +1075,7 @@ impl<'src> Compiler<'src> { self.allow(Token::Comma)?; } - let destination = Destination::Register(self.next_register()); + let destination = self.next_register(); let end = self.previous_position.1; let load_list = Instruction::from(LoadList { destination, @@ -1099,22 +1091,18 @@ impl<'src> Compiler<'src> { self.advance()?; self.parse_expression()?; - if matches!( - self.get_last_operations(), - Some([ - Operation::Equal | Operation::Less | Operation::LessEqual, - Operation::Jump, - Operation::LoadBoolean, - Operation::LoadBoolean, - ]) - ) { - self.instructions.pop(); - self.instructions.pop(); - self.instructions.pop(); - } else if let Some((instruction, _, _)) = self.instructions.last() { - let test_register = instruction.a(); + if let Some((instruction, _, _)) = self.instructions.last() { + let argument = match instruction.destination_as_argument() { + Some(argument) => argument, + None => { + return Err(CompileError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: self.previous_position, + }); + } + }; let test = Instruction::from(Test { - argument: Argument::Register(test_register), + argument, test_value: true, }); @@ -1135,7 +1123,7 @@ impl<'src> Compiler<'src> { } let if_block_end = self.instructions.len(); - let mut if_block_distance = (if_block_end - if_block_start) as u16; + let mut if_block_distance = (if_block_end - if_block_start) as u8; let if_block_type = self.get_last_instruction_type(); let if_last_register = self.next_register().saturating_sub(1); @@ -1162,7 +1150,7 @@ impl<'src> Compiler<'src> { }; let else_block_end = self.instructions.len(); - let else_block_distance = (else_block_end - if_block_end) as u16; + let else_block_distance = (else_block_end - if_block_end) as u8; let else_block_type = self.get_last_instruction_type(); if let Err(conflict) = if_block_type.check(&else_block_type) { @@ -1178,7 +1166,7 @@ impl<'src> Compiler<'src> { if let Some(skippable) = self.get_last_jumpable_mut_between(1, if_block_distance as usize) { - skippable.set_c_to_boolean(true); + skippable.c = true as u8; } else { if_block_distance += 1; let jump = Instruction::from(Jump { @@ -1230,7 +1218,7 @@ impl<'src> Compiler<'src> { fn parse_while(&mut self) -> Result<(), CompileError> { self.advance()?; - let expression_start = self.instructions.len() as u16; + let expression_start = self.instructions.len() as u8; self.parse_expression()?; @@ -1252,8 +1240,8 @@ impl<'src> Compiler<'src> { self.parse_block()?; - let block_end = self.instructions.len() as u16; - let jump_distance = block_end - block_start as u16 + 1; + let block_end = self.instructions.len() as u8; + let jump_distance = block_end - block_start as u8 + 1; let jump = Instruction::from(Jump { offset: jump_distance, is_positive: true, @@ -1303,7 +1291,7 @@ impl<'src> Compiler<'src> { let argument_count = destination - start_register; let return_type = function.r#type().return_type; let call_native = Instruction::from(CallNative { - destination: Destination::Register(destination), + destination, function, argument_count, }); @@ -1417,21 +1405,20 @@ impl<'src> Compiler<'src> { self.expect(Token::Equal)?; self.parse_expression()?; - let register = self.next_register() - 1; + let register_index = self.next_register() - 1; let r#type = if let Some(r#type) = explicit_type { r#type } else { - self.get_register_type(register)? + self.get_register_type(register_index)? }; - let (local_index, _) = - self.declare_local(identifier, r#type, is_mutable, self.current_scope); - let define_local = Instruction::from(DefineLocal { - local_index, - register, - is_mutable, - }); - self.emit_instruction(define_local, Type::None, position); + self.declare_local( + identifier, + register_index, + r#type, + is_mutable, + self.current_scope, + ); Ok(()) } @@ -1453,7 +1440,7 @@ impl<'src> Compiler<'src> { function_compiler.expect(Token::LeftParenthesis)?; - let mut value_parameters: Option> = None; + let mut value_parameters: Option> = None; while !function_compiler.allow(Token::RightParenthesis)? { let is_mutable = function_compiler.allow(Token::Mut)?; @@ -1478,8 +1465,10 @@ impl<'src> Compiler<'src> { function_compiler.advance()?; + let local_register_index = function_compiler.next_register(); let (_, identifier_index) = function_compiler.declare_local( parameter, + local_register_index, r#type.clone(), is_mutable, function_compiler.current_scope, @@ -1526,7 +1515,7 @@ impl<'src> Compiler<'src> { let function = ConcreteValue::function(function_compiler.finish(None, value_parameters.clone())); let constant_index = self.push_or_get_constant(function); - let register = self.next_register(); + let destination = self.next_register(); let function_type = FunctionType { type_parameters: None, value_parameters, @@ -1536,30 +1525,23 @@ impl<'src> Compiler<'src> { if let Some((identifier, position)) = identifier_info { let (local_index, _) = self.declare_local( identifier, + destination, Type::function(function_type.clone()), false, self.current_scope, ); - let load_constant = Instruction::from(LoadConstant { - destination: Destination::Register(register), - constant_index, - jump_next: false, - }); - let define_local = Instruction::from(DefineLocal { - local_index, - register, - is_mutable: false, - }); + let load_constant = Instruction::load_constant(destination, constant_index, false); + let set_local = Instruction::set_local(destination, local_index); self.emit_instruction( load_constant, Type::function(function_type), Span(function_start, function_end), ); - self.emit_instruction(define_local, Type::None, position); + self.emit_instruction(set_local, Type::None, position); } else { let load_constant = Instruction::from(LoadConstant { - destination: Destination::Register(register), + destination, constant_index, jump_next: false, }); @@ -1637,9 +1619,9 @@ impl<'src> Compiler<'src> { } let end = self.current_position.1; - let register = self.next_register(); + let destination = self.next_register(); let call = Instruction::from(Call { - destination: Destination::Register(register), + destination, function, argument_count, }); diff --git a/dust-lang/src/optimize.rs b/dust-lang/src/compiler/optimize.rs similarity index 78% rename from dust-lang/src/optimize.rs rename to dust-lang/src/compiler/optimize.rs index 53baa90..58ae274 100644 --- a/dust-lang/src/optimize.rs +++ b/dust-lang/src/compiler/optimize.rs @@ -2,7 +2,7 @@ use smallvec::SmallVec; -use crate::{instruction::SetLocal, Instruction, Operation, Span, Type}; +use crate::{instruction::SetLocal, CompileError, Compiler, Instruction, Operation, Span, Type}; fn get_last_operations( instructions: &[(Instruction, Type, Span)], @@ -58,13 +58,12 @@ pub fn optimize_control_flow(instructions: &mut [(Instruction, Type, Span)]) { let first_loader = &mut instructions.iter_mut().nth_back(1).unwrap().0; - first_loader.set_c_to_boolean(true); + first_loader.c = true as u8; - let first_loader_register = first_loader.a(); + let first_loader_destination = first_loader.a; let second_loader = &mut instructions.last_mut().unwrap().0; - let second_loader_new = *second_loader.clone().set_a(first_loader_register); - *second_loader = second_loader_new; + second_loader.a = first_loader_destination; } /// Optimizes a math instruction followed by a SetLocal instruction. @@ -85,9 +84,9 @@ pub fn optimize_control_flow(instructions: &mut [(Instruction, Type, Span)]) { /// The instructions must be in the following order: /// - `Add`, `Subtract`, `Multiply`, `Divide` or `Modulo` /// - `SetLocal` -pub fn optimize_set_local(instructions: &mut SmallVec<[(Instruction, Type, Span); 32]>) { +pub fn optimize_set_local(compiler: &mut Compiler) -> Result<(), CompileError> { if !matches!( - get_last_operations(instructions), + compiler.get_last_operations(), Some([ Operation::Add | Operation::Subtract @@ -97,17 +96,17 @@ pub fn optimize_set_local(instructions: &mut SmallVec<[(Instruction, Type, Span) Operation::SetLocal, ]) ) { - return; + return Ok(()); } log::debug!("Condensing math and SetLocal to math instruction"); - let set_local = SetLocal::from(&instructions.pop().unwrap().0); - let math_instruction = instructions.last_mut().unwrap().0; - let math_instruction_new = *math_instruction - .clone() - .set_a(set_local.local_index) - .set_a_is_local(true); + let set_local = SetLocal::from(&compiler.instructions.pop().unwrap().0); + let (local, _) = compiler.get_local(set_local.local_index)?; + let local_register_index = local.register_index; + let math_instruction = &mut compiler.instructions.last_mut().unwrap().0; - instructions.last_mut().unwrap().0 = math_instruction_new; + math_instruction.a = local_register_index; + + Ok(()) } diff --git a/dust-lang/src/disassembler.rs b/dust-lang/src/disassembler.rs index 133021d..089ab8e 100644 --- a/dust-lang/src/disassembler.rs +++ b/dust-lang/src/disassembler.rs @@ -61,8 +61,8 @@ const CONSTANT_HEADER: [&str; 4] = [ const LOCAL_HEADER: [&str; 4] = [ "Locals", "------", - " i SCOPE MUTABLE TYPE IDENTIFIER ", - "--- ------- ------- ---------------- ----------------", + " i IDENTIFIER REGISTER SCOPE MUTABLE", + "--- ---------------- -------- ------- -------", ]; /// Builder that constructs a human-readable representation of a chunk. @@ -209,13 +209,7 @@ impl<'a> Disassembler<'a> { self.push_header(line); } - for (index, (instruction, position)) in self - .chunk - .instructions() - .iter() - .zip(self.chunk.positions().iter()) - .enumerate() - { + for (index, (instruction, position)) in self.chunk.instructions().iter().enumerate() { let position = position.to_string(); let operation = instruction.operation().to_string(); let info = instruction.disassembly_info(); @@ -235,7 +229,7 @@ impl<'a> Disassembler<'a> { index, Local { identifier_index, - r#type, + register_index, scope, is_mutable, }, @@ -247,10 +241,10 @@ impl<'a> Disassembler<'a> { .get(*identifier_index as usize) .map(|value| value.to_string()) .unwrap_or_else(|| "unknown".to_string()); - let type_display = r#type.to_string(); + let register_display = format!("R{register_index}"); let scope = scope.to_string(); let local_display = format!( - "{index:^3} {scope:^7} {is_mutable:^7} {type_display:^16} {identifier_display:^16}" + "{index:^3} {identifier_display:^16} {register_display:^8} {scope:^7} {is_mutable:^7}" ); self.push_details(&local_display); diff --git a/dust-lang/src/dust_error.rs b/dust-lang/src/dust_error.rs index 00c5ac7..4db2a64 100644 --- a/dust-lang/src/dust_error.rs +++ b/dust-lang/src/dust_error.rs @@ -1,14 +1,12 @@ -//! Top-level Dust errors with source code annotations. - +//! Top-level error for the Dust language API that can create detailed reports with source code +//! annotations. use std::fmt::{self, Display, Formatter}; use annotate_snippets::{Level, Renderer, Snippet}; use crate::{CompileError, Span, VmError}; -/// A top-level error that can occur during the execution of Dust code. -/// -/// This error can display nicely formatted messages with source code annotations. +/// A top-level error that can occur during the interpretation of Dust code. #[derive(Debug, PartialEq)] pub enum DustError<'src> { Compile { @@ -30,7 +28,7 @@ impl<'src> DustError<'src> { DustError::Runtime { error, source } } - pub fn create_report(&self) -> String { + pub fn report(&self) -> String { let (position, title, description, details) = self.error_data(); let label = format!("{}: {}", title, description); let details = details.unwrap_or_else(|| "While parsing this code".to_string()); @@ -74,7 +72,7 @@ impl<'src> DustError<'src> { impl Display for DustError<'_> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.create_report()) + write!(f, "{}", self.report()) } } diff --git a/dust-lang/src/instruction/add.rs b/dust-lang/src/instruction/add.rs index d77d562..2117fd8 100644 --- a/dust-lang/src/instruction/add.rs +++ b/dust-lang/src/instruction/add.rs @@ -1,14 +1,14 @@ -use crate::{Argument, Destination, Instruction, Operation}; +use crate::{Argument, Instruction, Operation}; pub struct Add { - pub destination: Destination, + pub destination: u8, pub left: Argument, pub right: Argument, } impl From<&Instruction> for Add { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; let (left, right) = instruction.b_and_c_as_arguments(); Add { @@ -21,16 +21,12 @@ impl From<&Instruction> for Add { impl From for Instruction { fn from(add: Add) -> Self { - let (a, a_options) = add.destination.as_index_and_a_options(); + 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(); - Instruction { - operation: Operation::ADD, - options: a_options | b_options | c_options, - a, - b, - c, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/call.rs b/dust-lang/src/instruction/call.rs index 6145e9e..2176dfa 100644 --- a/dust-lang/src/instruction/call.rs +++ b/dust-lang/src/instruction/call.rs @@ -1,32 +1,32 @@ -use crate::{Argument, Destination, Instruction, Operation}; +use crate::{Argument, Instruction, Operation}; pub struct Call { - pub destination: Destination, + pub destination: u8, pub function: Argument, - pub argument_count: u16, + pub argument_count: u8, } impl From<&Instruction> for Call { fn from(instruction: &Instruction) -> Self { + let destination = instruction.a; + let function = instruction.b_as_argument(); + let argument_count = instruction.c; + Call { - destination: instruction.a_as_destination(), - function: instruction.b_as_argument(), - argument_count: instruction.c, + destination, + function, + argument_count, } } } impl From for Instruction { fn from(call: Call) -> Self { - let (a, a_options) = call.destination.as_index_and_a_options(); + let a = call.destination; let (b, b_options) = call.function.as_index_and_b_options(); + let c = call.argument_count; + let metadata = Operation::Call as u8 | b_options.bits(); - Instruction { - operation: Operation::CALL, - options: a_options | b_options, - a, - b, - c: call.argument_count, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/call_native.rs b/dust-lang/src/instruction/call_native.rs index 93b2412..0510475 100644 --- a/dust-lang/src/instruction/call_native.rs +++ b/dust-lang/src/instruction/call_native.rs @@ -1,14 +1,14 @@ -use crate::{Destination, Instruction, NativeFunction, Operation}; +use crate::{Instruction, NativeFunction, Operation}; pub struct CallNative { - pub destination: Destination, + pub destination: u8, pub function: NativeFunction, - pub argument_count: u16, + pub argument_count: u8, } impl From<&Instruction> for CallNative { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; let function = NativeFunction::from(instruction.b); CallNative { @@ -21,15 +21,11 @@ impl From<&Instruction> for CallNative { impl From for Instruction { fn from(call_native: CallNative) -> Self { - let (a, a_options) = call_native.destination.as_index_and_a_options(); - let b = call_native.function as u16; + let metadata = Operation::CallNative as u8; + let a = call_native.destination; + let b = call_native.function as u8; + let c = call_native.argument_count; - Instruction { - operation: Operation::CALL_NATIVE, - options: a_options, - a, - b, - c: call_native.argument_count, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/close.rs b/dust-lang/src/instruction/close.rs index ecb5e2d..fc7b39d 100644 --- a/dust-lang/src/instruction/close.rs +++ b/dust-lang/src/instruction/close.rs @@ -1,10 +1,8 @@ use crate::{Instruction, Operation}; -use super::InstructionOptions; - pub struct Close { - pub from: u16, - pub to: u16, + pub from: u8, + pub to: u8, } impl From<&Instruction> for Close { @@ -18,12 +16,9 @@ impl From<&Instruction> for Close { impl From for Instruction { fn from(close: Close) -> Self { - Instruction { - operation: Operation::CLOSE, - options: InstructionOptions::empty(), - a: 0, - b: close.from, - c: close.to, - } + let metadata = Operation::Close as u8; + let (a, b, c) = (0, close.from, close.to); + + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/divide.rs b/dust-lang/src/instruction/divide.rs index 39ff33e..dd7d948 100644 --- a/dust-lang/src/instruction/divide.rs +++ b/dust-lang/src/instruction/divide.rs @@ -1,14 +1,14 @@ -use crate::{Argument, Destination, Instruction, Operation}; +use crate::{Argument, Instruction, Operation}; pub struct Divide { - pub destination: Destination, + pub destination: u8, pub left: Argument, pub right: Argument, } impl From<&Instruction> for Divide { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; let (left, right) = instruction.b_and_c_as_arguments(); Divide { @@ -21,16 +21,11 @@ impl From<&Instruction> for Divide { impl From for Instruction { fn from(divide: Divide) -> Self { - let (a, a_options) = divide.destination.as_index_and_a_options(); + 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(); - Instruction { - operation: Operation::DIVIDE, - options: a_options | b_options | c_options, - a, - b, - c, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/equal.rs b/dust-lang/src/instruction/equal.rs index 8c1c023..f01a6ea 100644 --- a/dust-lang/src/instruction/equal.rs +++ b/dust-lang/src/instruction/equal.rs @@ -1,9 +1,9 @@ -use crate::{Argument, Destination, Instruction, Operation}; +use crate::{Argument, Instruction, Operation}; use super::InstructionOptions; pub struct Equal { - pub destination: Destination, + pub destination: u8, pub value: bool, pub left: Argument, pub right: Argument, @@ -11,8 +11,8 @@ pub struct Equal { impl From<&Instruction> for Equal { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); - let value = instruction.options.d(); + let destination = instruction.a; + let value = instruction.d(); let (left, right) = instruction.b_and_c_as_arguments(); Equal { @@ -26,7 +26,7 @@ impl From<&Instruction> for Equal { impl From for Instruction { fn from(equal: Equal) -> Self { - let (a, a_options) = equal.destination.as_index_and_a_options(); + 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 { @@ -34,13 +34,9 @@ impl From for Instruction { } else { InstructionOptions::empty() }; + let metadata = + Operation::Equal as u8 | b_options.bits() | c_options.bits() | d_options.bits(); - Instruction { - operation: Operation::EQUAL, - options: a_options | b_options | c_options | d_options, - a, - b, - c, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/get_local.rs b/dust-lang/src/instruction/get_local.rs index 01e358a..87ad587 100644 --- a/dust-lang/src/instruction/get_local.rs +++ b/dust-lang/src/instruction/get_local.rs @@ -1,31 +1,29 @@ -use crate::{Destination, Instruction, Operation}; +use crate::{Instruction, Operation}; pub struct GetLocal { - pub destination: Destination, - pub local_index: u16, + pub destination: u8, + pub local_index: u8, } impl From<&Instruction> for GetLocal { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; + let local_index = instruction.b; GetLocal { destination, - local_index: instruction.b, + local_index, } } } impl From for Instruction { fn from(get_local: GetLocal) -> Self { - let (a, a_options) = get_local.destination.as_index_and_a_options(); + let a = get_local.destination; + let b = get_local.local_index; + let c = 0; + let metadata = Operation::GetLocal as u8; - Instruction { - operation: Operation::GET_LOCAL, - options: a_options, - a, - b: get_local.local_index, - c: 0, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/jump.rs b/dust-lang/src/instruction/jump.rs index 085034d..557a44f 100644 --- a/dust-lang/src/instruction/jump.rs +++ b/dust-lang/src/instruction/jump.rs @@ -1,9 +1,7 @@ use crate::{Instruction, Operation}; -use super::InstructionOptions; - pub struct Jump { - pub offset: u16, + pub offset: u8, pub is_positive: bool, } @@ -18,12 +16,11 @@ impl From<&Instruction> for Jump { impl From for Instruction { fn from(jump: Jump) -> Self { - Instruction { - operation: Operation::JUMP, - options: InstructionOptions::empty(), - a: 0, - b: jump.offset, - c: jump.is_positive as u16, - } + let metadata = Operation::Jump as u8; + let a = 0; + let b = jump.offset; + let c = jump.is_positive as u8; + + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/less.rs b/dust-lang/src/instruction/less.rs index 04df91b..a62120c 100644 --- a/dust-lang/src/instruction/less.rs +++ b/dust-lang/src/instruction/less.rs @@ -1,9 +1,9 @@ -use crate::{Argument, Destination, Instruction, Operation}; +use crate::{Argument, Instruction, Operation}; use super::InstructionOptions; pub struct Less { - pub destination: Destination, + pub destination: u8, pub value: bool, pub left: Argument, pub right: Argument, @@ -11,8 +11,8 @@ pub struct Less { impl From<&Instruction> for Less { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); - let value = instruction.options.d(); + let destination = instruction.a; + let value = instruction.d(); let (left, right) = instruction.b_and_c_as_arguments(); Less { @@ -26,7 +26,7 @@ impl From<&Instruction> for Less { impl From for Instruction { fn from(less: Less) -> Self { - let (a, a_options) = less.destination.as_index_and_a_options(); + 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 { @@ -34,13 +34,9 @@ impl From for Instruction { } else { InstructionOptions::empty() }; + let metadata = + Operation::Less as u8 | b_options.bits() | c_options.bits() | d_options.bits(); - Instruction { - operation: Operation::LESS, - options: a_options | b_options | c_options | d_options, - a, - b, - c, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/less_equal.rs b/dust-lang/src/instruction/less_equal.rs index 1191575..28cea77 100644 --- a/dust-lang/src/instruction/less_equal.rs +++ b/dust-lang/src/instruction/less_equal.rs @@ -1,9 +1,9 @@ -use crate::{Argument, Destination, Instruction, Operation}; +use crate::{Argument, Instruction, Operation}; use super::InstructionOptions; pub struct LessEqual { - pub destination: Destination, + pub destination: u8, pub value: bool, pub left: Argument, pub right: Argument, @@ -11,8 +11,8 @@ pub struct LessEqual { impl From<&Instruction> for LessEqual { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); - let value = instruction.options.d(); + let destination = instruction.a; + let value = instruction.d(); let (left, right) = instruction.b_and_c_as_arguments(); LessEqual { @@ -26,7 +26,7 @@ impl From<&Instruction> for LessEqual { impl From for Instruction { fn from(less_equal: LessEqual) -> Self { - let (a, a_options) = less_equal.destination.as_index_and_a_options(); + 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 { @@ -34,13 +34,9 @@ impl From for Instruction { } else { InstructionOptions::empty() }; + let metadata = + Operation::LessEqual as u8 | b_options.bits() | c_options.bits() | d_options.bits(); - Instruction { - operation: Operation::LESS_EQUAL, - options: a_options | b_options | c_options | d_options, - a, - b, - c, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/load_boolean.rs b/dust-lang/src/instruction/load_boolean.rs index 9c24599..e5f1dcb 100644 --- a/dust-lang/src/instruction/load_boolean.rs +++ b/dust-lang/src/instruction/load_boolean.rs @@ -1,14 +1,14 @@ -use crate::{Destination, Instruction, Operation}; +use crate::{Instruction, Operation}; pub struct LoadBoolean { - pub destination: Destination, + pub destination: u8, pub value: bool, pub jump_next: bool, } impl From<&Instruction> for LoadBoolean { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; let value = instruction.b != 0; let jump_next = instruction.c != 0; @@ -22,16 +22,11 @@ impl From<&Instruction> for LoadBoolean { impl From for Instruction { fn from(load_boolean: LoadBoolean) -> Self { - let (a, options) = load_boolean.destination.as_index_and_a_options(); - let b = load_boolean.value as u16; - let c = load_boolean.jump_next as u16; + let metadata = Operation::LoadBoolean as u8; + let a = load_boolean.destination; + let b = load_boolean.value as u8; + let c = load_boolean.jump_next as u8; - Instruction { - operation: Operation::LOAD_BOOLEAN, - options, - a, - b, - c, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/load_constant.rs b/dust-lang/src/instruction/load_constant.rs index b91da50..8b8826a 100644 --- a/dust-lang/src/instruction/load_constant.rs +++ b/dust-lang/src/instruction/load_constant.rs @@ -1,14 +1,14 @@ -use crate::{Destination, Instruction, Operation}; +use crate::{Instruction, Operation}; pub struct LoadConstant { - pub destination: Destination, - pub constant_index: u16, + pub destination: u8, + pub constant_index: u8, pub jump_next: bool, } impl From<&Instruction> for LoadConstant { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; let constant_index = instruction.b; let jump_next = instruction.c != 0; @@ -22,16 +22,11 @@ impl From<&Instruction> for LoadConstant { impl From for Instruction { fn from(load_constant: LoadConstant) -> Self { - let (a, options) = load_constant.destination.as_index_and_a_options(); + let metadata = Operation::LoadConstant as u8; + let a = load_constant.destination; let b = load_constant.constant_index; - let c = load_constant.jump_next as u16; + let c = load_constant.jump_next as u8; - Instruction { - operation: Operation::LOAD_CONSTANT, - options, - a, - b, - c, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/load_list.rs b/dust-lang/src/instruction/load_list.rs index 095579e..43df68c 100644 --- a/dust-lang/src/instruction/load_list.rs +++ b/dust-lang/src/instruction/load_list.rs @@ -1,13 +1,13 @@ -use crate::{Destination, Instruction, Operation}; +use crate::{Instruction, Operation}; pub struct LoadList { - pub destination: Destination, - pub start_register: u16, + pub destination: u8, + pub start_register: u8, } impl From<&Instruction> for LoadList { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; let start_register = instruction.b; LoadList { @@ -19,15 +19,11 @@ impl From<&Instruction> for LoadList { impl From for Instruction { fn from(load_list: LoadList) -> Self { - let (a, options) = load_list.destination.as_index_and_a_options(); + let metadata = Operation::LoadList as u8; + let a = load_list.destination; let b = load_list.start_register; + let c = 0; - Instruction { - operation: Operation::LOAD_LIST, - options, - a, - b, - c: 0, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/load_self.rs b/dust-lang/src/instruction/load_self.rs index 5c81f81..aa4293e 100644 --- a/dust-lang/src/instruction/load_self.rs +++ b/dust-lang/src/instruction/load_self.rs @@ -1,12 +1,12 @@ -use crate::{Destination, Instruction, Operation}; +use crate::{Instruction, Operation}; pub struct LoadSelf { - pub destination: Destination, + pub destination: u8, } impl From<&Instruction> for LoadSelf { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; LoadSelf { destination } } @@ -14,14 +14,11 @@ impl From<&Instruction> for LoadSelf { impl From for Instruction { fn from(load_self: LoadSelf) -> Self { - let (a, options) = load_self.destination.as_index_and_a_options(); + let metadata = Operation::LoadSelf as u8; + let a = load_self.destination; + let b = 0; + let c = 0; - Instruction { - operation: Operation::LOAD_SELF, - options, - a, - b: 0, - c: 0, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index f9ad42f..dda70df 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -1,31 +1,32 @@ -//! An operation and its arguments for the Dust virtual machine. +//! Instructions for the Dust virtual machine. //! -//! Each instruction is 64 bits and holds ten distinct fields: +//! Each instruction is 32 bits and uses up to seven distinct fields: //! //! Bit | Description //! ----- | ----------- -//! 0-8 | Operation code -//! 9 | Flag for whether A is a local -//! 10 | Flag for whether B argument is a constant -//! 11 | Flag for whether C argument is a local -//! 12 | Flag for whether B argument is a constant -//! 13 | Flag for whether C argument is a local -//! 14 | D Argument -//! 15-16 | Unused -//! 17-32 | A argument -//! 33-48 | B argument -//! 49-63 | C argument +//! 0-4 | Operation code +//! 5 | Flag indicating if the B argument is a constant +//! 6 | Flag indicating if the C argument is a constant +//! 7 | D field (boolean) +//! 8-15 | A field (unsigned 8-bit integer) +//! 16-23 | B field (unsigned 8-bit integer) +//! 24-31 | C field (unsigned 8-bit integer) //! -//! Be careful when working with instructions directly. When modifying an instruction, be sure to -//! account for the fact that setting the A, B, or C arguments to 0 will have no effect. It is -//! usually best to remove instructions and insert new ones in their place instead of mutating them. +//! **Be careful when working with instructions directly**. When modifying an instruction's fields, +//! you may also need to modify its flags. It is usually best to remove instructions and insert new +//! ones in their place instead of mutating them. +//! +//! # Examples +//! +//! ## Creating Instructions //! //! For each operation, there are two ways to create an instruction: //! //! - Use the associated function on `Instruction` //! - Use the corresponding struct and call `Instruction::from` //! -//! # Examples +//! Both produce the same result, but the first is more concise. The structs are more useful when +//! reading instructions, as shown below. //! //! ``` //! # use dust_lang::instruction::{Instruction, Move}; @@ -35,24 +36,63 @@ //! assert_eq!(move_1, move_2); //! ``` //! -//! Use the `Destination` and `Argument` enums to create instructions. This is easier to read and -//! enforces consistency in how the `Instruction` methods are called. +//! Use the [`Argument`][] type when creating instructions. In addition to being easy to read and +//! write, this ensures that the instruction has the correct flags to represent the arguments. //! //! ``` -//! # use dust_lang::instruction::{Instruction, Add, Destination, Argument}; +//! # use dust_lang::instruction::{Instruction, Add, u8, Argument}; //! let add_1 = Instruction::add( -//! Destination::Register(0), +//! u8::Register(0), //! Argument::Local(1), //! Argument::Constant(2) //! ); //! let add_2 = Instruction::from(Add { -//! destination: Destination::Register(0), +//! destination: u8::Register(0), //! left: Argument::Local(1), //! right: Argument::Constant(2), //! }); //! //! assert_eq!(add_1, add_2); //! ``` +//! +//! ## Reading Instructions +//! +//! To read an instruction, check its `operation` field, then convert the instruction to the struct +//! that corresponds to that operation. Like the example above, this removes the burden of dealing +//! with the options and handles the process of reading the options and the instruction's fields +//! into `u8` or `Argument` enums when appropriate. +//! +//! ``` +//! # use dust_lang::instruction::{Instruction, Add, u8, Argument, Operation}; +//! # let mystery_instruction = Instruction::add( +//! u8::Local(1), +//! Argument::Local(1), +//! Argument::Constant(2) +//! ); +//! +//! // Let's read an instruction and see if it performs addition-assignment, like in one of the +//! // following examples: +//! // - `a += 2` +//! // - `a = a + 2` +//! // - `a = 2 + a` +//! +//! let operation = mystery_instruction.operation(); +//! +//! match operation { +//! Operation::ADD => { +//! let Add { destination, left, right } = Add::from(&mystery_instruction); +//! let is_add_assign = +//! left == Argument::Register(destination) +//! || right == Argument::Register(destination); +//! +//! assert!(is_add_assign); +//! } +//! // Handle other operations... +//! _ => { +//! panic!("Unknown operation code: {operation}"); +//! } +//! } +//! ``` mod add; mod call; mod call_native; @@ -117,23 +157,32 @@ 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 { - operation: Operation, - options: InstructionOptions, - a: u16, - b: u16, - c: u16, + pub metadata: u8, + pub a: u8, + pub b: u8, + pub c: u8, } impl Instruction { - pub fn r#move(from: u16, to: u16) -> Instruction { + pub fn operation(&self) -> Operation { + let operation_bits = self.metadata & 0b0001_1111; + + Operation::from(operation_bits) + } + + pub fn d(&self) -> bool { + self.metadata & 0b1000_0000 != 0 + } + + pub fn r#move(from: u8, to: u8) -> Instruction { Instruction::from(Move { from, to }) } - pub fn close(from: u16, to: u16) -> Instruction { + pub fn close(from: u8, to: u8) -> Instruction { Instruction::from(Close { from, to }) } - pub fn load_boolean(destination: Destination, value: bool, jump_next: bool) -> Instruction { + pub fn load_boolean(destination: u8, value: bool, jump_next: bool) -> Instruction { Instruction::from(LoadBoolean { destination, value, @@ -141,11 +190,7 @@ impl Instruction { }) } - pub fn load_constant( - destination: Destination, - constant_index: u16, - jump_next: bool, - ) -> Instruction { + pub fn load_constant(destination: u8, constant_index: u8, jump_next: bool) -> Instruction { Instruction::from(LoadConstant { destination, constant_index, @@ -153,32 +198,32 @@ impl Instruction { }) } - pub fn load_list(destination: Destination, start_register: u16) -> Instruction { + pub fn load_list(destination: u8, start_register: u8) -> Instruction { Instruction::from(LoadList { destination, start_register, }) } - pub fn load_self(destination: Destination) -> Instruction { + pub fn load_self(destination: u8) -> Instruction { Instruction::from(LoadSelf { destination }) } - pub fn get_local(destination: Destination, local_index: u16) -> Instruction { + pub fn get_local(destination: u8, local_index: u8) -> Instruction { Instruction::from(GetLocal { destination, local_index, }) } - pub fn set_local(register: u16, local_index: u16) -> Instruction { + pub fn set_local(register: u8, local_index: u8) -> Instruction { Instruction::from(SetLocal { local_index, register_index: register, }) } - pub fn add(destination: Destination, left: Argument, right: Argument) -> Instruction { + pub fn add(destination: u8, left: Argument, right: Argument) -> Instruction { Instruction::from(Add { destination, left, @@ -186,7 +231,7 @@ impl Instruction { }) } - pub fn subtract(destination: Destination, left: Argument, right: Argument) -> Instruction { + pub fn subtract(destination: u8, left: Argument, right: Argument) -> Instruction { Instruction::from(Subtract { destination, left, @@ -194,7 +239,7 @@ impl Instruction { }) } - pub fn multiply(destination: Destination, left: Argument, right: Argument) -> Instruction { + pub fn multiply(destination: u8, left: Argument, right: Argument) -> Instruction { Instruction::from(Multiply { destination, left, @@ -202,7 +247,7 @@ impl Instruction { }) } - pub fn divide(destination: Destination, left: Argument, right: Argument) -> Instruction { + pub fn divide(destination: u8, left: Argument, right: Argument) -> Instruction { Instruction::from(Divide { destination, left, @@ -210,7 +255,7 @@ impl Instruction { }) } - pub fn modulo(destination: Destination, left: Argument, right: Argument) -> Instruction { + pub fn modulo(destination: u8, left: Argument, right: Argument) -> Instruction { Instruction::from(Modulo { destination, left, @@ -225,7 +270,7 @@ impl Instruction { }) } - pub fn test_set(destination: Destination, argument: Argument, value: bool) -> Instruction { + pub fn test_set(destination: u8, argument: Argument, value: bool) -> Instruction { Instruction::from(TestSet { destination, argument, @@ -233,12 +278,7 @@ impl Instruction { }) } - pub fn equal( - destination: Destination, - value: bool, - left: Argument, - right: Argument, - ) -> Instruction { + pub fn equal(destination: u8, value: bool, left: Argument, right: Argument) -> Instruction { Instruction::from(Equal { destination, value, @@ -247,12 +287,7 @@ impl Instruction { }) } - pub fn less( - destination: Destination, - value: bool, - left: Argument, - right: Argument, - ) -> Instruction { + pub fn less(destination: u8, value: bool, left: Argument, right: Argument) -> Instruction { Instruction::from(Less { destination, value, @@ -262,7 +297,7 @@ impl Instruction { } pub fn less_equal( - destination: Destination, + destination: u8, value: bool, left: Argument, right: Argument, @@ -275,28 +310,28 @@ impl Instruction { }) } - pub fn negate(destination: Destination, argument: Argument) -> Instruction { + pub fn negate(destination: u8, argument: Argument) -> Instruction { Instruction::from(Negate { destination, argument, }) } - pub fn not(destination: Destination, argument: Argument) -> Instruction { + pub fn not(destination: u8, argument: Argument) -> Instruction { Instruction::from(Not { destination, argument, }) } - pub fn jump(offset: u16, is_positive: bool) -> Instruction { + pub fn jump(offset: u8, is_positive: bool) -> Instruction { Instruction::from(Jump { offset, is_positive, }) } - pub fn call(destination: Destination, function: Argument, argument_count: u16) -> Instruction { + pub fn call(destination: u8, function: Argument, argument_count: u8) -> Instruction { Instruction::from(Call { destination, function, @@ -305,9 +340,9 @@ impl Instruction { } pub fn call_native( - destination: Destination, + destination: u8, function: NativeFunction, - argument_count: u16, + argument_count: u8, ) -> Instruction { Instruction::from(CallNative { destination, @@ -323,55 +358,46 @@ impl Instruction { } pub fn destination_as_argument(&self) -> Option { - match self.operation { - Operation::LOAD_CONSTANT => Some(Argument::Constant(self.b)), - Operation::GET_LOCAL => Some(Argument::Local(self.b)), - Operation::LOAD_BOOLEAN - | Operation::LOAD_LIST - | Operation::LOAD_SELF - | Operation::ADD - | Operation::SUBTRACT - | Operation::MULTIPLY - | Operation::DIVIDE - | Operation::MODULO - | Operation::NEGATE - | Operation::NOT - | Operation::CALL - | Operation::CALL_NATIVE => Some(Argument::Register(self.a)), + match self.operation() { + Operation::LoadConstant => Some(Argument::Constant(self.b)), + Operation::LoadBoolean + | Operation::LoadList + | Operation::LoadSelf + | Operation::GetLocal + | Operation::Add + | Operation::Subtract + | Operation::Multiply + | Operation::Divide + | Operation::Modulo + | Operation::Negate + | Operation::Not + | Operation::Call + | Operation::CallNative => Some(Argument::Register(self.a)), _ => None, } } - pub fn a_as_destination(&self) -> Destination { - if self.options.a_is_local() { - Destination::Local(self.a) - } else { - Destination::Register(self.a) - } - } - pub fn b_as_argument(&self) -> Argument { - if self.options.b_is_constant() { + let b_is_constant = self.metadata & 0b0010_0000 != 0; + + if b_is_constant { Argument::Constant(self.b) - } else if self.options.b_is_local() { - Argument::Local(self.b) } else { Argument::Register(self.b) } } pub fn b_and_c_as_arguments(&self) -> (Argument, Argument) { - let left = if self.options.b_is_constant() { + 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) - } else if self.options.b_is_local() { - Argument::Local(self.b) } else { Argument::Register(self.b) }; - let right = if self.options.c_is_constant() { + let right = if c_is_constant { Argument::Constant(self.c) - } else if self.options.c_is_local() { - Argument::Local(self.c) } else { Argument::Register(self.c) }; @@ -380,58 +406,53 @@ impl Instruction { } pub fn yields_value(&self) -> bool { - match self.operation { - Operation::LOAD_BOOLEAN - | Operation::LOAD_CONSTANT - | Operation::LOAD_LIST - | Operation::LOAD_SELF - | Operation::GET_LOCAL - | Operation::ADD - | Operation::SUBTRACT - | Operation::MULTIPLY - | Operation::DIVIDE - | Operation::MODULO - | Operation::NEGATE - | Operation::NOT - | Operation::CALL => true, - Operation::MOVE - | Operation::CLOSE - | Operation::SET_LOCAL - | Operation::EQUAL - | Operation::LESS - | Operation::LESS_EQUAL - | Operation::TEST - | Operation::TEST_SET - | Operation::JUMP - | Operation::RETURN => false, - Operation::CALL_NATIVE => { + match self.operation() { + Operation::LoadBoolean + | Operation::LoadConstant + | Operation::LoadList + | Operation::LoadMap + | Operation::LoadSelf + | Operation::GetLocal + | Operation::Add + | Operation::Subtract + | Operation::Multiply + | Operation::Divide + | Operation::Modulo + | Operation::Power + | Operation::Negate + | Operation::Not + | Operation::Equal + | Operation::Less + | Operation::LessEqual + | Operation::Call => true, + Operation::CallNative => { let function = NativeFunction::from(self.b); function.returns_value() } - _ => { - if cfg!(debug_assertions) { - panic!("Unknown operation {}", self.operation); - } else { - false - } - } + Operation::Move + | Operation::Close + | Operation::SetLocal + | Operation::Test + | Operation::TestSet + | Operation::Jump + | Operation::Return => false, } } pub fn disassembly_info(&self) -> String { - match self.operation { - Operation::MOVE => { + match self.operation() { + Operation::Move => { let Move { from, to } = Move::from(self); format!("R{to} = R{from}") } - Operation::CLOSE => { + Operation::Close => { let Close { from, to } = Close::from(self); format!("R{from}..R{to}") } - Operation::LOAD_BOOLEAN => { + Operation::LoadBoolean => { let LoadBoolean { destination, value, @@ -439,12 +460,12 @@ impl Instruction { } = LoadBoolean::from(self); if jump_next { - format!("{destination} = {value} && JUMP +1") + format!("R{destination} = {value} && JUMP +1") } else { - format!("{destination} = {value}") + format!("R{destination} = {value}") } } - Operation::LOAD_CONSTANT => { + Operation::LoadConstant => { let LoadConstant { destination, constant_index, @@ -452,87 +473,87 @@ impl Instruction { } = LoadConstant::from(self); if jump_next { - format!("{destination} = C{constant_index} JUMP +1") + format!("R{destination} = C{constant_index} JUMP +1") } else { - format!("{destination} = C{constant_index}") + format!("R{destination} = C{constant_index}") } } - Operation::LOAD_LIST => { + Operation::LoadList => { let LoadList { destination, start_register, } = LoadList::from(self); - let end_register = destination.index().saturating_sub(1); + let end_register = destination.saturating_sub(1); - format!("{destination} = [R{start_register}..=R{end_register}]",) + format!("R{destination} = [R{start_register}..=R{end_register}]",) } - Operation::LOAD_SELF => { + Operation::LoadSelf => { let LoadSelf { destination } = LoadSelf::from(self); - format!("{destination} = self") + format!("R{destination} = self") } - Operation::GET_LOCAL => { + Operation::GetLocal => { let GetLocal { destination, local_index, } = GetLocal::from(self); - format!("{destination} = L{local_index}") + format!("R{destination} = L{local_index}") } - Operation::SET_LOCAL => { + Operation::SetLocal => { let SetLocal { - register_index: register, + register_index, local_index, } = SetLocal::from(self); - format!("L{local_index} = R{register}") + format!("L{local_index} = R{register_index}") } - Operation::ADD => { + Operation::Add => { let Add { destination, left, right, } = Add::from(self); - format!("{destination} = {left} + {right}") + format!("R{destination} = {left} + {right}") } - Operation::SUBTRACT => { + Operation::Subtract => { let Subtract { destination, left, right, } = Subtract::from(self); - format!("{destination} = {left} - {right}") + format!("R{destination} = {left} - {right}") } - Operation::MULTIPLY => { + Operation::Multiply => { let Multiply { destination, left, right, } = Multiply::from(self); - format!("{destination} = {left} * {right}") + format!("R{destination} = {left} * {right}") } - Operation::DIVIDE => { + Operation::Divide => { let Divide { destination, left, right, } = Divide::from(self); - format!("{destination} = {left} / {right}") + format!("R{destination} = {left} / {right}") } - Operation::MODULO => { + Operation::Modulo => { let Modulo { destination, left, right, } = Modulo::from(self); - format!("{destination} = {left} % {right}") + format!("R{destination} = {left} % {right}") } - Operation::TEST => { + Operation::Test => { let Test { argument, test_value: value, @@ -541,7 +562,7 @@ impl Instruction { format!("if {bang}{argument} {{ JUMP +1 }}",) } - Operation::TEST_SET => { + Operation::TestSet => { let TestSet { destination, argument, @@ -549,9 +570,9 @@ impl Instruction { } = TestSet::from(self); let bang = if value { "" } else { "!" }; - format!("if {bang}{argument} {{ JUMP +1 }} else {{ {destination} = {argument} }}") + format!("if {bang}{argument} {{ JUMP +1 }} else {{ R{destination} = {argument} }}") } - Operation::EQUAL => { + Operation::Equal => { let Equal { destination, value, @@ -560,9 +581,9 @@ impl Instruction { } = Equal::from(self); let comparison_symbol = if value { "==" } else { "!=" }; - format!("{destination} = {left} {comparison_symbol} {right}") + format!("R{destination} = {left} {comparison_symbol} {right}") } - Operation::LESS => { + Operation::Less => { let Less { destination, value, @@ -571,9 +592,9 @@ impl Instruction { } = Less::from(self); let comparison_symbol = if value { "<" } else { ">=" }; - format!("{destination} {left} {comparison_symbol} {right}") + format!("R{destination} {left} {comparison_symbol} {right}") } - Operation::LESS_EQUAL => { + Operation::LessEqual => { let LessEqual { destination, value, @@ -582,25 +603,25 @@ impl Instruction { } = LessEqual::from(self); let comparison_symbol = if value { "<=" } else { ">" }; - format!("{destination} {left} {comparison_symbol} {right}") + format!("R{destination} {left} {comparison_symbol} {right}") } - Operation::NEGATE => { + Operation::Negate => { let Negate { destination, argument, } = Negate::from(self); - format!("{destination} = -{argument}") + format!("R{destination} = -{argument}") } - Operation::NOT => { + Operation::Not => { let Not { destination, argument, } = Not::from(self); - format!("{destination} = !{argument}") + format!("R{destination} = !{argument}") } - Operation::JUMP => { + Operation::Jump => { let Jump { offset, is_positive, @@ -612,33 +633,33 @@ impl Instruction { format!("JUMP -{offset}") } } - Operation::CALL => { + Operation::Call => { let Call { destination, function, argument_count, } = Call::from(self); - let arguments_start = destination.index().saturating_sub(argument_count); + let arguments_start = destination.saturating_sub(argument_count); let arguments_end = arguments_start + argument_count; - format!("{destination} = {function}(R{arguments_start}..R{arguments_end})") + format!("R{destination} = {function}(R{arguments_start}..R{arguments_end})") } - Operation::CALL_NATIVE => { + Operation::CallNative => { let CallNative { destination, function, argument_count, } = CallNative::from(self); - let arguments_start = destination.index().saturating_sub(argument_count); + let arguments_start = destination.saturating_sub(argument_count); let arguments_end = arguments_start + argument_count; if function.returns_value() { - format!("{destination} = {function}(R{arguments_start}..R{arguments_end})") + format!("R{destination} = {function}(R{arguments_start}..R{arguments_end})") } else { format!("{function}(R{arguments_start}..R{arguments_end})") } } - Operation::RETURN => { + Operation::Return => { let Return { should_return_value, } = Return::from(self); @@ -651,7 +672,7 @@ impl Instruction { } _ => { if cfg!(debug_assertions) { - panic!("Unknown operation {}", self.operation); + panic!("Unknown operation {}", self.operation()); } else { "RETURN".to_string() } @@ -662,61 +683,20 @@ impl Instruction { impl Debug for Instruction { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{} {}", self.operation, self.disassembly_info()) - } -} - -#[derive(Debug, Clone, Copy)] -pub enum Destination { - Local(u16), - Register(u16), -} - -impl Destination { - pub fn index(&self) -> u16 { - match self { - Destination::Local(index) => *index, - Destination::Register(index) => *index, - } - } - - pub fn is_local(&self) -> bool { - matches!(self, Destination::Local(_)) - } - - pub fn is_register(&self) -> bool { - matches!(self, Destination::Register(_)) - } - - pub fn as_index_and_a_options(&self) -> (u16, InstructionOptions) { - match self { - Destination::Local(index) => (*index, InstructionOptions::A_IS_LOCAL), - Destination::Register(index) => (*index, InstructionOptions::empty()), - } - } -} - -impl Display for Destination { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Destination::Local(index) => write!(f, "L{index}"), - Destination::Register(index) => write!(f, "R{index}"), - } + write!(f, "{} {}", self.operation(), self.disassembly_info()) } } #[derive(Debug, Clone, Copy)] pub enum Argument { - Constant(u16), - Local(u16), - Register(u16), + Constant(u8), + Register(u8), } impl Argument { - pub fn index(&self) -> u16 { + pub fn index(&self) -> u8 { match self { Argument::Constant(index) => *index, - Argument::Local(index) => *index, Argument::Register(index) => *index, } } @@ -725,26 +705,20 @@ impl Argument { matches!(self, Argument::Constant(_)) } - pub fn is_local(&self) -> bool { - matches!(self, Argument::Local(_)) - } - pub fn is_register(&self) -> bool { matches!(self, Argument::Register(_)) } - pub fn as_index_and_b_options(&self) -> (u16, InstructionOptions) { + pub fn as_index_and_b_options(&self) -> (u8, InstructionOptions) { match self { Argument::Constant(index) => (*index, InstructionOptions::B_IS_CONSTANT), - Argument::Local(index) => (*index, InstructionOptions::B_IS_LOCAL), Argument::Register(index) => (*index, InstructionOptions::empty()), } } - pub fn as_index_and_c_options(&self) -> (u16, InstructionOptions) { + pub fn as_index_and_c_options(&self) -> (u8, InstructionOptions) { match self { Argument::Constant(index) => (*index, InstructionOptions::C_IS_CONSTANT), - Argument::Local(index) => (*index, InstructionOptions::C_IS_LOCAL), Argument::Register(index) => (*index, InstructionOptions::empty()), } } @@ -754,8 +728,27 @@ impl Display for Argument { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Argument::Constant(index) => write!(f, "C{index}"), - Argument::Local(index) => write!(f, "L{index}"), Argument::Register(index) => write!(f, "R{index}"), } } } + +#[cfg(test)] +mod tests { + use std::mem::offset_of; + + use super::*; + + #[test] + fn instruction_is_8_bytes() { + assert_eq!(size_of::(), 8); + } + + #[test] + fn instruction_layout() { + assert_eq!(offset_of!(Instruction, a), 0); + assert_eq!(offset_of!(Instruction, b), 1); + assert_eq!(offset_of!(Instruction, c), 2); + assert_eq!(offset_of!(Instruction, metadata), 3); + } +} diff --git a/dust-lang/src/instruction/modulo.rs b/dust-lang/src/instruction/modulo.rs index f187a60..ac3b04f 100644 --- a/dust-lang/src/instruction/modulo.rs +++ b/dust-lang/src/instruction/modulo.rs @@ -1,14 +1,14 @@ -use crate::{Argument, Destination, Instruction, Operation}; +use crate::{Argument, Instruction, Operation}; pub struct Modulo { - pub destination: Destination, + pub destination: u8, pub left: Argument, pub right: Argument, } impl From<&Instruction> for Modulo { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; let (left, right) = instruction.b_and_c_as_arguments(); Modulo { @@ -21,16 +21,11 @@ impl From<&Instruction> for Modulo { impl From for Instruction { fn from(modulo: Modulo) -> Self { - let (a, a_options) = modulo.destination.as_index_and_a_options(); + 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(); - Instruction { - operation: Operation::MODULO, - options: a_options | b_options | c_options, - a, - b, - c, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/move.rs b/dust-lang/src/instruction/move.rs index 6be4cc4..8eed616 100644 --- a/dust-lang/src/instruction/move.rs +++ b/dust-lang/src/instruction/move.rs @@ -1,10 +1,8 @@ use crate::{Instruction, Operation}; -use super::InstructionOptions; - pub struct Move { - pub from: u16, - pub to: u16, + pub from: u8, + pub to: u8, } impl From<&Instruction> for Move { @@ -18,12 +16,11 @@ impl From<&Instruction> for Move { impl From for Instruction { fn from(r#move: Move) -> Self { - Instruction { - operation: Operation::MOVE, - options: InstructionOptions::empty(), - a: 0, - b: r#move.from, - c: r#move.to, - } + let metadata = Operation::Move as u8; + let a = 0; + let b = r#move.from; + let c = r#move.to; + + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/multiply.rs b/dust-lang/src/instruction/multiply.rs index e00ae0b..53af4a6 100644 --- a/dust-lang/src/instruction/multiply.rs +++ b/dust-lang/src/instruction/multiply.rs @@ -1,14 +1,14 @@ -use crate::{Argument, Destination, Instruction, Operation}; +use crate::{Argument, Instruction, Operation}; pub struct Multiply { - pub destination: Destination, + pub destination: u8, pub left: Argument, pub right: Argument, } impl From<&Instruction> for Multiply { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; let (left, right) = instruction.b_and_c_as_arguments(); Multiply { @@ -21,16 +21,11 @@ impl From<&Instruction> for Multiply { impl From for Instruction { fn from(multiply: Multiply) -> Self { - let (a, a_options) = multiply.destination.as_index_and_a_options(); + 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(); - Instruction { - operation: Operation::MULTIPLY, - options: a_options | b_options | c_options, - a, - b, - c, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/negate.rs b/dust-lang/src/instruction/negate.rs index 1979b6a..b1db5d3 100644 --- a/dust-lang/src/instruction/negate.rs +++ b/dust-lang/src/instruction/negate.rs @@ -1,13 +1,13 @@ -use crate::{Argument, Destination, Instruction, Operation}; +use crate::{Argument, Instruction, Operation}; pub struct Negate { - pub destination: Destination, + pub destination: u8, pub argument: Argument, } impl From<&Instruction> for Negate { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; let argument = instruction.b_as_argument(); Negate { @@ -19,15 +19,11 @@ impl From<&Instruction> for Negate { impl From for Instruction { fn from(negate: Negate) -> Self { - let (a, a_options) = negate.destination.as_index_and_a_options(); + let a = negate.destination; let (b, b_options) = negate.argument.as_index_and_b_options(); + let c = 0; + let metadata = Operation::Negate as u8 | b_options.bits(); - Instruction { - operation: Operation::NEGATE, - options: a_options | b_options, - a, - b, - c: 0, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/not.rs b/dust-lang/src/instruction/not.rs index b67f3bc..156d059 100644 --- a/dust-lang/src/instruction/not.rs +++ b/dust-lang/src/instruction/not.rs @@ -1,13 +1,13 @@ -use crate::{Argument, Destination, Instruction, Operation}; +use crate::{Argument, Instruction, Operation}; pub struct Not { - pub destination: Destination, + pub destination: u8, pub argument: Argument, } impl From<&Instruction> for Not { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; let argument = instruction.b_as_argument(); Not { @@ -19,15 +19,11 @@ impl From<&Instruction> for Not { impl From for Instruction { fn from(not: Not) -> Self { - let (a, a_options) = not.destination.as_index_and_a_options(); + 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(); - Instruction { - operation: Operation::NOT, - options: a_options | b_options, - a, - b, - c: 0, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/operation.rs b/dust-lang/src/instruction/operation.rs index 0e9bbec..e1c1784 100644 --- a/dust-lang/src/instruction/operation.rs +++ b/dust-lang/src/instruction/operation.rs @@ -4,75 +4,141 @@ use std::fmt::{self, Debug, Display, Formatter}; use serde::{Deserialize, Serialize}; +pub const MOVE_BYTE: u8 = 0; +pub const CLOSE_BYTE: u8 = 1; +pub const LOAD_BOOLEAN_BYTE: u8 = 2; +pub const LOAD_CONSTANT_BYTE: u8 = 3; +pub const LOAD_LIST_BYTE: u8 = 4; +pub const LOAD_MAP_BYTE: u8 = 5; +pub const LOAD_SELF_BYTE: u8 = 6; +pub const GET_LOCAL_BYTE: u8 = 7; +pub const SET_LOCAL_BYTE: u8 = 8; +pub const ADD_BYTE: u8 = 9; +pub const SUBTRACT_BYTE: u8 = 10; +pub const MULTIPLY_BYTE: u8 = 11; +pub const DIVIDE_BYTE: u8 = 12; +pub const MODULO_BYTE: u8 = 13; +pub const POWER_BYTE: u8 = 14; +pub const TEST_BYTE: u8 = 15; +pub const TEST_SET_BYTE: u8 = 16; +pub const EQUAL_BYTE: u8 = 17; +pub const LESS_BYTE: u8 = 18; +pub const LESS_EQUAL_BYTE: u8 = 19; +pub const NEGATE_BYTE: u8 = 20; +pub const NOT_BYTE: u8 = 21; +pub const CALL_BYTE: u8 = 22; +pub const CALL_NATIVE_BYTE: u8 = 23; +pub const JUMP_BYTE: u8 = 24; +pub const RETURN_BYTE: u8 = 25; + +/// Part of an [Instruction][crate::Instruction] that is encoded as a single byte. #[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Operation(u8); +#[repr(u8)] +pub enum Operation { + Move = MOVE_BYTE, + Close = CLOSE_BYTE, + LoadBoolean = LOAD_BOOLEAN_BYTE, + LoadConstant = LOAD_CONSTANT_BYTE, + LoadList = LOAD_LIST_BYTE, + LoadMap = LOAD_MAP_BYTE, + LoadSelf = LOAD_SELF_BYTE, + GetLocal = GET_LOCAL_BYTE, + SetLocal = SET_LOCAL_BYTE, + Add = ADD_BYTE, + Subtract = SUBTRACT_BYTE, + Multiply = MULTIPLY_BYTE, + Divide = DIVIDE_BYTE, + Modulo = MODULO_BYTE, + Power = POWER_BYTE, + Test = TEST_BYTE, + TestSet = TEST_SET_BYTE, + Equal = EQUAL_BYTE, + Less = LESS_BYTE, + LessEqual = LESS_EQUAL_BYTE, + Negate = NEGATE_BYTE, + Not = NOT_BYTE, + Call = CALL_BYTE, + CallNative = CALL_NATIVE_BYTE, + Jump = JUMP_BYTE, + Return = RETURN_BYTE, +} -impl Operation { - pub const MOVE: Operation = Operation(0); - pub const CLOSE: Operation = Operation(1); - pub const LOAD_BOOLEAN: Operation = Operation(2); - pub const LOAD_CONSTANT: Operation = Operation(3); - pub const LOAD_LIST: Operation = Operation(4); - pub const LOAD_SELF: Operation = Operation(5); - pub const GET_LOCAL: Operation = Operation(6); - pub const SET_LOCAL: Operation = Operation(7); - pub const ADD: Operation = Operation(8); - pub const SUBTRACT: Operation = Operation(9); - pub const MULTIPLY: Operation = Operation(10); - pub const DIVIDE: Operation = Operation(11); - pub const MODULO: Operation = Operation(12); - pub const TEST: Operation = Operation(13); - pub const TEST_SET: Operation = Operation(14); - pub const EQUAL: Operation = Operation(15); - pub const LESS: Operation = Operation(16); - pub const LESS_EQUAL: Operation = Operation(17); - pub const NEGATE: Operation = Operation(18); - pub const NOT: Operation = Operation(19); - pub const CALL: Operation = Operation(20); - pub const CALL_NATIVE: Operation = Operation(21); - pub const JUMP: Operation = Operation(22); - pub const RETURN: Operation = Operation(23); - - pub fn name(self) -> &'static str { - match self { - Self::MOVE => "MOVE", - Self::CLOSE => "CLOSE", - Self::LOAD_BOOLEAN => "LOAD_BOOLEAN", - Self::LOAD_CONSTANT => "LOAD_CONSTANT", - Self::LOAD_LIST => "LOAD_LIST", - Self::LOAD_SELF => "LOAD_SELF", - Self::GET_LOCAL => "GET_LOCAL", - Self::SET_LOCAL => "SET_LOCAL", - Self::ADD => "ADD", - Self::SUBTRACT => "SUBTRACT", - Self::MULTIPLY => "MULTIPLY", - Self::DIVIDE => "DIVIDE", - Self::MODULO => "MODULO", - Self::TEST => "TEST", - Self::TEST_SET => "TEST_SET", - Self::EQUAL => "EQUAL", - Self::LESS => "LESS", - Self::LESS_EQUAL => "LESS_EQUAL", - Self::NEGATE => "NEGATE", - Self::NOT => "NOT", - Self::CALL => "CALL", - Self::CALL_NATIVE => "CALL_NATIVE", - Self::JUMP => "JUMP", - Self::RETURN => "RETURN", +impl From for Operation { + fn from(byte: u8) -> Self { + match byte { + MOVE_BYTE => Self::Move, + CLOSE_BYTE => Self::Close, + LOAD_BOOLEAN_BYTE => Self::LoadBoolean, + LOAD_CONSTANT_BYTE => Self::LoadConstant, + LOAD_LIST_BYTE => Self::LoadList, + LOAD_MAP_BYTE => Self::LoadMap, + LOAD_SELF_BYTE => Self::LoadSelf, + GET_LOCAL_BYTE => Self::GetLocal, + SET_LOCAL_BYTE => Self::SetLocal, + ADD_BYTE => Self::Add, + SUBTRACT_BYTE => Self::Subtract, + MULTIPLY_BYTE => Self::Multiply, + DIVIDE_BYTE => Self::Divide, + MODULO_BYTE => Self::Modulo, + POWER_BYTE => Self::Power, + TEST_BYTE => Self::Test, + TEST_SET_BYTE => Self::TestSet, + EQUAL_BYTE => Self::Equal, + LESS_BYTE => Self::Less, + LESS_EQUAL_BYTE => Self::LessEqual, + NEGATE_BYTE => Self::Negate, + NOT_BYTE => Self::Not, + CALL_BYTE => Self::Call, + CALL_NATIVE_BYTE => Self::CallNative, + JUMP_BYTE => Self::Jump, + RETURN_BYTE => Self::Return, _ => { if cfg!(debug_assertions) { - panic!("Unknown operation code {}", self.0); + panic!("Invalid operation byte: {}", byte) } else { - "RETURN" + Self::Return } } } } } +impl Operation { + pub fn name(self) -> &'static str { + match self { + Self::Move => "MOVE", + Self::Close => "CLOSE", + Self::LoadBoolean => "LOAD_BOOLEAN", + Self::LoadConstant => "LOAD_CONSTANT", + Self::LoadList => "LOAD_LIST", + Self::LoadMap => "LOAD_MAP", + Self::LoadSelf => "LOAD_SELF", + Self::GetLocal => "GET_LOCAL", + Self::SetLocal => "SET_LOCAL", + Self::Add => "ADD", + Self::Subtract => "SUBTRACT", + Self::Multiply => "MULTIPLY", + Self::Divide => "DIVIDE", + Self::Modulo => "MODULO", + Self::Power => "POWER", + Self::Test => "TEST", + Self::TestSet => "TEST_SET", + Self::Equal => "EQUAL", + Self::Less => "LESS", + Self::LessEqual => "LESS_EQUAL", + Self::Negate => "NEGATE", + Self::Not => "NOT", + Self::Call => "CALL", + Self::CallNative => "CALL_NATIVE", + Self::Jump => "JUMP", + Self::Return => "RETURN", + } + } +} + impl Debug for Operation { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Operation({})", self.name()) + write!(f, "{}", self.name()) } } @@ -81,3 +147,56 @@ impl Display for Operation { write!(f, "{}", self.name()) } } + +#[cfg(test)] +mod tests { + use super::*; + + const ALL_OPERATIONS: [Operation; 26] = [ + Operation::Move, + Operation::Close, + Operation::LoadBoolean, + Operation::LoadConstant, + Operation::LoadList, + Operation::LoadMap, + Operation::LoadSelf, + Operation::GetLocal, + Operation::SetLocal, + Operation::Add, + Operation::Subtract, + Operation::Multiply, + Operation::Divide, + Operation::Modulo, + Operation::Power, + Operation::Test, + Operation::TestSet, + Operation::Equal, + Operation::Less, + Operation::LessEqual, + Operation::Negate, + Operation::Not, + Operation::Call, + Operation::CallNative, + Operation::Jump, + Operation::Return, + ]; + + #[test] + fn operations_are_unique() { + for (i, operation) in ALL_OPERATIONS.into_iter().enumerate() { + assert_eq!(i, operation as usize); + } + } + + #[test] + fn operation_uses_five_bits() { + for operation in ALL_OPERATIONS { + assert_eq!(operation as u8 & 0b1110_0000, 0); + } + } + + #[test] + fn operation_is_one_byte() { + assert_eq!(size_of::(), 1); + } +} diff --git a/dust-lang/src/instruction/options.rs b/dust-lang/src/instruction/options.rs index f3f2b19..8c1f4d4 100644 --- a/dust-lang/src/instruction/options.rs +++ b/dust-lang/src/instruction/options.rs @@ -1,42 +1,33 @@ +//! 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 A_IS_LOCAL = 0b00000001; + const B_IS_CONSTANT = 0b0010_0000; - const B_IS_CONSTANT = 0b00000010; - const B_IS_LOCAL = 0b00000100; + const C_IS_CONSTANT = 0b0100_0000; - const C_IS_CONSTANT = 0b00001000; - const C_IS_LOCAL = 0b00010000; - - const D_IS_TRUE = 0b00100000; + const D_IS_TRUE = 0b1000_0000; } } impl InstructionOptions { - pub fn a_is_local(self) -> bool { - self.contains(Self::A_IS_LOCAL) - } - pub fn b_is_constant(self) -> bool { self.contains(Self::B_IS_CONSTANT) } - pub fn b_is_local(self) -> bool { - self.contains(Self::B_IS_LOCAL) - } - pub fn c_is_constant(self) -> bool { self.contains(Self::C_IS_CONSTANT) } - pub fn c_is_local(self) -> bool { - self.contains(Self::C_IS_LOCAL) - } - 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 d38dc4e..1c287d7 100644 --- a/dust-lang/src/instruction/return.rs +++ b/dust-lang/src/instruction/return.rs @@ -1,7 +1,5 @@ use crate::{Instruction, Operation}; -use super::InstructionOptions; - pub struct Return { pub should_return_value: bool, } @@ -18,14 +16,11 @@ impl From<&Instruction> for Return { impl From for Instruction { fn from(r#return: Return) -> Self { - let b = r#return.should_return_value as u16; + let metadata = Operation::Return as u8; + let a = 0; + let b = r#return.should_return_value as u8; + let c = 0; - Instruction { - operation: Operation::RETURN, - options: InstructionOptions::empty(), - a: 0, - b, - c: 0, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/set_local.rs b/dust-lang/src/instruction/set_local.rs index 1c2f8a3..317f63c 100644 --- a/dust-lang/src/instruction/set_local.rs +++ b/dust-lang/src/instruction/set_local.rs @@ -1,10 +1,8 @@ use crate::{Instruction, Operation}; -use super::InstructionOptions; - pub struct SetLocal { - pub register_index: u16, - pub local_index: u16, + pub register_index: u8, + pub local_index: u8, } impl From<&Instruction> for SetLocal { @@ -21,15 +19,11 @@ 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 b = set_local.register_index; let c = set_local.local_index; - Instruction { - operation: Operation::SET_LOCAL, - options: InstructionOptions::empty(), - a: 0, - b, - c, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/subtract.rs b/dust-lang/src/instruction/subtract.rs index e441a18..6fb32a8 100644 --- a/dust-lang/src/instruction/subtract.rs +++ b/dust-lang/src/instruction/subtract.rs @@ -1,14 +1,14 @@ -use crate::{Argument, Destination, Instruction, Operation}; +use crate::{Argument, Instruction, Operation}; pub struct Subtract { - pub destination: Destination, + pub destination: u8, pub left: Argument, pub right: Argument, } impl From<&Instruction> for Subtract { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; let (left, right) = instruction.b_and_c_as_arguments(); Subtract { @@ -21,16 +21,11 @@ impl From<&Instruction> for Subtract { impl From for Instruction { fn from(subtract: Subtract) -> Self { - let (a, a_options) = subtract.destination.as_index_and_a_options(); + 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(); - Instruction { - operation: Operation::SUBTRACT, - options: a_options | b_options | c_options, - a, - b, - c, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/test.rs b/dust-lang/src/instruction/test.rs index 420d2d5..5871187 100644 --- a/dust-lang/src/instruction/test.rs +++ b/dust-lang/src/instruction/test.rs @@ -19,15 +19,11 @@ 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 c = test.test_value as u16; + let c = test.test_value as u8; + let metadata = Operation::Test as u8 | options.bits(); - Instruction { - operation: Operation::TEST, - options, - a: 0, - b, - c, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/instruction/test_set.rs b/dust-lang/src/instruction/test_set.rs index 81fa8d2..3b4914b 100644 --- a/dust-lang/src/instruction/test_set.rs +++ b/dust-lang/src/instruction/test_set.rs @@ -1,14 +1,14 @@ -use crate::{Argument, Destination, Instruction, Operation}; +use crate::{Argument, Instruction, Operation}; pub struct TestSet { - pub destination: Destination, + pub destination: u8, pub argument: Argument, pub test_value: bool, } impl From<&Instruction> for TestSet { fn from(instruction: &Instruction) -> Self { - let destination = instruction.a_as_destination(); + let destination = instruction.a; let argument = instruction.b_as_argument(); let test_value = instruction.c != 0; @@ -22,16 +22,11 @@ impl From<&Instruction> for TestSet { impl From for Instruction { fn from(test_set: TestSet) -> Self { - let (a, a_options) = test_set.destination.as_index_and_a_options(); + let a = test_set.destination; let (b, b_options) = test_set.argument.as_index_and_b_options(); - let c = test_set.test_value as u16; + let c = test_set.test_value as u8; + let metadata = Operation::TestSet as u8 | b_options.bits(); - Instruction { - operation: Operation::TEST_SET, - options: a_options | b_options, - a, - b, - c, - } + Instruction { metadata, a, b, c } } } diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 19fbef4..1d0c765 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -35,7 +35,6 @@ pub mod dust_error; pub mod instruction; pub mod lexer; pub mod native_function; -pub mod optimize; pub mod scope; pub mod token; pub mod r#type; @@ -46,10 +45,9 @@ pub use crate::chunk::{Chunk, Local}; pub use crate::compiler::{compile, CompileError, Compiler}; pub use crate::disassembler::Disassembler; pub use crate::dust_error::{AnnotatedError, DustError}; -pub use crate::instruction::{Argument, Destination, Instruction, Operation}; +pub use crate::instruction::{Argument, Instruction, Operation}; pub use crate::lexer::{lex, LexError, Lexer}; pub use crate::native_function::{NativeFunction, NativeFunctionError}; -pub use crate::optimize::{optimize_control_flow, optimize_set_local}; pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict}; pub use crate::scope::Scope; pub use crate::token::{write_token_list, Token, TokenKind, TokenOwned}; diff --git a/dust-lang/src/native_function/logic.rs b/dust-lang/src/native_function/logic.rs index d0c055d..a06dc35 100644 --- a/dust-lang/src/native_function/logic.rs +++ b/dust-lang/src/native_function/logic.rs @@ -3,7 +3,7 @@ use std::io::{self, stdout, Write}; use crate::{ConcreteValue, Instruction, NativeFunctionError, Value, Vm, VmError}; pub fn panic<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result, VmError> { - let argument_count = instruction.c(); + let argument_count = instruction.c; let message = if argument_count == 0 { None } else { @@ -34,7 +34,7 @@ pub fn panic<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result(vm: &'a Vm<'a>, instruction: Instruction) -> Result, VmError> { - let argument_count = instruction.c(); + let argument_count = instruction.c; if argument_count != 1 { return Err(VmError::NativeFunction( @@ -63,7 +63,7 @@ pub fn to_string<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result(vm: &'a Vm<'a>, instruction: Instruction) -> Result, VmError> { - let argument_count = instruction.c(); + let argument_count = instruction.c; if argument_count != 0 { return Err(VmError::NativeFunction( @@ -91,8 +91,8 @@ pub fn read_line<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result(vm: &'a Vm<'a>, instruction: Instruction) -> Result, VmError> { - let to_register = instruction.a(); - let argument_count = instruction.c(); + let to_register = instruction.a; + let argument_count = instruction.c; let mut stdout = stdout(); let map_err = |io_error: io::Error| { VmError::NativeFunction(NativeFunctionError::Io { @@ -120,8 +120,8 @@ pub fn write<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result(vm: &'a Vm<'a>, instruction: Instruction) -> Result, VmError> { - let to_register = instruction.a(); - let argument_count = instruction.c(); + let to_register = instruction.a; + let argument_count = instruction.c; let mut stdout = stdout(); let map_err = |io_error: io::Error| { VmError::NativeFunction(NativeFunctionError::Io { diff --git a/dust-lang/src/native_function/mod.rs b/dust-lang/src/native_function/mod.rs index e0c40ea..0ba0949 100644 --- a/dust-lang/src/native_function/mod.rs +++ b/dust-lang/src/native_function/mod.rs @@ -75,8 +75,8 @@ macro_rules! define_native_function { } } - impl From for NativeFunction { - fn from(bytes: u16) -> Self { + impl From for NativeFunction { + fn from(bytes: u8) -> Self { match bytes { $( $bytes => NativeFunction::$name, diff --git a/dust-lang/src/type.rs b/dust-lang/src/type.rs index 04f743b..86b4419 100644 --- a/dust-lang/src/type.rs +++ b/dust-lang/src/type.rs @@ -278,13 +278,13 @@ impl Ord for Type { #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct FunctionType { - pub type_parameters: Option>, - pub value_parameters: Option>, + pub type_parameters: Option>, + pub value_parameters: Option>, pub return_type: Type, } impl FunctionType { - pub fn new>, U: Into>>( + pub fn new>, U: Into>>( type_parameters: Option, value_parameters: Option, return_type: Type, diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 7e49ed2..a521548 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -8,8 +8,7 @@ use smallvec::{smallvec, SmallVec}; use crate::{ compile, instruction::*, AbstractValue, AnnotatedError, Argument, Chunk, ConcreteValue, - Destination, DustError, Instruction, NativeFunctionError, Operation, Span, Value, ValueError, - ValueRef, + DustError, Instruction, NativeFunctionError, Operation, Span, Value, ValueError, ValueRef, }; pub fn run(source: &str) -> Result, DustError> { @@ -28,10 +27,9 @@ pub struct Vm<'a> { chunk: &'a Chunk, parent: Option<&'a Vm<'a>>, - local_definitions: SmallVec<[Option; 16]>, ip: usize, - last_assigned_register: Option, + last_assigned_register: Option, source: &'a str, } @@ -41,7 +39,6 @@ impl<'a> Vm<'a> { chunk, stack: smallvec![Register::Empty; chunk.stack_size()], parent, - local_definitions: smallvec![None; chunk.locals().len()], ip: 0, last_assigned_register: None, source, @@ -53,7 +50,10 @@ impl<'a> Vm<'a> { } pub fn current_position(&self) -> Span { - self.chunk.positions()[self.ip.saturating_sub(1)] + let index = self.ip.saturating_sub(1); + let (_, position) = self.chunk.instructions()[index]; + + position } pub fn run(&mut self) -> Result, VmError> { @@ -100,11 +100,10 @@ impl<'a> Vm<'a> { value, jump_next, } = LoadBoolean::from(&instruction); - let register_index = self.get_destination(destination)?; let boolean = ConcreteValue::Boolean(value).to_value(); let register = Register::Value(boolean); - self.set_register(register_index, register)?; + self.set_register(destination, register)?; if jump_next { self.jump(1, true); @@ -116,10 +115,9 @@ impl<'a> Vm<'a> { constant_index, jump_next, } = LoadConstant::from(&instruction); - let register_index = self.get_destination(destination)?; let register = Register::Pointer(Pointer::Constant(constant_index)); - self.set_register(register_index, register)?; + self.set_register(destination, register)?; if jump_next { self.jump(1, true); @@ -130,10 +128,9 @@ impl<'a> Vm<'a> { destination, start_register, } = LoadList::from(&instruction); - let register_index = self.get_destination(destination)?; let mut pointers = Vec::new(); - for register in start_register..register_index { + for register in start_register..destination { if let Some(Register::Empty) = self.stack.get(register as usize) { continue; } @@ -146,47 +143,33 @@ impl<'a> Vm<'a> { let register = Register::Value(AbstractValue::List { items: pointers }.to_value()); - self.set_register(register_index, register)?; + self.set_register(destination, register)?; } Operation::LoadSelf => { let LoadSelf { destination } = LoadSelf::from(&instruction); - let register_index = self.get_destination(destination)?; let register = Register::Value(AbstractValue::FunctionSelf.to_value()); - self.set_register(register_index, register)?; - } - Operation::DefineLocal => { - let DefineLocal { - register, - local_index, - is_mutable, - } = DefineLocal::from(&instruction); - - self.local_definitions[local_index as usize] = Some(register); + self.set_register(destination, register)?; } Operation::GetLocal => { let GetLocal { destination, local_index, } = GetLocal::from(&instruction); - let register_index = self.get_destination(destination)?; - let local_register = self.local_definitions[local_index as usize].ok_or( - VmError::UndefinedLocal { - local_index, - position: self.current_position(), - }, - )?; + let local_register = self.get_local_register(local_index)?; let register = Register::Pointer(Pointer::Stack(local_register)); - self.set_register(register_index, register)?; + self.set_register(destination, register)?; } Operation::SetLocal => { let SetLocal { - register_index: register, + register_index, local_index, } = SetLocal::from(&instruction); + let local_register_index = self.get_local_register(local_index)?; + let register = Register::Pointer(Pointer::Stack(register_index)); - self.local_definitions[local_index as usize] = Some(register); + self.set_register(local_register_index, register); } Operation::Add => { let Add { @@ -194,26 +177,20 @@ impl<'a> Vm<'a> { left, right, } = Add::from(&instruction); - let register_index = self.get_destination(destination)?; let left = self.get_argument(left)?; let right = self.get_argument(right)?; let sum_result = left.add(right); + let sum = match sum_result { + Ok(sum) => sum, + Err(error) => { + return Err(VmError::Value { + error, + position: self.current_position(), + }); + } + }; - assert!( - sum_result.is_ok(), - "VM Error: {}", - DustError::runtime( - VmError::Value { - error: sum_result.unwrap_err(), - position: self.current_position() - }, - self.source - ) - ); - - let sum = sum_result.unwrap(); - - self.set_register(register_index, Register::Value(sum))?; + self.set_register(destination, Register::Value(sum))?; } Operation::Subtract => { let Subtract { @@ -221,15 +198,20 @@ impl<'a> Vm<'a> { left, right, } = Subtract::from(&instruction); - let register_index = self.get_destination(destination)?; let left = self.get_argument(left)?; let right = self.get_argument(right)?; - let difference = left.subtract(right).map_err(|error| VmError::Value { - error, - position: self.current_position(), - })?; + let subtraction_result = left.subtract(right); + let difference = match subtraction_result { + Ok(difference) => difference, + Err(error) => { + return Err(VmError::Value { + error, + position: self.current_position(), + }); + } + }; - self.set_register(register_index, Register::Value(difference))?; + self.set_register(destination, Register::Value(difference))?; } Operation::Multiply => { let Multiply { @@ -237,15 +219,20 @@ impl<'a> Vm<'a> { left, right, } = Multiply::from(&instruction); - let register_index = self.get_destination(destination)?; let left = self.get_argument(left)?; let right = self.get_argument(right)?; - let product = left.multiply(right).map_err(|error| VmError::Value { - error, - position: self.current_position(), - })?; + let multiplication_result = left.multiply(right); + let product = match multiplication_result { + Ok(product) => product, + Err(error) => { + return Err(VmError::Value { + error, + position: self.current_position(), + }); + } + }; - self.set_register(register_index, Register::Value(product))?; + self.set_register(destination, Register::Value(product))?; } Operation::Divide => { let Divide { @@ -253,15 +240,20 @@ impl<'a> Vm<'a> { left, right, } = Divide::from(&instruction); - let register_index = self.get_destination(destination)?; let left = self.get_argument(left)?; let right = self.get_argument(right)?; - let quotient = left.divide(right).map_err(|error| VmError::Value { - error, - position: self.current_position(), - })?; + let division_result = left.divide(right); + let quotient = match division_result { + Ok(quotient) => quotient, + Err(error) => { + return Err(VmError::Value { + error, + position: self.current_position(), + }); + } + }; - self.set_register(register_index, Register::Value(quotient))?; + self.set_register(destination, Register::Value(quotient))?; } Operation::Modulo => { let Modulo { @@ -269,15 +261,20 @@ impl<'a> Vm<'a> { left, right, } = Modulo::from(&instruction); - let register_index = self.get_destination(destination)?; let left = self.get_argument(left)?; let right = self.get_argument(right)?; - let remainder = left.modulo(right).map_err(|error| VmError::Value { - error, - position: self.current_position(), - })?; + let modulo_result = left.modulo(right); + let remainder = match modulo_result { + Ok(remainder) => remainder, + Err(error) => { + return Err(VmError::Value { + error, + position: self.current_position(), + }); + } + }; - self.set_register(register_index, Register::Value(remainder))?; + self.set_register(destination, Register::Value(remainder))?; } Operation::Test => { let Test { @@ -305,7 +302,6 @@ impl<'a> Vm<'a> { argument, test_value, } = TestSet::from(&instruction); - let register_index = self.get_destination(destination)?; let value = self.get_argument(argument)?; let boolean = if let ValueRef::Concrete(ConcreteValue::Boolean(boolean)) = value { @@ -322,24 +318,20 @@ impl<'a> Vm<'a> { } else { let pointer = match argument { Argument::Constant(constant_index) => Pointer::Constant(constant_index), - Argument::Local(local_index) => { - let register_index = self.local_definitions[local_index as usize] - .ok_or(VmError::UndefinedLocal { - local_index, - position: self.current_position(), - })?; - - Pointer::Stack(register_index) - } Argument::Register(register_index) => Pointer::Stack(register_index), }; let register = Register::Pointer(pointer); - self.set_register(register_index, register)?; + self.set_register(destination, register)?; } } Operation::Equal => { - let Equal { value, left, right } = Equal::from(&instruction); + let Equal { + destination, + 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 { @@ -355,64 +347,79 @@ impl<'a> Vm<'a> { position: self.current_position(), }); }; + let comparison = is_equal == value; + let register = + Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison))); - if is_equal == value { - self.jump(1, true); - } + self.set_register(destination, register)?; } Operation::Less => { - let Less { value, left, right } = Less::from(&instruction); + let Less { + destination, + value, + left, + right, + } = Less::from(&instruction); let left = self.get_argument(left)?; let right = self.get_argument(right)?; let less_result = left.less_than(right); - - assert!( - less_result.is_ok(), - "VM Error: {}\nPosition: {}", - less_result.unwrap_err(), - self.current_position() - ); - - let less_than_value = less_result.unwrap(); - let less_than_boolean = less_than_value.as_boolean(); - - assert!( - less_than_boolean.is_some(), - "VM Error: Expected a boolean\nPosition: {}", - self.current_position() - ); - - let is_less_than = less_than_boolean.unwrap(); - - if is_less_than == &value { - self.jump(1, true); - } - } - Operation::LessEqual => { - let LessEqual { value, left, right } = LessEqual::from(&instruction); - let left = self.get_argument(left)?; - let right = self.get_argument(right)?; - let less_or_equal_result = - left.less_than_or_equal(right) - .map_err(|error| VmError::Value { + let less_than_value = match less_result { + Ok(value) => value, + Err(error) => { + return Err(VmError::Value { error, position: self.current_position(), - })?; - let is_less_than_or_equal = - if let Value::Concrete(ConcreteValue::Boolean(boolean)) = - less_or_equal_result - { - boolean - } else { + }); + } + }; + let is_less_than = match less_than_value { + Value::Concrete(ConcreteValue::Boolean(boolean)) => boolean, + _ => { return Err(VmError::ExpectedBoolean { - found: less_or_equal_result, + found: less_than_value, position: self.current_position(), }); - }; + } + }; + let comparison = is_less_than == value; + let register = + Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison))); - if is_less_than_or_equal == value { - self.jump(1, true); - } + self.set_register(destination, 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 less_or_equal_result = left.less_than_or_equal(right); + let less_or_equal_value = match less_or_equal_result { + Ok(value) => value, + Err(error) => { + return Err(VmError::Value { + error, + position: self.current_position(), + }); + } + }; + let is_less_than_or_equal = match less_or_equal_value { + Value::Concrete(ConcreteValue::Boolean(boolean)) => boolean, + _ => { + return Err(VmError::ExpectedBoolean { + found: less_or_equal_value, + position: self.current_position(), + }); + } + }; + let comparison = is_less_than_or_equal == value; + let register = + Register::Value(Value::Concrete(ConcreteValue::Boolean(comparison))); + + self.set_register(destination, register)?; } Operation::Negate => { let Negate { @@ -424,10 +431,9 @@ impl<'a> Vm<'a> { error, position: self.current_position(), })?; - let register_index = self.get_destination(destination)?; let register = Register::Value(negated); - self.set_register(register_index, register)?; + self.set_register(destination, register)?; } Operation::Not => { let Not { @@ -439,10 +445,9 @@ impl<'a> Vm<'a> { error, position: self.current_position(), })?; - let register_index = self.get_destination(destination)?; let register = Register::Value(not); - self.set_register(register_index, register)?; + self.set_register(destination, register)?; } Operation::Jump => { let Jump { @@ -458,7 +463,6 @@ impl<'a> Vm<'a> { function, argument_count, } = Call::from(&instruction); - let register_index = self.get_destination(destination)?; let function = self.get_argument(function)?; let chunk = if let ValueRef::Concrete(ConcreteValue::Function(chunk)) = function { @@ -472,17 +476,15 @@ impl<'a> Vm<'a> { }); }; let mut function_vm = Vm::new(self.source, chunk, Some(self)); - let first_argument_index = register_index - argument_count; + let first_argument_index = destination - argument_count; for (argument_index, argument_register_index) in - (first_argument_index..register_index).enumerate() + (first_argument_index..destination).enumerate() { function_vm.set_register( - argument_index as u16, + argument_index as u8, Register::Pointer(Pointer::ParentStack(argument_register_index)), )?; - - function_vm.local_definitions[argument_index] = Some(argument_index as u16); } let return_value = function_vm.run()?; @@ -490,7 +492,7 @@ impl<'a> Vm<'a> { if let Some(concrete_value) = return_value { let register = Register::Value(concrete_value.to_value()); - self.set_register(register_index, register)?; + self.set_register(destination, register)?; } } Operation::CallNative => { @@ -502,10 +504,9 @@ impl<'a> Vm<'a> { let return_value = function.call(self, instruction)?; if let Some(value) = return_value { - let register_index = self.get_destination(destination)?; let register = Register::Value(value); - self.set_register(register_index, register)?; + self.set_register(destination, register)?; } } Operation::Return => { @@ -529,6 +530,7 @@ impl<'a> Vm<'a> { }) }; } + _ => unreachable!(), } } } @@ -565,7 +567,7 @@ impl<'a> Vm<'a> { } } - fn open_register(&self, register_index: u16) -> Result { + fn open_register(&self, register_index: u8) -> Result { let register_index = register_index as usize; assert!( @@ -589,7 +591,7 @@ impl<'a> Vm<'a> { pub(crate) fn open_register_allow_empty( &self, - register_index: u16, + register_index: u8, ) -> Result, VmError> { let register_index = register_index as usize; let register = @@ -628,16 +630,6 @@ impl<'a> Vm<'a> { self.ip = new_ip; } - /// DRY helper to get a register index from a Destination - fn get_destination(&self, destination: Destination) -> Result { - let index = match destination { - Destination::Register(register_index) => register_index, - Destination::Local(local_index) => self.get_local_register(local_index)?, - }; - - Ok(index) - } - /// DRY helper to get a value from an Argument fn get_argument(&self, argument: Argument) -> Result { let value_ref = match argument { @@ -645,18 +637,13 @@ impl<'a> Vm<'a> { ValueRef::Concrete(self.get_constant(constant_index)) } Argument::Register(register_index) => self.open_register(register_index)?, - Argument::Local(local_index) => { - let register_index = self.get_local_register(local_index)?; - - self.open_register(register_index)? - } }; Ok(value_ref) } #[inline(always)] - fn set_register(&mut self, to_register: u16, register: Register) -> Result<(), VmError> { + 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; @@ -672,7 +659,7 @@ impl<'a> Vm<'a> { } #[inline(always)] - fn get_constant(&self, constant_index: u16) -> &ConcreteValue { + fn get_constant(&self, constant_index: u8) -> &ConcreteValue { let constant_index = constant_index as usize; let constants = self.chunk.constants(); @@ -685,18 +672,18 @@ impl<'a> Vm<'a> { } #[inline(always)] - fn get_local_register(&self, local_index: u16) -> Result { + fn get_local_register(&self, local_index: u8) -> Result { let local_index = local_index as usize; + let locals = self.chunk.locals(); assert!( - local_index < self.local_definitions.len(), + local_index < locals.len(), "VM Error: Local index out of bounds" ); - self.local_definitions[local_index].ok_or_else(|| VmError::UndefinedLocal { - local_index: local_index as u16, - position: self.current_position(), - }) + let register_index = locals[local_index].register_index; + + Ok(register_index) } #[inline(always)] @@ -715,7 +702,7 @@ impl<'a> Vm<'a> { ) ); - let instruction = instructions[self.ip]; + let (instruction, _) = instructions[self.ip]; self.ip += 1; @@ -742,10 +729,10 @@ impl Display for Register { #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Pointer { - Stack(u16), - Constant(u16), - ParentStack(u16), - ParentConstant(u16), + Stack(u8), + Constant(u8), + ParentStack(u8), + ParentConstant(u8), } impl Display for Pointer { @@ -789,7 +776,7 @@ pub enum VmError { // Local errors UndefinedLocal { - local_index: u16, + local_index: u8, position: Span, }, diff --git a/dust-lang/tests/assignment_errors.rs b/dust-lang/tests/assignment_errors.rs index cb15786..a117612 100644 --- a/dust-lang/tests/assignment_errors.rs +++ b/dust-lang/tests/assignment_errors.rs @@ -6,7 +6,7 @@ fn add_assign_expects_mutable_variable() { assert_eq!( compile(source), - Err(CreateReport::Compile { + Err(DustError::Compile { error: CompileError::ExpectedMutableVariable { found: Token::Integer("1").to_owned(), position: Span(0, 1) @@ -22,7 +22,7 @@ fn divide_assign_expects_mutable_variable() { assert_eq!( compile(source), - Err(CreateReport::Compile { + Err(DustError::Compile { error: CompileError::ExpectedMutableVariable { found: Token::Integer("1").to_owned(), position: Span(0, 1) @@ -38,7 +38,7 @@ fn multiply_assign_expects_mutable_variable() { assert_eq!( compile(source), - Err(CreateReport::Compile { + Err(DustError::Compile { error: CompileError::ExpectedMutableVariable { found: Token::Integer("1").to_owned(), position: Span(0, 1) @@ -54,7 +54,7 @@ fn subtract_assign_expects_mutable_variable() { assert_eq!( compile(source), - Err(CreateReport::Compile { + Err(DustError::Compile { error: CompileError::ExpectedMutableVariable { found: Token::Integer("1").to_owned(), position: Span(0, 1) @@ -70,7 +70,7 @@ fn modulo_assign_expects_mutable_variable() { assert_eq!( compile(source), - Err(CreateReport::Compile { + Err(DustError::Compile { error: CompileError::ExpectedMutableVariable { found: Token::Integer("1").to_owned(), position: Span(0, 1) diff --git a/dust-lang/tests/basic.rs b/dust-lang/tests/basic.rs index f89d43c..f57d371 100644 --- a/dust-lang/tests/basic.rs +++ b/dust-lang/tests/basic.rs @@ -11,7 +11,7 @@ fn constant() { FunctionType { type_parameters: None, value_parameters: None, - return_type: Box::new(Type::Integer) + return_type: Type::Integer }, vec![ (