1
0

Add optimizer

This commit is contained in:
Jeff 2024-11-06 13:09:29 -05:00
parent e2df823566
commit c0b998c0d8
3 changed files with 131 additions and 83 deletions

View File

@ -7,6 +7,7 @@ pub mod instruction;
pub mod lexer;
pub mod native_function;
pub mod operation;
pub mod optimizer;
pub mod parser;
pub mod token;
pub mod r#type;
@ -20,6 +21,7 @@ pub use crate::instruction::Instruction;
pub use crate::lexer::{lex, LexError, Lexer};
pub use crate::native_function::{NativeFunction, NativeFunctionError};
pub use crate::operation::Operation;
pub use crate::optimizer::{optimize, Optimizer};
pub use crate::parser::{parse, ParseError};
pub use crate::r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
pub use crate::token::{Token, TokenKind, TokenOwned};

View File

@ -0,0 +1,82 @@
use std::{iter::Map, slice::Iter};
use crate::{Instruction, Operation, Span};
type MapToOperation = fn(&(Instruction, Span)) -> Operation;
type OperationIter<'iter> = Map<Iter<'iter, (Instruction, Span)>, MapToOperation>;
pub fn optimize(instructions: &mut [(Instruction, Span)]) {
Optimizer::new(instructions).optimize();
}
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Optimizer<'chunk> {
instructions: &'chunk mut [(Instruction, Span)],
}
impl<'chunk> Optimizer<'chunk> {
pub fn new(instructions: &'chunk mut [(Instruction, Span)]) -> Self {
Self { instructions }
}
pub fn set_instructions(&mut self, instructions: &'chunk mut [(Instruction, Span)]) {
self.instructions = instructions;
}
pub fn optimize(&mut self) {
if matches!(
self.get_operations(),
Some([
Operation::Equal | Operation::Less | Operation::LessEqual,
Operation::Jump,
Operation::LoadBoolean | Operation::LoadConstant,
Operation::LoadBoolean | Operation::LoadConstant,
])
) {
self.optimize_comparison();
}
}
fn optimize_comparison(&mut self) {
log::trace!("Optimizing comparison");
let first_loader_register = {
let first_loader = &mut self.instructions[2].0;
first_loader.set_c_to_boolean(true);
first_loader.a()
};
let second_loader = &mut self.instructions[3].0;
let mut second_loader_new = Instruction::with_operation(second_loader.operation());
second_loader_new.set_a(first_loader_register);
second_loader_new.set_b(second_loader.b());
second_loader_new.set_c(second_loader.c());
second_loader_new.set_b_to_boolean(second_loader.b_is_constant());
second_loader_new.set_c_to_boolean(second_loader.c_is_constant());
*second_loader = second_loader_new;
}
fn operations_iter(&self) -> OperationIter {
self.instructions
.iter()
.map(|(instruction, _)| instruction.operation())
}
fn get_operations<const COUNT: usize>(&self) -> Option<[Operation; COUNT]> {
if self.instructions.len() < COUNT {
return None;
}
let mut n_operations = [Operation::Return; COUNT];
for (nth, operation) in n_operations.iter_mut().zip(self.operations_iter()) {
*nth = operation;
}
Some(n_operations)
}
}

View File

@ -11,10 +11,9 @@ use std::{
};
use colored::Colorize;
use serde::{Deserialize, Serialize};
use crate::{
AnnotatedError, Chunk, DustError, FunctionType, Instruction, LexError, Lexer, Local,
optimize, AnnotatedError, Chunk, DustError, FunctionType, Instruction, LexError, Lexer, Local,
NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type, Value,
};
@ -43,12 +42,13 @@ pub fn parse(source: &str) -> Result<Chunk, DustError> {
/// Low-level tool for parsing the input a token at a time while assembling a chunk.
///
/// See the [`parse`] function an example of how to create and use a Parser.
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize)]
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
struct Parser<'src> {
lexer: Lexer<'src>,
chunk: Chunk,
lexer: Lexer<'src>,
optimization_count: u16,
current_is_expression: bool,
previous_is_expression: bool,
minimum_register: u8,
current_token: Token<'src>,
@ -61,6 +61,7 @@ struct Parser<'src> {
impl<'src> Parser<'src> {
pub fn new(mut lexer: Lexer<'src>) -> Result<Self, ParseError> {
let (current_token, current_position) = lexer.next_token()?;
let chunk = Chunk::new(None);
log::info!(
"Begin chunk with {} at {}",
@ -69,9 +70,10 @@ impl<'src> Parser<'src> {
);
Ok(Parser {
chunk,
lexer,
chunk: Chunk::new(None),
current_is_expression: false,
optimization_count: 0,
previous_is_expression: false,
minimum_register: 0,
current_token,
current_position,
@ -217,41 +219,6 @@ impl<'src> Parser<'src> {
self.chunk.instructions_mut().push((instruction, position));
}
fn optimize_statement(&mut self) {
if matches!(
self.get_last_instructions(),
Some([
Operation::LoadBoolean | Operation::LoadConstant,
Operation::LoadBoolean | Operation::LoadConstant,
Operation::Jump,
Operation::Equal | Operation::Less | Operation::LessEqual
],)
) {
log::trace!("Optimizing boolean comparison");
let mut instructions = self
.chunk
.instructions_mut()
.iter_mut()
.rev()
.map(|(instruction, _)| instruction);
let second_loader = instructions.next().unwrap();
let first_loader = instructions.next().unwrap();
first_loader.set_c_to_boolean(true);
let mut second_loader_new = Instruction::with_operation(second_loader.operation());
second_loader_new.set_a(first_loader.a());
second_loader_new.set_b(second_loader.b());
second_loader_new.set_c(second_loader.c());
second_loader_new.set_b_to_boolean(second_loader.b_is_constant());
second_loader_new.set_c_to_boolean(second_loader.c_is_constant());
*second_loader = second_loader_new;
}
}
fn pop_last_instruction(&mut self) -> Result<(Instruction, Span), ParseError> {
self.chunk
.instructions_mut()
@ -339,7 +306,7 @@ impl<'src> Parser<'src> {
position,
);
self.current_is_expression = true;
self.previous_is_expression = true;
Ok(())
} else {
@ -363,7 +330,7 @@ impl<'src> Parser<'src> {
self.emit_constant(value, position)?;
self.current_is_expression = true;
self.previous_is_expression = true;
Ok(())
} else {
@ -385,7 +352,7 @@ impl<'src> Parser<'src> {
self.emit_constant(value, position)?;
self.current_is_expression = true;
self.previous_is_expression = true;
Ok(())
} else {
@ -413,7 +380,7 @@ impl<'src> Parser<'src> {
self.emit_constant(value, position)?;
self.current_is_expression = true;
self.previous_is_expression = true;
Ok(())
} else {
@ -441,7 +408,7 @@ impl<'src> Parser<'src> {
self.emit_constant(value, position)?;
self.current_is_expression = true;
self.previous_is_expression = true;
Ok(())
} else {
@ -463,7 +430,7 @@ impl<'src> Parser<'src> {
self.emit_constant(value, position)?;
self.current_is_expression = true;
self.previous_is_expression = true;
Ok(())
} else {
@ -480,7 +447,7 @@ impl<'src> Parser<'src> {
self.parse_expression()?;
self.expect(Token::RightParenthesis)?;
self.current_is_expression = true;
self.previous_is_expression = true;
Ok(())
}
@ -531,7 +498,7 @@ impl<'src> Parser<'src> {
self.emit_instruction(instruction, operator_position);
self.current_is_expression = true;
self.previous_is_expression = true;
Ok(())
}
@ -671,9 +638,9 @@ impl<'src> Parser<'src> {
| Token::SlashEqual
| Token::PercentEqual = operator
{
self.current_is_expression = false;
self.previous_is_expression = false;
} else {
self.current_is_expression = true;
self.previous_is_expression = true;
}
Ok(())
@ -770,7 +737,7 @@ impl<'src> Parser<'src> {
operator_position,
);
self.current_is_expression = true;
self.previous_is_expression = true;
Ok(())
}
@ -803,7 +770,7 @@ impl<'src> Parser<'src> {
self.emit_instruction(Instruction::jump(jump_distance, true), operator_position);
self.parse_sub_expression(&rule.precedence)?;
self.current_is_expression = true;
self.previous_is_expression = true;
Ok(())
}
@ -832,7 +799,7 @@ impl<'src> Parser<'src> {
self.emit_instruction(Instruction::load_self(register), start_position);
self.declare_local(identifier, None, false, scope, register);
self.current_is_expression = true;
self.previous_is_expression = true;
return Ok(());
} else {
@ -901,9 +868,8 @@ impl<'src> Parser<'src> {
Instruction::set_local(register, local_index),
start_position,
);
self.optimize_statement();
self.current_is_expression = false;
self.previous_is_expression = false;
} else {
let register = self.next_register();
@ -912,7 +878,7 @@ impl<'src> Parser<'src> {
self.previous_position,
);
self.current_is_expression = true;
self.previous_is_expression = true;
}
Ok(())
@ -982,7 +948,7 @@ impl<'src> Parser<'src> {
Span(start, end),
);
self.current_is_expression = true;
self.previous_is_expression = true;
Ok(())
}
@ -1047,7 +1013,16 @@ impl<'src> Parser<'src> {
let if_last_register = self.next_register().saturating_sub(1);
self.parse_else(allowed, block_allowed)?;
self.optimize_statement();
if self.chunk.len() >= 4 {
let possible_comparison_statement = {
let start = self.chunk.len() - 4;
&mut self.chunk.instructions_mut()[start..]
};
optimize(possible_comparison_statement);
}
let else_last_register = self.next_register().saturating_sub(1);
let else_end = self.chunk.len();
@ -1060,7 +1035,7 @@ impl<'src> Parser<'src> {
);
}
self.current_is_expression = if_block_is_expression && self.current_is_expression;
self.previous_is_expression = if_block_is_expression && self.previous_is_expression;
if jump_distance == 1 {
if let Some(skippable) = self.get_last_jumpable_mut() {
@ -1084,7 +1059,7 @@ impl<'src> Parser<'src> {
);
}
} else {
self.current_is_expression = false;
self.previous_is_expression = false;
}
Ok(())
@ -1166,9 +1141,8 @@ impl<'src> Parser<'src> {
let jump_back = Instruction::jump(jump_back_distance, false);
self.emit_instruction(jump_back, self.current_position);
self.optimize_statement();
self.current_is_expression = false;
self.previous_is_expression = false;
Ok(())
}
@ -1199,7 +1173,7 @@ impl<'src> Parser<'src> {
let end = self.previous_position.1;
let to_register = self.next_register();
let argument_count = to_register - start_register;
self.current_is_expression = function.r#type().return_type.is_some();
self.previous_is_expression = function.r#type().return_type.is_some();
self.emit_instruction(
Instruction::call_native(to_register, function, argument_count),
@ -1237,7 +1211,7 @@ impl<'src> Parser<'src> {
},
)?;
if !self.current_is_expression || self.chunk.is_empty() {
if !self.previous_is_expression || self.chunk.is_empty() {
return Err(ParseError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.current_position,
@ -1281,9 +1255,8 @@ impl<'src> Parser<'src> {
let end = self.current_position.1;
self.emit_instruction(Instruction::r#return(has_return_value), Span(start, end));
self.optimize_statement();
self.current_is_expression = false;
self.previous_is_expression = false;
Ok(())
}
@ -1295,7 +1268,7 @@ impl<'src> Parser<'src> {
self.emit_instruction(Instruction::r#return(false), self.current_position);
} else {
self.emit_instruction(
Instruction::r#return(self.current_is_expression),
Instruction::r#return(self.previous_is_expression),
self.current_position,
);
}
@ -1348,9 +1321,8 @@ impl<'src> Parser<'src> {
Instruction::define_local(register, local_index, is_mutable),
position,
);
self.optimize_statement();
self.current_is_expression = false;
self.previous_is_expression = false;
Ok(())
}
@ -1469,14 +1441,12 @@ impl<'src> Parser<'src> {
Instruction::define_local(register, local_index, false),
identifier_position,
);
self.optimize_statement();
self.current_is_expression = false;
self.previous_is_expression = false;
} else {
self.emit_constant(function, Span(function_start, function_end))?;
self.optimize_statement();
self.current_is_expression = true;
self.previous_is_expression = true;
}
Ok(())
@ -1530,7 +1500,7 @@ impl<'src> Parser<'src> {
Span(start, end),
);
self.current_is_expression = true;
self.previous_is_expression = true;
Ok(())
}
@ -1538,7 +1508,7 @@ impl<'src> Parser<'src> {
fn parse_semicolon(&mut self, _: Allowed) -> Result<(), ParseError> {
self.advance()?;
self.current_is_expression = false;
self.previous_is_expression = false;
Ok(())
}
@ -1629,12 +1599,6 @@ impl Display for Precedence {
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Context {
None,
Assignment,
}
#[derive(Debug, Clone, Copy)]
struct Allowed {
pub assignment: bool,