feat: simplify jmpifs by reversing branches if condition is negated#5891
Conversation
Changes to Brillig bytecode sizes
🧾 Summary (10% most significant diffs)
Full diff report 👇
|
|
I'm really unsure on why exactly this is causing test failures. The ordering of the branches in a jmpif shouldn't cause any changes to behaviour afaik. |
|
This branch changes the order of the jmpif as expected in the simplify_cfg pass however the issue comes up in flattening as we end up merging the values incorrectly. Master This branch |
If we do not see an simple or obvious way to have this optimization work w/ flattening we could make this optimization just work on Brillig for now. |
|
I'd like if we investigated why flattening isn't adapting to the new changes. Could be related to the bug I've been looking at. |
|
Agreed, we should at least understand the root cause of this first. |
* master: (60 commits) fix: suggest trait attributes in LSP (#5972) fix: Error when `quote` is used in runtime code (#5978) chore: document HashMap (#5984) fix: Restrict keccak256_injective test input to 8 bits (#5977) fix: Error when comptime functions are used in runtime code (#5976) chore: document BoundedVec (#5974) feat: add `Expr::as_let` (#5964) chore: remove 3 unused functions warnings in the stdlib (#5973) feat: let `nargo` and LSP work well in the stdlib (#5969) feat: show doc comments in LSP (#5968) feat: add a `panic` method to the stdlib (#5966) fix: LSP document symbol didn't work for primitive impls (#5970) fix(mem2reg): Handle aliases in function last store cleanup and additional alias unit test (#5967) fix: let `derive(Eq)` work for empty structs (#5965) feat: add `FunctionDefinition` methods `is_unconstrained` and `set_unconstrained` (#5962) feat: LSP autocompletion for attributes (#5963) feat: `Module::add_item` (#5947) feat: Add `StructDefinition::add_generic` (#5961) feat: Add `StructDefinition::name` (#5960) fix(mem2reg): Handle aliases better when setting a known value for a load (#5959) ...
|
I was able to narrow down one of the failing tests to the following: This fails the final assertion: // x == 5
fn main(x: Field) {
let mut z = false;
if x != 20 {
z = true;
}
assert(x != 20);
// fails here
assert(z);
}But this succeeds: // x == 5
fn main(x: Field) {
let mut z = false;
if x != 20 {
z = true;
} else {
}
assert(x != 20);
assert(z);
}So there appears to be a bug around single-block |
|
I was able to narrow the above case slightly to the following, which executes successfully on fn main(x: bool) {
let mut z = false;
if !x {
z = true;
}
assert(!x);
assert(z);
}
Dumping the SSA (skipping unused variables):
// After Simplifying:
acir(inline) fn main f0 {
// v0 = 0
b0(v0: u1):
v1 = allocate
store u1 0 at v1
// v1 = 0
jmpif v0 then: b2, else: b1
b2():
// (skipped)
store u1 0 at v1
jmp b3()
b3():
constrain v0 == u1 0
v6 = load v1
// v6 = 1
constrain v6 == u1 1
return
b1():
// branch taken
store u1 1 at v1
// v1 = 1
jmp b3()
}But flatting gives SSA that unconditionally fails (see final // After Flattening:
acir(inline) fn main f0 {
// v0 = 0
b0(v0: u1):
v1 = allocate
store u1 0 at v1
// v1 = 0
enable_side_effects v0
// no side effects
v4 = load v1
// v4 = 0
store u1 1 at v1
// not stored?
v6 = not v0
// v6 = 1
store v4 at v1
// not stored? either way, it's 0
enable_side_effects v6
store u1 0 at v1
// v1 = 0
enable_side_effects u1 1
store v0 at v1
// v1 = 0
constrain v0 == u1 0
v9 = load v1
// v9 = 0
constrain v9 == u1 1
return
} |
Changes to number of Brillig opcodes executed
🧾 Summary (10% most significant diffs)
Full diff report 👇
|
|
I've done some investigating on this and the error is due to the fact that we've moved the We then end up processing the |
|
The two options for solving this then are:
Considering the lack of any change in circuit sizes, I'm inclined to go for the latter as it avoids making changes across the codebase (and relying on someone not undoing this change and accidentally causing a breakage). |
On second thoughts, this is due to the fact that at least one program has failed in each commit of this PR so we haven't measured this. Another solution would be to query the CFG to make sure that we don't merge in the |
…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)
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)
Description
Problem*
Resolves
Summary*
Currently if an
if-elseblock has a negated condition we generate an extra instruction to calculate the condition rather than just swapping the "if" and "else" branches. I've added a check for this in thesimplify_cfgpass.Additional Context
Documentation*
Check one:
PR Checklist*
cargo fmton default settings.