Indirect inlining of targets from references of global constants
authorMartin Jambor <mjambor@suse.cz>
Wed, 18 May 2016 16:38:56 +0000 (18:38 +0200)
committerMartin Jambor <jamborm@gcc.gnu.org>
Wed, 18 May 2016 16:38:56 +0000 (18:38 +0200)
2016-05-18  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.

From-SVN: r236416

12 files changed:
gcc/ChangeLog
gcc/cgraph.h
gcc/ipa-cp.c
gcc/ipa-inline-analysis.c
gcc/ipa-prop.c
gcc/ipa-prop.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c [new file with mode: 0644]

index 8d09b501d6b181d247c6c956285ab531b3621174..3a8e9a76373b81bb1fcc9a9616f3e0561cd199a8 100644 (file)
@@ -1,3 +1,31 @@
+2016-05-18  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.
+
 2016-05-18  Jiong Wang  <jiong.wang@arm.com>
 
        PR rtl-optimization/71150
index 8ad9f456a22e57767e92b5c04787087bc8227535..ecafe63a5363e5b47ac4e670d73d77b2bfcb8b4b 100644 (file)
@@ -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;
index 5900d4d91584237a598264e7d92cc7ae48178c61..2183da07b68b1ad340397f4fb42a8b1469f614d8 100644 (file)
@@ -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);
     }
 
index 4a547a2f18880d35a75eff44ea49b8f72748ce7a..5d6721813d8100eec3a6e3d1bbb5dd633db2003d 100644 (file)
@@ -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;
index f02ec474dc97ebf2c7ae0fa4cd8e8eb622649bd3..785f2f43ab74c8813854a66491d3aa9f3aaef0c1 100644 (file)
@@ -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,125 @@ 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
+   none.  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 +2844,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 +2944,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 +3026,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 +4695,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 +4728,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);
index 4e11ca6d35cfd44445e2a5c7f8332e56a2298ac2..e32d078edb3502d06fa02b7f205c0b55cb113b02 100644 (file)
@@ -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);
index 0434803c5995007c957d9b561e37bb95df2d90e8..d7dc01607da1a754fbfd25399a13a39153945147 100644 (file)
@@ -1,3 +1,12 @@
+2016-05-18  Martin Jambor  <mjambor@suse.cz>
+
+       PR ipa/69708
+       * 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.
+
 2016-05-18  Paolo Carlini  <paolo.carlini@oracle.com>
 
        PR c++/69793
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 (file)
index 0000000..8656cb3
--- /dev/null
@@ -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 (file)
index 0000000..4d64ea7
--- /dev/null
@@ -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 (file)
index 0000000..f820140
--- /dev/null
@@ -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 (file)
index 0000000..917f138
--- /dev/null
@@ -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 (file)
index 0000000..7458402
--- /dev/null
@@ -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" } } */