Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ssa): don't deduplicate constraints in blocks that are not dominated #6627

Merged
merged 23 commits into from
Nov 28, 2024

Conversation

asterite
Copy link
Collaborator

@asterite asterite commented Nov 26, 2024

Description

Problem

Resolves #6588

Summary

The PR fixes two issues in constant_folding introduced by #6499 :

  1. The constraint_simplification_mapping didn't take into account the block in which the constraint was encountered to limit where the simplification can be used.
  2. Hoisting instructions into the common dominator should only happen when the instruction is free of side effects, because there can be other paths in which it's invalid.

Simplification

Here's an example where things went wrong:

fn main(x: Field, y: bool) {
    if y {
        assert_eq(x, 1);
    }
    print(x);
}

Calling the above with main(0, false) prints 1 on the console, because even though the body of the if is not hit, the constrain x, 1 ended up taking effect outside the if and "simplified" print(x) to print(1).

The PR fixes this by remembering that the constraint was encountered in the block corresponding to the then branch, and only uses the simplification in blocks which are dominated by it.

Hoisting

For this to kick in we needed two blocks have the same expression:

fn main(x: Field, y: bool, z: bool) {
    if y {
        assert_eq(x, 1); // Caches the result of the instruction
    } 
    if z {
        assert_eq(x, 1); // Finds the result in the cache, but it's from a sibling block, so it hoists it up in the common dominator
    }
}

Calling the above with main(0, false, false) causes the circuit to fail, because it is rewritten to something like this:

fn main(x: Field, y: bool, z: bool) {
    assert_eq(x, 1); // Instruction hoisted to the block before both `if`s
    if y {
        assert_eq(x, 1); // Original instruction still in place until the next deduplication
    } 
    if z {
        // Instruction result now reusable from cache
    }
    // Now we enforce the constraint even when both y and z are false
}

The PR fixes this by only hoisting instructions which don't have side effects (such as failing the circuit, or modifying an array).

Additional Context

Curiously the results cache does take into account the side effect variable a.k.a. predicate:

type InstructionResultCache = HashMap<Instruction, HashMap<Option<ValueId>, ResultCache>>;
                                                             ^
                                                             optional predicate

So it should be able to isolate the results based on whether we are before the if, in the then or in the else block. However it's use depends on whether Instruction::requires_acir_gen_predicate indicates that it should be used, and for Constrain this is not the case, so it always ends up being stored with None for key.

            let use_predicate =
                self.use_constraint_info && instruction.requires_acir_gen_predicate(dfg);
            let predicate = use_predicate.then_some(side_effects_enabled_var);

            self.cached_instruction_results
                .entry(instruction)
                .or_default()
                .entry(predicate)
                .or_default()
                .cache(block, instruction_results);

NB previously every block had its own cache, so maybe the intent behind this was completely different, but it would be good to have clarification on how flatten_cfg, enable_side_effect, and this, are supposed to work together.

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.

@TomAFrench
Copy link
Member

Nice! Good find.

@asterite asterite changed the title Add a test that fails fix: don't deduplicate constraints in blocks that are not dominated Nov 26, 2024
Copy link
Contributor

github-actions bot commented Nov 26, 2024

Changes to Brillig bytecode sizes

Generated at commit: b4480765b1f8b4e7c771b1a1e1ff6eaefcbd6622, compared to commit: dfc9ff7266d2b6694cae3da88418013664440daa

🧾 Summary (10% most significant diffs)

Program Brillig opcodes (+/-) %
fold_call_witness_condition +4 ❌ +3.64%
slice_dynamic_index +12 ❌ +0.48%

Full diff report 👇
Program Brillig opcodes (+/-) %
fold_call_witness_condition 114 (+4) +3.64%
slice_dynamic_index 2,532 (+12) +0.48%

Copy link
Contributor

github-actions bot commented Nov 26, 2024

Changes to number of Brillig opcodes executed

Generated at commit: b4480765b1f8b4e7c771b1a1e1ff6eaefcbd6622, compared to commit: dfc9ff7266d2b6694cae3da88418013664440daa

🧾 Summary (10% most significant diffs)

Program Brillig opcodes (+/-) %
fold_call_witness_condition +1 ❌ +1.45%
slice_regex -44 ✅ -1.28%

Full diff report 👇
Program Brillig opcodes (+/-) %
fold_call_witness_condition 70 (+1) +1.45%
uhashmap 146,340 (-3) -0.00%
hashmap 52,936 (-3) -0.01%
slice_dynamic_index 4,305 (-26) -0.60%
slice_regex 3,390 (-44) -1.28%

@aakoshh aakoshh changed the title fix: don't deduplicate constraints in blocks that are not dominated fix(ssa): don't deduplicate constraints in blocks that are not dominated Nov 27, 2024
@aakoshh aakoshh requested a review from a team November 28, 2024 10:00
@TomAFrench TomAFrench added this pull request to the merge queue Nov 28, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Nov 28, 2024
@TomAFrench TomAFrench added this pull request to the merge queue Nov 28, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Nov 28, 2024
@asterite asterite added this pull request to the merge queue Nov 28, 2024
@TomAFrench TomAFrench removed this pull request from the merge queue due to a manual request Nov 28, 2024
@TomAFrench TomAFrench added this pull request to the merge queue Nov 28, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Nov 28, 2024
@TomAFrench TomAFrench added this pull request to the merge queue Nov 28, 2024
@TomAFrench
Copy link
Member

@asterite @aakoshh Can you pull these fixes into AztecProtocol/aztec-packages#9972?

Merged via the queue into master with commit b024581 Nov 28, 2024
49 checks passed
@TomAFrench TomAFrench deleted the ab/constant_folding_bug branch November 28, 2024 14:34
@asterite
Copy link
Collaborator Author

I already pulled some early fixes into it. That problem gets solved but it fails with another one that gets fixed with #6585 . Unfortunately that PR introduces huge regressions so I don't know what's the path forward.

That said, we could try merging the changes here into AztecProtocol/aztec-packages#9972 anyway.

@asterite
Copy link
Collaborator Author

Done! I couldn't apply all the changes here because the hoisting logic doesn't exist yet in Aztec-Packages.

AztecBot added a commit to AztecProtocol/aztec-packages that referenced this pull request Nov 28, 2024
…ir#6635)

fix(ssa): don't deduplicate constraints in blocks that are not dominated (noir-lang/noir#6627)
chore: pin foundry version in CI (noir-lang/noir#6642)
feat(ssa): Deduplicate intrinsics with predicates (noir-lang/noir#6615)
chore: improve error message of `&T` (noir-lang/noir#6633)
fix: LSP code action wasn't triggering on beginning or end of identifier (noir-lang/noir#6616)
chore!: remove `ec` module from stdlib (noir-lang/noir#6612)
fix(LSP): use generic self type to narrow down methods to complete (noir-lang/noir#6617)
fix!: Disallow `#[export]` on associated methods (noir-lang/noir#6626)
chore: redo typo PR by donatik27 (noir-lang/noir#6575)
chore: redo typo PR by Dimitrolito (noir-lang/noir#6614)
feat: simplify `jmpif`s by reversing branches if condition is negated (noir-lang/noir#5891)
fix: Do not warn on unused functions marked with #[export] (noir-lang/noir#6625)
chore: Add panic for compiler error described in #6620 (noir-lang/noir#6621)
feat(perf): Track last loads per block in mem2reg and remove them if possible (noir-lang/noir#6088)
fix(ssa): Track all local allocations during flattening (noir-lang/noir#6619)
feat(comptime): Implement blackbox functions in comptime interpreter (noir-lang/noir#6551)
chore: derive PartialEq and Hash for FieldElement (noir-lang/noir#6610)
chore: ignore almost-empty directories in nargo_cli tests (noir-lang/noir#6611)
chore: remove temporary allocations from `num_bits` (noir-lang/noir#6600)
chore: Release Noir(1.0.0-beta.0) (noir-lang/noir#6562)
feat: Add `array_refcount` and `slice_refcount` builtins for debugging (noir-lang/noir#6584)
chore!: Require types of globals to be specified (noir-lang/noir#6592)
fix: don't report visibility errors when elaborating comptime value (noir-lang/noir#6498)
fix: preserve newlines between comments when formatting statements (noir-lang/noir#6601)
fix: parse a bit more SSA stuff (noir-lang/noir#6599)
chore!: remove eddsa from stdlib (noir-lang/noir#6591)
chore: Typo in oracles how to (noir-lang/noir#6598)
feat(ssa): Loop invariant code motion (noir-lang/noir#6563)
fix: remove `compiler_version` from new `Nargo.toml` (noir-lang/noir#6590)
feat: Avoid incrementing reference counts in some cases (noir-lang/noir#6568)
chore: fix typo in test name (noir-lang/noir#6589)
fix: consider prereleases to be compatible with pre-1.0.0 releases (noir-lang/noir#6580)
feat: try to inline brillig calls with all constant arguments  (noir-lang/noir#6548)
fix: correct type when simplifying `derive_pedersen_generators` (noir-lang/noir#6579)
feat: Sync from aztec-packages (noir-lang/noir#6576)
AztecBot added a commit to AztecProtocol/aztec-packages that referenced this pull request Nov 29, 2024
chore: refactor poseidon2 (noir-lang/noir#6655)
fix: correct types returned by constant EC operations simplified within SSA (noir-lang/noir#6652)
feat: Sync from aztec-packages (noir-lang/noir#6634)
fix: used signed division for signed modulo (noir-lang/noir#6635)
fix(ssa): don't deduplicate constraints in blocks that are not dominated (noir-lang/noir#6627)
chore: pin foundry version in CI (noir-lang/noir#6642)
feat(ssa): Deduplicate intrinsics with predicates (noir-lang/noir#6615)
chore: improve error message of `&T` (noir-lang/noir#6633)
fix: LSP code action wasn't triggering on beginning or end of identifier (noir-lang/noir#6616)
chore!: remove `ec` module from stdlib (noir-lang/noir#6612)
fix(LSP): use generic self type to narrow down methods to complete (noir-lang/noir#6617)
fix!: Disallow `#[export]` on associated methods (noir-lang/noir#6626)
chore: redo typo PR by donatik27 (noir-lang/noir#6575)
chore: redo typo PR by Dimitrolito (noir-lang/noir#6614)
feat: simplify `jmpif`s by reversing branches if condition is negated (noir-lang/noir#5891)
fix: Do not warn on unused functions marked with #[export] (noir-lang/noir#6625)
chore: Add panic for compiler error described in #6620 (noir-lang/noir#6621)
feat(perf): Track last loads per block in mem2reg and remove them if possible (noir-lang/noir#6088)
fix(ssa): Track all local allocations during flattening (noir-lang/noir#6619)
feat(comptime): Implement blackbox functions in comptime interpreter (noir-lang/noir#6551)
chore: derive PartialEq and Hash for FieldElement (noir-lang/noir#6610)
chore: ignore almost-empty directories in nargo_cli tests (noir-lang/noir#6611)
chore: remove temporary allocations from `num_bits` (noir-lang/noir#6600)
chore: Release Noir(1.0.0-beta.0) (noir-lang/noir#6562)
feat: Add `array_refcount` and `slice_refcount` builtins for debugging (noir-lang/noir#6584)
chore!: Require types of globals to be specified (noir-lang/noir#6592)
fix: don't report visibility errors when elaborating comptime value (noir-lang/noir#6498)
fix: preserve newlines between comments when formatting statements (noir-lang/noir#6601)
fix: parse a bit more SSA stuff (noir-lang/noir#6599)
chore!: remove eddsa from stdlib (noir-lang/noir#6591)
chore: Typo in oracles how to (noir-lang/noir#6598)
feat(ssa): Loop invariant code motion (noir-lang/noir#6563)
fix: remove `compiler_version` from new `Nargo.toml` (noir-lang/noir#6590)
feat: Avoid incrementing reference counts in some cases (noir-lang/noir#6568)
chore: fix typo in test name (noir-lang/noir#6589)
fix: consider prereleases to be compatible with pre-1.0.0 releases (noir-lang/noir#6580)
feat: try to inline brillig calls with all constant arguments  (noir-lang/noir#6548)
fix: correct type when simplifying `derive_pedersen_generators` (noir-lang/noir#6579)
feat: Sync from aztec-packages (noir-lang/noir#6576)
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.

Cross-block deduplication in constant folding has correctness bugs.
3 participants