[PATCH, PR 45934 6/6] Intraprocedural type-based devirtualization

Richard Guenther rguenther@suse.de
Thu Dec 2 15:40:00 GMT 2010


On Wed, 1 Dec 2010, Martin Jambor wrote:

> This patch is in some ways still in the RFC shape because it certainly
> depends very much on how the previous ones will look like in the end
> and I'd like to discuss where this functionality should be placed.  On
> the other hand, putting it into the cgraph rebuilding pass seems the
> best solution so far to me.  It certainly looks better then when I
> tried to cram it into ipa-prop analysis phase, if not for any other
> reasons then because cfg cleanup might be necessary.
> 
> Another reason is that I still want to try some more test-cases.
> However, I did not want to wait with the whole series any longer and
> so I am sending it out with the rest now.
> 
> This patch compensates for not devirtualizing when folding.  It looks
> for OBJ_TYPE_REF calls, and tries to deduce their type by watching out
> for dynamic type changes.  If the type is known and the call can be
> converted to a direct one with that knowledge, it is done so.
> 
> This patch is necessary for (modified variants of) testcases
> g++.dg/otr-fold-[12].C, g++.dg/tree-ssa/pr43411.C and
> g++.dg/tree-ssa/pr45605.C to pass again.  Without it, early inlining
> can be detrimental to devirtualization by making it a purely
> intraprocedural issue and slow down simple testcases three-fold.
>  
> Despite being an RFC patch, it also passes bootstrap and testing on
> x86_64-linux and make check-c++ on i686.  I'm looking forward to all
> comments and suggestions.
> 
> Thanks,
> 
> Martin
> 
> 
> 
> 2010-12-01  Martin Jambor  <mjambor@suse.cz>
> 
> 	* ipa-prop.c (get_ancestor_base_and_offset): New function.
> 	(compute_complex_assign_jump_func): Use it, check that op2 is NULL
> 	in the ancestor case.
> 	(compute_complex_ancestor_jump_func): Likewise.
> 	(ipa_try_devirtualize_immediately): New fuction.
> 	* cgraphbuild.c (rebuild_cgraph_edges): Renamed to
> 	rebuild_cgraph_edges_1, new parameter devirtualize, call
> 	ipa_try_devirtualize_immediately if it is true.
> 	(rebuild_cgraph_edges): New function.
> 	(rebuild_cgraph_edges_devirtualize): Likewise.
> 	(pass_rebuild_cgraph_edges_and_devirt): New variable.
> 	* passes.c (init_optimization_passes): Use it.
> 	* tree-pass.h (pass_rebuild_cgraph_edges_and_devirt): Declare.
> 	* ipa-prop.h (ipa_try_devirtualize_immediately): Declare.
> 
> 	* testsuite/g++.dg/otr-fold-1.C: Moved to...
> 	* testsuite/g++.dg/ipa/imm-devirt-1.C: ...here, compiling with -O2.
> 	* testsuite/g++.dg/otr-fold-2.C: Moved to...
> 	* testsuite/g++.dg/ipa/imm-devirt-2.C: ...here, compiling with -O2.
> 	* testsuite/g++.dg/tree-ssa/pr45605.C: Compile with O2, scan optimized
> 	dump.
> 
> 
> Index: icln/gcc/ipa-prop.c
> ===================================================================
> --- icln.orig/gcc/ipa-prop.c
> +++ icln/gcc/ipa-prop.c
> @@ -539,6 +539,24 @@ detect_type_change (tree arg, gimple cal
>    return detect_type_change_anc (arg, call, jfunc, 0);
>  }
>  
> +/* !!! Doc */
> +
> +static tree
> +get_ancestor_base_and_offset (tree op, HOST_WIDE_INT *offset)
> +{
> +  HOST_WIDE_INT size, max_size;
> +  tree base;
> +
> +  base = get_ref_base_and_extent (op, offset, &size, &max_size);
> +  if (TREE_CODE (base) != MEM_REF
> +      /* If this is a varying address, punt.  */
> +      || max_size == -1
> +      || max_size != size)
> +    return NULL_TREE;
> +  *offset += mem_ref_offset (base).low * BITS_PER_UNIT;
> +  return TREE_OPERAND (base, 0);
> +}
> +
>

How's this different from get_addr_base_and_unit_offset?  You'd have
the pointer-dereference check and the BITS_PER_UNIT multiplication
at the caller.  At least I'd use get_addr_base_and_unit_offset above
as it is cheaper than get_ref_base_and_extent:

   tree base = get_addr_base_and_unit_offset (op, offset);
   if (!base
       || TREE_CODE (base) != MEM_REF)
     return NULL_TREE;
   *offset += mem_ref_offset (base).low;
   *offset *= BITS_PER_UNIT;
   return TREE_OPERAND (base, 0);

(in fact that looks more like a true get_addr_base_and_unit_offset
while the existing one is misnamed and should probably be
called get_ref_base[_and_unit_offset]).

>  /* Given that an actual argument is an SSA_NAME (given in NAME) and is a result
>     of an assignment statement STMT, try to find out whether NAME can be
> @@ -551,7 +569,7 @@ compute_complex_assign_jump_func (struct
>  				  struct ipa_jump_func *jfunc,
>  				  gimple call, gimple stmt, tree name)
>  {
> -  HOST_WIDE_INT offset, size, max_size;
> +  HOST_WIDE_INT offset;
>    tree op1, op2, base;
>    int index;
>  
> @@ -588,20 +606,14 @@ compute_complex_assign_jump_func (struct
>        return;
>      }
>  
> -  if (TREE_CODE (op1) != ADDR_EXPR)
> +  if (op2 || TREE_CODE (op1) != ADDR_EXPR)
>      return;
>    op1 = TREE_OPERAND (op1, 0);
>    if (TREE_CODE (TREE_TYPE (op1)) != RECORD_TYPE)
>      return;
> -  base = get_ref_base_and_extent (op1, &offset, &size, &max_size);
> -  if (TREE_CODE (base) != MEM_REF
> -      /* If this is a varying address, punt.  */
> -      || max_size == -1
> -      || max_size != size)
> -    return;
> -  offset += mem_ref_offset (base).low * BITS_PER_UNIT;
> -  base = TREE_OPERAND (base, 0);
> -  if (TREE_CODE (base) != SSA_NAME
> +  base = get_ancestor_base_and_offset (op1, &offset);
> +  if (!base
> +      || TREE_CODE (base) != SSA_NAME
>        || !SSA_NAME_IS_DEFAULT_DEF (base)
>        || offset < 0)
>      return;
> @@ -645,10 +657,10 @@ compute_complex_ancestor_jump_func (stru
>  				    struct ipa_jump_func *jfunc,
>  				    gimple call, gimple phi)
>  {
> -  HOST_WIDE_INT offset, size, max_size;
> +  HOST_WIDE_INT offset;
>    gimple assign, cond;
>    basic_block phi_bb, assign_bb, cond_bb;
> -  tree tmp, parm, expr, obj;
> +  tree tmp, parm, expr;
>    int index, i;
>  
>    if (gimple_phi_num_args (phi) != 2)
> @@ -676,17 +688,9 @@ compute_complex_ancestor_jump_func (stru
>    if (TREE_CODE (expr) != ADDR_EXPR)
>      return;
>    expr = TREE_OPERAND (expr, 0);
> -  obj = expr;
> -  expr = get_ref_base_and_extent (expr, &offset, &size, &max_size);
> -
> -  if (TREE_CODE (expr) != MEM_REF
> -      /* If this is a varying address, punt.  */
> -      || max_size == -1
> -      || max_size != size)
> -    return;
> -  offset += mem_ref_offset (expr).low * BITS_PER_UNIT;
> -  parm = TREE_OPERAND (expr, 0);
> -  if (TREE_CODE (parm) != SSA_NAME
> +  parm = get_ancestor_base_and_offset (expr, &offset);
> +  if (!parm
> +      || TREE_CODE (parm) != SSA_NAME
>        || !SSA_NAME_IS_DEFAULT_DEF (parm)
>        || offset < 0)
>      return;
> @@ -712,12 +716,12 @@ compute_complex_ancestor_jump_func (stru
>  	return;
>      }
>  
> -  if (!detect_type_change_anc (obj, call, jfunc, offset))
> +  if (!detect_type_change_anc (expr, call, jfunc, offset))
>      {
>        jfunc->type = IPA_JF_ANCESTOR;
>        jfunc->value.ancestor.formal_id = index;
>        jfunc->value.ancestor.offset = offset;
> -      jfunc->value.ancestor.type = TREE_TYPE (obj);;
> +      jfunc->value.ancestor.type = TREE_TYPE (expr);;
>      }
>  }
>  
> @@ -1407,6 +1411,77 @@ ipa_analyze_indirect_call_uses (struct c
>    return;
>  }
>  
> +/* If a call to an OBJ_TYPE_REF given by GSI can be turned into a direct one
> +   according to the type of its object right away and if so, do it and return
> +   true.  If CFG is changed in the process, *CFG_CHANGED is set to true.  */
> +
> +tree
> +ipa_try_devirtualize_immediately (gimple_stmt_iterator *gsi, bool *cfg_changed)
> +{
> +  struct ipa_jump_func jfunc;
> +  tree obj, fndecl, delta, token;
> +  gimple call = gsi_stmt (*gsi);
> +  tree target = gimple_call_fn (call);
> +
> +  if (TREE_CODE (target) != OBJ_TYPE_REF)
> +    return NULL_TREE;

fold-stmt will still fold calls with OBJ_TYPE_REF_EXPR being a
&function-decl, right?  (But I can't find that it does so)

De-virtualization for that case seems wasteful, so maybe assert
that OBJ_TYPE_REF_EXPR is an SSA name here (or bail out).

> +  jfunc.type = IPA_JF_UNKNOWN;
> +  obj = OBJ_TYPE_REF_OBJECT (target);
> +  if (TREE_CODE (obj) == SSA_NAME)
> +    {
> +      HOST_WIDE_INT offset;
> +      tree op, base;
> +      gimple stmt = SSA_NAME_DEF_STMT (obj);
> +      if (!stmt || !is_gimple_assign (stmt) || gimple_assign_rhs2 (stmt))
> +	return NULL_TREE;
> +      op = gimple_assign_rhs1 (stmt);
> +      if (TREE_CODE (op) != ADDR_EXPR)
> +	return NULL_TREE;
> +      op = TREE_OPERAND (op, 0);
> +      if (TREE_CODE (TREE_TYPE (op)) != RECORD_TYPE)
> +	return NULL_TREE;
> +      base = get_ancestor_base_and_offset (op, &offset);
> +      if (!base || offset < 0)
> +	return NULL_TREE;
> +      detect_type_change_anc (op, call, &jfunc, offset);
> +    }
> +  else
> +    compute_known_type_jump_func (obj, &jfunc, call);
> +  if (jfunc.type != IPA_JF_KNOWN_TYPE)
> +    return NULL_TREE;
> +
> +  token = OBJ_TYPE_REF_TOKEN (target);
> +  fndecl = gimple_get_virt_mehtod_for_binfo (tree_low_cst (token, 1),

Check for host_integerp (token, 1) first or use TREE_INT_CST_LOW
if you know that's always the case.

Otherwise this looks mostly ok, I'll defer to Honza if this is a good
place to do this (I'd say inlining itself would be a good place as well,
before or during early inlining).

Thanks,
Richard.

> +					     jfunc.value.base_binfo,
> +					     &delta, true);
> +  if (!fndecl)
> +    return NULL_TREE;
> +  if (dump_file)
> +    {
> +      fprintf (dump_file, "ipa-prop: Immediately devirtualizing call ");
> +      print_gimple_expr (dump_file, call, 0, 0);
> +    }
> +
> +  if (integer_nonzerop (delta))
> +    gimple_adjust_this_by_delta (gsi, delta);
> +  gimple_call_set_fndecl (call, fndecl);
> +  update_stmt (call);
> +  if (maybe_clean_eh_stmt (call)
> +      && gimple_purge_dead_eh_edges (gimple_bb (call)))
> +    *cfg_changed = true;
> +
> +  if (dump_file)
> +    {
> +      fprintf (dump_file, "\n          into ");
> +      print_gimple_expr (dump_file, call, 0, 0);
> +      fprintf (dump_file, "\n          with delta ");
> +      print_generic_expr (dump_file, delta, 0);
> +      fprintf (dump_file, "\n");
> +    }
> +
> +  return fndecl;
> +}
> +
>  /* Analyze a CALL to an OBJ_TYPE_REF which is passed in TARGET and if the
>     object referenced in the expression is a formal parameter of the caller
>     (described by INFO), create a call note for the statement. */
> Index: icln/gcc/testsuite/g++.dg/ipa/imm-devirt-1.C
> ===================================================================
> --- /dev/null
> +++ icln/gcc/testsuite/g++.dg/ipa/imm-devirt-1.C
> @@ -0,0 +1,76 @@
> +/* Verify that virtual calls are folded even when a typecast to an
> +   ancestor is involved along the way.  */
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fdump-tree-optimized-slim"  } */
> +
> +extern "C" void abort (void);
> +
> +class Distraction
> +{
> +public:
> +  float f;
> +  double d;
> +  Distraction ()
> +  {
> +    f = 8.3;
> +    d = 10.2;
> +  }
> +  virtual float bar (float z);
> +};
> +
> +class A
> +{
> +public:
> +  int data;
> +  virtual int foo (int i);
> +};
> +
> +
> +class B : public Distraction, public A
> +{
> +public:
> +  virtual int foo (int i);
> +};
> +
> +float Distraction::bar (float z)
> +{
> +  f += z;
> +  return f/2;
> +}
> +
> +int __attribute__ ((noinline)) A::foo (int i)
> +{
> +  return i + 1;
> +}
> +
> +int __attribute__ ((noinline)) B::foo (int i)
> +{
> +  return i + 2;
> +}
> +
> +int __attribute__ ((noinline,noclone)) get_input(void)
> +{
> +  return 1;
> +}
> +
> +static inline int middleman_1 (class A *obj, int i)
> +{
> +  return obj->foo (i);
> +}
> +
> +static inline int middleman_2 (class B *obj, int i)
> +{
> +  return middleman_1 (obj, i);
> +}
> +
> +int main (int argc, char *argv[])
> +{
> +  class B b;
> +
> +  if (middleman_2 (&b, get_input ()) != 3)
> +    abort ();
> +  return 0;
> +}
> +
> +/* { dg-final { scan-tree-dump "= B::.*foo"  "optimized"  } } */
> +/* { dg-final { cleanup-tree-dump "optimized" } } */
> Index: icln/gcc/testsuite/g++.dg/ipa/imm-devirt-2.C
> ===================================================================
> --- /dev/null
> +++ icln/gcc/testsuite/g++.dg/ipa/imm-devirt-2.C
> @@ -0,0 +1,88 @@
> +/* Verify that virtual calls are folded even when a typecast to an
> +   ancestor is involved along the way.  */
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fdump-tree-optimized-slim"  } */
> +
> +extern "C" void abort (void);
> +
> +class Distraction
> +{
> +public:
> +  float f;
> +  double d;
> +  Distraction ()
> +  {
> +    f = 8.3;
> +    d = 10.2;
> +  }
> +  virtual float bar (float z);
> +};
> +
> +class A
> +{
> +public:
> +  int data;
> +  virtual int foo (int i);
> +};
> +
> +class A_2 : public A
> +{
> +public:
> +  int data_2;
> +  virtual int baz (int i);
> +};
> +
> +
> +class B : public Distraction, public A_2
> +{
> +public:
> +  virtual int foo (int i);
> +};
> +
> +float __attribute__ ((noinline)) Distraction::bar (float z)
> +{
> +  f += z;
> +  return f/2;
> +}
> +
> +int __attribute__ ((noinline)) A::foo (int i)
> +{
> +  return i + 1;
> +}
> +
> +int __attribute__ ((noinline)) A_2::baz (int i)
> +{
> +  return i * 15;
> +}
> +
> +int __attribute__ ((noinline)) B::foo (int i)
> +{
> +  return i + 2;
> +}
> +
> +int __attribute__ ((noinline,noclone)) get_input(void)
> +{
> +  return 1;
> +}
> +
> +static inline int middleman_1 (class A *obj, int i)
> +{
> +  return obj->foo (i);
> +}
> +
> +static inline int middleman_2 (class A *obj, int i)
> +{
> +  return middleman_1 (obj, i);
> +}
> +
> +int main (int argc, char *argv[])
> +{
> +  class B b;
> +
> +  if (middleman_2 (&b, get_input ()) != 3)
> +    abort ();
> +  return 0;
> +}
> +
> +/* { dg-final { scan-tree-dump "= B::.*foo"  "optimized"  } } */
> +/* { dg-final { cleanup-tree-dump "optimized" } } */
> Index: icln/gcc/testsuite/g++.dg/otr-fold-1.C
> ===================================================================
> --- icln.orig/gcc/testsuite/g++.dg/otr-fold-1.C
> +++ /dev/null
> @@ -1,76 +0,0 @@
> -/* Verify that virtual calls are folded even when a typecast to an
> -   ancestor is involved along the way.  */
> -/* { dg-do run } */
> -/* { dg-options "-O -fdump-tree-optimized-slim"  } */
> -
> -extern "C" void abort (void);
> -
> -class Distraction
> -{
> -public:
> -  float f;
> -  double d;
> -  Distraction ()
> -  {
> -    f = 8.3;
> -    d = 10.2;
> -  }
> -  virtual float bar (float z);
> -};
> -
> -class A
> -{
> -public:
> -  int data;
> -  virtual int foo (int i);
> -};
> -
> -
> -class B : public Distraction, public A
> -{
> -public:
> -  virtual int foo (int i);
> -};
> -
> -float Distraction::bar (float z)
> -{
> -  f += z;
> -  return f/2;
> -}
> -
> -int A::foo (int i)
> -{
> -  return i + 1;
> -}
> -
> -int B::foo (int i)
> -{
> -  return i + 2;
> -}
> -
> -int __attribute__ ((noinline,noclone)) get_input(void)
> -{
> -  return 1;
> -}
> -
> -static inline int middleman_1 (class A *obj, int i)
> -{
> -  return obj->foo (i);
> -}
> -
> -static inline int middleman_2 (class B *obj, int i)
> -{
> -  return middleman_1 (obj, i);
> -}
> -
> -int main (int argc, char *argv[])
> -{
> -  class B b;
> -
> -  if (middleman_2 (&b, get_input ()) != 3)
> -    abort ();
> -  return 0;
> -}
> -
> -/* { dg-final { scan-tree-dump "= B::.*foo"  "optimized"  } } */
> -/* { dg-final { cleanup-tree-dump "optimized" } } */
> Index: icln/gcc/testsuite/g++.dg/otr-fold-2.C
> ===================================================================
> --- icln.orig/gcc/testsuite/g++.dg/otr-fold-2.C
> +++ /dev/null
> @@ -1,88 +0,0 @@
> -/* Verify that virtual calls are folded even when a typecast to an
> -   ancestor is involved along the way.  */
> -/* { dg-do run } */
> -/* { dg-options "-O -fdump-tree-optimized-slim"  } */
> -
> -extern "C" void abort (void);
> -
> -class Distraction
> -{
> -public:
> -  float f;
> -  double d;
> -  Distraction ()
> -  {
> -    f = 8.3;
> -    d = 10.2;
> -  }
> -  virtual float bar (float z);
> -};
> -
> -class A
> -{
> -public:
> -  int data;
> -  virtual int foo (int i);
> -};
> -
> -class A_2 : public A
> -{
> -public:
> -  int data_2;
> -  virtual int baz (int i);
> -};
> -
> -
> -class B : public Distraction, public A_2
> -{
> -public:
> -  virtual int foo (int i);
> -};
> -
> -float Distraction::bar (float z)
> -{
> -  f += z;
> -  return f/2;
> -}
> -
> -int A::foo (int i)
> -{
> -  return i + 1;
> -}
> -
> -int A_2::baz (int i)
> -{
> -  return i * 15;
> -}
> -
> -int B::foo (int i)
> -{
> -  return i + 2;
> -}
> -
> -int __attribute__ ((noinline,noclone)) get_input(void)
> -{
> -  return 1;
> -}
> -
> -static inline int middleman_1 (class A *obj, int i)
> -{
> -  return obj->foo (i);
> -}
> -
> -static inline int middleman_2 (class A *obj, int i)
> -{
> -  return middleman_1 (obj, i);
> -}
> -
> -int main (int argc, char *argv[])
> -{
> -  class B b;
> -
> -  if (middleman_2 (&b, get_input ()) != 3)
> -    abort ();
> -  return 0;
> -}
> -
> -/* { dg-final { scan-tree-dump "= B::.*foo"  "optimized"  } } */
> -/* { dg-final { cleanup-tree-dump "optimized" } } */
> Index: icln/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
> ===================================================================
> --- icln.orig/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
> +++ icln/gcc/testsuite/g++.dg/tree-ssa/pr45605.C
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-O1 -fdump-tree-ssa" } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
>  extern "C" void abort(); 
>  bool destructor_called = false; 
>  
> @@ -33,5 +33,5 @@ int main() {
>  
>  
>  /* We should devirtualize call to D::Run */
> -/* { dg-final { scan-tree-dump-times "D::Run \\(" 1 "ssa"} } */
> -/* { dg-final { cleanup-tree-dump "ssa" } } */
> +/* { dg-final { scan-tree-dump-times "D::Run \\(" 1 "optimized"} } */
> +/* { dg-final { cleanup-tree-dump "optimized" } } */
> Index: icln/gcc/cgraphbuild.c
> ===================================================================
> --- icln.orig/gcc/cgraphbuild.c
> +++ icln/gcc/cgraphbuild.c
> @@ -33,6 +33,9 @@ along with GCC; see the file COPYING3.
>  #include "tree-pass.h"
>  #include "ipa-utils.h"
>  #include "except.h"
> +#include "ipa-prop.h"
> +
> +/* !!! Add ipa-prop.h to dependencies in Makefile.in */
>  
>  /* Context of record_reference.  */
>  struct record_reference_ctx
> @@ -429,12 +432,13 @@ record_references_in_initializer (tree d
>  /* Rebuild cgraph edges for current function node.  This needs to be run after
>     passes that don't update the cgraph.  */
>  
> -unsigned int
> -rebuild_cgraph_edges (void)
> +static unsigned int
> +rebuild_cgraph_edges_1 (bool devirtualize)
>  {
>    basic_block bb;
>    struct cgraph_node *node = cgraph_node (current_function_decl);
>    gimple_stmt_iterator gsi;
> +  bool cfg_changed = false;
>  
>    cgraph_node_remove_callees (node);
>    ipa_remove_all_references (&node->ref_list);
> @@ -453,6 +457,8 @@ rebuild_cgraph_edges (void)
>  	      int freq = compute_call_stmt_bb_frequency (current_function_decl,
>  							 bb);
>  	      decl = gimple_call_fndecl (stmt);
> +	      if (!decl && devirtualize)
> +		decl = ipa_try_devirtualize_immediately (&gsi, &cfg_changed);
>  	      if (decl)
>  		cgraph_create_edge (node, cgraph_node (decl), stmt,
>  				    bb->count, freq,
> @@ -474,7 +480,26 @@ rebuild_cgraph_edges (void)
>    record_eh_tables (node, cfun);
>    gcc_assert (!node->global.inlined_to);
>  
> -  return 0;
> +  if (cfg_changed)
> +    return TODO_cleanup_cfg;
> +  else
> +    return 0;
> +}
> +
> +/* !!!Doc */
> +
> +unsigned int
> +rebuild_cgraph_edges (void)
> +{
> + return rebuild_cgraph_edges_1 (false);
> +}
> +
> +/* !!!Doc */
> +
> +static unsigned int
> +rebuild_cgraph_edges_devirtualize (void)
> +{
> + return rebuild_cgraph_edges_1 (true);
>  }
>  
>  /* Rebuild cgraph edges for current function node.  This needs to be run after
> @@ -518,6 +543,25 @@ struct gimple_opt_pass pass_rebuild_cgra
>    NULL,					/* sub */
>    NULL,					/* next */
>    0,					/* static_pass_number */
> +  TV_CGRAPH,				/* tv_id */
> +  PROP_cfg,				/* properties_required */
> +  0,					/* properties_provided */
> +  0,					/* properties_destroyed */
> +  0,					/* todo_flags_start */
> +  0,					/* todo_flags_finish */
> + }
> +};
> +
> +struct gimple_opt_pass pass_rebuild_cgraph_edges_and_devirt =
> +{
> + {
> +  GIMPLE_PASS,
> +  "*rebuild_cgraph_edges_d",		/* name */
> +  NULL,					/* gate */
> +  rebuild_cgraph_edges_devirtualize,    /* execute */
> +  NULL,					/* sub */
> +  NULL,					/* next */
> +  0,					/* static_pass_number */
>    TV_CGRAPH,				/* tv_id */
>    PROP_cfg,				/* properties_required */
>    0,					/* properties_provided */
> Index: icln/gcc/ipa-prop.h
> ===================================================================
> --- icln.orig/gcc/ipa-prop.h
> +++ icln/gcc/ipa-prop.h
> @@ -429,6 +429,9 @@ void ipa_initialize_node_params (struct
>  bool ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
>  					VEC (cgraph_edge_p, heap) **new_edges);
>  
> +/* Intraprocedural devirtualization. */
> +tree ipa_try_devirtualize_immediately (gimple_stmt_iterator *, bool *);
> +
>  /* Indirect edge and binfo processing.  */
>  struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree,
>  						    tree);
> Index: icln/gcc/passes.c
> ===================================================================
> --- icln.orig/gcc/passes.c
> +++ icln/gcc/passes.c
> @@ -791,7 +791,7 @@ init_optimization_passes (void)
>            NEXT_PASS (pass_split_functions);
>  	}
>        NEXT_PASS (pass_release_ssa_names);
> -      NEXT_PASS (pass_rebuild_cgraph_edges);
> +      NEXT_PASS (pass_rebuild_cgraph_edges_and_devirt);
>        NEXT_PASS (pass_inline_parameters);
>      }
>    NEXT_PASS (pass_ipa_tree_profile);
> Index: icln/gcc/tree-pass.h
> ===================================================================
> --- icln.orig/gcc/tree-pass.h
> +++ icln/gcc/tree-pass.h
> @@ -436,6 +436,7 @@ extern struct gimple_opt_pass pass_uncpr
>  extern struct gimple_opt_pass pass_return_slot;
>  extern struct gimple_opt_pass pass_reassoc;
>  extern struct gimple_opt_pass pass_rebuild_cgraph_edges;
> +extern struct gimple_opt_pass pass_rebuild_cgraph_edges_and_devirt;
>  extern struct gimple_opt_pass pass_remove_cgraph_callee_edges;
>  extern struct gimple_opt_pass pass_build_cgraph_edges;
>  extern struct gimple_opt_pass pass_local_pure_const;
> 
> 

-- 
Richard Guenther <rguenther@suse.de>
Novell / SUSE Labs
SUSE LINUX Products GmbH - Nuernberg - AG Nuernberg - HRB 16746 - GF: Markus Rex



More information about the Gcc-patches mailing list