Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 1 addition & 1 deletion .github/benchmark_projects.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ projects:
cannot_execute: true
num_runs: 5
timeout: 60
compilation-timeout: 1.5
compilation-timeout: 2
compilation-memory-limit: 400
rollup-block-root-single-tx:
repo: AztecProtocol/aztec-packages
Expand Down
2 changes: 1 addition & 1 deletion EXTERNAL_NOIR_LIBRARIES.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ libraries:
repo: AztecProtocol/aztec-packages
ref: *AZ_COMMIT
path: noir-projects/noir-protocol-circuits/crates/types
timeout: 60
timeout: 70
critical: false
protocol_circuits_rollup_lib:
repo: AztecProtocol/aztec-packages
Expand Down
45 changes: 41 additions & 4 deletions compiler/noirc_evaluator/src/ssa/ir/call_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub(crate) struct CallGraph {

impl CallGraph {
/// Construct a [CallGraph] from the [Ssa]
pub(crate) fn new_from_ssa(ssa: &Ssa) -> Self {
pub(crate) fn from_ssa(ssa: &Ssa) -> Self {
let function_deps = ssa
.functions
.iter()
Expand All @@ -45,11 +45,11 @@ impl CallGraph {
})
.collect();

Self::new_from_deps(function_deps)
Self::from_deps(function_deps)
}

/// Construct a [CallGraph] from an explicit dependency mapping of (caller -> callees)
pub(crate) fn new_from_deps(dependencies: HashMap<FunctionId, BTreeSet<FunctionId>>) -> Self {
pub(crate) fn from_deps(dependencies: HashMap<FunctionId, BTreeSet<FunctionId>>) -> Self {
let mut graph = DiGraph::new();
let mut ids_to_indices = HashMap::default();
let mut indices_to_ids = HashMap::default();
Expand All @@ -63,7 +63,6 @@ impl CallGraph {
// Create edges from caller -> called
for (function, dependencies) in dependencies {
let function_index = ids_to_indices[&function];

for dependency in dependencies {
let dependency_index = ids_to_indices[&dependency];
graph.add_edge(function_index, dependency_index, ());
Expand Down Expand Up @@ -110,6 +109,44 @@ impl CallGraph {
pub(crate) fn indices_to_ids(&self) -> &HashMap<PetGraphIndex, FunctionId> {
&self.indices_to_ids
}

pub(crate) fn build_acyclic_subgraph(
&self,
recursive_functions: &HashSet<FunctionId>,
) -> CallGraph {
let mut graph = DiGraph::new();
let mut ids_to_indices = HashMap::default();
let mut indices_to_ids = HashMap::default();

// Add all non-recursive nodes
for (&function, _) in self.ids_to_indices.iter() {
if recursive_functions.contains(&function) {
continue;
}
let index = graph.add_node(function);
ids_to_indices.insert(function, index);
indices_to_ids.insert(index, function);
}

// Create edges from caller -> called between non-recursive nodes
for (&func_id, &old_idx) in self.ids_to_indices.iter() {
if recursive_functions.contains(&func_id) {
continue;
}
let new_src = ids_to_indices[&func_id];
for neighbor in self.graph.neighbors(old_idx) {
let callee_id = self.indices_to_ids[&neighbor];
if recursive_functions.contains(&callee_id) {
continue;
}
if let Some(&new_dst) = ids_to_indices.get(&callee_id) {
graph.add_edge(new_src, new_dst, ());
}
}
}

Self { graph, ids_to_indices, indices_to_ids }
}
}

/// Utility function to find out the direct calls of a function.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::ssa::{
impl Ssa {
/// See the [`inline_functions_with_at_most_one_instruction`][self] module for more information.
pub(crate) fn inline_functions_with_at_most_one_instruction(mut self: Ssa) -> Ssa {
let call_graph = CallGraph::new_from_ssa(&self);
let call_graph = CallGraph::from_ssa(&self);
let recursive_functions = call_graph.get_recursive_functions();

let should_inline_call = |callee: &Function| {
Expand Down
105 changes: 6 additions & 99 deletions compiler/noirc_evaluator/src/ssa/opt/inlining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,10 @@ impl Ssa {
inline_infos: &InlineInfos,
inline_no_predicates_functions: bool,
) -> Ssa {
let inline_targets =
inline_infos.iter().filter_map(|(id, info)| info.is_inline_target().then_some(*id));
let inline_targets = inline_infos.iter().filter_map(|(id, info)| {
let dfg = &self.functions[id].dfg;
info.is_inline_target(dfg).then_some(*id)
});

let should_inline_call = |callee: &Function| -> bool {
match callee.runtime() {
Expand All @@ -91,6 +93,7 @@ impl Ssa {
let new_function = function.inlined(&self, &should_inline_call);
(entry_point, new_function)
});

self
}
}
Expand Down Expand Up @@ -727,14 +730,9 @@ impl<'function> PerFunctionContext<'function> {

#[cfg(test)]
mod test {
use std::cmp::max;

use crate::{
assert_ssa_snapshot,
ssa::{
Ssa,
opt::{assert_normalized_ssa_equals, inlining::inline_info::compute_bottom_up_order},
},
ssa::{Ssa, opt::assert_normalized_ssa_equals},
};

#[test]
Expand Down Expand Up @@ -971,95 +969,4 @@ mod test {
}
");
}

#[test]
fn bottom_up_order_and_weights() {
let src = "
brillig(inline) fn main f0 {
b0(v0: u32, v1: u1):
v3 = call f2(v0) -> u1
v4 = eq v3, v1
constrain v3 == v1
return
}
brillig(inline) fn is_even f1 {
b0(v0: u32):
v3 = eq v0, u32 0
jmpif v3 then: b2, else: b1
b1():
v5 = call f3(v0) -> u32
v7 = call f2(v5) -> u1
jmp b3(v7)
b2():
jmp b3(u1 1)
b3(v1: u1):
return v1
}
brillig(inline) fn is_odd f2 {
b0(v0: u32):
v3 = eq v0, u32 0
jmpif v3 then: b2, else: b1
b1():
v5 = call f3(v0) -> u32
v7 = call f1(v5) -> u1
jmp b3(v7)
b2():
jmp b3(u1 0)
b3(v1: u1):
return v1
}
brillig(inline) fn decrement f3 {
b0(v0: u32):
v2 = sub v0, u32 1
return v2
}
";
// main
// |
// V
// is_odd <-> is_even
// | |
// V V
// decrement

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

assert_eq!(order.len(), 4);
let (ids, ws): (Vec<_>, Vec<_>) = order.into_iter().map(|(id, w)| (id.to_u32(), w)).unzip();
let (ows, tws): (Vec<_>, Vec<_>) = ws.into_iter().unzip();

// Check order
assert_eq!(ids[0], 3, "decrement: first, it doesn't call anything");
assert_eq!(ids[1], 1, "is_even: called by is_odd; removing first avoids cutting the graph");
assert_eq!(ids[2], 2, "is_odd: called by is_odd and main");
assert_eq!(ids[3], 0, "main: last, it's the entry");

// Check own weights
assert_eq!(ows, [2, 7, 7, 4]);

// Check transitive weights
assert_eq!(tws[0], ows[0], "decrement");
assert_eq!(
tws[1],
ows[1] + // own
tws[0] + // pushed from decrement
(ows[2] + tws[0]), // pulled from is_odd at the time is_even is emitted
"is_even"
);
assert_eq!(
tws[2],
ows[2] + // own
tws[0] + // pushed from decrement
tws[1], // pushed from is_even
"is_odd"
);
assert_eq!(
tws[3],
ows[3] + // own
tws[2], // pushed from is_odd
"main"
);
assert!(tws[3] > max(tws[1], tws[2]), "ideally 'main' has the most weight");
}
}
Loading
Loading