Added more optimizations in regards to function invocations and backing

up and restoring registers
This commit is contained in:
2025-12-17 21:05:01 -07:00
parent ecfed65221
commit 6b18489f54
7 changed files with 108 additions and 91 deletions

View File

@@ -6,6 +6,9 @@
- Shorthand is `lr` - Shorthand is `lr`
- Longform is `loadReagent` - Longform is `loadReagent`
- Update various Rust dependencies - Update various Rust dependencies
- Added more optimizations, prioritizing `pop` instead of `get` when available
when backing up / restoring registers for function invocations. This should
save approximately 2 lines per backed up register
[0.3.3] [0.3.3]

View File

@@ -54,9 +54,7 @@ fn nested_binary_expressions() -> Result<()> {
move r15 r2 move r15 r2
j __internal_L1 j __internal_L1
__internal_L1: __internal_L1:
sub r0 sp 1 pop ra
get ra db r0
sub sp sp 1
j ra j ra
main: main:
push 10 push 10

View File

@@ -18,9 +18,7 @@ fn no_arguments() -> anyhow::Result<()> {
doSomething: doSomething:
push ra push ra
__internal_L1: __internal_L1:
sub r0 sp 1 pop ra
get ra db r0
sub sp sp 1
j ra j ra
main: main:
jal doSomething jal doSomething
@@ -61,9 +59,7 @@ fn let_var_args() -> anyhow::Result<()> {
move r15 r1 move r15 r1
j __internal_L1 j __internal_L1
__internal_L1: __internal_L1:
sub r0 sp 1 pop ra
get ra db r0
sub sp sp 1
j ra j ra
main: main:
__internal_L2: __internal_L2:
@@ -71,9 +67,7 @@ fn let_var_args() -> anyhow::Result<()> {
push r8 push r8
push r8 push r8
jal mul2 jal mul2
sub r0 sp 1 pop r8
get r8 db r0
sub sp sp 1
move r9 r15 move r9 r15
pow r1 r9 2 pow r1 r9 2
move r9 r1 move r9 r1
@@ -129,9 +123,7 @@ fn inline_literal_args() -> anyhow::Result<()> {
move r15 5 move r15 5
j __internal_L1 j __internal_L1
__internal_L1: __internal_L1:
sub r0 sp 1 pop ra
get ra db r0
sub sp sp 1
j ra j ra
main: main:
move r8 123 move r8 123
@@ -139,9 +131,7 @@ fn inline_literal_args() -> anyhow::Result<()> {
push 12 push 12
push 34 push 34
jal doSomething jal doSomething
sub r0 sp 1 pop r8
get r8 db r0
sub sp sp 1
move r9 r15 move r9 r15
" "
} }
@@ -171,9 +161,7 @@ fn mixed_args() -> anyhow::Result<()> {
pop r9 pop r9
push ra push ra
__internal_L1: __internal_L1:
sub r0 sp 1 pop ra
get ra db r0
sub sp sp 1
j ra j ra
main: main:
move r8 123 move r8 123
@@ -181,9 +169,7 @@ fn mixed_args() -> anyhow::Result<()> {
push r8 push r8
push 456 push 456
jal doSomething jal doSomething
sub r0 sp 1 pop r8
get r8 db r0
sub sp sp 1
move r9 r15 move r9 r15
" "
} }
@@ -216,9 +202,7 @@ fn with_return_statement() -> anyhow::Result<()> {
move r15 456 move r15 456
j __internal_L1 j __internal_L1
__internal_L1: __internal_L1:
sub r0 sp 1 pop ra
get ra db r0
sub sp sp 1
j ra j ra
main: main:
push 123 push 123
@@ -252,9 +236,7 @@ fn with_negative_return_literal() -> anyhow::Result<()> {
push ra push ra
move r15 -1 move r15 -1
__internal_L1: __internal_L1:
sub r0 sp 1 pop ra
get ra db r0
sub sp sp 1
j ra j ra
main: main:
jal doSomething jal doSomething

View File

@@ -135,9 +135,7 @@ fn test_boolean_return() -> anyhow::Result<()> {
move r15 1 move r15 1
j __internal_L1 j __internal_L1
__internal_L1: __internal_L1:
sub r0 sp 1 pop ra
get ra db r0
sub sp sp 1
j ra j ra
main: main:
jal getTrue jal getTrue

View File

@@ -5,7 +5,12 @@ use pretty_assertions::assert_eq;
fn test_function_declaration_with_spillover_params() -> anyhow::Result<()> { fn test_function_declaration_with_spillover_params() -> anyhow::Result<()> {
let compiled = compile!(debug r#" let compiled = compile!(debug r#"
// we need more than 4 params to 'spill' into a stack var // we need more than 4 params to 'spill' into a stack var
fn doSomething(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {}; fn doSomething(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {
return arg1 + arg2 + arg3 + arg4 + arg5 + arg6 + arg7 + arg8 + arg9;
};
let item1 = 1;
let returned = doSomething(item1, 2, 3, 4, 5, 6, 7, 8, 9);
"#); "#);
assert_eq!( assert_eq!(
@@ -21,11 +26,39 @@ fn test_function_declaration_with_spillover_params() -> anyhow::Result<()> {
pop r13 pop r13
pop r14 pop r14
push ra push ra
sub r0 sp 3
get r1 db r0
sub r0 sp 2
get r2 db r0
add r3 r1 r2
add r4 r3 r14
add r5 r4 r13
add r6 r5 r12
add r7 r6 r11
add r1 r7 r10
add r2 r1 r9
add r3 r2 r8
move r15 r3
j __internal_L1
__internal_L1: __internal_L1:
sub r0 sp 1 pop ra
get ra db r0 sub sp sp 2
sub sp sp 3
j ra j ra
main:
move r8 1
push r8
push r8
push 2
push 3
push 4
push 5
push 6
push 7
push 8
push 9
jal doSomething
pop r8
move r9 r15
"} "}
); );
@@ -60,9 +93,7 @@ fn test_early_return() -> anyhow::Result<()> {
move r8 3 move r8 3
j __internal_L1 j __internal_L1
__internal_L1: __internal_L1:
sub r0 sp 1 pop ra
get ra db r0
sub sp sp 1
j ra j ra
main: main:
jal doSomething jal doSomething
@@ -91,9 +122,7 @@ fn test_function_declaration_with_register_params() -> anyhow::Result<()> {
pop r9 pop r9
push ra push ra
__internal_L1: __internal_L1:
sub r0 sp 1 pop ra
get ra db r0
sub sp sp 1
j ra j ra
"} "}
); );

View File

@@ -1115,43 +1115,26 @@ impl<'a> Compiler<'a> {
Some(name.span), Some(name.span),
)?; )?;
for register in active_registers { // cleanup spilled temporary variables
let VariableLocation::Stack(stack_offset) = stack let total_stack_usage = stack.stack_offset();
.get_location_of(&Cow::from(format!("temp_{register}")), None) let saved_regs_count = active_registers.len() as u16;
.map_err(Error::Scope)?
else { if total_stack_usage > saved_regs_count {
// This shouldn't happen if we just added it let spill_amount = total_stack_usage - saved_regs_count;
return Err(Error::Unknown(
format!("Failed to recover temp_{register}"),
Some(name.span),
));
};
self.write_instruction( self.write_instruction(
Instruction::Sub( Instruction::Sub(
Operand::Register(VariableScope::TEMP_STACK_REGISTER),
Operand::StackPointer, Operand::StackPointer,
Operand::Number(stack_offset.into()), Operand::StackPointer,
), Operand::Number(spill_amount.into()),
Some(name.span),
)?;
self.write_instruction(
Instruction::Get(
Operand::Register(register),
Operand::Device(Cow::from("db")),
Operand::Register(VariableScope::TEMP_STACK_REGISTER),
), ),
Some(name.span), Some(name.span),
)?; )?;
} }
if stack.stack_offset() > 0 { // restore the registers in reverse order from the stack, now using `pop`
for register in active_registers.iter().rev() {
self.write_instruction( self.write_instruction(
Instruction::Sub( Instruction::Pop(Operand::Register(*register)),
Operand::StackPointer,
Operand::StackPointer,
Operand::Number(Decimal::from(stack.stack_offset())),
),
Some(name.span), Some(name.span),
)?; )?;
} }
@@ -2735,6 +2718,21 @@ impl<'a> Compiler<'a> {
self.write_instruction(Instruction::LabelDef(return_label.clone()), Some(span))?; self.write_instruction(Instruction::LabelDef(return_label.clone()), Some(span))?;
if ra_stack_offset == 1 {
self.write_instruction(Instruction::Pop(Operand::ReturnAddress), Some(span))?;
let remaining_cleanup = block_scope.stack_offset() - 1;
if remaining_cleanup > 0 {
self.write_instruction(
Instruction::Sub(
Operand::StackPointer,
Operand::StackPointer,
Operand::Number(remaining_cleanup.into()),
),
Some(span),
)?;
}
} else {
self.write_instruction( self.write_instruction(
Instruction::Sub( Instruction::Sub(
Operand::Register(VariableScope::TEMP_STACK_REGISTER), Operand::Register(VariableScope::TEMP_STACK_REGISTER),
@@ -2763,6 +2761,7 @@ impl<'a> Compiler<'a> {
Some(span), Some(span),
)?; )?;
} }
}
self.write_instruction(Instruction::Jump(Operand::ReturnAddress), Some(span))?; self.write_instruction(Instruction::Jump(Operand::ReturnAddress), Some(span))?;
Ok(()) Ok(())

View File

@@ -661,6 +661,14 @@ fn reg_is_read(instr: &Instruction, reg: u8) -> bool {
Instruction::BranchEqZero(a, _) | Instruction::BranchNeZero(a, _) => check(a), Instruction::BranchEqZero(a, _) | Instruction::BranchNeZero(a, _) => check(a),
Instruction::LoadReagent(_, device, _, item_hash) => check(device) || check(item_hash),
Instruction::LoadSlot(_, dev, slot, _) => check(dev) || check(slot),
Instruction::LoadBatch(_, dev, _, mode) => check(dev) || check(mode),
Instruction::LoadBatchNamed(_, d_hash, n_hash, _, mode) => {
check(d_hash) || check(n_hash) || check(mode)
}
Instruction::SetEq(_, a, b) Instruction::SetEq(_, a, b)
| Instruction::SetNe(_, a, b) | Instruction::SetNe(_, a, b)
| Instruction::SetGt(_, a, b) | Instruction::SetGt(_, a, b)