Patchwork [PR,45572] Fix two minor issues with indirect inlining

login
register
mail settings
Submitter Martin Jambor
Date Sept. 22, 2010, 9:51 a.m.
Message ID <20100922095156.GA3994@virgil.arch.suse.de>
Download mbox | patch
Permalink /patch/65415/
State New
Headers show

Comments

Martin Jambor - Sept. 22, 2010, 9:51 a.m.
Hi,

PR 45572 has three testcases which exhibit two different bugs:

1) ipa_make_edge_direct_to_target can inadvertently lazily create new
cgraph nodes when looking up a node for a decl with cgraph_node
function.  This node then has an uid which is larger than size of
ipa_node_params_vector, causing index out of bounds assert.  The
function should not create new nodes and so I replaced the call with a
call to cgraph_get_node.

2) If recursive indirect edges are discovered during recursive
inlining, they are added to new_edges but
cgraph_decide_recursive_inlining then looks them itself and processes
them immediately.  When edges from new_edges are processed, it
attempts to inline recursively them again which leads to all sorts of
weird consequences.  Fixed by teaching the indirect inlining machinery
not to add recursive edges when doing recursive inlining, which is
flagged by new parameters of ipa_propagate_indirect_call_infos and
unfortunately also of cgraph_mark_inline_edge.

Bootstrapped and tested on x86_64-linux, OK for trunk?

Thanks,

Martin



2010-09-21  Martin Jambor  <mjambor@suse.cz>

	PR tree-optimization/45572
	* ipa-prop.c (ipa_make_edge_direct_to_target): Use cgraph_get_node
	instead of cgraph_node.
	(update_indirect_edges_after_inlining): New parameter ignore.
	(propagate_info_to_inlined_callees): Likewise.
	(ipa_propagate_indirect_call_infos): New parameter new_recursive.
	* ipa-inline.c (cgraph_mark_inline_edge): New parameter new_recursive.
	Updated all callers.

	* testsuite/g++.dg/torture/pr45572-1.C: New test.
	* testsuite/g++.dg/torture/pr45572-2.C: Likewise.
Richard Guenther - Sept. 22, 2010, 9:59 a.m.
On Wed, Sep 22, 2010 at 11:51 AM, Martin Jambor <mjambor@suse.cz> wrote:
> Hi,
>
> PR 45572 has three testcases which exhibit two different bugs:
>
> 1) ipa_make_edge_direct_to_target can inadvertently lazily create new
> cgraph nodes when looking up a node for a decl with cgraph_node
> function.  This node then has an uid which is larger than size of
> ipa_node_params_vector, causing index out of bounds assert.  The
> function should not create new nodes and so I replaced the call with a
> call to cgraph_get_node.

That part sounds obvious, you might want to install it separately.

> 2) If recursive indirect edges are discovered during recursive
> inlining, they are added to new_edges but
> cgraph_decide_recursive_inlining then looks them itself and processes
> them immediately.  When edges from new_edges are processed, it
> attempts to inline recursively them again which leads to all sorts of
> weird consequences.  Fixed by teaching the indirect inlining machinery
> not to add recursive edges when doing recursive inlining, which is
> flagged by new parameters of ipa_propagate_indirect_call_infos and
> unfortunately also of cgraph_mark_inline_edge.

Can you expand on the "weird consequences"?  Could we instead
disable indirect inlining during recursive inlining (and would that
make the patch any prettier?).

Thanks,
Richard.

> Bootstrapped and tested on x86_64-linux, OK for trunk?
>
> Thanks,
>
> Martin
>
>
>
> 2010-09-21  Martin Jambor  <mjambor@suse.cz>
>
>        PR tree-optimization/45572
>        * ipa-prop.c (ipa_make_edge_direct_to_target): Use cgraph_get_node
>        instead of cgraph_node.
>        (update_indirect_edges_after_inlining): New parameter ignore.
>        (propagate_info_to_inlined_callees): Likewise.
>        (ipa_propagate_indirect_call_infos): New parameter new_recursive.
>        * ipa-inline.c (cgraph_mark_inline_edge): New parameter new_recursive.
>        Updated all callers.
>
>        * testsuite/g++.dg/torture/pr45572-1.C: New test.
>        * testsuite/g++.dg/torture/pr45572-2.C: Likewise.
>
> Index: icln/gcc/ipa-inline.c
> ===================================================================
> --- icln.orig/gcc/ipa-inline.c
> +++ icln/gcc/ipa-inline.c
> @@ -305,11 +305,13 @@ cgraph_clone_inlined_nodes (struct cgrap
>  /* Mark edge E as inlined and update callgraph accordingly.  UPDATE_ORIGINAL
>    specify whether profile of original function should be updated.  If any new
>    indirect edges are discovered in the process, add them to NEW_EDGES, unless
> -   it is NULL.  Return true iff any new callgraph edges were discovered as a
> -   result of inlining.  */
> +   it is NULL.  NEW_RECURSIVE should be set to false iff new recursive edges
> +   should not be added to NEW_EDGES.  Return true iff any new callgraph edges
> +   were discovered as a result of inlining.  */
>
>  static bool
>  cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original,
> +                        bool new_recursive,
>                         VEC (cgraph_edge_p, heap) **new_edges)
>  {
>   int old_size = 0, new_size = 0;
> @@ -343,7 +345,7 @@ cgraph_mark_inline_edge (struct cgraph_e
>   /* FIXME: We should remove the optimize check after we ensure we never run
>      IPA passes when not optimizng.  */
>   if (flag_indirect_inlining && optimize)
> -    return ipa_propagate_indirect_call_infos (curr, new_edges);
> +    return ipa_propagate_indirect_call_infos (curr, new_recursive, new_edges);
>   else
>     return false;
>  }
> @@ -365,7 +367,7 @@ cgraph_mark_inline (struct cgraph_edge *
>       next = e->next_caller;
>       if (e->caller == to && e->inline_failed)
>        {
> -          cgraph_mark_inline_edge (e, true, NULL);
> +          cgraph_mark_inline_edge (e, true, false, NULL);
>          if (e == edge)
>            edge = next;
>        }
> @@ -960,7 +962,7 @@ cgraph_decide_recursive_inlining (struct
>          fprintf (dump_file, "\n");
>        }
>       cgraph_redirect_edge_callee (curr, master_clone);
> -      cgraph_mark_inline_edge (curr, false, new_edges);
> +      cgraph_mark_inline_edge (curr, false, false, new_edges);
>       lookup_recursive_calls (node, curr->callee, heap);
>       n++;
>     }
> @@ -1245,7 +1247,7 @@ cgraph_decide_inlining_of_small_function
>            }
>          callee = edge->callee;
>          gcc_checking_assert (!callee->global.inlined_to);
> -         cgraph_mark_inline_edge (edge, true, &new_indirect_edges);
> +         cgraph_mark_inline_edge (edge, true, true, &new_indirect_edges);
>          if (flag_indirect_inlining)
>            add_new_edges_to_heap (heap, new_indirect_edges);
>
> @@ -1409,7 +1411,7 @@ cgraph_flatten (struct cgraph_node *node
>                 cgraph_node_name (e->callee),
>                 cgraph_node_name (e->caller));
>       orig_callee = e->callee;
> -      cgraph_mark_inline_edge (e, true, NULL);
> +      cgraph_mark_inline_edge (e, true, false, NULL);
>       if (e->callee != orig_callee)
>        orig_callee->aux = (void *)(size_t) INLINE_ALL;
>       cgraph_flatten (e->callee);
> Index: icln/gcc/ipa-prop.c
> ===================================================================
> --- icln.orig/gcc/ipa-prop.c
> +++ icln/gcc/ipa-prop.c
> @@ -1444,7 +1444,7 @@ ipa_make_edge_direct_to_target (struct c
>   target = TREE_OPERAND (target, 0);
>   if (TREE_CODE (target) != FUNCTION_DECL)
>     return NULL;
> -  callee = cgraph_node (target);
> +  callee = cgraph_get_node (target);
>   if (!callee)
>     return NULL;
>
> @@ -1543,6 +1543,7 @@ try_make_edge_direct_virtual_call (struc
>  static bool
>  update_indirect_edges_after_inlining (struct cgraph_edge *cs,
>                                      struct cgraph_node *node,
> +                                     struct cgraph_node *ignore,
>                                      VEC (cgraph_edge_p, heap) **new_edges)
>  {
>   struct ipa_edge_args *top;
> @@ -1594,7 +1595,7 @@ update_indirect_edges_after_inlining (st
>       if (new_direct_edge)
>        {
>          new_direct_edge->indirect_inlining_edge = 1;
> -         if (new_edges)
> +         if (new_edges && new_direct_edge->callee != ignore)
>            {
>              VEC_safe_push (cgraph_edge_p, heap, *new_edges,
>                             new_direct_edge);
> @@ -1612,22 +1613,24 @@ update_indirect_edges_after_inlining (st
>    update_indirect_edges_after_inlining on all nodes and
>    update_jump_functions_after_inlining on all non-inlined edges that lead out
>    of this subtree.  Newly discovered indirect edges will be added to
> -   *NEW_EDGES, unless NEW_EDGES is NULL.  Return true iff a new edge(s) were
> -   created.  */
> +   *NEW_EDGES except when NEW_EDGES is NULL or the edge leads to the node
> +   IGNORE.  Return true iff a new edge(s) were added to the vector.  */
>
>  static bool
>  propagate_info_to_inlined_callees (struct cgraph_edge *cs,
>                                   struct cgraph_node *node,
> +                                  struct cgraph_node *ignore,
>                                   VEC (cgraph_edge_p, heap) **new_edges)
>  {
>   struct cgraph_edge *e;
>   bool res;
>
> -  res = update_indirect_edges_after_inlining (cs, node, new_edges);
> +  res = update_indirect_edges_after_inlining (cs, node, ignore, new_edges);
>
>   for (e = node->callees; e; e = e->next_callee)
>     if (!e->inline_failed)
> -      res |= propagate_info_to_inlined_callees (cs, e->callee, new_edges);
> +      res |= propagate_info_to_inlined_callees (cs, e->callee, ignore,
> +                                               new_edges);
>     else
>       update_jump_functions_after_inlining (cs, e);
>
> @@ -1637,13 +1640,16 @@ propagate_info_to_inlined_callees (struc
>  /* Update jump functions and call note functions on inlining the call site CS.
>    CS is expected to lead to a node already cloned by
>    cgraph_clone_inline_nodes.  Newly discovered indirect edges will be added to
> -   *NEW_EDGES, unless NEW_EDGES is NULL.  Return true iff a new edge(s) were +
> -   created.  */
> +   *NEW_EDGES except when NEW_EDGES is NULL or the edge leads to the node
> +   IGNORE.  Return true iff any new edges were added to the vector.  */
>
>  bool
>  ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
> +                                  bool new_recursive,
>                                   VEC (cgraph_edge_p, heap) **new_edges)
>  {
> +  struct cgraph_node *ignore;
> +
>   /* FIXME lto: We do not stream out indirect call information.  */
>   if (flag_wpa)
>     return false;
> @@ -1654,7 +1660,16 @@ ipa_propagate_indirect_call_infos (struc
>     return false;
>   gcc_assert (ipa_edge_args_vector);
>
> -  return propagate_info_to_inlined_callees (cs, cs->callee, new_edges);
> +  if (new_recursive)
> +    ignore = NULL;
> +  else
> +    {
> +      ignore = cs->caller;
> +      if (ignore->global.inlined_to)
> +       ignore = ignore->global.inlined_to;
> +    }
> +
> +  return propagate_info_to_inlined_callees (cs, cs->callee, ignore, new_edges);
>  }
>
>  /* Frees all dynamically allocated structures that the argument info points
> Index: icln/gcc/ipa-prop.h
> ===================================================================
> --- icln.orig/gcc/ipa-prop.h
> +++ icln/gcc/ipa-prop.h
> @@ -427,6 +427,7 @@ void ipa_analyze_node (struct cgraph_nod
>  /* Function formal parameters related computations.  */
>  void ipa_initialize_node_params (struct cgraph_node *node);
>  bool ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
> +                                       bool new_recursive,
>                                        VEC (cgraph_edge_p, heap) **new_edges);
>
>  /* Indirect edge and binfo processing.  */
> Index: icln/gcc/testsuite/g++.dg/torture/pr45572-1.C
> ===================================================================
> --- /dev/null
> +++ icln/gcc/testsuite/g++.dg/torture/pr45572-1.C
> @@ -0,0 +1,63 @@
> +// { dg-do compile }
> +
> +extern "C" {
> +typedef long unsigned int size_t;
> +typedef long int __ssize_t;
> +typedef struct _IO_FILE FILE;
> +typedef struct
> +{
> +} __mbstate_t;
> +extern __inline __attribute__ ((__gnu_inline__)) int
> +fgetc_unlocked (FILE *__fp)
> +{
> +}
> +extern __inline __attribute__ ((__gnu_inline__)) int
> +putc_unlocked (int __c, FILE *__stream)
> +{
> +}
> +extern __inline __attribute__ ((__gnu_inline__)) __ssize_t
> +getline (char **__lineptr, size_t *__n, FILE *__stream)
> +{
> +}
> +extern __inline __attribute__ ((__gnu_inline__)) int
> +ferror_unlocked (FILE *__stream) throw ()
> +{
> +}
> +}
> +typedef struct
> +{} __mpf_struct;
> +typedef __mpf_struct mpf_t[1];
> +typedef const __mpf_struct *mpf_srcptr;
> +typedef __mpf_struct *mpf_ptr;
> +extern "C" {
> + void __gmpf_add (mpf_ptr, mpf_srcptr, mpf_srcptr);
> +}
> +class _knumber
> +{
> + public:
> +  enum NumType {SpecialType, IntegerType, FractionType, FloatType};
> +  virtual NumType type(void) const = 0;
> +  virtual _knumber * add(_knumber const & arg2) const = 0;
> +  virtual operator long int(void) const = 0;
> +};
> +class _knumfloat : public _knumber
> +{
> +  _knumfloat(double num = 1.0)
> +  ;
> +  virtual NumType type(void) const ;
> +  virtual _knumber * add(_knumber const & arg2) const;
> +  virtual operator long int (void) const;
> +    mpf_t _mpf;
> +};
> +_knumber *_knumfloat::add(_knumber const & arg2) const
> +{
> +  if (arg2.type() == SpecialType)
> +    return arg2.add(*this);
> +{
> +    _knumfloat tmp_num(arg2);
> +    return tmp_num.add(*this);
> +  }
> +  _knumfloat * tmp_num = new _knumfloat();
> +  __gmpf_add(tmp_num->_mpf, _mpf,
> +   dynamic_cast<_knumfloat const &>(arg2)._mpf);
> +}
> Index: icln/gcc/testsuite/g++.dg/torture/pr45572-2.C
> ===================================================================
> --- /dev/null
> +++ icln/gcc/testsuite/g++.dg/torture/pr45572-2.C
> @@ -0,0 +1,39 @@
> +// { dg-do compile }
> +
> +typedef struct
> +{} __mpf_struct;
> +typedef __mpf_struct mpf_t[1];
> +typedef const __mpf_struct *mpf_srcptr;
> +typedef __mpf_struct *mpf_ptr;
> +extern "C" {
> + void __gmpf_add (mpf_ptr, mpf_srcptr, mpf_srcptr);
> +}
> +class _knumber
> +{
> + public:
> +  enum NumType {SpecialType, IntegerType, FractionType, FloatType};
> +  virtual NumType type(void) const = 0;
> +  virtual _knumber * add(_knumber const & arg2) const = 0;
> +  virtual operator long int(void) const = 0;
> +};
> +class _knumfloat : public _knumber
> +{
> +  _knumfloat(double num = 1.0)
> +  ;
> +  virtual NumType type(void) const ;
> +  virtual _knumber * add(_knumber const & arg2) const;
> +  virtual operator long int (void) const;
> +    mpf_t _mpf;
> +};
> +_knumber *_knumfloat::add(_knumber const & arg2) const
> +{
> +  if (arg2.type() == SpecialType)
> +    return arg2.add(*this);
> +{
> +    _knumfloat tmp_num(arg2);
> +    return tmp_num.add(*this);
> +  }
> +  _knumfloat * tmp_num = new _knumfloat();
> +  __gmpf_add(tmp_num->_mpf, _mpf,
> +   dynamic_cast<_knumfloat const &>(arg2)._mpf);
> +}
>
>
Jan Hubicka - Sept. 22, 2010, 10:20 a.m.
> On Wed, Sep 22, 2010 at 11:51 AM, Martin Jambor <mjambor@suse.cz> wrote:
> > Hi,
> >
> > PR 45572 has three testcases which exhibit two different bugs:
> >
> > 1) ipa_make_edge_direct_to_target can inadvertently lazily create new
> > cgraph nodes when looking up a node for a decl with cgraph_node
> > function.  This node then has an uid which is larger than size of
> > ipa_node_params_vector, causing index out of bounds assert.  The
> > function should not create new nodes and so I replaced the call with a
> > call to cgraph_get_node.
> 
> That part sounds obvious, you might want to install it separately.

This is the case where we have previously virtual call but devirtualization finds
call using object with external vtable (so function never seen previously becomes
reachable).

I don't like the approach of keeping calls indirect when they are really known
to be direct.  Even when it won't degrade inlining (since the function was unseen
previously and thus has no body), still how much pain would be to update
tables instead?

> 
> > 2) If recursive indirect edges are discovered during recursive
> > inlining, they are added to new_edges but
> > cgraph_decide_recursive_inlining then looks them itself and processes
> > them immediately.  When edges from new_edges are processed, it
> > attempts to inline recursively them again which leads to all sorts of
> > weird consequences.  Fixed by teaching the indirect inlining machinery
> > not to add recursive edges when doing recursive inlining, which is
> > flagged by new parameters of ipa_propagate_indirect_call_infos and
> > unfortunately also of cgraph_mark_inline_edge.
> 
> Can you expand on the "weird consequences"?  Could we instead
> disable indirect inlining during recursive inlining (and would that
> make the patch any prettier?).

I saw here a testcase where virtual method was self recursive. Only after
inlining the first call the self recursiveness become obvious, so I guess
we should handle this.

I looked into the PR briefly before and noticed it has an weird consequences,
but didn't quite understood why?

Honza
> 
> Thanks,
> Richard.
> 
> > Bootstrapped and tested on x86_64-linux, OK for trunk?
> >
> > Thanks,
> >
> > Martin
> >
> >
> >
> > 2010-09-21  Martin Jambor  <mjambor@suse.cz>
> >
> >        PR tree-optimization/45572
> >        * ipa-prop.c (ipa_make_edge_direct_to_target): Use cgraph_get_node
> >        instead of cgraph_node.
> >        (update_indirect_edges_after_inlining): New parameter ignore.
> >        (propagate_info_to_inlined_callees): Likewise.
> >        (ipa_propagate_indirect_call_infos): New parameter new_recursive.
> >        * ipa-inline.c (cgraph_mark_inline_edge): New parameter new_recursive.
> >        Updated all callers.
> >
> >        * testsuite/g++.dg/torture/pr45572-1.C: New test.
> >        * testsuite/g++.dg/torture/pr45572-2.C: Likewise.
> >
> > Index: icln/gcc/ipa-inline.c
> > ===================================================================
> > --- icln.orig/gcc/ipa-inline.c
> > +++ icln/gcc/ipa-inline.c
> > @@ -305,11 +305,13 @@ cgraph_clone_inlined_nodes (struct cgrap
> >  /* Mark edge E as inlined and update callgraph accordingly.  UPDATE_ORIGINAL
> >    specify whether profile of original function should be updated.  If any new
> >    indirect edges are discovered in the process, add them to NEW_EDGES, unless
> > -   it is NULL.  Return true iff any new callgraph edges were discovered as a
> > -   result of inlining.  */
> > +   it is NULL.  NEW_RECURSIVE should be set to false iff new recursive edges
> > +   should not be added to NEW_EDGES.  Return true iff any new callgraph edges
> > +   were discovered as a result of inlining.  */
> >
> >  static bool
> >  cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original,
> > +                        bool new_recursive,
> >                         VEC (cgraph_edge_p, heap) **new_edges)
> >  {
> >   int old_size = 0, new_size = 0;
> > @@ -343,7 +345,7 @@ cgraph_mark_inline_edge (struct cgraph_e
> >   /* FIXME: We should remove the optimize check after we ensure we never run
> >      IPA passes when not optimizng.  */
> >   if (flag_indirect_inlining && optimize)
> > -    return ipa_propagate_indirect_call_infos (curr, new_edges);
> > +    return ipa_propagate_indirect_call_infos (curr, new_recursive, new_edges);
> >   else
> >     return false;
> >  }
> > @@ -365,7 +367,7 @@ cgraph_mark_inline (struct cgraph_edge *
> >       next = e->next_caller;
> >       if (e->caller == to && e->inline_failed)
> >        {
> > -          cgraph_mark_inline_edge (e, true, NULL);
> > +          cgraph_mark_inline_edge (e, true, false, NULL);
> >          if (e == edge)
> >            edge = next;
> >        }
> > @@ -960,7 +962,7 @@ cgraph_decide_recursive_inlining (struct
> >          fprintf (dump_file, "\n");
> >        }
> >       cgraph_redirect_edge_callee (curr, master_clone);
> > -      cgraph_mark_inline_edge (curr, false, new_edges);
> > +      cgraph_mark_inline_edge (curr, false, false, new_edges);
> >       lookup_recursive_calls (node, curr->callee, heap);
> >       n++;
> >     }
> > @@ -1245,7 +1247,7 @@ cgraph_decide_inlining_of_small_function
> >            }
> >          callee = edge->callee;
> >          gcc_checking_assert (!callee->global.inlined_to);
> > -         cgraph_mark_inline_edge (edge, true, &new_indirect_edges);
> > +         cgraph_mark_inline_edge (edge, true, true, &new_indirect_edges);
> >          if (flag_indirect_inlining)
> >            add_new_edges_to_heap (heap, new_indirect_edges);
> >
> > @@ -1409,7 +1411,7 @@ cgraph_flatten (struct cgraph_node *node
> >                 cgraph_node_name (e->callee),
> >                 cgraph_node_name (e->caller));
> >       orig_callee = e->callee;
> > -      cgraph_mark_inline_edge (e, true, NULL);
> > +      cgraph_mark_inline_edge (e, true, false, NULL);
> >       if (e->callee != orig_callee)
> >        orig_callee->aux = (void *)(size_t) INLINE_ALL;
> >       cgraph_flatten (e->callee);
> > Index: icln/gcc/ipa-prop.c
> > ===================================================================
> > --- icln.orig/gcc/ipa-prop.c
> > +++ icln/gcc/ipa-prop.c
> > @@ -1444,7 +1444,7 @@ ipa_make_edge_direct_to_target (struct c
> >   target = TREE_OPERAND (target, 0);
> >   if (TREE_CODE (target) != FUNCTION_DECL)
> >     return NULL;
> > -  callee = cgraph_node (target);
> > +  callee = cgraph_get_node (target);
> >   if (!callee)
> >     return NULL;
> >
> > @@ -1543,6 +1543,7 @@ try_make_edge_direct_virtual_call (struc
> >  static bool
> >  update_indirect_edges_after_inlining (struct cgraph_edge *cs,
> >                                      struct cgraph_node *node,
> > +                                     struct cgraph_node *ignore,
> >                                      VEC (cgraph_edge_p, heap) **new_edges)
> >  {
> >   struct ipa_edge_args *top;
> > @@ -1594,7 +1595,7 @@ update_indirect_edges_after_inlining (st
> >       if (new_direct_edge)
> >        {
> >          new_direct_edge->indirect_inlining_edge = 1;
> > -         if (new_edges)
> > +         if (new_edges && new_direct_edge->callee != ignore)
> >            {
> >              VEC_safe_push (cgraph_edge_p, heap, *new_edges,
> >                             new_direct_edge);
> > @@ -1612,22 +1613,24 @@ update_indirect_edges_after_inlining (st
> >    update_indirect_edges_after_inlining on all nodes and
> >    update_jump_functions_after_inlining on all non-inlined edges that lead out
> >    of this subtree.  Newly discovered indirect edges will be added to
> > -   *NEW_EDGES, unless NEW_EDGES is NULL.  Return true iff a new edge(s) were
> > -   created.  */
> > +   *NEW_EDGES except when NEW_EDGES is NULL or the edge leads to the node
> > +   IGNORE.  Return true iff a new edge(s) were added to the vector.  */
> >
> >  static bool
> >  propagate_info_to_inlined_callees (struct cgraph_edge *cs,
> >                                   struct cgraph_node *node,
> > +                                  struct cgraph_node *ignore,
> >                                   VEC (cgraph_edge_p, heap) **new_edges)
> >  {
> >   struct cgraph_edge *e;
> >   bool res;
> >
> > -  res = update_indirect_edges_after_inlining (cs, node, new_edges);
> > +  res = update_indirect_edges_after_inlining (cs, node, ignore, new_edges);
> >
> >   for (e = node->callees; e; e = e->next_callee)
> >     if (!e->inline_failed)
> > -      res |= propagate_info_to_inlined_callees (cs, e->callee, new_edges);
> > +      res |= propagate_info_to_inlined_callees (cs, e->callee, ignore,
> > +                                               new_edges);
> >     else
> >       update_jump_functions_after_inlining (cs, e);
> >
> > @@ -1637,13 +1640,16 @@ propagate_info_to_inlined_callees (struc
> >  /* Update jump functions and call note functions on inlining the call site CS.
> >    CS is expected to lead to a node already cloned by
> >    cgraph_clone_inline_nodes.  Newly discovered indirect edges will be added to
> > -   *NEW_EDGES, unless NEW_EDGES is NULL.  Return true iff a new edge(s) were +
> > -   created.  */
> > +   *NEW_EDGES except when NEW_EDGES is NULL or the edge leads to the node
> > +   IGNORE.  Return true iff any new edges were added to the vector.  */
> >
> >  bool
> >  ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
> > +                                  bool new_recursive,
> >                                   VEC (cgraph_edge_p, heap) **new_edges)
> >  {
> > +  struct cgraph_node *ignore;
> > +
> >   /* FIXME lto: We do not stream out indirect call information.  */
> >   if (flag_wpa)
> >     return false;
> > @@ -1654,7 +1660,16 @@ ipa_propagate_indirect_call_infos (struc
> >     return false;
> >   gcc_assert (ipa_edge_args_vector);
> >
> > -  return propagate_info_to_inlined_callees (cs, cs->callee, new_edges);
> > +  if (new_recursive)
> > +    ignore = NULL;
> > +  else
> > +    {
> > +      ignore = cs->caller;
> > +      if (ignore->global.inlined_to)
> > +       ignore = ignore->global.inlined_to;
> > +    }
> > +
> > +  return propagate_info_to_inlined_callees (cs, cs->callee, ignore, new_edges);
> >  }
> >
> >  /* Frees all dynamically allocated structures that the argument info points
> > Index: icln/gcc/ipa-prop.h
> > ===================================================================
> > --- icln.orig/gcc/ipa-prop.h
> > +++ icln/gcc/ipa-prop.h
> > @@ -427,6 +427,7 @@ void ipa_analyze_node (struct cgraph_nod
> >  /* Function formal parameters related computations.  */
> >  void ipa_initialize_node_params (struct cgraph_node *node);
> >  bool ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
> > +                                       bool new_recursive,
> >                                        VEC (cgraph_edge_p, heap) **new_edges);
> >
> >  /* Indirect edge and binfo processing.  */
> > Index: icln/gcc/testsuite/g++.dg/torture/pr45572-1.C
> > ===================================================================
> > --- /dev/null
> > +++ icln/gcc/testsuite/g++.dg/torture/pr45572-1.C
> > @@ -0,0 +1,63 @@
> > +// { dg-do compile }
> > +
> > +extern "C" {
> > +typedef long unsigned int size_t;
> > +typedef long int __ssize_t;
> > +typedef struct _IO_FILE FILE;
> > +typedef struct
> > +{
> > +} __mbstate_t;
> > +extern __inline __attribute__ ((__gnu_inline__)) int
> > +fgetc_unlocked (FILE *__fp)
> > +{
> > +}
> > +extern __inline __attribute__ ((__gnu_inline__)) int
> > +putc_unlocked (int __c, FILE *__stream)
> > +{
> > +}
> > +extern __inline __attribute__ ((__gnu_inline__)) __ssize_t
> > +getline (char **__lineptr, size_t *__n, FILE *__stream)
> > +{
> > +}
> > +extern __inline __attribute__ ((__gnu_inline__)) int
> > +ferror_unlocked (FILE *__stream) throw ()
> > +{
> > +}
> > +}
> > +typedef struct
> > +{} __mpf_struct;
> > +typedef __mpf_struct mpf_t[1];
> > +typedef const __mpf_struct *mpf_srcptr;
> > +typedef __mpf_struct *mpf_ptr;
> > +extern "C" {
> > + void __gmpf_add (mpf_ptr, mpf_srcptr, mpf_srcptr);
> > +}
> > +class _knumber
> > +{
> > + public:
> > +  enum NumType {SpecialType, IntegerType, FractionType, FloatType};
> > +  virtual NumType type(void) const = 0;
> > +  virtual _knumber * add(_knumber const & arg2) const = 0;
> > +  virtual operator long int(void) const = 0;
> > +};
> > +class _knumfloat : public _knumber
> > +{
> > +  _knumfloat(double num = 1.0)
> > +  ;
> > +  virtual NumType type(void) const ;
> > +  virtual _knumber * add(_knumber const & arg2) const;
> > +  virtual operator long int (void) const;
> > +    mpf_t _mpf;
> > +};
> > +_knumber *_knumfloat::add(_knumber const & arg2) const
> > +{
> > +  if (arg2.type() == SpecialType)
> > +    return arg2.add(*this);
> > +{
> > +    _knumfloat tmp_num(arg2);
> > +    return tmp_num.add(*this);
> > +  }
> > +  _knumfloat * tmp_num = new _knumfloat();
> > +  __gmpf_add(tmp_num->_mpf, _mpf,
> > +   dynamic_cast<_knumfloat const &>(arg2)._mpf);
> > +}
> > Index: icln/gcc/testsuite/g++.dg/torture/pr45572-2.C
> > ===================================================================
> > --- /dev/null
> > +++ icln/gcc/testsuite/g++.dg/torture/pr45572-2.C
> > @@ -0,0 +1,39 @@
> > +// { dg-do compile }
> > +
> > +typedef struct
> > +{} __mpf_struct;
> > +typedef __mpf_struct mpf_t[1];
> > +typedef const __mpf_struct *mpf_srcptr;
> > +typedef __mpf_struct *mpf_ptr;
> > +extern "C" {
> > + void __gmpf_add (mpf_ptr, mpf_srcptr, mpf_srcptr);
> > +}
> > +class _knumber
> > +{
> > + public:
> > +  enum NumType {SpecialType, IntegerType, FractionType, FloatType};
> > +  virtual NumType type(void) const = 0;
> > +  virtual _knumber * add(_knumber const & arg2) const = 0;
> > +  virtual operator long int(void) const = 0;
> > +};
> > +class _knumfloat : public _knumber
> > +{
> > +  _knumfloat(double num = 1.0)
> > +  ;
> > +  virtual NumType type(void) const ;
> > +  virtual _knumber * add(_knumber const & arg2) const;
> > +  virtual operator long int (void) const;
> > +    mpf_t _mpf;
> > +};
> > +_knumber *_knumfloat::add(_knumber const & arg2) const
> > +{
> > +  if (arg2.type() == SpecialType)
> > +    return arg2.add(*this);
> > +{
> > +    _knumfloat tmp_num(arg2);
> > +    return tmp_num.add(*this);
> > +  }
> > +  _knumfloat * tmp_num = new _knumfloat();
> > +  __gmpf_add(tmp_num->_mpf, _mpf,
> > +   dynamic_cast<_knumfloat const &>(arg2)._mpf);
> > +}
> >
> >

Patch

Index: icln/gcc/ipa-inline.c
===================================================================
--- icln.orig/gcc/ipa-inline.c
+++ icln/gcc/ipa-inline.c
@@ -305,11 +305,13 @@  cgraph_clone_inlined_nodes (struct cgrap
 /* Mark edge E as inlined and update callgraph accordingly.  UPDATE_ORIGINAL
    specify whether profile of original function should be updated.  If any new
    indirect edges are discovered in the process, add them to NEW_EDGES, unless
-   it is NULL.  Return true iff any new callgraph edges were discovered as a
-   result of inlining.  */
+   it is NULL.  NEW_RECURSIVE should be set to false iff new recursive edges
+   should not be added to NEW_EDGES.  Return true iff any new callgraph edges
+   were discovered as a result of inlining.  */
 
 static bool
 cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original,
+			 bool new_recursive,
 			 VEC (cgraph_edge_p, heap) **new_edges)
 {
   int old_size = 0, new_size = 0;
@@ -343,7 +345,7 @@  cgraph_mark_inline_edge (struct cgraph_e
   /* FIXME: We should remove the optimize check after we ensure we never run
      IPA passes when not optimizng.  */
   if (flag_indirect_inlining && optimize)
-    return ipa_propagate_indirect_call_infos (curr, new_edges);
+    return ipa_propagate_indirect_call_infos (curr, new_recursive, new_edges);
   else
     return false;
 }
@@ -365,7 +367,7 @@  cgraph_mark_inline (struct cgraph_edge *
       next = e->next_caller;
       if (e->caller == to && e->inline_failed)
 	{
-          cgraph_mark_inline_edge (e, true, NULL);
+          cgraph_mark_inline_edge (e, true, false, NULL);
 	  if (e == edge)
 	    edge = next;
 	}
@@ -960,7 +962,7 @@  cgraph_decide_recursive_inlining (struct
 	  fprintf (dump_file, "\n");
 	}
       cgraph_redirect_edge_callee (curr, master_clone);
-      cgraph_mark_inline_edge (curr, false, new_edges);
+      cgraph_mark_inline_edge (curr, false, false, new_edges);
       lookup_recursive_calls (node, curr->callee, heap);
       n++;
     }
@@ -1245,7 +1247,7 @@  cgraph_decide_inlining_of_small_function
 	    }
 	  callee = edge->callee;
 	  gcc_checking_assert (!callee->global.inlined_to);
-	  cgraph_mark_inline_edge (edge, true, &new_indirect_edges);
+	  cgraph_mark_inline_edge (edge, true, true, &new_indirect_edges);
 	  if (flag_indirect_inlining)
 	    add_new_edges_to_heap (heap, new_indirect_edges);
 
@@ -1409,7 +1411,7 @@  cgraph_flatten (struct cgraph_node *node
 		 cgraph_node_name (e->callee),
 		 cgraph_node_name (e->caller));
       orig_callee = e->callee;
-      cgraph_mark_inline_edge (e, true, NULL);
+      cgraph_mark_inline_edge (e, true, false, NULL);
       if (e->callee != orig_callee)
 	orig_callee->aux = (void *)(size_t) INLINE_ALL;
       cgraph_flatten (e->callee);
Index: icln/gcc/ipa-prop.c
===================================================================
--- icln.orig/gcc/ipa-prop.c
+++ icln/gcc/ipa-prop.c
@@ -1444,7 +1444,7 @@  ipa_make_edge_direct_to_target (struct c
   target = TREE_OPERAND (target, 0);
   if (TREE_CODE (target) != FUNCTION_DECL)
     return NULL;
-  callee = cgraph_node (target);
+  callee = cgraph_get_node (target);
   if (!callee)
     return NULL;
 
@@ -1543,6 +1543,7 @@  try_make_edge_direct_virtual_call (struc
 static bool
 update_indirect_edges_after_inlining (struct cgraph_edge *cs,
 				      struct cgraph_node *node,
+				      struct cgraph_node *ignore,
 				      VEC (cgraph_edge_p, heap) **new_edges)
 {
   struct ipa_edge_args *top;
@@ -1594,7 +1595,7 @@  update_indirect_edges_after_inlining (st
       if (new_direct_edge)
 	{
 	  new_direct_edge->indirect_inlining_edge = 1;
-	  if (new_edges)
+	  if (new_edges && new_direct_edge->callee != ignore)
 	    {
 	      VEC_safe_push (cgraph_edge_p, heap, *new_edges,
 			     new_direct_edge);
@@ -1612,22 +1613,24 @@  update_indirect_edges_after_inlining (st
    update_indirect_edges_after_inlining on all nodes and
    update_jump_functions_after_inlining on all non-inlined edges that lead out
    of this subtree.  Newly discovered indirect edges will be added to
-   *NEW_EDGES, unless NEW_EDGES is NULL.  Return true iff a new edge(s) were
-   created.  */
+   *NEW_EDGES except when NEW_EDGES is NULL or the edge leads to the node
+   IGNORE.  Return true iff a new edge(s) were added to the vector.  */
 
 static bool
 propagate_info_to_inlined_callees (struct cgraph_edge *cs,
 				   struct cgraph_node *node,
+				   struct cgraph_node *ignore,
 				   VEC (cgraph_edge_p, heap) **new_edges)
 {
   struct cgraph_edge *e;
   bool res;
 
-  res = update_indirect_edges_after_inlining (cs, node, new_edges);
+  res = update_indirect_edges_after_inlining (cs, node, ignore, new_edges);
 
   for (e = node->callees; e; e = e->next_callee)
     if (!e->inline_failed)
-      res |= propagate_info_to_inlined_callees (cs, e->callee, new_edges);
+      res |= propagate_info_to_inlined_callees (cs, e->callee, ignore,
+						new_edges);
     else
       update_jump_functions_after_inlining (cs, e);
 
@@ -1637,13 +1640,16 @@  propagate_info_to_inlined_callees (struc
 /* Update jump functions and call note functions on inlining the call site CS.
    CS is expected to lead to a node already cloned by
    cgraph_clone_inline_nodes.  Newly discovered indirect edges will be added to
-   *NEW_EDGES, unless NEW_EDGES is NULL.  Return true iff a new edge(s) were +
-   created.  */
+   *NEW_EDGES except when NEW_EDGES is NULL or the edge leads to the node
+   IGNORE.  Return true iff any new edges were added to the vector.  */
 
 bool
 ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
+				   bool new_recursive,
 				   VEC (cgraph_edge_p, heap) **new_edges)
 {
+  struct cgraph_node *ignore;
+
   /* FIXME lto: We do not stream out indirect call information.  */
   if (flag_wpa)
     return false;
@@ -1654,7 +1660,16 @@  ipa_propagate_indirect_call_infos (struc
     return false;
   gcc_assert (ipa_edge_args_vector);
 
-  return propagate_info_to_inlined_callees (cs, cs->callee, new_edges);
+  if (new_recursive)
+    ignore = NULL;
+  else
+    {
+      ignore = cs->caller;
+      if (ignore->global.inlined_to)
+	ignore = ignore->global.inlined_to;
+    }
+
+  return propagate_info_to_inlined_callees (cs, cs->callee, ignore, new_edges);
 }
 
 /* Frees all dynamically allocated structures that the argument info points
Index: icln/gcc/ipa-prop.h
===================================================================
--- icln.orig/gcc/ipa-prop.h
+++ icln/gcc/ipa-prop.h
@@ -427,6 +427,7 @@  void ipa_analyze_node (struct cgraph_nod
 /* Function formal parameters related computations.  */
 void ipa_initialize_node_params (struct cgraph_node *node);
 bool ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
+					bool new_recursive,
 					VEC (cgraph_edge_p, heap) **new_edges);
 
 /* Indirect edge and binfo processing.  */
Index: icln/gcc/testsuite/g++.dg/torture/pr45572-1.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/torture/pr45572-1.C
@@ -0,0 +1,63 @@ 
+// { dg-do compile }
+
+extern "C" {
+typedef long unsigned int size_t;
+typedef long int __ssize_t;
+typedef struct _IO_FILE FILE;
+typedef struct
+{
+} __mbstate_t;
+extern __inline __attribute__ ((__gnu_inline__)) int
+fgetc_unlocked (FILE *__fp)
+{
+}
+extern __inline __attribute__ ((__gnu_inline__)) int
+putc_unlocked (int __c, FILE *__stream)
+{
+}
+extern __inline __attribute__ ((__gnu_inline__)) __ssize_t
+getline (char **__lineptr, size_t *__n, FILE *__stream)
+{
+}
+extern __inline __attribute__ ((__gnu_inline__)) int
+ferror_unlocked (FILE *__stream) throw ()
+{
+}
+}
+typedef struct
+{} __mpf_struct;
+typedef __mpf_struct mpf_t[1];
+typedef const __mpf_struct *mpf_srcptr;
+typedef __mpf_struct *mpf_ptr;
+extern "C" {
+ void __gmpf_add (mpf_ptr, mpf_srcptr, mpf_srcptr);
+}
+class _knumber
+{
+ public:
+  enum NumType {SpecialType, IntegerType, FractionType, FloatType};
+  virtual NumType type(void) const = 0;
+  virtual _knumber * add(_knumber const & arg2) const = 0;
+  virtual operator long int(void) const = 0;
+};
+class _knumfloat : public _knumber
+{
+  _knumfloat(double num = 1.0)
+  ;
+  virtual NumType type(void) const ;
+  virtual _knumber * add(_knumber const & arg2) const;
+  virtual operator long int (void) const;
+    mpf_t _mpf;
+};
+_knumber *_knumfloat::add(_knumber const & arg2) const
+{
+  if (arg2.type() == SpecialType)
+    return arg2.add(*this);
+{
+    _knumfloat tmp_num(arg2);
+    return tmp_num.add(*this);
+  }
+  _knumfloat * tmp_num = new _knumfloat();
+  __gmpf_add(tmp_num->_mpf, _mpf,
+   dynamic_cast<_knumfloat const &>(arg2)._mpf);
+}
Index: icln/gcc/testsuite/g++.dg/torture/pr45572-2.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/torture/pr45572-2.C
@@ -0,0 +1,39 @@ 
+// { dg-do compile }
+
+typedef struct
+{} __mpf_struct;
+typedef __mpf_struct mpf_t[1];
+typedef const __mpf_struct *mpf_srcptr;
+typedef __mpf_struct *mpf_ptr;
+extern "C" {
+ void __gmpf_add (mpf_ptr, mpf_srcptr, mpf_srcptr);
+}
+class _knumber
+{
+ public:
+  enum NumType {SpecialType, IntegerType, FractionType, FloatType};
+  virtual NumType type(void) const = 0;
+  virtual _knumber * add(_knumber const & arg2) const = 0;
+  virtual operator long int(void) const = 0;
+};
+class _knumfloat : public _knumber
+{
+  _knumfloat(double num = 1.0)
+  ;
+  virtual NumType type(void) const ;
+  virtual _knumber * add(_knumber const & arg2) const;
+  virtual operator long int (void) const;
+    mpf_t _mpf;
+};
+_knumber *_knumfloat::add(_knumber const & arg2) const
+{
+  if (arg2.type() == SpecialType)
+    return arg2.add(*this);
+{
+    _knumfloat tmp_num(arg2);
+    return tmp_num.add(*this);
+  }
+  _knumfloat * tmp_num = new _knumfloat();
+  __gmpf_add(tmp_num->_mpf, _mpf,
+   dynamic_cast<_knumfloat const &>(arg2)._mpf);
+}