re PR middle-end/42228 (verify_cgraph_node failed:node has wrong clone_of)
authorJan Hubicka <jh@suse.cz>
Thu, 10 Dec 2009 20:50:47 +0000 (21:50 +0100)
committerJan Hubicka <hubicka@gcc.gnu.org>
Thu, 10 Dec 2009 20:50:47 +0000 (20:50 +0000)
PR middle-end/42228
PR middle-end/42110
* cgraph.c (cgraph_create_edge_including_clones): Add old_stmt parameter;
update edge if it already exists.
(cgraph_remove_node): Handle correctly cases where we are removing node having
clones.
* cgraph.h (cgraph_create_edge_including_clones): Declare.
(verify_cgraph_node): Add missing error_found = true code.
(cgraph_materialize_all_clones): Remove call edges of dead nodes.
* ipa.c (cgraph_remove_unreachable_nodes): Correctly look for master
clone; fix double linked list removal.
* tree-inline.c (copy_bb): Update cgraph_create_edge_including_clones call;
fix frequency of newly created edge.

* g++.dg/torture/pr42110.C: new file.

From-SVN: r155140

gcc/ChangeLog
gcc/cgraph.c
gcc/cgraph.h
gcc/cgraphunit.c
gcc/ipa.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/torture/pr42110.C [new file with mode: 0644]
gcc/tree-inline.c

index 2e53db23cd47ca174782b034f9b03dc2e9d23d0b..b027bc9f162ebfd0b1d146d9f50afc5de6c663dd 100644 (file)
@@ -1,3 +1,19 @@
+2009-12-10  Jan Hubicka  <jh@suse.cz>
+
+       PR middle-end/42228
+       PR middle-end/42110
+       * cgraph.c (cgraph_create_edge_including_clones): Add old_stmt parameter;
+       update edge if it already exists.
+       (cgraph_remove_node): Handle correctly cases where we are removing node having
+       clones.
+       * cgraph.h (cgraph_create_edge_including_clones): Declare.
+       (verify_cgraph_node): Add missing error_found = true code.
+       (cgraph_materialize_all_clones): Remove call edges of dead nodes.
+       * ipa.c (cgraph_remove_unreachable_nodes): Correctly look for master
+       clone; fix double linked list removal.
+       * tree-inline.c (copy_bb): Update cgraph_create_edge_including_clones call;
+       fix frequency of newly created edge.
+
 2009-12-10  Bernd Schmidt  <bernd.schmidt@analog.com>
 
        PR rtl-opt/42216
index 651618c4232782c4faf20c1239fcd6756f51887e..a3efdfc0ce10da76ff71b6e74535476f498c286e 100644 (file)
@@ -829,7 +829,8 @@ cgraph_set_call_stmt_including_clones (struct cgraph_node *orig,
 }
 
 /* Like cgraph_create_edge walk the clone tree and update all clones sharing
-   same function body.
+   same function body.  If clones already have edge for OLD_STMT; only
+   update the edge same way as cgraph_set_call_stmt_including_clones does.
 
    TODO: COUNT and LOOP_DEPTH should be properly distributed based on relative
    frequencies of the clones.  */
@@ -837,6 +838,7 @@ cgraph_set_call_stmt_including_clones (struct cgraph_node *orig,
 void
 cgraph_create_edge_including_clones (struct cgraph_node *orig,
                                     struct cgraph_node *callee,
+                                    gimple old_stmt,
                                     gimple stmt, gcov_type count,
                                     int freq, int loop_depth,
                                     cgraph_inline_failed_t reason)
@@ -854,9 +856,15 @@ cgraph_create_edge_including_clones (struct cgraph_node *orig,
   if (node)
     while (node != orig)
       {
-        /* It is possible that we already constant propagated into the clone
-          and turned indirect call into dirrect call.  */
-        if (!cgraph_edge (node, stmt))
+       struct cgraph_edge *edge = cgraph_edge (node, old_stmt);
+
+        /* It is possible that clones already contain the edge while
+          master didn't.  Either we promoted indirect call into direct
+          call in the clone or we are processing clones of unreachable
+          master where edges has been rmeoved.  */
+       if (edge)
+         cgraph_set_call_stmt (edge, stmt);
+       else if (!cgraph_edge (node, stmt))
          {
            edge = cgraph_create_edge (node, callee, stmt, count,
                                       freq, loop_depth);
@@ -1337,11 +1345,15 @@ cgraph_remove_node (struct cgraph_node *node)
              = next_inline_clone->prev_sibling_clone;
          if (next_inline_clone->prev_sibling_clone)
            {
+             gcc_assert (node->clones != next_inline_clone);
              next_inline_clone->prev_sibling_clone->next_sibling_clone
                = next_inline_clone->next_sibling_clone;
            }
          else
-          node->clones = next_inline_clone->next_sibling_clone;
+           {
+             gcc_assert (node->clones == next_inline_clone);
+             node->clones = next_inline_clone->next_sibling_clone;
+           }
 
          new_clones = node->clones;
          node->clones = NULL;
@@ -1355,6 +1367,8 @@ cgraph_remove_node (struct cgraph_node *node)
          next_inline_clone->next_sibling_clone = NULL;
          if (node->clone_of)
            {
+             if (node->clone_of->clones)
+               node->clone_of->clones->prev_sibling_clone = next_inline_clone;
              next_inline_clone->next_sibling_clone = node->clone_of->clones;
              node->clone_of->clones = next_inline_clone;
            }
@@ -1389,8 +1403,6 @@ cgraph_remove_node (struct cgraph_node *node)
        }
 
     }
-  else
-    gcc_assert (node->clone_of);
   if (node->prev_sibling_clone)
     node->prev_sibling_clone->next_sibling_clone = node->next_sibling_clone;
   else if (node->clone_of)
@@ -1399,15 +1411,33 @@ cgraph_remove_node (struct cgraph_node *node)
     node->next_sibling_clone->prev_sibling_clone = node->prev_sibling_clone;
   if (node->clones)
     {
-      struct cgraph_node *n;
+      struct cgraph_node *n, *next;
 
-      for (n = node->clones; n->next_sibling_clone; n = n->next_sibling_clone)
-       n->clone_of = node->clone_of;
-      n->clone_of = node->clone_of;
-      n->next_sibling_clone = node->clone_of->clones;
-      if (node->clone_of->clones)
-       node->clone_of->clones->prev_sibling_clone = n;
-      node->clone_of->clones = node->clones;
+      if (node->clone_of)
+        {
+         for (n = node->clones; n->next_sibling_clone; n = n->next_sibling_clone)
+           n->clone_of = node->clone_of;
+         n->clone_of = node->clone_of;
+         n->next_sibling_clone = node->clone_of->clones;
+         if (node->clone_of->clones)
+           node->clone_of->clones->prev_sibling_clone = n;
+         node->clone_of->clones = node->clones;
+       }
+      else
+        {
+         /* We are removing node with clones.  this makes clones inconsistent,
+            but assume they will be removed subsequently and just keep clone
+            tree intact.  This can happen in unreachable function removal since
+            we remove unreachable functions in random order, not by bottom-up
+            walk of clone trees.  */
+         for (n = node->clones; n; n = next)
+           {
+              next = n->next_sibling_clone;
+              n->next_sibling_clone = NULL;
+              n->prev_sibling_clone = NULL;
+              n->clone_of = NULL;
+           }
+       }
     }
 
   while (node->same_body)
index d79d3e4d86b52604488abf500b15e4a89b7446af..51426e60ab72b5762d909d58b76de5e8dc060688 100644 (file)
@@ -445,7 +445,7 @@ void cgraph_set_call_stmt (struct cgraph_edge *, gimple);
 void cgraph_set_call_stmt_including_clones (struct cgraph_node *, gimple, gimple);
 void cgraph_create_edge_including_clones (struct cgraph_node *,
                                          struct cgraph_node *,
-                                         gimple, gcov_type, int, int,
+                                         gimple, gimple, gcov_type, int, int,
                                          cgraph_inline_failed_t);
 void cgraph_update_edges_for_call_stmt (gimple, tree, gimple);
 struct cgraph_local_info *cgraph_local_info (tree);
index 345fb670a82a796fa02253a4820c977eef77ceca..58bdd8512c2e0e50f7c57130314966a30446d8c4 100644 (file)
@@ -749,6 +749,7 @@ verify_cgraph_node (struct cgraph_node *node)
                          {
                            error ("edge points to same body alias:");
                            debug_tree (e->callee->decl);
+                           error_found = true;
                          }
                        else if (!clone_of_p (cgraph_node (decl), e->callee)
                                 && !e->callee->global.inlined_to)
@@ -757,6 +758,7 @@ verify_cgraph_node (struct cgraph_node *node)
                            debug_tree (e->callee->decl);
                            fprintf (stderr," Instead of:");
                            debug_tree (decl);
+                           error_found = true;
                          }
                        e->aux = (void *)1;
                      }
@@ -2248,6 +2250,9 @@ cgraph_materialize_all_clones (void)
            }
        }
     }
+  for (node = cgraph_nodes; node; node = node->next)
+    if (!node->analyzed && node->callees)
+      cgraph_node_remove_callees (node);
   if (cgraph_dump_file)
     fprintf (cgraph_dump_file, "Updating call sites\n");
   for (node = cgraph_nodes; node; node = node->next)
index 1b68a7a409278aa95191abfe24f08d26dd1425bc..708b800a1a60c991b3dc437cab22b2a4379b4641 100644 (file)
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -179,11 +179,21 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
                  first = e->callee;
                }
            }
+       
+      /* We can freely remove inline clones even if they are cloned, however if
+        function is clone of real clone, we must keep it around in order to
+        make materialize_clones produce function body with the changes
+        applied.  */
       while (node->clone_of && !node->clone_of->aux && !gimple_has_body_p (node->decl))
         {
+         bool noninline = node->clone_of->decl != node->decl;
          node = node->clone_of;
-         node->aux = first;
-         first = node;
+         if (noninline)
+           {
+             node->aux = first;
+             first = node;
+             break;
+           }
        }
     }
 
@@ -244,6 +254,9 @@ cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
                    node->clone_of->clones = node->next_sibling_clone;
                  if (node->next_sibling_clone)
                    node->next_sibling_clone->prev_sibling_clone = node->prev_sibling_clone;
+                 node->clone_of = NULL;
+                 node->next_sibling_clone = NULL;
+                 node->prev_sibling_clone = NULL;
                }
              else
                cgraph_remove_node (node);
index b28f1da27679604d9670c597ad824fab01e78f74..fd0628f5207a0bf3b56dbd07a90c9273148dc1e2 100644 (file)
@@ -1,3 +1,8 @@
+2009-12-10  Jan Hubicka  <jh@suse.cz>
+
+       PR middle-end/42110
+       * g++.dg/torture/pr42110.C: new file.
+
 2009-12-10  Daniel Franke  <franke.daniel@gmail.com>
 
         PR fortran/34402
diff --git a/gcc/testsuite/g++.dg/torture/pr42110.C b/gcc/testsuite/g++.dg/torture/pr42110.C
new file mode 100644 (file)
index 0000000..c778b4f
--- /dev/null
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+bool foo();
+
+struct A
+{
+  A* fooA() { if (foo()) foo(); return this; }
+
+  virtual void barA(char);
+};
+
+template<int> struct B
+{
+  A *p, *q;
+
+  void fooB(char c) { p->fooA()->barA(c); }
+};
+
+template<int N> inline void bar(B<N> b) { b.fooB(0); }
+
+extern template void bar(B<0>);
+
+void (*f)(B<0>) = bar;
+
+void baz()
+{
+  B<0>().fooB(0);
+}
index 3c909419bd2fb0b788526194367348e78a87dcbb..aacd903bac532cdcae8c11f2dfd2c860f170ddc9 100644 (file)
@@ -1694,13 +1694,15 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale,
                              || !id->src_node->analyzed);
                  if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES)
                    cgraph_create_edge_including_clones
-                     (id->dst_node, dest, stmt, bb->count,
+                     (id->dst_node, dest, orig_stmt, stmt, bb->count,
                       compute_call_stmt_bb_frequency (id->dst_node->decl,
                                                       copy_basic_block),
                       bb->loop_depth, CIF_ORIGINALLY_INDIRECT_CALL);
                  else
                    cgraph_create_edge (id->dst_node, dest, stmt,
-                                       bb->count, CGRAPH_FREQ_BASE,
+                                       bb->count,
+                                       compute_call_stmt_bb_frequency
+                                         (id->dst_node->decl, copy_basic_block),
                                        bb->loop_depth)->inline_failed
                      = CIF_ORIGINALLY_INDIRECT_CALL;
                  if (dump_file)