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

feat(ssa): Switch mem2reg pass to be per function rather than per block #2243

Merged
merged 31 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
504aad0
cleanup load instruction search w/ predecessors methods
vezenovm Aug 9, 2023
d129a0a
update regression_2218 test
vezenovm Aug 9, 2023
2106b6c
merge conflicts w/ master
vezenovm Aug 9, 2023
17d66aa
cargo clippy
vezenovm Aug 10, 2023
e32e665
clean up recursive load fetch method
vezenovm Aug 10, 2023
ada6129
add test to mem2reg pass
vezenovm Aug 10, 2023
bedfa2a
cleaner search methods
vezenovm Aug 10, 2023
3d422e1
switch from recursive to while-loop based search for loads
vezenovm Aug 10, 2023
c12c5bb
add comments to mem2reg pass
vezenovm Aug 10, 2023
9988d91
fmt
vezenovm Aug 10, 2023
7cc6afe
remove dbg
vezenovm Aug 10, 2023
fc7327a
move dom tree per func
vezenovm Aug 10, 2023
0bb6401
cargo fmt
vezenovm Aug 10, 2023
5baf9e8
make dom tree part of the func context
vezenovm Aug 10, 2023
06256c6
remove old ocmment
vezenovm Aug 10, 2023
c8f9317
remove loops from mem2reg function context
vezenovm Aug 11, 2023
a824ed3
remove import
vezenovm Aug 11, 2023
e345f8c
Update crates/noirc_evaluator/src/ssa/opt/mem2reg.rs
jfecher Aug 11, 2023
fb53610
Update crates/noirc_evaluator/src/ssa/opt/mem2reg.rs
jfecher Aug 11, 2023
e07c999
m em2reg debug work
vezenovm Aug 12, 2023
265b883
working func mem2reg for various reference alias cases
vezenovm Aug 15, 2023
3906ca2
array dynamic back to original
vezenovm Aug 15, 2023
7ed502d
use reachable blocks
vezenovm Aug 15, 2023
bac1e30
merge conflicts w/ master
vezenovm Aug 15, 2023
d697231
merge conflicts from master
vezenovm Aug 15, 2023
71c5693
restore sha2_blocks test
vezenovm Aug 15, 2023
c612b45
remove unnecessary clone and unused methods
vezenovm Aug 15, 2023
ef10232
bring back loops check inside fetch methods
vezenovm Aug 15, 2023
8d535eb
Update crates/nargo_cli/tests/execution_success/regression/src/main.nr
vezenovm Aug 15, 2023
f989fe9
guarantee that x == 3 in references regressio nfor 2218
vezenovm Aug 15, 2023
12ee7ad
remove leftover comments
vezenovm Aug 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn dyn_array(mut x: [u32; 5], y: Field, z: Field) {
assert(x[y] == 111);
assert(x[z] == 101);
x[z] = 0;
assert(x[y] == 111);
assert(x[y] == 111);
assert(x[1] == 0);
if y as u32 < 10 {
x[y] = x[y] - 2;
Expand Down
119 changes: 119 additions & 0 deletions crates/nargo_cli/tests/execution_success/references/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ fn main(mut x: Field) {
regression_2054();
regression_2030();
regression_2255();

// x == 3 at this point
regression_2218_if_inner_if(x, 10);
regression_2218_if_inner_else(20, x);
regression_2218_else(x, 3);
regression_2218_loop(x, 10);
jfecher marked this conversation as resolved.
Show resolved Hide resolved
}

fn add1(x: &mut Field) {
Expand Down Expand Up @@ -111,3 +117,116 @@ fn regression_2255() {
fn regression_2255_helper(mut x: &mut Field) {
*x = 1;
}

fn regression_2218(x: Field, y: Field) -> Field {
let q = &mut &mut 0;
let q1 = *q;
let q2 = *q;

if x != y {
*q1 = 1;
// Make sure that we correct load reference aliases through multiple blocks
if x != 20 {
*q1 = 10;
*q2 = 2; // now we'd expect q1 == q2 == 2

assert(*q1 == 2);
} else {
*q2 = 15;
assert(*q1 == 15);
}
} else {
*q2 = 20;
assert(*q1 == 20);
}
// Have to assign value to return it
let value = *q1;
value
}

fn regression_2218_if_inner_if(x: Field, y: Field) {
let value = regression_2218(x, y);
assert(value == 2);
}

fn regression_2218_if_inner_else(x: Field, y: Field) {
let value = regression_2218(x, y);
assert(value == 15);
}

fn regression_2218_else(x: Field, y: Field) {
let value = regression_2218(x, y);
assert(value == 20);
}

fn regression_2218_loop(x: Field, y: Field) {
let q = &mut &mut 0;
let q1 = *q;
let q2 = *q;

for _ in 0..1 {
if x != y {
*q1 = 10;
*q2 = 2; // now we'd expect q1 == q2 == 2

assert(*q1 == 2);
} else {
*q2 = 20;
assert(*q1 == 20);
}
}
assert(*q1 == 2);

for _ in 0..1 {
for _ in 0..5 {
if x != y {
*q1 = 1;
// Make sure that we correct load reference aliases through multiple blocks
if x != 20 {
*q1 = 10;
*q2 = 2; // now we'd expect q1 == q2 == 2

assert(*q1 == 2);
}
} else {
*q2 = 20;
assert(*q1 == 20);
}
}
if x != y {
*q1 = 1;
for _ in 0..5 {
// Make sure that we correct load reference aliases through multiple blocks
if x != 20 {
*q1 = 10;
*q2 = 2; // now we'd expect q1 == q2 == 2

assert(*q1 == 2);
}
}
} else {
*q2 = 20;
assert(*q1 == 20);
}
}
assert(*q1 == 2);

if x != y {
for _ in 0..5 {
if x != y {
*q1 = 1;
// Make sure that we correct load reference aliases through multiple blocks
if x != 20 {
*q1 = 10;
*q2 = 2; // now we'd expect q1 == q2 == 2

assert(*q1 == 2);
}
}
}
} else {
*q2 = 20;
assert(*q1 == 20);
}
assert(*q1 == 2);
}
8 changes: 8 additions & 0 deletions crates/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,21 @@ pub(crate) fn optimize_into_acir(
ssa = ssa
.inline_functions()
.print(print_ssa_passes, "After Inlining:")
// Run mem2reg with the CFG separated into blocks
.mem2reg()
.print(print_ssa_passes, "After Mem2Reg:")
.evaluate_assert_constant()?
.unroll_loops()?
.print(print_ssa_passes, "After Unrolling:")
.simplify_cfg()
.print(print_ssa_passes, "After Simplifying:")
// Run mem2reg before flattening to handle any promotion
// of values that can be accessed after loop unrolling
.mem2reg()
.print(print_ssa_passes, "After Mem2Reg:")
.flatten_cfg()
.print(print_ssa_passes, "After Flattening:")
// Run mem2reg once more with the flattened CFG to catch any remaining loads/stores
.mem2reg()
.print(print_ssa_passes, "After Mem2Reg:")
.fold_constants()
Expand Down
9 changes: 5 additions & 4 deletions crates/noirc_evaluator/src/ssa/ir/cfg.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::{HashMap, HashSet};
use std::collections::{BTreeSet, HashMap};

use super::{
basic_block::{BasicBlock, BasicBlockId},
Expand All @@ -10,13 +10,14 @@ use super::{
struct CfgNode {
/// Set of blocks that containing jumps that target this block.
/// The predecessor set has no meaningful order.
pub(crate) predecessors: HashSet<BasicBlockId>,
pub(crate) predecessors: BTreeSet<BasicBlockId>,

/// Set of blocks that are the targets of jumps in this block.
/// The successors set has no meaningful order.
pub(crate) successors: HashSet<BasicBlockId>,
pub(crate) successors: BTreeSet<BasicBlockId>,
}

#[derive(Clone)]
/// The Control Flow Graph maintains a mapping of blocks to their predecessors
/// and successors where predecessors are basic blocks and successors are
/// basic blocks.
Expand All @@ -31,7 +32,7 @@ impl ControlFlowGraph {
// therefore we must ensure that a node exists for the entry block, regardless of whether
// it later comes to describe any edges after calling compute.
let entry_block = func.entry_block();
let empty_node = CfgNode { predecessors: HashSet::new(), successors: HashSet::new() };
let empty_node = CfgNode { predecessors: BTreeSet::new(), successors: BTreeSet::new() };
let data = HashMap::from([(entry_block, empty_node)]);

let mut cfg = ControlFlowGraph { data };
Expand Down
6 changes: 3 additions & 3 deletions crates/noirc_evaluator/src/ssa/ir/function.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashSet;
use std::collections::BTreeSet;

use iter_extended::vecmap;

Expand Down Expand Up @@ -107,8 +107,8 @@ impl Function {
/// Note that self.dfg.basic_blocks_iter() iterates over all blocks,
/// whether reachable or not. This function should be used if you
/// want to iterate only reachable blocks.
pub(crate) fn reachable_blocks(&self) -> HashSet<BasicBlockId> {
let mut blocks = HashSet::new();
pub(crate) fn reachable_blocks(&self) -> BTreeSet<BasicBlockId> {
let mut blocks = BTreeSet::new();
jfecher marked this conversation as resolved.
Show resolved Hide resolved
let mut stack = vec![self.entry_block];

while let Some(block) = stack.pop() {
Expand Down
Loading