https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117710
Bug ID: 117710 Summary: repeated calls to std::function are not inlined Product: gcc Version: unknown Status: UNCONFIRMED Severity: normal Priority: P3 Component: tree-optimization Assignee: unassigned at gcc dot gnu.org Reporter: hubicka at gcc dot gnu.org Target Milestone: --- in the following testcase: #include <functional> int test (std::function<int(int)> f) { return f(0) + f(1); } int test2 () { return test ([](int a)->int{return a;}); } we should optimize test2 to "return 1", but instead we produce: int test2 () { void * D.43602; struct function D.42851; int _3; void * _7; bool (*<T325d>) (union _Any_data & {ref-all}, const union _Any_data & {ref-all}, _Manager_operation) _13; bool (*<T325d>) (union _Any_data & {ref-all}, const union _Any_data & {ref-all}, _Manager_operation) _15; vector(2) long unsigned int _16; long unsigned int _20; long unsigned int _22; <bb 2> [local count: 1073741824]: _20 = (long unsigned int) _M_invoke; _22 = (long unsigned int) _M_manager; _16 = {_22, _20}; D.42851 ={v} {CLOBBER(bob)}; MEM <char[16]> [(struct _Function_base *)&D.42851] = {}; MEM <vector(2) long unsigned int> [(void *)&D.42851 + 16B] = _16; _3 = test (&D.42851); <bb 3> [local count: 1073741824]: _13 = D.42851._M_manager; if (_13 != 0B) goto <bb 4>; [70.00%] else goto <bb 5>; [30.00%] <bb 4> [local count: 751619280]: _13 (&D.42851._M_functor, &D.42851._M_functor, 3); <bb 5> [local count: 1073741824]: D.42851 ={v} {CLOBBER(eob)}; D.42851 ={v} {CLOBBER(eos)}; return _3; <bb 6> [count: 0]: <L4>: _15 = D.42851._M_manager; if (_15 != 0B) goto <bb 7>; [70.00%] else goto <bb 8>; [30.00%] <bb 7> [count: 0]: _15 (&D.42851._M_functor, &D.42851._M_functor, 3); <bb 8> [count: 0]: D.42851 ={v} {CLOBBER(eob)}; _7 = __builtin_eh_pointer (3); __builtin_unwind_resume (_7); } The problem is that we are not able to track that parameter F is not modified by the first call. This would be done if we managed to mark F as noescape, but we can't because this is what we see at release_ssa time: int test (struct function & f) { int _4; int _6; int _7; <bb 2> [local count: 1073741824]: _4 = std::function<int(int)>::operator() (f_2(D), 0); _6 = std::function<int(int)>::operator() (f_2(D), 1); _7 = _4 + _6; return _7; } here operator call is not inlined (yet) since it does two calls: int std::function<int(int)>::operator() (const struct function * const this, int __args#0) { int (*<T83c2>) (const union _Any_data & {ref-all}, int &) _1; const union _Any_data * {ref-all} _2; bool (*<T325d>) (union _Any_data & {ref-all}, const union _Any_data & {ref-all}, _Manager_operation) _5; int _7; <bb 2> [local count: 1073741824]: _5 = MEM[(const struct _Function_base *)this_3(D)]._M_manager; if (_5 == 0B) goto <bb 3>; [0.00%] else goto <bb 4>; [100.00%] <bb 3> [count: 0]: std::__throw_bad_function_call (); <bb 4> [local count: 1073741824]: _1 = this_3(D)->_M_invoker; _2 = &this_3(D)->D.42484._M_functor; _7 = _1 (_2, &__args#0); return _7; } and now the problem is that _2 = &this_3(D)->D.42484._M_functor; _7 = _1 (_2, &__args#0); makes parameter f to escape into indirect call of _1, which is _M_invoker implemented as: int std::_Function_handler<int(int), test2()::<lambda(int)> >::_M_invoke (const union _Any_data & {ref-all} __functor, int & __args#0) { int _4; <bb 2> [local count: 1073741824]: _4 = *__args#0_3(D); return _4; } so it does not even use __functor. If __functor was passed by value, it would be all OK. If we had nocapture attribute for function parameters, perhaps this can be declared as such?