From 6b18489f54adfca49b5cb9e5d92859e48a1cf629 Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Wed, 17 Dec 2025 21:05:01 -0700 Subject: [PATCH] Added more optimizations in regards to function invocations and backing up and restoring registers --- Changelog.md | 3 + .../compiler/src/test/binary_expression.rs | 4 +- .../test/declaration_function_invocation.rs | 36 ++----- .../compiler/src/test/declaration_literal.rs | 4 +- .../compiler/src/test/function_declaration.rs | 49 ++++++++-- rust_compiler/libs/compiler/src/v1.rs | 95 +++++++++---------- rust_compiler/libs/optimizer/src/lib.rs | 8 ++ 7 files changed, 108 insertions(+), 91 deletions(-) diff --git a/Changelog.md b/Changelog.md index 1478236..59ede2b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,9 @@ - Shorthand is `lr` - Longform is `loadReagent` - 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] diff --git a/rust_compiler/libs/compiler/src/test/binary_expression.rs b/rust_compiler/libs/compiler/src/test/binary_expression.rs index 18d7885..5effd99 100644 --- a/rust_compiler/libs/compiler/src/test/binary_expression.rs +++ b/rust_compiler/libs/compiler/src/test/binary_expression.rs @@ -54,9 +54,7 @@ fn nested_binary_expressions() -> Result<()> { move r15 r2 j __internal_L1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: push 10 diff --git a/rust_compiler/libs/compiler/src/test/declaration_function_invocation.rs b/rust_compiler/libs/compiler/src/test/declaration_function_invocation.rs index e4e8fa7..aa797bb 100644 --- a/rust_compiler/libs/compiler/src/test/declaration_function_invocation.rs +++ b/rust_compiler/libs/compiler/src/test/declaration_function_invocation.rs @@ -18,9 +18,7 @@ fn no_arguments() -> anyhow::Result<()> { doSomething: push ra __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: jal doSomething @@ -61,9 +59,7 @@ fn let_var_args() -> anyhow::Result<()> { move r15 r1 j __internal_L1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: __internal_L2: @@ -71,9 +67,7 @@ fn let_var_args() -> anyhow::Result<()> { push r8 push r8 jal mul2 - sub r0 sp 1 - get r8 db r0 - sub sp sp 1 + pop r8 move r9 r15 pow r1 r9 2 move r9 r1 @@ -129,9 +123,7 @@ fn inline_literal_args() -> anyhow::Result<()> { move r15 5 j __internal_L1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: move r8 123 @@ -139,9 +131,7 @@ fn inline_literal_args() -> anyhow::Result<()> { push 12 push 34 jal doSomething - sub r0 sp 1 - get r8 db r0 - sub sp sp 1 + pop r8 move r9 r15 " } @@ -171,9 +161,7 @@ fn mixed_args() -> anyhow::Result<()> { pop r9 push ra __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: move r8 123 @@ -181,9 +169,7 @@ fn mixed_args() -> anyhow::Result<()> { push r8 push 456 jal doSomething - sub r0 sp 1 - get r8 db r0 - sub sp sp 1 + pop r8 move r9 r15 " } @@ -216,9 +202,7 @@ fn with_return_statement() -> anyhow::Result<()> { move r15 456 j __internal_L1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: push 123 @@ -252,9 +236,7 @@ fn with_negative_return_literal() -> anyhow::Result<()> { push ra move r15 -1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: jal doSomething diff --git a/rust_compiler/libs/compiler/src/test/declaration_literal.rs b/rust_compiler/libs/compiler/src/test/declaration_literal.rs index 58927c4..5f438d3 100644 --- a/rust_compiler/libs/compiler/src/test/declaration_literal.rs +++ b/rust_compiler/libs/compiler/src/test/declaration_literal.rs @@ -135,9 +135,7 @@ fn test_boolean_return() -> anyhow::Result<()> { move r15 1 j __internal_L1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: jal getTrue diff --git a/rust_compiler/libs/compiler/src/test/function_declaration.rs b/rust_compiler/libs/compiler/src/test/function_declaration.rs index 2c909f6..8e24a54 100644 --- a/rust_compiler/libs/compiler/src/test/function_declaration.rs +++ b/rust_compiler/libs/compiler/src/test/function_declaration.rs @@ -5,7 +5,12 @@ use pretty_assertions::assert_eq; fn test_function_declaration_with_spillover_params() -> anyhow::Result<()> { let compiled = compile!(debug r#" // 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!( @@ -21,11 +26,39 @@ fn test_function_declaration_with_spillover_params() -> anyhow::Result<()> { pop r13 pop r14 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: - sub r0 sp 1 - get ra db r0 - sub sp sp 3 + pop ra + sub sp sp 2 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 j __internal_L1 __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra main: jal doSomething @@ -91,9 +122,7 @@ fn test_function_declaration_with_register_params() -> anyhow::Result<()> { pop r9 push ra __internal_L1: - sub r0 sp 1 - get ra db r0 - sub sp sp 1 + pop ra j ra "} ); diff --git a/rust_compiler/libs/compiler/src/v1.rs b/rust_compiler/libs/compiler/src/v1.rs index c536f64..69cf3e9 100644 --- a/rust_compiler/libs/compiler/src/v1.rs +++ b/rust_compiler/libs/compiler/src/v1.rs @@ -1115,43 +1115,26 @@ impl<'a> Compiler<'a> { Some(name.span), )?; - for register in active_registers { - let VariableLocation::Stack(stack_offset) = stack - .get_location_of(&Cow::from(format!("temp_{register}")), None) - .map_err(Error::Scope)? - else { - // This shouldn't happen if we just added it - return Err(Error::Unknown( - format!("Failed to recover temp_{register}"), - Some(name.span), - )); - }; + // cleanup spilled temporary variables + let total_stack_usage = stack.stack_offset(); + let saved_regs_count = active_registers.len() as u16; + + if total_stack_usage > saved_regs_count { + let spill_amount = total_stack_usage - saved_regs_count; self.write_instruction( Instruction::Sub( - Operand::Register(VariableScope::TEMP_STACK_REGISTER), Operand::StackPointer, - Operand::Number(stack_offset.into()), - ), - Some(name.span), - )?; - - self.write_instruction( - Instruction::Get( - Operand::Register(register), - Operand::Device(Cow::from("db")), - Operand::Register(VariableScope::TEMP_STACK_REGISTER), + Operand::StackPointer, + Operand::Number(spill_amount.into()), ), 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( - Instruction::Sub( - Operand::StackPointer, - Operand::StackPointer, - Operand::Number(Decimal::from(stack.stack_offset())), - ), + Instruction::Pop(Operand::Register(*register)), Some(name.span), )?; } @@ -2735,33 +2718,49 @@ impl<'a> Compiler<'a> { self.write_instruction(Instruction::LabelDef(return_label.clone()), Some(span))?; - self.write_instruction( - Instruction::Sub( - Operand::Register(VariableScope::TEMP_STACK_REGISTER), - Operand::StackPointer, - Operand::Number(ra_stack_offset.into()), - ), - Some(span), - )?; + if ra_stack_offset == 1 { + self.write_instruction(Instruction::Pop(Operand::ReturnAddress), Some(span))?; - self.write_instruction( - Instruction::Get( - Operand::ReturnAddress, - Operand::Device(Cow::from("db")), - Operand::Register(VariableScope::TEMP_STACK_REGISTER), - ), - Some(span), - )?; - - if block_scope.stack_offset() > 0 { + 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( Instruction::Sub( + Operand::Register(VariableScope::TEMP_STACK_REGISTER), Operand::StackPointer, - Operand::StackPointer, - Operand::Number(block_scope.stack_offset().into()), + Operand::Number(ra_stack_offset.into()), ), Some(span), )?; + + self.write_instruction( + Instruction::Get( + Operand::ReturnAddress, + Operand::Device(Cow::from("db")), + Operand::Register(VariableScope::TEMP_STACK_REGISTER), + ), + Some(span), + )?; + + if block_scope.stack_offset() > 0 { + self.write_instruction( + Instruction::Sub( + Operand::StackPointer, + Operand::StackPointer, + Operand::Number(block_scope.stack_offset().into()), + ), + Some(span), + )?; + } } self.write_instruction(Instruction::Jump(Operand::ReturnAddress), Some(span))?; diff --git a/rust_compiler/libs/optimizer/src/lib.rs b/rust_compiler/libs/optimizer/src/lib.rs index 10f5d95..855f913 100644 --- a/rust_compiler/libs/optimizer/src/lib.rs +++ b/rust_compiler/libs/optimizer/src/lib.rs @@ -661,6 +661,14 @@ fn reg_is_read(instr: &Instruction, reg: u8) -> bool { 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::SetNe(_, a, b) | Instruction::SetGt(_, a, b)