More optimizer fixes
All checks were successful
CI/CD Pipeline / test (pull_request) Successful in 2m2s
CI/CD Pipeline / build (pull_request) Has been skipped
CI/CD Pipeline / release (pull_request) Has been skipped

This commit is contained in:
2025-12-30 23:34:14 -07:00
parent dbc4c72c3b
commit 95c17b563c
8 changed files with 341 additions and 7 deletions

View File

@@ -205,4 +205,11 @@ mod tests {
let output = compile_with_and_without_optimization(source); let output = compile_with_and_without_optimization(source);
insta::assert_snapshot!(output); insta::assert_snapshot!(output);
} }
#[test]
fn test_larre_script() {
let source = include_str!("./test_files/test_larre_script.stlg");
let output = compile_with_and_without_optimization(source);
insta::assert_snapshot!(output);
}
} }

View File

@@ -31,7 +31,7 @@ j ra
## Optimized Output ## Optimized Output
j 11 j 10
pop r8 pop r8
pop r9 pop r9
push sp push sp

View File

@@ -0,0 +1,214 @@
---
source: libs/integration_tests/src/lib.rs
expression: output
---
## Unoptimized Output
j main
waitForIdle:
push sp
push ra
yield
__internal_L2:
l r1 d0 Idle
seq r2 r1 0
beqz r2 __internal_L3
yield
j __internal_L2
__internal_L3:
__internal_L1:
pop ra
pop sp
j ra
deposit:
push sp
push ra
s d0 Setting 1
jal waitForIdle
move r1 r15
s d0 Activate 1
jal waitForIdle
move r2 r15
s d1 Open 0
__internal_L4:
pop ra
pop sp
j ra
checkAndHarvest:
pop r8
push sp
push ra
sle r1 r8 1
ls r15 d0 255 Seeding
slt r2 r15 1
or r3 r1 r2
beqz r3 __internal_L6
j __internal_L5
__internal_L6:
__internal_L7:
ls r15 d0 255 Mature
beqz r15 __internal_L8
yield
s d0 Activate 1
j __internal_L7
__internal_L8:
ls r15 d0 255 Occupied
move r9 r15
s d0 Setting 1
push r8
push r9
jal waitForIdle
pop r9
pop r8
move r4 r15
push r8
push r9
jal deposit
pop r9
pop r8
move r5 r15
beqz r9 __internal_L9
push r8
push r9
jal deposit
pop r9
pop r8
move r6 r15
__internal_L9:
s d0 Setting r8
push r8
push r9
jal waitForIdle
pop r9
pop r8
move r6 r15
ls r15 d0 0 Occupied
beqz r15 __internal_L10
s d0 Activate 1
__internal_L10:
push r8
push r9
jal waitForIdle
pop r9
pop r8
move r7 r15
__internal_L5:
pop ra
pop sp
j ra
main:
move r8 0
__internal_L11:
yield
l r1 d0 Idle
seq r2 r1 0
beqz r2 __internal_L13
j __internal_L11
__internal_L13:
add r3 r8 1
sgt r4 r3 19
add r5 r8 1
select r6 r4 2 r5
move r9 r6
push r8
push r9
push r8
jal checkAndHarvest
pop r9
pop r8
move r7 r15
s d0 Setting r9
move r8 r9
j __internal_L11
__internal_L12:
## Optimized Output
j 71
push sp
push ra
yield
l r1 d0 Idle
bne r1 0 8
yield
j 4
pop ra
pop sp
j ra
push sp
push ra
s d0 Setting 1
jal 1
s d0 Activate 1
jal 1
s d1 Open 0
pop ra
pop sp
j ra
pop r8
push sp
push ra
sle r1 r8 1
ls r15 d0 255 Seeding
slt r2 r15 1
or r3 r1 r2
beqz r3 30
j 68
ls r15 d0 255 Mature
beqz r15 35
yield
s d0 Activate 1
j 30
ls r15 d0 255 Occupied
move r9 r15
s d0 Setting 1
push r8
push r9
jal 1
pop r9
pop r8
push r8
push r9
jal 11
pop r9
pop r8
beqz r9 54
push r8
push r9
jal 11
pop r9
pop r8
s d0 Setting r8
push r8
push r9
jal 1
pop r9
pop r8
ls r15 d0 0 Occupied
beqz r15 63
s d0 Activate 1
push r8
push r9
jal 1
pop r9
pop r8
pop ra
pop sp
j ra
yield
l r1 d0 Idle
bne r1 0 75
j 71
add r3 r8 1
sgt r4 r3 19
add r5 r8 1
select r6 r4 2 r5
move r9 r6
push r8
push r9
push r8
jal 21
pop r9
pop r8
s d0 Setting r9
j 71

View File

@@ -97,7 +97,7 @@ push r9
push r10 push r10
push r10 push r10
push 2 push 2
jal 11 jal 10
pop r10 pop r10
pop r9 pop r9
pop r8 pop r8

View File

@@ -54,7 +54,7 @@ __internal_L4:
## Optimized Output ## Optimized Output
j 27 j 25
pop r8 pop r8
push sp push sp
push ra push ra
@@ -80,15 +80,15 @@ sub r0 sp 4
get ra db r0 get ra db r0
j ra j ra
yield yield
jal 10 jal 9
pop r0 pop r0
pop r9 pop r9
pop r8 pop r8
move sp r15 move sp r15
jal 10 jal 9
pop r0 pop r0
pop r0 pop r0
pop r9 pop r9
move sp r15 move sp r15
s db Setting r9 s db Setting r9
j 27 j 25

View File

@@ -0,0 +1,72 @@
/// Laree script V1
device self = "db";
device larre = "d0";
device exportChute = "d1";
const TOTAL_SLOTS = 19;
const EXPORT_CHUTE = 1;
const START_STATION = 2;
let currentIndex = 0;
/// Waits for the larre to be idle before continuing
fn waitForIdle() {
yield();
while (!larre.Idle) {
yield();
}
}
/// Instructs the Larre to go to the chute and deposit
/// what is currently in its arm
fn deposit() {
larre.Setting = EXPORT_CHUTE;
waitForIdle();
larre.Activate = true;
waitForIdle();
exportChute.Open = false;
}
/// This function is responsible for checking the plant under
/// the larre at this index, and harvesting if applicable
fn checkAndHarvest(currentIndex) {
if (currentIndex <= EXPORT_CHUTE || ls(larre, 255, "Seeding") < 1) {
return;
}
// harvest from this device
while (ls(larre, 255, "Mature")) {
yield();
larre.Activate = true;
}
let hasRemainingPlant = ls(larre, 255, "Occupied");
// move to the export chute
larre.Setting = EXPORT_CHUTE;
waitForIdle();
deposit();
if (hasRemainingPlant) {
deposit();
}
larre.Setting = currentIndex;
waitForIdle();
if (ls(larre, 0, "Occupied")) {
larre.Activate = true;
}
waitForIdle();
}
loop {
yield();
if (!larre.Idle) {
continue;
}
let newIndex = currentIndex + 1 > TOTAL_SLOTS ? START_STATION : currentIndex + 1;
checkAndHarvest(currentIndex);
larre.Setting = newIndex;
currentIndex = newIndex;
}

View File

@@ -46,12 +46,15 @@ pub fn remove_unreachable_code<'a>(
/// Pass: Remove Redundant Jumps /// Pass: Remove Redundant Jumps
/// Removes jumps to the next instruction (after label resolution). /// Removes jumps to the next instruction (after label resolution).
/// Must run AFTER label resolution since it needs line numbers. /// Must run AFTER label resolution since it needs line numbers.
/// This pass also adjusts all jump targets to account for removed instructions.
pub fn remove_redundant_jumps<'a>( pub fn remove_redundant_jumps<'a>(
input: Vec<InstructionNode<'a>>, input: Vec<InstructionNode<'a>>,
) -> (Vec<InstructionNode<'a>>, bool) { ) -> (Vec<InstructionNode<'a>>, bool) {
let mut output = Vec::with_capacity(input.len()); let mut output = Vec::with_capacity(input.len());
let mut changed = false; let mut changed = false;
let mut removed_lines = Vec::new();
// First pass: identify redundant jumps
for (i, node) in input.iter().enumerate() { for (i, node) in input.iter().enumerate() {
// Check if this is a jump to the next line number // Check if this is a jump to the next line number
if let Instruction::Jump(Operand::Number(target)) = &node.instruction { if let Instruction::Jump(Operand::Number(target)) = &node.instruction {
@@ -59,12 +62,50 @@ pub fn remove_redundant_jumps<'a>(
// If jump target equals the next line, it's redundant // If jump target equals the next line, it's redundant
if target.to_string().parse::<usize>().ok() == Some(i + 1) { if target.to_string().parse::<usize>().ok() == Some(i + 1) {
changed = true; changed = true;
removed_lines.push(i);
continue; // Skip this redundant jump continue; // Skip this redundant jump
} }
} }
output.push(node.clone()); output.push(node.clone());
} }
// Second pass: adjust all jump/branch targets to account for removed lines
if changed {
for node in &mut output {
// Helper to adjust a target line number
let adjust_target = |target_line: usize| -> usize {
// Count how many removed lines are before the target
let offset = removed_lines
.iter()
.filter(|&&removed| removed < target_line)
.count();
target_line.saturating_sub(offset)
};
match &mut node.instruction {
Instruction::Jump(Operand::Number(target))
| Instruction::JumpAndLink(Operand::Number(target)) => {
if let Ok(target_line) = target.to_string().parse::<usize>() {
*target = rust_decimal::Decimal::from(adjust_target(target_line));
}
}
Instruction::BranchEq(_, _, Operand::Number(target))
| Instruction::BranchNe(_, _, Operand::Number(target))
| Instruction::BranchGt(_, _, Operand::Number(target))
| Instruction::BranchLt(_, _, Operand::Number(target))
| Instruction::BranchGe(_, _, Operand::Number(target))
| Instruction::BranchLe(_, _, Operand::Number(target))
| Instruction::BranchEqZero(_, Operand::Number(target))
| Instruction::BranchNeZero(_, Operand::Number(target)) => {
if let Ok(target_line) = target.to_string().parse::<usize>() {
*target = rust_decimal::Decimal::from(adjust_target(target_line));
}
}
_ => {}
}
}
}
(output, changed) (output, changed)
} }

View File

@@ -91,7 +91,7 @@ pub fn optimize<'a>(instructions: Instructions<'a>) -> Instructions<'a> {
} }
// Final Pass: Resolve Labels to Line Numbers // Final Pass: Resolve Labels to Line Numbers
let mut instructions = resolve_labels(instructions); let instructions = resolve_labels(instructions);
// Post-resolution Pass: Remove redundant jumps (must run after label resolution) // Post-resolution Pass: Remove redundant jumps (must run after label resolution)
let (instructions, _) = remove_redundant_jumps(instructions); let (instructions, _) = remove_redundant_jumps(instructions);