diff mbox

[2/4] Handle calls to ancestor objects in IPA-CP devirtualization

Message ID 20110415125645.359939786@virgil.suse.cz
State New
Headers show

Commit Message

Martin Jambor April 15, 2011, 12:56 p.m. UTC
Hi,

early inlining can create virtual calls based on the part of an object
that represents an ancestor.  This patch makes ipa-prop analysis able
to recognize such calls and store the required offset along with such
calls (the field is already there for similar purposes of indirect
inlining).  The constant propagation is then made aware of the offset
field and takes it into account when looking up the proper BINFO.

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

Thanks,

Martin



2011-04-13  Martin Jambor  <mjambor@suse.cz>

	* ipa-cp.c (ipcp_process_devirtualization_opportunities): Take into
	account anc_offset and otr_type from the indirect edge info.
	* ipa-prop.c (get_ancestor_addr_info): New function.
	(compute_complex_ancestor_jump_func): Assignment analysis moved to
	get_ancestor_addr_info, call it.
	(ipa_note_param_call): Do not initialize information about polymorphic
	calls, return the indirect call graph edge.  Remove the last
	parameter, adjust all callers.
	(ipa_analyze_virtual_call_uses): Process also calls to ancestors of
	parameters.  Initialize polymorphic information in the indirect edge.

	* testsuite/g++.dg/ipa/devirt-7.C: New test.

Comments

Richard Biener April 15, 2011, 3:26 p.m. UTC | #1
On Fri, 15 Apr 2011, Martin Jambor wrote:

> Hi,
> 
> early inlining can create virtual calls based on the part of an object
> that represents an ancestor.  This patch makes ipa-prop analysis able
> to recognize such calls and store the required offset along with such
> calls (the field is already there for similar purposes of indirect
> inlining).  The constant propagation is then made aware of the offset
> field and takes it into account when looking up the proper BINFO.
> 
> Bootstrapped and tested on x86_64-linux. OK for trunk?
> 
> Thanks,
> 
> Martin
> 
> 
> 
> 2011-04-13  Martin Jambor  <mjambor@suse.cz>
> 
> 	* ipa-cp.c (ipcp_process_devirtualization_opportunities): Take into
> 	account anc_offset and otr_type from the indirect edge info.
> 	* ipa-prop.c (get_ancestor_addr_info): New function.
> 	(compute_complex_ancestor_jump_func): Assignment analysis moved to
> 	get_ancestor_addr_info, call it.
> 	(ipa_note_param_call): Do not initialize information about polymorphic
> 	calls, return the indirect call graph edge.  Remove the last
> 	parameter, adjust all callers.
> 	(ipa_analyze_virtual_call_uses): Process also calls to ancestors of
> 	parameters.  Initialize polymorphic information in the indirect edge.
> 
> 	* testsuite/g++.dg/ipa/devirt-7.C: New test.
> 
> 
> Index: src/gcc/ipa-cp.c
> ===================================================================
> --- src.orig/gcc/ipa-cp.c
> +++ src/gcc/ipa-cp.c
> @@ -1246,8 +1246,8 @@ ipcp_process_devirtualization_opportunit
>    for (ie = node->indirect_calls; ie; ie = next_ie)
>      {
>        int param_index, types_count, j;
> -      HOST_WIDE_INT token;
> -      tree target, delta;
> +      HOST_WIDE_INT token, anc_offset;
> +      tree target, delta, otr_type;
>  
>        next_ie = ie->next_callee;
>        if (!ie->indirect_info->polymorphic)
> @@ -1259,14 +1259,23 @@ ipcp_process_devirtualization_opportunit
>  	continue;
>  
>        token = ie->indirect_info->otr_token;
> +      anc_offset = ie->indirect_info->anc_offset;
> +      otr_type = ie->indirect_info->otr_type;
>        target = NULL_TREE;
>        types_count = VEC_length (tree, info->params[param_index].types);
>        for (j = 0; j < types_count; j++)
>  	{
>  	  tree binfo = VEC_index (tree, info->params[param_index].types, j);
> -	  tree d;
> -	  tree t = gimple_get_virt_mehtod_for_binfo (token, binfo, &d, true);
> +	  tree d, t;
>  
> +	  binfo = get_binfo_at_offset (binfo, anc_offset, otr_type);
> +	  if (!binfo)
> +	    {
> +	      target = NULL_TREE;
> +	      break;
> +	    }
> +
> +	  t = gimple_get_virt_mehtod_for_binfo (token, binfo, &d, true);
>  	  if (!t)
>  	    {
>  	      target = NULL_TREE;
> Index: src/gcc/ipa-prop.c
> ===================================================================
> --- src.orig/gcc/ipa-prop.c
> +++ src/gcc/ipa-prop.c
> @@ -576,6 +576,49 @@ compute_complex_assign_jump_func (struct
>      }
>  }
>  
> +/* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
> +   it looks like:
> +
> +   iftmp.1_3 = &obj_2(D)->D.1762;
> +
> +   The base of the MEM_REF must be a default definition SSA NAME of a
> +   parameter.  Return NULL_TREE if it looks otherwise.  If case of success, the
> +   whole MEM_REF expression is returned and the offset calculated from any
> +   handled components and the MEM_REF itself is stored into *OFFSET.  The whole
> +   RHS stripped off the ADDR_EXPR is stored into *OBJ_P.  */
> +
> +static tree
> +get_ancestor_addr_info (gimple assign, tree *obj_p, HOST_WIDE_INT *offset)
> +{
> +  HOST_WIDE_INT size, max_size;
> +  tree expr, parm, obj;
> +
> +  if (!gimple_assign_single_p (assign))
> +    return NULL_TREE;
> +  expr = gimple_assign_rhs1 (assign);
> +
> +  if (TREE_CODE (expr) != ADDR_EXPR)
> +    return NULL_TREE;
> +  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 NULL_TREE;
> +  parm = TREE_OPERAND (expr, 0);
> +  if (TREE_CODE (parm) != SSA_NAME
> +      || !SSA_NAME_IS_DEFAULT_DEF (parm)

Might be an uninitialized variable, so also check
TREE_CODE (SSA_NAME_VAR (parm)) == PARM_DECL?

> +      || *offset < 0)

Check this above where you check max_size/size.

> +    return NULL_TREE;
> +
> +  *offset += mem_ref_offset (expr).low * BITS_PER_UNIT;

At some point it might be worth switching to
get_addr_base_and_unit_offsets and not use bit but unit offsets
throughout the code.

> +  *obj_p = obj;
> +  return expr;
> +}
> +
>  
>  /* Given that an actual argument is an SSA_NAME that is a result of a phi
>     statement PHI, try to find out whether NAME is in fact a
> @@ -603,7 +646,7 @@ 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;
> @@ -626,29 +669,12 @@ compute_complex_ancestor_jump_func (stru
>  
>    assign = SSA_NAME_DEF_STMT (tmp);
>    assign_bb = gimple_bb (assign);
> -  if (!single_pred_p (assign_bb)
> -      || !gimple_assign_single_p (assign))
> +  if (!single_pred_p (assign_bb))
>      return;
> -  expr = gimple_assign_rhs1 (assign);
> -
> -  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)
> +  expr = get_ancestor_addr_info (assign, &obj, &offset);
> +  if (!expr)
>      return;
> -  offset += mem_ref_offset (expr).low * BITS_PER_UNIT;
>    parm = TREE_OPERAND (expr, 0);
> -  if (TREE_CODE (parm) != SSA_NAME
> -      || !SSA_NAME_IS_DEFAULT_DEF (parm)
> -      || offset < 0)
> -    return;
> -
>    index = ipa_get_param_decl_index (info, SSA_NAME_VAR (parm));
>    if (index < 0)
>      return;
> @@ -675,7 +701,7 @@ compute_complex_ancestor_jump_func (stru
>        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 (obj);
>      }
>  }
>  
> @@ -1162,29 +1188,20 @@ ipa_is_ssa_with_stmt_def (tree t)
>      return false;
>  }
>  
> -/* Find the indirect call graph edge corresponding to STMT and add to it all
> -   information necessary to describe a call to a parameter number PARAM_INDEX.
> -   NODE is the caller.  POLYMORPHIC should be set to true iff the call is a
> -   virtual one.  */
> +/* Find the indirect call graph edge corresponding to STMT and mark it as a
> +   call to a parameter number PARAM_INDEX.  NODE is the caller.  Return the
> +   indirect call graph edge.  */
>  
> -static void
> -ipa_note_param_call (struct cgraph_node *node, int param_index, gimple stmt,
> -		     bool polymorphic)
> +static struct cgraph_edge *
> +ipa_note_param_call (struct cgraph_node *node, int param_index, gimple stmt)
>  {
>    struct cgraph_edge *cs;
>  
>    cs = cgraph_edge (node, stmt);
>    cs->indirect_info->param_index = param_index;
>    cs->indirect_info->anc_offset = 0;
> -  cs->indirect_info->polymorphic = polymorphic;
> -  if (polymorphic)
> -    {
> -      tree otr = gimple_call_fn (stmt);
> -      tree type, token = OBJ_TYPE_REF_TOKEN (otr);
> -      cs->indirect_info->otr_token = tree_low_cst (token, 1);
> -      type = TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (otr)));
> -      cs->indirect_info->otr_type = type;
> -    }
> +  cs->indirect_info->polymorphic = 0;
> +  return cs;
>  }
>  
>  /* Analyze the CALL and examine uses of formal parameters of the caller NODE
> @@ -1263,7 +1280,7 @@ ipa_analyze_indirect_call_uses (struct c
>        tree var = SSA_NAME_VAR (target);
>        index = ipa_get_param_decl_index (info, var);
>        if (index >= 0)
> -	ipa_note_param_call (node, index, call, false);
> +	ipa_note_param_call (node, index, call);
>        return;
>      }
>  
> @@ -1361,7 +1378,7 @@ ipa_analyze_indirect_call_uses (struct c
>    index = ipa_get_param_decl_index (info, rec);
>    if (index >= 0 && !is_parm_modified_before_call (&parms_info[index],
>  						   call, rec))
> -    ipa_note_param_call (node, index, call, false);
> +    ipa_note_param_call (node, index, call);
>  
>    return;
>  }
> @@ -1375,24 +1392,48 @@ ipa_analyze_virtual_call_uses (struct cg
>  			       struct ipa_node_params *info, gimple call,
>  			       tree target)
>  {
> +  struct cgraph_edge *cs;
> +  struct cgraph_indirect_call_info *ii;
>    struct ipa_jump_func jfunc;
>    tree obj = OBJ_TYPE_REF_OBJECT (target);
> -  tree var;
>    int index;
> +  HOST_WIDE_INT anc_offset;
>  
>    if (!flag_devirtualize)
>      return;
>  
> -  if (TREE_CODE (obj) != SSA_NAME
> -      || !SSA_NAME_IS_DEFAULT_DEF (obj))
> +  if (TREE_CODE (obj) != SSA_NAME)
>      return;
>  
> -  var = SSA_NAME_VAR (obj);
> -  index = ipa_get_param_decl_index (info, var);
> +  if (SSA_NAME_IS_DEFAULT_DEF (obj))

Check for PARM_DECL.

Otherwise ok.

Thanks,
Richard.

> +    {
> +      anc_offset = 0;
> +      index = ipa_get_param_decl_index (info, SSA_NAME_VAR (obj));
> +      if (index < 0
> +	  || detect_type_change_ssa (obj, call, &jfunc))
> +	return;
> +    }
> +  else
> +    {
> +      gimple stmt = SSA_NAME_DEF_STMT (obj);
> +      tree expr;
> +
> +      expr = get_ancestor_addr_info (stmt, &obj, &anc_offset);
> +      if (!expr)
> +	return;
> +      index = ipa_get_param_decl_index (info,
> +					SSA_NAME_VAR (TREE_OPERAND (expr, 0)));
> +      if (index < 0
> +	  || detect_type_change (obj, expr, call, &jfunc, anc_offset))
> +	return;
> +    }
>  
> -  if (index >= 0
> -      && !detect_type_change_ssa (obj, call, &jfunc))
> -    ipa_note_param_call (node, index, call, true);
> +  cs = ipa_note_param_call (node, index, call);
> +  ii = cs->indirect_info;
> +  ii->anc_offset = anc_offset;
> +  ii->otr_token = tree_low_cst (OBJ_TYPE_REF_TOKEN (target), 1);
> +  ii->otr_type = TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (target)));
> +  ii->polymorphic = 1;
>  }
>  
>  /* Analyze a call statement CALL whether and how it utilizes formal parameters
> Index: src/gcc/testsuite/g++.dg/ipa/devirt-7.C
> ===================================================================
> --- /dev/null
> +++ src/gcc/testsuite/g++.dg/ipa/devirt-7.C
> @@ -0,0 +1,87 @@
> +/* Verify that IPA-CP can do devirtualization even if the virtual call
> +   comes from a method that has been early-inlined into a descendant.  */
> +/* { dg-do run } */
> +/* { dg-options "-O3 -fdump-ipa-cp"  } */
> +
> +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);
> +  int middleman_1 (int i);
> +};
> +
> +
> +class B : public Distraction, public A
> +{
> +public:
> +  virtual int foo (int i);
> +  int middleman_2 (int i);
> +  __attribute__ ((noinline)) B();
> +};
> +
> +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;
> +}
> +
> +int __attribute__ ((always_inline))
> +A::middleman_1 (int i)
> +{
> +  return this->foo (i);
> +}
> +
> +int __attribute__ ((noinline))
> +B::middleman_2 (int i)
> +{
> +  return this->middleman_1 (i);
> +}
> +
> +B::B ()
> +{
> +}
> +
> +int main (int argc, char *argv[])
> +{
> +  class B b;
> +  int i;
> +
> +  for (i = 0; i < get_input(); i++)
> +    if (b.middleman_2 (get_input ()) != 3)
> +      abort ();
> +  return 0;
> +}
> +
> +/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo"  "cp"  } } */
> +/* { dg-final { cleanup-ipa-dump "cp" } } */
> 
>
Martin Jambor April 18, 2011, 3:41 p.m. UTC | #2
Hi,

On Fri, Apr 15, 2011 at 05:26:44PM +0200, Richard Guenther wrote:
> On Fri, 15 Apr 2011, Martin Jambor wrote:
> 
> > Hi,
> > 
> > early inlining can create virtual calls based on the part of an object
> > that represents an ancestor.  This patch makes ipa-prop analysis able
> > to recognize such calls and store the required offset along with such
> > calls (the field is already there for similar purposes of indirect
> > inlining).  The constant propagation is then made aware of the offset
> > field and takes it into account when looking up the proper BINFO.
> > 
> > Bootstrapped and tested on x86_64-linux. OK for trunk?
> > 
> > Thanks,
> > 
> > Martin
> > 
> > 
> > 
> > 2011-04-13  Martin Jambor  <mjambor@suse.cz>
> > 
> > 	* ipa-cp.c (ipcp_process_devirtualization_opportunities): Take into
> > 	account anc_offset and otr_type from the indirect edge info.
> > 	* ipa-prop.c (get_ancestor_addr_info): New function.
> > 	(compute_complex_ancestor_jump_func): Assignment analysis moved to
> > 	get_ancestor_addr_info, call it.
> > 	(ipa_note_param_call): Do not initialize information about polymorphic
> > 	calls, return the indirect call graph edge.  Remove the last
> > 	parameter, adjust all callers.
> > 	(ipa_analyze_virtual_call_uses): Process also calls to ancestors of
> > 	parameters.  Initialize polymorphic information in the indirect edge.
> > 
> > 	* testsuite/g++.dg/ipa/devirt-7.C: New test.
> > 
> > 

...

> > Index: src/gcc/ipa-prop.c
> > ===================================================================
> > --- src.orig/gcc/ipa-prop.c
> > +++ src/gcc/ipa-prop.c
> > @@ -576,6 +576,49 @@ compute_complex_assign_jump_func (struct
> >      }
> >  }
> >  
> > +/* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
> > +   it looks like:
> > +
> > +   iftmp.1_3 = &obj_2(D)->D.1762;
> > +
> > +   The base of the MEM_REF must be a default definition SSA NAME of a
> > +   parameter.  Return NULL_TREE if it looks otherwise.  If case of success, the
> > +   whole MEM_REF expression is returned and the offset calculated from any
> > +   handled components and the MEM_REF itself is stored into *OFFSET.  The whole
> > +   RHS stripped off the ADDR_EXPR is stored into *OBJ_P.  */
> > +
> > +static tree
> > +get_ancestor_addr_info (gimple assign, tree *obj_p, HOST_WIDE_INT *offset)
> > +{
> > +  HOST_WIDE_INT size, max_size;
> > +  tree expr, parm, obj;
> > +
> > +  if (!gimple_assign_single_p (assign))
> > +    return NULL_TREE;
> > +  expr = gimple_assign_rhs1 (assign);
> > +
> > +  if (TREE_CODE (expr) != ADDR_EXPR)
> > +    return NULL_TREE;
> > +  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 NULL_TREE;
> > +  parm = TREE_OPERAND (expr, 0);
> > +  if (TREE_CODE (parm) != SSA_NAME
> > +      || !SSA_NAME_IS_DEFAULT_DEF (parm)
> 
> Might be an uninitialized variable, so also check
> TREE_CODE (SSA_NAME_VAR (parm)) == PARM_DECL?
> 

This was already handled by checking the return value of
ipa_get_param_decl_index but I guess that the code will be more
readable with that check explicit and an assert that
ipa_get_param_decl_index can indeed find an index for the decl so I
changed both places accordingly.

> > +      || *offset < 0)
> 
> Check this above where you check max_size/size.

OK

...

> > -  var = SSA_NAME_VAR (obj);
> > -  index = ipa_get_param_decl_index (info, var);
> > +  if (SSA_NAME_IS_DEFAULT_DEF (obj))
> 
> Check for PARM_DECL.
> 

Like above.

> Otherwise ok.
> 

Thanks, this is what I'm currently testing and what I intend to commit
if all goes well.

Martin


2011-04-18  Martin Jambor  <mjambor@suse.cz>

	* ipa-cp.c (ipcp_process_devirtualization_opportunities): Take into
	account anc_offset and otr_type from the indirect edge info.
	* ipa-prop.c (get_ancestor_addr_info): New function.
	(compute_complex_ancestor_jump_func): Assignment analysis moved to
	get_ancestor_addr_info, call it.
	(ipa_note_param_call): Do not initialize information about polymorphic
	calls, return the indirect call graph edge.  Remove the last
	parameter, adjust all callers.
	(ipa_analyze_virtual_call_uses): Process also calls to ancestors of
	parameters.  Initialize polymorphic information in the indirect edge.

	* testsuite/g++.dg/ipa/devirt-7.C: New test.


Index: src/gcc/ipa-cp.c
===================================================================
*** src.orig/gcc/ipa-cp.c
--- src/gcc/ipa-cp.c
*************** ipcp_process_devirtualization_opportunit
*** 1247,1254 ****
    for (ie = node->indirect_calls; ie; ie = next_ie)
      {
        int param_index, types_count, j;
!       HOST_WIDE_INT token;
!       tree target, delta;
  
        next_ie = ie->next_callee;
        if (!ie->indirect_info->polymorphic)
--- 1247,1254 ----
    for (ie = node->indirect_calls; ie; ie = next_ie)
      {
        int param_index, types_count, j;
!       HOST_WIDE_INT token, anc_offset;
!       tree target, delta, otr_type;
  
        next_ie = ie->next_callee;
        if (!ie->indirect_info->polymorphic)
*************** ipcp_process_devirtualization_opportunit
*** 1260,1273 ****
  	continue;
  
        token = ie->indirect_info->otr_token;
        target = NULL_TREE;
        types_count = VEC_length (tree, info->params[param_index].types);
        for (j = 0; j < types_count; j++)
  	{
  	  tree binfo = VEC_index (tree, info->params[param_index].types, j);
! 	  tree d;
! 	  tree t = gimple_get_virt_mehtod_for_binfo (token, binfo, &d, true);
  
  	  if (!t)
  	    {
  	      target = NULL_TREE;
--- 1260,1282 ----
  	continue;
  
        token = ie->indirect_info->otr_token;
+       anc_offset = ie->indirect_info->anc_offset;
+       otr_type = ie->indirect_info->otr_type;
        target = NULL_TREE;
        types_count = VEC_length (tree, info->params[param_index].types);
        for (j = 0; j < types_count; j++)
  	{
  	  tree binfo = VEC_index (tree, info->params[param_index].types, j);
! 	  tree d, t;
  
+ 	  binfo = get_binfo_at_offset (binfo, anc_offset, otr_type);
+ 	  if (!binfo)
+ 	    {
+ 	      target = NULL_TREE;
+ 	      break;
+ 	    }
+ 
+ 	  t = gimple_get_virt_mehtod_for_binfo (token, binfo, &d, true);
  	  if (!t)
  	    {
  	      target = NULL_TREE;
Index: src/gcc/ipa-prop.c
===================================================================
*** src.orig/gcc/ipa-prop.c
--- src/gcc/ipa-prop.c
*************** compute_complex_assign_jump_func (struct
*** 576,581 ****
--- 576,625 ----
      }
  }
  
+ /* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
+    it looks like:
+ 
+    iftmp.1_3 = &obj_2(D)->D.1762;
+ 
+    The base of the MEM_REF must be a default definition SSA NAME of a
+    parameter.  Return NULL_TREE if it looks otherwise.  If case of success, the
+    whole MEM_REF expression is returned and the offset calculated from any
+    handled components and the MEM_REF itself is stored into *OFFSET.  The whole
+    RHS stripped off the ADDR_EXPR is stored into *OBJ_P.  */
+ 
+ static tree
+ get_ancestor_addr_info (gimple assign, tree *obj_p, HOST_WIDE_INT *offset)
+ {
+   HOST_WIDE_INT size, max_size;
+   tree expr, parm, obj;
+ 
+   if (!gimple_assign_single_p (assign))
+     return NULL_TREE;
+   expr = gimple_assign_rhs1 (assign);
+ 
+   if (TREE_CODE (expr) != ADDR_EXPR)
+     return NULL_TREE;
+   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
+       || *offset < 0)
+     return NULL_TREE;
+   parm = TREE_OPERAND (expr, 0);
+   if (TREE_CODE (parm) != SSA_NAME
+       || !SSA_NAME_IS_DEFAULT_DEF (parm)
+       || TREE_CODE (SSA_NAME_VAR (parm)) != PARM_DECL)
+     return NULL_TREE;
+ 
+   *offset += mem_ref_offset (expr).low * BITS_PER_UNIT;
+   *obj_p = obj;
+   return expr;
+ }
+ 
  
  /* Given that an actual argument is an SSA_NAME that is a result of a phi
     statement PHI, try to find out whether NAME is in fact a
*************** compute_complex_ancestor_jump_func (stru
*** 603,609 ****
  				    struct ipa_jump_func *jfunc,
  				    gimple call, gimple phi)
  {
!   HOST_WIDE_INT offset, size, max_size;
    gimple assign, cond;
    basic_block phi_bb, assign_bb, cond_bb;
    tree tmp, parm, expr, obj;
--- 647,653 ----
  				    struct ipa_jump_func *jfunc,
  				    gimple call, gimple phi)
  {
!   HOST_WIDE_INT offset;
    gimple assign, cond;
    basic_block phi_bb, assign_bb, cond_bb;
    tree tmp, parm, expr, obj;
*************** compute_complex_ancestor_jump_func (stru
*** 626,657 ****
  
    assign = SSA_NAME_DEF_STMT (tmp);
    assign_bb = gimple_bb (assign);
!   if (!single_pred_p (assign_bb)
!       || !gimple_assign_single_p (assign))
      return;
!   expr = gimple_assign_rhs1 (assign);
! 
!   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
-       || !SSA_NAME_IS_DEFAULT_DEF (parm)
-       || offset < 0)
-     return;
- 
    index = ipa_get_param_decl_index (info, SSA_NAME_VAR (parm));
!   if (index < 0)
!     return;
  
    cond_bb = single_pred (assign_bb);
    cond = last_stmt (cond_bb);
--- 670,683 ----
  
    assign = SSA_NAME_DEF_STMT (tmp);
    assign_bb = gimple_bb (assign);
!   if (!single_pred_p (assign_bb))
      return;
!   expr = get_ancestor_addr_info (assign, &obj, &offset);
!   if (!expr)
      return;
    parm = TREE_OPERAND (expr, 0);
    index = ipa_get_param_decl_index (info, SSA_NAME_VAR (parm));
!   gcc_assert (index >= 0);
  
    cond_bb = single_pred (assign_bb);
    cond = last_stmt (cond_bb);
*************** compute_complex_ancestor_jump_func (stru
*** 675,681 ****
        jfunc->type = IPA_JF_ANCESTOR;
        jfunc->value.ancestor.formal_id = index;
        jfunc->value.ancestor.offset = offset;
!       jfunc->value.ancestor.type = TREE_TYPE (obj);;
      }
  }
  
--- 701,707 ----
        jfunc->type = IPA_JF_ANCESTOR;
        jfunc->value.ancestor.formal_id = index;
        jfunc->value.ancestor.offset = offset;
!       jfunc->value.ancestor.type = TREE_TYPE (obj);
      }
  }
  
*************** ipa_is_ssa_with_stmt_def (tree t)
*** 1162,1190 ****
      return false;
  }
  
! /* Find the indirect call graph edge corresponding to STMT and add to it all
!    information necessary to describe a call to a parameter number PARAM_INDEX.
!    NODE is the caller.  POLYMORPHIC should be set to true iff the call is a
!    virtual one.  */
  
! static void
! ipa_note_param_call (struct cgraph_node *node, int param_index, gimple stmt,
! 		     bool polymorphic)
  {
    struct cgraph_edge *cs;
  
    cs = cgraph_edge (node, stmt);
    cs->indirect_info->param_index = param_index;
    cs->indirect_info->anc_offset = 0;
!   cs->indirect_info->polymorphic = polymorphic;
!   if (polymorphic)
!     {
!       tree otr = gimple_call_fn (stmt);
!       tree type, token = OBJ_TYPE_REF_TOKEN (otr);
!       cs->indirect_info->otr_token = tree_low_cst (token, 1);
!       type = TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (otr)));
!       cs->indirect_info->otr_type = type;
!     }
  }
  
  /* Analyze the CALL and examine uses of formal parameters of the caller NODE
--- 1188,1207 ----
      return false;
  }
  
! /* Find the indirect call graph edge corresponding to STMT and mark it as a
!    call to a parameter number PARAM_INDEX.  NODE is the caller.  Return the
!    indirect call graph edge.  */
  
! static struct cgraph_edge *
! ipa_note_param_call (struct cgraph_node *node, int param_index, gimple stmt)
  {
    struct cgraph_edge *cs;
  
    cs = cgraph_edge (node, stmt);
    cs->indirect_info->param_index = param_index;
    cs->indirect_info->anc_offset = 0;
!   cs->indirect_info->polymorphic = 0;
!   return cs;
  }
  
  /* Analyze the CALL and examine uses of formal parameters of the caller NODE
*************** ipa_analyze_indirect_call_uses (struct c
*** 1263,1269 ****
        tree var = SSA_NAME_VAR (target);
        index = ipa_get_param_decl_index (info, var);
        if (index >= 0)
! 	ipa_note_param_call (node, index, call, false);
        return;
      }
  
--- 1280,1286 ----
        tree var = SSA_NAME_VAR (target);
        index = ipa_get_param_decl_index (info, var);
        if (index >= 0)
! 	ipa_note_param_call (node, index, call);
        return;
      }
  
*************** ipa_analyze_indirect_call_uses (struct c
*** 1361,1367 ****
    index = ipa_get_param_decl_index (info, rec);
    if (index >= 0 && !is_parm_modified_before_call (&parms_info[index],
  						   call, rec))
!     ipa_note_param_call (node, index, call, false);
  
    return;
  }
--- 1378,1384 ----
    index = ipa_get_param_decl_index (info, rec);
    if (index >= 0 && !is_parm_modified_before_call (&parms_info[index],
  						   call, rec))
!     ipa_note_param_call (node, index, call);
  
    return;
  }
*************** ipa_analyze_virtual_call_uses (struct cg
*** 1375,1398 ****
  			       struct ipa_node_params *info, gimple call,
  			       tree target)
  {
    struct ipa_jump_func jfunc;
    tree obj = OBJ_TYPE_REF_OBJECT (target);
-   tree var;
    int index;
  
    if (!flag_devirtualize)
      return;
  
!   if (TREE_CODE (obj) != SSA_NAME
!       || !SSA_NAME_IS_DEFAULT_DEF (obj))
      return;
  
!   var = SSA_NAME_VAR (obj);
!   index = ipa_get_param_decl_index (info, var);
  
!   if (index >= 0
!       && !detect_type_change_ssa (obj, call, &jfunc))
!     ipa_note_param_call (node, index, call, true);
  }
  
  /* Analyze a call statement CALL whether and how it utilizes formal parameters
--- 1392,1442 ----
  			       struct ipa_node_params *info, gimple call,
  			       tree target)
  {
+   struct cgraph_edge *cs;
+   struct cgraph_indirect_call_info *ii;
    struct ipa_jump_func jfunc;
    tree obj = OBJ_TYPE_REF_OBJECT (target);
    int index;
+   HOST_WIDE_INT anc_offset;
  
    if (!flag_devirtualize)
      return;
  
!   if (TREE_CODE (obj) != SSA_NAME)
      return;
  
!   if (SSA_NAME_IS_DEFAULT_DEF (obj))
!     {
!       if (TREE_CODE (SSA_NAME_VAR (obj)) != PARM_DECL)
! 	return;
! 
!       anc_offset = 0;
!       index = ipa_get_param_decl_index (info, SSA_NAME_VAR (obj));
!       gcc_assert (index >= 0);
!       if (detect_type_change_ssa (obj, call, &jfunc))
! 	return;
!     }
!   else
!     {
!       gimple stmt = SSA_NAME_DEF_STMT (obj);
!       tree expr;
! 
!       expr = get_ancestor_addr_info (stmt, &obj, &anc_offset);
!       if (!expr)
! 	return;
!       index = ipa_get_param_decl_index (info,
! 					SSA_NAME_VAR (TREE_OPERAND (expr, 0)));
!       gcc_assert (index >= 0);
!       if (detect_type_change (obj, expr, call, &jfunc, anc_offset))
! 	return;
!     }
  
!   cs = ipa_note_param_call (node, index, call);
!   ii = cs->indirect_info;
!   ii->anc_offset = anc_offset;
!   ii->otr_token = tree_low_cst (OBJ_TYPE_REF_TOKEN (target), 1);
!   ii->otr_type = TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (target)));
!   ii->polymorphic = 1;
  }
  
  /* Analyze a call statement CALL whether and how it utilizes formal parameters
Index: src/gcc/testsuite/g++.dg/ipa/devirt-7.C
===================================================================
*** /dev/null
--- src/gcc/testsuite/g++.dg/ipa/devirt-7.C
***************
*** 0 ****
--- 1,87 ----
+ /* Verify that IPA-CP can do devirtualization even if the virtual call
+    comes from a method that has been early-inlined into a descendant.  */
+ /* { dg-do run } */
+ /* { dg-options "-O3 -fdump-ipa-cp"  } */
+ 
+ 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);
+   int middleman_1 (int i);
+ };
+ 
+ 
+ class B : public Distraction, public A
+ {
+ public:
+   virtual int foo (int i);
+   int middleman_2 (int i);
+   __attribute__ ((noinline)) B();
+ };
+ 
+ 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;
+ }
+ 
+ int __attribute__ ((always_inline))
+ A::middleman_1 (int i)
+ {
+   return this->foo (i);
+ }
+ 
+ int __attribute__ ((noinline))
+ B::middleman_2 (int i)
+ {
+   return this->middleman_1 (i);
+ }
+ 
+ B::B ()
+ {
+ }
+ 
+ int main (int argc, char *argv[])
+ {
+   class B b;
+   int i;
+ 
+   for (i = 0; i < get_input(); i++)
+     if (b.middleman_2 (get_input ()) != 3)
+       abort ();
+   return 0;
+ }
+ 
+ /* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo"  "cp"  } } */
+ /* { dg-final { cleanup-ipa-dump "cp" } } */
diff mbox

Patch

Index: src/gcc/ipa-cp.c
===================================================================
--- src.orig/gcc/ipa-cp.c
+++ src/gcc/ipa-cp.c
@@ -1246,8 +1246,8 @@  ipcp_process_devirtualization_opportunit
   for (ie = node->indirect_calls; ie; ie = next_ie)
     {
       int param_index, types_count, j;
-      HOST_WIDE_INT token;
-      tree target, delta;
+      HOST_WIDE_INT token, anc_offset;
+      tree target, delta, otr_type;
 
       next_ie = ie->next_callee;
       if (!ie->indirect_info->polymorphic)
@@ -1259,14 +1259,23 @@  ipcp_process_devirtualization_opportunit
 	continue;
 
       token = ie->indirect_info->otr_token;
+      anc_offset = ie->indirect_info->anc_offset;
+      otr_type = ie->indirect_info->otr_type;
       target = NULL_TREE;
       types_count = VEC_length (tree, info->params[param_index].types);
       for (j = 0; j < types_count; j++)
 	{
 	  tree binfo = VEC_index (tree, info->params[param_index].types, j);
-	  tree d;
-	  tree t = gimple_get_virt_mehtod_for_binfo (token, binfo, &d, true);
+	  tree d, t;
 
+	  binfo = get_binfo_at_offset (binfo, anc_offset, otr_type);
+	  if (!binfo)
+	    {
+	      target = NULL_TREE;
+	      break;
+	    }
+
+	  t = gimple_get_virt_mehtod_for_binfo (token, binfo, &d, true);
 	  if (!t)
 	    {
 	      target = NULL_TREE;
Index: src/gcc/ipa-prop.c
===================================================================
--- src.orig/gcc/ipa-prop.c
+++ src/gcc/ipa-prop.c
@@ -576,6 +576,49 @@  compute_complex_assign_jump_func (struct
     }
 }
 
+/* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
+   it looks like:
+
+   iftmp.1_3 = &obj_2(D)->D.1762;
+
+   The base of the MEM_REF must be a default definition SSA NAME of a
+   parameter.  Return NULL_TREE if it looks otherwise.  If case of success, the
+   whole MEM_REF expression is returned and the offset calculated from any
+   handled components and the MEM_REF itself is stored into *OFFSET.  The whole
+   RHS stripped off the ADDR_EXPR is stored into *OBJ_P.  */
+
+static tree
+get_ancestor_addr_info (gimple assign, tree *obj_p, HOST_WIDE_INT *offset)
+{
+  HOST_WIDE_INT size, max_size;
+  tree expr, parm, obj;
+
+  if (!gimple_assign_single_p (assign))
+    return NULL_TREE;
+  expr = gimple_assign_rhs1 (assign);
+
+  if (TREE_CODE (expr) != ADDR_EXPR)
+    return NULL_TREE;
+  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 NULL_TREE;
+  parm = TREE_OPERAND (expr, 0);
+  if (TREE_CODE (parm) != SSA_NAME
+      || !SSA_NAME_IS_DEFAULT_DEF (parm)
+      || *offset < 0)
+    return NULL_TREE;
+
+  *offset += mem_ref_offset (expr).low * BITS_PER_UNIT;
+  *obj_p = obj;
+  return expr;
+}
+
 
 /* Given that an actual argument is an SSA_NAME that is a result of a phi
    statement PHI, try to find out whether NAME is in fact a
@@ -603,7 +646,7 @@  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;
@@ -626,29 +669,12 @@  compute_complex_ancestor_jump_func (stru
 
   assign = SSA_NAME_DEF_STMT (tmp);
   assign_bb = gimple_bb (assign);
-  if (!single_pred_p (assign_bb)
-      || !gimple_assign_single_p (assign))
+  if (!single_pred_p (assign_bb))
     return;
-  expr = gimple_assign_rhs1 (assign);
-
-  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)
+  expr = get_ancestor_addr_info (assign, &obj, &offset);
+  if (!expr)
     return;
-  offset += mem_ref_offset (expr).low * BITS_PER_UNIT;
   parm = TREE_OPERAND (expr, 0);
-  if (TREE_CODE (parm) != SSA_NAME
-      || !SSA_NAME_IS_DEFAULT_DEF (parm)
-      || offset < 0)
-    return;
-
   index = ipa_get_param_decl_index (info, SSA_NAME_VAR (parm));
   if (index < 0)
     return;
@@ -675,7 +701,7 @@  compute_complex_ancestor_jump_func (stru
       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 (obj);
     }
 }
 
@@ -1162,29 +1188,20 @@  ipa_is_ssa_with_stmt_def (tree t)
     return false;
 }
 
-/* Find the indirect call graph edge corresponding to STMT and add to it all
-   information necessary to describe a call to a parameter number PARAM_INDEX.
-   NODE is the caller.  POLYMORPHIC should be set to true iff the call is a
-   virtual one.  */
+/* Find the indirect call graph edge corresponding to STMT and mark it as a
+   call to a parameter number PARAM_INDEX.  NODE is the caller.  Return the
+   indirect call graph edge.  */
 
-static void
-ipa_note_param_call (struct cgraph_node *node, int param_index, gimple stmt,
-		     bool polymorphic)
+static struct cgraph_edge *
+ipa_note_param_call (struct cgraph_node *node, int param_index, gimple stmt)
 {
   struct cgraph_edge *cs;
 
   cs = cgraph_edge (node, stmt);
   cs->indirect_info->param_index = param_index;
   cs->indirect_info->anc_offset = 0;
-  cs->indirect_info->polymorphic = polymorphic;
-  if (polymorphic)
-    {
-      tree otr = gimple_call_fn (stmt);
-      tree type, token = OBJ_TYPE_REF_TOKEN (otr);
-      cs->indirect_info->otr_token = tree_low_cst (token, 1);
-      type = TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (otr)));
-      cs->indirect_info->otr_type = type;
-    }
+  cs->indirect_info->polymorphic = 0;
+  return cs;
 }
 
 /* Analyze the CALL and examine uses of formal parameters of the caller NODE
@@ -1263,7 +1280,7 @@  ipa_analyze_indirect_call_uses (struct c
       tree var = SSA_NAME_VAR (target);
       index = ipa_get_param_decl_index (info, var);
       if (index >= 0)
-	ipa_note_param_call (node, index, call, false);
+	ipa_note_param_call (node, index, call);
       return;
     }
 
@@ -1361,7 +1378,7 @@  ipa_analyze_indirect_call_uses (struct c
   index = ipa_get_param_decl_index (info, rec);
   if (index >= 0 && !is_parm_modified_before_call (&parms_info[index],
 						   call, rec))
-    ipa_note_param_call (node, index, call, false);
+    ipa_note_param_call (node, index, call);
 
   return;
 }
@@ -1375,24 +1392,48 @@  ipa_analyze_virtual_call_uses (struct cg
 			       struct ipa_node_params *info, gimple call,
 			       tree target)
 {
+  struct cgraph_edge *cs;
+  struct cgraph_indirect_call_info *ii;
   struct ipa_jump_func jfunc;
   tree obj = OBJ_TYPE_REF_OBJECT (target);
-  tree var;
   int index;
+  HOST_WIDE_INT anc_offset;
 
   if (!flag_devirtualize)
     return;
 
-  if (TREE_CODE (obj) != SSA_NAME
-      || !SSA_NAME_IS_DEFAULT_DEF (obj))
+  if (TREE_CODE (obj) != SSA_NAME)
     return;
 
-  var = SSA_NAME_VAR (obj);
-  index = ipa_get_param_decl_index (info, var);
+  if (SSA_NAME_IS_DEFAULT_DEF (obj))
+    {
+      anc_offset = 0;
+      index = ipa_get_param_decl_index (info, SSA_NAME_VAR (obj));
+      if (index < 0
+	  || detect_type_change_ssa (obj, call, &jfunc))
+	return;
+    }
+  else
+    {
+      gimple stmt = SSA_NAME_DEF_STMT (obj);
+      tree expr;
+
+      expr = get_ancestor_addr_info (stmt, &obj, &anc_offset);
+      if (!expr)
+	return;
+      index = ipa_get_param_decl_index (info,
+					SSA_NAME_VAR (TREE_OPERAND (expr, 0)));
+      if (index < 0
+	  || detect_type_change (obj, expr, call, &jfunc, anc_offset))
+	return;
+    }
 
-  if (index >= 0
-      && !detect_type_change_ssa (obj, call, &jfunc))
-    ipa_note_param_call (node, index, call, true);
+  cs = ipa_note_param_call (node, index, call);
+  ii = cs->indirect_info;
+  ii->anc_offset = anc_offset;
+  ii->otr_token = tree_low_cst (OBJ_TYPE_REF_TOKEN (target), 1);
+  ii->otr_type = TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (target)));
+  ii->polymorphic = 1;
 }
 
 /* Analyze a call statement CALL whether and how it utilizes formal parameters
Index: src/gcc/testsuite/g++.dg/ipa/devirt-7.C
===================================================================
--- /dev/null
+++ src/gcc/testsuite/g++.dg/ipa/devirt-7.C
@@ -0,0 +1,87 @@ 
+/* Verify that IPA-CP can do devirtualization even if the virtual call
+   comes from a method that has been early-inlined into a descendant.  */
+/* { dg-do run } */
+/* { dg-options "-O3 -fdump-ipa-cp"  } */
+
+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);
+  int middleman_1 (int i);
+};
+
+
+class B : public Distraction, public A
+{
+public:
+  virtual int foo (int i);
+  int middleman_2 (int i);
+  __attribute__ ((noinline)) B();
+};
+
+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;
+}
+
+int __attribute__ ((always_inline))
+A::middleman_1 (int i)
+{
+  return this->foo (i);
+}
+
+int __attribute__ ((noinline))
+B::middleman_2 (int i)
+{
+  return this->middleman_1 (i);
+}
+
+B::B ()
+{
+}
+
+int main (int argc, char *argv[])
+{
+  class B b;
+  int i;
+
+  for (i = 0; i < get_input(); i++)
+    if (b.middleman_2 (get_input ()) != 3)
+      abort ();
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo"  "cp"  } } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */