Skip to content

Commit

Permalink
Merge branch 'mv/hoist-make-array-fromloops' into mv/pr-6782-w-6783
Browse files Browse the repository at this point in the history
  • Loading branch information
vezenovm committed Dec 12, 2024
2 parents 1b0dd41 + 1777215 commit 19e0cca
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 5 deletions.
114 changes: 114 additions & 0 deletions compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//! We also check that we are not hoisting instructions with side effects.
use acvm::{acir::AcirField, FieldElement};
use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet};
use iter_extended::vecmap;

use crate::ssa::{
ir::{
Expand Down Expand Up @@ -113,6 +114,28 @@ impl<'f> LoopInvariantContext<'f> {

if hoist_invariant {
self.inserter.push_instruction(instruction_id, pre_header);

// We track whether we may mutate MakeArray instructions before we deduplicate
// them but we still need to issue an extra inc_rc in case they're mutated afterward.
if matches!(
self.inserter.function.dfg[instruction_id],
Instruction::MakeArray { .. }
) {
let results =
self.inserter.function.dfg.instruction_results(instruction_id).to_vec();
let results = vecmap(results, |value| self.inserter.resolve(value));
assert_eq!(
results.len(),
1,
"ICE: We expect only a single result from an `Instruction::MakeArray`"
);
let inc_rc = Instruction::IncrementRc { value: results[0] };
let call_stack = self.inserter.function.dfg.get_call_stack(instruction_id);
self.inserter
.function
.dfg
.insert_instruction_and_results(inc_rc, *block, None, call_stack);
}
} else {
self.inserter.push_instruction(instruction_id, *block);
}
Expand Down Expand Up @@ -190,6 +213,7 @@ impl<'f> LoopInvariantContext<'f> {
});

let can_be_deduplicated = instruction.can_be_deduplicated(self.inserter.function, false)
|| matches!(instruction, Instruction::MakeArray { .. })
|| self.can_be_deduplicated_from_upper_bound(&instruction);

is_loop_invariant && can_be_deduplicated
Expand Down Expand Up @@ -559,4 +583,94 @@ mod test {
let ssa = ssa.loop_invariant_code_motion();
assert_normalized_ssa_equals(ssa, expected);
}

#[test]
fn insert_inc_rc_when_moving_make_array() {
// SSA for the following program:
//
// unconstrained fn main(x: u32, y: u32) {
// let mut a1 = [1, 2, 3, 4, 5];
// a1[x] = 64;
// for i in 0 .. 5 {
// let mut a2 = [1, 2, 3, 4, 5];
// a2[y + i] = 128;
// foo(a2);
// }
// foo(a1);
// }
//
// We want to make sure move a loop invariant make_array instruction,
// to account for whether that array has been marked as mutable.
// To do so, we increment the reference counter on the array we are moving.
// In the SSA below, we want to move `v42` out of the loop.
let src = "
brillig(inline) fn main f0 {
b0(v0: u32, v1: u32):
v8 = make_array [Field 1, Field 2, Field 3, Field 4, Field 5] : [Field; 5]
v9 = allocate -> &mut [Field; 5]
v11 = array_set v8, index v0, value Field 64
v13 = add v0, u32 1
store v11 at v9
jmp b1(u32 0)
b1(v2: u32):
v16 = lt v2, u32 5
jmpif v16 then: b3, else: b2
b2():
v17 = load v9 -> [Field; 5]
call f1(v17)
return
b3():
v19 = make_array [Field 1, Field 2, Field 3, Field 4, Field 5] : [Field; 5]
v20 = allocate -> &mut [Field; 5]
v21 = add v1, v2
v23 = array_set v19, index v21, value Field 128
call f1(v23)
v25 = add v2, u32 1
jmp b1(v25)
}
brillig(inline) fn foo f1 {
b0(v0: [Field; 5]):
return
}
";

let ssa = Ssa::from_str(src).unwrap();

// We expect the `make_array` at the top of `b3` to be replaced with an `inc_rc`
// of the newly hoisted `make_array` at the end of `b0`.
let expected = "
brillig(inline) fn main f0 {
b0(v0: u32, v1: u32):
v8 = make_array [Field 1, Field 2, Field 3, Field 4, Field 5] : [Field; 5]
v9 = allocate -> &mut [Field; 5]
v11 = array_set v8, index v0, value Field 64
v13 = add v0, u32 1
store v11 at v9
v14 = make_array [Field 1, Field 2, Field 3, Field 4, Field 5] : [Field; 5]
jmp b1(u32 0)
b1(v2: u32):
v17 = lt v2, u32 5
jmpif v17 then: b3, else: b2
b2():
v18 = load v9 -> [Field; 5]
call f1(v18)
return
b3():
inc_rc v14
v20 = allocate -> &mut [Field; 5]
v21 = add v1, v2
v23 = array_set v14, index v21, value Field 128
call f1(v23)
v25 = add v2, u32 1
jmp b1(v25)
}
brillig(inline) fn foo f1 {
b0(v0: [Field; 5]):
return
}
";

let ssa = ssa.loop_invariant_code_motion();
assert_normalized_ssa_equals(ssa, expected);
}
}
2 changes: 0 additions & 2 deletions test_programs/gates_report_brillig_execution.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ excluded_dirs=(
"double_verify_nested_proof"
"overlapping_dep_and_mod"
"comptime_println"
# Takes a very long time to execute as large loops do not get simplified.
"regression_4709"
# bit sizes for bigint operation doesn't match up.
"bigint"
# Expected to fail as test asserts on which runtime it is in.
Expand Down
4 changes: 1 addition & 3 deletions tooling/nargo_cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ fn main() {

/// Some tests are explicitly ignored in brillig due to them failing.
/// These should be fixed and removed from this list.
const IGNORED_BRILLIG_TESTS: [&str; 11] = [
// Takes a very long time to execute as large loops do not get simplified.
"regression_4709",
const IGNORED_BRILLIG_TESTS: [&str; 10] = [
// bit sizes for bigint operation doesn't match up.
"bigint",
// ICE due to looking for function which doesn't exist.
Expand Down

0 comments on commit 19e0cca

Please sign in to comment.