diff mbox

[3/5] Use aggregate jump functions in indirect inlining

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

Commit Message

Martin Jambor June 1, 2012, 12:02 a.m. UTC
Hi,

this patch uses the aggregate jump functions collected by the previous
one to do indirect inlining.  It can do stuff we could not do before
(see the testcase) and also removes the special constant jump
functions representing C++ function member pointers and represents
that information with ordinary aggregate jump functions and is also
able to indirectly inline through them.

I hoped I'd be able to get rid of type_like_member_ptr_p altogether
but so far I did not and still use it when examining indirect calls
which are from member pointers (we need to match slightly complex
pattern, see ipa_analyze_indirect_call_uses).  The reason is that
there are architectures that store the bit deciding whether the
pointer represents a normal or virtual function in delta, not in the
function pointer field itself and thus I'd need to check values of two
aggregate fields to make sure we do not inline wrong function.  For
now, I needed to moved on and so kept the type check as it is.
It is however ugly and I plan to return to this later.

The patch passed bootstrap and testing on x86_64-linux.

Thanks,

Martin


2012-05-30  Martin Jambor  <mjambor@suse.cz>

	* cgraph.h (cgraph_indirect_call_info): Field anc_offset renamed to
	offset.  New field agg_contents.

	* ipa-prop.h (jump_func_type): Removed IPA_JF_CONST_MEMBER_PTR.
	(ipa_member_ptr_cst): Removed.
	(ipa_jump_func): Removed field member_cst.
	(ipa_find_agg_cst_for_param): Declare.
	(ipa_get_jf_member_ptr_pfn): Removed.

	* ipa-prop.c (ipa_print_node_jump_functions_for_edge): Do not dumo
	const member functions.
	(ipa_set_jf_member_ptr_cst): Removed.
	(type_like_member_ptr_p): Also check field position are known and
	sane.
	(determine_cst_member_ptr): Removed.
	(ipa_compute_jump_functions_for_edge): Remove creation of member
	pointer jump functions.  Simplify.
	(ipa_get_member_ptr_load_param): Incorporate into
	ipa_get_stmt_member_ptr_load_param, also return offset of the required
	field.  Update the remaining caller.
	(ipa_note_param_call): Initialize new fields of
	cgraph_indirect_call_info.
	(ipa_analyze_indirect_call_uses): Also look for simple pointers loaded
	from aggregates.  In such cases, store offset of the called field.
	(ipa_find_agg_cst_for_param): New function.
	(try_make_edge_direct_simple_call): Handle called aggregate values.
	(update_indirect_edges_after_inlining): Update after various
	identifier and parameter changes.
	(ipa_write_jump_function): Do not stream member pointer constant jump
	functions.
	(ipa_read_jump_function): Likewise.
	(ipa_write_indirect_edge_info): Stream new cgraph_indirect_call_info
	fields.
	(ipa_read_indirect_edge_info): Likewise.

	* testsuite/gcc.dg/ipa/iinline-4.c: New test.
diff mbox

Patch

Index: src/gcc/cgraph.h
===================================================================
--- src.orig/gcc/cgraph.h
+++ src/gcc/cgraph.h
@@ -338,9 +338,11 @@  typedef enum cgraph_inline_failed_enum {
 
 struct GTY(()) cgraph_indirect_call_info
 {
-  /* Offset accumulated from ancestor jump functions of inlined call graph
-     edges.  */
-  HOST_WIDE_INT anc_offset;
+  /* When polymorphic is set, this field contains offset where the object which
+     was actually used in the polymorphic resides within a larger structure.
+     If agg_contents is set, the field contains the offset within the aggregate
+     from which the address to call was loaded.  */
+  HOST_WIDE_INT offset;
   /* OBJ_TYPE_REF_TOKEN of a polymorphic call (if polymorphic is set).  */
   HOST_WIDE_INT otr_token;
   /* Type of the object from OBJ_TYPE_REF_OBJECT. */
@@ -353,6 +355,9 @@  struct GTY(()) cgraph_indirect_call_info
   /* Set when the call is a virtual call with the parameter being the
      associated object pointer rather than a simple direct call.  */
   unsigned polymorphic : 1;
+  /* Set when the call is a call of a pointer loaded from contents of an
+     aggregate at offset.  */
+  unsigned agg_contents : 1;
 };
 
 struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgraph_edge {
Index: src/gcc/ipa-prop.c
===================================================================
--- src.orig/gcc/ipa-prop.c
+++ src/gcc/ipa-prop.c
@@ -182,14 +182,6 @@  ipa_print_node_jump_functions_for_edge (
 	    }
 	  fprintf (f, "\n");
 	}
-      else if (type == IPA_JF_CONST_MEMBER_PTR)
-	{
-	  fprintf (f, "CONST MEMBER PTR: ");
-	  print_generic_expr (f, jump_func->value.member_cst.pfn, 0);
-	  fprintf (f, ", ");
-	  print_generic_expr (f, jump_func->value.member_cst.delta, 0);
-	  fprintf (f, "\n");
-	}
       else if (type == IPA_JF_PASS_THROUGH)
 	{
 	  fprintf (f, "PASS THROUGH: ");
@@ -356,18 +348,6 @@  ipa_set_ancestor_jf (struct ipa_jump_fun
   jfunc->value.ancestor.agg_preserved = agg_preserved;
 }
 
-/* Simple function filling in a member pointer constant jump function (with PFN
-   and DELTA as the constant value) into JFUNC.  */
-
-static void
-ipa_set_jf_member_ptr_cst (struct ipa_jump_func *jfunc,
-			   tree pfn, tree delta)
-{
-  jfunc->type = IPA_JF_CONST_MEMBER_PTR;
-  jfunc->value.member_cst.pfn = pfn;
-  jfunc->value.member_cst.delta = delta;
-}
-
 /* Structure to be passed in between detect_type_change and
    check_stmt_for_type_change.  */
 
@@ -1013,14 +993,16 @@  type_like_member_ptr_p (tree type, tree
 
   fld = TYPE_FIELDS (type);
   if (!fld || !POINTER_TYPE_P (TREE_TYPE (fld))
-      || TREE_CODE (TREE_TYPE (TREE_TYPE (fld))) != METHOD_TYPE)
+      || TREE_CODE (TREE_TYPE (TREE_TYPE (fld))) != METHOD_TYPE
+      || !host_integerp (DECL_FIELD_OFFSET (fld), 1))
     return false;
 
   if (method_ptr)
     *method_ptr = fld;
 
   fld = DECL_CHAIN (fld);
-  if (!fld || INTEGRAL_TYPE_P (fld))
+  if (!fld || INTEGRAL_TYPE_P (fld)
+      || !host_integerp (DECL_FIELD_OFFSET (fld), 1))
     return false;
   if (delta)
     *delta = fld;
@@ -1050,83 +1032,6 @@  get_ssa_def_if_simple_copy (tree rhs)
   return rhs;
 }
 
-/* Traverse statements from CALL backwards, scanning whether the argument ARG
-   which is a member pointer is filled in with constant values.  If it is, fill
-   the jump function JFUNC in appropriately.  METHOD_FIELD and DELTA_FIELD are
-   fields of the record type of the member pointer.  To give an example, we
-   look for a pattern looking like the following:
-
-     D.2515.__pfn ={v} printStuff;
-     D.2515.__delta ={v} 0;
-     i_1 = doprinting (D.2515);  */
-
-static void
-determine_cst_member_ptr (gimple call, tree arg, tree method_field,
-			  tree delta_field, struct ipa_jump_func *jfunc)
-{
-  gimple_stmt_iterator gsi;
-  tree method = NULL_TREE;
-  tree delta = NULL_TREE;
-
-  gsi = gsi_for_stmt (call);
-
-  gsi_prev (&gsi);
-  for (; !gsi_end_p (gsi); gsi_prev (&gsi))
-    {
-      gimple stmt = gsi_stmt (gsi);
-      tree lhs, rhs, fld;
-
-      if (!stmt_may_clobber_ref_p (stmt, arg))
-	continue;
-      if (!gimple_assign_single_p (stmt))
-	return;
-
-      lhs = gimple_assign_lhs (stmt);
-      rhs = gimple_assign_rhs1 (stmt);
-
-      if (TREE_CODE (lhs) != COMPONENT_REF
-	  || TREE_OPERAND (lhs, 0) != arg)
-	return;
-
-      fld = TREE_OPERAND (lhs, 1);
-      if (!method && fld == method_field)
-	{
-	  rhs = get_ssa_def_if_simple_copy (rhs);
-	  if (TREE_CODE (rhs) == ADDR_EXPR
-	      && TREE_CODE (TREE_OPERAND (rhs, 0)) == FUNCTION_DECL
-	      && TREE_CODE (TREE_TYPE (TREE_OPERAND (rhs, 0))) == METHOD_TYPE)
-	    {
-	      method = TREE_OPERAND (rhs, 0);
-	      if (delta)
-		{
-		  ipa_set_jf_member_ptr_cst (jfunc, rhs, delta);
-		  return;
-		}
-	    }
-	  else
-	    return;
-	}
-
-      if (!delta && fld == delta_field)
-	{
-	  rhs = get_ssa_def_if_simple_copy (rhs);
-	  if (TREE_CODE (rhs) == INTEGER_CST)
-	    {
-	      delta = rhs;
-	      if (method)
-		{
-		  ipa_set_jf_member_ptr_cst (jfunc, rhs, delta);
-		  return;
-		}
-	    }
-	  else
-	    return;
-	}
-    }
-
-  return;
-}
-
 /* Helper for determine_known_aggregate_parts, initializes *R for an aggregate
    passed by reference based on BASE and with the given TYPE.  */
 
@@ -1383,32 +1288,20 @@  ipa_compute_jump_functions_for_edge (str
 
       if (is_gimple_ip_invariant (arg))
 	ipa_set_jf_constant (jfunc, arg);
-      else if (!is_gimple_reg_type (TREE_TYPE (arg)))
+      else if (!is_gimple_reg_type (TREE_TYPE (arg))
+	       && TREE_CODE (arg) == PARM_DECL)
 	{
-	  tree method_field, delta_field;
-
-	  /* Aggregate passed by value, check for pass-through, otherwise fill
-	     in aggregate contents.  */
+	  int index = ipa_get_param_decl_index (info, arg);
 
-	  if (TREE_CODE (arg) == PARM_DECL)
+	  gcc_assert (index >=0);
+	  /* Aggregate passed by value, check for pass-through, otherwise we
+	     will attempt to fill in aggregate contents later in this
+	     function.  */
+	  if (is_parm_preserved_before_stmt (&parms_ainfo[index], call, arg))
 	    {
-	      int index = ipa_get_param_decl_index (info, arg);
-	      gcc_assert (index >=0);
-	      if (is_parm_preserved_before_stmt (&parms_ainfo[index], call,
-						 arg))
-		{
-		  ipa_set_jf_simple_pass_through (jfunc, index, true);
-		  continue;
-		}
+	      ipa_set_jf_simple_pass_through (jfunc, index, true);
+	      continue;
 	    }
-
-	  /* TODO: The call to determine_cst_member_ptr will be removed by a
-	     subsequent patch which will do away with IPA_JF_CONST_MEMBER_PTR
-	     altogether.  */
-	  if (type_like_member_ptr_p (TREE_TYPE (arg), &method_field,
-				      &delta_field))
-	    determine_cst_member_ptr (call, arg, method_field, delta_field,
-				      jfunc);
 	}
       else if (TREE_CODE (arg) == SSA_NAME)
 	{
@@ -1442,7 +1335,6 @@  ipa_compute_jump_functions_for_edge (str
 	      || !ipa_get_jf_pass_through_agg_preserved (jfunc))
 	  && (jfunc->type != IPA_JF_ANCESTOR
 	      || !ipa_get_jf_ancestor_agg_preserved (jfunc))
-	  && jfunc->type != IPA_JF_CONST_MEMBER_PTR
 	  && (AGGREGATE_TYPE_P (TREE_TYPE (arg))
 	      || (POINTER_TYPE_P (TREE_TYPE (arg))
 		  && AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (arg))))))
@@ -1474,16 +1366,22 @@  ipa_compute_jump_functions (struct cgrap
     ipa_compute_jump_functions_for_edge (parms_ainfo, cs);
 }
 
-/* If RHS looks like a rhs of a statement loading pfn from a member
-   pointer formal parameter, return the parameter, otherwise return
-   NULL.  If USE_DELTA, then we look for a use of the delta field
-   rather than the pfn.  */
+/* If STMT looks like a statement loading a value from a member pointer formal
+   parameter, return that parameter and store the offset of the field to
+   *OFFSET_P, if it is non-NULL.  Otherwise return NULL (but *OFFSET_P still
+   might be clobbered).  If USE_DELTA, then we look for a use of the delta
+   field rather than the pfn.  */
 
 static tree
-ipa_get_member_ptr_load_param (tree rhs, bool use_delta)
+ipa_get_stmt_member_ptr_load_param (gimple stmt, bool use_delta,
+				    HOST_WIDE_INT *offset_p)
 {
-  tree rec, ref_field, ref_offset, fld, fld_offset, ptr_field, delta_field;
+  tree rhs, rec, ref_field, ref_offset, fld, ptr_field, delta_field;
+
+  if (!gimple_assign_single_p (stmt))
+    return NULL_TREE;
 
+  rhs = gimple_assign_rhs1 (stmt);
   if (TREE_CODE (rhs) == COMPONENT_REF)
     {
       ref_field = TREE_OPERAND (rhs, 1);
@@ -1500,43 +1398,24 @@  ipa_get_member_ptr_load_param (tree rhs,
   if (TREE_CODE (rec) != PARM_DECL
       || !type_like_member_ptr_p (TREE_TYPE (rec), &ptr_field, &delta_field))
     return NULL_TREE;
-
   ref_offset = TREE_OPERAND (rhs, 1);
 
+  if (use_delta)
+    fld = delta_field;
+  else
+    fld = ptr_field;
+  if (offset_p)
+    *offset_p = int_bit_position (fld);
+
   if (ref_field)
     {
       if (integer_nonzerop (ref_offset))
 	return NULL_TREE;
-
-      if (use_delta)
-	fld = delta_field;
-      else
-	fld = ptr_field;
-
       return ref_field == fld ? rec : NULL_TREE;
     }
-
-  if (use_delta)
-    fld_offset = byte_position (delta_field);
   else
-    fld_offset = byte_position (ptr_field);
-
-  return tree_int_cst_equal (ref_offset, fld_offset) ? rec : NULL_TREE;
-}
-
-/* If STMT looks like a statement loading a value from a member pointer formal
-   parameter, this function returns that parameter.  */
-
-static tree
-ipa_get_stmt_member_ptr_load_param (gimple stmt, bool use_delta)
-{
-  tree rhs;
-
-  if (!gimple_assign_single_p (stmt))
-    return NULL_TREE;
-
-  rhs = gimple_assign_rhs1 (stmt);
-  return ipa_get_member_ptr_load_param (rhs, use_delta);
+    return tree_int_cst_equal (byte_position (fld), ref_offset) ? rec
+      : NULL_TREE;
 }
 
 /* Returns true iff T is an SSA_NAME defined by a statement.  */
@@ -1562,8 +1441,9 @@  ipa_note_param_call (struct cgraph_node
 
   cs = cgraph_edge (node, stmt);
   cs->indirect_info->param_index = param_index;
-  cs->indirect_info->anc_offset = 0;
+  cs->indirect_info->offset = 0;
   cs->indirect_info->polymorphic = 0;
+  cs->indirect_info->agg_contents = 0;
   return cs;
 }
 
@@ -1622,7 +1502,9 @@  ipa_note_param_call (struct cgraph_node
 
        return (S.*f)(4);
      }
-*/
+
+   Moreover, the function also looks for called pointers loaded from aggregates
+   passed by value or reference.  */
 
 static void
 ipa_analyze_indirect_call_uses (struct cgraph_node *node,
@@ -1637,6 +1519,7 @@  ipa_analyze_indirect_call_uses (struct c
   gimple branch;
   int index;
   basic_block bb, virt_bb, join;
+  HOST_WIDE_INT offset;
 
   if (SSA_NAME_IS_DEFAULT_DEF (target))
     {
@@ -1647,18 +1530,52 @@  ipa_analyze_indirect_call_uses (struct c
       return;
     }
 
-  /* Now we need to try to match the complex pattern of calling a member
-     pointer. */
+  def = SSA_NAME_DEF_STMT (target);
+  if (gimple_assign_single_p (def))
+    {
+      struct cgraph_edge *cs;
+      HOST_WIDE_INT size, max_size;
+      tree base = get_ref_base_and_extent (gimple_assign_rhs1 (def),
+					   &offset, &size, &max_size);
+      if (max_size == -1 || max_size != size || offset < 0)
+	return;
 
-  if (!POINTER_TYPE_P (TREE_TYPE (target))
-      || TREE_CODE (TREE_TYPE (TREE_TYPE (target))) != METHOD_TYPE)
-    return;
+      if (DECL_P (base))
+	{
+	  index = ipa_get_param_decl_index (info, base);
+	  if (index < 0 || !is_parm_preserved_before_stmt (&parms_ainfo[index],
+							   call, base))
+	    return;
+	}
+      else if (TREE_CODE (base) == MEM_REF
+	       && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME
+	       && SSA_NAME_IS_DEFAULT_DEF (TREE_OPERAND (base, 0))
+	       && integer_zerop (TREE_OPERAND (base, 1)))
+	{
 
-  def = SSA_NAME_DEF_STMT (target);
-  if (gimple_code (def) != GIMPLE_PHI)
-    return;
+	  index = ipa_get_param_decl_index (info,
+					    SSA_NAME_VAR (TREE_OPERAND (base,
+									0)));
+	  if (index < 0 || !is_parm_ref_data_preserved (&parms_ainfo[index],
+							call,
+							TREE_OPERAND (base, 0)))
+	    return;
+	}
+      else
+	return;
+
+      cs = ipa_note_param_call (node, index, call);
+      cs->indirect_info->offset = offset;
+      cs->indirect_info->agg_contents = 1;
+      return;
+    }
 
-  if (gimple_phi_num_args (def) != 2)
+  /* Now we need to try to match the complex pattern of calling a member
+     pointer. */
+  if (gimple_code (def) != GIMPLE_PHI
+      || gimple_phi_num_args (def) != 2
+      || !POINTER_TYPE_P (TREE_TYPE (target))
+      || TREE_CODE (TREE_TYPE (TREE_TYPE (target))) != METHOD_TYPE)
     return;
 
   /* First, we need to check whether one of these is a load from a member
@@ -1671,15 +1588,15 @@  ipa_analyze_indirect_call_uses (struct c
   d2 = SSA_NAME_DEF_STMT (n2);
 
   join = gimple_bb (def);
-  if ((rec = ipa_get_stmt_member_ptr_load_param (d1, false)))
+  if ((rec = ipa_get_stmt_member_ptr_load_param (d1, false, &offset)))
     {
-      if (ipa_get_stmt_member_ptr_load_param (d2, false))
+      if (ipa_get_stmt_member_ptr_load_param (d2, false, NULL))
 	return;
 
       bb = EDGE_PRED (join, 0)->src;
       virt_bb = gimple_bb (d2);
     }
-  else if ((rec = ipa_get_stmt_member_ptr_load_param (d2, false)))
+  else if ((rec = ipa_get_stmt_member_ptr_load_param (d2, false, &offset)))
     {
       bb = EDGE_PRED (join, 1)->src;
       virt_bb = gimple_bb (d1);
@@ -1734,15 +1651,19 @@  ipa_analyze_indirect_call_uses (struct c
 
   rec2 = ipa_get_stmt_member_ptr_load_param (def,
 					     (TARGET_PTRMEMFUNC_VBIT_LOCATION
-					      == ptrmemfunc_vbit_in_delta));
-
+					      == ptrmemfunc_vbit_in_delta),
+					     NULL);
   if (rec != rec2)
     return;
 
   index = ipa_get_param_decl_index (info, rec);
   if (index >= 0 && is_parm_preserved_before_stmt (&parms_ainfo[index],
 						   call, rec))
-    ipa_note_param_call (node, index, call);
+    {
+      struct cgraph_edge *cs = ipa_note_param_call (node, index, call);
+      cs->indirect_info->offset = offset;
+      cs->indirect_info->agg_contents = 1;
+    }
 
   return;
 }
@@ -1797,7 +1718,7 @@  ipa_analyze_virtual_call_uses (struct cg
 
   cs = ipa_note_param_call (node, index, call);
   ii = cs->indirect_info;
-  ii->anc_offset = anc_offset;
+  ii->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;
@@ -2191,6 +2112,41 @@  ipa_make_edge_direct_to_target (struct c
   return ie;
 }
 
+/* Provided that values from AGG can be propagated to parameter of NODE with
+    index PARAM_INDEX so that is_parm_ref_data_preserved results are reliable
+    and that there is a known constant value in AGG the given OFFSET, return
+    the nown constant value.  */
+
+tree
+ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg,
+			    HOST_WIDE_INT offset,
+			    struct cgraph_node *node,
+			    int param_index)
+{
+  struct ipa_node_params *info = IPA_NODE_REF (node);
+  tree t = TREE_TYPE (ipa_get_param (info, param_index));
+  struct ipa_agg_jf_item *item;
+  int i;
+
+  if (!agg->items
+      || !ipa_agg_types_propagatable_p (agg, t, 0))
+    return NULL;
+
+  FOR_EACH_VEC_ELT (ipa_agg_jf_item_t, agg->items, i, item)
+    {
+      if (item->offset == offset)
+	{
+	  /* CUrrently we do not have clobber values, return NULL fro them once
+	     we do.  */
+	  gcc_checking_assert (is_gimple_ip_invariant (item->value));
+	  return item->value;
+	}
+      else if (item->offset > offset)
+	return NULL;
+    }
+  return NULL;
+}
+
 /* Try to find a destination for indirect edge IE that corresponds to a simple
    call or a call of a member function pointer and where the destination is a
    pointer formal parameter described by jump function JFUNC.  If it can be
@@ -2198,17 +2154,24 @@  ipa_make_edge_direct_to_target (struct c
 
 static struct cgraph_edge *
 try_make_edge_direct_simple_call (struct cgraph_edge *ie,
-				  struct ipa_jump_func *jfunc)
+				  struct ipa_jump_func *jfunc, int param_index)
 {
   tree target;
 
-  if (jfunc->type == IPA_JF_CONST)
-    target = ipa_get_jf_constant (jfunc);
-  else if (jfunc->type == IPA_JF_CONST_MEMBER_PTR)
-    target = ipa_get_jf_member_ptr_pfn (jfunc);
+  if (ie->indirect_info->agg_contents)
+    {
+      target = ipa_find_agg_cst_for_param (&jfunc->agg,
+					   ie->indirect_info->offset,
+					   ie->caller, param_index);
+      if (!target)
+	return NULL;
+    }
   else
-    return NULL;
-
+    {
+      if (jfunc->type != IPA_JF_CONST)
+	return NULL;
+      target = ipa_get_jf_constant (jfunc);
+    }
   return ipa_make_edge_direct_to_target (ie, target);
 }
 
@@ -2229,7 +2192,7 @@  try_make_edge_direct_virtual_call (struc
   binfo = TYPE_BINFO (ipa_get_jf_known_type_base_type (jfunc));
   gcc_checking_assert (binfo);
   binfo = get_binfo_at_offset (binfo, ipa_get_jf_known_type_offset (jfunc)
-			       + ie->indirect_info->anc_offset,
+			       + ie->indirect_info->offset,
 			       ie->indirect_info->otr_type);
   if (binfo)
     target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token,
@@ -2265,6 +2228,7 @@  update_indirect_edges_after_inlining (st
     {
       struct cgraph_indirect_call_info *ici = ie->indirect_info;
       struct ipa_jump_func *jfunc;
+      int param_index;
 
       next_ie = ie->next_callee;
 
@@ -2278,14 +2242,15 @@  update_indirect_edges_after_inlining (st
 	  continue;
 	}
 
-      jfunc = ipa_get_ith_jump_func (top, ici->param_index);
+      param_index = ici->param_index;
+      jfunc = ipa_get_ith_jump_func (top, param_index);
       if (jfunc->type == IPA_JF_PASS_THROUGH
 	  && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
 	ici->param_index = ipa_get_jf_pass_through_formal_id (jfunc);
       else if (jfunc->type == IPA_JF_ANCESTOR)
 	{
  	  ici->param_index = ipa_get_jf_ancestor_formal_id (jfunc);
- 	  ici->anc_offset += ipa_get_jf_ancestor_offset (jfunc);
+ 	  ici->offset += ipa_get_jf_ancestor_offset (jfunc);
 	}
       else
 	/* Either we can find a destination for this edge now or never. */
@@ -2297,7 +2262,8 @@  update_indirect_edges_after_inlining (st
       if (ici->polymorphic)
 	new_direct_edge = try_make_edge_direct_virtual_call (ie, jfunc);
       else
-	new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc);
+	new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc,
+							    param_index);
 
       if (new_direct_edge)
 	{
@@ -3207,10 +3173,6 @@  ipa_write_jump_function (struct output_b
       bp_pack_value (&bp, jump_func->value.ancestor.agg_preserved, 1);
       streamer_write_bitpack (&bp);
       break;
-    case IPA_JF_CONST_MEMBER_PTR:
-      stream_write_tree (ob, jump_func->value.member_cst.pfn, true);
-      stream_write_tree (ob, jump_func->value.member_cst.delta, false);
-      break;
     }
 
   count = VEC_length (ipa_agg_jf_item_t, jump_func->agg.items);
@@ -3263,10 +3225,6 @@  ipa_read_jump_function (struct lto_input
       bp = streamer_read_bitpack (ib);
       jump_func->value.ancestor.agg_preserved = bp_unpack_value (&bp, 1);
       break;
-    case IPA_JF_CONST_MEMBER_PTR:
-      jump_func->value.member_cst.pfn = stream_read_tree (ib, data_in);
-      jump_func->value.member_cst.delta = stream_read_tree (ib, data_in);
-      break;
     }
 
   count = streamer_read_uhwi (ib);
@@ -3294,9 +3252,10 @@  ipa_write_indirect_edge_info (struct out
   struct bitpack_d bp;
 
   streamer_write_hwi (ob, ii->param_index);
-  streamer_write_hwi (ob, ii->anc_offset);
+  streamer_write_hwi (ob, ii->offset);
   bp = bitpack_create (ob->main_stream);
   bp_pack_value (&bp, ii->polymorphic, 1);
+  bp_pack_value (&bp, ii->agg_contents, 1);
   streamer_write_bitpack (&bp);
 
   if (ii->polymorphic)
@@ -3318,9 +3277,10 @@  ipa_read_indirect_edge_info (struct lto_
   struct bitpack_d bp;
 
   ii->param_index = (int) streamer_read_hwi (ib);
-  ii->anc_offset = (HOST_WIDE_INT) streamer_read_hwi (ib);
+  ii->offset = (HOST_WIDE_INT) streamer_read_hwi (ib);
   bp = streamer_read_bitpack (ib);
   ii->polymorphic = bp_unpack_value (&bp, 1);
+  ii->agg_contents = bp_unpack_value (&bp, 1);
   if (ii->polymorphic)
     {
       ii->otr_token = (HOST_WIDE_INT) streamer_read_hwi (ib);
Index: src/gcc/ipa-prop.h
===================================================================
--- src.orig/gcc/ipa-prop.h
+++ src/gcc/ipa-prop.h
@@ -44,10 +44,6 @@  along with GCC; see the file COPYING3.
                   argument.
    Unknown      - neither of the above.
 
-   IPA_JF_CONST_MEMBER_PTR stands for C++ member pointers, it is a special
-   constant in this regard because it is in fact a structure consisting of two
-   values.  Other constants are represented with IPA_JF_CONST.
-
    IPA_JF_ANCESTOR is a special pass-through jump function, which means that
    the result is an address of a part of the object pointed to by the formal
    parameter to which the function refers.  It is mainly intended to represent
@@ -74,7 +70,6 @@  enum jump_func_type
   IPA_JF_UNKNOWN = 0,  /* newly allocated and zeroed jump functions default */
   IPA_JF_KNOWN_TYPE,        /* represented by field known_type */
   IPA_JF_CONST,             /* represented by field costant */
-  IPA_JF_CONST_MEMBER_PTR,  /* represented by field member_cst */
   IPA_JF_PASS_THROUGH,	    /* represented by field pass_through */
   IPA_JF_ANCESTOR	    /* represented by field ancestor */
 };
@@ -128,14 +123,6 @@  struct GTY(()) ipa_ancestor_jf_data
   bool agg_preserved;
 };
 
-/* Structure holding a C++ member pointer constant.  Holds a pointer to the
-   method and delta offset.  */
-struct GTY(()) ipa_member_ptr_cst
-{
-  tree pfn;
-  tree delta;
-};
-
 /* An element in an aggegate part of a jump function describing a known value
    at a given offset.  When the this is part of a pass-through jump function
    with agg_preserved set or an ancestor jump function with agg_preserved set,
@@ -191,7 +178,6 @@  typedef struct GTY (()) ipa_jump_func
   {
     struct ipa_known_type_data GTY ((tag ("IPA_JF_KNOWN_TYPE"))) known_type;
     tree GTY ((tag ("IPA_JF_CONST"))) constant;
-    struct ipa_member_ptr_cst GTY ((tag ("IPA_JF_CONST_MEMBER_PTR"))) member_cst;
     struct ipa_pass_through_data GTY ((tag ("IPA_JF_PASS_THROUGH"))) pass_through;
     struct ipa_ancestor_jf_data GTY ((tag ("IPA_JF_ANCESTOR"))) ancestor;
   } GTY ((desc ("%1.type"))) value;
@@ -311,14 +297,10 @@  ipa_get_jf_ancestor_agg_preserved (struc
   return jfunc->value.ancestor.agg_preserved;
 }
 
-/* Return the pfn part of a member pointer constant jump function JFUNC.  */
+/* Aggregate jump function related functions.  */
+tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *, HOST_WIDE_INT,
+				 struct cgraph_node *, int);
 
-static inline tree
-ipa_get_jf_member_ptr_pfn (struct ipa_jump_func *jfunc)
-{
-  gcc_checking_assert (jfunc->type == IPA_JF_CONST_MEMBER_PTR);
-  return jfunc->value.member_cst.pfn;
-}
 
 /* Summary describing a single formal parameter.  */
 
Index: src/gcc/ipa-cp.c
===================================================================
--- src.orig/gcc/ipa-cp.c
+++ src/gcc/ipa-cp.c
@@ -1110,7 +1110,7 @@  ipa_get_indirect_edge_target (struct cgr
     }
 
   token = ie->indirect_info->otr_token;
-  anc_offset = ie->indirect_info->anc_offset;
+  anc_offset = ie->indirect_info->offset;
   otr_type = ie->indirect_info->otr_type;
 
   t = VEC_index (tree, known_vals, param_index);
Index: src/gcc/testsuite/gcc.dg/ipa/iinline-4.c
===================================================================
--- /dev/null
+++ src/gcc/testsuite/gcc.dg/ipa/iinline-4.c
@@ -0,0 +1,87 @@ 
+/* Verify that simple indirect calls are inlined even without early
+   inlining..  */
+/* { dg-do compile } */
+/* { dg-options "-O3 -c -fdump-ipa-inline -fno-early-inlining"  } */
+
+struct S
+{
+  int i;
+  void (*f)(struct S *);
+  char c;
+};
+
+extern void non_existent(struct S *p, int);
+
+static void hooray1 (struct S *p)
+{
+  non_existent (p, 1);
+}
+
+static void hiphip1 (struct S *p)
+{
+  p->f (p);
+}
+
+int test1 (void)
+{
+  struct S s;
+  s.i = 1234;
+  s.f = hooray1;
+  s.c = 'c';
+  hiphip1 (&s);
+  return 0;
+}
+
+struct S gs;
+struct S *gp = &gs;
+
+static void hooray2 (struct S *p)
+{
+  non_existent (p, 2);
+}
+
+static void hip2 (struct S *p)
+{
+  p->f (p);
+}
+
+static void hiphip2 (struct S *p)
+{
+  hip2 (p);
+}
+
+int test2 (void)
+{
+  struct S *p = gp;
+  p->i = 2341;
+  p->f = hooray2;
+  p->c = 'c';
+  hiphip2 (p);
+  return 0;
+}
+
+static void hooray3 (struct S *p)
+{
+  non_existent (p, 3);
+}
+
+static void hiphip3 (struct S s)
+{
+  s.f (&s);
+}
+
+int test3(void)
+{
+  struct S s;
+  s.i = 3412;
+  s.f = hooray3;
+  s.c = 'c';
+  hiphip3 (s);
+  return 0;
+}
+
+
+/* { dg-final { scan-ipa-dump "hooray1\[^\\n\]*inline copy in test1"  "inline"  } } */
+/* { dg-final { scan-ipa-dump "hooray2\[^\\n\]*inline copy in test2"  "inline"  } } */
+/* { dg-final { scan-ipa-dump "hooray2\[^\\n\]*inline copy in test2"  "inline"  } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */