This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[lto][patch] Fix how we handle clones in wpa


This patch fixes two errors that correspond to the two included tests.

The first problem is simple (the -1 test): If we see a static function
being inlined in its current file, we don't check that if it was also
inlined in another file. This is fixed by the change
lto_promote_cross_file_statics. It can probably be an independent
patch, but I haven't tested that.

The second problem is that we try to include the master clone of a
node if we include that node in a set. This is wasteful since ltrans
can easily handle which clone is the master and it can actually be
wrong.

In some cases, gcc will use an existing node for inlining and we have
a node that has inlined_to set and node == node->master_clone. This
happens in cgraph_clone_inlined_node. When a node is removed, the
master clone is also updated to point to the next one in the list. I
think a similar condition can be generated by that.

Bootstrapped and regression tested.

gcc/
2009-02-19  Rafael Avila de Espindola  <espindola@google.com>

	* lto-cgraph.c: Include lto-util.h
	(maybe_redirect_inlined_node): Remove.
	(output_edge): Don't worry about an edge poiting to a node not in know
	by the encoder.
	(output_node): Output the decl number instead of the master node
	number in the case of a clone.
	(output_cgraph_verify_node): Don't check that the master is in the set.
	(output_cgraph): Don't output the current master for each decl. The
	first node output will be the master in ltrans.
	(input_node): If a clone, read the decl number and use that decls'
	master node.
	(input_cgraph_1): Update call to input_node.
	* lto-function-out.c (lto_output): Proces each function once. Don't
	care if the node is the master.

gcc/lto/
2009-02-19  Rafael Avila de Espindola  <espindola@google.com>

	* lto.c (lto_add_inline_clones): Don't add the master clone. Check
	for a decl in the original bitmap, not a node.
	(lto_add_all_inlinees): Remove original nodes that are not needed.
	(lto_scan_statics_in_cgraph_node): Don't care if the node is the master.
	(lto_promote_cross_file_statics): Use a new context.seen_node_decls
	for each set.

gcc/testsuite/
2009-02-19  Rafael Avila de Espindola  <espindola@google.com>

	* gcc.dg/lto/20090218-1_0.c: New.
	* gcc.dg/lto/20090218-1_1.c: New.
	* gcc.dg/lto/20090218-2_0.c: New.
	* gcc.dg/lto/20090218-2_1.c: New.

Cheers,
--
Rafael Avila de Espindola

Google | Gordon House | Barrow Street | Dublin 4 | Ireland
Registered in Dublin, Ireland | Registration Number: 368047
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index 32deb8e..335b826 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -52,6 +52,7 @@ Boston, MA 02110-1301, USA.  */
 #include "lto-section-out.h"
 #include "pointer-set.h"
 #include "lto-tree-in.h"
+#include "lto-utils.h"
 
 /* Call-Graph Streamer.
 
@@ -156,35 +157,15 @@ const char * LTO_cgraph_tag_names[LTO_cgraph_last_tag] =
 {"", "avail", "overwrite", "unavail", "edge"};
 #endif
 
-/* Redirect inlined NODE to its master clone if it is not in SET. 
-   Return NODE itself if it is in SET or it is not inlined.  */
-
-static struct cgraph_node *
-maybe_redirect_inlined_node (struct cgraph_node *node, cgraph_node_set set)
-{
-  struct cgraph_node *master_clone;
-
-  if (!cgraph_node_in_set_p (node, set) && node->global.inlined_to != NULL)
-    {
-      master_clone = node->master_clone;
-      gcc_assert (master_clone && master_clone != node);
-      return master_clone;
-    }
-  else
-    return node;
-}
-
 /* Output the cgraph EDGE to OB using ENCODER.  */
 
 static void
 output_edge (struct lto_simple_output_block *ob,
-	     struct cgraph_edge *edge, lto_cgraph_encoder_t encoder,
-	     cgraph_node_set set)
+	     struct cgraph_edge *edge, lto_cgraph_encoder_t encoder)
 {
   unsigned int uid;
   intptr_t ref;
   unsigned HOST_WIDEST_INT flags = 0;
-  struct cgraph_node *callee;
 
   lto_output_uleb128_stream (ob->main_stream, LTO_cgraph_edge);
   LTO_DEBUG_INDENT (LTO_cgraph_edge);
@@ -195,8 +176,7 @@ output_edge (struct lto_simple_output_block *ob,
   lto_output_sleb128_stream (ob->main_stream, ref);
 
   LTO_DEBUG_TOKEN ("callee");
-  callee = maybe_redirect_inlined_node (edge->callee, set);
-  ref = lto_cgraph_encoder_lookup (encoder, callee);
+  ref = lto_cgraph_encoder_lookup (encoder, edge->callee);
   gcc_assert (ref != LCC_NOT_FOUND); 
   lto_output_sleb128_stream (ob->main_stream, ref);
 
@@ -204,13 +184,8 @@ output_edge (struct lto_simple_output_block *ob,
   uid = flag_wpa ? edge->lto_stmt_uid : gimple_uid (edge->call_stmt);
   lto_output_uleb128_stream (ob->main_stream, uid);
 
-  /* If we have redirected an inlined callee outside SET to its master
-     clone, mark edge as not inlined.  */
   LTO_DEBUG_TOKEN ("inline_failed");
-  if (callee != edge->callee)
-    lto_output_uleb128_stream (ob->main_stream, CIF_UNSPECIFIED);
-  else
-    lto_output_uleb128_stream (ob->main_stream, edge->inline_failed);
+  lto_output_uleb128_stream (ob->main_stream, edge->inline_failed);
 
   LTO_DEBUG_TOKEN ("count");
   lto_output_uleb128_stream (ob->main_stream, edge->count);
@@ -232,14 +207,14 @@ output_edge (struct lto_simple_output_block *ob,
 
 static void
 output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
-	     lto_cgraph_encoder_t encoder, cgraph_node_set set)
+	     lto_cgraph_encoder_t encoder, cgraph_node_set set,
+	     bitmap wrote_master)
 {
   unsigned int tag;
   unsigned HOST_WIDEST_INT flags = 0;
-  struct cgraph_node *master_clone = node->master_clone;
   unsigned local, externally_visible, inlinable;
   bool boundary_p = !cgraph_node_in_set_p (node, set);
-  bool clone_p = node->master_clone && node != node->master_clone;
+  bool clone_p = bitmap_bit_p (wrote_master, DECL_UID (node->decl));
   intptr_t ref;
 
   switch (cgraph_function_body_availability (node))
@@ -302,13 +277,12 @@ output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
 
   if (clone_p)
     {
-      intptr_t ref = lto_cgraph_encoder_lookup (encoder, master_clone);
       LTO_DEBUG_TOKEN ("master");
-      gcc_assert (ref != LCC_NOT_FOUND);
-      lto_output_sleb128_stream (ob->main_stream, ref);
+      lto_output_fn_decl_index (ob->decl_state, ob->main_stream, node->decl);
     }
   else
-    { 
+    {
+      bitmap_set_bit (wrote_master, DECL_UID (node->decl));
       lto_output_fn_decl_index (ob->decl_state, ob->main_stream, node->decl);
       LTO_DEBUG_FN_NAME (node->decl);
     }
@@ -379,9 +353,8 @@ output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
    master_clone nodes or nodes that have no function bodies.  */
 
 static void
-output_cgraph_verify_node (cgraph_node_set set, struct cgraph_node *node)
+output_cgraph_verify_node (struct cgraph_node *node)
 {
-  struct cgraph_node *master_clone;
   switch (cgraph_function_body_availability (node))
     {
     case AVAIL_UNSET:
@@ -395,13 +368,6 @@ output_cgraph_verify_node (cgraph_node_set set, struct cgraph_node *node)
     case AVAIL_OVERWRITABLE:
     case AVAIL_AVAILABLE:
     case AVAIL_LOCAL:
-      master_clone = cgraph_master_clone (node, false);
-      if  (node != master_clone
-	   && !cgraph_node_in_set_p (master_clone, set))
-	{
-	  fprintf (stderr, "found clone with no master\n.");
-	  gcc_assert (0);
-	}
       break;
     }
 }
@@ -414,7 +380,7 @@ output_cgraph_verify_node (cgraph_node_set set, struct cgraph_node *node)
 static void
 output_cgraph (cgraph_node_set set)
 {
-  struct cgraph_node *node, *callee, *master_clone;
+  struct cgraph_node *node;
   struct lto_simple_output_block *ob 
     = lto_create_simple_output_block (LTO_section_cgraph);
   cgraph_node_set_iterator csi;
@@ -422,6 +388,11 @@ output_cgraph (cgraph_node_set set)
   lto_cgraph_encoder_t encoder = lto_cgraph_encoder_new ();
   int i, n_nodes;
 
+  /* The decls for which we have written a master node. Note the the node
+     we select as master might not be the current master. In fact, the
+     current master might not be in the current file at all! */
+  bitmap wrote_master = lto_bitmap_alloc ();
+
 #ifdef LTO_STREAM_DEBUGGING
   lto_debug_context.tag_names = LTO_cgraph_tag_names;
   lto_debug_context.stream_name = "cgraph";
@@ -431,26 +402,24 @@ output_cgraph (cgraph_node_set set)
   for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
     {
       node = csi_node (csi);
-      /* Make sure master clone appears before other clones.  */
-      master_clone = node->master_clone;
-      if (master_clone != NULL && master_clone != node) 
-	{
-	  gcc_assert (cgraph_node_in_set_p (master_clone, set));
-	  lto_cgraph_encoder_encode (encoder, master_clone);
-	}
-	lto_cgraph_encoder_encode (encoder, node);
+      lto_cgraph_encoder_encode (encoder, node);
     }
 
-  /* Go over all the nodes again to include callees that are not in SET.
-     For inlined callee that are not in the set, redirect the edge to 
-     its master clone.  */
+  /* Go over all the nodes again to include callees that are not in SET. */
   for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
     {
       node = csi_node (csi);
       for (edge = node->callees; edge; edge = edge->next_callee)
 	{
-	  callee = maybe_redirect_inlined_node (edge->callee, set);
-	  lto_cgraph_encoder_encode (encoder, callee);
+	  struct cgraph_node *callee = edge->callee;
+	  /* We moved all the inlines. */
+	  if (callee->global.inlined_to != NULL)
+	    gcc_assert (cgraph_node_in_set_p (callee, set));
+	  if (!cgraph_node_in_set_p (callee, set))
+	    {
+	      gcc_assert (!callee->global.inlined_to);
+	      lto_cgraph_encoder_encode (encoder, callee);
+	    }
 	}
     }
 
@@ -460,17 +429,18 @@ output_cgraph (cgraph_node_set set)
     {
       node = lto_cgraph_encoder_deref (encoder, i);
 #ifdef ENABLE_CHECKING
-      output_cgraph_verify_node (set, node);
+      output_cgraph_verify_node (node);
 #endif
-      output_node (ob, node, encoder, set);
+      output_node (ob, node, encoder, set, wrote_master);
     }
+  lto_bitmap_free (wrote_master);
 
   /* Go over the nodes in SET again to write edges.  */
   for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
     {
       node = csi_node (csi);
       for (edge = node->callees; edge; edge = edge->next_callee)
-	output_edge (ob, edge, encoder, set);
+	output_edge (ob, edge, encoder);
     }
 
   lto_output_uleb128_stream (ob->main_stream, 0);
@@ -523,8 +493,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data,
 static struct cgraph_node *
 input_node (struct lto_file_decl_data* file_data,
 	    struct lto_input_block *ib,
-	    enum LTO_cgraph_tags tag,
-	    VEC(cgraph_node_ptr, heap) *nodes)
+	    enum LTO_cgraph_tags tag)
 {
   tree fn_decl;
   struct cgraph_node *node, *master_clone;
@@ -546,7 +515,11 @@ input_node (struct lto_file_decl_data* file_data,
   if (clone_p)
     {
       LTO_DEBUG_TOKEN ("master");
-      master_clone = VEC_index (cgraph_node_ptr, nodes, lto_input_sleb128 (ib));
+      decl_index = lto_input_uleb128 (ib);
+      fn_decl = lto_file_decl_data_get_fn_decl (file_data, decl_index);
+      /* Cannot use cgraph_master_clone in here. Fais assert that
+	 cgraph_function_flags_ready is true. */
+      master_clone = cgraph_node(fn_decl)->master_clone;
       gcc_assert (master_clone);
       node = cgraph_clone_input_node (master_clone);
     }
@@ -723,7 +696,7 @@ input_cgraph_1 (struct lto_file_decl_data* file_data,
         input_edge (ib, nodes);
       else 
 	{
-	  node = input_node (file_data, ib, tag, nodes);
+	  node = input_node (file_data, ib, tag);
 	  gcc_assert (node);
 	  gcc_assert (node->decl);
 	  VEC_safe_push (cgraph_node_ptr, heap, nodes, node);
diff --git a/gcc/lto-function-out.c b/gcc/lto-function-out.c
index d983405..09030c4 100644
--- a/gcc/lto-function-out.c
+++ b/gcc/lto-function-out.c
@@ -2272,16 +2272,16 @@ lto_output (cgraph_node_set set)
   struct cgraph_node *node;
   struct lto_out_decl_state *decl_state;
   cgraph_node_set_iterator csi;
+  bitmap output = lto_bitmap_alloc ();
 
   lto_static_init_local ();
 
-  /* Process only the functions with bodies and only process the master
-     ones of them.  */
   for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
     {
       node = csi_node (csi);
-      if (node->analyzed && cgraph_is_master_clone (node, false))
+      if (node->analyzed && !bitmap_bit_p (output, DECL_UID (node->decl)))
 	{
+	  bitmap_set_bit (output, DECL_UID (node->decl));
 	  decl_state = lto_new_out_decl_state ();
 	  lto_push_out_decl_state (decl_state);
 	  if (!flag_wpa)
@@ -2293,6 +2293,7 @@ lto_output (cgraph_node_set set)
 	  lto_record_function_out_decl_state (node->decl, decl_state);
 	}
     }
+  lto_bitmap_free (output);
 }
 
 struct ipa_opt_pass pass_ipa_lto_gimple_out =
diff --git a/gcc/lto/lto.c b/gcc/lto/lto.c
index 90e1f02..ec142af 100644
--- a/gcc/lto/lto.c
+++ b/gcc/lto/lto.c
@@ -557,32 +557,23 @@ lto_1_to_1_map (void)
 
 static void
 lto_add_inline_clones (cgraph_node_set set, struct cgraph_node *node,
-		       bitmap original_nodes, bitmap inlined_decls)
+		       bitmap original_decls, bitmap inlined_decls)
 {
-   struct cgraph_node *master_clone, *callee;
+   struct cgraph_node *callee;
    struct cgraph_edge *edge;
 
-   /* NODE must be an inlined clone.  Add both its master clone and node
-      itself to SET and mark the decls as inlined.  */
-   if (!bitmap_bit_p (original_nodes, node->uid))
-     {
-	master_clone = cgraph_master_clone (node, false);
-	gcc_assert (master_clone != NULL && master_clone != node);
-	if (!cgraph_node_in_set_p (master_clone, set))
-	  {
-	    cgraph_node_set_add (set, master_clone);
-	    bitmap_set_bit (inlined_decls, DECL_UID (node->decl));
-	  }
-	cgraph_node_set_add (set, node);
-     }
-   
+   cgraph_node_set_add (set, node);
+
+   if (!bitmap_bit_p (original_decls, DECL_UID (node->decl)))
+     bitmap_set_bit (inlined_decls, DECL_UID (node->decl));
+
    /* Check to see if NODE has any inlined callee.  */
    for (edge = node->callees; edge != NULL; edge = edge->next_callee)
      {
 	callee = edge->callee;
 	if (callee->global.inlined_to != NULL)
-	    lto_add_inline_clones (set, callee, original_nodes,
-				   inlined_decls);
+	  lto_add_inline_clones (set, callee, original_decls,
+				 inlined_decls);
      }
 }
 
@@ -596,21 +587,59 @@ lto_add_all_inlinees (cgraph_node_set set)
   cgraph_node_set_iterator csi;
   struct cgraph_node *node;
   bitmap original_nodes = lto_bitmap_alloc ();
+  bitmap original_decls = lto_bitmap_alloc ();
   bitmap inlined_decls = lto_bitmap_alloc();
+  bool changed;
 
   /* We are going to iterate SET will adding to it, mark all original
      nodes so that we only add node inlined to original nodes.  */
   for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
-    bitmap_set_bit (original_nodes, csi_node (csi)->uid);
+    {
+      bitmap_set_bit (original_nodes, csi_node (csi)->uid);
+      bitmap_set_bit (original_decls, DECL_UID (csi_node (csi)->decl));
+    }
+
+  /* Some of the original nodes might not be needed anymore.  Remove them. */
+  do
+    {
+      changed = false;
+      for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
+	{
+	  struct cgraph_node *inlined_to;
+	  struct cgraph_node *caller;
+	  node = csi_node (csi);
+
+	  /* node was not inlined. We still need it. */
+	  if (!node->global.inlined_to)
+	    continue;
+
+	  inlined_to = node->global.inlined_to;
+	  caller = node->callers->caller;
+
+	  /* only one caller */
+	  gcc_assert (!node->callers->next_caller);
+
+	  if (!bitmap_bit_p (original_nodes, inlined_to->uid)
+	      || !bitmap_bit_p (original_nodes, caller->uid))
+	    {
+	      gcc_assert (!bitmap_bit_p (original_nodes, inlined_to->uid));
+	      gcc_assert (!bitmap_bit_p (original_nodes, caller->uid));
+	      bitmap_clear_bit (original_nodes, node->uid);
+	      cgraph_node_set_remove (set, node);
+	      changed = true;
+	    }
+	}
+    } while (changed);
 
   for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
     {
       node = csi_node (csi);
       if (bitmap_bit_p (original_nodes, node->uid))
-	lto_add_inline_clones (set, node, original_nodes, inlined_decls);
+	lto_add_inline_clones (set, node, original_decls, inlined_decls);
     }
 
   lto_bitmap_free (original_nodes);
+  lto_bitmap_free (original_decls);
   return inlined_decls;
 }
 
@@ -740,8 +769,7 @@ lto_scan_statics_in_cgraph_node (struct cgraph_node *node,
   struct lto_in_decl_state *state;
   
   /* Return if NODE has no function body or is not the master clone. */
-  if (!node->analyzed
-      || (node->master_clone != NULL && node->master_clone != node))
+  if (!node->analyzed)
     return;
   
   /* Return if the DECL of nodes has been visited before.  */
@@ -796,7 +824,6 @@ lto_promote_cross_file_statics (void)
   memset (&context, 0, sizeof (context));
   context.all_vars = lto_bitmap_alloc ();
   context.all_static_vars = lto_bitmap_alloc ();
-  context.seen_node_decls = lto_bitmap_alloc ();
 
   n_sets = VEC_length (cgraph_node_set, lto_cgraph_node_sets);
   for (i = 0; i < n_sets; i++)
@@ -805,6 +832,8 @@ lto_promote_cross_file_statics (void)
       context.set = set;
       context.visited = pointer_set_create ();
       context.static_vars_in_set = lto_bitmap_alloc ();
+      context.seen_node_decls = lto_bitmap_alloc ();
+
       for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
 	lto_scan_statics_in_cgraph_node (csi_node (csi), &context);
 
@@ -812,13 +841,14 @@ lto_promote_cross_file_statics (void)
         lto_scan_statics_in_remaining_global_vars (&context);
 
       bitmap_ior_into (context.all_static_vars, context.static_vars_in_set);
+
       pointer_set_destroy (context.visited);
       lto_bitmap_free (context.static_vars_in_set);
+      lto_bitmap_free (context.seen_node_decls);
     }
 
   lto_bitmap_free (context.all_vars);
   lto_bitmap_free (context.all_static_vars);
-  lto_bitmap_free (context.seen_node_decls);
 }
 
 static lto_file *current_lto_file;
diff --git a/gcc/testsuite/gcc.dg/lto/20090218-1_0.c b/gcc/testsuite/gcc.dg/lto/20090218-1_0.c
new file mode 100644
index 0000000..1dc9ee0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/lto/20090218-1_0.c
@@ -0,0 +1,4 @@
+void set_mem_alias_set ()  __attribute__ ((always_inline));
+void emit_push_insn () {
+  set_mem_alias_set ();
+}
diff --git a/gcc/testsuite/gcc.dg/lto/20090218-1_1.c b/gcc/testsuite/gcc.dg/lto/20090218-1_1.c
new file mode 100644
index 0000000..33d4fb0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/lto/20090218-1_1.c
@@ -0,0 +1,9 @@
+int main(void)
+{
+  return 0;
+}
+static void  __attribute__ ((noinline)) get_mem_attrs () {
+}
+void  __attribute__ ((always_inline)) set_mem_alias_set () {
+  get_mem_attrs ();
+}
diff --git a/gcc/testsuite/gcc.dg/lto/20090218-2_0.c b/gcc/testsuite/gcc.dg/lto/20090218-2_0.c
new file mode 100644
index 0000000..8857e7a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/lto/20090218-2_0.c
@@ -0,0 +1,3 @@
+void emit_push_insn () {
+  set_mem_alias_set ();
+}
diff --git a/gcc/testsuite/gcc.dg/lto/20090218-2_1.c b/gcc/testsuite/gcc.dg/lto/20090218-2_1.c
new file mode 100644
index 0000000..119fbe4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/lto/20090218-2_1.c
@@ -0,0 +1,19 @@
+typedef struct {
+} mem_attrs;
+int main(void)
+{
+  return 0;
+}
+void *malloc(unsigned long size);
+void *memcpy(void *dest, const void *src, unsigned long n);
+static mem_attrs * get_mem_attrs () {
+  void **slot;
+  *slot = malloc (3);
+  memcpy (*slot, 0, 3);
+}
+void set_mem_attributes () {
+  get_mem_attrs ();
+}
+void set_mem_alias_set () {
+  get_mem_attrs ();
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]