Skip to content

fix(ssa): Keep reference count increments for array set values#9344

Merged
jfecher merged 4 commits intomasterfrom
mv/keep-inc-rc-on-nested-array-sets
Jul 29, 2025
Merged

fix(ssa): Keep reference count increments for array set values#9344
jfecher merged 4 commits intomasterfrom
mv/keep-inc-rc-on-nested-array-sets

Conversation

@vezenovm
Copy link
Contributor

Description

Problem*

Resolves #9155

Summary*

Take the SSA posted from the issue:

Details

brillig(inline) predicate_pure fn func_1 f1 {
  b0(v0: [[Field; 1]; 1]):
    v2 = allocate -> &mut [[Field; 1]; 1]
    store v0 at v2
    v4 = make_array [Field 2] : [Field; 1]
    jmp b1(u32 0)
  b1(v1: u32):
    v7 = lt v1, u32 2
    jmpif v7 then: b2, else: b3
  b2():
    v11 = allocate -> &mut Field
    store Field 0 at v11
    jmp b4()
  b3():
    v8 = load v2 -> [[Field; 1]; 1]
    v9 = array_get v8, index u32 0 -> [Field; 1]
    inc_rc v9
    v10 = array_get v9, index u32 0 -> Field
    return v10
  b4():
    v13 = load v11 -> Field
    v15 = eq v13, Field 1
    jmpif v15 then: b5, else: b6
  b5():
    inc_rc v4                                                               // <- this inc_rc gets removed
    v22 = load v2 -> [[Field; 1]; 1]
    v23 = array_set v22, index u32 0, value v4
    store v23 at v2
    v25 = unchecked_add v1, u32 1
    jmp b1(v25)
  b6():
    v16 = load v11 -> Field
    v17 = add v16, Field 1
    store v17 at v11
    v18 = load v2 -> [[Field; 1]; 1]
    v19 = array_get v18, index u32 0 -> [Field; 1]
    v20 = array_set v19, index u32 0, value Field 1
    v21 = array_set v18, index u32 0, value v20
    store v21 at v2
    jmp b4()
}

When inc_rc v4 gets removed, we risk it later being mutated in v20 = array_set v19, index u32 0, value Field 1. Thus, when we later go to do v23 = array_set v22, index u32 0, value v4 once more, we will be writing [1] rather than [2].

The inc_rc in b5 is inserted by LICM (correctly). And if we hoist [2] manually and check the monomorphized program we can see that we are expected to clone [2]:

unconstrained fn func_1$f1(mut a$l0: [[Field; 1]; 1]) -> Field {
    let two$l1 = [2];
    {
        let mut i$l2 = 0;
        loop {
            if (i$l2 == 2) {
                break
            } else {
                i$l2 = (i$l2 + 1);
                let mut j$l3 = 0;
                loop {
                    if (j$l3 == 1) {
                        break
                    } else {
                        j$l3 = (j$l3 + 1);
                        a$l0[0][0] = 1
                    }
                };
                a$l0[0] = two$l1.clone()
            }
        }
    };;
    a$l0[0].clone()[0]
}

Additional Context

Thanks to @asterite for debugging this issue.

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.

@vezenovm vezenovm requested review from a team and jfecher July 28, 2025 19:25
@github-actions
Copy link
Contributor

github-actions bot commented Jul 28, 2025

Changes to Brillig bytecode sizes

Generated at commit: 51b4a8a8fcc0b84b28b9c655b52f75553862f7d6, compared to commit: 4f733adf44501d52eea35d03c9761b27e295af0f

🧾 Summary (10% most significant diffs)

Program Brillig opcodes (+/-) %
fold_complex_outputs_inliner_max +54 ❌ +14.96%
nested_array_in_slice_inliner_max +90 ❌ +10.23%

Full diff report 👇
Program Brillig opcodes (+/-) %
fold_complex_outputs_inliner_max 415 (+54) +14.96%
nested_array_in_slice_inliner_max 970 (+90) +10.23%
fold_complex_outputs_inliner_zero 471 (+36) +8.28%
fold_complex_outputs_inliner_min 542 (+36) +7.11%
nested_array_dynamic_inliner_max 1,768 (+44) +2.55%

@github-actions
Copy link
Contributor

github-actions bot commented Jul 28, 2025

Changes to number of Brillig opcodes executed

Generated at commit: 51b4a8a8fcc0b84b28b9c655b52f75553862f7d6, compared to commit: 4f733adf44501d52eea35d03c9761b27e295af0f

🧾 Summary (10% most significant diffs)

Program Brillig opcodes (+/-) %
fold_complex_outputs_inliner_max +54 ❌ +11.95%
nested_array_in_slice_inliner_max +90 ❌ +8.40%

Full diff report 👇
Program Brillig opcodes (+/-) %
fold_complex_outputs_inliner_max 506 (+54) +11.95%
nested_array_in_slice_inliner_max 1,162 (+90) +8.40%
fold_complex_outputs_inliner_zero 681 (+36) +5.58%
fold_complex_outputs_inliner_min 754 (+36) +5.01%
nested_array_dynamic_inliner_max 2,693 (+44) +1.66%

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: 03a0b9a Previous: 4f733ad Ratio
test_report_AztecProtocol_aztec-packages_noir-projects_aztec-nr 4 s 3 s 1.33
test_report_AztecProtocol_aztec-packages_noir-projects_noir-protocol-circuits_crates_private-kernel-lib 2 s 1 s 2
test_report_AztecProtocol_aztec-packages_noir-projects_noir-protocol-circuits_crates_types 2 s 1 s 2

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

CC: @TomAFrench

Copy link
Collaborator

@asterite asterite left a comment

Choose a reason for hiding this comment

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

LGTM

@jfecher
Copy link
Contributor

jfecher commented Jul 29, 2025

@guipublic this seems related to #9341, can you confirm whether we still need that PR with this change?

Edit: Wrong PR, this one is not the one for lvalues

@jfecher jfecher added this pull request to the merge queue Jul 29, 2025
Merged via the queue into master with commit be626a1 Jul 29, 2025
101 checks passed
@jfecher jfecher deleted the mv/keep-inc-rc-on-nested-array-sets branch July 29, 2025 17:17
github-merge-queue bot pushed a commit to AztecProtocol/aztec-packages that referenced this pull request Aug 4, 2025
Automated pull of nightly from the
[noir](https://github.com/noir-lang/noir) programming language, a
dependency of Aztec.
BEGIN_COMMIT_OVERRIDE
fix: forbid self-referencing type aliases
(noir-lang/noir#9103)
chore: add a mem2reg test for when all references need to be invalidated
(noir-lang/noir#9377)
fix(ssa): Do not check ArrayGet/Set as unreachable for Brillig
(noir-lang/noir#9376)
chore: use SSA parser in all mem2reg tests
(noir-lang/noir#9372)
fix: trait where clause check fixes
(noir-lang/noir#9369)
fix: Correct doc comments for SSA passes
(noir-lang/noir#9371)
fix: prevent `SignedField::from(i128::MIN)` from crashing
(noir-lang/noir#9366)
fix: allow constants in the type-system to be negative
(noir-lang/noir#9360)
feat: show circuit output as a value of the program's return type
(noir-lang/noir#9364)
feat: add `FunctionDefinition::visibility`
(noir-lang/noir#9363)
chore(docs): Add example for `$crate` in docs
(noir-lang/noir#9361)
fix: Prevent accidental tuple sharing in comptime code
(noir-lang/noir#9313)
fix: perserve purities after SSA normalization
(noir-lang/noir#9355)
fix: modulo overflow in comptime
(noir-lang/noir#9348)
fix: handle short-syntax for trait constraints on trait generics
(noir-lang/noir#9167)
chore: enhance trait constraint comment
(noir-lang/noir#9358)
fix: replace implicitly added named generics with fresh type vars in
check_trait_impl_where_clause_matches_trait_where_clause
(noir-lang/noir#9352)
fix: push definition trait constraints after trait item constraint
(noir-lang/noir#9354)
chore(ci): Update status of noir_json_parser
(noir-lang/noir#9351)
fix(ssa): Keep reference count increments for array set values
(noir-lang/noir#9344)
chore: remove unused `compile_workspace`
(noir-lang/noir#9353)
chore: try printing byte arrays as strings in the SSA interpreter
(noir-lang/noir#9346)
feat(lsp): allow opening noir stdlib files
(noir-lang/noir#9339)
fix: do u128 operations with u128, not i128
(noir-lang/noir#9345)
chore(acir): ACIR parser error handling for blackbox inputs/outputs
(noir-lang/noir#9342)
fix: prevent invalid types in test/fuzz functions
(noir-lang/noir#9343)
chore(lsp): avoid redundant type checking
(noir-lang/noir#9337)
feat(acir): Parse ACIR memory and call opcodes
(noir-lang/noir#9331)
fix(ssa_gen): Add constraint on slice length before popping
(noir-lang/noir#9323)
chore: impl for u16 conversions
(noir-lang/noir#9314)
fix: substitute bindings in type before canonicalization
(noir-lang/noir#9328)
fix(ssa_interpreter): `push_back` and `pop_back` to slices with padding
(noir-lang/noir#9320)
fix: wildcard type should be allowed in lambda parameter types
(noir-lang/noir#9325)
chore: graceful handling of SIGPIPE
(noir-lang/noir#9075)
feat: return unsolvable opcode from `CircuitSimulator`
(noir-lang/noir#8943)
fix: allow nested fmtstr (noir-lang/noir#9309)
feat: Initial ACIR parser (arithmetic exprs and black box functions)
(noir-lang/noir#9316)
fix(mem2reg): Register aliases when the `IfElse` result in a reference
(noir-lang/noir#9305)
fix: Make Ssa-gen use existing reference when compiling `&mut
foo.bar.baz` (noir-lang/noir#9307)
fix: top-level item in dependency isn't always visible
(noir-lang/noir#9295)
fix(ssa-interpreter): Return error if slice length is 0 during popping
(noir-lang/noir#9308)
chore: Release Noir(1.0.0-beta.9)
(noir-lang/noir#9184)
chore(LSP): simplify code lens request handling
(noir-lang/noir#9279)
chore: add regression tests for #6383
(noir-lang/noir#9302)
fix: disallow `_` in signatures and struct members
(noir-lang/noir#9301)
fix: check associated types after validating where clause when looking
up trait impls, plus some unification fixes
(noir-lang/noir#9265)
chore: Add fmtstr to coercions list
(noir-lang/noir#9300)
chore: Add a helper function `fmtstr::as_quoted_str`
(noir-lang/noir#9293)
chore(docs): Copy Type Coercions docs into v1.0.0-beta.8 versioned docs
(noir-lang/noir#9298)
feat: only inject "out of bounds" checks in brillig
(noir-lang/noir#9200)
END_COMMIT_OVERRIDE

---------

Co-authored-by: AztecBot <tech@aztecprotocol.com>
Co-authored-by: Maxim Vezenov <mvezenov@gmail.com>
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.

Unexpected behaviour of array variables inside nested loops in Brillig

3 participants