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]

coalesce SSA versions in var-tracking notes


i was asked to look into a Systemtap debug info issue reported in the
thread containing the message archived here:
<URL:http://sourceware.org/ml/systemtap/2006-q3/msg00096.html>

As it turns out, the problem is that the location list for one of the
variables in the schedule() function in the kernel is, shall I say,
corrupted.

The out-of-SSA machinery generates 3 separate copies of variable
`prev', one for each of the separate defs of this variable, that have
disjoint sets of uses, so they need not be coalesced.

var-tracking treats the 3 copies as completely unrelated variables.
Since they end up being assigned to separate registers, var-tracking's
dataflow analysis finds that different registers might hold live
values for such variables at certain points, and emits notes even
though at least one of the registers may possibly contain many other
variables.

Basic block 6:
IN:
[...]
Reg 24: prev+0
[...]
Reg 31: p+0 sd+0 sd.4011+0 n+0 prev.4004+0
[...]
(note 2218 2217 2219 6 ( Y (expr_list:REG_DEP_TRUE (reg/v/f:DI 24 24 [orig:354 Y ] [354])
    (const_int 0 [0x0]))) NOTE_INSN_VAR_LOCATION)
[...]
(note 2238 2237 2239 6 ( Y.4004 (expr_list:REG_DEP_TRUE (reg/v/f:DI 31 31 [orig:125 Y.4004 ] [125])
    (const_int 0 [0x0]))) NOTE_INSN_VAR_LOCATION)

Later on, dwarf2out follows DECL_DEBUG_EXPR (decl) and thus unifies
such conflicting var location notes into something as ugly as:

        .8byte  .LVL1356         # Location list begin address (*.LLST625)
        .8byte  .LVL1356         # Location list end address (*.LLST625)
        .2byte  0x1      # Location expression size
        .byte   0x68     # DW_OP_reg24
        .8byte  .LVL1356         # Location list begin address (*.LLST625)
        .8byte  .LVL1365         # Location list end address (*.LLST625)
        .2byte  0x1      # Location expression size
        .byte   0x6f     # DW_OP_reg31


It is a bug that we actually allow such potential live values to even
be considered.  It appears to me that, as soon as the user sees a set
to a variable at a certain location, that copy of the variable should
be the only active one at that point, and any other copies should be
rendered obsolete.  This will deal with almost all cases of split live
ranges, and that magically removes all of the potential conflicts that
I was worried about in yesterday's e-mail.

There's still one possibility of conflict, though: if out-of-ssa finds
that two SSA versions of a variable are live throughout a basic block,
then we won't know which location to choose for debug info, but in
this case we're already screwed anyway, and there's nothing we can
really do.


So I went ahead and arranged for var-tracking to keep track of debug
variables, as opposed to possibly-compiled-introduced copies/versions
of variable; arranged for it to kill any other live copies of the same
part of a debug variable when a set is encountered, and the problems
were all gone.

Well, except that debug info size grew, since because of the
additional killing, copies between registers holding the same variable
(e.g. (set (reg x [ var ]) (reg y [ var ]))), that would formerly be
regarded as an additional copy of the variable, now killed the
available value in reg y.  So I introduced a distinct micro-operation
type MO_COPY, to denote copies between the same parts of the same
variable, such that we can know to not kill other available copies:
the set is value-preserving in this case.

This in turn exposed a latent bug in that var_reg_set wouldn't add a
location to set->regs if the corresponding reg already had a location
in it.  When the value of multiple variables is live in the same
register, we really have to do that, otherwise dataflow analysis may
find changes forever, alternating between two or more different
variables in the same register.  I fixed that too.

In order to debug this latter problem, I added some new debug code,
which I have now removed, but I post it here as a separate patch (the
second) for completeness.  Is there any interest in integrating that?


The first patch that follows is what I've bootstrap-tested (trunk) on
amd64-linux-gnu.  Ok to install?

for gcc/ChangeLog
from  Alexandre Oliva  <aoliva@redhat.com>

	* var-tracking.c (enum micro_operation_type): Add MO_COPY.
	(var_debug_decl): New function.
	(var_reg_set): Follow debug decl link.  Add location even if
	reg is already known to hold some other variable.
	(var_mem_set): Follow debug decl link.
	(var_reg_delete_and_set, var_mem_delete_and_set): Follow debug
	decl link.  Delete other known locations of the variable part
	if requested.
	(var_reg_delete, var_mem_delete): Delete other known locations
	of the variable part if requested.
	(same_variable_part_p): New function.
	(add_stores): Select MO_COPY when appropriate.
	(vt_initialize): Handle it.
	(compute_bb_dataflow, emit_notes_in_bb): Likewise.  Delete
	known locations for MO_SET and MO_CLOBBER.
	(clobber_variable_part): New function.
	* dwarf2out.c (dwarf2out_var_location): Do not follow debug
	decl link.

Index: trunk/gcc/var-tracking.c
===================================================================
--- trunk.orig/gcc/var-tracking.c	2006-07-21 03:45:54.000000000 -0300
+++ trunk/gcc/var-tracking.c	2006-07-21 03:45:59.000000000 -0300
@@ -114,6 +114,8 @@ enum micro_operation_type
   MO_USE_NO_VAR,/* Use location which is not associated with a variable
 		   or the variable is not trackable.  */
   MO_SET,	/* Set location.  */
+  MO_COPY,	/* Copy the same portion of a variable from one
+		   loation to another.  */
   MO_CLOBBER,	/* Clobber location.  */
   MO_CALL,	/* Call insn.  */
   MO_ADJUST	/* Adjust stack pointer.  */
@@ -295,13 +297,14 @@ static void vars_clear (htab_t);
 static variable unshare_variable (dataflow_set *set, variable var);
 static int vars_copy_1 (void **, void *);
 static void vars_copy (htab_t, htab_t);
+static tree var_debug_decl (tree);
 static void var_reg_set (dataflow_set *, rtx);
-static void var_reg_delete_and_set (dataflow_set *, rtx);
-static void var_reg_delete (dataflow_set *, rtx);
+static void var_reg_delete_and_set (dataflow_set *, rtx, bool);
+static void var_reg_delete (dataflow_set *, rtx, bool);
 static void var_regno_delete (dataflow_set *, int);
 static void var_mem_set (dataflow_set *, rtx);
-static void var_mem_delete_and_set (dataflow_set *, rtx);
-static void var_mem_delete (dataflow_set *, rtx);
+static void var_mem_delete_and_set (dataflow_set *, rtx, bool);
+static void var_mem_delete (dataflow_set *, rtx, bool);
 
 static void dataflow_set_init (dataflow_set *, int);
 static void dataflow_set_clear (dataflow_set *);
@@ -318,6 +321,7 @@ static void dataflow_set_destroy (datafl
 
 static bool contains_symbol_ref (rtx);
 static bool track_expr_p (tree);
+static bool same_variable_part_p (rtx, tree, HOST_WIDE_INT);
 static int count_uses (rtx *, void *);
 static void count_uses_1 (rtx *, void *);
 static void count_stores (rtx, rtx, void *);
@@ -335,6 +339,7 @@ static void dump_dataflow_sets (void);
 
 static void variable_was_changed (variable, htab_t);
 static void set_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
+static void clobber_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
 static void delete_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
 static int emit_note_insn_var_location (void **, void *);
 static void emit_notes_for_changes (rtx, enum emit_note_where);
@@ -798,6 +803,19 @@ vars_copy (htab_t dst, htab_t src)
   htab_traverse (src, vars_copy_1, dst);
 }
 
+/* Map a decl to its main debug decl.  */
+
+static inline tree
+var_debug_decl (tree decl)
+{
+  if (decl && DECL_P (decl)
+      && DECL_DEBUG_EXPR_IS_FROM (decl) && DECL_DEBUG_EXPR (decl)
+      && DECL_P (DECL_DEBUG_EXPR (decl)))
+    decl = DECL_DEBUG_EXPR (decl);
+
+  return decl;
+}
+
 /* Set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  */
 
 static void
@@ -805,23 +823,35 @@ var_reg_set (dataflow_set *set, rtx loc)
 {
   tree decl = REG_EXPR (loc);
   HOST_WIDE_INT offset = REG_OFFSET (loc);
+  attrs node;
+
+  decl = var_debug_decl (decl);
 
-  if (set->regs[REGNO (loc)] == NULL)
+  for (node = set->regs[REGNO (loc)]; node; node = node->next)
+    if (node->decl == decl && node->offset == offset)
+      break;
+  if (!node)
     attrs_list_insert (&set->regs[REGNO (loc)], decl, offset, loc);
   set_variable_part (set, loc, decl, offset);
 }
 
-/* Delete current content of register LOC in dataflow set SET
-   and set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  */
+/* Delete current content of register LOC in dataflow set SET and set
+   the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  If
+   MODIFY is true, any other live copies of the same variable part are
+   also deleted from the dataflow set, otherwise the variable part is
+   assumed to be copied from another location holding the same
+   part.  */
 
 static void
-var_reg_delete_and_set (dataflow_set *set, rtx loc)
+var_reg_delete_and_set (dataflow_set *set, rtx loc, bool modify)
 {
   tree decl = REG_EXPR (loc);
   HOST_WIDE_INT offset = REG_OFFSET (loc);
   attrs node, next;
   attrs *nextp;
 
+  decl = var_debug_decl (decl);
+
   nextp = &set->regs[REGNO (loc)];
   for (node = *nextp; node; node = next)
     {
@@ -838,17 +868,31 @@ var_reg_delete_and_set (dataflow_set *se
 	  nextp = &node->next;
 	}
     }
+  if (modify)
+    clobber_variable_part (set, loc, decl, offset);
   var_reg_set (set, loc);
 }
 
-/* Delete current content of register LOC in dataflow set SET.  */
+/* Delete current content of register LOC in dataflow set SET.  If
+   CLOBBER is true, also delete any other live copies of the same
+   variable part.  */
 
 static void
-var_reg_delete (dataflow_set *set, rtx loc)
+var_reg_delete (dataflow_set *set, rtx loc, bool clobber)
 {
   attrs *reg = &set->regs[REGNO (loc)];
   attrs node, next;
 
+  if (clobber)
+    {
+      tree decl = REG_EXPR (loc);
+      HOST_WIDE_INT offset = REG_OFFSET (loc);
+
+      decl = var_debug_decl (decl);
+
+      clobber_variable_part (set, NULL, decl, offset);
+    }
+
   for (node = *reg; node; node = next)
     {
       next = node->next;
@@ -885,28 +929,44 @@ var_mem_set (dataflow_set *set, rtx loc)
   tree decl = MEM_EXPR (loc);
   HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
 
+  decl = var_debug_decl (decl);
+
   set_variable_part (set, loc, decl, offset);
 }
 
-/* Delete and set the location part of variable MEM_EXPR (LOC)
-   in dataflow set SET to LOC.
+/* Delete and set the location part of variable MEM_EXPR (LOC) in
+   dataflow set SET to LOC.  If MODIFY is true, any other live copies
+   of the same variable part are also deleted from the dataflow set,
+   otherwise the variable part is assumed to be copied from another
+   location holding the same part.
    Adjust the address first if it is stack pointer based.  */
 
 static void
-var_mem_delete_and_set (dataflow_set *set, rtx loc)
+var_mem_delete_and_set (dataflow_set *set, rtx loc, bool modify)
 {
+  tree decl = MEM_EXPR (loc);
+  HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
+
+  decl = var_debug_decl (decl);
+
+  if (modify)
+    clobber_variable_part (set, NULL, decl, offset);
   var_mem_set (set, loc);
 }
 
-/* Delete the location part LOC from dataflow set SET.
+/* Delete the location part LOC from dataflow set SET.  If CLOBBER is
+   true, also delete any other live copies of the same variable part.
    Adjust the address first if it is stack pointer based.  */
 
 static void
-var_mem_delete (dataflow_set *set, rtx loc)
+var_mem_delete (dataflow_set *set, rtx loc, bool clobber)
 {
   tree decl = MEM_EXPR (loc);
   HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
 
+  decl = var_debug_decl (decl);
+  if (clobber)
+    clobber_variable_part (set, NULL, decl, offset);
   delete_variable_part (set, loc, decl, offset);
 }
 
@@ -1560,6 +1620,41 @@ track_expr_p (tree expr)
   return 1;
 }
 
+/* Determine whether a given LOC refers to the same variable part as
+   EXPR+OFFSET.  */
+
+static bool
+same_variable_part_p (rtx loc, tree expr, HOST_WIDE_INT offset)
+{
+  tree expr2;
+  HOST_WIDE_INT offset2;
+
+  if (! DECL_P (expr))
+    return false;
+
+  if (REG_P (loc))
+    {
+      expr2 = REG_EXPR (loc);
+      offset2 = REG_OFFSET (loc);
+    }
+  else if (MEM_P (loc))
+    {
+      expr2 = MEM_EXPR (loc);
+      offset2 = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
+    }
+  else
+    return false;
+
+  if (! expr2 || ! DECL_P (expr2))
+    return false;
+
+  expr = var_debug_decl (expr);
+  expr2 = var_debug_decl (expr2);
+
+  return (expr == expr2 && offset == offset2);
+}
+
+
 /* Count uses (register and memory references) LOC which will be tracked.
    INSN is instruction which the LOC is part of.  */
 
@@ -1651,9 +1746,18 @@ add_stores (rtx loc, rtx expr, void *ins
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
-      mo->type = ((GET_CODE (expr) != CLOBBER && REG_EXPR (loc)
-		   && track_expr_p (REG_EXPR (loc)))
-		  ? MO_SET : MO_CLOBBER);
+      if (GET_CODE (expr) == CLOBBER
+	  || ! REG_EXPR (loc)
+	  || ! track_expr_p (REG_EXPR (loc)))
+	mo->type = MO_CLOBBER;
+      else if (GET_CODE (expr) == SET
+	       && SET_DEST (expr) == loc
+	       && same_variable_part_p (SET_SRC (expr),
+					REG_EXPR (loc),
+					REG_OFFSET (loc)))
+	mo->type = MO_COPY;
+      else
+	mo->type = MO_SET;
       mo->u.loc = loc;
       mo->insn = NEXT_INSN ((rtx) insn);
     }
@@ -1664,7 +1768,17 @@ add_stores (rtx loc, rtx expr, void *ins
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
-      mo->type = GET_CODE (expr) == CLOBBER ? MO_CLOBBER : MO_SET;
+      if (GET_CODE (expr) == CLOBBER)
+	mo->type = MO_CLOBBER;
+      else if (GET_CODE (expr) == SET
+	       && SET_DEST (expr) == loc
+	       && same_variable_part_p (SET_SRC (expr),
+					MEM_EXPR (loc),
+					MEM_OFFSET (loc)
+					? INTVAL (MEM_OFFSET (loc)) : 0))
+	mo->type = MO_COPY;
+      else
+	mo->type = MO_SET;
       mo->u.loc = loc;
       mo->insn = NEXT_INSN ((rtx) insn);
     }
@@ -1712,21 +1826,42 @@ compute_bb_dataflow (basic_block bb)
 	      rtx loc = VTI (bb)->mos[i].u.loc;
 
 	      if (REG_P (loc))
-		var_reg_delete_and_set (out, loc);
+		var_reg_delete_and_set (out, loc, true);
 	      else if (MEM_P (loc))
-		var_mem_delete_and_set (out, loc);
+		var_mem_delete_and_set (out, loc, true);
+	    }
+	    break;
+
+	  case MO_COPY:
+	    {
+	      rtx loc = VTI (bb)->mos[i].u.loc;
+
+	      if (REG_P (loc))
+		var_reg_delete_and_set (out, loc, false);
+	      else if (MEM_P (loc))
+		var_mem_delete_and_set (out, loc, false);
 	    }
 	    break;
 
 	  case MO_USE_NO_VAR:
+	    {
+	      rtx loc = VTI (bb)->mos[i].u.loc;
+
+	      if (REG_P (loc))
+		var_reg_delete (out, loc, false);
+	      else if (MEM_P (loc))
+		var_mem_delete (out, loc, false);
+	    }
+	    break;
+
 	  case MO_CLOBBER:
 	    {
 	      rtx loc = VTI (bb)->mos[i].u.loc;
 
 	      if (REG_P (loc))
-		var_reg_delete (out, loc);
+		var_reg_delete (out, loc, true);
 	      else if (MEM_P (loc))
-		var_mem_delete (out, loc);
+		var_mem_delete (out, loc, true);
 	    }
 	    break;
 
@@ -2108,6 +2243,56 @@ set_variable_part (dataflow_set *set, rt
     }
 }
 
+/* Remove all recorded register locations for the given variable part
+   from dataflow set SET, except for those that are identical to loc.
+   The variable part is specified by variable's declaration DECL and
+   offset OFFSET.  */
+
+static void
+clobber_variable_part (dataflow_set *set, rtx loc, tree decl,
+		      HOST_WIDE_INT offset)
+{
+  int pos, low, high;
+  void **slot;
+
+  if (! decl || ! DECL_P (decl))
+    return;
+
+  slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl),
+				   NO_INSERT);
+  if (slot)
+    {
+      variable var = (variable) *slot;
+
+      /* Find the location part.  */
+      low = 0;
+      high = var->n_var_parts;
+      while (low != high)
+	{
+	  pos = (low + high) / 2;
+	  if (var->var_part[pos].offset < offset)
+	    low = pos + 1;
+	  else
+	    high = pos;
+	}
+      pos = low;
+
+      if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
+	{
+	  location_chain node, next;
+
+	  /* Remove the register locations from the dataflow set.  */
+	  next = var->var_part[pos].loc_chain;
+	  for (node = next; node; node = next)
+	    {
+	      next = node->next;
+	      if (REG_P (node->loc) && node->loc != loc)
+		var_reg_delete (set, node->loc, false);
+	    }
+	}
+    }
+}
+
 /* Delete the part of variable's location from dataflow set SET.  The variable
    part is specified by variable's declaration DECL and offset OFFSET and the
    part's location by LOC.  */
@@ -2204,7 +2389,7 @@ delete_variable_part (dataflow_set *set,
 		}
 	    }
 	  if (changed)
-	      variable_was_changed (var, set->vars);
+	    variable_was_changed (var, set->vars);
 	}
     }
 }
@@ -2495,28 +2680,50 @@ emit_notes_in_bb (basic_block bb)
 	      rtx loc = VTI (bb)->mos[i].u.loc;
 
 	      if (REG_P (loc))
-		var_reg_delete_and_set (&set, loc);
+		var_reg_delete_and_set (&set, loc, true);
 	      else
-		var_mem_delete_and_set (&set, loc);
+		var_mem_delete_and_set (&set, loc, true);
+
+	      emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
+	    }
+	    break;
+
+	  case MO_COPY:
+	    {
+	      rtx loc = VTI (bb)->mos[i].u.loc;
+
+	      if (REG_P (loc))
+		var_reg_delete_and_set (&set, loc, false);
+	      else
+		var_mem_delete_and_set (&set, loc, false);
 
 	      emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
 	    }
 	    break;
 
 	  case MO_USE_NO_VAR:
-	  case MO_CLOBBER:
 	    {
 	      rtx loc = VTI (bb)->mos[i].u.loc;
 
 	      if (REG_P (loc))
-		var_reg_delete (&set, loc);
+		var_reg_delete (&set, loc, false);
 	      else
-		var_mem_delete (&set, loc);
+		var_mem_delete (&set, loc, false);
+
+	      emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
+	    }
+	    break;
 
-	      if (VTI (bb)->mos[i].type == MO_USE_NO_VAR)
-		emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
+	  case MO_CLOBBER:
+	    {
+	      rtx loc = VTI (bb)->mos[i].u.loc;
+
+	      if (REG_P (loc))
+		var_reg_delete (&set, loc, true);
 	      else
-		emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
+		var_mem_delete (&set, loc, true);
+
+	      emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
 	    }
 	    break;
 
@@ -2740,7 +2947,8 @@ vt_initialize (void)
 		{
 		  while (n1 < n2 && VTI (bb)->mos[n1].type == MO_CLOBBER)
 		    n1++;
-		  while (n1 < n2 && VTI (bb)->mos[n2].type == MO_SET)
+		  while (n1 < n2 && (VTI (bb)->mos[n2].type == MO_SET
+				     || VTI (bb)->mos[n2].type == MO_COPY))
 		    n2--;
 		  if (n1 < n2)
 		    {
Index: trunk/gcc/dwarf2out.c
===================================================================
--- trunk.orig/gcc/dwarf2out.c	2006-05-23 02:30:17.000000000 -0300
+++ trunk/gcc/dwarf2out.c	2006-07-21 05:14:15.000000000 -0300
@@ -13547,9 +13547,6 @@ dwarf2out_var_location (rtx loc_note)
   last_insn = loc_note;
   last_label = newloc->label;
   decl = NOTE_VAR_LOCATION_DECL (loc_note);
-  if (DECL_DEBUG_EXPR_IS_FROM (decl) && DECL_DEBUG_EXPR (decl) 
-      && DECL_P (DECL_DEBUG_EXPR (decl)))
-    decl = DECL_DEBUG_EXPR (decl); 
   add_var_loc_to_decl (decl, newloc);
 }
 
And here's the patch for dump_dataflow_differences.  It still needs
some polishing, such as removing the needless out variable, for one;
adding a separate command line option to control its use, and adding a
ChangeLog entry.  I thought I'd ask whether there's interest before
doing it all.

Index: trunk/gcc/var-tracking.c
===================================================================
--- trunk.orig/gcc/var-tracking.c	2006-07-21 03:42:09.000000000 -0300
+++ trunk/gcc/var-tracking.c	2006-07-21 03:45:54.000000000 -0300
@@ -1358,6 +1358,87 @@ dataflow_set_different (dataflow_set *ol
   return dataflow_set_different_value;
 }
 
+/* A trivial wrapper around dump_variable.  */
+
+static void
+dump_variable_simple (variable var)
+{
+  void *v = var;
+
+  dump_variable (&v, 0);
+}
+
+/* Compare variable *SLOT with the same variable in hash table DATA
+   and dump the changes if they are different.  */
+
+static int
+dataflow_set_differences_1 (void **slot, void *data)
+{
+  htab_t htab = (htab_t) data;
+  variable var1, var2;
+
+  var1 = *(variable *) slot;
+  var2 = htab_find_with_hash (htab, var1->decl,
+			      VARIABLE_HASH_VAL (var1->decl));
+  if (!var2)
+    {
+      fprintf (dump_file, "-");
+      dump_variable_simple (var1);
+
+      /* Stop traversing the hash table.  */
+      return 1;
+    }
+
+  if (variable_different_p (var1, var2, false))
+    {
+      fprintf (dump_file, "-");
+      dump_variable_simple (var1);
+      fprintf (dump_file, "+");
+      dump_variable_simple (var2);
+      /* Stop traversing the hash table.  */
+      return 1;
+    }
+
+  /* Continue traversing the hash table.  */
+  return 1;
+}
+
+/* Compare variable *SLOT with the same variable in hash table DATA
+   and set DATAFLOW_SET_DIFFERENT_VALUE if they are different.  */
+
+static int
+dataflow_set_differences_2 (void **slot, void *data)
+{
+  htab_t htab = (htab_t) data;
+  variable var1, var2;
+
+  var1 = *(variable *) slot;
+  var2 = htab_find_with_hash (htab, var1->decl,
+			      VARIABLE_HASH_VAL (var1->decl));
+  if (!var2)
+    {
+      fprintf (dump_file, "+");
+      dump_variable_simple (var1);
+
+      /* Stop traversing the hash table.  */
+      return 1;
+    }
+
+  /* Continue traversing the hash table.  */
+  return 1;
+}
+
+/* Dump all differences between two dataflow sets.  */
+
+static void
+dump_dataflow_differences (int bbidx, dataflow_set *old_set,
+			   dataflow_set *new_set)
+{
+  fprintf (dump_file, "\nBasic block %d OUT changes:\n", bbidx);
+  htab_traverse (old_set->vars, dataflow_set_differences_1, new_set->vars);
+  htab_traverse (new_set->vars, dataflow_set_differences_2, old_set->vars);
+}
+
 /* Free the contents of dataflow set SET.  */
 
 static void
@@ -1656,10 +1737,14 @@ compute_bb_dataflow (basic_block bb)
     }
 
   changed = dataflow_set_different (&old_out, out);
+  if (changed && dump_file)
+    dump_dataflow_differences (bb->index, &old_out, out);
   dataflow_set_destroy (&old_out);
   return changed;
 }
 
+static FILE **out __attribute__ ((__used__)) = &stderr;
+
 /* Find the locations of variables in the whole function.  */
 
 static void
-- 
Alexandre Oliva         http://www.lsd.ic.unicamp.br/~oliva/
Secretary for FSF Latin America        http://www.fsfla.org/
Red Hat Compiler Engineer   aoliva@{redhat.com, gcc.gnu.org}
Free Software Evangelist  oliva@{lsd.ic.unicamp.br, gnu.org}

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