Skip to content

chore(ssa): Refactor unrolling#9653

Merged
aakoshh merged 9 commits intomasterfrom
af/refactor-unrolling
Aug 27, 2025
Merged

chore(ssa): Refactor unrolling#9653
aakoshh merged 9 commits intomasterfrom
af/refactor-unrolling

Conversation

@aakoshh
Copy link
Contributor

@aakoshh aakoshh commented Aug 27, 2025

Description

Problem*

Some refactoring opportunities I saw to make the code easier to audit.

Summary*

A few changes to unrolling that shouldn't alter its behaviour, but hopefully make the code cleaner:

  • Remove the elimination of inc_rc and dec_rc from ACIR functions: these have nothing to do with unrolling (we only count instructions for Brillig to see if a loop is small), and when we insert instructions into the block we unroll into, the simplification inevitably calls is_handled_by_runtime, which returns false for these, thus they will not be inserted anyway.
  • Switch to std::collections::HashSet from im::HashSet as we don't need deterministic data structures for unrolling, and hopefully O(1) is faster than O(log(n)).
  • Get rid of unroll_loop_block_helper: it was only called from unroll_loop_block and didn't seem to increase clarity.
  • Get rid of the recursion in unroll_each: previously when Loops::unroll_each encountered a nested loop, it stopped consuming yet_to_unroll, created a new Loops, made a recursive call to itself, then merged the results. We would probably not have loops nested deep enough to cause a stack overflow, however:
    • I found it a difficult to grok; now we have a simpler loop construct with a succession of continue conditions.
    • It never processed the rest of yet_to_unroll, so all the unrelated "sibling" loops we discovered in find_all were wasted. Now we finish going through them before refreshing the context.
    • It required carefully passing the failed_to_unroll to the next Loops to avoid redoing them again.
  • Move the failed_to_unroll and modified_blocks fields from Loops into the unroll_each method: Loops are used from multiple modules now (e.g. LICM), and these were only relevant to unrolling. As mentioned they also required passing from one context to the next. By keeping them local there is less reasoning to be done about their scope.
  • Removed Loops::unroll_each and moved all functionality into Function::try_unroll_loops. The latter only forwarded to the former, and now that it's no longer recursive, I think it's cleaner to keep all unrolling in there, rather than Loops, because during the iteration we do call Loops::find_all to refresh the context, which makes it look a bit odd to call it on self.
  • Only iterating reachable blocks.
  • Added some extra assertions where empty values were returned.

Additional Context

Documentation*

Check one:

  • No documentation needed.
  • Documentation included in this PR.
  • [For Experimental Features] Documentation to be submitted in a separate PR.

PR Checklist*

  • I have tested the changes locally.
  • I have formatted the changes with Prettier and/or cargo fmt on default settings.

@aakoshh aakoshh changed the title Af/refactor unrolling chore(ssa): Refactor unrolling Aug 27, 2025
@aakoshh aakoshh requested a review from a team August 27, 2025 11:11
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Test Suite Duration'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.20.

Benchmark suite Current: b3e4166 Previous: d94400f Ratio
test_report_AztecProtocol_aztec-packages_noir-projects_noir-protocol-circuits_crates_blob 238 s 177 s 1.34
test_report_zkpassport_noir-ecdsa_ 3 s 2 s 1.50

This comment was automatically generated by workflow using github-action-benchmark.

CC: @TomAFrench

Copy link
Member

@TomAFrench TomAFrench left a comment

Choose a reason for hiding this comment

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

Looks sensible to me.

@aakoshh aakoshh added this pull request to the merge queue Aug 27, 2025
@aakoshh aakoshh removed this pull request from the merge queue due to a manual request Aug 27, 2025
@aakoshh aakoshh enabled auto-merge August 27, 2025 20:10
@aakoshh aakoshh added this pull request to the merge queue Aug 27, 2025
Merged via the queue into master with commit 3e195c6 Aug 27, 2025
122 checks passed
@aakoshh aakoshh deleted the af/refactor-unrolling branch August 27, 2025 21:05
github-merge-queue bot pushed a commit to AztecProtocol/aztec-packages that referenced this pull request Aug 28, 2025
Automated pull of nightly from the
[noir](https://github.com/noir-lang/noir) programming language, a
dependency of Aztec.
BEGIN_COMMIT_OVERRIDE
fix: make Ord for slices lexicographic (elements first, then length)
(noir-lang/noir#9555)
chore(ssa): Refactor `unrolling`
(noir-lang/noir#9653)
chore(docs): Update dependency page's examples
(noir-lang/noir#9634)
fix(ssa): Constant fold Brillig calls using the SSA interpreter
(noir-lang/noir#9655)
chore: LICM refactors (noir-lang/noir#9642)
chore: add test for trait bound on implementing type
(noir-lang/noir#9652)
chore: pass `DataFlowGraph` instead of `Function` as arg
(noir-lang/noir#9656)
feat: Group one audit tests
(noir-lang/noir#9445)
fix(expand): better handling of dereferences (again)
(noir-lang/noir#9654)
feat(mem2reg): address last known value is independent of its aliases
(take three) (noir-lang/noir#9633)
chore: remove handling for slice arguments to MSM
(noir-lang/noir#9648)
fix: validate binary operations which do not allow fields
(noir-lang/noir#9649)
END_COMMIT_OVERRIDE
github-merge-queue bot pushed a commit to AztecProtocol/aztec-packages that referenced this pull request Aug 28, 2025
Automated pull of nightly from the
[noir](https://github.com/noir-lang/noir) programming language, a
dependency of Aztec.
BEGIN_COMMIT_OVERRIDE
fix: make Ord for slices lexicographic (elements first, then length)
(noir-lang/noir#9555)
chore(ssa): Refactor `unrolling`
(noir-lang/noir#9653)
chore(docs): Update dependency page's examples
(noir-lang/noir#9634)
fix(ssa): Constant fold Brillig calls using the SSA interpreter
(noir-lang/noir#9655)
chore: LICM refactors (noir-lang/noir#9642)
chore: add test for trait bound on implementing type
(noir-lang/noir#9652)
chore: pass `DataFlowGraph` instead of `Function` as arg
(noir-lang/noir#9656)
feat: Group one audit tests
(noir-lang/noir#9445)
fix(expand): better handling of dereferences (again)
(noir-lang/noir#9654)
feat(mem2reg): address last known value is independent of its aliases
(take three) (noir-lang/noir#9633)
chore: remove handling for slice arguments to MSM
(noir-lang/noir#9648)
fix: validate binary operations which do not allow fields
(noir-lang/noir#9649)
END_COMMIT_OVERRIDE
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