Continue refactor; Condense registers in logic chains
This commit is contained in:
parent
a9e867aaab
commit
98a7b7984a
@ -610,11 +610,12 @@ impl<'src> Compiler<'src> {
|
||||
&mut self,
|
||||
instruction: &Instruction,
|
||||
) -> Result<(Argument, bool), CompileError> {
|
||||
let argument = instruction.destination_as_argument().ok_or_else(|| {
|
||||
CompileError::ExpectedExpression {
|
||||
let argument =
|
||||
instruction
|
||||
.as_argument()
|
||||
.ok_or_else(|| CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: self.previous_position,
|
||||
}
|
||||
})?;
|
||||
let push_back = matches!(argument, Argument::Register(_));
|
||||
|
||||
@ -867,7 +868,20 @@ impl<'src> Compiler<'src> {
|
||||
self.get_last_operations(),
|
||||
Some([Operation::Test, Operation::Jump, _])
|
||||
);
|
||||
let (left_instruction, left_type, left_position) = self.pop_last_instruction()?;
|
||||
|
||||
let (mut left_instruction, left_type, left_position) = self.pop_last_instruction()?;
|
||||
|
||||
if is_logic_chain {
|
||||
let destination = self
|
||||
.instructions
|
||||
.iter()
|
||||
.rev()
|
||||
.nth(2)
|
||||
.map_or(0, |(instruction, _, _)| instruction.a);
|
||||
|
||||
left_instruction.a = destination;
|
||||
}
|
||||
|
||||
let jump_index = self.instructions.len().saturating_sub(1);
|
||||
let mut jump_distance = if is_logic_chain {
|
||||
self.instructions.pop().map_or(0, |(jump, _, _)| {
|
||||
@ -886,13 +900,10 @@ impl<'src> Compiler<'src> {
|
||||
});
|
||||
}
|
||||
|
||||
let (argument, push_back) = self.handle_binary_argument(&left_instruction)?;
|
||||
let is_local = left_instruction.operation() == Operation::GetLocal;
|
||||
let (left, _) = self.handle_binary_argument(&left_instruction)?;
|
||||
|
||||
if push_back || is_local {
|
||||
self.instructions
|
||||
.push((left_instruction, left_type.clone(), left_position));
|
||||
}
|
||||
|
||||
let operator = self.current_token;
|
||||
let operator_position = self.current_position;
|
||||
@ -908,16 +919,16 @@ impl<'src> Compiler<'src> {
|
||||
})
|
||||
}
|
||||
};
|
||||
let test = Instruction::from(Test {
|
||||
argument,
|
||||
test_value: test_boolean,
|
||||
});
|
||||
let test = Instruction::test(left, test_boolean);
|
||||
|
||||
self.advance()?;
|
||||
self.emit_instruction(test, Type::None, operator_position);
|
||||
self.emit_instruction(Instruction::jump(1, true), Type::None, operator_position);
|
||||
self.parse_sub_expression(&rule.precedence)?;
|
||||
|
||||
let (mut right_instruction, _, _) = self.instructions.last_mut().unwrap();
|
||||
right_instruction.a = left_instruction.a;
|
||||
|
||||
if is_logic_chain {
|
||||
let expression_length = self.instructions.len() - jump_index - 1;
|
||||
jump_distance += expression_length as u8;
|
||||
@ -992,7 +1003,7 @@ impl<'src> Compiler<'src> {
|
||||
});
|
||||
|
||||
self.emit_instruction(set_local, Type::None, start_position);
|
||||
optimize_set_local(self);
|
||||
optimize_set_local(self)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
@ -1092,7 +1103,7 @@ impl<'src> Compiler<'src> {
|
||||
self.parse_expression()?;
|
||||
|
||||
if let Some((instruction, _, _)) = self.instructions.last() {
|
||||
let argument = match instruction.destination_as_argument() {
|
||||
let argument = match instruction.as_argument() {
|
||||
Some(argument) => argument,
|
||||
None => {
|
||||
return Err(CompileError::ExpectedExpression {
|
||||
@ -1323,7 +1334,18 @@ impl<'src> Compiler<'src> {
|
||||
}
|
||||
|
||||
fn parse_sub_expression(&mut self, precedence: &Precedence) -> Result<(), CompileError> {
|
||||
self.parse(precedence.increment())
|
||||
self.parse(precedence.increment())?;
|
||||
|
||||
let expression_type = self.get_last_instruction_type();
|
||||
|
||||
if expression_type == Type::None || self.instructions.is_empty() {
|
||||
return Err(CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: self.current_position,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_return_statement(&mut self) -> Result<(), CompileError> {
|
||||
@ -1572,11 +1594,12 @@ impl<'src> Compiler<'src> {
|
||||
});
|
||||
}
|
||||
|
||||
let function = last_instruction.destination_as_argument().ok_or_else(|| {
|
||||
CompileError::ExpectedExpression {
|
||||
let function =
|
||||
last_instruction
|
||||
.as_argument()
|
||||
.ok_or_else(|| CompileError::ExpectedExpression {
|
||||
found: self.previous_token.to_owned(),
|
||||
position: self.previous_position,
|
||||
}
|
||||
})?;
|
||||
let register_type = self.get_register_type(function.index())?;
|
||||
let function_return_type = match register_type {
|
||||
|
@ -1,7 +1,5 @@
|
||||
//! Tools used by the compiler to optimize a chunk's bytecode.
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{instruction::SetLocal, CompileError, Compiler, Instruction, Operation, Span, Type};
|
||||
|
||||
fn get_last_operations<const COUNT: usize>(
|
||||
|
@ -40,15 +40,15 @@
|
||||
//! write, this ensures that the instruction has the correct flags to represent the arguments.
|
||||
//!
|
||||
//! ```
|
||||
//! # use dust_lang::instruction::{Instruction, Add, u8, Argument};
|
||||
//! # use dust_lang::instruction::{Instruction, Add, Argument};
|
||||
//! let add_1 = Instruction::add(
|
||||
//! u8::Register(0),
|
||||
//! Argument::Local(1),
|
||||
//! 0,
|
||||
//! Argument::Register(1),
|
||||
//! Argument::Constant(2)
|
||||
//! );
|
||||
//! let add_2 = Instruction::from(Add {
|
||||
//! destination: u8::Register(0),
|
||||
//! left: Argument::Local(1),
|
||||
//! destination: 0,
|
||||
//! left: Argument::Register(1),
|
||||
//! right: Argument::Constant(2),
|
||||
//! });
|
||||
//!
|
||||
@ -57,21 +57,20 @@
|
||||
//!
|
||||
//! ## 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.
|
||||
//! To read an instruction, check its operation with [`Instruction::operation`], 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 directly and automatically casts the A, B, C and
|
||||
//! D fields as `u8`, `bool` or `Argument` values.
|
||||
//!
|
||||
//! ```
|
||||
//! # use dust_lang::instruction::{Instruction, Add, u8, Argument, Operation};
|
||||
//! # use dust_lang::instruction::{Instruction, Add, 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:
|
||||
//! # 1,
|
||||
//! # Argument::Register(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`
|
||||
@ -79,7 +78,7 @@
|
||||
//! let operation = mystery_instruction.operation();
|
||||
//!
|
||||
//! match operation {
|
||||
//! Operation::ADD => {
|
||||
//! Operation::Add => {
|
||||
//! let Add { destination, left, right } = Add::from(&mystery_instruction);
|
||||
//! let is_add_assign =
|
||||
//! left == Argument::Register(destination)
|
||||
@ -87,10 +86,7 @@
|
||||
//!
|
||||
//! assert!(is_add_assign);
|
||||
//! }
|
||||
//! // Handle other operations...
|
||||
//! _ => {
|
||||
//! panic!("Unknown operation code: {operation}");
|
||||
//! }
|
||||
//! _ => {} // Handle other operations...
|
||||
//! }
|
||||
//! ```
|
||||
mod add;
|
||||
@ -357,7 +353,7 @@ impl Instruction {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn destination_as_argument(&self) -> Option<Argument> {
|
||||
pub fn as_argument(&self) -> Option<Argument> {
|
||||
match self.operation() {
|
||||
Operation::LoadConstant => Some(Argument::Constant(self.b)),
|
||||
Operation::LoadBoolean
|
||||
@ -592,7 +588,7 @@ impl Instruction {
|
||||
} = Less::from(self);
|
||||
let comparison_symbol = if value { "<" } else { ">=" };
|
||||
|
||||
format!("R{destination} {left} {comparison_symbol} {right}")
|
||||
format!("R{destination} = {left} {comparison_symbol} {right}")
|
||||
}
|
||||
Operation::LessEqual => {
|
||||
let LessEqual {
|
||||
@ -603,7 +599,7 @@ impl Instruction {
|
||||
} = LessEqual::from(self);
|
||||
let comparison_symbol = if value { "<=" } else { ">" };
|
||||
|
||||
format!("R{destination} {left} {comparison_symbol} {right}")
|
||||
format!("R{destination} = {left} {comparison_symbol} {right}")
|
||||
}
|
||||
Operation::Negate => {
|
||||
let Negate {
|
||||
@ -687,7 +683,7 @@ impl Debug for Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Argument {
|
||||
Constant(u8),
|
||||
Register(u8),
|
||||
@ -740,15 +736,15 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn instruction_is_8_bytes() {
|
||||
assert_eq!(size_of::<Instruction>(), 8);
|
||||
fn instruction_is_4_bytes() {
|
||||
assert_eq!(size_of::<Instruction>(), 4);
|
||||
}
|
||||
|
||||
#[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);
|
||||
assert_eq!(offset_of!(Instruction, metadata), 0);
|
||||
assert_eq!(offset_of!(Instruction, a), 1);
|
||||
assert_eq!(offset_of!(Instruction, b), 2);
|
||||
assert_eq!(offset_of!(Instruction, c), 3);
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,6 @@ impl Scope {
|
||||
|
||||
impl Display for Scope {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "({}, {})", self.depth, self.block_index)
|
||||
write!(f, "{}.{}", self.depth, self.block_index)
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ impl AbstractValue {
|
||||
let mut resolved_items = Vec::with_capacity(items.len());
|
||||
|
||||
for pointer in items {
|
||||
let resolved_item = vm.follow_pointer(*pointer)?.to_concrete_owned(vm)?;
|
||||
let resolved_item = vm.follow_pointer(*pointer)?.into_concrete_owned(vm)?;
|
||||
|
||||
resolved_items.push(resolved_item);
|
||||
}
|
||||
|
@ -25,10 +25,10 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_concrete_owned(&self, vm: &Vm) -> Result<ConcreteValue, VmError> {
|
||||
pub fn into_concrete_owned(self, vm: &Vm) -> Result<ConcreteValue, VmError> {
|
||||
match self {
|
||||
Value::Abstract(abstract_value) => abstract_value.to_concrete_owned(vm),
|
||||
Value::Concrete(concrete_value) => Ok(concrete_value.clone()),
|
||||
Value::Concrete(concrete_value) => Ok(concrete_value),
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,10 +64,10 @@ impl ValueRef<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_concrete_owned(&self, vm: &Vm) -> Result<ConcreteValue, VmError> {
|
||||
pub fn into_concrete_owned(self, vm: &Vm) -> Result<ConcreteValue, VmError> {
|
||||
match self {
|
||||
ValueRef::Abstract(abstract_value) => abstract_value.to_concrete_owned(vm),
|
||||
ValueRef::Concrete(concrete_value) => Ok((*concrete_value).clone()),
|
||||
ValueRef::Concrete(concrete_value) => Ok(concrete_value.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ impl<'a> Vm<'a> {
|
||||
let local_register_index = self.get_local_register(local_index)?;
|
||||
let register = Register::Pointer(Pointer::Stack(register_index));
|
||||
|
||||
self.set_register(local_register_index, register);
|
||||
self.set_register(local_register_index, register)?;
|
||||
}
|
||||
Operation::Add => {
|
||||
let Add {
|
||||
@ -471,7 +471,7 @@ impl<'a> Vm<'a> {
|
||||
self.chunk
|
||||
} else {
|
||||
return Err(VmError::ExpectedFunction {
|
||||
found: function.to_concrete_owned(self)?,
|
||||
found: function.into_concrete_owned(self)?,
|
||||
position: self.current_position(),
|
||||
});
|
||||
};
|
||||
@ -521,7 +521,7 @@ impl<'a> Vm<'a> {
|
||||
return if let Some(register_index) = self.last_assigned_register {
|
||||
let return_value = self
|
||||
.open_register(register_index)?
|
||||
.to_concrete_owned(self)?;
|
||||
.into_concrete_owned(self)?;
|
||||
|
||||
Ok(Some(return_value))
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user