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?