Compare commits
10 Commits
63f55b66cb
...
0.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 397aa47217 | |||
|
6f86563863
|
|||
|
352041746c
|
|||
|
5f4335dbcc
|
|||
|
2a5dfd9ab6
|
|||
|
2dfe36f8be
|
|||
|
d28cdfcc7b
|
|||
|
95c17b563c
|
|||
|
dbc4c72c3b
|
|||
|
964ad92077
|
4
.github/copilot-instructions.md
vendored
4
.github/copilot-instructions.md
vendored
@@ -80,10 +80,14 @@ cargo test --package compiler --lib -- test::tuple_literals::test::test_tuple_li
|
||||
|
||||
### Quick Compilation
|
||||
|
||||
!IMPORTANT: make sure you use these commands instead of creating temporary files.
|
||||
|
||||
```bash
|
||||
cd rust_compiler
|
||||
# Compile Slang code to IC10 using current compiler changes
|
||||
echo 'let x = 5;' | cargo run --bin slang
|
||||
# Compile Slang code to IC10 with optimization
|
||||
echo 'let x = 5;' | cargo run --bin slang -z
|
||||
# Or from file
|
||||
cargo run --bin slang -- input.slang -o output.ic10
|
||||
# Optimize the output with -z flag
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
target
|
||||
*.ic10
|
||||
*.snap.new
|
||||
release
|
||||
csharp_mod/bin
|
||||
obj
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
[0.5.0]
|
||||
|
||||
- Added support for tuple types
|
||||
- Added support for tuple returns from functions
|
||||
- Added support for ignoring tuple values
|
||||
- Fixed various compiler bugs
|
||||
- Added full tuple support: declarations, assignments, and returns
|
||||
- Refactored optimizer into modular passes with improved code generation
|
||||
- Enhanced peephole optimizations and pattern recognition
|
||||
- Comprehensive test coverage for edge cases and error handling
|
||||
|
||||
[0.4.7]
|
||||
|
||||
|
||||
@@ -172,4 +172,51 @@ mod tests {
|
||||
let output = compile_with_and_without_optimization(source);
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuples() {
|
||||
let source = indoc! {r#"
|
||||
device self = "db";
|
||||
device day = "d0";
|
||||
|
||||
fn getSomethingElse(input) {
|
||||
return input + 1;
|
||||
}
|
||||
|
||||
fn getSensorData() {
|
||||
return (
|
||||
day.Vertical,
|
||||
day.Horizontal,
|
||||
getSomethingElse(3)
|
||||
);
|
||||
}
|
||||
|
||||
loop {
|
||||
yield();
|
||||
|
||||
let (vertical, horizontal, _) = getSensorData();
|
||||
|
||||
(horizontal, _, _) = getSensorData();
|
||||
|
||||
self.Setting = horizontal;
|
||||
}
|
||||
"#};
|
||||
|
||||
let output = compile_with_and_without_optimization(source);
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reagent_processing() {
|
||||
let source = include_str!("./test_files/reagent_processing.stlg");
|
||||
let output = compile_with_and_without_optimization(source);
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ move r9 r1
|
||||
|
||||
## Optimized Output
|
||||
|
||||
j 1
|
||||
move r8 5
|
||||
move r1 5
|
||||
move r9 5
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: libs/integration_tests/src/lib.rs
|
||||
assertion_line: 158
|
||||
expression: output
|
||||
---
|
||||
## Unoptimized Output
|
||||
@@ -34,16 +35,11 @@ pop r9
|
||||
pop r10
|
||||
push sp
|
||||
push ra
|
||||
add r1 r10 r10
|
||||
move r11 r1
|
||||
move r2 r9
|
||||
move r12 r2
|
||||
move r3 r8
|
||||
move r13 r3
|
||||
add r11 r10 r10
|
||||
move r12 r9
|
||||
move r13 r8
|
||||
add r4 r11 r12
|
||||
add r5 r4 r13
|
||||
move r15 r5
|
||||
j 16
|
||||
add r15 r4 r13
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
|
||||
@@ -10,5 +10,4 @@ move r8 15
|
||||
|
||||
## Optimized Output
|
||||
|
||||
j 1
|
||||
move r8 15
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: libs/integration_tests/src/lib.rs
|
||||
assertion_line: 103
|
||||
expression: output
|
||||
---
|
||||
## Unoptimized Output
|
||||
@@ -24,9 +25,8 @@ j main
|
||||
pop r8
|
||||
push sp
|
||||
push ra
|
||||
add r1 r8 1
|
||||
move r15 r1
|
||||
j 7
|
||||
move r9 20
|
||||
add r15 r8 1
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
source: libs/integration_tests/src/lib.rs
|
||||
assertion_line: 103
|
||||
expression: output
|
||||
---
|
||||
## Unoptimized Output
|
||||
|
||||
j main
|
||||
compute:
|
||||
pop r8
|
||||
push sp
|
||||
push ra
|
||||
move r9 20
|
||||
add r1 r8 1
|
||||
move r15 r1
|
||||
j __internal_L1
|
||||
__internal_L1:
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
|
||||
## Optimized Output
|
||||
|
||||
j main
|
||||
pop r8
|
||||
push sp
|
||||
push ra
|
||||
add r1 r8 1
|
||||
move r15 r1
|
||||
j 7
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: libs/integration_tests/src/lib.rs
|
||||
assertion_line: 70
|
||||
expression: output
|
||||
---
|
||||
## Unoptimized Output
|
||||
@@ -31,14 +32,12 @@ j ra
|
||||
|
||||
## Optimized Output
|
||||
|
||||
j 11
|
||||
j 9
|
||||
pop r8
|
||||
pop r9
|
||||
push sp
|
||||
push ra
|
||||
add r1 r9 r8
|
||||
move r15 r1
|
||||
j 8
|
||||
add r15 r9 r8
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
@@ -47,6 +46,7 @@ push ra
|
||||
push 5
|
||||
push 10
|
||||
jal 1
|
||||
move r8 r15
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
---
|
||||
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 77
|
||||
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
|
||||
move r1 r15
|
||||
s d0 Activate 1
|
||||
jal 1
|
||||
move r2 r15
|
||||
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 32
|
||||
j 74
|
||||
ls r15 d0 255 Mature
|
||||
beqz r15 37
|
||||
yield
|
||||
s d0 Activate 1
|
||||
j 32
|
||||
ls r9 d0 255 Occupied
|
||||
s d0 Setting 1
|
||||
push r8
|
||||
push r9
|
||||
jal 1
|
||||
pop r9
|
||||
pop r8
|
||||
move r4 r15
|
||||
push r8
|
||||
push r9
|
||||
jal 11
|
||||
pop r9
|
||||
pop r8
|
||||
move r5 r15
|
||||
beqz r9 58
|
||||
push r8
|
||||
push r9
|
||||
jal 11
|
||||
pop r9
|
||||
pop r8
|
||||
move r6 r15
|
||||
s d0 Setting r8
|
||||
push r8
|
||||
push r9
|
||||
jal 1
|
||||
pop r9
|
||||
pop r8
|
||||
move r6 r15
|
||||
ls r15 d0 0 Occupied
|
||||
beqz r15 68
|
||||
s d0 Activate 1
|
||||
push r8
|
||||
push r9
|
||||
jal 1
|
||||
pop r9
|
||||
pop r8
|
||||
move r7 r15
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
move r8 0
|
||||
yield
|
||||
l r1 d0 Idle
|
||||
bne r1 0 82
|
||||
j 78
|
||||
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 23
|
||||
pop r9
|
||||
pop r8
|
||||
move r7 r15
|
||||
s d0 Setting r9
|
||||
move r8 r9
|
||||
j 78
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: libs/integration_tests/src/lib.rs
|
||||
assertion_line: 144
|
||||
expression: output
|
||||
---
|
||||
## Unoptimized Output
|
||||
@@ -19,4 +20,11 @@ j ra
|
||||
## Optimized Output
|
||||
|
||||
j main
|
||||
pop r8
|
||||
push sp
|
||||
push ra
|
||||
add r1 r8 1
|
||||
move r8 r1
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: libs/integration_tests/src/lib.rs
|
||||
assertion_line: 173
|
||||
expression: output
|
||||
---
|
||||
## Unoptimized Output
|
||||
@@ -66,18 +67,15 @@ pop r8
|
||||
pop r9
|
||||
push sp
|
||||
push ra
|
||||
add r1 r9 r8
|
||||
move r15 r1
|
||||
j 8
|
||||
add r15 r9 r8
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
pop r8
|
||||
pop r9
|
||||
push sp
|
||||
push ra
|
||||
add r1 r9 r9
|
||||
move r15 r1
|
||||
j 17
|
||||
add r15 r9 r9
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
@@ -98,10 +96,12 @@ push r9
|
||||
push r10
|
||||
push r10
|
||||
push 2
|
||||
jal 11
|
||||
jal 9
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
move r11 r15
|
||||
move r15 r11
|
||||
j 41
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
---
|
||||
source: libs/integration_tests/src/lib.rs
|
||||
assertion_line: 173
|
||||
expression: output
|
||||
---
|
||||
## Unoptimized Output
|
||||
|
||||
j main
|
||||
add:
|
||||
pop r8
|
||||
pop r9
|
||||
push sp
|
||||
push ra
|
||||
add r1 r9 r8
|
||||
move r15 r1
|
||||
j __internal_L1
|
||||
__internal_L1:
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
multiply:
|
||||
pop r8
|
||||
pop r9
|
||||
push sp
|
||||
push ra
|
||||
mul r1 r9 2
|
||||
move r15 r1
|
||||
j __internal_L2
|
||||
__internal_L2:
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
complex:
|
||||
pop r8
|
||||
pop r9
|
||||
push sp
|
||||
push ra
|
||||
push r8
|
||||
push r9
|
||||
push r9
|
||||
push r8
|
||||
jal add
|
||||
pop r9
|
||||
pop r8
|
||||
move r10 r15
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r10
|
||||
push 2
|
||||
jal multiply
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
move r11 r15
|
||||
move r15 r11
|
||||
j __internal_L3
|
||||
__internal_L3:
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
|
||||
## Optimized Output
|
||||
|
||||
j main
|
||||
pop r8
|
||||
pop r9
|
||||
push sp
|
||||
push ra
|
||||
add r1 r9 r8
|
||||
move r15 r1
|
||||
j 8
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
pop r9
|
||||
push sp
|
||||
push ra
|
||||
add r1 r9 r9
|
||||
move r15 r1
|
||||
j 17
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
pop r8
|
||||
pop r9
|
||||
push sp
|
||||
push ra
|
||||
push r8
|
||||
push r9
|
||||
push r9
|
||||
push r8
|
||||
jal 1
|
||||
pop r9
|
||||
pop r8
|
||||
move r10 r15
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r10
|
||||
push 2
|
||||
jal 11
|
||||
move r11 r15
|
||||
move r15 r11
|
||||
j 41
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: libs/integration_tests/src/lib.rs
|
||||
assertion_line: 116
|
||||
expression: output
|
||||
---
|
||||
## Unoptimized Output
|
||||
@@ -24,5 +25,10 @@ j ra
|
||||
j main
|
||||
pop r8
|
||||
pop r9
|
||||
ble r9 r8 4
|
||||
push sp
|
||||
push ra
|
||||
ble r9 r8 7
|
||||
move r10 1
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
---
|
||||
source: libs/integration_tests/src/lib.rs
|
||||
expression: output
|
||||
---
|
||||
## Unoptimized Output
|
||||
|
||||
j main
|
||||
main:
|
||||
s d2 Mode 1
|
||||
s d2 On 0
|
||||
move r8 0
|
||||
move r9 0
|
||||
__internal_L1:
|
||||
yield
|
||||
l r1 d0 Reagents
|
||||
move r10 r1
|
||||
sge r2 r10 100
|
||||
sge r3 r9 2
|
||||
or r4 r2 r3
|
||||
beqz r4 __internal_L3
|
||||
move r8 1
|
||||
__internal_L3:
|
||||
slt r5 r10 100
|
||||
ls r15 d0 0 Occupied
|
||||
seq r6 r15 0
|
||||
and r7 r5 r6
|
||||
add r1 r9 1
|
||||
select r2 r7 r1 0
|
||||
move r9 r2
|
||||
l r3 d0 Rpm
|
||||
slt r4 r3 1
|
||||
and r5 r8 r4
|
||||
beqz r5 __internal_L4
|
||||
s d0 Open 1
|
||||
seq r6 r10 0
|
||||
ls r15 d0 0 Occupied
|
||||
and r7 r6 r15
|
||||
seq r1 r7 0
|
||||
move r8 r1
|
||||
__internal_L4:
|
||||
seq r6 r8 0
|
||||
s d0 On r6
|
||||
ls r15 d1 0 Quantity
|
||||
move r11 r15
|
||||
l r7 d3 Pressure
|
||||
sgt r1 r7 200
|
||||
beqz r1 __internal_L5
|
||||
j __internal_L1
|
||||
__internal_L5:
|
||||
sgt r2 r11 0
|
||||
s d1 On r2
|
||||
sgt r3 r11 0
|
||||
s d1 Activate r3
|
||||
l r4 d3 Pressure
|
||||
sgt r5 r4 0
|
||||
l r6 d1 Activate
|
||||
or r7 r5 r6
|
||||
s d2 On r7
|
||||
l r1 d1 Activate
|
||||
s db Setting r1
|
||||
j __internal_L1
|
||||
__internal_L2:
|
||||
|
||||
## Optimized Output
|
||||
|
||||
s d2 Mode 1
|
||||
s d2 On 0
|
||||
move r8 0
|
||||
move r9 0
|
||||
yield
|
||||
l r10 d0 Reagents
|
||||
sge r2 r10 100
|
||||
sge r3 r9 2
|
||||
or r4 r2 r3
|
||||
beqz r4 11
|
||||
move r8 1
|
||||
slt r5 r10 100
|
||||
ls r15 d0 0 Occupied
|
||||
seq r6 r15 0
|
||||
and r7 r5 r6
|
||||
add r1 r9 1
|
||||
select r2 r7 r1 0
|
||||
move r9 r2
|
||||
l r3 d0 Rpm
|
||||
slt r4 r3 1
|
||||
and r5 r8 r4
|
||||
beqz r5 27
|
||||
s d0 Open 1
|
||||
seq r6 r10 0
|
||||
ls r15 d0 0 Occupied
|
||||
and r7 r6 r15
|
||||
seq r8 r7 0
|
||||
seq r6 r8 0
|
||||
s d0 On r6
|
||||
ls r15 d1 0 Quantity
|
||||
move r11 r15
|
||||
l r7 d3 Pressure
|
||||
ble r7 200 34
|
||||
j 4
|
||||
sgt r2 r11 0
|
||||
s d1 On r2
|
||||
sgt r3 r11 0
|
||||
s d1 Activate r3
|
||||
l r4 d3 Pressure
|
||||
sgt r5 r4 0
|
||||
l r6 d1 Activate
|
||||
or r7 r5 r6
|
||||
s d2 On r7
|
||||
l r1 d1 Activate
|
||||
s db Setting r1
|
||||
j 4
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: libs/integration_tests/src/lib.rs
|
||||
assertion_line: 133
|
||||
expression: output
|
||||
---
|
||||
## Unoptimized Output
|
||||
@@ -29,9 +30,7 @@ j main
|
||||
pop r8
|
||||
push sp
|
||||
push ra
|
||||
select r9 r8 10 20
|
||||
move r15 r9
|
||||
j 7
|
||||
select r15 r8 10 20
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: libs/integration_tests/src/lib.rs
|
||||
assertion_line: 60
|
||||
expression: output
|
||||
---
|
||||
## Unoptimized Output
|
||||
@@ -17,4 +18,9 @@ j ra
|
||||
## Optimized Output
|
||||
|
||||
j main
|
||||
push sp
|
||||
push ra
|
||||
move r8 10
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: libs/integration_tests/src/lib.rs
|
||||
assertion_line: 91
|
||||
expression: output
|
||||
---
|
||||
## Unoptimized Output
|
||||
@@ -23,9 +24,7 @@ j main
|
||||
pop r8
|
||||
push sp
|
||||
push ra
|
||||
add r1 r8 r8
|
||||
move r15 r1
|
||||
j 7
|
||||
add r15 r8 r8
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
source: libs/integration_tests/src/lib.rs
|
||||
assertion_line: 206
|
||||
expression: output
|
||||
---
|
||||
## Unoptimized Output
|
||||
|
||||
j main
|
||||
getSomethingElse:
|
||||
pop r8
|
||||
push sp
|
||||
push ra
|
||||
add r1 r8 1
|
||||
move r15 r1
|
||||
j __internal_L1
|
||||
__internal_L1:
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
getSensorData:
|
||||
push sp
|
||||
push ra
|
||||
l r1 d0 Vertical
|
||||
push r1
|
||||
l r2 d0 Horizontal
|
||||
push r2
|
||||
push 3
|
||||
jal getSomethingElse
|
||||
move r3 r15
|
||||
push r3
|
||||
sub r0 sp 5
|
||||
get r0 db r0
|
||||
move r15 r0
|
||||
j __internal_L2
|
||||
__internal_L2:
|
||||
sub r0 sp 4
|
||||
get ra db r0
|
||||
j ra
|
||||
main:
|
||||
__internal_L3:
|
||||
yield
|
||||
jal getSensorData
|
||||
pop r0
|
||||
pop r9
|
||||
pop r8
|
||||
move sp r15
|
||||
jal getSensorData
|
||||
pop r0
|
||||
pop r0
|
||||
pop r9
|
||||
move sp r15
|
||||
s db Setting r9
|
||||
j __internal_L3
|
||||
__internal_L4:
|
||||
|
||||
## Optimized Output
|
||||
|
||||
j 23
|
||||
pop r8
|
||||
push sp
|
||||
push ra
|
||||
add r15 r8 1
|
||||
pop ra
|
||||
pop sp
|
||||
j ra
|
||||
push sp
|
||||
push ra
|
||||
l r1 d0 Vertical
|
||||
push r1
|
||||
l r2 d0 Horizontal
|
||||
push r2
|
||||
push 3
|
||||
jal 1
|
||||
move r3 r15
|
||||
push r3
|
||||
sub r0 sp 5
|
||||
get r15 db r0
|
||||
sub r0 sp 4
|
||||
get ra db r0
|
||||
j ra
|
||||
yield
|
||||
jal 8
|
||||
pop r0
|
||||
pop r9
|
||||
pop r8
|
||||
move sp r15
|
||||
jal 8
|
||||
pop r0
|
||||
pop r0
|
||||
pop r9
|
||||
move sp r15
|
||||
s db Setting r9
|
||||
j 23
|
||||
@@ -0,0 +1,49 @@
|
||||
device combustion = "d0";
|
||||
device furnace = "d1";
|
||||
device vent = "d2";
|
||||
device gasSensor = "d3";
|
||||
device self = "db";
|
||||
|
||||
const MAX_WAIT_ITER = 2;
|
||||
const STACK_SIZE = 100;
|
||||
|
||||
vent.Mode = 1; // Vent inward into pipes
|
||||
vent.On = false;
|
||||
let ejecting = false;
|
||||
let combustionWaitIter = 0;
|
||||
|
||||
loop {
|
||||
yield();
|
||||
|
||||
let reagentCount = combustion.Reagents;
|
||||
|
||||
if (reagentCount >= STACK_SIZE || combustionWaitIter >= MAX_WAIT_ITER) {
|
||||
ejecting = true;
|
||||
}
|
||||
|
||||
combustionWaitIter = (reagentCount < STACK_SIZE && !ls(combustion, 0, "Occupied"))
|
||||
? combustionWaitIter + 1
|
||||
: 0;
|
||||
|
||||
if (ejecting && combustion.Rpm < 1) {
|
||||
combustion.Open = true;
|
||||
ejecting = !(reagentCount == 0 && ls(combustion, 0, "Occupied"));
|
||||
}
|
||||
|
||||
combustion.On = !ejecting;
|
||||
|
||||
let furnaceAmt = ls(furnace, 0, "Quantity");
|
||||
|
||||
if (gasSensor.Pressure > 200) {
|
||||
// safety: don't turn this on if we have gas still to process
|
||||
// This should prevent pipes from blowing. This will NOT hault
|
||||
// The in-progress burn job, but it'll prevent new jobs from
|
||||
// blowing the walls or pipes.
|
||||
continue;
|
||||
}
|
||||
|
||||
furnace.On = furnaceAmt > 0;
|
||||
furnace.Activate = furnaceAmt > 0;
|
||||
vent.On = gasSensor.Pressure > 0 || furnace.Activate;
|
||||
self.Setting = furnace.Activate;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -25,24 +25,12 @@ pub fn constant_propagation<'a>(
|
||||
Instruction::Add(dst, a, b) => try_fold_math(dst, a, b, ®isters, |x, y| x + y),
|
||||
Instruction::Sub(dst, a, b) => try_fold_math(dst, a, b, ®isters, |x, y| x - y),
|
||||
Instruction::Mul(dst, a, b) => try_fold_math(dst, a, b, ®isters, |x, y| x * y),
|
||||
Instruction::Div(dst, a, b) => {
|
||||
try_fold_math(
|
||||
dst,
|
||||
a,
|
||||
b,
|
||||
®isters,
|
||||
|x, y| if y.is_zero() { x } else { x / y },
|
||||
)
|
||||
}
|
||||
Instruction::Mod(dst, a, b) => {
|
||||
try_fold_math(
|
||||
dst,
|
||||
a,
|
||||
b,
|
||||
®isters,
|
||||
|x, y| if y.is_zero() { x } else { x % y },
|
||||
)
|
||||
}
|
||||
Instruction::Div(dst, a, b) => try_fold_math(dst, a, b, ®isters, |x, y| {
|
||||
if y.is_zero() { Decimal::ZERO } else { x / y }
|
||||
}),
|
||||
Instruction::Mod(dst, a, b) => try_fold_math(dst, a, b, ®isters, |x, y| {
|
||||
if y.is_zero() { Decimal::ZERO } else { x % y }
|
||||
}),
|
||||
Instruction::BranchEq(a, b, l) => {
|
||||
try_resolve_branch(a, b, l, ®isters, |x, y| x == y)
|
||||
}
|
||||
|
||||
@@ -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,72 @@ 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.
|
||||
/// This pass also adjusts all jump targets to account for removed instructions.
|
||||
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;
|
||||
let mut removed_lines = Vec::new();
|
||||
|
||||
// First pass: identify redundant jumps
|
||||
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;
|
||||
removed_lines.push(i);
|
||||
continue; // Skip this redundant jump
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -7,24 +7,43 @@ use std::collections::HashMap;
|
||||
pub fn dead_store_elimination<'a>(
|
||||
input: Vec<InstructionNode<'a>>,
|
||||
) -> (Vec<InstructionNode<'a>>, bool) {
|
||||
let mut changed = false;
|
||||
// Forward pass: Remove writes that are immediately overwritten
|
||||
let (input, forward_changed) = eliminate_overwritten_stores(input);
|
||||
|
||||
// Note: Backward pass disabled for now - it needs more work to handle all cases correctly
|
||||
// The forward pass is sufficient for most common patterns
|
||||
// (e.g., move r6 r15 immediately followed by move r6 r15 again)
|
||||
|
||||
(input, forward_changed)
|
||||
}
|
||||
|
||||
/// Forward pass: Remove stores that are overwritten before being read
|
||||
fn eliminate_overwritten_stores<'a>(
|
||||
input: Vec<InstructionNode<'a>>,
|
||||
) -> (Vec<InstructionNode<'a>>, bool) {
|
||||
let mut last_write: HashMap<u8, usize> = HashMap::new();
|
||||
let mut to_remove = Vec::new();
|
||||
|
||||
// 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
|
||||
to_remove.push(prev_idx);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,34 +51,31 @@ pub fn dead_store_elimination<'a>(
|
||||
last_write.insert(dest_reg, i);
|
||||
}
|
||||
|
||||
// Before clearing on labels/calls, check if current tracked writes are dead
|
||||
if matches!(
|
||||
node.instruction,
|
||||
Instruction::LabelDef(_) | Instruction::JumpAndLink(_)
|
||||
) {
|
||||
// Check all currently tracked writes to see if they're dead
|
||||
for (®, &idx) in &last_write {
|
||||
// Don't remove writes to r15 (return register)
|
||||
if reg == 15 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this write was used between write and now
|
||||
let was_used = input[idx + 1..i]
|
||||
.iter()
|
||||
.any(|n| reg_is_read_or_affects_control(&n.instruction, reg));
|
||||
|
||||
if !was_used && !to_remove.contains(&idx) {
|
||||
to_remove.push(idx);
|
||||
changed = true;
|
||||
}
|
||||
// Handle control flow instructions
|
||||
match &node.instruction {
|
||||
// JumpAndLink (function calls) only clobbers the return register (r15)
|
||||
// We can continue tracking other registers across function calls
|
||||
Instruction::JumpAndLink(_) => {
|
||||
last_write.remove(&15);
|
||||
}
|
||||
|
||||
last_write.clear();
|
||||
// Other control flow instructions create complexity - clear all tracking
|
||||
Instruction::Jump(_)
|
||||
| Instruction::LabelDef(_)
|
||||
| Instruction::BranchEq(_, _, _)
|
||||
| Instruction::BranchNe(_, _, _)
|
||||
| Instruction::BranchGt(_, _, _)
|
||||
| Instruction::BranchLt(_, _, _)
|
||||
| Instruction::BranchGe(_, _, _)
|
||||
| Instruction::BranchLe(_, _, _)
|
||||
| Instruction::BranchEqZero(_, _)
|
||||
| Instruction::BranchNeZero(_, _) => {
|
||||
last_write.clear();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if changed {
|
||||
if !to_remove.is_empty() {
|
||||
let output = input
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
|
||||
@@ -1,150 +1,41 @@
|
||||
use crate::leaf_function::find_leaf_functions;
|
||||
use il::{Instruction, InstructionNode, Operand};
|
||||
use rust_decimal::Decimal;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
/// Helper: Check if a function body contains unsafe stack manipulation.
|
||||
fn function_has_complex_stack_ops(
|
||||
instructions: &[InstructionNode],
|
||||
start_idx: usize,
|
||||
end_idx: usize,
|
||||
) -> bool {
|
||||
for instruction in instructions.iter().take(end_idx).skip(start_idx) {
|
||||
match instruction.instruction {
|
||||
Instruction::Push(_) | Instruction::Pop(_) => return true,
|
||||
Instruction::Add(Operand::StackPointer, _, _)
|
||||
| Instruction::Sub(Operand::StackPointer, _, _)
|
||||
| Instruction::Mul(Operand::StackPointer, _, _)
|
||||
| Instruction::Div(Operand::StackPointer, _, _)
|
||||
| Instruction::Move(Operand::StackPointer, _) => return true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
use il::InstructionNode;
|
||||
|
||||
/// Pass: Leaf Function Optimization
|
||||
/// If a function makes no calls (is a leaf), it doesn't need to save/restore `ra`.
|
||||
///
|
||||
/// NOTE: This optimization is DISABLED due to correctness issues.
|
||||
/// The optimization was designed for a specific calling convention (GET/PUT for RA)
|
||||
/// but the compiler generates POP ra for return address restoration. Without proper
|
||||
/// tracking of both conventions and validation of balanced push/pop pairs, this
|
||||
/// optimization corrupts the stack frame by:
|
||||
///
|
||||
/// 1. Removing `push ra` but not `pop ra`, leaving unbalanced push/pop pairs
|
||||
/// 2. Not accounting for parameter pops that occur before `push sp`
|
||||
/// 3. Assuming all RA restoration uses GET instruction, but code uses POP
|
||||
///
|
||||
/// Example of broken optimization:
|
||||
/// ```
|
||||
/// Unoptimized: Optimized (BROKEN):
|
||||
/// compare: pop r8
|
||||
/// pop r8 pop r9
|
||||
/// pop r9 ble r9 r8 5
|
||||
/// push sp move r10 1
|
||||
/// push ra j ra
|
||||
/// sgt r1 r9 r8 ^ Missing stack frame!
|
||||
/// ...
|
||||
/// pop ra
|
||||
/// pop sp
|
||||
/// j ra
|
||||
/// ```
|
||||
///
|
||||
/// Future work: Fix by handling both POP and GET calling conventions, validating
|
||||
/// balanced push/pop pairs, and accounting for parameter pops.
|
||||
pub fn optimize_leaf_functions<'a>(
|
||||
input: Vec<InstructionNode<'a>>,
|
||||
) -> (Vec<InstructionNode<'a>>, bool) {
|
||||
let leaves = find_leaf_functions(&input);
|
||||
if leaves.is_empty() {
|
||||
return (input, false);
|
||||
}
|
||||
|
||||
let mut changed = false;
|
||||
let mut to_remove = HashSet::new();
|
||||
let mut func_restore_indices = HashMap::new();
|
||||
let mut func_ra_offsets = HashMap::new();
|
||||
let mut current_function: Option<String> = None;
|
||||
let mut function_start_indices = HashMap::new();
|
||||
|
||||
// First scan: Identify instructions to remove and capture RA offsets
|
||||
for (i, node) in input.iter().enumerate() {
|
||||
match &node.instruction {
|
||||
Instruction::LabelDef(label) if !label.starts_with("__internal_L") => {
|
||||
current_function = Some(label.to_string());
|
||||
function_start_indices.insert(label.to_string(), i);
|
||||
}
|
||||
Instruction::Push(Operand::ReturnAddress) => {
|
||||
if let Some(func) = ¤t_function
|
||||
&& leaves.contains(func)
|
||||
{
|
||||
to_remove.insert(i);
|
||||
}
|
||||
}
|
||||
Instruction::Get(Operand::ReturnAddress, _, Operand::Register(_)) => {
|
||||
if let Some(func) = ¤t_function
|
||||
&& leaves.contains(func)
|
||||
{
|
||||
to_remove.insert(i);
|
||||
func_restore_indices.insert(func.clone(), i);
|
||||
|
||||
// Look back for the address calc: `sub r0 sp OFFSET`
|
||||
if i > 0
|
||||
&& let Instruction::Sub(_, Operand::StackPointer, Operand::Number(n)) =
|
||||
&input[i - 1].instruction
|
||||
{
|
||||
func_ra_offsets.insert(func.clone(), *n);
|
||||
to_remove.insert(i - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Safety Check: Verify functions don't have complex stack ops
|
||||
let mut safe_functions = HashSet::new();
|
||||
|
||||
for (func, start_idx) in &function_start_indices {
|
||||
if let Some(restore_idx) = func_restore_indices.get(func) {
|
||||
let check_start = if to_remove.contains(&(start_idx + 1)) {
|
||||
start_idx + 2
|
||||
} else {
|
||||
start_idx + 1
|
||||
};
|
||||
|
||||
if !function_has_complex_stack_ops(&input, check_start, *restore_idx) {
|
||||
safe_functions.insert(func.clone());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !changed {
|
||||
return (input, false);
|
||||
}
|
||||
|
||||
// Second scan: Rebuild with adjustments
|
||||
let mut output = Vec::with_capacity(input.len());
|
||||
let mut processing_function: Option<String> = None;
|
||||
|
||||
for (i, mut node) in input.into_iter().enumerate() {
|
||||
if to_remove.contains(&i)
|
||||
&& let Some(func) = &processing_function
|
||||
&& safe_functions.contains(func)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Instruction::LabelDef(l) = &node.instruction
|
||||
&& !l.starts_with("__internal_L")
|
||||
{
|
||||
processing_function = Some(l.to_string());
|
||||
}
|
||||
|
||||
// Apply Stack Adjustments
|
||||
if let Some(func) = &processing_function
|
||||
&& safe_functions.contains(func)
|
||||
&& let Some(ra_offset) = func_ra_offsets.get(func)
|
||||
{
|
||||
// Stack Cleanup Adjustment
|
||||
if let Instruction::Sub(
|
||||
Operand::StackPointer,
|
||||
Operand::StackPointer,
|
||||
Operand::Number(n),
|
||||
) = &mut node.instruction
|
||||
{
|
||||
let new_n = *n - Decimal::from(1);
|
||||
if new_n.is_zero() {
|
||||
continue;
|
||||
}
|
||||
*n = new_n;
|
||||
}
|
||||
|
||||
// Stack Variable Offset Adjustment
|
||||
if let Instruction::Sub(_, Operand::StackPointer, Operand::Number(n)) =
|
||||
&mut node.instruction
|
||||
&& *n > *ra_offset
|
||||
{
|
||||
*n -= Decimal::from(1);
|
||||
}
|
||||
}
|
||||
|
||||
output.push(node);
|
||||
}
|
||||
|
||||
(output, true)
|
||||
// Optimization disabled - returns input unchanged
|
||||
#[allow(unused)]
|
||||
let _leaves = find_leaf_functions(&input);
|
||||
(input, false)
|
||||
}
|
||||
|
||||
@@ -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 instructions = resolve_labels(instructions);
|
||||
|
||||
// Post-resolution Pass: Remove redundant jumps (must run after label resolution)
|
||||
let (instructions, _) = remove_redundant_jumps(instructions);
|
||||
|
||||
Instructions::new(instructions)
|
||||
}
|
||||
|
||||
@@ -358,11 +358,12 @@ fn find_matching_ra_pop<'a>(
|
||||
return Some((idx, &instructions[1..idx]));
|
||||
}
|
||||
|
||||
// Stop searching if we hit a jump (different control flow)
|
||||
// Labels are OK - they're just markers
|
||||
// Stop searching if we hit a jump (different control flow) or a function label
|
||||
// Labels are OK - they're just markers EXCEPT for user-defined function labels
|
||||
// which indicate a function boundary
|
||||
if matches!(
|
||||
node.instruction,
|
||||
Instruction::Jump(_) | Instruction::JumpRelative(_)
|
||||
Instruction::Jump(_) | Instruction::JumpRelative(_) | Instruction::LabelDef(_)
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::helpers::{get_destination_reg, reg_is_read, set_destination_reg};
|
||||
use il::{Instruction, InstructionNode};
|
||||
use il::{Instruction, InstructionNode, Operand};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Pass: Register Forwarding
|
||||
/// Eliminates intermediate moves by writing directly to the final destination.
|
||||
@@ -10,6 +11,20 @@ pub fn register_forwarding<'a>(
|
||||
let mut changed = false;
|
||||
let mut i = 0;
|
||||
|
||||
// Build a map of label positions to detect backward jumps
|
||||
// Use String keys to avoid lifetime issues with references into input
|
||||
let label_positions: HashMap<String, usize> = input
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, node)| {
|
||||
if let Instruction::LabelDef(label) = &node.instruction {
|
||||
Some((label.to_string(), idx))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
while i < input.len().saturating_sub(1) {
|
||||
let next_idx = i + 1;
|
||||
|
||||
@@ -48,23 +63,51 @@ pub fn register_forwarding<'a>(
|
||||
break;
|
||||
}
|
||||
|
||||
// Conservative: assume liveness might leak at labels/jumps
|
||||
if matches!(
|
||||
node.instruction,
|
||||
Instruction::LabelDef(_) | Instruction::Jump(_) | Instruction::JumpAndLink(_)
|
||||
) {
|
||||
temp_is_dead = false;
|
||||
// Function calls (jal) clobber the return register (r15)
|
||||
// So if we're tracking r15 and hit a function call, the old value is dead
|
||||
if matches!(node.instruction, Instruction::JumpAndLink(_)) && temp_reg == 15 {
|
||||
break;
|
||||
}
|
||||
|
||||
// Labels are just markers - they don't affect register liveness
|
||||
// But backward jumps create loops we need to analyze carefully
|
||||
let jump_target = match &node.instruction {
|
||||
Instruction::Jump(Operand::Label(target)) => Some(target.as_ref()),
|
||||
Instruction::BranchEq(_, _, Operand::Label(target))
|
||||
| Instruction::BranchNe(_, _, Operand::Label(target))
|
||||
| Instruction::BranchGt(_, _, Operand::Label(target))
|
||||
| Instruction::BranchLt(_, _, Operand::Label(target))
|
||||
| Instruction::BranchGe(_, _, Operand::Label(target))
|
||||
| Instruction::BranchLe(_, _, Operand::Label(target))
|
||||
| Instruction::BranchEqZero(_, Operand::Label(target))
|
||||
| Instruction::BranchNeZero(_, Operand::Label(target)) => Some(target.as_ref()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(target) = jump_target {
|
||||
// Check if this is a backward jump (target appears before current position)
|
||||
if let Some(&target_pos) = label_positions.get(target) {
|
||||
if target_pos < i {
|
||||
// Backward jump - could loop back, register might be live
|
||||
temp_is_dead = false;
|
||||
break;
|
||||
}
|
||||
// Forward jump is OK - doesn't affect liveness before it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if temp_is_dead {
|
||||
// Rewrite to use final destination directly
|
||||
if let Some(new_instr) = set_destination_reg(&input[i].instruction, final_reg) {
|
||||
input[i].instruction = new_instr;
|
||||
input.remove(next_idx);
|
||||
changed = true;
|
||||
continue;
|
||||
// Safety check: ensure final_reg is not used as an operand in the current instruction.
|
||||
// This prevents generating invalid instructions like `sub r5 r0 r5` (read and write same register).
|
||||
if !reg_is_read(&input[i].instruction, final_reg) {
|
||||
// Rewrite to use final destination directly
|
||||
if let Some(new_instr) = set_destination_reg(&input[i].instruction, final_reg) {
|
||||
input[i].instruction = new_instr;
|
||||
input.remove(next_idx);
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user