aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJan Hubicka <jh@suse.cz>2009-12-10 21:50:47 +0100
committerJan Hubicka <hubicka@gcc.gnu.org>2009-12-10 20:50:47 +0000
commit47cb0d7da16c57888fdc04437530d86f33ee3c1b (patch)
tree4b7897c70e32ed3a7ade0ed23cc48ffbcd023f09 /gcc
parent604df1167c464da3710e28270e6561d427df0444 (diff)
downloadgcc-47cb0d7da16c57888fdc04437530d86f33ee3c1b.zip
gcc-47cb0d7da16c57888fdc04437530d86f33ee3c1b.tar.gz
gcc-47cb0d7da16c57888fdc04437530d86f33ee3c1b.tar.bz2
re PR middle-end/42228 (verify_cgraph_node failed:node has wrong clone_of)
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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog16
-rw-r--r--gcc/cgraph.c60
-rw-r--r--gcc/cgraph.h2
-rw-r--r--gcc/cgraphunit.c5
-rw-r--r--gcc/ipa.c17
-rw-r--r--gcc/testsuite/ChangeLog5
-rw-r--r--gcc/testsuite/g++.dg/torture/pr42110.C27
-rw-r--r--gcc/tree-inline.c6
8 files changed, 118 insertions, 20 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 2e53db2..b027bc9 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 651618c..a3efdfc 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -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)
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index d79d3e4..51426e6 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -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);
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 345fb67..58bdd85 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -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)
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 1b68a7a..708b800 100644
--- 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);
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index b28f1da..fd0628f 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
index 0000000..c778b4f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/pr42110.C
@@ -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);
+}
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 3c90941..aacd903 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -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)