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 1 commit
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
2 changes: 2 additions & 0 deletions crates/noirc_evaluator/src/ssa_refactor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#[allow(dead_code)]
mod basic_block;
#[allow(dead_code)]
mod cfg;
#[allow(dead_code)]
mod dfg;
#[allow(dead_code)]
mod ir;
8 changes: 7 additions & 1 deletion crates/noirc_evaluator/src/ssa_refactor/basic_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ pub(crate) struct BasicBlock {
terminator: TerminatorInstruction,
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
impl BasicBlock {
pub(crate) fn terminator(&self) -> &TerminatorInstruction {
&self.terminator
}
}

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// An identifier for a Basic Block.
pub(crate) struct BasicBlockId;

Expand Down
110 changes: 110 additions & 0 deletions crates/noirc_evaluator/src/ssa_refactor/cfg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use std::collections::{hash_set, HashMap, HashSet};

use super::{
basic_block::BasicBlockId,
ir::{function::Function, instruction_predicates},
};

/// A container for the successors and predecessors of some Block.
#[derive(Clone, Default)]
struct CfgNode {
/// Set of blocks that containing jumps that target this block.
/// The predecessor set has no meaningful order.
pub(crate) predecessors: HashSet<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>,
}

/// The Control Flow Graph maintains a mapping of blocks to their predecessors
/// and successors where predecessors are basic blocks and successors are
/// basic blocks.
pub(crate) struct ControlFlowGraph {
data: HashMap<BasicBlockId, CfgNode>,
}

impl ControlFlowGraph {
/// Allocate and compute the control flow graph for `func`.
pub(crate) fn with_function(func: &Function) -> Self {
let mut cfg = ControlFlowGraph { data: HashMap::new() };
cfg.compute(func);
cfg
}

fn compute(&mut self, func: &Function) {
for basic_block_id in func.basic_block_ids_iter() {
self.compute_block(func, basic_block_id);
}
}

fn compute_block(&mut self, func: &Function, basic_block_id: BasicBlockId) {
instruction_predicates::visit_block_succs(func, basic_block_id, |dest| {
self.add_edge(basic_block_id, dest);
});
}

fn invalidate_block_successors(&mut self, basic_block_id: BasicBlockId) {
let node = self
.data
.get_mut(&basic_block_id)
.expect("ICE: Attempted to invalidate cfg node successors for non-existent node.");
let old_successors = node.successors.clone();
node.successors.clear();
for successor_id in old_successors {
self.data
.get_mut(&successor_id)
.expect("ICE: Cfg node successor doesn't exist.")
.predecessors
.remove(&basic_block_id);
}
}

/// Recompute the control flow graph of `block`.
///
/// This is for use after modifying instructions within a specific block. It recomputes all edges
/// 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);
self.compute_block(func, basic_block_id);
}

fn add_edge(&mut self, from: BasicBlockId, to: BasicBlockId) {
let predecessor_node = self.data.entry(from).or_default();
assert!(
predecessor_node.successors.len() < 2,
"ICE: A cfg node cannot have more than two successors"
);
predecessor_node.successors.insert(to);
let successor_node = self.data.entry(to).or_default();
assert!(
successor_node.predecessors.len() < 2,
"ICE: A cfg node cannot have more than two predecessors"
);
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 {
jfecher marked this conversation as resolved.
Show resolved Hide resolved
self.data
.get(&basic_block_id)
.expect("ICE: Attempted to iterate predecessors of block not found within cfg.")
.predecessors
.iter()
}

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

/// An iterator over block predecessors. The iterator type is `BasicBlockId`.
pub(crate) type PredIter<'a> = hash_set::Iter<'a, BasicBlockId>;

/// An iterator over block successors. The iterator type is `BasicBlockId`.
pub(crate) type SuccIter<'a> = hash_set::Iter<'a, BasicBlockId>;
3 changes: 2 additions & 1 deletion crates/noirc_evaluator/src/ssa_refactor/ir.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub(crate) mod extfunc;
mod function;
pub(crate) mod function;
pub(crate) mod instruction;
pub(crate) mod instruction_predicates;
pub(crate) mod map;
pub(crate) mod types;
pub(crate) mod value;
33 changes: 32 additions & 1 deletion crates/noirc_evaluator/src/ssa_refactor/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use crate::ssa_refactor::basic_block::{BasicBlock, BasicBlockId};
use super::instruction::Instruction;

use noirc_errors::Location;
use std::collections::HashMap;
use std::{
collections::{hash_map, HashMap},
ops::Index,
};

/// A function holds a list of instructions.
/// These instructions are further grouped into
Expand All @@ -20,6 +23,34 @@ pub(crate) struct Function {
entry_block: BasicBlockId,
}

impl Function {
/// Get an iterator over the ids of the basic blocks within the function.
///
/// The ids are iterated in no meaningful order.
pub(crate) fn basic_block_ids_iter(&self) -> BasicBlockIdsIter {
BasicBlockIdsIter(self.basic_blocks.keys())
}
}

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

/// An iterator over a function's basic block ids. The iterator type is `BasicBlockId`.
pub(crate) struct BasicBlockIdsIter<'a>(hash_map::Keys<'a, BasicBlockId, BasicBlock>);

impl<'a> Iterator for BasicBlockIdsIter<'a> {
type Item = BasicBlockId;

fn next(&mut self) -> Option<BasicBlockId> {
self.0.next().map(|k| *k)
}
}
jfecher marked this conversation as resolved.
Show resolved Hide resolved

/// FunctionId is a reference for a function
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub(crate) struct FunctionId(pub(crate) u32);
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use super::{
super::basic_block::BasicBlockId, function::Function, instruction::TerminatorInstruction,
};

/// Visit all successors of a block with a given visitor closure. The closure
jfecher marked this conversation as resolved.
Show resolved Hide resolved
/// arguments are the branch instruction that is used to reach the successor,
/// and the id of the successor block itself.
pub(crate) fn visit_block_succs<F: FnMut(BasicBlockId)>(
func: &Function,
basic_block_id: BasicBlockId,
mut visit: F,
) {
let terminator = &func[basic_block_id].terminator();
jfecher marked this conversation as resolved.
Show resolved Hide resolved
match terminator {
TerminatorInstruction::Jmp { destination, .. } => visit(*destination),
TerminatorInstruction::JmpIf { then_destination, else_destination, .. } => {
visit(*then_destination);
visit(*else_destination);
}
}
}