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

chore(ssa): Add intial control flow graph #1200

Merged
merged 22 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
71efa70
Add Context structs and start ssa gen pass
jfecher Apr 20, 2023
7dc56ed
Fix block arguments
jfecher Apr 21, 2023
d63f5f5
Fix clippy lint
jfecher Apr 21, 2023
5b92199
Fix merge conflict
jfecher Apr 21, 2023
b211c71
chore(ssa): cfg
joss-aztec Apr 21, 2023
282fd18
Use the correct dfg
jfecher Apr 21, 2023
750e1e0
Rename contexts to highlight the inner contexts are shared rather tha…
jfecher Apr 21, 2023
580da67
Fix merge conflict
jfecher Apr 21, 2023
6de7854
Correctly handle function parameters
jfecher Apr 21, 2023
0bce52a
Rename Nested to Tree; add comment
jfecher Apr 21, 2023
b7f95d7
Merge branch 'master' of github.com:noir-lang/noir into joss/ssa_refa…
joss-aztec Apr 24, 2023
072ec60
chore(ssa refactor): fix up merge regressions
joss-aztec Apr 24, 2023
8aeba85
chore(ssa refactor): tidy up
joss-aztec Apr 24, 2023
50a575e
chore(ssa refactor): rm iterator type aliases
joss-aztec Apr 24, 2023
04d3105
Merge branch 'jf/ssa' of github.com:noir-lang/noir into joss/ssa_refa…
joss-aztec Apr 24, 2023
778aaaa
chore(ssa refactor): handle return inst
joss-aztec Apr 24, 2023
d41d82e
chore(ssa refactor): cfg tests
joss-aztec Apr 24, 2023
335dc87
Merge branch 'master' of github.com:noir-lang/noir into joss/ssa_refa…
joss-aztec Apr 24, 2023
14a98d6
chore(ssa refactor): add cfg test comments
joss-aztec Apr 25, 2023
69189bc
Merge branch 'master' of github.com:noir-lang/noir into joss/ssa_refa…
joss-aztec Apr 25, 2023
169c491
chore(ssa refactor): cfg - merge related fixes
joss-aztec Apr 25, 2023
111831c
chore(ssa refactor): fix cfg tests
joss-aztec Apr 25, 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 @@ -13,5 +13,8 @@ pub(crate) fn visit_block_succs<F: FnMut(BasicBlockId)>(basic_block: &BasicBlock
visit(*then_destination);
visit(*else_destination);
}
TerminatorInstruction::Return { .. } => {
// The last block of the control flow - no successors
}
}
}
144 changes: 133 additions & 11 deletions crates/noirc_evaluator/src/ssa_refactor/ir/cfg.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::{hash_set, HashMap, HashSet};
use std::collections::{HashMap, HashSet};

use super::{
basic_block::{BasicBlock, BasicBlockId},
Expand Down Expand Up @@ -34,7 +34,7 @@ impl ControlFlowGraph {
}

fn compute(&mut self, func: &Function) {
for (basic_block_id, basic_block) in func.basic_blocks_iter() {
for (basic_block_id, basic_block) in func.dfg.basic_blocks_iter() {
self.compute_block(basic_block_id, basic_block);
}
}
Expand Down Expand Up @@ -67,7 +67,7 @@ impl ControlFlowGraph {
/// from `basic_block_id` while leaving edges to `basic_block_id` intact.
pub(crate) fn recompute_block(&mut self, func: &Function, basic_block_id: BasicBlockId) {
self.invalidate_block_successors(basic_block_id);
let basic_block = &func[basic_block_id];
let basic_block = &func.dfg[basic_block_id];
self.compute_block(basic_block_id, basic_block);
}

Expand All @@ -86,27 +86,149 @@ impl ControlFlowGraph {
successor_node.predecessors.insert(from);
}

/// Get an iterator over the CFG predecessors to `block`.
pub(crate) fn pred_iter(&self, basic_block_id: BasicBlockId) -> PredIter {
/// Get an iterator over the CFG predecessors to `basic_block_id`.
pub(crate) fn pred_iter(
&self,
basic_block_id: BasicBlockId,
) -> impl ExactSizeIterator<Item = BasicBlockId> + '_ {
self.data
.get(&basic_block_id)
.expect("ICE: Attempted to iterate predecessors of block not found within cfg.")
.predecessors
.iter()
.copied()
}

/// Get an iterator over the CFG successors to `block`.
pub(crate) fn succ_iter(&self, basic_block_id: BasicBlockId) -> SuccIter {
/// Get an iterator over the CFG successors to `basic_block_id`.
pub(crate) fn succ_iter(
&self,
basic_block_id: BasicBlockId,
) -> impl ExactSizeIterator<Item = BasicBlockId> + '_ {
self.data
.get(&basic_block_id)
.expect("ICE: Attempted to iterate successors of block not found within cfg.")
.successors
.iter()
.copied()
}
}

/// An iterator over block predecessors. The iterator type is `BasicBlockId`.
pub(crate) type PredIter<'a> = hash_set::Iter<'a, BasicBlockId>;
#[cfg(test)]
mod tests {
use crate::ssa_refactor::ir::{instruction::TerminatorInstruction, types::Type};

/// An iterator over block successors. The iterator type is `BasicBlockId`.
pub(crate) type SuccIter<'a> = hash_set::Iter<'a, BasicBlockId>;
use super::{super::function::Function, ControlFlowGraph};

#[test]
fn empty() {
let mut func = Function::new();
let block_id = func.entry_block();
func.dfg[block_id].set_terminator(TerminatorInstruction::Return { return_values: vec![] });

ControlFlowGraph::with_function(&func);
}

#[test]
fn jumps() {
let mut func = Function::new();
let block0_id = func.entry_block();
let cond = func.dfg.add_block_parameter(block0_id, Type::unsigned(1));
let block1_id = func.dfg.new_block();
let block2_id = func.dfg.new_block();

func.dfg[block0_id].set_terminator(TerminatorInstruction::JmpIf {
joss-aztec marked this conversation as resolved.
Show resolved Hide resolved
condition: cond,
then_destination: block2_id,
else_destination: block1_id,
arguments: vec![],
});
func.dfg[block1_id].set_terminator(TerminatorInstruction::JmpIf {
condition: cond,
then_destination: block1_id,
else_destination: block2_id,
arguments: vec![],
});
func.dfg[block2_id].set_terminator(TerminatorInstruction::Return { return_values: vec![] });

let mut cfg = ControlFlowGraph::with_function(&func);

{
let block0_predecessors = cfg.pred_iter(block0_id).collect::<Vec<_>>();
let block1_predecessors = cfg.pred_iter(block1_id).collect::<Vec<_>>();
let block2_predecessors = cfg.pred_iter(block2_id).collect::<Vec<_>>();

let block0_successors = cfg.succ_iter(block0_id).collect::<Vec<_>>();
let block1_successors = cfg.succ_iter(block1_id).collect::<Vec<_>>();
let block2_successors = cfg.succ_iter(block2_id).collect::<Vec<_>>();

assert_eq!(block0_predecessors.len(), 0);
assert_eq!(block1_predecessors.len(), 2);
assert_eq!(block2_predecessors.len(), 2);

assert_eq!(block1_predecessors.contains(&block0_id), true);
assert_eq!(block1_predecessors.contains(&block1_id), true);
assert_eq!(block2_predecessors.contains(&block0_id), true);
assert_eq!(block2_predecessors.contains(&block1_id), true);

assert_eq!(block0_successors.len(), 2);
assert_eq!(block1_successors.len(), 2);
assert_eq!(block2_successors.len(), 0);

assert_eq!(block0_successors.contains(&block1_id), true);
assert_eq!(block0_successors.contains(&block2_id), true);
assert_eq!(block1_successors.contains(&block1_id), true);
assert_eq!(block1_successors.contains(&block2_id), true);
assert_eq!(block2_successors, []);
}

// Add a new return block replacing the return in block2
let ret_block_id = func.dfg.new_block();
joss-aztec marked this conversation as resolved.
Show resolved Hide resolved
func.dfg[ret_block_id]
.set_terminator(TerminatorInstruction::Return { return_values: vec![] });
func.dfg[block2_id].set_terminator(TerminatorInstruction::Jmp {
destination: ret_block_id,
arguments: vec![],
});

// Change some instructions and recompute block0, block2 and ret_block

func.dfg[block0_id].set_terminator(TerminatorInstruction::JmpIf {
condition: cond,
then_destination: block1_id,
else_destination: ret_block_id,
arguments: vec![],
});
cfg.recompute_block(&mut func, block0_id);
cfg.recompute_block(&mut func, block2_id);
cfg.recompute_block(&mut func, ret_block_id);

{
let block0_predecessors = cfg.pred_iter(block0_id).collect::<Vec<_>>();
let block1_predecessors = cfg.pred_iter(block1_id).collect::<Vec<_>>();
let block2_predecessors = cfg.pred_iter(block2_id).collect::<Vec<_>>();

let block0_successors = cfg.succ_iter(block0_id).collect::<Vec<_>>();
let block1_successors = cfg.succ_iter(block1_id).collect::<Vec<_>>();
let block2_successors = cfg.succ_iter(block2_id).collect::<Vec<_>>();

assert_eq!(block0_predecessors.len(), 0);
assert_eq!(block1_predecessors.len(), 2);
assert_eq!(block2_predecessors.len(), 1);

assert_eq!(block1_predecessors.contains(&block0_id), true);
assert_eq!(block1_predecessors.contains(&block1_id), true);
assert_eq!(block2_predecessors.contains(&block0_id), false);
assert_eq!(block2_predecessors.contains(&block1_id), true);

assert_eq!(block0_successors.len(), 2);
assert_eq!(block1_successors.len(), 2);
assert_eq!(block2_successors.len(), 1);

assert_eq!(block0_successors.contains(&block1_id), true);
assert_eq!(block0_successors.contains(&ret_block_id), true);
assert_eq!(block1_successors.contains(&block1_id), true);
assert_eq!(block1_successors.contains(&block2_id), true);
assert_eq!(block2_successors.contains(&ret_block_id), true);
}
}
}
27 changes: 27 additions & 0 deletions crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::ops::{Index, IndexMut};

use super::{
basic_block::{BasicBlock, BasicBlockId},
function::Signature,
Expand Down Expand Up @@ -86,6 +88,16 @@ impl DataFlowGraph {
})
}

/// Get an iterator over references to each basic block within the dfg, paired with the basic
/// block's id.
///
/// The pairs are order by id, which is not guaranteed to be meaningful.
pub(crate) fn basic_blocks_iter(
&self,
) -> impl ExactSizeIterator<Item = (BasicBlockId, &BasicBlock)> {
self.blocks.iter()
}

pub(crate) fn block_parameters(&self, block: BasicBlockId) -> &[ValueId] {
self.blocks[block].parameters()
}
Expand Down Expand Up @@ -183,6 +195,21 @@ impl DataFlowGraph {
}
}

impl Index<BasicBlockId> for DataFlowGraph {
type Output = BasicBlock;
/// Get a reference to a function's basic block for the given id.
fn index(&self, id: BasicBlockId) -> &BasicBlock {
&self.blocks[id]
}
}

impl IndexMut<BasicBlockId> for DataFlowGraph {
/// Get a mutable reference to a function's basic block for the given id.
fn index_mut(&mut self, id: BasicBlockId) -> &mut BasicBlock {
&mut self.blocks[id]
}
}

#[cfg(test)]
mod tests {
use super::DataFlowGraph;
Expand Down
24 changes: 1 addition & 23 deletions crates/noirc_evaluator/src/ssa_refactor/ir/function.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use std::ops::Index;

use super::basic_block::{BasicBlock, BasicBlockId};
use super::basic_block::BasicBlockId;
use super::dfg::DataFlowGraph;
use super::instruction::Instruction;
use super::map::{Id, SecondaryMap};
Expand Down Expand Up @@ -40,26 +38,6 @@ impl Function {
}
}

impl Function {
/// Get an iterator over references to each basic block within the function, paired with the
/// basic block's id.
///
/// The pairs are order by id, which is not guaranteed to be meaningful.
pub(crate) fn basic_blocks_iter(
&self,
) -> impl ExactSizeIterator<Item = (BasicBlockId, &BasicBlock)> {
self.basic_blocks.iter()
}
}

impl Index<BasicBlockId> for Function {
type Output = BasicBlock;
/// Get a function's basic block for the given id.
fn index(&self, id: BasicBlockId) -> &BasicBlock {
&self.basic_blocks[id]
}
}

/// FunctionId is a reference for a function
pub(crate) type FunctionId = Id<Function>;

Expand Down