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?

Reply via email to