Patchwork [PR,46053] Devirtualization and thunks revisited

login
register
mail settings
Submitter Martin Jambor
Date Oct. 21, 2010, 1:43 p.m.
Message ID <20101021134336.GB21785@virgil.arch.suse.de>
Download mbox | patch
Permalink /patch/68607/
State New
Headers show

Comments

Martin Jambor - Oct. 21, 2010, 1:43 p.m.
Hi,

PR 46053 showed that my previous attempt to incorporate thunks to
devirtualization (fix to PR 45699) was only half-baked and did not
work when we do devirtualization after IPA propagation of type
information (rather obviously, I must admit).

Basically, some thunk or delta information has to be attached to
indirect edges representing polymorphic calls too.  However, I did not
like the idea of having thunks as callees of cgraph edges and,
moreover, there have been concerns that direct calls to thunks might
be unintelligible to some our analyses as well.

So I revisited the whole approach to devirtualization to thunks which
are there to adjust the this offset by a delta and resorted to adding
delta to the parameter directly in the caller.  In order to do this, I
save the delta in the indirect_info of an originally-indirect edge.

Because generating a delta addition statement is impossible when
folding a statement inplace, I rearranged the folding code a bit as
well. 

This patch is to be applied on top of
http://gcc.gnu.org/ml/gcc-patches/2010-10/msg01328.html
(It might apply and work without it but I have not tried it).

Bootstrapped and tested on x86_64-linux without any problems.  OK for
trunk (preferably after the patch referenced above)?

Thanks,

Martin


2010-10-19  Martin Jambor  <mjambor@suse.cz>

	PR tree-optimization/46053
	* cgraph.h (cgraph_indirect_call_info): New field.
	* gimple.h (gimple_fold_obj_type_ref): Declaration removed.
	(gimple_fold_call): Declare.
	(gimple_adjust_this_by_delta): Likewise.
	* cgraph.c (cgraph_make_edge_direct): New parameter delta.  Updated
	all users.
	(cgraph_clone_edge): Create a copy of indirect_info also for direct
	edges.
	* cgraphunit.c (cgraph_redirect_edge_call_stmt_to_callee): Adjust this
	parameters.
	* gimple-fold.c (gimple_fold_obj_type_ref_known_binfo): Renamed to
	gimple_get_virt_mehtod_for_binfo, new parameter delta.  Do not search
	through thunks, check that BINFO_VIRTUALS is not NULL.
	(gimple_adjust_this_by_delta): New function.
	(gimple_fold_obj_type_ref): Removed.
	(gimple_fold_obj_type_ref_call): New function.
	(fold_gimple_call): Renamed to gimple_fold_call, made external.
	Updated users.  Call gimple_fold_obj_type_ref_call instead of
	gimple_fold_obj_type_ref.
	* ipa-cp.c (ipcp_process_devirtualization_opportunities): Process
	thunk deltas.
	(ipcp_discover_new_direct_edges): Likewise.
	* ipa-prop.c (ipa_make_edge_direct_to_target): New parameter delta.
	Updated callers.
	(ipa_write_indirect_edge_info): Stream thunk_delta.
	(ipa_read_indirect_edge_info): Likewise.
	* tree-ssa-ccp.c (ccp_fold_stmt): Use gimple_fold_call instead of
	gimple_fold_obj_type_ref.

	* testsuite/g++.dg/ipa/pr46053.C: New test.
Richard Guenther - Oct. 21, 2010, 2:07 p.m.
On Thu, 21 Oct 2010, Martin Jambor wrote:

> Hi,
> 
> PR 46053 showed that my previous attempt to incorporate thunks to
> devirtualization (fix to PR 45699) was only half-baked and did not
> work when we do devirtualization after IPA propagation of type
> information (rather obviously, I must admit).
> 
> Basically, some thunk or delta information has to be attached to
> indirect edges representing polymorphic calls too.  However, I did not
> like the idea of having thunks as callees of cgraph edges and,
> moreover, there have been concerns that direct calls to thunks might
> be unintelligible to some our analyses as well.
> 
> So I revisited the whole approach to devirtualization to thunks which
> are there to adjust the this offset by a delta and resorted to adding
> delta to the parameter directly in the caller.  In order to do this, I
> save the delta in the indirect_info of an originally-indirect edge.
> 
> Because generating a delta addition statement is impossible when
> folding a statement inplace, I rearranged the folding code a bit as
> well. 
> 
> This patch is to be applied on top of
> http://gcc.gnu.org/ml/gcc-patches/2010-10/msg01328.html
> (It might apply and work without it but I have not tried it).
> 
> Bootstrapped and tested on x86_64-linux without any problems.  OK for
> trunk (preferably after the patch referenced above)?

Without actually looking at the patch I think that call edges
to thunks make more sense, how'd you otherwise represent a direct
call to it (and thus possibly inline it)?

Richard.

> Thanks,
> 
> Martin
> 
> 
> 2010-10-19  Martin Jambor  <mjambor@suse.cz>
> 
> 	PR tree-optimization/46053
> 	* cgraph.h (cgraph_indirect_call_info): New field.
> 	* gimple.h (gimple_fold_obj_type_ref): Declaration removed.
> 	(gimple_fold_call): Declare.
> 	(gimple_adjust_this_by_delta): Likewise.
> 	* cgraph.c (cgraph_make_edge_direct): New parameter delta.  Updated
> 	all users.
> 	(cgraph_clone_edge): Create a copy of indirect_info also for direct
> 	edges.
> 	* cgraphunit.c (cgraph_redirect_edge_call_stmt_to_callee): Adjust this
> 	parameters.
> 	* gimple-fold.c (gimple_fold_obj_type_ref_known_binfo): Renamed to
> 	gimple_get_virt_mehtod_for_binfo, new parameter delta.  Do not search
> 	through thunks, check that BINFO_VIRTUALS is not NULL.
> 	(gimple_adjust_this_by_delta): New function.
> 	(gimple_fold_obj_type_ref): Removed.
> 	(gimple_fold_obj_type_ref_call): New function.
> 	(fold_gimple_call): Renamed to gimple_fold_call, made external.
> 	Updated users.  Call gimple_fold_obj_type_ref_call instead of
> 	gimple_fold_obj_type_ref.
> 	* ipa-cp.c (ipcp_process_devirtualization_opportunities): Process
> 	thunk deltas.
> 	(ipcp_discover_new_direct_edges): Likewise.
> 	* ipa-prop.c (ipa_make_edge_direct_to_target): New parameter delta.
> 	Updated callers.
> 	(ipa_write_indirect_edge_info): Stream thunk_delta.
> 	(ipa_read_indirect_edge_info): Likewise.
> 	* tree-ssa-ccp.c (ccp_fold_stmt): Use gimple_fold_call instead of
> 	gimple_fold_obj_type_ref.
> 
> 	* testsuite/g++.dg/ipa/pr46053.C: New test.
> 
> Index: icln/gcc/cgraph.c
> ===================================================================
> --- icln.orig/gcc/cgraph.c
> +++ icln/gcc/cgraph.c
> @@ -847,7 +847,7 @@ cgraph_set_call_stmt (struct cgraph_edge
>  	 indirect call into a direct one.  */
>        struct cgraph_node *new_callee = cgraph_node (decl);
>  
> -      cgraph_make_edge_direct (e, new_callee);
> +      cgraph_make_edge_direct (e, new_callee, NULL);
>      }
>  
>    push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
> @@ -1184,12 +1184,15 @@ cgraph_redirect_edge_callee (struct cgra
>  }
>  
>  /* Make an indirect EDGE with an unknown callee an ordinary edge leading to
> -   CALLEE.  */
> +   CALLEE.  DELTA, if non-NULL, is an integer constant that is to be added to
> +   the this pointer (first parameter).  */
>  
>  void
> -cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee)
> +cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee,
> +			 tree delta)
>  {
>    edge->indirect_unknown_callee = 0;
> +  edge->indirect_info->thunk_delta = delta;
>  
>    /* Get the edge out of the indirect edge list. */
>    if (edge->prev_callee)
> @@ -2101,8 +2104,16 @@ cgraph_clone_edge (struct cgraph_edge *e
>  	}
>      }
>    else
> -    new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq,
> -				   e->loop_nest + loop_nest);
> +    {
> +      new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq,
> +				     e->loop_nest + loop_nest);
> +      if (e->indirect_info)
> +	{
> +	  new_edge->indirect_info
> +	    = ggc_alloc_cleared_cgraph_indirect_call_info ();
> +	  *new_edge->indirect_info = *e->indirect_info;
> +	}
> +    }
>  
>    new_edge->inline_failed = e->inline_failed;
>    new_edge->indirect_inlining_edge = e->indirect_inlining_edge;
> Index: icln/gcc/cgraph.h
> ===================================================================
> --- icln.orig/gcc/cgraph.h
> +++ icln/gcc/cgraph.h
> @@ -398,6 +398,9 @@ struct GTY(()) cgraph_indirect_call_info
>    HOST_WIDE_INT otr_token;
>    /* Type of the object from OBJ_TYPE_REF_OBJECT. */
>    tree otr_type;
> +  /* Delta by which must be added to this parameter.  For polymorphic calls
> +     only.  */
> +  tree thunk_delta;
>    /* Index of the parameter that is called.  */
>    int param_index;
>    /* ECF flags determined from the caller.  */
> @@ -585,7 +588,7 @@ struct cgraph_node * cgraph_clone_node (
>  					int, bool, VEC(cgraph_edge_p,heap) *);
>  
>  void cgraph_redirect_edge_callee (struct cgraph_edge *, struct cgraph_node *);
> -void cgraph_make_edge_direct (struct cgraph_edge *, struct cgraph_node *);
> +void cgraph_make_edge_direct (struct cgraph_edge *, struct cgraph_node *, tree);
>  
>  struct cgraph_asm_node *cgraph_add_asm_node (tree);
>  
> Index: icln/gcc/cgraphunit.c
> ===================================================================
> --- icln.orig/gcc/cgraphunit.c
> +++ icln/gcc/cgraphunit.c
> @@ -2115,6 +2115,8 @@ cgraph_redirect_edge_call_stmt_to_callee
>  {
>    tree decl = gimple_call_fndecl (e->call_stmt);
>    gimple new_stmt;
> +  gimple_stmt_iterator gsi;
> +  bool gsi_computed = false;
>  #ifdef ENABLE_CHECKING
>    struct cgraph_node *node;
>  #endif
> @@ -2147,9 +2149,24 @@ cgraph_redirect_edge_call_stmt_to_callee
>  	}
>      }
>  
> +  if (e->indirect_info && e->indirect_info->thunk_delta
> +      && integer_nonzerop (e->indirect_info->thunk_delta))
> +    {
> +      if (cgraph_dump_file)
> +	{
> +	  fprintf (cgraph_dump_file, "          Thunk delta is ");
> +	  print_generic_expr (cgraph_dump_file,
> +			      e->indirect_info->thunk_delta, 0);
> +	  fprintf (cgraph_dump_file, "\n");
> +	}
> +      gsi = gsi_for_stmt (e->call_stmt);
> +      gsi_computed = true;
> +      gimple_adjust_this_by_delta (&gsi, e->indirect_info->thunk_delta);
> +      e->indirect_info->thunk_delta = NULL_TREE;
> +    }
> +
>    if (e->callee->clone.combined_args_to_skip)
>      {
> -      gimple_stmt_iterator gsi;
>        int lp_nr;
>  
>        new_stmt
> @@ -2161,7 +2178,8 @@ cgraph_redirect_edge_call_stmt_to_callee
>  	  && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
>  	SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
>  
> -      gsi = gsi_for_stmt (e->call_stmt);
> +      if (!gsi_computed)
> +	gsi = gsi_for_stmt (e->call_stmt);
>        gsi_replace (&gsi, new_stmt, false);
>        /* We need to defer cleaning EH info on the new statement to
>           fixup-cfg.  We may not have dominator information at this point
> Index: icln/gcc/gimple-fold.c
> ===================================================================
> --- icln.orig/gcc/gimple-fold.c
> +++ icln/gcc/gimple-fold.c
> @@ -1459,14 +1459,20 @@ gimple_get_relevant_ref_binfo (tree ref,
>  
>  /* Fold a OBJ_TYPE_REF expression to the address of a function. TOKEN is
>     integer form of OBJ_TYPE_REF_TOKEN of the reference expression.  KNOWN_BINFO
> -   carries the binfo describing the true type of OBJ_TYPE_REF_OBJECT(REF).  */
> +   carries the binfo describing the true type of OBJ_TYPE_REF_OBJECT(REF).
> +   Delta, if non-NULL, is an integer contant that should be added to this
> +   pointer (first parameter). */
>  
>  tree
> -gimple_fold_obj_type_ref_known_binfo (HOST_WIDE_INT token, tree known_binfo)
> +gimple_get_virt_mehtod_for_binfo (HOST_WIDE_INT token, tree known_binfo,
> +				  tree *delta)
>  {
>    HOST_WIDE_INT i;
> -  tree v, fndecl, delta;
> +  tree v, fndecl;
>  
> +  /* If there is no virtual methods leave the OBJ_TYPE_REF alone.  */
> +  if (!BINFO_VIRTUALS (known_binfo))
> +    return NULL_TREE;
>    gcc_assert (BINFO_NEW_VTABLE_MARKED (known_binfo));
>    v = BINFO_VIRTUALS (known_binfo);
>    i = 0;
> @@ -1478,62 +1484,77 @@ gimple_fold_obj_type_ref_known_binfo (HO
>      }
>  
>    fndecl = TREE_VALUE (v);
> -  delta = TREE_PURPOSE (v);
> -  gcc_assert (host_integerp (delta, 0));
> -
> -  if (integer_nonzerop (delta))
> -    {
> -      struct cgraph_node *node = cgraph_get_node (fndecl);
> -      HOST_WIDE_INT off = tree_low_cst (delta, 0);
> -
> -      if (!node)
> -        return NULL;
> -      for (node = node->same_body; node; node = node->next)
> -        if (node->thunk.thunk_p && off == node->thunk.fixed_offset)
> -          break;
> -      if (node)
> -        fndecl = node->decl;
> -      else
> -        return NULL;
> -     }
> +  *delta = TREE_PURPOSE (v);
> +  gcc_checking_assert (host_integerp (*delta, 0));
>  
>    /* When cgraph node is missing and function is not public, we cannot
>       devirtualize.  This can happen in WHOPR when the actual method
>       ends up in other partition, because we found devirtualization
>       possibility too late.  */
> -  if (!can_refer_decl_in_current_unit_p (fndecl))
> -    return NULL;
> -  return build_fold_addr_expr (fndecl);
> +  if (!can_refer_decl_in_current_unit_p (TREE_VALUE (v)))
> +    return NULL_TREE;
> +  return fndecl;
>  }
>  
> +/* Generate code adjusting the first parameter of a call statement determined
> +   by GSI by DELTA.  */
>  
> -/* Fold a OBJ_TYPE_REF expression to the address of a function.  If KNOWN_TYPE
> -   is not NULL_TREE, it is the true type of the outmost encapsulating object if
> -   that comes from a pointer SSA_NAME.  If the true outmost encapsulating type
> -   can be determined from a declaration OBJ_TYPE_REF_OBJECT(REF), it is used
> -   regardless of KNOWN_TYPE (which thus can be NULL_TREE).  */
> +void
> +gimple_adjust_this_by_delta (gimple_stmt_iterator *gsi, tree delta)
> +{
> +  gimple call_stmt = gsi_stmt (*gsi);
> +  tree parm, tmp;
> +  gimple new_stmt;
> +
> +  delta = fold_convert (sizetype, delta);
> +  gcc_assert (gimple_call_num_args (call_stmt) >= 1);
> +  parm = gimple_call_arg (call_stmt, 0);
> +  gcc_assert (POINTER_TYPE_P (TREE_TYPE (parm)));
> +  tmp = create_tmp_var (TREE_TYPE (parm), NULL);
> +  add_referenced_var (tmp);
> +
> +  tmp = make_ssa_name (tmp, NULL);
> +  new_stmt = gimple_build_assign_with_ops (POINTER_PLUS_EXPR, tmp, parm, delta);
> +  SSA_NAME_DEF_STMT (tmp) = new_stmt;
> +  gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
> +  gimple_call_set_arg (call_stmt, 0, tmp);
> +}
>  
> -tree
> -gimple_fold_obj_type_ref (tree ref, tree known_type)
> +/* Fold a call statement to OBJ_TYPE_REF to a direct call, if possible.  GSI
> +   determines the statement, generating new statements is allowed only if
> +   INPLACE is false.  Return true iff the statement was changed.  */
> +
> +static bool
> +gimple_fold_obj_type_ref_call (gimple_stmt_iterator *gsi, bool inplace)
>  {
> +  gimple stmt = gsi_stmt (*gsi);
> +  tree ref = gimple_call_fn (stmt);
>    tree obj = OBJ_TYPE_REF_OBJECT (ref);
> -  tree known_binfo = known_type ? TYPE_BINFO (known_type) : NULL_TREE;
> -  tree binfo;
> +  tree binfo, fndecl, delta;
> +  HOST_WIDE_INT token;
>  
>    if (TREE_CODE (obj) == ADDR_EXPR)
>      obj = TREE_OPERAND (obj, 0);
> +  else
> +    return false;
>  
> -  binfo = gimple_get_relevant_ref_binfo (obj, known_binfo);
> -  if (binfo)
> +  binfo = gimple_get_relevant_ref_binfo (obj, NULL_TREE);
> +  if (!binfo)
> +    return false;
> +  token = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
> +  fndecl = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta);
> +  if (!fndecl)
> +    return false;
> +
> +  if (integer_nonzerop (delta))
>      {
> -      HOST_WIDE_INT token = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
> -      /* If there is no virtual methods leave the OBJ_TYPE_REF alone.  */
> -      if (!BINFO_VIRTUALS (binfo))
> -	return NULL_TREE;
> -      return gimple_fold_obj_type_ref_known_binfo (token, binfo);
> +      if (inplace)
> +        return false;
> +      gimple_adjust_this_by_delta (gsi, delta);
>      }
> -  else
> -    return NULL_TREE;
> +
> +  gimple_call_set_fndecl (stmt, fndecl);
> +  return true;
>  }
>  
>  /* Attempt to fold a call statement referenced by the statement iterator GSI.
> @@ -1541,8 +1562,8 @@ gimple_fold_obj_type_ref (tree ref, tree
>     simplifies to a constant value. Return true if any changes were made.
>     It is assumed that the operands have been previously folded.  */
>  
> -static bool
> -fold_gimple_call (gimple_stmt_iterator *gsi, bool inplace)
> +bool
> +gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
>  {
>    gimple stmt = gsi_stmt (*gsi);
>  
> @@ -1568,18 +1589,8 @@ fold_gimple_call (gimple_stmt_iterator *
>           copying EH region info to the new node.  Easier to just do it
>           here where we can just smash the call operand.  */
>        callee = gimple_call_fn (stmt);
> -      if (TREE_CODE (callee) == OBJ_TYPE_REF
> -          && TREE_CODE (OBJ_TYPE_REF_OBJECT (callee)) == ADDR_EXPR)
> -        {
> -          tree t;
> -
> -          t = gimple_fold_obj_type_ref (callee, NULL_TREE);
> -          if (t)
> -            {
> -              gimple_call_set_fn (stmt, t);
> -              return true;
> -            }
> -        }
> +      if (TREE_CODE (callee) == OBJ_TYPE_REF)
> +	return gimple_fold_obj_type_ref_call (gsi, inplace);
>      }
>  
>    return false;
> @@ -1633,7 +1644,7 @@ fold_stmt_1 (gimple_stmt_iterator *gsi,
>  		changed = true;
>  	      }
>  	  }
> -      changed |= fold_gimple_call (gsi, inplace);
> +      changed |= gimple_fold_call (gsi, inplace);
>        break;
>  
>      case GIMPLE_ASM:
> Index: icln/gcc/gimple.h
> ===================================================================
> --- icln.orig/gcc/gimple.h
> +++ icln/gcc/gimple.h
> @@ -894,10 +894,10 @@ unsigned get_gimple_rhs_num_ops (enum tr
>  #define gimple_alloc(c, n) gimple_alloc_stat (c, n MEM_STAT_INFO)
>  gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL);
>  const char *gimple_decl_printable_name (tree, int);
> -tree gimple_fold_obj_type_ref (tree, tree);
> +bool gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace);
>  tree gimple_get_relevant_ref_binfo (tree ref, tree known_binfo);
> -tree gimple_fold_obj_type_ref_known_binfo (HOST_WIDE_INT, tree);
> -
> +tree gimple_get_virt_mehtod_for_binfo (HOST_WIDE_INT, tree, tree *);
> +void gimple_adjust_this_by_delta (gimple_stmt_iterator *, tree);
>  /* Returns true iff T is a valid GIMPLE statement.  */
>  extern bool is_gimple_stmt (tree);
>  
> Index: icln/gcc/ipa-cp.c
> ===================================================================
> --- icln.orig/gcc/ipa-cp.c
> +++ icln/gcc/ipa-cp.c
> @@ -1205,7 +1205,7 @@ ipcp_process_devirtualization_opportunit
>      {
>        int param_index, types_count, j;
>        HOST_WIDE_INT token;
> -      tree target;
> +      tree target, delta;
>  
>        next_ie = ie->next_callee;
>        if (!ie->indirect_info->polymorphic)
> @@ -1222,7 +1222,8 @@ ipcp_process_devirtualization_opportunit
>        for (j = 0; j < types_count; j++)
>  	{
>  	  tree binfo = VEC_index (tree, info->params[param_index].types, j);
> -	  tree t = gimple_fold_obj_type_ref_known_binfo (token, binfo);
> +	  tree d;
> +	  tree t = gimple_get_virt_mehtod_for_binfo (token, binfo, &d);
>  
>  	  if (!t)
>  	    {
> @@ -1230,8 +1231,11 @@ ipcp_process_devirtualization_opportunit
>  	      break;
>  	    }
>  	  else if (!target)
> -	    target = t;
> -	  else if (target != t)
> +	    {
> +	      target = t;
> +	      delta = d;
> +	    }
> +	  else if (target != t || !tree_int_cst_equal (delta, d))
>  	    {
>  	      target = NULL_TREE;
>  	      break;
> @@ -1239,7 +1243,7 @@ ipcp_process_devirtualization_opportunit
>  	}
>  
>        if (target)
> -	ipa_make_edge_direct_to_target (ie, target);
> +	ipa_make_edge_direct_to_target (ie, target, delta);
>      }
>  }
>  
> @@ -1279,6 +1283,7 @@ ipcp_discover_new_direct_edges (struct c
>    for (ie = node->indirect_calls; ie; ie = next_ie)
>      {
>        struct cgraph_indirect_call_info *ici = ie->indirect_info;
> +      tree target, delta = NULL_TREE;
>  
>        next_ie = ie->next_callee;
>        if (ici->param_index != index)
> @@ -1298,12 +1303,14 @@ ipcp_discover_new_direct_edges (struct c
>  	    continue;
>  	  gcc_assert (ie->indirect_info->anc_offset == 0);
>  	  token = ie->indirect_info->otr_token;
> -	  cst = gimple_fold_obj_type_ref_known_binfo (token, binfo);
> -	  if (!cst)
> +	  target = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta);
> +	  if (!target)
>  	    continue;
>  	}
> +      else
> +	target = cst;
>  
> -      ipa_make_edge_direct_to_target (ie, cst);
> +      ipa_make_edge_direct_to_target (ie, target, delta);
>      }
>  }
>  
> Index: icln/gcc/ipa-prop.c
> ===================================================================
> --- icln.orig/gcc/ipa-prop.c
> +++ icln/gcc/ipa-prop.c
> @@ -1432,35 +1432,43 @@ update_jump_functions_after_inlining (st
>  }
>  
>  /* If TARGET is an addr_expr of a function declaration, make it the destination
> -   of an indirect edge IE and return the edge.  Otherwise, return NULL.  */
> +   of an indirect edge IE and return the edge.  Otherwise, return NULL.  Delta,
> +   if non-NULL, is an integer constant that must be added to this pointer
> +   (first parameter).  */
>  
>  struct cgraph_edge *
> -ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target)
> +ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target, tree delta)
>  {
>    struct cgraph_node *callee;
>  
> -  if (TREE_CODE (target) != ADDR_EXPR)
> -    return NULL;
> -  target = TREE_OPERAND (target, 0);
> +  if (TREE_CODE (target) == ADDR_EXPR)
> +    target = TREE_OPERAND (target, 0);
>    if (TREE_CODE (target) != FUNCTION_DECL)
>      return NULL;
>    callee = cgraph_node (target);
>    if (!callee)
>      return NULL;
>    ipa_check_create_node_params ();
> -  cgraph_make_edge_direct (ie, callee);
> +
> +  cgraph_make_edge_direct (ie, callee, delta);
>    if (dump_file)
>      {
>        fprintf (dump_file, "ipa-prop: Discovered %s call to a known target "
> -	       "(%s/%i -> %s/%i) for stmt ",
> +	       "(%s/%i -> %s/%i), for stmt ",
>  	       ie->indirect_info->polymorphic ? "a virtual" : "an indirect",
>  	       cgraph_node_name (ie->caller), ie->caller->uid,
>  	       cgraph_node_name (ie->callee), ie->callee->uid);
> -
>        if (ie->call_stmt)
>  	print_gimple_stmt (dump_file, ie->call_stmt, 2, TDF_SLIM);
>        else
>  	fprintf (dump_file, "with uid %i\n", ie->lto_stmt_uid);
> +
> +      if (delta)
> +	{
> +	  fprintf (dump_file, "          Thunk delta is ");
> +	  print_generic_expr (dump_file, delta, 0);
> +	  fprintf (dump_file, "\n");
> +	}
>      }
>  
>    if (ipa_get_cs_argument_count (IPA_EDGE_REF (ie))
> @@ -1488,7 +1496,7 @@ try_make_edge_direct_simple_call (struct
>    else
>      return NULL;
>  
> -  return ipa_make_edge_direct_to_target (ie, target);
> +  return ipa_make_edge_direct_to_target (ie, target, NULL_TREE);
>  }
>  
>  /* Try to find a destination for indirect edge IE that corresponds to a
> @@ -1500,7 +1508,7 @@ static struct cgraph_edge *
>  try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
>  				   struct ipa_jump_func *jfunc)
>  {
> -  tree binfo, type, target;
> +  tree binfo, type, target, delta;
>    HOST_WIDE_INT token;
>  
>    if (jfunc->type == IPA_JF_KNOWN_TYPE)
> @@ -1524,12 +1532,12 @@ try_make_edge_direct_virtual_call (struc
>    type = ie->indirect_info->otr_type;
>    binfo = get_binfo_at_offset (binfo, ie->indirect_info->anc_offset, type);
>    if (binfo)
> -    target = gimple_fold_obj_type_ref_known_binfo (token, binfo);
> +    target = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta);
>    else
>      return NULL;
>  
>    if (target)
> -    return ipa_make_edge_direct_to_target (ie, target);
> +    return ipa_make_edge_direct_to_target (ie, target, delta);
>    else
>      return NULL;
>  }
> @@ -2520,6 +2528,7 @@ ipa_write_indirect_edge_info (struct out
>      {
>        lto_output_sleb128_stream (ob->main_stream, ii->otr_token);
>        lto_output_tree (ob, ii->otr_type, true);
> +      lto_output_tree (ob, ii->thunk_delta, true);
>      }
>  }
>  
> @@ -2542,6 +2551,7 @@ ipa_read_indirect_edge_info (struct lto_
>      {
>        ii->otr_token = (HOST_WIDE_INT) lto_input_sleb128 (ib);
>        ii->otr_type = lto_input_tree (ib, data_in);
> +      ii->thunk_delta = lto_input_tree (ib, data_in);
>      }
>  }
>  
> Index: icln/gcc/ipa-prop.h
> ===================================================================
> --- icln.orig/gcc/ipa-prop.h
> +++ icln/gcc/ipa-prop.h
> @@ -430,7 +430,8 @@ bool ipa_propagate_indirect_call_infos (
>  					VEC (cgraph_edge_p, heap) **new_edges);
>  
>  /* Indirect edge and binfo processing.  */
> -struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree);
> +struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree,
> +						    tree);
>  
>  
>  /* Debugging interface.  */
> Index: icln/gcc/tree-ssa-ccp.c
> ===================================================================
> --- icln.orig/gcc/tree-ssa-ccp.c
> +++ icln/gcc/tree-ssa-ccp.c
> @@ -2314,16 +2314,8 @@ ccp_fold_stmt (gimple_stmt_iterator *gsi
>  	  {
>  	    tree expr = OBJ_TYPE_REF_EXPR (callee);
>  	    OBJ_TYPE_REF_EXPR (callee) = valueize_op (expr);
> -	    if (TREE_CODE (OBJ_TYPE_REF_EXPR (callee)) == ADDR_EXPR)
> -	      {
> -		tree t;
> -		t = gimple_fold_obj_type_ref (callee, NULL_TREE);
> -		if (t)
> -		  {
> -		    gimple_call_set_fn (stmt, t);
> -		    changed = true;
> -		  }
> -	      }
> +	    if (gimple_fold_call (gsi, false))
> +	      changed = true;
>  	    OBJ_TYPE_REF_EXPR (callee) = expr;
>  	  }
>  
> Index: icln/gcc/testsuite/g++.dg/ipa/pr46053.C
> ===================================================================
> --- /dev/null
> +++ icln/gcc/testsuite/g++.dg/ipa/pr46053.C
> @@ -0,0 +1,41 @@
> +/* { dg-do run } */
> +/* { dg-options "-O -fipa-cp -fno-early-inlining"  } */
> +
> +extern "C" void abort ();
> +
> +struct A
> +{
> +  virtual void foo () = 0;
> +};
> +
> +struct B : A
> +{
> +  virtual void foo () = 0;
> +};
> +
> +struct C : A
> +{
> +};
> +
> +struct D : C, B
> +{
> +  int i;
> +  D () : i(0xaaaa) {}
> +  virtual void foo ()
> +  {
> +    if (i != 0xaaaa)
> +      abort();
> +  }
> +};
> +
> +static inline void bar (B &b)
> +{
> +  b.foo ();
> +}
> +
> +int main()
> +{
> +  D d;
> +  bar (d);
> +  return 0;
> +}
> 
>
Martin Jambor - Oct. 21, 2010, 4:03 p.m.
Hi,

On Thu, Oct 21, 2010 at 04:07:30PM +0200, Richard Guenther wrote:
> On Thu, 21 Oct 2010, Martin Jambor wrote:
> 
> > PR 46053 showed that my previous attempt to incorporate thunks to
> > devirtualization (fix to PR 45699) was only half-baked and did not
> > work when we do devirtualization after IPA propagation of type
> > information (rather obviously, I must admit).
> > 
> > Basically, some thunk or delta information has to be attached to
> > indirect edges representing polymorphic calls too.  However, I did not
> > like the idea of having thunks as callees of cgraph edges and,
> > moreover, there have been concerns that direct calls to thunks might
> > be unintelligible to some our analyses as well.
> > 
> > So I revisited the whole approach to devirtualization to thunks which
> > are there to adjust the this offset by a delta and resorted to adding
> > delta to the parameter directly in the caller.  In order to do this, I
> > save the delta in the indirect_info of an originally-indirect edge.
> > 
> > Because generating a delta addition statement is impossible when
> > folding a statement inplace, I rearranged the folding code a bit as
> > well. 
> > 
> > This patch is to be applied on top of
> > http://gcc.gnu.org/ml/gcc-patches/2010-10/msg01328.html
> > (It might apply and work without it but I have not tried it).
> > 
> > Bootstrapped and tested on x86_64-linux without any problems.  OK for
> > trunk (preferably after the patch referenced above)?
> 
> Without actually looking at the patch I think that call edges
> to thunks make more sense, how'd you otherwise represent a direct
> call to it (and thus possibly inline it)?
> 

In fact, IPA passes get direct calls to thunks horribly wrong.  I have
just added a loop to main in testsuite/g++.dg/torture/pr45699.C so
that gcc attempts to inline the direct call to thunk and I got aborts
caused by ipa-cp and ipa-inline (when enabling either or both).

The reason is that because we rebuild call graph, the direct calls to
thunks get represented in the call graph as edges leading to the
non-thunk nodes.  Information about deltas are not represented in any
way and the resulting inlined code is unable to access data of the
object.  tree-inline.c seems to have no special code to handle thunks
and their deltas (as far as I grepped).

So my conclusion is that until my "fix" of PR 45699, there had never
been direct calls to thunks and currently the IPA passes are unable to
handle it (most probably all of them).  The proposed patch fixes these
problems, of course, because the direct calls go away again.

Alternatively, we can teach the cgraph rebuilding code to recognize
thunks _and_ all ipa-passes to check for thunks and handle them
specially in wide range of situations.  A lot of questions would arise
in the process, such as to what extent two different cgraph node
structures are actually one single node (e.g. when computing IPA-CP
lattices or generally when considering any modifications whatsoever to
either node), how do we clone thunks, and so on and so forth.  I'd
prefer to avoid that if we can.

Thanks,

Martin



> Richard.
> 
> > Thanks,
> > 
> > Martin
> > 
> > 
> > 2010-10-19  Martin Jambor  <mjambor@suse.cz>
> > 
> > 	PR tree-optimization/46053
> > 	* cgraph.h (cgraph_indirect_call_info): New field.
> > 	* gimple.h (gimple_fold_obj_type_ref): Declaration removed.
> > 	(gimple_fold_call): Declare.
> > 	(gimple_adjust_this_by_delta): Likewise.
> > 	* cgraph.c (cgraph_make_edge_direct): New parameter delta.  Updated
> > 	all users.
> > 	(cgraph_clone_edge): Create a copy of indirect_info also for direct
> > 	edges.
> > 	* cgraphunit.c (cgraph_redirect_edge_call_stmt_to_callee): Adjust this
> > 	parameters.
> > 	* gimple-fold.c (gimple_fold_obj_type_ref_known_binfo): Renamed to
> > 	gimple_get_virt_mehtod_for_binfo, new parameter delta.  Do not search
> > 	through thunks, check that BINFO_VIRTUALS is not NULL.
> > 	(gimple_adjust_this_by_delta): New function.
> > 	(gimple_fold_obj_type_ref): Removed.
> > 	(gimple_fold_obj_type_ref_call): New function.
> > 	(fold_gimple_call): Renamed to gimple_fold_call, made external.
> > 	Updated users.  Call gimple_fold_obj_type_ref_call instead of
> > 	gimple_fold_obj_type_ref.
> > 	* ipa-cp.c (ipcp_process_devirtualization_opportunities): Process
> > 	thunk deltas.
> > 	(ipcp_discover_new_direct_edges): Likewise.
> > 	* ipa-prop.c (ipa_make_edge_direct_to_target): New parameter delta.
> > 	Updated callers.
> > 	(ipa_write_indirect_edge_info): Stream thunk_delta.
> > 	(ipa_read_indirect_edge_info): Likewise.
> > 	* tree-ssa-ccp.c (ccp_fold_stmt): Use gimple_fold_call instead of
> > 	gimple_fold_obj_type_ref.
> > 
> > 	* testsuite/g++.dg/ipa/pr46053.C: New test.
> > 
> > Index: icln/gcc/cgraph.c
> > ===================================================================
> > --- icln.orig/gcc/cgraph.c
> > +++ icln/gcc/cgraph.c
> > @@ -847,7 +847,7 @@ cgraph_set_call_stmt (struct cgraph_edge
> >  	 indirect call into a direct one.  */
> >        struct cgraph_node *new_callee = cgraph_node (decl);
> >  
> > -      cgraph_make_edge_direct (e, new_callee);
> > +      cgraph_make_edge_direct (e, new_callee, NULL);
> >      }
> >  
> >    push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
> > @@ -1184,12 +1184,15 @@ cgraph_redirect_edge_callee (struct cgra
> >  }
> >  
> >  /* Make an indirect EDGE with an unknown callee an ordinary edge leading to
> > -   CALLEE.  */
> > +   CALLEE.  DELTA, if non-NULL, is an integer constant that is to be added to
> > +   the this pointer (first parameter).  */
> >  
> >  void
> > -cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee)
> > +cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee,
> > +			 tree delta)
> >  {
> >    edge->indirect_unknown_callee = 0;
> > +  edge->indirect_info->thunk_delta = delta;
> >  
> >    /* Get the edge out of the indirect edge list. */
> >    if (edge->prev_callee)
> > @@ -2101,8 +2104,16 @@ cgraph_clone_edge (struct cgraph_edge *e
> >  	}
> >      }
> >    else
> > -    new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq,
> > -				   e->loop_nest + loop_nest);
> > +    {
> > +      new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq,
> > +				     e->loop_nest + loop_nest);
> > +      if (e->indirect_info)
> > +	{
> > +	  new_edge->indirect_info
> > +	    = ggc_alloc_cleared_cgraph_indirect_call_info ();
> > +	  *new_edge->indirect_info = *e->indirect_info;
> > +	}
> > +    }
> >  
> >    new_edge->inline_failed = e->inline_failed;
> >    new_edge->indirect_inlining_edge = e->indirect_inlining_edge;
> > Index: icln/gcc/cgraph.h
> > ===================================================================
> > --- icln.orig/gcc/cgraph.h
> > +++ icln/gcc/cgraph.h
> > @@ -398,6 +398,9 @@ struct GTY(()) cgraph_indirect_call_info
> >    HOST_WIDE_INT otr_token;
> >    /* Type of the object from OBJ_TYPE_REF_OBJECT. */
> >    tree otr_type;
> > +  /* Delta by which must be added to this parameter.  For polymorphic calls
> > +     only.  */
> > +  tree thunk_delta;
> >    /* Index of the parameter that is called.  */
> >    int param_index;
> >    /* ECF flags determined from the caller.  */
> > @@ -585,7 +588,7 @@ struct cgraph_node * cgraph_clone_node (
> >  					int, bool, VEC(cgraph_edge_p,heap) *);
> >  
> >  void cgraph_redirect_edge_callee (struct cgraph_edge *, struct cgraph_node *);
> > -void cgraph_make_edge_direct (struct cgraph_edge *, struct cgraph_node *);
> > +void cgraph_make_edge_direct (struct cgraph_edge *, struct cgraph_node *, tree);
> >  
> >  struct cgraph_asm_node *cgraph_add_asm_node (tree);
> >  
> > Index: icln/gcc/cgraphunit.c
> > ===================================================================
> > --- icln.orig/gcc/cgraphunit.c
> > +++ icln/gcc/cgraphunit.c
> > @@ -2115,6 +2115,8 @@ cgraph_redirect_edge_call_stmt_to_callee
> >  {
> >    tree decl = gimple_call_fndecl (e->call_stmt);
> >    gimple new_stmt;
> > +  gimple_stmt_iterator gsi;
> > +  bool gsi_computed = false;
> >  #ifdef ENABLE_CHECKING
> >    struct cgraph_node *node;
> >  #endif
> > @@ -2147,9 +2149,24 @@ cgraph_redirect_edge_call_stmt_to_callee
> >  	}
> >      }
> >  
> > +  if (e->indirect_info && e->indirect_info->thunk_delta
> > +      && integer_nonzerop (e->indirect_info->thunk_delta))
> > +    {
> > +      if (cgraph_dump_file)
> > +	{
> > +	  fprintf (cgraph_dump_file, "          Thunk delta is ");
> > +	  print_generic_expr (cgraph_dump_file,
> > +			      e->indirect_info->thunk_delta, 0);
> > +	  fprintf (cgraph_dump_file, "\n");
> > +	}
> > +      gsi = gsi_for_stmt (e->call_stmt);
> > +      gsi_computed = true;
> > +      gimple_adjust_this_by_delta (&gsi, e->indirect_info->thunk_delta);
> > +      e->indirect_info->thunk_delta = NULL_TREE;
> > +    }
> > +
> >    if (e->callee->clone.combined_args_to_skip)
> >      {
> > -      gimple_stmt_iterator gsi;
> >        int lp_nr;
> >  
> >        new_stmt
> > @@ -2161,7 +2178,8 @@ cgraph_redirect_edge_call_stmt_to_callee
> >  	  && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
> >  	SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
> >  
> > -      gsi = gsi_for_stmt (e->call_stmt);
> > +      if (!gsi_computed)
> > +	gsi = gsi_for_stmt (e->call_stmt);
> >        gsi_replace (&gsi, new_stmt, false);
> >        /* We need to defer cleaning EH info on the new statement to
> >           fixup-cfg.  We may not have dominator information at this point
> > Index: icln/gcc/gimple-fold.c
> > ===================================================================
> > --- icln.orig/gcc/gimple-fold.c
> > +++ icln/gcc/gimple-fold.c
> > @@ -1459,14 +1459,20 @@ gimple_get_relevant_ref_binfo (tree ref,
> >  
> >  /* Fold a OBJ_TYPE_REF expression to the address of a function. TOKEN is
> >     integer form of OBJ_TYPE_REF_TOKEN of the reference expression.  KNOWN_BINFO
> > -   carries the binfo describing the true type of OBJ_TYPE_REF_OBJECT(REF).  */
> > +   carries the binfo describing the true type of OBJ_TYPE_REF_OBJECT(REF).
> > +   Delta, if non-NULL, is an integer contant that should be added to this
> > +   pointer (first parameter). */
> >  
> >  tree
> > -gimple_fold_obj_type_ref_known_binfo (HOST_WIDE_INT token, tree known_binfo)
> > +gimple_get_virt_mehtod_for_binfo (HOST_WIDE_INT token, tree known_binfo,
> > +				  tree *delta)
> >  {
> >    HOST_WIDE_INT i;
> > -  tree v, fndecl, delta;
> > +  tree v, fndecl;
> >  
> > +  /* If there is no virtual methods leave the OBJ_TYPE_REF alone.  */
> > +  if (!BINFO_VIRTUALS (known_binfo))
> > +    return NULL_TREE;
> >    gcc_assert (BINFO_NEW_VTABLE_MARKED (known_binfo));
> >    v = BINFO_VIRTUALS (known_binfo);
> >    i = 0;
> > @@ -1478,62 +1484,77 @@ gimple_fold_obj_type_ref_known_binfo (HO
> >      }
> >  
> >    fndecl = TREE_VALUE (v);
> > -  delta = TREE_PURPOSE (v);
> > -  gcc_assert (host_integerp (delta, 0));
> > -
> > -  if (integer_nonzerop (delta))
> > -    {
> > -      struct cgraph_node *node = cgraph_get_node (fndecl);
> > -      HOST_WIDE_INT off = tree_low_cst (delta, 0);
> > -
> > -      if (!node)
> > -        return NULL;
> > -      for (node = node->same_body; node; node = node->next)
> > -        if (node->thunk.thunk_p && off == node->thunk.fixed_offset)
> > -          break;
> > -      if (node)
> > -        fndecl = node->decl;
> > -      else
> > -        return NULL;
> > -     }
> > +  *delta = TREE_PURPOSE (v);
> > +  gcc_checking_assert (host_integerp (*delta, 0));
> >  
> >    /* When cgraph node is missing and function is not public, we cannot
> >       devirtualize.  This can happen in WHOPR when the actual method
> >       ends up in other partition, because we found devirtualization
> >       possibility too late.  */
> > -  if (!can_refer_decl_in_current_unit_p (fndecl))
> > -    return NULL;
> > -  return build_fold_addr_expr (fndecl);
> > +  if (!can_refer_decl_in_current_unit_p (TREE_VALUE (v)))
> > +    return NULL_TREE;
> > +  return fndecl;
> >  }
> >  
> > +/* Generate code adjusting the first parameter of a call statement determined
> > +   by GSI by DELTA.  */
> >  
> > -/* Fold a OBJ_TYPE_REF expression to the address of a function.  If KNOWN_TYPE
> > -   is not NULL_TREE, it is the true type of the outmost encapsulating object if
> > -   that comes from a pointer SSA_NAME.  If the true outmost encapsulating type
> > -   can be determined from a declaration OBJ_TYPE_REF_OBJECT(REF), it is used
> > -   regardless of KNOWN_TYPE (which thus can be NULL_TREE).  */
> > +void
> > +gimple_adjust_this_by_delta (gimple_stmt_iterator *gsi, tree delta)
> > +{
> > +  gimple call_stmt = gsi_stmt (*gsi);
> > +  tree parm, tmp;
> > +  gimple new_stmt;
> > +
> > +  delta = fold_convert (sizetype, delta);
> > +  gcc_assert (gimple_call_num_args (call_stmt) >= 1);
> > +  parm = gimple_call_arg (call_stmt, 0);
> > +  gcc_assert (POINTER_TYPE_P (TREE_TYPE (parm)));
> > +  tmp = create_tmp_var (TREE_TYPE (parm), NULL);
> > +  add_referenced_var (tmp);
> > +
> > +  tmp = make_ssa_name (tmp, NULL);
> > +  new_stmt = gimple_build_assign_with_ops (POINTER_PLUS_EXPR, tmp, parm, delta);
> > +  SSA_NAME_DEF_STMT (tmp) = new_stmt;
> > +  gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
> > +  gimple_call_set_arg (call_stmt, 0, tmp);
> > +}
> >  
> > -tree
> > -gimple_fold_obj_type_ref (tree ref, tree known_type)
> > +/* Fold a call statement to OBJ_TYPE_REF to a direct call, if possible.  GSI
> > +   determines the statement, generating new statements is allowed only if
> > +   INPLACE is false.  Return true iff the statement was changed.  */
> > +
> > +static bool
> > +gimple_fold_obj_type_ref_call (gimple_stmt_iterator *gsi, bool inplace)
> >  {
> > +  gimple stmt = gsi_stmt (*gsi);
> > +  tree ref = gimple_call_fn (stmt);
> >    tree obj = OBJ_TYPE_REF_OBJECT (ref);
> > -  tree known_binfo = known_type ? TYPE_BINFO (known_type) : NULL_TREE;
> > -  tree binfo;
> > +  tree binfo, fndecl, delta;
> > +  HOST_WIDE_INT token;
> >  
> >    if (TREE_CODE (obj) == ADDR_EXPR)
> >      obj = TREE_OPERAND (obj, 0);
> > +  else
> > +    return false;
> >  
> > -  binfo = gimple_get_relevant_ref_binfo (obj, known_binfo);
> > -  if (binfo)
> > +  binfo = gimple_get_relevant_ref_binfo (obj, NULL_TREE);
> > +  if (!binfo)
> > +    return false;
> > +  token = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
> > +  fndecl = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta);
> > +  if (!fndecl)
> > +    return false;
> > +
> > +  if (integer_nonzerop (delta))
> >      {
> > -      HOST_WIDE_INT token = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
> > -      /* If there is no virtual methods leave the OBJ_TYPE_REF alone.  */
> > -      if (!BINFO_VIRTUALS (binfo))
> > -	return NULL_TREE;
> > -      return gimple_fold_obj_type_ref_known_binfo (token, binfo);
> > +      if (inplace)
> > +        return false;
> > +      gimple_adjust_this_by_delta (gsi, delta);
> >      }
> > -  else
> > -    return NULL_TREE;
> > +
> > +  gimple_call_set_fndecl (stmt, fndecl);
> > +  return true;
> >  }
> >  
> >  /* Attempt to fold a call statement referenced by the statement iterator GSI.
> > @@ -1541,8 +1562,8 @@ gimple_fold_obj_type_ref (tree ref, tree
> >     simplifies to a constant value. Return true if any changes were made.
> >     It is assumed that the operands have been previously folded.  */
> >  
> > -static bool
> > -fold_gimple_call (gimple_stmt_iterator *gsi, bool inplace)
> > +bool
> > +gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
> >  {
> >    gimple stmt = gsi_stmt (*gsi);
> >  
> > @@ -1568,18 +1589,8 @@ fold_gimple_call (gimple_stmt_iterator *
> >           copying EH region info to the new node.  Easier to just do it
> >           here where we can just smash the call operand.  */
> >        callee = gimple_call_fn (stmt);
> > -      if (TREE_CODE (callee) == OBJ_TYPE_REF
> > -          && TREE_CODE (OBJ_TYPE_REF_OBJECT (callee)) == ADDR_EXPR)
> > -        {
> > -          tree t;
> > -
> > -          t = gimple_fold_obj_type_ref (callee, NULL_TREE);
> > -          if (t)
> > -            {
> > -              gimple_call_set_fn (stmt, t);
> > -              return true;
> > -            }
> > -        }
> > +      if (TREE_CODE (callee) == OBJ_TYPE_REF)
> > +	return gimple_fold_obj_type_ref_call (gsi, inplace);
> >      }
> >  
> >    return false;
> > @@ -1633,7 +1644,7 @@ fold_stmt_1 (gimple_stmt_iterator *gsi,
> >  		changed = true;
> >  	      }
> >  	  }
> > -      changed |= fold_gimple_call (gsi, inplace);
> > +      changed |= gimple_fold_call (gsi, inplace);
> >        break;
> >  
> >      case GIMPLE_ASM:
> > Index: icln/gcc/gimple.h
> > ===================================================================
> > --- icln.orig/gcc/gimple.h
> > +++ icln/gcc/gimple.h
> > @@ -894,10 +894,10 @@ unsigned get_gimple_rhs_num_ops (enum tr
> >  #define gimple_alloc(c, n) gimple_alloc_stat (c, n MEM_STAT_INFO)
> >  gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL);
> >  const char *gimple_decl_printable_name (tree, int);
> > -tree gimple_fold_obj_type_ref (tree, tree);
> > +bool gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace);
> >  tree gimple_get_relevant_ref_binfo (tree ref, tree known_binfo);
> > -tree gimple_fold_obj_type_ref_known_binfo (HOST_WIDE_INT, tree);
> > -
> > +tree gimple_get_virt_mehtod_for_binfo (HOST_WIDE_INT, tree, tree *);
> > +void gimple_adjust_this_by_delta (gimple_stmt_iterator *, tree);
> >  /* Returns true iff T is a valid GIMPLE statement.  */
> >  extern bool is_gimple_stmt (tree);
> >  
> > Index: icln/gcc/ipa-cp.c
> > ===================================================================
> > --- icln.orig/gcc/ipa-cp.c
> > +++ icln/gcc/ipa-cp.c
> > @@ -1205,7 +1205,7 @@ ipcp_process_devirtualization_opportunit
> >      {
> >        int param_index, types_count, j;
> >        HOST_WIDE_INT token;
> > -      tree target;
> > +      tree target, delta;
> >  
> >        next_ie = ie->next_callee;
> >        if (!ie->indirect_info->polymorphic)
> > @@ -1222,7 +1222,8 @@ ipcp_process_devirtualization_opportunit
> >        for (j = 0; j < types_count; j++)
> >  	{
> >  	  tree binfo = VEC_index (tree, info->params[param_index].types, j);
> > -	  tree t = gimple_fold_obj_type_ref_known_binfo (token, binfo);
> > +	  tree d;
> > +	  tree t = gimple_get_virt_mehtod_for_binfo (token, binfo, &d);
> >  
> >  	  if (!t)
> >  	    {
> > @@ -1230,8 +1231,11 @@ ipcp_process_devirtualization_opportunit
> >  	      break;
> >  	    }
> >  	  else if (!target)
> > -	    target = t;
> > -	  else if (target != t)
> > +	    {
> > +	      target = t;
> > +	      delta = d;
> > +	    }
> > +	  else if (target != t || !tree_int_cst_equal (delta, d))
> >  	    {
> >  	      target = NULL_TREE;
> >  	      break;
> > @@ -1239,7 +1243,7 @@ ipcp_process_devirtualization_opportunit
> >  	}
> >  
> >        if (target)
> > -	ipa_make_edge_direct_to_target (ie, target);
> > +	ipa_make_edge_direct_to_target (ie, target, delta);
> >      }
> >  }
> >  
> > @@ -1279,6 +1283,7 @@ ipcp_discover_new_direct_edges (struct c
> >    for (ie = node->indirect_calls; ie; ie = next_ie)
> >      {
> >        struct cgraph_indirect_call_info *ici = ie->indirect_info;
> > +      tree target, delta = NULL_TREE;
> >  
> >        next_ie = ie->next_callee;
> >        if (ici->param_index != index)
> > @@ -1298,12 +1303,14 @@ ipcp_discover_new_direct_edges (struct c
> >  	    continue;
> >  	  gcc_assert (ie->indirect_info->anc_offset == 0);
> >  	  token = ie->indirect_info->otr_token;
> > -	  cst = gimple_fold_obj_type_ref_known_binfo (token, binfo);
> > -	  if (!cst)
> > +	  target = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta);
> > +	  if (!target)
> >  	    continue;
> >  	}
> > +      else
> > +	target = cst;
> >  
> > -      ipa_make_edge_direct_to_target (ie, cst);
> > +      ipa_make_edge_direct_to_target (ie, target, delta);
> >      }
> >  }
> >  
> > Index: icln/gcc/ipa-prop.c
> > ===================================================================
> > --- icln.orig/gcc/ipa-prop.c
> > +++ icln/gcc/ipa-prop.c
> > @@ -1432,35 +1432,43 @@ update_jump_functions_after_inlining (st
> >  }
> >  
> >  /* If TARGET is an addr_expr of a function declaration, make it the destination
> > -   of an indirect edge IE and return the edge.  Otherwise, return NULL.  */
> > +   of an indirect edge IE and return the edge.  Otherwise, return NULL.  Delta,
> > +   if non-NULL, is an integer constant that must be added to this pointer
> > +   (first parameter).  */
> >  
> >  struct cgraph_edge *
> > -ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target)
> > +ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target, tree delta)
> >  {
> >    struct cgraph_node *callee;
> >  
> > -  if (TREE_CODE (target) != ADDR_EXPR)
> > -    return NULL;
> > -  target = TREE_OPERAND (target, 0);
> > +  if (TREE_CODE (target) == ADDR_EXPR)
> > +    target = TREE_OPERAND (target, 0);
> >    if (TREE_CODE (target) != FUNCTION_DECL)
> >      return NULL;
> >    callee = cgraph_node (target);
> >    if (!callee)
> >      return NULL;
> >    ipa_check_create_node_params ();
> > -  cgraph_make_edge_direct (ie, callee);
> > +
> > +  cgraph_make_edge_direct (ie, callee, delta);
> >    if (dump_file)
> >      {
> >        fprintf (dump_file, "ipa-prop: Discovered %s call to a known target "
> > -	       "(%s/%i -> %s/%i) for stmt ",
> > +	       "(%s/%i -> %s/%i), for stmt ",
> >  	       ie->indirect_info->polymorphic ? "a virtual" : "an indirect",
> >  	       cgraph_node_name (ie->caller), ie->caller->uid,
> >  	       cgraph_node_name (ie->callee), ie->callee->uid);
> > -
> >        if (ie->call_stmt)
> >  	print_gimple_stmt (dump_file, ie->call_stmt, 2, TDF_SLIM);
> >        else
> >  	fprintf (dump_file, "with uid %i\n", ie->lto_stmt_uid);
> > +
> > +      if (delta)
> > +	{
> > +	  fprintf (dump_file, "          Thunk delta is ");
> > +	  print_generic_expr (dump_file, delta, 0);
> > +	  fprintf (dump_file, "\n");
> > +	}
> >      }
> >  
> >    if (ipa_get_cs_argument_count (IPA_EDGE_REF (ie))
> > @@ -1488,7 +1496,7 @@ try_make_edge_direct_simple_call (struct
> >    else
> >      return NULL;
> >  
> > -  return ipa_make_edge_direct_to_target (ie, target);
> > +  return ipa_make_edge_direct_to_target (ie, target, NULL_TREE);
> >  }
> >  
> >  /* Try to find a destination for indirect edge IE that corresponds to a
> > @@ -1500,7 +1508,7 @@ static struct cgraph_edge *
> >  try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
> >  				   struct ipa_jump_func *jfunc)
> >  {
> > -  tree binfo, type, target;
> > +  tree binfo, type, target, delta;
> >    HOST_WIDE_INT token;
> >  
> >    if (jfunc->type == IPA_JF_KNOWN_TYPE)
> > @@ -1524,12 +1532,12 @@ try_make_edge_direct_virtual_call (struc
> >    type = ie->indirect_info->otr_type;
> >    binfo = get_binfo_at_offset (binfo, ie->indirect_info->anc_offset, type);
> >    if (binfo)
> > -    target = gimple_fold_obj_type_ref_known_binfo (token, binfo);
> > +    target = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta);
> >    else
> >      return NULL;
> >  
> >    if (target)
> > -    return ipa_make_edge_direct_to_target (ie, target);
> > +    return ipa_make_edge_direct_to_target (ie, target, delta);
> >    else
> >      return NULL;
> >  }
> > @@ -2520,6 +2528,7 @@ ipa_write_indirect_edge_info (struct out
> >      {
> >        lto_output_sleb128_stream (ob->main_stream, ii->otr_token);
> >        lto_output_tree (ob, ii->otr_type, true);
> > +      lto_output_tree (ob, ii->thunk_delta, true);
> >      }
> >  }
> >  
> > @@ -2542,6 +2551,7 @@ ipa_read_indirect_edge_info (struct lto_
> >      {
> >        ii->otr_token = (HOST_WIDE_INT) lto_input_sleb128 (ib);
> >        ii->otr_type = lto_input_tree (ib, data_in);
> > +      ii->thunk_delta = lto_input_tree (ib, data_in);
> >      }
> >  }
> >  
> > Index: icln/gcc/ipa-prop.h
> > ===================================================================
> > --- icln.orig/gcc/ipa-prop.h
> > +++ icln/gcc/ipa-prop.h
> > @@ -430,7 +430,8 @@ bool ipa_propagate_indirect_call_infos (
> >  					VEC (cgraph_edge_p, heap) **new_edges);
> >  
> >  /* Indirect edge and binfo processing.  */
> > -struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree);
> > +struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree,
> > +						    tree);
> >  
> >  
> >  /* Debugging interface.  */
> > Index: icln/gcc/tree-ssa-ccp.c
> > ===================================================================
> > --- icln.orig/gcc/tree-ssa-ccp.c
> > +++ icln/gcc/tree-ssa-ccp.c
> > @@ -2314,16 +2314,8 @@ ccp_fold_stmt (gimple_stmt_iterator *gsi
> >  	  {
> >  	    tree expr = OBJ_TYPE_REF_EXPR (callee);
> >  	    OBJ_TYPE_REF_EXPR (callee) = valueize_op (expr);
> > -	    if (TREE_CODE (OBJ_TYPE_REF_EXPR (callee)) == ADDR_EXPR)
> > -	      {
> > -		tree t;
> > -		t = gimple_fold_obj_type_ref (callee, NULL_TREE);
> > -		if (t)
> > -		  {
> > -		    gimple_call_set_fn (stmt, t);
> > -		    changed = true;
> > -		  }
> > -	      }
> > +	    if (gimple_fold_call (gsi, false))
> > +	      changed = true;
> >  	    OBJ_TYPE_REF_EXPR (callee) = expr;
> >  	  }
> >  
> > Index: icln/gcc/testsuite/g++.dg/ipa/pr46053.C
> > ===================================================================
> > --- /dev/null
> > +++ icln/gcc/testsuite/g++.dg/ipa/pr46053.C
> > @@ -0,0 +1,41 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-O -fipa-cp -fno-early-inlining"  } */
> > +
> > +extern "C" void abort ();
> > +
> > +struct A
> > +{
> > +  virtual void foo () = 0;
> > +};
> > +
> > +struct B : A
> > +{
> > +  virtual void foo () = 0;
> > +};
> > +
> > +struct C : A
> > +{
> > +};
> > +
> > +struct D : C, B
> > +{
> > +  int i;
> > +  D () : i(0xaaaa) {}
> > +  virtual void foo ()
> > +  {
> > +    if (i != 0xaaaa)
> > +      abort();
> > +  }
> > +};
> > +
> > +static inline void bar (B &b)
> > +{
> > +  b.foo ();
> > +}
> > +
> > +int main()
> > +{
> > +  D d;
> > +  bar (d);
> > +  return 0;
> > +}
> > 
> >
Andi Kleen - Oct. 21, 2010, 5 p.m.
Martin Jambor <mjambor@suse.cz> writes:
>
> In fact, IPA passes get direct calls to thunks horribly wrong.  I have
> just added a loop to main in testsuite/g++.dg/torture/pr45699.C so
> that gcc attempts to inline the direct call to thunk and I got aborts
> caused by ipa-cp and ipa-inline (when enabling either or both).

At least I found devirtualization doesn't even work for very simple C 
pointers, see PR45631
-Andi
Jan Hubicka - Oct. 21, 2010, 10:14 p.m.
> On Thu, 21 Oct 2010, Martin Jambor wrote:
> 
> > Hi,
> > 
> > PR 46053 showed that my previous attempt to incorporate thunks to
> > devirtualization (fix to PR 45699) was only half-baked and did not
> > work when we do devirtualization after IPA propagation of type
> > information (rather obviously, I must admit).
> > 
> > Basically, some thunk or delta information has to be attached to
> > indirect edges representing polymorphic calls too.  However, I did not
> > like the idea of having thunks as callees of cgraph edges and,
> > moreover, there have been concerns that direct calls to thunks might
> > be unintelligible to some our analyses as well.
> > 
> > So I revisited the whole approach to devirtualization to thunks which
> > are there to adjust the this offset by a delta and resorted to adding
> > delta to the parameter directly in the caller.  In order to do this, I
> > save the delta in the indirect_info of an originally-indirect edge.
> > 
> > Because generating a delta addition statement is impossible when
> > folding a statement inplace, I rearranged the folding code a bit as
> > well. 
> > 
> > This patch is to be applied on top of
> > http://gcc.gnu.org/ml/gcc-patches/2010-10/msg01328.html
> > (It might apply and work without it but I have not tried it).
> > 
> > Bootstrapped and tested on x86_64-linux without any problems.  OK for
> > trunk (preferably after the patch referenced above)?
> 
> Without actually looking at the patch I think that call edges
> to thunks make more sense, how'd you otherwise represent a direct
> call to it (and thus possibly inline it)?

At beggining we have no direct calls to thunks.  One approach is to always inlie
the thunk code when turning indirect call to the direct call that IMO makes most
sense.  This I guess can hapen as part of statement folding, but I am not 100% sure
(since we will be producing more statements for one folded call)

Honza
Jan Hubicka - Oct. 21, 2010, 10:32 p.m.
Hi,
the patch seems to make sense in general to me.  I can't comment much of folder
bugs.

I think always inlining thunks even at -Os makes sense as the code will likely
combine (and we do that for non-thunked calls anyway).
An alternative I see is to take multiple entry points seriously on cgraph level:
have edges annotated with entry point, make ipa-prop to generate different jump
functions for every entry point and make inliner to handle thunk adjustments.
This will integrate more seamlessly once (if?) we get real multiple entry point
support.

> +  /* Delta by which must be added to this parameter.  For polymorphic calls
> +     only.  */
> +  tree thunk_delta;

Thunks can get bit more complex than this (i.e. the covariant thunk etc).  I think you
need to save all of thunk_info and make folding logic to match to all the adjustments
cgraph does.

Honza

Patch

Index: icln/gcc/cgraph.c
===================================================================
--- icln.orig/gcc/cgraph.c
+++ icln/gcc/cgraph.c
@@ -847,7 +847,7 @@  cgraph_set_call_stmt (struct cgraph_edge
 	 indirect call into a direct one.  */
       struct cgraph_node *new_callee = cgraph_node (decl);
 
-      cgraph_make_edge_direct (e, new_callee);
+      cgraph_make_edge_direct (e, new_callee, NULL);
     }
 
   push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
@@ -1184,12 +1184,15 @@  cgraph_redirect_edge_callee (struct cgra
 }
 
 /* Make an indirect EDGE with an unknown callee an ordinary edge leading to
-   CALLEE.  */
+   CALLEE.  DELTA, if non-NULL, is an integer constant that is to be added to
+   the this pointer (first parameter).  */
 
 void
-cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee)
+cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee,
+			 tree delta)
 {
   edge->indirect_unknown_callee = 0;
+  edge->indirect_info->thunk_delta = delta;
 
   /* Get the edge out of the indirect edge list. */
   if (edge->prev_callee)
@@ -2101,8 +2104,16 @@  cgraph_clone_edge (struct cgraph_edge *e
 	}
     }
   else
-    new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq,
-				   e->loop_nest + loop_nest);
+    {
+      new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq,
+				     e->loop_nest + loop_nest);
+      if (e->indirect_info)
+	{
+	  new_edge->indirect_info
+	    = ggc_alloc_cleared_cgraph_indirect_call_info ();
+	  *new_edge->indirect_info = *e->indirect_info;
+	}
+    }
 
   new_edge->inline_failed = e->inline_failed;
   new_edge->indirect_inlining_edge = e->indirect_inlining_edge;
Index: icln/gcc/cgraph.h
===================================================================
--- icln.orig/gcc/cgraph.h
+++ icln/gcc/cgraph.h
@@ -398,6 +398,9 @@  struct GTY(()) cgraph_indirect_call_info
   HOST_WIDE_INT otr_token;
   /* Type of the object from OBJ_TYPE_REF_OBJECT. */
   tree otr_type;
+  /* Delta by which must be added to this parameter.  For polymorphic calls
+     only.  */
+  tree thunk_delta;
   /* Index of the parameter that is called.  */
   int param_index;
   /* ECF flags determined from the caller.  */
@@ -585,7 +588,7 @@  struct cgraph_node * cgraph_clone_node (
 					int, bool, VEC(cgraph_edge_p,heap) *);
 
 void cgraph_redirect_edge_callee (struct cgraph_edge *, struct cgraph_node *);
-void cgraph_make_edge_direct (struct cgraph_edge *, struct cgraph_node *);
+void cgraph_make_edge_direct (struct cgraph_edge *, struct cgraph_node *, tree);
 
 struct cgraph_asm_node *cgraph_add_asm_node (tree);
 
Index: icln/gcc/cgraphunit.c
===================================================================
--- icln.orig/gcc/cgraphunit.c
+++ icln/gcc/cgraphunit.c
@@ -2115,6 +2115,8 @@  cgraph_redirect_edge_call_stmt_to_callee
 {
   tree decl = gimple_call_fndecl (e->call_stmt);
   gimple new_stmt;
+  gimple_stmt_iterator gsi;
+  bool gsi_computed = false;
 #ifdef ENABLE_CHECKING
   struct cgraph_node *node;
 #endif
@@ -2147,9 +2149,24 @@  cgraph_redirect_edge_call_stmt_to_callee
 	}
     }
 
+  if (e->indirect_info && e->indirect_info->thunk_delta
+      && integer_nonzerop (e->indirect_info->thunk_delta))
+    {
+      if (cgraph_dump_file)
+	{
+	  fprintf (cgraph_dump_file, "          Thunk delta is ");
+	  print_generic_expr (cgraph_dump_file,
+			      e->indirect_info->thunk_delta, 0);
+	  fprintf (cgraph_dump_file, "\n");
+	}
+      gsi = gsi_for_stmt (e->call_stmt);
+      gsi_computed = true;
+      gimple_adjust_this_by_delta (&gsi, e->indirect_info->thunk_delta);
+      e->indirect_info->thunk_delta = NULL_TREE;
+    }
+
   if (e->callee->clone.combined_args_to_skip)
     {
-      gimple_stmt_iterator gsi;
       int lp_nr;
 
       new_stmt
@@ -2161,7 +2178,8 @@  cgraph_redirect_edge_call_stmt_to_callee
 	  && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
 	SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
 
-      gsi = gsi_for_stmt (e->call_stmt);
+      if (!gsi_computed)
+	gsi = gsi_for_stmt (e->call_stmt);
       gsi_replace (&gsi, new_stmt, false);
       /* We need to defer cleaning EH info on the new statement to
          fixup-cfg.  We may not have dominator information at this point
Index: icln/gcc/gimple-fold.c
===================================================================
--- icln.orig/gcc/gimple-fold.c
+++ icln/gcc/gimple-fold.c
@@ -1459,14 +1459,20 @@  gimple_get_relevant_ref_binfo (tree ref,
 
 /* Fold a OBJ_TYPE_REF expression to the address of a function. TOKEN is
    integer form of OBJ_TYPE_REF_TOKEN of the reference expression.  KNOWN_BINFO
-   carries the binfo describing the true type of OBJ_TYPE_REF_OBJECT(REF).  */
+   carries the binfo describing the true type of OBJ_TYPE_REF_OBJECT(REF).
+   Delta, if non-NULL, is an integer contant that should be added to this
+   pointer (first parameter). */
 
 tree
-gimple_fold_obj_type_ref_known_binfo (HOST_WIDE_INT token, tree known_binfo)
+gimple_get_virt_mehtod_for_binfo (HOST_WIDE_INT token, tree known_binfo,
+				  tree *delta)
 {
   HOST_WIDE_INT i;
-  tree v, fndecl, delta;
+  tree v, fndecl;
 
+  /* If there is no virtual methods leave the OBJ_TYPE_REF alone.  */
+  if (!BINFO_VIRTUALS (known_binfo))
+    return NULL_TREE;
   gcc_assert (BINFO_NEW_VTABLE_MARKED (known_binfo));
   v = BINFO_VIRTUALS (known_binfo);
   i = 0;
@@ -1478,62 +1484,77 @@  gimple_fold_obj_type_ref_known_binfo (HO
     }
 
   fndecl = TREE_VALUE (v);
-  delta = TREE_PURPOSE (v);
-  gcc_assert (host_integerp (delta, 0));
-
-  if (integer_nonzerop (delta))
-    {
-      struct cgraph_node *node = cgraph_get_node (fndecl);
-      HOST_WIDE_INT off = tree_low_cst (delta, 0);
-
-      if (!node)
-        return NULL;
-      for (node = node->same_body; node; node = node->next)
-        if (node->thunk.thunk_p && off == node->thunk.fixed_offset)
-          break;
-      if (node)
-        fndecl = node->decl;
-      else
-        return NULL;
-     }
+  *delta = TREE_PURPOSE (v);
+  gcc_checking_assert (host_integerp (*delta, 0));
 
   /* When cgraph node is missing and function is not public, we cannot
      devirtualize.  This can happen in WHOPR when the actual method
      ends up in other partition, because we found devirtualization
      possibility too late.  */
-  if (!can_refer_decl_in_current_unit_p (fndecl))
-    return NULL;
-  return build_fold_addr_expr (fndecl);
+  if (!can_refer_decl_in_current_unit_p (TREE_VALUE (v)))
+    return NULL_TREE;
+  return fndecl;
 }
 
+/* Generate code adjusting the first parameter of a call statement determined
+   by GSI by DELTA.  */
 
-/* Fold a OBJ_TYPE_REF expression to the address of a function.  If KNOWN_TYPE
-   is not NULL_TREE, it is the true type of the outmost encapsulating object if
-   that comes from a pointer SSA_NAME.  If the true outmost encapsulating type
-   can be determined from a declaration OBJ_TYPE_REF_OBJECT(REF), it is used
-   regardless of KNOWN_TYPE (which thus can be NULL_TREE).  */
+void
+gimple_adjust_this_by_delta (gimple_stmt_iterator *gsi, tree delta)
+{
+  gimple call_stmt = gsi_stmt (*gsi);
+  tree parm, tmp;
+  gimple new_stmt;
+
+  delta = fold_convert (sizetype, delta);
+  gcc_assert (gimple_call_num_args (call_stmt) >= 1);
+  parm = gimple_call_arg (call_stmt, 0);
+  gcc_assert (POINTER_TYPE_P (TREE_TYPE (parm)));
+  tmp = create_tmp_var (TREE_TYPE (parm), NULL);
+  add_referenced_var (tmp);
+
+  tmp = make_ssa_name (tmp, NULL);
+  new_stmt = gimple_build_assign_with_ops (POINTER_PLUS_EXPR, tmp, parm, delta);
+  SSA_NAME_DEF_STMT (tmp) = new_stmt;
+  gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT);
+  gimple_call_set_arg (call_stmt, 0, tmp);
+}
 
-tree
-gimple_fold_obj_type_ref (tree ref, tree known_type)
+/* Fold a call statement to OBJ_TYPE_REF to a direct call, if possible.  GSI
+   determines the statement, generating new statements is allowed only if
+   INPLACE is false.  Return true iff the statement was changed.  */
+
+static bool
+gimple_fold_obj_type_ref_call (gimple_stmt_iterator *gsi, bool inplace)
 {
+  gimple stmt = gsi_stmt (*gsi);
+  tree ref = gimple_call_fn (stmt);
   tree obj = OBJ_TYPE_REF_OBJECT (ref);
-  tree known_binfo = known_type ? TYPE_BINFO (known_type) : NULL_TREE;
-  tree binfo;
+  tree binfo, fndecl, delta;
+  HOST_WIDE_INT token;
 
   if (TREE_CODE (obj) == ADDR_EXPR)
     obj = TREE_OPERAND (obj, 0);
+  else
+    return false;
 
-  binfo = gimple_get_relevant_ref_binfo (obj, known_binfo);
-  if (binfo)
+  binfo = gimple_get_relevant_ref_binfo (obj, NULL_TREE);
+  if (!binfo)
+    return false;
+  token = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
+  fndecl = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta);
+  if (!fndecl)
+    return false;
+
+  if (integer_nonzerop (delta))
     {
-      HOST_WIDE_INT token = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
-      /* If there is no virtual methods leave the OBJ_TYPE_REF alone.  */
-      if (!BINFO_VIRTUALS (binfo))
-	return NULL_TREE;
-      return gimple_fold_obj_type_ref_known_binfo (token, binfo);
+      if (inplace)
+        return false;
+      gimple_adjust_this_by_delta (gsi, delta);
     }
-  else
-    return NULL_TREE;
+
+  gimple_call_set_fndecl (stmt, fndecl);
+  return true;
 }
 
 /* Attempt to fold a call statement referenced by the statement iterator GSI.
@@ -1541,8 +1562,8 @@  gimple_fold_obj_type_ref (tree ref, tree
    simplifies to a constant value. Return true if any changes were made.
    It is assumed that the operands have been previously folded.  */
 
-static bool
-fold_gimple_call (gimple_stmt_iterator *gsi, bool inplace)
+bool
+gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
 {
   gimple stmt = gsi_stmt (*gsi);
 
@@ -1568,18 +1589,8 @@  fold_gimple_call (gimple_stmt_iterator *
          copying EH region info to the new node.  Easier to just do it
          here where we can just smash the call operand.  */
       callee = gimple_call_fn (stmt);
-      if (TREE_CODE (callee) == OBJ_TYPE_REF
-          && TREE_CODE (OBJ_TYPE_REF_OBJECT (callee)) == ADDR_EXPR)
-        {
-          tree t;
-
-          t = gimple_fold_obj_type_ref (callee, NULL_TREE);
-          if (t)
-            {
-              gimple_call_set_fn (stmt, t);
-              return true;
-            }
-        }
+      if (TREE_CODE (callee) == OBJ_TYPE_REF)
+	return gimple_fold_obj_type_ref_call (gsi, inplace);
     }
 
   return false;
@@ -1633,7 +1644,7 @@  fold_stmt_1 (gimple_stmt_iterator *gsi,
 		changed = true;
 	      }
 	  }
-      changed |= fold_gimple_call (gsi, inplace);
+      changed |= gimple_fold_call (gsi, inplace);
       break;
 
     case GIMPLE_ASM:
Index: icln/gcc/gimple.h
===================================================================
--- icln.orig/gcc/gimple.h
+++ icln/gcc/gimple.h
@@ -894,10 +894,10 @@  unsigned get_gimple_rhs_num_ops (enum tr
 #define gimple_alloc(c, n) gimple_alloc_stat (c, n MEM_STAT_INFO)
 gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL);
 const char *gimple_decl_printable_name (tree, int);
-tree gimple_fold_obj_type_ref (tree, tree);
+bool gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace);
 tree gimple_get_relevant_ref_binfo (tree ref, tree known_binfo);
-tree gimple_fold_obj_type_ref_known_binfo (HOST_WIDE_INT, tree);
-
+tree gimple_get_virt_mehtod_for_binfo (HOST_WIDE_INT, tree, tree *);
+void gimple_adjust_this_by_delta (gimple_stmt_iterator *, tree);
 /* Returns true iff T is a valid GIMPLE statement.  */
 extern bool is_gimple_stmt (tree);
 
Index: icln/gcc/ipa-cp.c
===================================================================
--- icln.orig/gcc/ipa-cp.c
+++ icln/gcc/ipa-cp.c
@@ -1205,7 +1205,7 @@  ipcp_process_devirtualization_opportunit
     {
       int param_index, types_count, j;
       HOST_WIDE_INT token;
-      tree target;
+      tree target, delta;
 
       next_ie = ie->next_callee;
       if (!ie->indirect_info->polymorphic)
@@ -1222,7 +1222,8 @@  ipcp_process_devirtualization_opportunit
       for (j = 0; j < types_count; j++)
 	{
 	  tree binfo = VEC_index (tree, info->params[param_index].types, j);
-	  tree t = gimple_fold_obj_type_ref_known_binfo (token, binfo);
+	  tree d;
+	  tree t = gimple_get_virt_mehtod_for_binfo (token, binfo, &d);
 
 	  if (!t)
 	    {
@@ -1230,8 +1231,11 @@  ipcp_process_devirtualization_opportunit
 	      break;
 	    }
 	  else if (!target)
-	    target = t;
-	  else if (target != t)
+	    {
+	      target = t;
+	      delta = d;
+	    }
+	  else if (target != t || !tree_int_cst_equal (delta, d))
 	    {
 	      target = NULL_TREE;
 	      break;
@@ -1239,7 +1243,7 @@  ipcp_process_devirtualization_opportunit
 	}
 
       if (target)
-	ipa_make_edge_direct_to_target (ie, target);
+	ipa_make_edge_direct_to_target (ie, target, delta);
     }
 }
 
@@ -1279,6 +1283,7 @@  ipcp_discover_new_direct_edges (struct c
   for (ie = node->indirect_calls; ie; ie = next_ie)
     {
       struct cgraph_indirect_call_info *ici = ie->indirect_info;
+      tree target, delta = NULL_TREE;
 
       next_ie = ie->next_callee;
       if (ici->param_index != index)
@@ -1298,12 +1303,14 @@  ipcp_discover_new_direct_edges (struct c
 	    continue;
 	  gcc_assert (ie->indirect_info->anc_offset == 0);
 	  token = ie->indirect_info->otr_token;
-	  cst = gimple_fold_obj_type_ref_known_binfo (token, binfo);
-	  if (!cst)
+	  target = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta);
+	  if (!target)
 	    continue;
 	}
+      else
+	target = cst;
 
-      ipa_make_edge_direct_to_target (ie, cst);
+      ipa_make_edge_direct_to_target (ie, target, delta);
     }
 }
 
Index: icln/gcc/ipa-prop.c
===================================================================
--- icln.orig/gcc/ipa-prop.c
+++ icln/gcc/ipa-prop.c
@@ -1432,35 +1432,43 @@  update_jump_functions_after_inlining (st
 }
 
 /* If TARGET is an addr_expr of a function declaration, make it the destination
-   of an indirect edge IE and return the edge.  Otherwise, return NULL.  */
+   of an indirect edge IE and return the edge.  Otherwise, return NULL.  Delta,
+   if non-NULL, is an integer constant that must be added to this pointer
+   (first parameter).  */
 
 struct cgraph_edge *
-ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target)
+ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target, tree delta)
 {
   struct cgraph_node *callee;
 
-  if (TREE_CODE (target) != ADDR_EXPR)
-    return NULL;
-  target = TREE_OPERAND (target, 0);
+  if (TREE_CODE (target) == ADDR_EXPR)
+    target = TREE_OPERAND (target, 0);
   if (TREE_CODE (target) != FUNCTION_DECL)
     return NULL;
   callee = cgraph_node (target);
   if (!callee)
     return NULL;
   ipa_check_create_node_params ();
-  cgraph_make_edge_direct (ie, callee);
+
+  cgraph_make_edge_direct (ie, callee, delta);
   if (dump_file)
     {
       fprintf (dump_file, "ipa-prop: Discovered %s call to a known target "
-	       "(%s/%i -> %s/%i) for stmt ",
+	       "(%s/%i -> %s/%i), for stmt ",
 	       ie->indirect_info->polymorphic ? "a virtual" : "an indirect",
 	       cgraph_node_name (ie->caller), ie->caller->uid,
 	       cgraph_node_name (ie->callee), ie->callee->uid);
-
       if (ie->call_stmt)
 	print_gimple_stmt (dump_file, ie->call_stmt, 2, TDF_SLIM);
       else
 	fprintf (dump_file, "with uid %i\n", ie->lto_stmt_uid);
+
+      if (delta)
+	{
+	  fprintf (dump_file, "          Thunk delta is ");
+	  print_generic_expr (dump_file, delta, 0);
+	  fprintf (dump_file, "\n");
+	}
     }
 
   if (ipa_get_cs_argument_count (IPA_EDGE_REF (ie))
@@ -1488,7 +1496,7 @@  try_make_edge_direct_simple_call (struct
   else
     return NULL;
 
-  return ipa_make_edge_direct_to_target (ie, target);
+  return ipa_make_edge_direct_to_target (ie, target, NULL_TREE);
 }
 
 /* Try to find a destination for indirect edge IE that corresponds to a
@@ -1500,7 +1508,7 @@  static struct cgraph_edge *
 try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
 				   struct ipa_jump_func *jfunc)
 {
-  tree binfo, type, target;
+  tree binfo, type, target, delta;
   HOST_WIDE_INT token;
 
   if (jfunc->type == IPA_JF_KNOWN_TYPE)
@@ -1524,12 +1532,12 @@  try_make_edge_direct_virtual_call (struc
   type = ie->indirect_info->otr_type;
   binfo = get_binfo_at_offset (binfo, ie->indirect_info->anc_offset, type);
   if (binfo)
-    target = gimple_fold_obj_type_ref_known_binfo (token, binfo);
+    target = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta);
   else
     return NULL;
 
   if (target)
-    return ipa_make_edge_direct_to_target (ie, target);
+    return ipa_make_edge_direct_to_target (ie, target, delta);
   else
     return NULL;
 }
@@ -2520,6 +2528,7 @@  ipa_write_indirect_edge_info (struct out
     {
       lto_output_sleb128_stream (ob->main_stream, ii->otr_token);
       lto_output_tree (ob, ii->otr_type, true);
+      lto_output_tree (ob, ii->thunk_delta, true);
     }
 }
 
@@ -2542,6 +2551,7 @@  ipa_read_indirect_edge_info (struct lto_
     {
       ii->otr_token = (HOST_WIDE_INT) lto_input_sleb128 (ib);
       ii->otr_type = lto_input_tree (ib, data_in);
+      ii->thunk_delta = lto_input_tree (ib, data_in);
     }
 }
 
Index: icln/gcc/ipa-prop.h
===================================================================
--- icln.orig/gcc/ipa-prop.h
+++ icln/gcc/ipa-prop.h
@@ -430,7 +430,8 @@  bool ipa_propagate_indirect_call_infos (
 					VEC (cgraph_edge_p, heap) **new_edges);
 
 /* Indirect edge and binfo processing.  */
-struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree);
+struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree,
+						    tree);
 
 
 /* Debugging interface.  */
Index: icln/gcc/tree-ssa-ccp.c
===================================================================
--- icln.orig/gcc/tree-ssa-ccp.c
+++ icln/gcc/tree-ssa-ccp.c
@@ -2314,16 +2314,8 @@  ccp_fold_stmt (gimple_stmt_iterator *gsi
 	  {
 	    tree expr = OBJ_TYPE_REF_EXPR (callee);
 	    OBJ_TYPE_REF_EXPR (callee) = valueize_op (expr);
-	    if (TREE_CODE (OBJ_TYPE_REF_EXPR (callee)) == ADDR_EXPR)
-	      {
-		tree t;
-		t = gimple_fold_obj_type_ref (callee, NULL_TREE);
-		if (t)
-		  {
-		    gimple_call_set_fn (stmt, t);
-		    changed = true;
-		  }
-	      }
+	    if (gimple_fold_call (gsi, false))
+	      changed = true;
 	    OBJ_TYPE_REF_EXPR (callee) = expr;
 	  }
 
Index: icln/gcc/testsuite/g++.dg/ipa/pr46053.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/ipa/pr46053.C
@@ -0,0 +1,41 @@ 
+/* { dg-do run } */
+/* { dg-options "-O -fipa-cp -fno-early-inlining"  } */
+
+extern "C" void abort ();
+
+struct A
+{
+  virtual void foo () = 0;
+};
+
+struct B : A
+{
+  virtual void foo () = 0;
+};
+
+struct C : A
+{
+};
+
+struct D : C, B
+{
+  int i;
+  D () : i(0xaaaa) {}
+  virtual void foo ()
+  {
+    if (i != 0xaaaa)
+      abort();
+  }
+};
+
+static inline void bar (B &b)
+{
+  b.foo ();
+}
+
+int main()
+{
+  D d;
+  bar (d);
+  return 0;
+}