Bug 42228 - [4.5 Regression] verify_cgraph_node failed:node has wrong clone_of
Summary: [4.5 Regression] verify_cgraph_node failed:node has wrong clone_of
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 4.5.0
: P1 normal
Target Milestone: 4.5.0
Assignee: Not yet assigned to anyone
URL:
Keywords: ice-checking, ice-on-valid-code
Depends on:
Blocks:
 
Reported: 2009-11-30 10:04 UTC by David Binderman
Modified: 2009-12-11 11:17 UTC (History)
2 users (show)

See Also:
Host: suse-linux-x86_64
Target:
Build:
Known to work: 4.4.2
Known to fail:
Last reconfirmed: 2009-11-30 10:41:11


Attachments
gzipped C++ source code (163.90 KB, application/octet-stream)
2009-11-30 10:06 UTC, David Binderman
Details

Note You need to log in before you can comment on or make changes to this bug.
Description David Binderman 2009-11-30 10:04:16 UTC
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.
Comment 1 David Binderman 2009-11-30 10:06:48 UTC
Created attachment 19186 [details]
gzipped C++ source code
Comment 2 Richard Biener 2009-11-30 10:41:11 UTC
Confirmed.
Comment 3 Volker Reichelt 2009-12-08 14:16:33 UTC
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.
Comment 4 Jan Hubicka 2009-12-10 17:36:09 UTC
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)
Comment 5 Jan Hubicka 2009-12-10 20:51:02 UTC
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

Comment 6 Jan Hubicka 2009-12-11 11:17:28 UTC
Fixed.