diff --git a/compiler/noirc_evaluator/src/acir/tests/brillig_call.rs b/compiler/noirc_evaluator/src/acir/tests/brillig_call.rs index a9d89570caf..4e12391f470 100644 --- a/compiler/noirc_evaluator/src/acir/tests/brillig_call.rs +++ b/compiler/noirc_evaluator/src/acir/tests/brillig_call.rs @@ -271,7 +271,7 @@ fn brillig_stdlib_calls_with_multiple_acir_calls() { BLACKBOX::RANGE [(w7, 32)] [] BRILLIG CALL func 0: inputs: [EXPR [ (1, w0) 0 ], EXPR [ (1, w1) 0 ]], outputs: [w8] BLACKBOX::RANGE [(w8, 32)] [] - CALL func 2: PREDICATE: EXPR [ 1 ] + CALL func 1: PREDICATE: EXPR [ 1 ] inputs: [w0, w1], outputs: [w9] BRILLIG CALL func 1: inputs: [EXPR [ (1, w2) 0 ]], outputs: [w10] EXPR [ (1, w2, w10) -1 ] @@ -281,7 +281,7 @@ fn brillig_stdlib_calls_with_multiple_acir_calls() { BLACKBOX::RANGE [(w13, 32)] [] EXPR [ (-1, w2, w11) (1, w1) (-1, w12) 0 ] EXPR [ (1, w11) -1 ] - + func 1 current witness: w5 private parameters: [w0, w1] @@ -295,7 +295,7 @@ fn brillig_stdlib_calls_with_multiple_acir_calls() { EXPR [ (1, w3, w5) 0 ] EXPR [ (1, w5) 0 ] EXPR [ (-1, w0) (1, w2) 0 ] - + unconstrained func 0 [Const { destination: Direct(2), bit_size: Integer(U32), value: 1 }, Const { destination: Direct(1), bit_size: Integer(U32), value: 32839 }, Const { destination: Direct(0), bit_size: Integer(U32), value: 3 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 2 }, Const { destination: Relative(4), bit_size: Integer(U32), value: 0 }, CalldataCopy { destination_address: Direct(32836), size_address: Relative(3), offset_address: Relative(4) }, Cast { destination: Direct(32836), source: Direct(32836), bit_size: Integer(U32) }, Cast { destination: Direct(32837), source: Direct(32837), bit_size: Integer(U32) }, Mov { destination: Relative(1), source: Direct(32836) }, Mov { destination: Relative(2), source: Direct(32837) }, Call { location: 16 }, Call { location: 17 }, Mov { destination: Direct(32838), source: Relative(1) }, Const { destination: Relative(2), bit_size: Integer(U32), value: 32838 }, Const { destination: Relative(3), bit_size: Integer(U32), value: 1 }, Stop { return_data: HeapVector { pointer: Relative(2), size: Relative(3) } }, Return, Call { location: 25 }, BinaryIntOp { destination: Relative(3), op: Equals, bit_size: U32, lhs: Relative(1), rhs: Relative(2) }, Const { destination: Relative(2), bit_size: Integer(U1), value: 0 }, BinaryIntOp { destination: Relative(4), op: Equals, bit_size: U1, lhs: Relative(3), rhs: Relative(2) }, JumpIf { condition: Relative(4), location: 24 }, Const { destination: Relative(5), bit_size: Integer(U32), value: 0 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Relative(5) } }, Return, Const { destination: Direct(32772), bit_size: Integer(U32), value: 30720 }, BinaryIntOp { destination: Direct(32771), op: LessThan, bit_size: U32, lhs: Direct(0), rhs: Direct(32772) }, JumpIf { condition: Direct(32771), location: 30 }, IndirectConst { destination_pointer: Direct(1), bit_size: Integer(U64), value: 15764276373176857197 }, Trap { revert_data: HeapVector { pointer: Direct(1), size: Direct(2) } }, Return] unconstrained func 1 diff --git a/compiler/noirc_evaluator/src/ssa/ir/function.rs b/compiler/noirc_evaluator/src/ssa/ir/function.rs index 6c96ce5225b..fad86fef6b8 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/function.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/function.rs @@ -26,10 +26,16 @@ impl RuntimeType { /// We return `false` for InlineType::Inline on default, which is true /// in all cases except for main. `main` should be supported with special /// handling in any places where this function determines logic. + /// + /// ## Important + /// If a Brillig function is not main it requires special handling to determine + /// whether it is an entry point. Brillig entry points can also be anywhere we start + /// Brillig execution from an ACIR runtime. This requires analyzing the call sites of the ACIR runtime. pub(crate) fn is_entry_point(&self) -> bool { match self { - RuntimeType::Acir(inline_type) => inline_type.is_entry_point(), - RuntimeType::Brillig(_) => true, + RuntimeType::Acir(inline_type) | RuntimeType::Brillig(inline_type) => { + inline_type.is_entry_point() + } } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/die/prune_dead_parameters.rs b/compiler/noirc_evaluator/src/ssa/opt/die/prune_dead_parameters.rs index 06ce68b8ffe..4d3ab99693c 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die/prune_dead_parameters.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die/prune_dead_parameters.rs @@ -178,9 +178,9 @@ impl Function { let cfg = ControlFlowGraph::with_function(self); let post_order = PostOrder::with_cfg(&cfg); - let is_acir_entry_point = self.runtime().is_acir() && self.runtime().is_entry_point(); + let is_entry_point = self.runtime().is_entry_point(); let is_main = self.id() == main_id; - let can_prune_entry_block = !(is_acir_entry_point || is_main); + let can_prune_entry_block = !(is_entry_point || is_main); let mut entry_block_keep_list = None; for &block in post_order.as_slice() { diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_unreachable_functions.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_unreachable_functions.rs index d6f74c249f7..db6006c3a5f 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_unreachable_functions.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_unreachable_functions.rs @@ -80,13 +80,8 @@ fn remove_unreachable_functions_post_check(ssa: &Ssa) { /// A sorted set of [`FunctionId`]s that are reachable from the entry points of the SSA. fn reachable_functions(ssa: &Ssa) -> HashSet { // Identify entry points - let entry_points = ssa.functions.iter().filter_map(|(&id, func)| { - // Not using `Ssa::is_entry_point` because it could leave Brillig functions that nobody calls in the SSA, - // because it considers every Brillig function as an entry point. - let is_entry_point = - id == ssa.main_id || func.runtime().is_acir() && func.runtime().is_entry_point(); - is_entry_point.then_some(id) - }); + let entry_points = + ssa.functions.iter().filter_map(|(&id, _)| ssa.is_entry_point(id).then_some(id)); // Build call graph dependencies using this passes definition of reachability. let dependencies = ssa.functions.iter().map(|(&id, func)| (id, used_functions(func))).collect(); diff --git a/tooling/ssa_executor/src/lib.rs b/tooling/ssa_executor/src/lib.rs index d96e9f25b35..a31e0379bb8 100644 --- a/tooling/ssa_executor/src/lib.rs +++ b/tooling/ssa_executor/src/lib.rs @@ -139,4 +139,39 @@ mod tests { _ => {} } } + + #[test] + fn execute_brillig_stdlib_call_with_multiple_acir_calls() { + let src = " + acir(inline) fn main f0 { + b0(v0: u32, v1: u32, v2: u32): + v5 = div v0, v1 + constrain v5 == v2 + v6 = call f1(v0, v1) -> u32 + v7 = call f1(v0, v1) -> u32 + v8 = call f2(v0, v1) -> u32 + v9 = div v1, v2 + constrain v9 == u32 1 + return + } + brillig(inline) fn foo f1 { + b0(v0: u32, v1: u32): + v2 = eq v0, v1 + constrain v2 == u1 0 + return v0 + } + acir(fold) fn foo f2 { + b0(v0: u32, v1: u32): + v2 = eq v0, v1 + constrain v2 == u1 0 + return v0 + } + "; + let mut witness_map = WitnessMap::new(); + witness_map.insert(Witness(0), FieldElement::from(9_u32)); + witness_map.insert(Witness(1), FieldElement::from(3_u32)); + witness_map.insert(Witness(2), FieldElement::from(3_u32)); + let result = execute_ssa(src.to_string(), witness_map, CompileOptions::default()); + assert!(result.is_ok()); + } }