ipa-prop.c (remove_described_reference): Accept missing references, return false...
authorMartin Jambor <mjambor@suse.cz>
Thu, 5 Sep 2013 12:41:16 +0000 (14:41 +0200)
committerMartin Jambor <jamborm@gcc.gnu.org>
Thu, 5 Sep 2013 12:41:16 +0000 (14:41 +0200)
2013-09-05  Martin Jambor  <mjambor@suse.cz>

* ipa-prop.c (remove_described_reference): Accept missing references,
return false if that hppens, otherwise return true.
(cgraph_node_for_jfunc): New function.
(try_decrement_rdesc_refcount): Likewise.
(try_make_edge_direct_simple_call): Use them.
(ipa_edge_removal_hook): Remove references from rdescs.
(ipa_edge_duplication_hook): Clone rdescs and their references
when the new edge has the same caller as the old one.
* cgraph.c (cgraph_resolve_speculation): Remove speculative
reference before removing any edges.

testsuite/
* g++.dg/ipa/remref-1.C: New test.
* g++.dg/ipa/remref-2.C: Likewise.

From-SVN: r202281

gcc/ChangeLog
gcc/cgraph.c
gcc/ipa-prop.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ipa/remref-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ipa/remref-2.C [new file with mode: 0644]

index 46153b7edf0d021682c3718a882e3576c5dab4b5..d7b6bfa6728ed63fb21b50e30cf9a6d46087df39 100644 (file)
@@ -1,3 +1,16 @@
+2013-09-05  Martin Jambor  <mjambor@suse.cz>
+
+       * ipa-prop.c (remove_described_reference): Accept missing references,
+       return false if that hppens, otherwise return true.
+       (cgraph_node_for_jfunc): New function.
+       (try_decrement_rdesc_refcount): Likewise.
+       (try_make_edge_direct_simple_call): Use them.
+       (ipa_edge_removal_hook): Remove references from rdescs.
+       (ipa_edge_duplication_hook): Clone rdescs and their references
+       when the new edge has the same caller as the old one.
+       * cgraph.c (cgraph_resolve_speculation): Remove speculative
+       reference before removing any edges.
+
 2013-09-05  Richard Earnshaw  <rearnsha@arm.com>
 
        * arm.c (thumb2_emit_strd_push): Rewrite to use pre-decrement on
index 5fc87ae339d9434feff2df774fba6f1b3518ed1a..f12bf1ba4be9809052285034d64e3dd5d61118fd 100644 (file)
@@ -1225,13 +1225,13 @@ cgraph_resolve_speculation (struct cgraph_edge *edge, tree callee_decl)
     edge->frequency = CGRAPH_FREQ_MAX;
   edge->speculative = false;
   e2->speculative = false;
+  ipa_remove_reference (ref);
   if (e2->indirect_unknown_callee || e2->inline_failed)
     cgraph_remove_edge (e2);
   else
     cgraph_remove_node_and_inline_clones (e2->callee, NULL);
   if (edge->caller->call_site_hash)
     cgraph_update_edge_in_call_site_hash (edge);
-  ipa_remove_reference (ref);
   return edge;
 }
 
index 177283c72673d89bb8d2547f78fa5a7b3a9c3a9b..f9f8e2d773e9934c49ed699884b3f91194376563 100644 (file)
@@ -2496,9 +2496,10 @@ ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg,
 }
 
 /* Remove a reference to SYMBOL from the list of references of a node given by
-   reference description RDESC.  */
+   reference description RDESC.  Return true if the reference has been
+   successfully found and removed.  */
 
-static void
+static bool
 remove_described_reference (symtab_node symbol, struct ipa_cst_ref_desc *rdesc)
 {
   struct ipa_ref *to_del;
@@ -2507,12 +2508,15 @@ remove_described_reference (symtab_node symbol, struct ipa_cst_ref_desc *rdesc)
   origin = rdesc->cs;
   to_del = ipa_find_reference ((symtab_node) origin->caller, symbol,
                               origin->call_stmt, origin->lto_stmt_uid);
-  gcc_assert (to_del);
+  if (!to_del)
+    return false;
+
   ipa_remove_reference (to_del);
   if (dump_file)
     fprintf (dump_file, "ipa-prop: Removed a reference from %s/%i to %s.\n",
             xstrdup (cgraph_node_name (origin->caller)),
             origin->caller->symbol.order, xstrdup (symtab_node_name (symbol)));
+  return true;
 }
 
 /* If JFUNC has a reference description with refcount different from
@@ -2529,6 +2533,45 @@ jfunc_rdesc_usable (struct ipa_jump_func *jfunc)
     return NULL;
 }
 
+/* If the value of constant jump function JFUNC is an address of a function
+   declaration, return the associated call graph node.  Otherwise return
+   NULL.  */
+
+static cgraph_node *
+cgraph_node_for_jfunc (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_CONST);
+  tree cst = ipa_get_jf_constant (jfunc);
+  if (TREE_CODE (cst) != ADDR_EXPR
+      || TREE_CODE (TREE_OPERAND (cst, 0)) != FUNCTION_DECL)
+    return NULL;
+
+  return cgraph_get_node (TREE_OPERAND (cst, 0));
+}
+
+
+/* If JFUNC is a constant jump function with a usable rdesc, decrement its
+   refcount and if it hits zero, remove reference to SYMBOL from the caller of
+   the edge specified in the rdesc.  Return false if either the symbol or the
+   reference could not be found, otherwise return true.  */
+
+static bool
+try_decrement_rdesc_refcount (struct ipa_jump_func *jfunc)
+{
+  struct ipa_cst_ref_desc *rdesc;
+  if (jfunc->type == IPA_JF_CONST
+      && (rdesc = jfunc_rdesc_usable (jfunc))
+      && --rdesc->refcount == 0)
+    {
+      symtab_node symbol = (symtab_node) cgraph_node_for_jfunc (jfunc);
+      if (!symbol)
+       return false;
+
+      return remove_described_reference (symbol, rdesc);
+    }
+  return true;
+}
+
 /* 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
@@ -2544,7 +2587,6 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie,
   tree target;
   bool agg_contents = ie->indirect_info->agg_contents;
   bool speculative = ie->speculative;
-  struct ipa_cst_ref_desc *rdesc;
 
   if (ie->indirect_info->agg_contents)
     target = ipa_find_agg_cst_for_param (&jfunc->agg,
@@ -2557,11 +2599,16 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie,
   cs = ipa_make_edge_direct_to_target (ie, target);
 
   /* FIXME: speculative edges can be handled.  */
-  if (cs && !agg_contents && !speculative
-      && jfunc->type == IPA_JF_CONST
-      && (rdesc = jfunc_rdesc_usable (jfunc))
-      && --rdesc->refcount == 0)
-    remove_described_reference ((symtab_node) cs->callee, rdesc);
+  if (cs && !agg_contents && !speculative)
+    {
+      bool ok;
+      gcc_checking_assert (cs->callee
+                          && (jfunc->type != IPA_JF_CONST
+                              || !cgraph_node_for_jfunc (jfunc)
+                              || cs->callee == cgraph_node_for_jfunc (jfunc)));
+      ok = try_decrement_rdesc_refcount (jfunc);
+      gcc_checking_assert (ok);
+    }
 
   return cs;
 }
@@ -2817,7 +2864,9 @@ propagate_controlled_uses (struct cgraph_edge *cs)
              if (n)
                {
                  struct cgraph_node *clone;
-                 remove_described_reference ((symtab_node) n, rdesc);
+                 bool ok;
+                 ok = remove_described_reference ((symtab_node) n, rdesc);
+                 gcc_checking_assert (ok);
 
                  clone = cs->caller;
                  while (clone->global.inlined_to
@@ -2960,9 +3009,21 @@ ipa_set_node_agg_value_chain (struct cgraph_node *node,
 static void
 ipa_edge_removal_hook (struct cgraph_edge *cs, void *data ATTRIBUTE_UNUSED)
 {
-  /* During IPA-CP updating we can be called on not-yet analyze clones.  */
+  struct ipa_edge_args *args;
+
+  /* During IPA-CP updating we can be called on not-yet analyzed clones.  */
   if (vec_safe_length (ipa_edge_args_vector) <= (unsigned)cs->uid)
     return;
+
+  args = IPA_EDGE_REF (cs);
+  if (args->jump_functions)
+    {
+      struct ipa_jump_func *jf;
+      int i;
+      FOR_EACH_VEC_ELT (*args->jump_functions, i, jf)
+       try_decrement_rdesc_refcount (jf);
+    }
+
   ipa_free_edge_args_substructures (IPA_EDGE_REF (cs));
 }
 
@@ -3007,6 +3068,24 @@ ipa_edge_duplication_hook (struct cgraph_edge *src, struct cgraph_edge *dst,
 
          if (!src_rdesc)
            dst_jf->value.constant.rdesc = NULL;
+         else if (src->caller == dst->caller)
+           {
+             struct ipa_ref *ref;
+             symtab_node n = (symtab_node) cgraph_node_for_jfunc (src_jf);
+             gcc_checking_assert (n);
+             ref = ipa_find_reference ((symtab_node) src->caller, n,
+                                       src->call_stmt, src->lto_stmt_uid);
+             gcc_checking_assert (ref);
+             ipa_clone_ref (ref, (symtab_node) dst->caller, ref->stmt);
+
+             gcc_checking_assert (ipa_refdesc_pool);
+             struct ipa_cst_ref_desc *dst_rdesc
+               = (struct ipa_cst_ref_desc *) pool_alloc (ipa_refdesc_pool);
+             dst_rdesc->cs = dst;
+             dst_rdesc->refcount = src_rdesc->refcount;
+             dst_rdesc->next_duplicate = NULL;
+             dst_jf->value.constant.rdesc = dst_rdesc;
+           }
          else if (src_rdesc->cs == src)
            {
              struct ipa_cst_ref_desc *dst_rdesc;
index 176d9973f6ce8f8da68bcf1c74dede3ce15f9b6f..5431d212acccddf616b06b31a7d546810ef1c2bb 100644 (file)
@@ -1,3 +1,8 @@
+2013-09-05  Martin Jambor  <mjambor@suse.cz>
+
+       * g++.dg/ipa/remref-1.C: New test.
+       * g++.dg/ipa/remref-2.C: Likewise.
+
 2013-09-04  Paolo Carlini  <paolo.carlini@oracle.com>
 
        PR c++/24926
diff --git a/gcc/testsuite/g++.dg/ipa/remref-1.C b/gcc/testsuite/g++.dg/ipa/remref-1.C
new file mode 100644 (file)
index 0000000..c25c425
--- /dev/null
@@ -0,0 +1,36 @@
+/* Verify that indirect-inlining induced removal of referenes will not remove
+   too many references in presence of speculative devirtualization.  */
+/* { dg-do link } */
+/* { dg-options "-O3 -fno-early-inlining"  } */
+
+class A
+{
+  public:
+  virtual void foo(void (*)(void));
+};
+
+static
+void b(void)
+{
+}
+
+void
+A::foo(void (*back)(void))
+{
+  back();
+}
+
+class A *a;
+
+void __attribute__ ((noinline, noclone))
+allocate_a ()
+{
+  a = new A();
+}
+
+main()
+{
+  allocate_a();
+  for (int i=0; i<10000;i++)
+    a->foo(b);
+}
diff --git a/gcc/testsuite/g++.dg/ipa/remref-2.C b/gcc/testsuite/g++.dg/ipa/remref-2.C
new file mode 100644 (file)
index 0000000..06bc71a
--- /dev/null
@@ -0,0 +1,37 @@
+/* Verify that we survive creation and deletion of references to facilitate
+   reference removal while also doing (unsuccessful) speculative
+   devirtualization.  */
+/* { dg-do link } */
+/* { dg-options "-O3 -fno-early-inlining"  } */
+
+class A
+{
+  public:
+  virtual void __attribute__ ((noinline)) foo(void (*)(void));
+};
+
+static
+void b(void)
+{
+}
+
+void  __attribute__ ((noinline))
+A::foo(void (*back)(void))
+{
+  back();
+}
+
+class A *a;
+
+void __attribute__ ((noinline, noclone))
+allocate_a ()
+{
+  a = new A();
+}
+
+main()
+{
+  allocate_a();
+  for (int i=0; i<10000;i++)
+    a->foo(b);
+}