fix: Monomorphize function values as pairs of (constrained, unconstrained)#9484
fix: Monomorphize function values as pairs of (constrained, unconstrained)#9484
(constrained, unconstrained)#9484Conversation
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark 'Opcode count'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.10.
| Benchmark suite | Current: 7b7e2bc | Previous: b51b616 | Ratio |
|---|---|---|---|
rollup-block-root-empty |
88193 opcodes |
68110 opcodes |
1.29 |
This comment was automatically generated by workflow using github-action-benchmark.
CC: @TomAFrench
There was a problem hiding this comment.
⚠️ 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: adf99d9 | Previous: c6835b5 | Ratio |
|---|---|---|---|
test_report_zkpassport_noir-ecdsa_ |
3 s |
1 s |
3 |
This comment was automatically generated by workflow using github-action-benchmark.
CC: @TomAFrench
Changes to number of Brillig opcodes executed
🧾 Summary (10% most significant diffs)
Full diff report 👇
|
Changes to Brillig bytecode sizes
🧾 Summary (10% most significant diffs)
Full diff report 👇
|
Changes to circuit sizes
🧾 Summary (10% most significant diffs)
Full diff report 👇
|
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark 'Execution Time'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.20.
| Benchmark suite | Current: a3d6a6b | Previous: 6870579 | Ratio |
|---|---|---|---|
sha512-100-bytes |
0.137 s |
0.101 s |
1.36 |
This comment was automatically generated by workflow using github-action-benchmark.
CC: @TomAFrench
|
Looks like arb_ast_roundtrip isn't true anymore. It asserts that the Ast is the same before/after monomorphization but after monomorphization we now turn all functions into tuples of (constrained, unconstrained). I added a hack to avoid showing the second element of this tuple for tuples and for tuple types of functions but am still seeing some differences. Notably some method calls look like function calls now for some reason (unsure where the code for displaying that is) and because my hack arbitrarily displays the first element of the tuple, unconstrained function types are showing as being changed to constrained. I'm also not sure if there is value to keeping this test though? It may also be more clear if my hack to not display these function tuples is only optional since without them we're not seeing the code that is actually sent to SSA and it is unclear why we see e.g. lambdas compiled as both constrained and unconstrained but one version looks unused. |
This test did uncover discrepancies between the fuzzer and the actual monomorphizer, so if possible I'd try to keep it. Arguably if the monomorphizer creates tuples for lambdas and the fuzzer does not, then that's a pretty significant difference. If that in itself isn't a problem, then instead of hacking the display, you could just add |
|
@aakoshh thanks - I think I was misunderstanding the purpose of the test then. I'll add the |
This reverts commit f2fc8ef.
|
I just had a look: I may have misunderstood something when I added function parameters to the fuzzer, but I thought there are no actual lambdas in the monomorphized AST, only first class functions, it's just that some of them have been created from Noir lambdas. So what I did was that as I create the signature of functions that I'll generate the body for, they have a chance to get a function parameter that matches one of the already generated first class function signatures. Then, when we need to generate a call, we can look for values in the global function list or the local parameter list. But there are no lambda "literals" if that makes sense. |
|
What the fuzzer relies heavily on though, is the ability to print the monomorphized AST as Noir with the Maybe the lambda signature to display (in types) could be based on the context of the function being printed?
Could that be related to this? |
|
I guess it can also be a problem that I choose a signature of specific first class function for a function parameter, say it's unconstrained, there won't be a constraint counterpart to be passed along it. Maybe the fuzzer only implements the spirit of |
@vezenovm do you know if this'd be an issue? E.g. if users may be expecting only certain names in the artifact and now there are a bunch of lambda entries. |
So yes this is what happens, however, I think this was always the case for entry point lambdas and can be made more user friendly in follow-up work (perhaps a start would be just adding unique index suffixes to matching names like in the profiler). In testing this I think I found something awry though. For example, I can write the following: fn main(x: u32) {
let functions: [unconstrained fn(u32) -> u32; 1] = [x_times_2];
let f = functions[x];
unsafe {
assert_eq(f(x), x * 2);
}
}
unconstrained fn x_times_2(x: u32) -> u32 {
x * 2
}This will give me the appropriate function name of This is expected as I can then do the following: fn main(x: u32) {
let functions = [|x: u32| x * 2];
let f = functions[x];
unsafe {
assert_eq(f(x), x * 2);
}
}This will give Even though the lambda is called in an unsafe block there is nothing that indicates this lambda should be Brillig as it was specified in an ACIR runtime and called from an ACIR runtime. Combined with the bug in #9484 (review) the above leads me to think that we are incorrectly marking runtimes. |
To add onto this statement, when compiling the above with nightly I get a single |
I think this is happening since functions are now compiled as a tuple of |
I was thinking that this is most likely a similar bug to the one that triggered #9484 (review). Yes, we are storing both versions in an array but it looks like we are triggering the one with the Brillig runtime when we should be triggering the one with ACIR runtime. If the two bugs have different root causes though I don't think we need to wait on the latter bug to get this PR in. |
So your program was the following?: fn main() {
foo(|| std::runtime::is_unconstrained());
}
// #7289 originates from a lambda being used in both acir & brillig
fn foo(is_brillig: fn() -> bool) {
// If this is true the `--force-brillig` flag was passed
let force_brillig = std::runtime::is_unconstrained();
println(force_brillig);
assert(!acir_function(is_brillig) | force_brillig);
// safety:
unsafe {
assert(brillig_function(is_brillig));
assert(brillig_function2(is_brillig));
};
}
fn acir_function(f: fn() -> bool) -> bool {
f()
}
unconstrained fn brillig_function(f: fn() -> bool) -> bool {
f()
}
// acir_function should become unconstrained when called in
// an unconstrained context - and this should apply to the lambda
// it takes as an argument as well.
unconstrained fn brillig_function2(f: fn() -> bool) -> bool {
acir_function(f)
}I get no error for this program |
Hmm yes. That is what I had. Let me reproduce once more. |
Ah you need |
The bug looks to be the same as the cause of #9097 which is resolved by #9365. You can look at the PR description of #9365 as to why this bug occurs. As this bug is already captured (with a potential solution) we can merge this PR. |
Automated pull of nightly from the [noir](https://github.com/noir-lang/noir) programming language, a dependency of Aztec. BEGIN_COMMIT_OVERRIDE fix: don't thread-bomb unnecessarily (noir-lang/noir#9643) chore(mem2reg): Only add to per function last_loads if load is not removed (noir-lang/noir#9647) chore(mem2reg): add a few regression tests (noir-lang/noir#9615) fix: Monomorphize function values as pairs of `(constrained, unconstrained)` (noir-lang/noir#9484) fix(mem2reg): Mark block parameters with unknown alias sets in presence of nested references (noir-lang/noir#9629) fix(ssa): Put some default in `Value::uninitialized` for references in the SSA interpreter (noir-lang/noir#9603) feat(ssa_fuzzer): ecdsa blackbox functions (noir-lang/noir#9584) fix(mem2reg): missing alias from block parameter to its argument (noir-lang/noir#9640) fix(acir_gen): A slice might be a nested Array, not a flattened DynamicArray (noir-lang/noir#9600) chore: add another mem2reg regression for #9613 (noir-lang/noir#9635) chore: document remove_if_else (in preparation for audit) (noir-lang/noir#9621) fix(mem2reg): Assume all function reference parameters have an unknown alias set with nested references (noir-lang/noir#9632) chore: add a regression test for #9613 (noir-lang/noir#9630) fix: Revert "feat(mem2reg): address last known value is independent of its… (noir-lang/noir#9628) fix(inlining): Do not inline globals and lower them during ACIR gen (noir-lang/noir#9626) chore(brillig): Include function name with `--count-array-copies` debug information (noir-lang/noir#9623) chore: use `assert_ssa_does_not_change` throughout all SSA tests (noir-lang/noir#9625) chore: only run remove_paired_rc in brillig functions (noir-lang/noir#9624) chore: some inlining refactors (noir-lang/noir#9622) chore(mem2reg): avoid redundant PostOrder computation (noir-lang/noir#9620) chore: bump external pinned commits (noir-lang/noir#9618) chore: document intrinsics (noir-lang/noir#9382) fix: Make inc/dec_rc impure (noir-lang/noir#9617) chore: greenlight `checked_to_unchecked` for audits (noir-lang/noir#9537) feat(mem2reg): address last known value is independent of its aliases (noir-lang/noir#9613) fix: Fix if-else alias in mem2reg (noir-lang/noir#9611) END_COMMIT_OVERRIDE
Automated pull of nightly from the [noir](https://github.com/noir-lang/noir) programming language, a dependency of Aztec. BEGIN_COMMIT_OVERRIDE fix: don't thread-bomb unnecessarily (noir-lang/noir#9643) chore(mem2reg): Only add to per function last_loads if load is not removed (noir-lang/noir#9647) chore(mem2reg): add a few regression tests (noir-lang/noir#9615) fix: Monomorphize function values as pairs of `(constrained, unconstrained)` (noir-lang/noir#9484) fix(mem2reg): Mark block parameters with unknown alias sets in presence of nested references (noir-lang/noir#9629) fix(ssa): Put some default in `Value::uninitialized` for references in the SSA interpreter (noir-lang/noir#9603) feat(ssa_fuzzer): ecdsa blackbox functions (noir-lang/noir#9584) fix(mem2reg): missing alias from block parameter to its argument (noir-lang/noir#9640) fix(acir_gen): A slice might be a nested Array, not a flattened DynamicArray (noir-lang/noir#9600) chore: add another mem2reg regression for #9613 (noir-lang/noir#9635) chore: document remove_if_else (in preparation for audit) (noir-lang/noir#9621) fix(mem2reg): Assume all function reference parameters have an unknown alias set with nested references (noir-lang/noir#9632) chore: add a regression test for #9613 (noir-lang/noir#9630) fix: Revert "feat(mem2reg): address last known value is independent of its… (noir-lang/noir#9628) fix(inlining): Do not inline globals and lower them during ACIR gen (noir-lang/noir#9626) chore(brillig): Include function name with `--count-array-copies` debug information (noir-lang/noir#9623) chore: use `assert_ssa_does_not_change` throughout all SSA tests (noir-lang/noir#9625) chore: only run remove_paired_rc in brillig functions (noir-lang/noir#9624) chore: some inlining refactors (noir-lang/noir#9622) chore(mem2reg): avoid redundant PostOrder computation (noir-lang/noir#9620) chore: bump external pinned commits (noir-lang/noir#9618) chore: document intrinsics (noir-lang/noir#9382) fix: Make inc/dec_rc impure (noir-lang/noir#9617) chore: greenlight `checked_to_unchecked` for audits (noir-lang/noir#9537) feat(mem2reg): address last known value is independent of its aliases (noir-lang/noir#9613) fix: Fix if-else alias in mem2reg (noir-lang/noir#9611) END_COMMIT_OVERRIDE
Automated pull of nightly from the [noir](https://github.com/noir-lang/noir) programming language, a dependency of Aztec. BEGIN_COMMIT_OVERRIDE fix: don't thread-bomb unnecessarily (noir-lang/noir#9643) chore(mem2reg): Only add to per function last_loads if load is not removed (noir-lang/noir#9647) chore(mem2reg): add a few regression tests (noir-lang/noir#9615) fix: Monomorphize function values as pairs of `(constrained, unconstrained)` (noir-lang/noir#9484) fix(mem2reg): Mark block parameters with unknown alias sets in presence of nested references (noir-lang/noir#9629) fix(ssa): Put some default in `Value::uninitialized` for references in the SSA interpreter (noir-lang/noir#9603) feat(ssa_fuzzer): ecdsa blackbox functions (noir-lang/noir#9584) fix(mem2reg): missing alias from block parameter to its argument (noir-lang/noir#9640) fix(acir_gen): A slice might be a nested Array, not a flattened DynamicArray (noir-lang/noir#9600) chore: add another mem2reg regression for #9613 (noir-lang/noir#9635) chore: document remove_if_else (in preparation for audit) (noir-lang/noir#9621) fix(mem2reg): Assume all function reference parameters have an unknown alias set with nested references (noir-lang/noir#9632) chore: add a regression test for #9613 (noir-lang/noir#9630) fix: Revert "feat(mem2reg): address last known value is independent of its… (noir-lang/noir#9628) fix(inlining): Do not inline globals and lower them during ACIR gen (noir-lang/noir#9626) chore(brillig): Include function name with `--count-array-copies` debug information (noir-lang/noir#9623) chore: use `assert_ssa_does_not_change` throughout all SSA tests (noir-lang/noir#9625) chore: only run remove_paired_rc in brillig functions (noir-lang/noir#9624) chore: some inlining refactors (noir-lang/noir#9622) chore(mem2reg): avoid redundant PostOrder computation (noir-lang/noir#9620) chore: bump external pinned commits (noir-lang/noir#9618) chore: document intrinsics (noir-lang/noir#9382) fix: Make inc/dec_rc impure (noir-lang/noir#9617) chore: greenlight `checked_to_unchecked` for audits (noir-lang/noir#9537) feat(mem2reg): address last known value is independent of its aliases (noir-lang/noir#9613) fix: Fix if-else alias in mem2reg (noir-lang/noir#9611) END_COMMIT_OVERRIDE
Automated pull of nightly from the [noir](https://github.com/noir-lang/noir) programming language, a dependency of Aztec. BEGIN_COMMIT_OVERRIDE fix: don't thread-bomb unnecessarily (noir-lang/noir#9643) chore(mem2reg): Only add to per function last_loads if load is not removed (noir-lang/noir#9647) chore(mem2reg): add a few regression tests (noir-lang/noir#9615) fix: Monomorphize function values as pairs of `(constrained, unconstrained)` (noir-lang/noir#9484) fix(mem2reg): Mark block parameters with unknown alias sets in presence of nested references (noir-lang/noir#9629) fix(ssa): Put some default in `Value::uninitialized` for references in the SSA interpreter (noir-lang/noir#9603) feat(ssa_fuzzer): ecdsa blackbox functions (noir-lang/noir#9584) fix(mem2reg): missing alias from block parameter to its argument (noir-lang/noir#9640) fix(acir_gen): A slice might be a nested Array, not a flattened DynamicArray (noir-lang/noir#9600) chore: add another mem2reg regression for #9613 (noir-lang/noir#9635) chore: document remove_if_else (in preparation for audit) (noir-lang/noir#9621) fix(mem2reg): Assume all function reference parameters have an unknown alias set with nested references (noir-lang/noir#9632) chore: add a regression test for #9613 (noir-lang/noir#9630) fix: Revert "feat(mem2reg): address last known value is independent of its… (noir-lang/noir#9628) fix(inlining): Do not inline globals and lower them during ACIR gen (noir-lang/noir#9626) chore(brillig): Include function name with `--count-array-copies` debug information (noir-lang/noir#9623) chore: use `assert_ssa_does_not_change` throughout all SSA tests (noir-lang/noir#9625) chore: only run remove_paired_rc in brillig functions (noir-lang/noir#9624) chore: some inlining refactors (noir-lang/noir#9622) chore(mem2reg): avoid redundant PostOrder computation (noir-lang/noir#9620) chore: bump external pinned commits (noir-lang/noir#9618) chore: document intrinsics (noir-lang/noir#9382) fix: Make inc/dec_rc impure (noir-lang/noir#9617) chore: greenlight `checked_to_unchecked` for audits (noir-lang/noir#9537) feat(mem2reg): address last known value is independent of its aliases (noir-lang/noir#9613) fix: Fix if-else alias in mem2reg (noir-lang/noir#9611) END_COMMIT_OVERRIDE
Description
Problem*
Resolves #7289
Summary*
Changes the monomorphizer to represent function values as a pair of a constrained version and unconstrained version of the same function. In the case of closures, the entire closure tuple is duplicated in each element (so you'd have
(constrained_closure_tuple, unconstrained_closure_tuple). Then, when the function is later called we index the tuple to extract the correct version based on the current runtime.When calling a function, if the function called is a name referring to a known function we avoid monomorphizing both versions and monomorphize only the necessary version instead, although this optimization is not necessary for correctness.
Additional Context
This is the monomorphizer-only variant of this change with no corresponding
?unconstrainedsyntax. If we're alright with the approach here we could merge this first and add?unconstrainedlater (if desired) to limit the functions we compile as both constrained & unconstrained to only the necessary ones.All lambdas are compiled as both constrained and unconstrained which can add a bunch of `"lambda", "lambda", "lambda",..." entries to the compiled artifact.
Documentation*
Check one:
PR Checklist*
cargo fmton default settings.