I just tried to compile package libcrypto++ with the GNU C++ compiler version 4.5 snapshot 20091126 and the compiler said validat2.cpp: At global scope: validat2.cpp:764:1: error: node has wrong clone_of validat2.cpp:764:1: error: double linked list of clones corrupted CryptoPP::DL_FixedBasePrecomputationImpl<T>::DL_FixedBasePrecomputationImpl() [with T = CryptoPP::ECPPoint]/4286(-1) @0x2affd636b110 (clone of CryptoPP::DL_FixedBasePrecomputationImpl<T>::DL_FixedBasePrecomputationImpl() [with T = CryptoPP::ECPPoint]/5234) availability:available 30 time, 19 benefit (41 after inlining) 17 size, 11 benefit (23 after inlining) body finalized inlinable called by: calls: CryptoPP::Integer::Integer()/7921 (1.00 per call) CryptoPP::ECPPoint::~ECPPoint()/1510 validat2.cpp:764:1: internal compiler error: verify_cgraph_node failed Please submit a full bug report, with preprocessed source if appropriate. See <http://gcc.gnu.org/bugs.html> for instructions. Preprocessed source code attached. Flag -O3 required.
Created attachment 19186 [details] gzipped C++ source code
Confirmed.
After removing a couple of lines from the preprocessed code I end up with the same problem as in PR41290. Apparently this bug hasn't been fixed and still haunts us.
Subject: Re: [4.5 Regression] verify_cgraph_node failed:node has wrong clone_of Hi, it turns out that this testcase seems firs tthat excercise code path where clone is created partially inlined and as a result of later optimization out of line copy of that clone is no longer needed. It shows I got quite few updates of the clone tree wrong here. Sadly I failed to distille small testcase, but I am testing the following (it contains also few other minor fixes I spotted while proofreading the code) * 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. Index: cgraph.c =================================================================== --- cgraph.c (revision 155090) +++ cgraph.c (working copy) @@ -829,7 +829,8 @@ cgraph_set_call_stmt_including_clones (s } /* 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 (s 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 (str 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 * = 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 * 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 * } } - 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->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: cgraph.h =================================================================== --- cgraph.h (revision 155090) +++ cgraph.h (working copy) @@ -445,7 +445,7 @@ void cgraph_set_call_stmt (struct cgraph 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: cgraphunit.c =================================================================== --- cgraphunit.c (revision 155090) +++ cgraphunit.c (working copy) @@ -749,6 +749,7 @@ verify_cgraph_node (struct cgraph_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 * 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: ipa.c =================================================================== --- ipa.c (revision 155090) +++ ipa.c (working copy) @@ -179,11 +179,21 @@ cgraph_remove_unreachable_nodes (bool be 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 be 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: tree-inline.c =================================================================== --- tree-inline.c (revision 155090) +++ tree-inline.c (working copy) @@ -1694,13 +1694,15 @@ copy_bb (copy_body_data *id, basic_block || !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)
Subject: Bug 42228 Author: hubicka Date: Thu Dec 10 20:50:47 2009 New Revision: 155140 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=155140 Log: 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. Added: trunk/gcc/testsuite/g++.dg/torture/pr42110.C Modified: trunk/gcc/ChangeLog trunk/gcc/cgraph.c trunk/gcc/cgraph.h trunk/gcc/cgraphunit.c trunk/gcc/ipa.c trunk/gcc/testsuite/ChangeLog trunk/gcc/tree-inline.c
Fixed.