Skip to content

Commit

Permalink
feat: Separate runtimes of SSA functions before inlining (#5121)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

Resolves #5043

<details>
<summary>Opcode count of Aztec public functions before => after this
change in the inlining strategy</summary>

```
_approve_bridge_and_exit_input_asset_to_L1 on contract Uniswap with size 3788 => 3166
_assert_token_is_same on contract TokenBridge with size 916 => 640
_assert_token_is_same on contract Uniswap with size 1845 => 1546
_borrow on contract Lending with size 10988 => 10152
_call_mint_on_token on contract TokenBridge with size 1427 => 1125
_check_deadline on contract Crowdfunding with size 1015 => 752
_deposit on contract Lending with size 1699 => 1220
_increase_public_balance on contract GasToken with size 1325 => 1103
_increase_public_balance on contract Token with size 1577 => 1201
_increase_public_balance on contract TokenBlacklist with size 1577 => 1201
_reduce_total_supply on contract Token with size 945 => 626
_reduce_total_supply on contract TokenBlacklist with size 945 => 626
_repay on contract Lending with size 6775 => 6284
_withdraw on contract Lending with size 10715 => 9850
add_args_return on contract AvmNestedCallsTest with size 15 => 15
add_args_return on contract AvmTest with size 15 => 15
add_storage_map on contract AvmTest with size 797 => 664
add_to_tally_public on contract EasyPrivateVoting with size 1496 => 1112
add_u128 on contract AvmTest with size 187 => 187
admin on contract Token with size 495 => 348
approve_public_authwit on contract EcdsaAccount with size 977 => 718
approve_public_authwit on contract SchnorrAccount with size 743 => 621
approve_public_authwit on contract SchnorrHardcodedAccount with size 742 => 620
approve_public_authwit on contract SchnorrSingleKeyAccount with size 742 => 620
assert_block_number on contract AppSubscription with size 920 => 669
assert_minter_and_mint on contract Token with size 1434 => 1035
assert_not_expired on contract AppSubscription with size 910 => 658
assert_nullifier_exists on contract AvmTest with size 242 => 156
assert_public_global_vars on contract Test with size 1054 => 679
assert_same on contract AvmNestedCallsTest with size 138 => 138
assert_unsiloed_nullifier_acvm on contract AvmAcvmInteropTest with size 102 => 16
assertion_failure on contract AvmTest with size 166 => 166
avm_to_acvm_call on contract AvmAcvmInteropTest with size 287 => 169
balance_of_public on contract GasToken with size 651 => 612
balance_of_public on contract Token with size 880 => 710
balance_of_public on contract TokenBlacklist with size 880 => 710
borrow_public on contract Lending with size 1193 => 939
broadcast on contract Benchmarking with size 419 => 337
burn_public on contract Token with size 2426 => 1962
burn_public on contract TokenBlacklist with size 3148 => 2468
call_acvm_from_avm on contract AvmAcvmInteropTest with size 574 => 503
call_avm_from_acvm on contract AvmAcvmInteropTest with size 578 => 507
check_balance on contract GasToken with size 856 => 768
check_selector on contract AvmTest with size 463 => 463
claim_public on contract GasToken with size 3963 => 3416
claim_public on contract TokenBridge with size 4176 => 3457
constant_field_acvm on contract AvmAcvmInteropTest with size 13 => 13
constant_field_avm on contract AvmAcvmInteropTest with size 13 => 13
constructor on contract AppSubscription with size 3376 => 1983
constructor on contract Auth with size 1414 => 994
constructor on contract AvmInitializerTest with size 1290 => 908
constructor on contract Claim with size 1700 => 1123
constructor on contract EasyPrivateVoting with size 1454 => 941
constructor on contract FPC with size 1700 => 1123
constructor on contract InclusionProofs with size 1021 => 704
constructor on contract Token with size 2737 => 1961
constructor on contract TokenBlacklist with size 1925 => 1285
constructor on contract TokenBridge with size 1400 => 924
constructor on contract Uniswap with size 1326 => 909
consume_message_from_arbitrary_sender_public on contract Test with size 2203 => 1893
consume_mint_public_message on contract Test with size 2910 => 2600
create_different_nullifier_in_nested_call on contract AvmNestedCallsTest with size 743 => 639
create_l2_to_l1_message_arbitrary_recipient_public on contract Test with size 56 => 12
create_l2_to_l1_message_public on contract Test with size 73 => 29
create_same_nullifier_in_nested_call on contract AvmNestedCallsTest with size 741 => 637
debug_logging on contract AvmTest with size 725 => 402
deposit_public on contract Lending with size 2286 => 1846
dummy_public_call on contract Test with size 354 => 284
emit_nullifier_and_check on contract AvmTest with size 436 => 314
emit_nullifier_public on contract Test with size 47 => 11
emit_unencrypted on contract Test with size 54 => 16
emit_unencrypted_log on contract AvmTest with size 757 => 685
end_vote on contract EasyPrivateVoting with size 514 => 279
exit_to_l1_public on contract TokenBridge with size 2154 => 1773
get_address on contract AvmTest with size 45 => 13
get_args_hash on contract AvmTest with size 18 => 18
get_asset on contract Lending with size 888 => 718
get_assets on contract Lending with size 587 => 411
get_authorized on contract Auth with size 597 => 443
get_authorized_delay on contract Auth with size 702 => 517
get_block_number on contract AvmTest with size 45 => 13
get_chain_id on contract AvmTest with size 45 => 13
get_fee_per_da_gas on contract AvmTest with size 45 => 13
get_fee_per_l2_gas on contract AvmTest with size 45 => 13
get_portal_address_public on contract TokenBridge with size 297 => 145
get_position on contract Lending with size 3908 => 3623
get_price on contract PriceFeed with size 617 => 579
get_public_value on contract StatefulTest with size 636 => 598
get_roles on contract TokenBlacklist with size 989 => 749
get_scheduled_authorized on contract Auth with size 595 => 443
get_sender on contract AvmTest with size 45 => 13
get_shared_immutable_constrained_public on contract DocsExample with size 438 => 406
get_shared_immutable_constrained_public_indirect on contract DocsExample with size 701 => 607
get_shared_immutable_constrained_public_multiple on contract DocsExample with size 136 => 98
get_storage_address on contract AvmTest with size 45 => 13
get_timestamp on contract AvmTest with size 45 => 13
get_token on contract TokenBridge with size 513 => 366
get_transaction_fee on contract AvmTest with size 45 => 13
get_version on contract AvmTest with size 45 => 13
increment_balance on contract Benchmarking with size 1472 => 1264
increment_public_value on contract StatefulTest with size 695 => 449
increment_public_value_no_init_check on contract StatefulTest with size 447 => 351
init on contract Crowdfunding with size 2100 => 1341
init on contract Lending with size 958 => 598
initialize_public_immutable on contract DocsExample with size 433 => 252
initialize_shared_immutable on contract DocsExample with size 433 => 252
is_minter on contract Token with size 838 => 668
is_time_equal on contract Test with size 56 => 18
keccak_hash on contract AvmTest with size 49 => 49
l1_to_l2_msg_exists on contract AvmTest with size 65 => 17
mint_private on contract Token with size 1247 => 790
mint_private on contract TokenBlacklist with size 1495 => 906
mint_public on contract GasToken with size 946 => 813
mint_public on contract Token with size 2048 => 1520
mint_public on contract TokenBlacklist with size 2821 => 2137
modulo2 on contract AvmTest with size 16 => 16
nested_call_to_add on contract AvmNestedCallsTest with size 862 => 756
nested_call_to_add_with_gas on contract AvmNestedCallsTest with size 929 => 799
nested_static_call_to_add on contract AvmNestedCallsTest with size 890 => 784
nested_static_call_to_set_storage on contract AvmNestedCallsTest with size 759 => 665
new_note_hash on contract AvmTest with size 47 => 11
new_nullifier on contract AvmAcvmInteropTest with size 47 => 11
new_nullifier on contract AvmNestedCallsTest with size 47 => 11
new_nullifier on contract AvmTest with size 47 => 11
new_nullifier_acvm on contract AvmAcvmInteropTest with size 47 => 11
note_hash_exists on contract AvmTest with size 65 => 17
nullifier_collision on contract AvmTest with size 82 => 12
nullifier_exists on contract AvmTest with size 103 => 17
on_card_played on contract CardGame with size 2386 => 2151
on_cards_claimed on contract CardGame with size 2176 => 1927
on_game_joined on contract CardGame with size 1972 => 1733
pay_refund on contract FPC with size 2052 => 1685
pay_refund_with_shielded_rebate on contract FPC with size 2138 => 1771
pedersen_hash on contract AvmTest with size 19 => 19
pedersen_hash_with_index on contract AvmTest with size 19 => 19
poseidon2_hash on contract AvmTest with size 1370 => 1370
prepare_fee on contract FPC with size 1643 => 1280
pub_call_public_fn on contract ImportTest with size 710 => 636
pub_entry_point on contract Parent with size 263 => 189
pub_entry_point_twice on contract Parent with size 497 => 339
pub_get_value on contract Child with size 174 => 22
pub_get_value on contract StaticChild with size 409 => 281
pub_illegal_inc_value on contract StaticChild with size 448 => 336
pub_inc_value on contract Child with size 175 => 45
pub_inc_value on contract StaticChild with size 175 => 45
pub_inc_value_internal on contract Child with size 552 => 340
pub_set_value on contract Child with size 108 => 30
pub_set_value on contract StaticChild with size 108 => 30
public_call on contract StaticParent with size 263 => 189
public_constructor on contract StatefulTest with size 1808 => 1399
public_delegate_set_value on contract Delegator with size 724 => 724
public_get_decimals on contract Token with size 557 => 411
public_get_name on contract Token with size 538 => 392
public_get_symbol on contract Token with size 546 => 400
public_get_value_from_child on contract StaticParent with size 759 => 685
public_nested_static_call on contract Parent with size 674 => 561
public_nested_static_call on contract StaticParent with size 1012 => 894
public_set_value on contract DelegatedOn with size 68 => 28
public_static_call on contract Parent with size 328 => 236
public_static_call on contract StaticParent with size 328 => 236
push_nullifier_public on contract InclusionProofs with size 285 => 117
read_storage_immutable on contract AvmInitializerTest with size 286 => 133
read_storage_list on contract AvmTest with size 101 => 62
read_storage_map on contract AvmTest with size 385 => 341
read_storage_single on contract AvmTest with size 66 => 27
register on contract KeyRegistry with size 6385 => 5041
repay_public on contract Lending with size 2141 => 1747
rotate_npk_m on contract KeyRegistry with size 2592 => 1843
send_l2_to_l1_msg on contract AvmTest with size 56 => 12
set_admin on contract Token with size 528 => 257
set_authorized on contract Auth with size 925 => 474
set_authorized_delay on contract Auth with size 679 => 400
set_minter on contract Token with size 865 => 559
set_opcode_big_field on contract AvmTest with size 19 => 19
set_opcode_small_field on contract AvmTest with size 13 => 13
set_opcode_u32 on contract AvmTest with size 13 => 13
set_opcode_u64 on contract AvmTest with size 13 => 13
set_opcode_u8 on contract AvmTest with size 13 => 13
set_portal on contract GasToken with size 434 => 272
set_price on contract PriceFeed with size 401 => 349
set_read_storage_single on contract AvmTest with size 122 => 42
set_storage_list on contract AvmTest with size 76 => 30
set_storage_map on contract AvmTest with size 409 => 355
set_storage_single on contract AvmNestedCallsTest with size 65 => 25
set_storage_single on contract AvmTest with size 65 => 25
set_value_twice_with_nested_first on contract Child with size 803 => 654
set_value_twice_with_nested_last on contract Child with size 791 => 654
set_value_with_two_nested_calls on contract Child with size 1308 => 1063
sha256_hash on contract AvmTest with size 47 => 47
shield on contract Token with size 2368 => 1809
shield on contract TokenBlacklist with size 3102 => 2315
spend_public_authwit on contract DocsExample with size 14 => 14
spend_public_authwit on contract EcdsaAccount with size 1008 => 637
spend_public_authwit on contract SchnorrAccount with size 770 => 541
spend_public_authwit on contract SchnorrHardcodedAccount with size 770 => 540
spend_public_authwit on contract SchnorrSingleKeyAccount with size 770 => 540
spend_public_authwit on contract Uniswap with size 749 => 486
start_game on contract CardGame with size 2518 => 2175
swap_public on contract Uniswap with size 7983 => 6584
test_authwit_send_money on contract AvmAcvmInteropTest with size 803 => 709
test_get_contract_instance on contract AvmTest with size 522 => 410
test_get_contract_instance_raw on contract AvmTest with size 159 => 83
test_nullifier_inclusion_from_public on contract InclusionProofs with size 332 => 121
to_radix_le on contract AvmTest with size 187 => 187
total_supply on contract Token with size 536 => 390
total_supply on contract TokenBlacklist with size 536 => 390
transfer_public on contract Token with size 3071 => 2512
transfer_public on contract TokenBlacklist with size 4491 => 3533
u128_addition_overflow on contract AvmTest with size 772 => 668
u128_from_integer_overflow on contract AvmTest with size 357 => 357
update_accumulator on contract Lending with size 8695 => 8358
update_leader on contract DocsExample with size 430 => 290
update_roles on contract TokenBlacklist with size 2058 => 1252
withdraw_public on contract Lending with size 1190 => 93
```

</details>


## Summary\*

This PR:
- Creates a separate pass for "runtime separation" where we convert any
Acir function called from brillig to a brillig function. After that
pass, all functions have correct runtime, meaning they'll be converted
to the runtime they hold.
- Updates the inliner to only consider entry points all brillig
functions called from ACIR and all brillig recursive functions, instead
of all brillig functions
 
With this change the inliner can correctly detect which brillig
functions **must not** be inlined. This opens the door to allowing
annotating brillig functions with inlining suggestions in the future.

## Additional Context



## Documentation\*

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
sirasistant authored May 29, 2024
1 parent b545744 commit 69eca9b
Show file tree
Hide file tree
Showing 13 changed files with 531 additions and 55 deletions.
6 changes: 6 additions & 0 deletions acvm-repo/brillig_vm/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,12 @@ impl<F: AcirField> Memory<F> {
}

pub fn read_slice(&self, addr: MemoryAddress, len: usize) -> &[MemoryValue<F>] {
// Allows to read a slice of uninitialized memory if the length is zero.
// Ideally we'd be able to read uninitialized memory in general (as read does)
// but that's not possible if we want to return a slice instead of owned data.
if len == 0 {
return &[];
}
&self.inner[addr.to_usize()..(addr.to_usize() + len)]
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ pub(crate) fn optimize_into_acir(
let ssa = SsaBuilder::new(program, print_passes, force_brillig_output, print_timings)?
.run_pass(Ssa::defunctionalize, "After Defunctionalization:")
.run_pass(Ssa::remove_paired_rc, "After Removing Paired rc_inc & rc_decs:")
.run_pass(Ssa::inline_functions, "After Inlining:")
.run_pass(Ssa::separate_runtime, "After Runtime Separation:")
.run_pass(Ssa::resolve_is_unconstrained, "After Resolving IsUnconstrained:")
.run_pass(Ssa::inline_functions, "After Inlining:")
// Run mem2reg with the CFG separated into blocks
.run_pass(Ssa::mem2reg, "After Mem2Reg:")
.run_pass(Ssa::as_slice_optimization, "After `as_slice` optimization")
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_evaluator/src/ssa/ir/dfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use noirc_errors::Location;
/// its blocks, instructions, and values. This struct is largely responsible for
/// owning most data in a function and handing out Ids to this data that can be
/// shared without worrying about ownership.
#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub(crate) struct DataFlowGraph {
/// All of the instructions in a function
instructions: DenseMap<Instruction>,
Expand Down
7 changes: 7 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ impl Function {
Self { name, id, entry_block, dfg, runtime: RuntimeType::Acir(InlineType::default()) }
}

/// Creates a new function as a clone of the one passed in with the passed in id.
pub(crate) fn clone_with_id(id: FunctionId, another: &Function) -> Self {
let dfg = another.dfg.clone();
let entry_block = another.entry_block;
Self { name: another.name.clone(), id, entry_block, dfg, runtime: another.runtime }
}

/// The name of the function.
/// Used exclusively for debugging purposes.
pub(crate) fn name(&self) -> &str {
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_evaluator/src/ssa/ir/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ impl std::fmt::Display for Id<super::instruction::Instruction> {
/// access to indices is provided. Since IDs must be stable and correspond
/// to indices in the internal Vec, operations that would change element
/// ordering like pop, remove, swap_remove, etc, are not possible.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub(crate) struct DenseMap<T> {
storage: Vec<T>,
}
Expand Down
160 changes: 127 additions & 33 deletions compiler/noirc_evaluator/src/ssa/opt/inlining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const RECURSION_LIMIT: u32 = 1000;
impl Ssa {
/// Inline all functions within the IR.
///
/// In the case of recursive functions, this will attempt
/// In the case of recursive Acir functions, this will attempt
/// to recursively inline until the RECURSION_LIMIT is reached.
///
/// Functions are recursively inlined into main until either we finish
Expand All @@ -41,6 +41,8 @@ impl Ssa {
/// There are some attributes that allow inlining a function at a different step of codegen.
/// Currently this is just `InlineType::NoPredicates` for which we have a flag indicating
/// whether treating that inline functions. The default is to treat these functions as entry points.
///
/// This step should run after runtime separation, since it relies on the runtime of the called functions being final.
#[tracing::instrument(level = "trace", skip(self))]
pub(crate) fn inline_functions(self) -> Ssa {
Self::inline_functions_inner(self, true)
Expand All @@ -52,12 +54,17 @@ impl Ssa {
}

fn inline_functions_inner(mut self, no_predicates_is_entry_point: bool) -> Ssa {
let recursive_functions = find_all_recursive_functions(&self);
self.functions = btree_map(
get_entry_point_functions(&self, no_predicates_is_entry_point),
get_functions_to_inline_into(&self, no_predicates_is_entry_point),
|entry_point| {
let new_function =
InlineContext::new(&self, entry_point, no_predicates_is_entry_point)
.inline_all(&self);
let new_function = InlineContext::new(
&self,
entry_point,
no_predicates_is_entry_point,
recursive_functions.clone(),
)
.inline_all(&self);
(entry_point, new_function)
},
);
Expand All @@ -80,6 +87,8 @@ struct InlineContext {
entry_point: FunctionId,

no_predicates_is_entry_point: bool,
// We keep track of the recursive functions in the SSA to avoid inlining them in a brillig context.
recursive_functions: BTreeSet<FunctionId>,
}

/// The per-function inlining context contains information that is only valid for one function.
Expand Down Expand Up @@ -113,28 +122,101 @@ struct PerFunctionContext<'function> {
inlining_entry: bool,
}

/// The entry point functions are each function we should inline into - and each function that
/// should be left in the final program.
/// This is the `main` function, any Acir functions with a [fold inline type][InlineType::Fold],
/// and any brillig functions used.
fn get_entry_point_functions(
/// Utility function to find out the direct calls of a function.
fn called_functions(func: &Function) -> BTreeSet<FunctionId> {
let mut called_function_ids = BTreeSet::default();
for block_id in func.reachable_blocks() {
for instruction_id in func.dfg[block_id].instructions() {
let Instruction::Call { func: called_value_id, .. } = &func.dfg[*instruction_id] else {
continue;
};

if let Value::Function(function_id) = func.dfg[*called_value_id] {
called_function_ids.insert(function_id);
}
}
}

called_function_ids
}

// Recursively explore the SSA to find the functions that end up calling themselves
fn find_recursive_functions(
ssa: &Ssa,
current_function: FunctionId,
mut explored_functions: im::HashSet<FunctionId>,
recursive_functions: &mut BTreeSet<FunctionId>,
) {
if explored_functions.contains(&current_function) {
recursive_functions.insert(current_function);
return;
}

let called_functions = called_functions(&ssa.functions[&current_function]);

explored_functions.insert(current_function);

for called_function in called_functions {
find_recursive_functions(
ssa,
called_function,
explored_functions.clone(),
recursive_functions,
);
}
}

fn find_all_recursive_functions(ssa: &Ssa) -> BTreeSet<FunctionId> {
let mut recursive_functions = BTreeSet::default();
find_recursive_functions(ssa, ssa.main_id, im::HashSet::default(), &mut recursive_functions);
recursive_functions
}

/// The functions we should inline into (and that should be left in the final program) are:
/// - main
/// - Any Brillig function called from Acir
/// - Any Brillig recursive function (Acir recursive functions will be inlined into the main function)
/// - Any Acir functions with a [fold inline type][InlineType::Fold],
fn get_functions_to_inline_into(
ssa: &Ssa,
no_predicates_is_entry_point: bool,
) -> BTreeSet<FunctionId> {
let functions = ssa.functions.iter();
let mut entry_points = functions
.filter(|(_, function)| {
// If we have not already finished the flattening pass, functions marked
// to not have predicates should be marked as entry points.
let no_predicates_is_entry_point =
no_predicates_is_entry_point && function.is_no_predicates();
function.runtime().is_entry_point() || no_predicates_is_entry_point
let mut brillig_entry_points = BTreeSet::default();
let mut acir_entry_points = BTreeSet::default();

for (func_id, function) in ssa.functions.iter() {
if function.runtime() == RuntimeType::Brillig {
continue;
}

// If we have not already finished the flattening pass, functions marked
// to not have predicates should be marked as entry points.
let no_predicates_is_entry_point =
no_predicates_is_entry_point && function.is_no_predicates();
if function.runtime().is_entry_point() || no_predicates_is_entry_point {
acir_entry_points.insert(*func_id);
}

for called_function_id in called_functions(function) {
if ssa.functions[&called_function_id].runtime() == RuntimeType::Brillig {
brillig_entry_points.insert(called_function_id);
}
}
}

let brillig_recursive_functions: BTreeSet<_> = find_all_recursive_functions(ssa)
.into_iter()
.filter(|recursive_function_id| {
let function = &ssa.functions[&recursive_function_id];
function.runtime() == RuntimeType::Brillig
})
.map(|(id, _)| *id)
.collect::<BTreeSet<_>>();
.collect();

entry_points.insert(ssa.main_id);
entry_points
std::iter::once(ssa.main_id)
.chain(acir_entry_points)
.chain(brillig_entry_points)
.chain(brillig_recursive_functions)
.collect()
}

impl InlineContext {
Expand All @@ -147,6 +229,7 @@ impl InlineContext {
ssa: &Ssa,
entry_point: FunctionId,
no_predicates_is_entry_point: bool,
recursive_functions: BTreeSet<FunctionId>,
) -> InlineContext {
let source = &ssa.functions[&entry_point];
let mut builder = FunctionBuilder::new(source.name().to_owned(), entry_point);
Expand All @@ -157,6 +240,7 @@ impl InlineContext {
entry_point,
call_stack: CallStack::new(),
no_predicates_is_entry_point,
recursive_functions,
}
}

Expand Down Expand Up @@ -391,18 +475,10 @@ impl<'function> PerFunctionContext<'function> {
match &self.source_function.dfg[*id] {
Instruction::Call { func, arguments } => match self.get_function(*func) {
Some(func_id) => {
let function = &ssa.functions[&func_id];
// If we have not already finished the flattening pass, functions marked
// to not have predicates should be marked as entry points unless we are inlining into brillig.
let entry_point = &ssa.functions[&self.context.entry_point];
let no_predicates_is_entry_point =
self.context.no_predicates_is_entry_point
&& function.is_no_predicates()
&& !matches!(entry_point.runtime(), RuntimeType::Brillig);
if function.runtime().is_entry_point() || no_predicates_is_entry_point {
self.push_instruction(*id);
} else {
if self.should_inline_call(ssa, func_id) {
self.inline_function(ssa, *id, func_id, arguments);
} else {
self.push_instruction(*id);
}
}
None => self.push_instruction(*id),
Expand All @@ -412,6 +488,24 @@ impl<'function> PerFunctionContext<'function> {
}
}

fn should_inline_call(&self, ssa: &Ssa, called_func_id: FunctionId) -> bool {
let function = &ssa.functions[&called_func_id];

if let RuntimeType::Acir(inline_type) = function.runtime() {
// If the called function is acir, we inline if it's not an entry point

// If we have not already finished the flattening pass, functions marked
// to not have predicates should be marked as entry points.
let no_predicates_is_entry_point =
self.context.no_predicates_is_entry_point && function.is_no_predicates();
!inline_type.is_entry_point() && !no_predicates_is_entry_point
} else {
// If the called function is brillig, we inline only if it's into brillig and the function is not recursive
ssa.functions[&self.context.entry_point].runtime() == RuntimeType::Brillig
&& !self.context.recursive_functions.contains(&called_func_id)
}
}

/// Inline a function call and remember the inlined return values in the values map
fn inline_function(
&mut self,
Expand Down
1 change: 1 addition & 0 deletions compiler/noirc_evaluator/src/ssa/opt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ mod remove_bit_shifts;
mod remove_enable_side_effects;
mod remove_if_else;
mod resolve_is_unconstrained;
mod runtime_separation;
mod simplify_cfg;
mod unrolling;
Loading

0 comments on commit 69eca9b

Please sign in to comment.