diff mbox

[1/3] Indirect inlining of targets from references of global constants

Message ID 20160512160806.GI5580@virgil.suse.cz
State New
Headers show

Commit Message

Martin Jambor May 12, 2016, 4:08 p.m. UTC
Hi,

the patch below implements deducing aggregate contents from pointers
to constant variables for inlining and IPA-CP, which finally makes us
perform the optimization requested in
https://gcc.gnu.org/ml/gcc/2014-07/msg00240.html. It also lays down
the basis for doing optimization requested in PR 69708 but two
additional small patches are required for that.

This means we do not give up if we can't use AA to prove that
the memory in question has not been clobbered since invocation of the
function but only mark this fact in the indirect_call_info.  Later on
we still use this information if we know that the parameter in
question points to a constant variable.

If this is deemed a god approach, we will probably want to add a
similar bit to inlining conditions.

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

Thanks,

Martin


2016-05-11  Martin Jambor  <mjambor@suse.cz>

	PR ipa/69708
	* cgraph.h (cgraph_indirect_call_info): New field
	guaranteed_unmodified.
	* ipa-cp.c (ipa_get_indirect_edge_target_1): Also pass parameter value
	to ipa_find_agg_cst_for_param, check guaranteed_unmodified when
	appropriate.
	* ipa-inline-analysis.c (evaluate_conditions_for_known_args): Also
	pass the parameter value to ipa_find_agg_cst_for_param.
	* ipa-prop.c (ipa_load_from_parm_agg): New parameter
	guaranteed_unmodified, store AA results there instead of bailing out
	if present.
	(ipa_note_param_call): Also initialize guaranteed_unmodified flag.
	(ipa_analyze_indirect_call_uses): Also set guaranteed_unmodified flag.
	(find_constructor_constant_at_offset): New function.
	(ipa_find_agg_cst_from_init): Likewise.
	(ipa_find_agg_cst_for_param): Also seearch for aggregate values in
	static initializers of contants, report back through a new paameter
	from_global_constant if that was the case.
	(try_make_edge_direct_simple_call): Also pass parameter value to
	ipa_find_agg_cst_for_param, check guaranteed_unmodified when
	appropriate.
	(ipa_write_indirect_edge_info): Stream new flag guaranteed_unmodified.
	(ipa_read_indirect_edge_info): Likewise.
	* ipa-prop.h (ipa_find_agg_cst_for_param): Update declaration.
	(ipa_load_from_parm_agg): Likewise.

testsuite/
	* gcc.dg/ipa/iinline-cstagg-1.c: New test.
	* gcc.dg/ipa/ipcp-cstagg-1.c: Likewise.
	* gcc.dg/ipa/ipcp-cstagg-2.c: Likewise.
	* gcc.dg/ipa/ipcp-cstagg-3.c: Likewise.
	* gcc.dg/ipa/ipcp-cstagg-4.c: Likewise.
---
 gcc/cgraph.h                                |   9 +-
 gcc/ipa-cp.c                                |  26 ++--
 gcc/ipa-inline-analysis.c                   |   3 +-
 gcc/ipa-prop.c                              | 180 ++++++++++++++++++++++++----
 gcc/ipa-prop.h                              |  13 +-
 gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c |  37 ++++++
 gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c    |  42 +++++++
 gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c    |  46 +++++++
 gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c    |  58 +++++++++
 gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c    |  64 ++++++++++
 10 files changed, 440 insertions(+), 38 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c
 create mode 100644 gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c
 create mode 100644 gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c
 create mode 100644 gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c
 create mode 100644 gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c

Comments

Jeff Law May 16, 2016, 9:45 p.m. UTC | #1
On 05/12/2016 10:08 AM, Martin Jambor wrote:
> Hi,
>
> the patch below implements deducing aggregate contents from pointers
> to constant variables for inlining and IPA-CP, which finally makes us
> perform the optimization requested in
> https://gcc.gnu.org/ml/gcc/2014-07/msg00240.html. It also lays down
> the basis for doing optimization requested in PR 69708 but two
> additional small patches are required for that.
>
> This means we do not give up if we can't use AA to prove that
> the memory in question has not been clobbered since invocation of the
> function but only mark this fact in the indirect_call_info.  Later on
> we still use this information if we know that the parameter in
> question points to a constant variable.
>
> If this is deemed a god approach, we will probably want to add a
> similar bit to inlining conditions.
>
> Bootstrapped, lto-bootstrapped and tested on x86_64-linux. OK for
> trunk?
>
> Thanks,
>
> Martin
>
>
> 2016-05-11  Martin Jambor  <mjambor@suse.cz>
>
> 	PR ipa/69708
> 	* cgraph.h (cgraph_indirect_call_info): New field
> 	guaranteed_unmodified.
> 	* ipa-cp.c (ipa_get_indirect_edge_target_1): Also pass parameter value
> 	to ipa_find_agg_cst_for_param, check guaranteed_unmodified when
> 	appropriate.
> 	* ipa-inline-analysis.c (evaluate_conditions_for_known_args): Also
> 	pass the parameter value to ipa_find_agg_cst_for_param.
> 	* ipa-prop.c (ipa_load_from_parm_agg): New parameter
> 	guaranteed_unmodified, store AA results there instead of bailing out
> 	if present.
> 	(ipa_note_param_call): Also initialize guaranteed_unmodified flag.
> 	(ipa_analyze_indirect_call_uses): Also set guaranteed_unmodified flag.
> 	(find_constructor_constant_at_offset): New function.
> 	(ipa_find_agg_cst_from_init): Likewise.
> 	(ipa_find_agg_cst_for_param): Also seearch for aggregate values in
> 	static initializers of contants, report back through a new paameter
> 	from_global_constant if that was the case.
> 	(try_make_edge_direct_simple_call): Also pass parameter value to
> 	ipa_find_agg_cst_for_param, check guaranteed_unmodified when
> 	appropriate.
> 	(ipa_write_indirect_edge_info): Stream new flag guaranteed_unmodified.
> 	(ipa_read_indirect_edge_info): Likewise.
> 	* ipa-prop.h (ipa_find_agg_cst_for_param): Update declaration.
> 	(ipa_load_from_parm_agg): Likewise.
>
> testsuite/
> 	* gcc.dg/ipa/iinline-cstagg-1.c: New test.
> 	* gcc.dg/ipa/ipcp-cstagg-1.c: Likewise.
> 	* gcc.dg/ipa/ipcp-cstagg-2.c: Likewise.
> 	* gcc.dg/ipa/ipcp-cstagg-3.c: Likewise.
> 	* gcc.dg/ipa/ipcp-cstagg-4.c: Likewise.
> ---
LGTM.
jeff
diff mbox

Patch

diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 8ad9f45..ecafe63 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1579,9 +1579,14 @@  struct GTY(()) cgraph_indirect_call_info
   unsigned agg_contents : 1;
   /* Set when this is a call through a member pointer.  */
   unsigned member_ptr : 1;
-  /* When the previous bit is set, this one determines whether the destination
-     is loaded from a parameter passed by reference. */
+  /* When the agg_contents bit is set, this one determines whether the
+     destination is loaded from a parameter passed by reference. */
   unsigned by_ref : 1;
+  /* When the agg_contents bit is set, this one determines whether we can
+     deduce from the function body that the loaded value from the reference is
+     never modified between the invocation of the function and the load
+     point.  */
+  unsigned guaranteed_unmodified : 1;
   /* For polymorphic calls this specify whether the virtual table pointer
      may have changed in between function entry and the call.  */
   unsigned vptr_changed : 1;
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 5900d4d..2183da0 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1999,9 +1999,9 @@  ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
 
       if (ie->indirect_info->agg_contents)
 	{
-	  if (agg_reps)
+	  t = NULL;
+	  if (agg_reps && ie->indirect_info->guaranteed_unmodified)
 	    {
-	      t = NULL;
 	      while (agg_reps)
 		{
 		  if (agg_reps->index == param_index
@@ -2014,15 +2014,22 @@  ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
 		  agg_reps = agg_reps->next;
 		}
 	    }
-	  else if (known_aggs.length () > (unsigned int) param_index)
+	  if (!t)
 	    {
 	      struct ipa_agg_jump_function *agg;
-	      agg = known_aggs[param_index];
-	      t = ipa_find_agg_cst_for_param (agg, ie->indirect_info->offset,
-					      ie->indirect_info->by_ref);
+	      if (known_aggs.length () > (unsigned int) param_index)
+		agg = known_aggs[param_index];
+	      else
+		agg = NULL;
+	      bool from_global_constant;
+	      t = ipa_find_agg_cst_for_param (agg, known_csts[param_index],
+					      ie->indirect_info->offset,
+					      ie->indirect_info->by_ref,
+					      &from_global_constant);
+	      if (!from_global_constant
+		  && !ie->indirect_info->guaranteed_unmodified)
+		t = NULL_TREE;
 	    }
-	  else
-	    t = NULL;
 	}
       else
 	t = known_csts[param_index];
@@ -2066,7 +2073,8 @@  ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
     {
        struct ipa_agg_jump_function *agg;
        agg = known_aggs[param_index];
-       t = ipa_find_agg_cst_for_param (agg, ie->indirect_info->offset,
+       t = ipa_find_agg_cst_for_param (agg, known_csts[param_index],
+				       ie->indirect_info->offset,
 				       true);
     }
 
diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
index 68824f7..8cfc9f6 100644
--- a/gcc/ipa-inline-analysis.c
+++ b/gcc/ipa-inline-analysis.c
@@ -853,7 +853,8 @@  evaluate_conditions_for_known_args (struct cgraph_node *node,
 	  if (known_aggs.exists ())
 	    {
 	      agg = known_aggs[c->operand_num];
-	      val = ipa_find_agg_cst_for_param (agg, c->offset, c->by_ref);
+	      val = ipa_find_agg_cst_for_param (agg, known_vals[c->operand_num],
+						c->offset, c->by_ref);
 	    }
 	  else
 	    val = NULL_TREE;
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index f02ec47..afbc32b 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -930,10 +930,15 @@  parm_ref_data_pass_through_p (struct ipa_func_body_info *fbi, int index,
   return !modified;
 }
 
-/* Return true if we can prove that OP is a memory reference loading unmodified
-   data from an aggregate passed as a parameter and if the aggregate is passed
-   by reference, that the alias type of the load corresponds to the type of the
-   formal parameter (so that we can rely on this type for TBAA in callers).
+/* Return true if we can prove that OP is a memory reference loading
+   data from an aggregate passed as a parameter.
+
+   The function works in two modes.  If GUARANTEED_UNMODIFIED is NULL, it return
+   false if it cannot prove that the value has not been modified before the
+   load in STMT.  If GUARANTEED_UNMODIFIED is not NULL, it will return true even
+   if it cannot prove the value has not been modified, in that case it will
+   store false to *GUARANTEED_UNMODIFIED, otherwise it will store true there.
+
    INFO and PARMS_AINFO describe parameters of the current function (but the
    latter can be NULL), STMT is the load statement.  If function returns true,
    *INDEX_P, *OFFSET_P and *BY_REF is filled with the parameter index, offset
@@ -945,7 +950,7 @@  ipa_load_from_parm_agg (struct ipa_func_body_info *fbi,
 			vec<ipa_param_descriptor> descriptors,
 			gimple *stmt, tree op, int *index_p,
 			HOST_WIDE_INT *offset_p, HOST_WIDE_INT *size_p,
-			bool *by_ref_p)
+			bool *by_ref_p, bool *guaranteed_unmodified)
 {
   int index;
   HOST_WIDE_INT size, max_size;
@@ -966,6 +971,8 @@  ipa_load_from_parm_agg (struct ipa_func_body_info *fbi,
 	  *by_ref_p = false;
 	  if (size_p)
 	    *size_p = size;
+	  if (guaranteed_unmodified)
+	    *guaranteed_unmodified = true;
 	  return true;
 	}
       return false;
@@ -1002,13 +1009,18 @@  ipa_load_from_parm_agg (struct ipa_func_body_info *fbi,
       index = load_from_unmodified_param (fbi, descriptors, def);
     }
 
-  if (index >= 0
-      && parm_ref_data_preserved_p (fbi, index, stmt, op))
+  if (index >= 0)
     {
+      bool data_preserved = parm_ref_data_preserved_p (fbi, index, stmt, op);
+      if (!data_preserved && !guaranteed_unmodified)
+	return false;
+
       *index_p = index;
       *by_ref_p = true;
       if (size_p)
 	*size_p = size;
+      if (guaranteed_unmodified)
+	*guaranteed_unmodified = data_preserved;
       return true;
     }
   return false;
@@ -1824,6 +1836,7 @@  ipa_note_param_call (struct cgraph_node *node, int param_index,
   cs->indirect_info->param_index = param_index;
   cs->indirect_info->agg_contents = 0;
   cs->indirect_info->member_ptr = 0;
+  cs->indirect_info->guaranteed_unmodified = 0;
   return cs;
 }
 
@@ -1905,15 +1918,17 @@  ipa_analyze_indirect_call_uses (struct ipa_func_body_info *fbi, gcall *call,
 
   int index;
   gimple *def = SSA_NAME_DEF_STMT (target);
+  bool guaranteed_unmodified;
   if (gimple_assign_single_p (def)
       && ipa_load_from_parm_agg (fbi, info->descriptors, def,
 				 gimple_assign_rhs1 (def), &index, &offset,
-				 NULL, &by_ref))
+				 NULL, &by_ref, &guaranteed_unmodified))
     {
       struct cgraph_edge *cs = ipa_note_param_call (fbi->node, index, call);
       cs->indirect_info->offset = offset;
       cs->indirect_info->agg_contents = 1;
       cs->indirect_info->by_ref = by_ref;
+      cs->indirect_info->guaranteed_unmodified = guaranteed_unmodified;
       return;
     }
 
@@ -2014,6 +2029,7 @@  ipa_analyze_indirect_call_uses (struct ipa_func_body_info *fbi, gcall *call,
       cs->indirect_info->offset = offset;
       cs->indirect_info->agg_contents = 1;
       cs->indirect_info->member_ptr = 1;
+      cs->indirect_info->guaranteed_unmodified = 1;
     }
 
   return;
@@ -2701,18 +2717,126 @@  ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
   return ie;
 }
 
-/* Retrieve value from aggregate jump function AGG for the given OFFSET or
-   return NULL if there is not any.  BY_REF specifies whether the value has to
-   be passed by reference or by value.  */
+/* Attempt to locate an interprocedural constant at a given REQ_OFFSET in
+   CONSTRUCTOR and return it.  Return NULL if the search fails for some
+   reason.  */
+
+static tree
+find_constructor_constant_at_offset (tree constructor, HOST_WIDE_INT req_offset)
+{
+  tree type = TREE_TYPE (constructor);
+  if (TREE_CODE (type) != ARRAY_TYPE
+      && TREE_CODE (type) != RECORD_TYPE)
+    return NULL;
+
+  unsigned ix;
+  tree index, val;
+  FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (constructor), ix, index, val)
+    {
+      HOST_WIDE_INT elt_offset;
+      if (TREE_CODE (type) == ARRAY_TYPE)
+       {
+         offset_int off;
+         tree unit_size = TYPE_SIZE_UNIT (TREE_TYPE (type));
+         gcc_assert (TREE_CODE (unit_size) == INTEGER_CST);
+
+         if (index)
+           {
+             off = wi::to_offset (index);
+             if (TYPE_DOMAIN (type) && TYPE_MIN_VALUE (TYPE_DOMAIN (type)))
+               {
+                 tree low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
+                 gcc_assert (TREE_CODE (unit_size) == INTEGER_CST);
+                 off = wi::sext (off - wi::to_offset (low_bound),
+                                 TYPE_PRECISION (TREE_TYPE (index)));
+               }
+             off *= wi::to_offset (unit_size);
+           }
+         else
+           off = wi::to_offset (unit_size) * ix;
+
+         off = wi::lshift (off, LOG2_BITS_PER_UNIT);
+         if (!wi::fits_shwi_p (off) || wi::neg_p (off))
+           continue;
+         elt_offset = off.to_shwi ();
+       }
+      else if (TREE_CODE (type) == RECORD_TYPE)
+       {
+         gcc_checking_assert (index && TREE_CODE (index) == FIELD_DECL);
+         if (DECL_BIT_FIELD (index))
+           continue;
+         elt_offset = int_bit_position (index);
+       }
+      else
+       gcc_unreachable ();
+
+      if (elt_offset > req_offset)
+	return NULL;
+
+      if (TREE_CODE (val) == CONSTRUCTOR)
+	return find_constructor_constant_at_offset (val,
+						    req_offset - elt_offset);
+
+      if (elt_offset == req_offset
+	  && is_gimple_reg_type (TREE_TYPE (val))
+	  && is_gimple_ip_invariant (val))
+	return val;
+    }
+  return NULL;
+}
+
+/* Check whether SCALAR could be used to look up an aggregate interprocedural
+   invariant from a static constructor and if so, return it.  Otherwise return
+   NULL. */
+
+static tree
+ipa_find_agg_cst_from_init (tree scalar, HOST_WIDE_INT offset, bool by_ref)
+{
+  if (by_ref)
+    {
+      if (TREE_CODE (scalar) != ADDR_EXPR)
+	return NULL;;
+      scalar = TREE_OPERAND (scalar, 0);
+    }
+
+  if (TREE_CODE (scalar) != VAR_DECL
+      || !is_global_var (scalar)
+      || !TREE_READONLY (scalar)
+      || !DECL_INITIAL (scalar)
+      || TREE_CODE (DECL_INITIAL (scalar)) != CONSTRUCTOR)
+    return NULL;
+
+  return find_constructor_constant_at_offset (DECL_INITIAL (scalar), offset);
+}
+
+/* Retrieve value from aggregate jump function AGG or static initializer of
+   SCALAR (which can be NULL) for the given OFFSET or return NULL if there is
+   not any.  BY_REF specifies whether the value has to be passed by reference
+   or by value.  If FROM_GLOBAL_CONSTANT is non-NULL, then the boolean it
+   points to is set to true if the value comes from an initializer of a
+   constant.  */
 
 tree
-ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg,
-			    HOST_WIDE_INT offset, bool by_ref)
+ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar,
+			    HOST_WIDE_INT offset, bool by_ref,
+			    bool *from_global_constant)
 {
   struct ipa_agg_jf_item *item;
   int i;
 
-  if (by_ref != agg->by_ref)
+  if (scalar)
+    {
+      tree res = ipa_find_agg_cst_from_init (scalar, offset, by_ref);
+      if (res)
+	{
+	  if (from_global_constant)
+	    *from_global_constant = true;
+	  return res;
+	}
+    }
+
+  if (!agg
+      || by_ref != agg->by_ref)
     return NULL;
 
   FOR_EACH_VEC_SAFE_ELT (agg->items, i, item)
@@ -2721,6 +2845,8 @@  ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg,
 	/* Currently we do not have clobber values, return NULL for them once
 	   we do.  */
 	gcc_checking_assert (is_gimple_ip_invariant (item->value));
+	if (from_global_constant)
+	  *from_global_constant = false;
 	return item->value;
       }
   return NULL;
@@ -2819,13 +2945,21 @@  try_make_edge_direct_simple_call (struct cgraph_edge *ie,
   struct cgraph_edge *cs;
   tree target;
   bool agg_contents = ie->indirect_info->agg_contents;
-
-  if (ie->indirect_info->agg_contents)
-    target = ipa_find_agg_cst_for_param (&jfunc->agg,
-					 ie->indirect_info->offset,
-					 ie->indirect_info->by_ref);
+  tree scalar = ipa_value_from_jfunc (new_root_info, jfunc);
+  if (agg_contents)
+    {
+      bool from_global_constant;
+      target = ipa_find_agg_cst_for_param (&jfunc->agg, scalar,
+					   ie->indirect_info->offset,
+					   ie->indirect_info->by_ref,
+					   &from_global_constant);
+      if (target
+	  && !from_global_constant
+	  && !ie->indirect_info->guaranteed_unmodified)
+	return NULL;
+    }
   else
-    target = ipa_value_from_jfunc (new_root_info, jfunc);
+    target = scalar;
   if (!target)
     return NULL;
   cs = ipa_make_edge_direct_to_target (ie, target);
@@ -2893,7 +3027,9 @@  try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
     {
       tree vtable;
       unsigned HOST_WIDE_INT offset;
-      tree t = ipa_find_agg_cst_for_param (&jfunc->agg,
+      tree scalar = (jfunc->type == IPA_JF_CONST) ? ipa_get_jf_constant (jfunc)
+	: NULL;
+      tree t = ipa_find_agg_cst_for_param (&jfunc->agg, scalar,
 					   ie->indirect_info->offset,
 					   true);
       if (t && vtable_pointer_value_to_vtable (t, &vtable, &offset))
@@ -4560,6 +4696,7 @@  ipa_write_indirect_edge_info (struct output_block *ob,
   bp_pack_value (&bp, ii->agg_contents, 1);
   bp_pack_value (&bp, ii->member_ptr, 1);
   bp_pack_value (&bp, ii->by_ref, 1);
+  bp_pack_value (&bp, ii->guaranteed_unmodified, 1);
   bp_pack_value (&bp, ii->vptr_changed, 1);
   streamer_write_bitpack (&bp);
   if (ii->agg_contents || ii->polymorphic)
@@ -4592,6 +4729,7 @@  ipa_read_indirect_edge_info (struct lto_input_block *ib,
   ii->agg_contents = bp_unpack_value (&bp, 1);
   ii->member_ptr = bp_unpack_value (&bp, 1);
   ii->by_ref = bp_unpack_value (&bp, 1);
+  ii->guaranteed_unmodified = bp_unpack_value (&bp, 1);
   ii->vptr_changed = bp_unpack_value (&bp, 1);
   if (ii->agg_contents || ii->polymorphic)
     ii->offset = (HOST_WIDE_INT) streamer_read_hwi (ib);
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index 4e11ca6..e32d078 100644
--- a/gcc/ipa-prop.h
+++ b/gcc/ipa-prop.h
@@ -636,11 +636,14 @@  tree ipa_impossible_devirt_target (struct cgraph_edge *, tree);
 void ipa_analyze_node (struct cgraph_node *);
 
 /* Aggregate jump function related functions.  */
-tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *, HOST_WIDE_INT,
-				 bool);
-bool ipa_load_from_parm_agg (struct ipa_func_body_info *,
-			     vec<ipa_param_descriptor>, gimple *, tree, int *,
-			     HOST_WIDE_INT *, HOST_WIDE_INT *, bool *);
+tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar,
+				 HOST_WIDE_INT offset, bool by_ref,
+				 bool *from_global_constant = NULL);
+bool ipa_load_from_parm_agg (struct ipa_func_body_info *fbi,
+			     vec<ipa_param_descriptor> descriptors,
+			     gimple *stmt, tree op, int *index_p,
+			     HOST_WIDE_INT *offset_p, HOST_WIDE_INT *size_p,
+			     bool *by_ref, bool *guaranteed_unmodified = NULL);
 
 /* Debugging interface.  */
 void ipa_print_node_params (FILE *, struct cgraph_node *node);
diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c b/gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c
new file mode 100644
index 0000000..8656cb3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c
@@ -0,0 +1,37 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-inline-details -fno-early-inlining -fno-ipa-sra -fno-ipa-cp" } */
+
+typedef struct S
+{
+  int add_offset;
+  int (*call)(int);
+} S;
+
+static int
+bar (const S *f, int x)
+{
+  x = f->call(x);
+  return x;
+}
+
+static int
+thisisthetarget (int x)
+{
+  return x * x;
+}
+
+static const S s = {16, thisisthetarget};
+
+int
+outerfunction (int x)
+{
+  return bar (&s, x);
+}
+
+int
+obfuscate (int x)
+{
+  return bar ((S *) 0, x);
+}
+
+/* { dg-final { scan-ipa-dump "thisisthetarget\[^\\n\]*inline copy in outerfunction"  "inline"  } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c
new file mode 100644
index 0000000..4d64ea7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c
@@ -0,0 +1,42 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-cp-details" } */
+
+typedef struct S
+{
+  int add_offset;
+  int (*call)(int);
+} S;
+
+extern const S *gs;
+
+static int __attribute__((noinline))
+bar (const S *f, int x)
+{
+  x = f->call(x);
+  x = f->call(x);
+  x = f->call(x);
+  gs = f;
+  return x;
+}
+
+static int
+sq (int x)
+{
+  return x * x;
+}
+
+static const S s = {16, sq};
+
+int
+g (int x)
+{
+  return bar (&s, x);
+}
+
+int
+obfuscate (int x)
+{
+  return bar ((S *) 0, x);
+}
+
+/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c
new file mode 100644
index 0000000..f820140
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c
@@ -0,0 +1,46 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-cp-details" } */
+
+typedef struct S
+{
+  int (*call)(int);
+} S;
+
+static int __attribute__((noinline))
+bar (const S *f, int x)
+{
+  x = f->call(x);
+  return x;
+}
+
+extern void impossible_aa (void);
+
+static int __attribute__((noinline))
+baz (const S *f, int x)
+{
+  impossible_aa ();
+  return bar (f, x);
+}
+
+static int
+sq (int x)
+{
+  return x * x;
+}
+
+static const S s = {sq};
+
+int
+g (int x)
+{
+  return baz (&s, x);
+}
+
+int
+obfuscate (int x)
+{
+  return baz ((S *) 0, x);
+}
+
+/* { dg-final { scan-ipa-dump "Discovered an indirect call to a known target" "cp" } } */
+
diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c
new file mode 100644
index 0000000..917f138
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c
@@ -0,0 +1,58 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-cp-details" } */
+
+#define N 4
+
+typedef int (* const A[N])(int);
+
+extern const A *ga;
+
+static int  __attribute__((noinline))
+bar (const A *f, int x)
+{
+  x = (*f)[2](x);
+  x = (*f)[2](x);
+  x = (*f)[2](x);
+  ga = f;
+  return x;
+}
+
+static int
+zero (int x)
+{
+  return 0;
+}
+
+static int
+addone (int x)
+{
+  return x + 1;
+}
+
+static int
+sq (int x)
+{
+  return x * x;
+}
+
+static int
+cube (int x)
+{
+  return x * x * x;
+}
+
+static const A a = {zero, addone, sq, cube};
+
+int
+g (int x)
+{
+  return bar (&a, x);
+}
+
+int
+obfuscate (int x)
+{
+  return bar ((A *) 0, x);
+}
+
+/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c
new file mode 100644
index 0000000..7458402
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c
@@ -0,0 +1,64 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-cp-details" } */
+
+#define N 4
+
+typedef int (* const A[N])(int);
+
+typedef struct S
+{
+  int add_offset;
+  A a;
+} S;
+
+extern const S *gs;
+
+static int  __attribute__((noinline))
+bar (const S *f, int x)
+{
+  gs = f;
+  x = f->a[2](x);
+  x = f->a[2](x);
+  x = f->a[2](x);
+  return x;
+}
+
+static int
+zero (int x)
+{
+  return 0;
+}
+
+static int
+addone (int x)
+{
+  return x + 1;
+}
+
+static int
+sq (int x)
+{
+  return x * x;
+}
+
+static int
+cube (int x)
+{
+  return x * x * x;
+}
+
+static const S s = {64, {zero, addone, sq, cube}};
+
+int
+g (int x)
+{
+  return bar (&s, x);
+}
+
+int
+obfuscate (int x)
+{
+  return bar ((S *) 0, x);
+}
+
+/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" } } */