Skip to content

feat(lowering): trim unused parameters of generated loop functions - #10246

Open
TomerStarkware wants to merge 1 commit into
mainfrom
tomer/trim_loop_unused_parameters
Open

feat(lowering): trim unused parameters of generated loop functions#10246
TomerStarkware wants to merge 1 commit into
mainfrom
tomer/trim_loop_unused_parameters

Conversation

@TomerStarkware

Copy link
Copy Markdown
Collaborator

Adds a TrimUnusedParams optimization phase that removes parameters never used by a compiler-generated loop function's body, from both the function's signature and every call site.

  • A new unused_parameters query computes, per function, the parameter positions (in the PreOptimizations layout) that are unused by the body. Only GeneratedFunctionKey::Loop functions are considered — trimming a user-visible function would change its externally callable signature — and nothing is trimmed when optimizations are disabled.
  • The phase runs once, directly on the PreOptimizations lowering, removing the unused parameters from the function's signature and from the inputs of every call site (coupon inputs are past the parameters and are never removed).
  • The sierra generator's get_function_signature skips the trimmed parameters so the Sierra signature matches the post-optimizations lowering.

🤖 Generated with Claude Code

@cursor

cursor Bot commented Jul 23, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes core lowering signatures and call conventions for generated loops and specialized functions; incorrect trimming could break semantics or ABI, though extensive tests and guards limit exposure.

Overview
Adds a TrimUnusedParams lowering phase that drops parameters never used in the function body from both the callee signature and every call site.

The phase runs once at the start of the final optimization pipeline on PostBaseline lowering (before LowerImplicits prepends implicits), followed by ReorderStatements to remove dead argument producers. It applies only to compiler-generated loop functions and specialized functions; ordinary user functions keep their public ABI. An unused_parameters query treats “only forwarded unchanged into a recursive call at the same index” as unused, but skips undroppable values, real uses, conditional uses, destruct requirements, and params threaded across different specializations.

Tests and examples add file-based coverage, extend specialized-function fixtures with both branches of if keep, refresh loop lowering goldens, and add examples/trim_unused_params with Sierra/CASM/runner snapshots.

Reviewed by Cursor Bugbot for commit 4f536cf. Bugbot is set up for automated code reviews on this repo. Configure here.

@reviewable-StarkWare

Copy link
Copy Markdown

This change is Reviewable

@TomerStarkware
TomerStarkware requested a review from orizi July 23, 2026 14:18

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi made 1 comment.
Reviewable status: 0 of 11 files reviewed, 1 unresolved discussion (waiting on TomerStarkware).


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 20 at r1 (raw file):

/// Query implementation of [crate::db::LoweringGroup::unused_parameters].
#[salsa::tracked(returns(ref))]
pub fn unused_parameters<'db>(

would changing usages analysis to not include these somehow have the same effect?

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi made 1 comment.
Reviewable status: 0 of 11 files reviewed, 2 unresolved discussions (waiting on TomerStarkware).


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 30 at r1 (raw file):

    }
    // Only compiler-generated loop functions are trimmed, as trimming the parameters of a
    // user-visible function would change its externally callable signature.

As a first stage - allow for specialized functions as well (as again, not externally called ever)

Code quote:

    // Only compiler-generated loop functions are trimmed, as trimming the parameters of a
    // user-visible function would change its externally callable signature.

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi made 2 comments.
Reviewable status: 0 of 11 files reviewed, 4 unresolved discussions (waiting on TomerStarkware).


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 40 at r1 (raw file):

        return vec![];
    }
    let Ok(lowered) = db.lowered_body(function, LoweringStage::PreOptimizations) else {

probably post-baseline - as we would remove more unused code.

Code quote:

PreOptimizations

crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 93 at r1 (raw file):

                // which is beyond the parameters and is therefore never removed.
                remove_indices(&mut call_stmt.inputs, unused);
            }

Suggestion:

            remove_indices(&mut call_stmt.inputs, db.unused_parameters(callee));

@TomerStarkware
TomerStarkware force-pushed the tomer/trim_loop_unused_parameters branch from a43609b to 5578367 Compare July 28, 2026 14:25

@TomerStarkware TomerStarkware left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TomerStarkware made 4 comments.
Reviewable status: 0 of 12 files reviewed, 4 unresolved discussions (waiting on orizi).


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 20 at r1 (raw file):

Previously, orizi wrote…

would changing usages analysis to not include these somehow have the same effect?

used use_site analysis


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 30 at r1 (raw file):

Previously, orizi wrote…

As a first stage - allow for specialized functions as well (as again, not externally called ever)

Done.


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 40 at r1 (raw file):

Previously, orizi wrote…

probably post-baseline - as we would remove more unused code.

Done.


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 93 at r1 (raw file):

                // which is beyond the parameters and is therefore never removed.
                remove_indices(&mut call_stmt.inputs, unused);
            }

Done.

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi reviewed 5 files and all commit messages, made 2 comments, and resolved 4 discussions.
Reviewable status: 5 of 12 files reviewed, 2 unresolved discussions (waiting on TomerStarkware).


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 28 at r2 (raw file):

    if matches!(db.optimizations(), Optimizations::Disabled) {
        return vec![];
    }

should be part of the optimization fn - not the info query.

Code quote:

    // The `TrimUnusedParams` phase is only part of the enabled optimization strategy, so when
    // optimizations are disabled no parameter is trimmed.
    if matches!(db.optimizations(), Optimizations::Disabled) {
        return vec![];
    }

crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 69 at r2 (raw file):

                    && call_stmt.function.body(db) == Ok(Some(function))
                    && call_stmt.inputs.get(*position).map(|input| input.var_id) == Some(**param)
            })

Suggestion:

                if count == 1
                && let UseLocation::Statement((block_id, stmt_idx)) = loc
                && let Statement::Call(call_stmt) = &lowered.blocks[block_id].statements[stmt_idx]
                && call_stmt.function.body(db) == Ok(Some(function))
                && call_stmt.inputs.get(*position).map(|input| input.var_id) == Some(**param) {
                    true
                } else {
                    false
                }
            })

@TomerStarkware
TomerStarkware force-pushed the tomer/trim_loop_unused_parameters branch from 5578367 to d3b2811 Compare August 2, 2026 12:25

@TomerStarkware TomerStarkware left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TomerStarkware made 2 comments.
Reviewable status: 5 of 12 files reviewed, 2 unresolved discussions (waiting on orizi).


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 28 at r2 (raw file):

Previously, orizi wrote…

should be part of the optimization fn - not the info query.

Done.


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 69 at r2 (raw file):

                    && call_stmt.function.body(db) == Ok(Some(function))
                    && call_stmt.inputs.get(*position).map(|input| input.var_id) == Some(**param)
            })

Done.

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi made 2 comments and resolved 1 discussion.
Reviewable status: 4 of 12 files reviewed, 3 unresolved discussions (waiting on TomerStarkware).


crates/cairo-lang-lowering/src/db.rs line 114 at r3 (raw file):

    /// applied they should be skipped when computing the
    /// function's external signature.
    fn unused_parameters<'db>(

why is it required to be pub at all?
isn't it just local for trim?


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 34 at r3 (raw file):

    ) {
        return vec![];
    }

same issue - this sounds like something to be handled at callsite - this isn't actually used in any case.

or have a wrapper fn that has a different name.

Code quote:

    // Only compiler-generated loop functions and specialized functions are trimmed, as trimming
    // the parameters of a user-visible function would change its externally callable signature.
    if !matches!(
        function.long(db),
        ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction {
            key: GeneratedFunctionKey::Loop(_),
            ..
        }) | ConcreteFunctionWithBodyLongId::Specialized(_)
    ) {
        return vec![];
    }

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi reviewed 2 files and all commit messages, and resolved 1 discussion.
Reviewable status: 6 of 12 files reviewed, 2 unresolved discussions (waiting on TomerStarkware).

@TomerStarkware
TomerStarkware force-pushed the tomer/trim_loop_unused_parameters branch from d3b2811 to 6d7722c Compare August 2, 2026 14:18
@TomerStarkware
TomerStarkware changed the base branch from main to tomer/sierra_gen_final_signature August 2, 2026 14:18
@TomerStarkware
TomerStarkware force-pushed the tomer/sierra_gen_final_signature branch from 7b5f859 to 5447d91 Compare August 5, 2026 09:50
@TomerStarkware
TomerStarkware force-pushed the tomer/trim_loop_unused_parameters branch 2 times, most recently from a2ce4e8 to b27c8dd Compare August 11, 2026 16:55
@TomerStarkware
TomerStarkware changed the base branch from tomer/sierra_gen_final_signature to main August 11, 2026 16:58

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi reviewed all commit messages and made 2 comments.
Reviewable status: 3 of 12 files reviewed, 4 unresolved discussions (waiting on TomerStarkware).


crates/cairo-lang-lowering/src/db.rs line 106 at r5 (raw file):

    }

    /// Returns the sorted positions of the parameters of the function that are never used by its

i believe this can be totally removed now.


crates/cairo-lang-lowering/src/optimizations/trim_unused_params_test.rs line 24 at r5 (raw file):

use crate::{LoweringStage, Statement};

cairo_lang_test_utils::test_file_test!(

use the testing framework refactor.

@TomerStarkware
TomerStarkware force-pushed the tomer/trim_loop_unused_parameters branch from b27c8dd to 2859c4c Compare August 12, 2026 08:56

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi reviewed all commit messages and made 1 comment.
Reviewable status: 3 of 13 files reviewed, 5 unresolved discussions (waiting on TomerStarkware).


crates/cairo-lang-lowering/src/optimizations/test_data/trim_unused_params line 10 at r6 (raw file):

extern fn ext() -> bool nopanic;
#[target_function]
fn foo(a: felt252, b: felt252) -> felt252 {

can you replace all the testing with foo calling an unused_x fn - which calls an extern using particular vars?

all tests would be more readable, and we'd probably not need a special test code.

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi resolved 2 discussions.
Reviewable status: 3 of 13 files reviewed, 3 unresolved discussions (waiting on TomerStarkware).

@TomerStarkware
TomerStarkware force-pushed the tomer/trim_loop_unused_parameters branch from 2859c4c to 5f32d6e Compare August 12, 2026 12:43

@TomerStarkware TomerStarkware left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TomerStarkware made 5 comments.
Reviewable status: 3 of 13 files reviewed, 3 unresolved discussions (waiting on orizi).


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 34 at r3 (raw file):

Previously, orizi wrote…

same issue - this sounds like something to be handled at callsite - this isn't actually used in any case.

or have a wrapper fn that has a different name.

Done.


crates/cairo-lang-lowering/src/optimizations/trim_unused_params_test.rs line 24 at r5 (raw file):

Previously, orizi wrote…

use the testing framework refactor.

Done.


crates/cairo-lang-lowering/src/optimizations/test_data/trim_unused_params line 10 at r6 (raw file):

Previously, orizi wrote…

can you replace all the testing with foo calling an unused_x fn - which calls an extern using particular vars?

all tests would be more readable, and we'd probably not need a special test code.

Done.


crates/cairo-lang-lowering/src/db.rs line 114 at r3 (raw file):

Previously, orizi wrote…

why is it required to be pub at all?
isn't it just local for trim?

Done.


crates/cairo-lang-lowering/src/db.rs line 106 at r5 (raw file):

Previously, orizi wrote…

i believe this can be totally removed now.

Done.

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi reviewed 2 files, made 5 comments, and resolved 3 discussions.
Reviewable status: 5 of 13 files reviewed, 5 unresolved discussions (waiting on TomerStarkware).


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 105 at r7 (raw file):

            if trimmable(callee) {
                remove_indices(&mut call_stmt.inputs, unused_parameters(db, callee));
            }

Suggestion:

            if let Statement::Call(call_stmt) = stmt 
            && let Some(callee) = call_stmt.function.body(db)?
            && trimmable(callee) {
                remove_indices(&mut call_stmt.inputs, unused_parameters(db, callee));
            }

crates/cairo-lang-lowering/src/optimizations/trim_unused_params_test.rs line 45 at r7 (raw file):

                _ => 100,
            })
        }),

would #[inline] on the expected function just

Code quote:

    init_lowering_group(
        &mut db,
        Optimizations::Enabled(OptimizationConfig {
            moveable_functions: vec![],
            inlining_strategy: InliningStrategy::InlineSmallFunctions(0),
            skip_const_folding: false,
        }),
        Some(|db, function_id| {
            Ok(match function_id.long(db) {
                ConcreteFunctionWithBodyLongId::Specialized(_) => 1,
                _ => 100,
            })
        }),

crates/cairo-lang-lowering/src/optimizations/test_data/trim_unused_params line 18 at r7 (raw file):

fn foo(a: felt252, b: u32) {
    unused_a(0, a, b);
}

this is the better ordering. (caller before callee)

prefer good name over extra doc.

(do this everywhere in this file)

Suggestion:

//! > cairo_code
#[target_function]
fn foo(a: felt252, b: u32) {
    callee(0, a, b);
}

fn callee(_assure_specialized: felt252, unused: felt252, used: u32) {
    ext(used);
}

#[allow(extern_outside_corelib)]
extern fn ext(v: u32) nopanic;

crates/cairo-lang-lowering/src/optimizations/test_data/trim_unused_params line 58 at r7 (raw file):

fn foo(a: u8, n: felt252) {
    thread_a(0, a, n);
}

Suggestion:

#[target_function]
fn foo(a: u8, n: felt252) {
    callee(0, a, n);
}

// `a` is only passed to the recursive call at its own position, so the entire chain is dead.
fn callee(_assure_specialized: felt252, threaded: u8, n: felt252) {
    if n == 0 {
        return;
    }
    thread_a(0, threaded, n - 1);
}

tests/e2e_test_data/trim_unused_params line 1 at r7 (raw file):

//! > Recursive loop function with a param that is always overwritten before use.

this dir is for sierra-to-casm related testing.

examples dir might be more relevant.

@TomerStarkware
TomerStarkware force-pushed the tomer/trim_loop_unused_parameters branch from 5f32d6e to cc52f34 Compare August 12, 2026 14:06

@TomerStarkware TomerStarkware left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TomerStarkware made 5 comments.
Reviewable status: 5 of 13 files reviewed, 5 unresolved discussions (waiting on orizi).


crates/cairo-lang-lowering/src/optimizations/trim_unused_params_test.rs line 45 at r7 (raw file):

Previously, orizi wrote…

would #[inline] on the expected function just

Done.


crates/cairo-lang-lowering/src/optimizations/test_data/trim_unused_params line 18 at r7 (raw file):

Previously, orizi wrote…

this is the better ordering. (caller before callee)

prefer good name over extra doc.

(do this everywhere in this file)

Done.


tests/e2e_test_data/trim_unused_params line 1 at r7 (raw file):

Previously, orizi wrote…

this dir is for sierra-to-casm related testing.

examples dir might be more relevant.

Done.


crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs line 105 at r7 (raw file):

            if trimmable(callee) {
                remove_indices(&mut call_stmt.inputs, unused_parameters(db, callee));
            }

Done.


crates/cairo-lang-lowering/src/optimizations/test_data/trim_unused_params line 58 at r7 (raw file):

fn foo(a: u8, n: felt252) {
    thread_a(0, a, n);
}

Done.

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

@orizi reviewed 14 files and all commit messages, made 3 comments, and resolved 5 discussions.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on TomerStarkware).


crates/cairo-lang-lowering/src/optimizations/test_data/trim_unused_params line 97 at r8 (raw file):

    ext_felt252(used_a);
    ext_u32(used_b);
}

Suggestion:

fn callee(_assure_specialized: felt252, used_a: felt252, used_b: u32) {
    ext((used_a, used_b));
}

crates/cairo-lang-lowering/src/optimizations/test_data/trim_unused_params line 102 at r8 (raw file):

extern fn ext_felt252(v: felt252) nopanic;
#[allow(extern_outside_corelib)]
extern fn ext_u32(v: u32) nopanic;

Suggestion:

#[allow(extern_outside_corelib)]
extern fn ext<T>(t: T) nopanic;

@TomerStarkware
TomerStarkware force-pushed the tomer/trim_loop_unused_parameters branch from cc52f34 to 944d086 Compare August 13, 2026 08:22

@TomerStarkware TomerStarkware left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TomerStarkware made 2 comments.
Reviewable status: 17 of 18 files reviewed, 2 unresolved discussions (waiting on orizi).


crates/cairo-lang-lowering/src/optimizations/test_data/trim_unused_params line 97 at r8 (raw file):

    ext_felt252(used_a);
    ext_u32(used_b);
}

Done.


crates/cairo-lang-lowering/src/optimizations/test_data/trim_unused_params line 102 at r8 (raw file):

extern fn ext_felt252(v: felt252) nopanic;
#[allow(extern_outside_corelib)]
extern fn ext_u32(v: u32) nopanic;

Done.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 944d086. Configure here.

Comment thread crates/cairo-lang-lowering/src/optimizations/trim_unused_params.rs

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi reviewed 1 file and all commit messages, and resolved 2 discussions.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on TomerStarkware).

Adds a `TrimUnusedParams` optimization phase that removes parameters
never used by a compiler-generated loop function's body, from both the
function's signature and every call site. Only loop functions are
trimmed, as trimming a user-visible function would change its
externally callable signature.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@TomerStarkware
TomerStarkware force-pushed the tomer/trim_loop_unused_parameters branch from 944d086 to 4f536cf Compare August 13, 2026 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants