More compiler optimizations
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use il::{Instruction, InstructionNode};
|
||||
use il::{Instruction, InstructionNode, Operand};
|
||||
|
||||
/// Pass: Redundant Move Elimination
|
||||
/// Removes moves where source and destination are the same: `move rx rx`
|
||||
@@ -43,6 +43,31 @@ pub fn remove_unreachable_code<'a>(
|
||||
(output, changed)
|
||||
}
|
||||
|
||||
/// Pass: Remove Redundant Jumps
|
||||
/// Removes jumps to the next instruction (after label resolution).
|
||||
/// Must run AFTER label resolution since it needs line numbers.
|
||||
pub fn remove_redundant_jumps<'a>(
|
||||
input: Vec<InstructionNode<'a>>,
|
||||
) -> (Vec<InstructionNode<'a>>, bool) {
|
||||
let mut output = Vec::with_capacity(input.len());
|
||||
let mut changed = false;
|
||||
|
||||
for (i, node) in input.iter().enumerate() {
|
||||
// Check if this is a jump to the next line number
|
||||
if let Instruction::Jump(Operand::Number(target)) = &node.instruction {
|
||||
// Current line number is i, next line number is i+1
|
||||
// If jump target equals the next line, it's redundant
|
||||
if target.to_string().parse::<usize>().ok() == Some(i + 1) {
|
||||
changed = true;
|
||||
continue; // Skip this redundant jump
|
||||
}
|
||||
}
|
||||
output.push(node.clone());
|
||||
}
|
||||
|
||||
(output, changed)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -13,13 +13,20 @@ pub fn dead_store_elimination<'a>(
|
||||
|
||||
// Scan for dead writes
|
||||
for (i, node) in input.iter().enumerate() {
|
||||
// Never remove Pop instructions - they have critical side effects on the stack pointer
|
||||
if matches!(node.instruction, Instruction::Pop(_)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(dest_reg) = get_destination_reg(&node.instruction) {
|
||||
// If this register was written before and hasn't been read, previous write is dead
|
||||
if let Some(&prev_idx) = last_write.get(&dest_reg) {
|
||||
// Check if the value was ever used between prev_idx and current
|
||||
let was_used = input[prev_idx + 1..i]
|
||||
.iter()
|
||||
.any(|n| reg_is_read_or_affects_control(&n.instruction, dest_reg));
|
||||
.any(|n| reg_is_read_or_affects_control(&n.instruction, dest_reg))
|
||||
// Also check if current instruction reads the register before overwriting it
|
||||
|| reg_is_read_or_affects_control(&node.instruction, dest_reg);
|
||||
|
||||
if !was_used {
|
||||
// Previous write was dead
|
||||
|
||||
@@ -17,7 +17,7 @@ mod strength_reduction;
|
||||
|
||||
use algebraic_simplification::algebraic_simplification;
|
||||
use constant_propagation::constant_propagation;
|
||||
use dead_code::{remove_redundant_moves, remove_unreachable_code};
|
||||
use dead_code::{remove_redundant_jumps, remove_redundant_moves, remove_unreachable_code};
|
||||
use dead_store_elimination::dead_store_elimination;
|
||||
use function_call_optimization::optimize_function_calls;
|
||||
use label_resolution::resolve_labels;
|
||||
@@ -91,5 +91,10 @@ pub fn optimize<'a>(instructions: Instructions<'a>) -> Instructions<'a> {
|
||||
}
|
||||
|
||||
// Final Pass: Resolve Labels to Line Numbers
|
||||
Instructions::new(resolve_labels(instructions))
|
||||
let mut instructions = resolve_labels(instructions);
|
||||
|
||||
// Post-resolution Pass: Remove redundant jumps (must run after label resolution)
|
||||
let (instructions, _) = remove_redundant_jumps(instructions);
|
||||
|
||||
Instructions::new(instructions)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user