From ee3f5fc0f04108d47a518ce7efadd87eb51a9cc3 Mon Sep 17 00:00:00 2001 From: hubicka Date: Thu, 10 Dec 2009 20:50:47 +0000 Subject: [PATCH] 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@155140 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 16 +++++++++ gcc/cgraph.c | 60 +++++++++++++++++++++++++--------- gcc/cgraph.h | 2 +- gcc/cgraphunit.c | 5 +++ gcc/ipa.c | 17 ++++++++-- gcc/testsuite/ChangeLog | 5 +++ gcc/testsuite/g++.dg/torture/pr42110.C | 27 +++++++++++++++ gcc/tree-inline.c | 6 ++-- 8 files changed, 118 insertions(+), 20 deletions(-) create mode 100644 gcc/testsuite/g++.dg/torture/pr42110.C 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 + + 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 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 + + PR middle-end/42110 + * g++.dg/torture/pr42110.C: new file. + 2009-12-10 Daniel Franke 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 struct B +{ + A *p, *q; + + void fooB(char c) { p->fooA()->barA(c); } +}; + +template inline void bar(B 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) -- 2.7.4