devirt-11.C: Update template.
authorJan Hubicka <jh@suse.cz>
Sun, 8 Sep 2013 16:42:21 +0000 (18:42 +0200)
committerJan Hubicka <hubicka@gcc.gnu.org>
Sun, 8 Sep 2013 16:42:21 +0000 (16:42 +0000)
* testsuite/g++.dg/ipa/devirt-11.C: Update template.
* testsuite/g++.dg/ipa/devirt-16.C: New testcase.
* testsuite/g++.dg/ipa/devirt-17.C: New testcase.
* testsuite/g++.dg/ipa/devirt-18.C: New testcase.

* cgraphunit.c (walk_polymorphic_call_targets): Permit 0 possible
targets and devirtualize to BUILT_IN_UNREACHABLE.
* timevar.def (TV_IPA_UNREACHABLE): New timevar.
* ipa.c (walk_polymorphic_call_targets): New function.
(symtab_remove_unreachable_nodes): Use it; do not keep all virtual
functions; use the new timevar.
* ipa-devirt.c (maybe_record_node): Do not insert static nodes that
was removed from the program.
(record_binfo): If BINFO corresponds to an anonymous namespace, we may
not consider it in the walk when its vtable is dead.
(possible_polymorphic_call_targets_1): Pass anonymous flag to
record_binfo.
(devirt_variable_node_removal_hook): New function.
(possible_polymorphic_call_targets): Also register
devirt_variable_node_removal_hook.
(ipa_devirt): Do not do non-speculative devirtualization.
(gate_ipa_devirt): One execute if devirtualizing speculatively.

From-SVN: r202368

gcc/ChangeLog
gcc/cgraphunit.c
gcc/ipa-devirt.c
gcc/ipa.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ipa/devirt-11.C
gcc/testsuite/g++.dg/ipa/devirt-16.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ipa/devirt-17.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ipa/devirt-18.C [new file with mode: 0644]
gcc/timevar.def

index 7d69e19bb51fd1eefd09e2e23ce489510404c720..df3d29e530d36e9979d90af7c7d17052c6903e56 100644 (file)
@@ -1,3 +1,23 @@
+2013-09-08  Jan Hubicka  <jh@suse.cz>
+
+       * cgraphunit.c (walk_polymorphic_call_targets): Permit 0 possible
+       targets and devirtualize to BUILT_IN_UNREACHABLE.
+       * timevar.def (TV_IPA_UNREACHABLE): New timevar.
+       * ipa.c (walk_polymorphic_call_targets): New function.
+       (symtab_remove_unreachable_nodes): Use it; do not keep all virtual
+       functions; use the new timevar.
+       * ipa-devirt.c (maybe_record_node): Do not insert static nodes that
+       was removed from the program.
+       (record_binfo): If BINFO corresponds to an anonymous namespace, we may
+       not consider it in the walk when its vtable is dead.
+       (possible_polymorphic_call_targets_1): Pass anonymous flag to
+       record_binfo.
+       (devirt_variable_node_removal_hook): New function.
+       (possible_polymorphic_call_targets): Also register
+       devirt_variable_node_removal_hook.
+       (ipa_devirt): Do not do non-speculative devirtualization.
+       (gate_ipa_devirt): One execute if devirtualizing speculatively.
+
 2013-09-08  Jan Hubicka  <jh@suse.cz>
 
        * cgraph.h (varpool_node_hook, varpool_node_hook_list,
index 1afdd439dda98d1b62310c8c889906e2cec9f023..9681df518cda018b23c141b473035e511850ccfc 100644 (file)
@@ -866,9 +866,15 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
      make the edge direct.  */
   if (final)
     {
-      gcc_assert (targets.length());
-      if (targets.length() == 1)
+      if (targets.length() <= 1)
        {
+         cgraph_node *target;
+         if (targets.length () == 1)
+           target = targets[0];
+         else
+           target = cgraph_get_create_node
+                      (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
+
          if (cgraph_dump_file)
            {
              fprintf (cgraph_dump_file,
@@ -877,7 +883,7 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
                                 edge->call_stmt, 0,
                                 TDF_SLIM);
            }
-         cgraph_make_edge_direct (edge, targets[0]);
+         cgraph_make_edge_direct (edge, target);
          cgraph_redirect_edge_call_stmt_to_callee (edge);
          if (cgraph_dump_file)
            {
@@ -1092,7 +1098,7 @@ analyze_functions (void)
      mangling and same body alias creation before we free DECL_ARGUMENTS
      used by it.  */
   if (!seen_error ())
-  symtab_initialize_asm_name_hash ();
+    symtab_initialize_asm_name_hash ();
 }
 
 /* Translate the ugly representation of aliases as alias pairs into nice
index 2a50284c9b6511d3a2b0522087ddd9a351cfdac0..96622b25b1bdf1b7455819fbf5e38be605a06d79 100644 (file)
@@ -570,6 +570,8 @@ maybe_record_node (vec <cgraph_node *> &nodes,
       && fcode != BUILT_IN_TRAP
       && !pointer_set_insert (inserted, target)
       && (target_node = cgraph_get_node (target)) != NULL
+      && (TREE_PUBLIC (target)
+         || target_node->symbol.definition)
       && symtab_real_symbol_p ((symtab_node)target_node))
     {
       pointer_set_insert (cached_polymorphic_call_targets,
@@ -591,6 +593,8 @@ maybe_record_node (vec <cgraph_node *> &nodes,
 
    MATCHED_VTABLES tracks virtual tables we already did lookup
    for virtual function in.
+
+   ANONYMOUS is true if BINFO is part of anonymous namespace.
   */
 
 static void
@@ -600,7 +604,8 @@ record_binfo (vec <cgraph_node *> &nodes,
              tree type_binfo,
              HOST_WIDE_INT otr_token,
              pointer_set_t *inserted,
-             pointer_set_t *matched_vtables)
+             pointer_set_t *matched_vtables,
+             bool anonymous)
 {
   tree type = BINFO_TYPE (binfo);
   int i;
@@ -611,6 +616,19 @@ record_binfo (vec <cgraph_node *> &nodes,
   if (types_same_for_odr (type, otr_type)
       && !pointer_set_insert (matched_vtables, BINFO_VTABLE (type_binfo)))
     {
+      /* For types in anonymous namespace first check if the respective vtable
+        is alive. If not, we know the type can't be called.  */
+      if (!flag_ltrans && anonymous)
+       {
+         tree vtable = BINFO_VTABLE (type_binfo);
+         struct varpool_node *vnode;
+
+         if (TREE_CODE (vtable) == POINTER_PLUS_EXPR)
+           vtable = TREE_OPERAND (TREE_OPERAND (vtable, 0), 0);
+         vnode = varpool_get_node (vtable);
+         if (!vnode || !vnode->symbol.definition)
+           return;
+       }
       tree target = gimple_get_virt_method_for_binfo (otr_token, type_binfo);
       if (target)
        maybe_record_node (nodes, target, inserted);
@@ -626,7 +644,7 @@ record_binfo (vec <cgraph_node *> &nodes,
                       is shared with the outer type.  */
                    BINFO_VTABLE (base_binfo) ? base_binfo : type_binfo,
                    otr_token, inserted,
-                   matched_vtables);
+                   matched_vtables, anonymous);
 }
      
 /* Lookup virtual methods matching OTR_TYPE (with OFFSET and OTR_TOKEN)
@@ -646,7 +664,7 @@ possible_polymorphic_call_targets_1 (vec <cgraph_node *> &nodes,
   unsigned int i;
 
   record_binfo (nodes, binfo, otr_type, binfo, otr_token, inserted,
-               matched_vtables);
+               matched_vtables, type->anonymous_namespace);
   for (i = 0; i < type->derived_types.length(); i++)
     possible_polymorphic_call_targets_1 (nodes, inserted, 
                                         matched_vtables,
@@ -735,6 +753,18 @@ devirt_node_removal_hook (struct cgraph_node *n, void *d ATTRIBUTE_UNUSED)
     free_polymorphic_call_targets_hash ();
 }
 
+/* When virtual table is removed, we may need to flush the cache.  */
+
+static void
+devirt_variable_node_removal_hook (struct varpool_node *n,
+                                  void *d ATTRIBUTE_UNUSED)
+{
+  if (cached_polymorphic_call_targets
+      && DECL_VIRTUAL_P (n->symbol.decl)
+      && type_in_anonymous_namespace_p (DECL_CONTEXT (n->symbol.decl)))
+    free_polymorphic_call_targets_hash ();
+}
+
 /* Return vector containing possible targets of polymorphic call of type
    OTR_TYPE caling method OTR_TOKEN with OFFSET.  If FINALp is non-NULL,
    store true if the list is complette. 
@@ -782,8 +812,12 @@ possible_polymorphic_call_targets (tree otr_type,
       cached_polymorphic_call_targets = pointer_set_create ();
       polymorphic_call_target_hash.create (23);
       if (!node_removal_hook_holder)
-       node_removal_hook_holder =
-         cgraph_add_node_removal_hook (&devirt_node_removal_hook, NULL);
+       {
+         node_removal_hook_holder =
+           cgraph_add_node_removal_hook (&devirt_node_removal_hook, NULL);
+         varpool_add_node_removal_hook (&devirt_variable_node_removal_hook,
+                                        NULL);
+       }
     }
 
   /* Lookup cached answer.  */
@@ -928,11 +962,8 @@ likely_target_p (struct cgraph_node *n)
 }
 
 /* The ipa-devirt pass.
-   This performs very trivial devirtualization:
-     1) when polymorphic call is known to have precisely one target,
-        turn it into direct call
-     2) when polymorphic call has only one likely target in the unit,
-        turn it into speculative call.  */
+   When polymorphic call has only one likely target in the unit,
+   turn it into speculative call.  */
 
 static unsigned int
 ipa_devirt (void)
@@ -965,26 +996,9 @@ ipa_devirt (void)
            if (dump_file)
              dump_possible_polymorphic_call_targets 
                (dump_file, e);
+
            npolymorphic++;
 
-           if (final)
-             {
-               gcc_assert (targets.length());
-               if (targets.length() == 1)
-                 {
-                   if (dump_file)
-                     fprintf (dump_file,
-                              "Devirtualizing call in %s/%i to %s/%i\n",
-                              cgraph_node_name (n), n->symbol.order,
-                              cgraph_node_name (targets[0]), targets[0]->symbol.order);
-                   cgraph_make_edge_direct (e, targets[0]);
-                   ndevirtualized++;
-                   update = true;
-                   continue;
-                 }
-             }
-           if (!flag_devirtualize_speculatively)
-             continue;
            if (!cgraph_maybe_hot_edge_p (e))
              {
                if (dump_file)
@@ -1114,7 +1128,7 @@ ipa_devirt (void)
 static bool
 gate_ipa_devirt (void)
 {
-  return flag_devirtualize && !in_lto_p && optimize;
+  return flag_devirtualize_speculatively && !in_lto_p && optimize;
 }
 
 namespace {
index 37b6629b206b6c066196a96b5eadd1e568936c35..4b82d1d4bc59858accaadad5048bbd7386269345 100644 (file)
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -149,6 +149,84 @@ process_references (struct ipa_ref_list *list,
     }
 }
 
+/* EDGE is an polymorphic call.  If BEFORE_INLINING_P is set, mark
+   all its potential targets as reachable to permit later inlining if
+   devirtualization happens.  After inlining still keep their declarations
+   around, so we can devirtualize to a direct call.
+
+   Also try to make trivial devirutalization when no or only one target is
+   possible.  */
+
+static void
+walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
+                              struct cgraph_edge *edge,
+                              symtab_node *first,
+                              pointer_set_t *reachable, bool before_inlining_p)
+{
+  unsigned int i;
+  void *cache_token;
+  bool final;
+  vec <cgraph_node *>targets
+    = possible_polymorphic_call_targets
+       (edge, &final, &cache_token);
+
+  if (!pointer_set_insert (reachable_call_targets,
+                          cache_token))
+    {
+      for (i = 0; i < targets.length(); i++)
+       {
+         struct cgraph_node *n = targets[i];
+
+         /* Do not bother to mark virtual methods in anonymous namespace;
+            either we will find use of virtual table defining it, or it is
+            unused.  */
+         if (TREE_CODE (TREE_TYPE (n->symbol.decl)) == METHOD_TYPE
+             && type_in_anonymous_namespace_p
+                   (method_class_type (TREE_TYPE (n->symbol.decl))))
+           continue;
+
+         /* Prior inlining, keep alive bodies of possible targets for
+            devirtualization.  */
+          if (n->symbol.definition
+              && before_inlining_p)
+            pointer_set_insert (reachable, n);
+
+         /* Even after inlining we want to keep the possible targets in the
+            boundary, so late passes can still produce direct call even if
+            the chance for inlining is lost.  */
+         enqueue_node ((symtab_node) n, first, reachable);
+       }
+    }
+
+  /* Very trivial devirtualization; when the type is
+     final or anonymous (so we know all its derivation)
+     and there is only one possible virtual call target,
+     make the edge direct.  */
+  if (final)
+    {
+      if (targets.length() <= 1)
+       {
+         cgraph_node *target;
+         if (targets.length () == 1)
+           target = targets[0];
+         else
+           target = cgraph_get_create_node
+                      (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
+
+         if (dump_file)
+           fprintf (dump_file,
+                    "Devirtualizing call in %s/%i to %s/%i\n",
+                    cgraph_node_name (edge->caller),
+                    edge->caller->symbol.order,
+                    cgraph_node_name (target), target->symbol.order);
+         edge = cgraph_make_edge_direct (edge, target);
+         if (cgraph_state != CGRAPH_STATE_IPA_SSA)
+           cgraph_redirect_edge_call_stmt_to_callee (edge);
+         else
+           inline_update_overall_summary (edge->caller);
+       }
+    }
+}
 
 /* Perform reachability analysis and reclaim all unreachable nodes.
 
@@ -214,7 +292,9 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
   bool changed = false;
   struct pointer_set_t *reachable = pointer_set_create ();
   struct pointer_set_t *body_needed_for_clonning = pointer_set_create ();
+  struct pointer_set_t *reachable_call_targets = pointer_set_create ();
 
+  timevar_push (TV_IPA_UNREACHABLE);
 #ifdef ENABLE_CHECKING
   verify_symtab ();
 #endif
@@ -238,10 +318,7 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
       if (node->symbol.definition
          && !node->global.inlined_to
          && !node->symbol.in_other_partition
-         && (!cgraph_can_remove_if_no_direct_calls_and_refs_p (node)
-             /* Keep around virtual functions for possible devirtualization.  */
-             || (before_inlining_p
-                 && DECL_VIRTUAL_P (node->symbol.decl))))
+         && !cgraph_can_remove_if_no_direct_calls_and_refs_p (node))
        {
          gcc_assert (!node->global.inlined_to);
          pointer_set_insert (reachable, node);
@@ -304,6 +381,19 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
          if (!in_boundary_p)
            {
              struct cgraph_edge *e;
+             /* Keep alive possible targets for devirtualization.  */
+             if (optimize && flag_devirtualize)
+               {
+                 struct cgraph_edge *next;
+                 for (e = cnode->indirect_calls; e; e = next)
+                   {
+                     next = e->next_callee;
+                     if (e->indirect_info->polymorphic)
+                       walk_polymorphic_call_targets (reachable_call_targets,
+                                                      e, &first, reachable,
+                                                      before_inlining_p);
+                   }
+               }
              for (e = cnode->callees; e; e = e->next_callee)
                {
                  if (e->callee->symbol.definition
@@ -449,6 +539,7 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
 
   pointer_set_destroy (reachable);
   pointer_set_destroy (body_needed_for_clonning);
+  pointer_set_destroy (reachable_call_targets);
 
   /* Now update address_taken flags and try to promote functions to be local.  */
   if (file)
@@ -483,6 +574,7 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
     FOR_EACH_DEFINED_FUNCTION (node)
       ipa_propagate_frequency (node);
 
+  timevar_pop (TV_IPA_UNREACHABLE);
   return changed;
 }
 
index 21b761cdee4a246270fe50dad2d75753e68dde05..9289831ced89035f63610373d911aa184a86aeff 100644 (file)
@@ -1,3 +1,10 @@
+2013-09-08  Jan Hubicka  <jh@suse.cz>
+
+       * testsuite/g++.dg/ipa/devirt-11.C: Update template.
+       * testsuite/g++.dg/ipa/devirt-16.C: New testcase.
+       * testsuite/g++.dg/ipa/devirt-17.C: New testcase.
+       * testsuite/g++.dg/ipa/devirt-18.C: New testcase.
+
 2013-09-08  Paolo Carlini  <paolo.carlini@oracle.com>
 
        PR c++/54941
index b888935ff307b410e2deb3b43d5f9c9c863a59bd..d30d56cff24e15528060315f59d512f9274914f2 100644 (file)
@@ -46,5 +46,4 @@ bar ()
    and two to fn3. While doing so the new symbol for fn2 needs to be
    introduced.  */
 /* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 3 "inline"  } } */
-/* { dg-final { scan-ipa-dump-times "and turned into root of the clone tree" 1 "inline"  } } */
 /* { dg-final { cleanup-ipa-dump "inline" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-16.C b/gcc/testsuite/g++.dg/ipa/devirt-16.C
new file mode 100644 (file)
index 0000000..8556786
--- /dev/null
@@ -0,0 +1,39 @@
+/* We shall devirtualize to unreachable.  No anonymous type method should surivve
+   reachability.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-whole-program"  } */
+namespace {
+class B {
+public:
+  virtual int foo(void)
+{
+  return 0;
+}
+};
+class A : public B {
+public:
+  virtual int foo(void)
+{
+  return 1;
+}
+};
+}
+class B *b;
+main()
+{
+  int c;
+  if (c)
+    {
+    class A a;
+    a.foo();
+    class B b;
+    b.foo();
+    }
+  return b->foo();
+}
+
+/* { dg-final { scan-ipa-dump "Devirtualizing" "whole-program"} } */
+/* { dg-final { scan-ipa-dump "builtin_unreachable" "whole-program"} } */
+/* { dg-final { scan-ipa-dump-not "A::foo" "whole-program"} } */
+/* { dg-final { scan-ipa-dump-not "A::foo" "whole-program"} } */
+/* { dg-final { cleanup-ipa-dump "whole-program" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-17.C b/gcc/testsuite/g++.dg/ipa/devirt-17.C
new file mode 100644 (file)
index 0000000..9edfd73
--- /dev/null
@@ -0,0 +1,44 @@
+/* We shall devirtualize to B::foo since it is the only live candidate of an
+   anonymous type.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-whole-program"  } */
+namespace {
+class B {
+public:
+  virtual int foo(void)
+{
+  return 0;
+}
+};
+class A : public B {
+public:
+  virtual int foo(void)
+{
+  return 1;
+}
+};
+}
+class B *b;
+void get_me_lost (void *);
+main()
+{
+  int c;
+  if (c)
+    {
+    class A a;
+    a.foo();
+    }
+  else
+    {
+    b = new (class B);
+    b->foo();
+       get_me_lost ((void *)&b);
+    }
+  return b->foo();
+}
+
+/* { dg-final { scan-ipa-dump "Devirtualizing" "whole-program"} } */
+/* { dg-final { scan-ipa-dump-not "builtin_unreachable" "whole-program"} } */
+/* { dg-final { scan-ipa-dump "B::foo" "whole-program"} } */
+/* { dg-final { scan-ipa-dump-not "A::foo" "whole-program"} } */
+/* { dg-final { cleanup-ipa-dump "whole-program" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-18.C b/gcc/testsuite/g++.dg/ipa/devirt-18.C
new file mode 100644 (file)
index 0000000..dbbe597
--- /dev/null
@@ -0,0 +1,37 @@
+/* We shall devirtualize to unreachable.  No anonymous type method should surivve
+   reachability.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-ssa"  } */
+namespace {
+class B {
+public:
+  virtual int foo(void)
+{
+  return 0;
+}
+};
+class A : public B {
+public:
+  virtual int foo(void)
+{
+  return 1;
+}
+};
+}
+class B *b;
+main()
+{
+  if (0)
+    {
+    class A a;
+    a.foo();
+    class B b;
+    b.foo();
+    }
+  return b->foo();
+}
+
+/* { dg-final { scan-tree-dump-not "A::foo" "ssa"} } */
+/* { dg-final { scan-tree-dump-not "B::foo" "ssa"} } */
+/* { dg-final { scan-tree-dump "builtin_unreachable" "ssa"} } */
+/* { dg-final { cleanup-tree-dump "ssa" } } */
index 5fe9095323578d361fa176f550b424da9949ad79..5a880a8a3645da5e91b43c8d1eaaa1d881f87044 100644 (file)
@@ -64,6 +64,7 @@ DEFTIMEVAR (TV_PCH_CPP_RESTORE       , "PCH preprocessor state restore")
 
 DEFTIMEVAR (TV_CGRAPH                , "callgraph construction")
 DEFTIMEVAR (TV_CGRAPHOPT             , "callgraph optimization")
+DEFTIMEVAR (TV_IPA_UNREACHABLE       , "ipa dead code removal")
 DEFTIMEVAR (TV_IPA_INHERITANCE       , "ipa inheritance graph")
 DEFTIMEVAR (TV_IPA_VIRTUAL_CALL      , "ipa virtual call target")
 DEFTIMEVAR (TV_IPA_DEVIRT           , "ipa devirtualization")