public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* fix left-over debug insns in DCE
@ 2011-05-30 12:15 Alexandre Oliva
  2011-06-02 15:26 ` Eric Botcazou
  0 siblings, 1 reply; 23+ messages in thread
From: Alexandre Oliva @ 2011-05-30 12:15 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1118 bytes --]

The insn shuffling in one of my patches for PR48866 shook out some
latent problems in DCE.

One of the issues was that DCE removed an insn that set a REG in a
certain mode, without adjusting a debug use of that REG.  This was in
libstdc++, but I failed to take note of the affected file.  DF later
attached that debug use to another SET to the same REG in a different,
incompatible mode.  When that one was found to be dead by DF, we ended
up ICEing as we attempted to emit the invalid SUBREGs.

I reused some of the infrastructure to propagate dead DEFs into debug
uses in DF to get DCE to emit debug temps and adjust debug uses as well,
fixing this issue.  While at that, I improved the handling of unused
DEFs in DF, that previously resulted in loss of debug information, so as
to retain it as much as possible.

The changes above ended up exposing another latent problem in regstack,
by which a debug insn referenced a stack register that wasn't available.
I adjusted the code to tolerate this possibility.

This is the patch I ended up with.  Regstrapped on x86_64-linux-gnu and
i686-linux-gnu.  Ok to install?



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: vta-df-preserve-unused-dead-in-debug-temp-pr48866.patch --]
[-- Type: text/x-diff, Size: 21336 bytes --]

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

	* df.h (enum debug_temp_where): New.
	(dead_debug_init, dead_debug_finish) Declare.
	(dead_debug_add, dead_debug_insert_temp): Declare.
	(struct dead_debug_use, struct dead_debug): Moved from...
	* df-problems.c: ... here.
	(df_set_unused_notes_for_mw): Bind debug uses of unused regno
	to a debug temp.
	(df_create_unused_note): Likewise.
	(df_set_dead_notes_for_mw): Move comment where it belongs.
	(dead_debug_init): Export.
	(dead_debug_reset_uses): New, factored out of...
	(dead_debug_finish): ...this.  Export.
	(dead_debug_reset): Remove.
	(dead_debug_add): Export.
	(dead_debug_insert_before): Rename to...
	(dead_debug_insert_temp): ... this.  Add where argument.  Export.
	Locate stored value for BEFORE_WITH_VALUE.  Avoid repeat inserts.
	Return insertion count.
	(df_note_bb_compute): Adjust.
	* dce.c (reset_unmarked_insns_debug_uses): New.
	(delete_unmarked_insns): Skip debug insns.
	(prescan_insns_for_dce): Likewise.
	(rest_of_handle_ud_dce): Propagate debug uses.
	(word_dce_process_block): Adjust dead debug uses.
	(dce_process_block): Likewise.
	* reg-stack.c (subst_stack_regs_in_debug_insn): Signal when no
	active reg can be found.
	(subst_all_stack_regs_in_debug_insn): New.  Reset debug insn then.
	(convert_regs_1): Use it.

Index: gcc/df-problems.c
===================================================================
--- gcc/df-problems.c.orig	2011-05-26 05:03:08.545989094 -0300
+++ gcc/df-problems.c	2011-05-26 05:03:28.163028026 -0300
@@ -2842,25 +2842,6 @@ df_whole_mw_reg_unused_p (struct df_mw_h
 }
 
 
-/* Node of a linked list of uses of dead REGs in debug insns.  */
-struct dead_debug_use
-{
-  df_ref use;
-  struct dead_debug_use *next;
-};
-
-/* Linked list of the above, with a bitmap of the REGs in the
-   list.  */
-struct dead_debug
-{
-  struct dead_debug_use *head;
-  bitmap used;
-  bitmap to_rescan;
-};
-
-static void dead_debug_reset (struct dead_debug *, unsigned int);
-
-
 /* Set the REG_UNUSED notes for the multiword hardreg defs in INSN
    based on the bits in LIVE.  Do not generate notes for registers in
    artificial uses.  DO_NOT_GEN is updated so that REG_DEAD notes are
@@ -2886,7 +2867,7 @@ df_set_unused_notes_for_mw (rtx insn, st
     {
       unsigned int regno = mws->start_regno;
       df_set_note (REG_UNUSED, insn, mws->mw_reg);
-      dead_debug_reset (debug, regno);
+      dead_debug_insert_temp (debug, regno, insn, DEBUG_TEMP_AFTER_WITH_REG);
 
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 1: ", insn, REG_NOTES (insn));
@@ -2901,7 +2882,7 @@ df_set_unused_notes_for_mw (rtx insn, st
 	    && !bitmap_bit_p (artificial_uses, r))
 	  {
 	    df_set_note (REG_UNUSED, insn, regno_reg_rtx[r]);
-	    dead_debug_reset (debug, r);
+	    dead_debug_insert_temp (debug, r, insn, DEBUG_TEMP_AFTER_WITH_REG);
 #ifdef REG_DEAD_DEBUGGING
 	    df_print_note ("adding 2: ", insn, REG_NOTES (insn));
 #endif
@@ -2969,12 +2950,12 @@ df_set_dead_notes_for_mw (rtx insn, stru
 
   if (df_whole_mw_reg_dead_p (mws, live, artificial_uses, do_not_gen))
     {
-      /* Add a dead note for the entire multi word register.  */
       if (is_debug)
 	{
 	  *added_notes_p = true;
 	  return;
 	}
+      /* Add a dead note for the entire multi word register.  */
       df_set_note (REG_DEAD, insn, mws->mw_reg);
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 1: ", insn, REG_NOTES (insn));
@@ -3028,7 +3009,7 @@ df_create_unused_note (rtx insn, df_ref 
       rtx reg = (DF_REF_LOC (def))
                 ? *DF_REF_REAL_LOC (def): DF_REF_REG (def);
       df_set_note (REG_UNUSED, insn, reg);
-      dead_debug_reset (debug, dregno);
+      dead_debug_insert_temp (debug, dregno, insn, DEBUG_TEMP_AFTER_WITH_REG);
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 3: ", insn, REG_NOTES (insn));
 #endif
@@ -3039,7 +3020,7 @@ df_create_unused_note (rtx insn, df_ref 
 
 
 /* Initialize DEBUG to an empty list, and clear USED, if given.  */
-static inline void
+void
 dead_debug_init (struct dead_debug *debug, bitmap used)
 {
   debug->head = NULL;
@@ -3049,32 +3030,83 @@ dead_debug_init (struct dead_debug *debu
     bitmap_clear (used);
 }
 
-/* Reset all debug insns with pending uses.  Release the bitmap in it,
-   unless it is USED.  USED must be the same bitmap passed to
-   dead_debug_init.  */
-static inline void
-dead_debug_finish (struct dead_debug *debug, bitmap used)
+/* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of
+   each reset insn.  DEBUG is not otherwise modified.  If HEAD is
+   DEBUG->head, DEBUG->head will be set to NULL at the end.
+   Otherwise, entries from DEBUG->head that pertain to reset insns
+   will be removed, and only then rescanned.  */
+
+static void
+dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
 {
-  struct dead_debug_use *head;
-  rtx insn = NULL;
+  bool got_head = (debug->head == head);
+  bitmap rescan;
+  struct dead_debug_use **tailp = &debug->head;
+  struct dead_debug_use *cur;
+  bitmap_iterator bi;
+  unsigned int uid;
 
-  if (debug->used != used)
-    BITMAP_FREE (debug->used);
+  if (got_head)
+    rescan = NULL;
+  else
+    rescan = BITMAP_ALLOC (NULL);
 
-  while ((head = debug->head))
+  while (head)
     {
+      struct dead_debug_use *next = head->next;
+      rtx insn;
+
       insn = DF_REF_INSN (head->use);
-      if (!head->next || DF_REF_INSN (head->next->use) != insn)
+      if (!next || DF_REF_INSN (next->use) != insn)
 	{
 	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-	  df_insn_rescan_debug_internal (insn);
+	  if (got_head)
+	    df_insn_rescan_debug_internal (insn);
+	  else
+	    bitmap_set_bit (rescan, INSN_UID (insn));
 	  if (debug->to_rescan)
 	    bitmap_clear_bit (debug->to_rescan, INSN_UID (insn));
 	}
-      debug->head = head->next;
       XDELETE (head);
+      head = next;
     }
 
+  if (got_head)
+    {
+      debug->head = NULL;
+      return;
+    }
+
+  while ((cur = *tailp))
+    if (bitmap_bit_p (rescan, INSN_UID (DF_REF_INSN (cur->use))))
+      {
+	*tailp = cur->next;
+	XDELETE (cur);
+      }
+    else
+      tailp = &cur->next;
+
+  EXECUTE_IF_SET_IN_BITMAP (rescan, 0, uid, bi)
+    {
+      struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
+      if (insn_info)
+	df_insn_rescan_debug_internal (insn_info->insn);
+    }
+
+  BITMAP_FREE (rescan);
+}
+
+/* Reset all debug insns with pending uses.  Release the bitmap in it,
+   unless it is USED.  USED must be the same bitmap passed to
+   dead_debug_init.  */
+void
+dead_debug_finish (struct dead_debug *debug, bitmap used)
+{
+  if (debug->used != used)
+    BITMAP_FREE (debug->used);
+
+  dead_debug_reset_uses (debug, debug->head);
+
   if (debug->to_rescan)
     {
       bitmap_iterator bi;
@@ -3090,37 +3122,9 @@ dead_debug_finish (struct dead_debug *de
     }
 }
 
-/* Reset DEBUG_INSNs with pending uses of DREGNO.  */
-static void
-dead_debug_reset (struct dead_debug *debug, unsigned int dregno)
-{
-  struct dead_debug_use **tailp = &debug->head;
-  struct dead_debug_use *cur;
-  rtx insn;
-
-  if (!debug->used || !bitmap_clear_bit (debug->used, dregno))
-    return;
-
-  while ((cur = *tailp))
-    {
-      if (DF_REF_REGNO (cur->use) == dregno)
-	{
-	  *tailp = cur->next;
-	  insn = DF_REF_INSN (cur->use);
-	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-	  if (debug->to_rescan == NULL)
-	    debug->to_rescan = BITMAP_ALLOC (NULL);
-	  bitmap_set_bit (debug->to_rescan, INSN_UID (insn));
-	  XDELETE (cur);
-	}
-      else
-	tailp = &(*tailp)->next;
-    }
-}
-
 /* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
    insn.  Create a bitmap for DEBUG as needed.  */
-static inline void
+void
 dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
 {
   struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
@@ -3136,12 +3140,15 @@ dead_debug_add (struct dead_debug *debug
 }
 
 /* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
-   before INSN that binds the REG to a debug temp, and replace all
-   uses of UREGNO in DEBUG with uses of the debug temp.  INSN must be
-   the insn where UREGNO dies.  */
-static inline void
-dead_debug_insert_before (struct dead_debug *debug, unsigned int uregno,
-			  rtx insn)
+   before or after INSN (depending on WHERE), that binds a debug temp
+   to the widest-mode use of UREGNO, if WHERE is _WITH_REG, or
+   _WITH_VALUE otherwise, and replace all uses of UREGNO in DEBUG with
+   uses of the debug temp.  INSN must be where UREGNO dies, if WHERE
+   is DEAD_DEBUG_BEFORE_WITH_REG, or where it is set otherwise.
+   Return the number of debug insns emitted.  */
+int
+dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
+			rtx insn, enum debug_temp_where where)
 {
   struct dead_debug_use **tailp = &debug->head;
   struct dead_debug_use *cur;
@@ -3152,7 +3159,7 @@ dead_debug_insert_before (struct dead_de
   rtx bind;
 
   if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
-    return;
+    return 0;
 
   /* Move all uses of uregno from debug->head to uses, setting mode to
      the widest referenced mode.  */
@@ -3173,17 +3180,100 @@ dead_debug_insert_before (struct dead_de
 	tailp = &(*tailp)->next;
     }
 
-  gcc_assert (reg);
+  if (!reg)
+    {
+      gcc_checking_assert (!uses);
+      return 0;
+    }
+
+  gcc_checking_assert (uses);
+
+  bind = reg;
+  if (where == DEBUG_TEMP_BEFORE_WITH_VALUE)
+    {
+      rtx set = single_set (insn);
+      rtx dest, src;
+
+      if (set)
+	{
+	  dest = SET_DEST (set);
+	  src = SET_SRC (set);
+	  if (GET_CODE (src) == CALL)
+	    {
+	      while (uses)
+		{
+		  cur = uses->next;
+		  XDELETE (uses);
+		  uses = cur;
+		}
+	      return 0;
+	    }
+	}
+      else
+	set = NULL;
+
+      if (!set)
+	bind = NULL;
+      else if (dest == reg)
+	bind = copy_rtx (src);
+      else if (REG_P (dest))
+	{
+	  if (REGNO (dest) != REGNO (reg))
+	    bind = NULL;
+	  else
+	    bind = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      else if (GET_CODE (dest) == SUBREG)
+	{
+	  if (REGNO (SUBREG_REG (dest)) != REGNO (reg))
+	    bind = NULL;
+	  else if (!subreg_lowpart_p (dest))
+	    bind = NULL;
+	  else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
+		   && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
+		       == hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
+	    bind = NULL;
+	  else
+	    bind = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      else
+	bind = NULL;
+
+      if (!bind)
+	{
+	  dead_debug_reset_uses (debug, uses);
+	  return 0;
+	}
+    }
+
+  /* If there's a single (debug) use of an otherwise unused REG, and
+     the debug use is not part of a larger expression, then it
+     probably doesn't make sense to introduce a new debug temp.  */
+  if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next)
+    {
+      rtx next = DF_REF_INSN (uses->use);
+
+      if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
+	{
+	  XDELETE (uses);
+	  return 0;
+	}
+    }
 
   /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
   dval = make_debug_expr_from_rtl (reg);
 
   /* Emit a debug bind insn before the insn in which reg dies.  */
   bind = gen_rtx_VAR_LOCATION (GET_MODE (reg),
-			       DEBUG_EXPR_TREE_DECL (dval), reg,
+			       DEBUG_EXPR_TREE_DECL (dval), bind,
 			       VAR_INIT_STATUS_INITIALIZED);
 
-  bind = emit_debug_insn_before (bind, insn);
+  if (where == DEBUG_TEMP_AFTER_WITH_REG)
+    bind = emit_debug_insn_after (bind, insn);
+  else
+    bind = emit_debug_insn_before (bind, insn);
   df_insn_rescan (bind);
 
   /* Adjust all uses.  */
@@ -3201,6 +3291,8 @@ dead_debug_insert_before (struct dead_de
       uses = cur->next;
       XDELETE (cur);
     }
+
+  return 1;
 }
 
 /* Recompute the REG_DEAD and REG_UNUSED notes and compute register
@@ -3395,7 +3487,8 @@ df_note_bb_compute (unsigned int bb_inde
 		  break;
 		}
 	      else
-		dead_debug_insert_before (&debug, uregno, insn);
+		dead_debug_insert_temp (&debug, uregno, insn,
+					DEBUG_TEMP_BEFORE_WITH_REG);
 
 	      if ( (!(DF_REF_FLAGS (use)
 		      & (DF_REF_MW_HARDREG | DF_REF_READ_WRITE)))
Index: gcc/dce.c
===================================================================
--- gcc/dce.c.orig	2011-05-26 05:03:08.552989108 -0300
+++ gcc/dce.c	2011-05-27 03:09:07.711388764 -0300
@@ -493,6 +493,43 @@ remove_reg_equal_equiv_notes_for_defs (r
     remove_reg_equal_equiv_notes_for_regno (DF_REF_REGNO (*def_rec));
 }
 
+/* Scan all BBs for debug insns and reset those that reference values
+   defined in unmarked insns.  */
+
+static void
+reset_unmarked_insns_debug_uses (void)
+{
+  basic_block bb;
+  rtx insn, next;
+
+  FOR_EACH_BB_REVERSE (bb)
+    FOR_BB_INSNS_REVERSE_SAFE (bb, insn, next)
+      if (DEBUG_INSN_P (insn))
+	{
+	  df_ref *use_rec;
+
+	  for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
+	    {
+	      df_ref use = *use_rec;
+	      struct df_link *defs;
+	      for (defs = DF_REF_CHAIN (use); defs; defs = defs->next)
+		{
+		  rtx insn;
+		  if (DF_REF_IS_ARTIFICIAL (defs->ref))
+		    continue;
+		  insn = DF_REF_INSN (defs->ref);
+		  if (!marked_insn_p (insn))
+		    break;
+		}
+	      if (!defs)
+		continue;
+	      /* ??? FIXME could we propagate the values assigned to
+		 each of the DEFs?  */
+	      INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
+	      df_insn_rescan_debug_internal (insn);
+	    }
+	}
+}
 
 /* Delete every instruction that hasn't been marked.  */
 
@@ -505,7 +542,7 @@ delete_unmarked_insns (void)
 
   FOR_EACH_BB_REVERSE (bb)
     FOR_BB_INSNS_REVERSE_SAFE (bb, insn, next)
-      if (INSN_P (insn))
+      if (NONDEBUG_INSN_P (insn))
 	{
 	  /* Always delete no-op moves.  */
 	  if (noop_move_p (insn))
@@ -579,7 +616,7 @@ prescan_insns_for_dce (bool fast)
   FOR_EACH_BB (bb)
     {
       FOR_BB_INSNS_REVERSE_SAFE (bb, insn, prev)
-	if (INSN_P (insn))
+	if (NONDEBUG_INSN_P (insn))
 	  {
 	    /* Don't mark argument stores now.  They will be marked
 	       if needed when the associated CALL is marked.  */
@@ -713,6 +750,9 @@ rest_of_handle_ud_dce (void)
     }
   VEC_free (rtx, heap, worklist);
 
+  if (MAY_HAVE_DEBUG_INSNS)
+    reset_unmarked_insns_debug_uses ();
+
   /* Before any insns are deleted, we must remove the chains since
      they are not bidirectional.  */
   df_remove_problem (df_chain);
@@ -767,6 +807,7 @@ word_dce_process_block (basic_block bb, 
   bitmap local_live = BITMAP_ALLOC (&dce_tmp_bitmap_obstack);
   rtx insn;
   bool block_changed;
+  struct dead_debug debug;
 
   if (redo_out)
     {
@@ -788,11 +829,24 @@ word_dce_process_block (basic_block bb, 
     }
 
   bitmap_copy (local_live, DF_WORD_LR_OUT (bb));
+  dead_debug_init (&debug, NULL);
 
   FOR_BB_INSNS_REVERSE (bb, insn)
-    if (NONDEBUG_INSN_P (insn))
+    if (DEBUG_INSN_P (insn))
+      {
+	df_ref *use_rec;
+	for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
+	  if (DF_REF_REGNO (*use_rec) >= FIRST_PSEUDO_REGISTER
+	      && (GET_MODE_SIZE (GET_MODE (DF_REF_REAL_REG (*use_rec)))
+		  == 2 * UNITS_PER_WORD)
+	      && !bitmap_bit_p (local_live, 2 * DF_REF_REGNO (*use_rec))
+	      && !bitmap_bit_p (local_live, 2 * DF_REF_REGNO (*use_rec) + 1))
+	    dead_debug_add (&debug, *use_rec, DF_REF_REGNO (*use_rec));
+      }
+    else if (INSN_P (insn))
       {
 	bool any_changed;
+
 	/* No matter if the instruction is needed or not, we remove
 	   any regno in the defs from the live set.  */
 	any_changed = df_word_lr_simulate_defs (insn, local_live);
@@ -804,6 +858,15 @@ word_dce_process_block (basic_block bb, 
 	if (marked_insn_p (insn))
 	  df_word_lr_simulate_uses (insn, local_live);
 
+	if (debug.used && !bitmap_empty_p (debug.used))
+	  {
+	    df_ref *def_rec;
+
+	    for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
+	      dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn,
+				      DEBUG_TEMP_BEFORE_WITH_VALUE);
+	  }
+
 	if (dump_file)
 	  {
 	    fprintf (dump_file, "finished processing insn %d live out = ",
@@ -816,6 +879,7 @@ word_dce_process_block (basic_block bb, 
   if (block_changed)
     bitmap_copy (DF_WORD_LR_IN (bb), local_live);
 
+  dead_debug_finish (&debug, NULL);
   BITMAP_FREE (local_live);
   return block_changed;
 }
@@ -833,6 +897,7 @@ dce_process_block (basic_block bb, bool 
   rtx insn;
   bool block_changed;
   df_ref *def_rec;
+  struct dead_debug debug;
 
   if (redo_out)
     {
@@ -856,22 +921,36 @@ dce_process_block (basic_block bb, bool 
   bitmap_copy (local_live, DF_LR_OUT (bb));
 
   df_simulate_initialize_backwards (bb, local_live);
+  dead_debug_init (&debug, NULL);
 
   FOR_BB_INSNS_REVERSE (bb, insn)
-    if (INSN_P (insn))
+    if (DEBUG_INSN_P (insn))
+      {
+	df_ref *use_rec;
+	for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
+	  if (!bitmap_bit_p (local_live, DF_REF_REGNO (*use_rec))
+	      && !bitmap_bit_p (au, DF_REF_REGNO (*use_rec)))
+	    dead_debug_add (&debug, *use_rec, DF_REF_REGNO (*use_rec));
+      }
+    else if (INSN_P (insn))
       {
 	bool needed = marked_insn_p (insn);
 
 	/* The insn is needed if there is someone who uses the output.  */
 	if (!needed)
 	  for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
-	    if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec))
-		|| bitmap_bit_p (au, DF_REF_REGNO (*def_rec)))
-	      {
-		needed = true;
-		mark_insn (insn, true);
-		break;
-	      }
+	    {
+	      dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn,
+				      DEBUG_TEMP_BEFORE_WITH_VALUE);
+
+	      if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec))
+		  || bitmap_bit_p (au, DF_REF_REGNO (*def_rec)))
+		{
+		  needed = true;
+		  mark_insn (insn, true);
+		  break;
+		}
+	    }
 
 	/* No matter if the instruction is needed or not, we remove
 	   any regno in the defs from the live set.  */
@@ -883,6 +962,7 @@ dce_process_block (basic_block bb, bool 
 	  df_simulate_uses (insn, local_live);
       }
 
+  dead_debug_finish (&debug, NULL);
   df_simulate_finalize_backwards (bb, local_live);
 
   block_changed = !bitmap_equal_p (local_live, DF_LR_IN (bb));
Index: gcc/reg-stack.c
===================================================================
--- gcc/reg-stack.c.orig	2011-05-26 05:03:08.554989112 -0300
+++ gcc/reg-stack.c	2011-05-27 03:11:23.756589358 -0300
@@ -1333,6 +1333,11 @@ subst_stack_regs_in_debug_insn (rtx *loc
     return 0;
 
   hard_regno = get_hard_regnum (regstack, *loc);
+
+  /* If we can't find an active register, reset this debug insn.  */
+  if (hard_regno == -1)
+    return 1;
+
   gcc_assert (hard_regno >= FIRST_STACK_REG);
 
   replace_reg (loc, hard_regno);
@@ -1340,6 +1345,23 @@ subst_stack_regs_in_debug_insn (rtx *loc
   return -1;
 }
 
+/* Substitute hardware stack regs in debug insn INSN, using stack
+   layout REGSTACK.  If we can't find a hardware stack reg for any of
+   the REGs in it, reset the debug insn.  */
+
+static void
+subst_all_stack_regs_in_debug_insn (rtx insn, struct stack_def *regstack)
+{
+  int ret = for_each_rtx (&INSN_VAR_LOCATION_LOC (insn),
+			  subst_stack_regs_in_debug_insn,
+			  regstack);
+
+  if (ret == 1)
+    INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
+  else
+    gcc_checking_assert (ret == 0);
+}
+
 /* Substitute new registers in PAT, which is part of INSN.  REGSTACK
    is the current register layout.  Return whether a control flow insn
    was deleted in the process.  */
@@ -2947,8 +2969,7 @@ convert_regs_1 (basic_block block)
 	    debug_insns_with_starting_stack++;
 	  else
 	    {
-	      for_each_rtx (&PATTERN (insn), subst_stack_regs_in_debug_insn,
-			    &regstack);
+	      subst_all_stack_regs_in_debug_insn (insn, &regstack);
 
 	      /* Nothing must ever die at a debug insn.  If something
 		 is referenced in it that becomes dead, it should have
@@ -2986,8 +3007,7 @@ convert_regs_1 (basic_block block)
 	    continue;
 
 	  debug_insns_with_starting_stack--;
-	  for_each_rtx (&PATTERN (insn), subst_stack_regs_in_debug_insn,
-			&bi->stack_in);
+	  subst_all_stack_regs_in_debug_insn (insn, &bi->stack_in);
 	}
     }
 
Index: gcc/df.h
===================================================================
--- gcc/df.h.orig	2011-05-26 05:03:08.555989114 -0300
+++ gcc/df.h	2011-05-26 05:03:28.170028039 -0300
@@ -1101,4 +1101,35 @@ extern void union_defs (df_ref, struct w
 			unsigned int *used, struct web_entry *,
 			bool (*fun) (struct web_entry *, struct web_entry *));
 
+/* Debug uses of dead regs.  */
+
+/* Node of a linked list of uses of dead REGs in debug insns.  */
+struct dead_debug_use
+{
+  df_ref use;
+  struct dead_debug_use *next;
+};
+
+/* Linked list of the above, with a bitmap of the REGs in the
+   list.  */
+struct dead_debug
+{
+  struct dead_debug_use *head;
+  bitmap used;
+  bitmap to_rescan;
+};
+
+enum debug_temp_where
+  {
+    DEBUG_TEMP_BEFORE_WITH_REG = -1,
+    DEBUG_TEMP_BEFORE_WITH_VALUE = 0,
+    DEBUG_TEMP_AFTER_WITH_REG = 1
+  };
+
+void dead_debug_init (struct dead_debug *, bitmap);
+void dead_debug_finish (struct dead_debug *, bitmap);
+void dead_debug_add (struct dead_debug *, df_ref, unsigned int);
+int dead_debug_insert_temp (struct dead_debug *, unsigned int, rtx,
+			    enum debug_temp_where);
+
 #endif /* GCC_DF_H */

[-- Attachment #3: Type: text/plain, Size: 258 bytes --]



-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: fix left-over debug insns in DCE
  2011-05-30 12:15 fix left-over debug insns in DCE Alexandre Oliva
@ 2011-06-02 15:26 ` Eric Botcazou
  2011-06-03  1:16   ` Alexandre Oliva
  0 siblings, 1 reply; 23+ messages in thread
From: Eric Botcazou @ 2011-06-02 15:26 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: gcc-patches

> One of the issues was that DCE removed an insn that set a REG in a
> certain mode, without adjusting a debug use of that REG.  This was in
> libstdc++, but I failed to take note of the affected file.  DF later
> attached that debug use to another SET to the same REG in a different,
> incompatible mode.  When that one was found to be dead by DF, we ended
> up ICEing as we attempted to emit the invalid SUBREGs.
>
> I reused some of the infrastructure to propagate dead DEFs into debug
> uses in DF to get DCE to emit debug temps and adjust debug uses as well,
> fixing this issue.  While at that, I improved the handling of unused
> DEFs in DF, that previously resulted in loss of debug information, so as
> to retain it as much as possible.

Why can't the problem be addressed purely within DF?  Starting to spill the DF 
logic to individual RTL passes doesn't look very appealing to me.

> This is the patch I ended up with.  Regstrapped on x86_64-linux-gnu and
> i686-linux-gnu.  Ok to install?

OK for the usual debug insn bookkeeping, i.e.

        * dce.c (reset_unmarked_insns_debug_uses): New.
        (delete_unmarked_insns): Skip debug insns.
        (prescan_insns_for_dce): Likewise.
        (rest_of_handle_ud_dce): Propagate debug uses.
        * reg-stack.c (subst_stack_regs_in_debug_insn): Signal when no
        active reg can be found.
        (subst_all_stack_regs_in_debug_insn): New.  Reset debug insn then.
        (convert_regs_1): Use it.

The rest needs further discussing IMO.

-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: fix left-over debug insns in DCE
  2011-06-02 15:26 ` Eric Botcazou
@ 2011-06-03  1:16   ` Alexandre Oliva
  2011-06-03 11:06     ` Eric Botcazou
  0 siblings, 1 reply; 23+ messages in thread
From: Alexandre Oliva @ 2011-06-03  1:16 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

On Jun  2, 2011, Eric Botcazou <ebotcazou@adacore.com> wrote:

> Why can't the problem be addressed purely within DF?

Hmm...  Maybe it could, I'm not sure.  The problem is that DCE removes
insns, and then DF associates remaining uses in debug insns to earlier
DEFs.  Adjusting debug insns in DCE is right per the VTA design motto:
decide as if debug insns weren't there, adjust them as you would adjust
non-debug insns.  This code borrowed from DF into DCE is the “adjust”
bit.

> Starting to spill the DF 
> logic to individual RTL passes doesn't look very appealing to me.

Propagation of uses isn't DF-specific material, it just so happened that
it offered an adequate interface.  Other passes already have their own
propagation machinery, but it didn't look quite as suitable.

>> This is the patch I ended up with.  Regstrapped on x86_64-linux-gnu and
>> i686-linux-gnu.  Ok to install?

> OK for the usual debug insn bookkeeping, i.e.

Err...  These depend on the interface changes of functions defined
within DF to work.  Should they perhaps be moved out of DF-specific
files?

-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: fix left-over debug insns in DCE
  2011-06-03  1:16   ` Alexandre Oliva
@ 2011-06-03 11:06     ` Eric Botcazou
  2011-06-06  5:38       ` Alexandre Oliva
  2011-06-06  7:11       ` fix left-over debug insns in DCE Alexandre Oliva
  0 siblings, 2 replies; 23+ messages in thread
From: Eric Botcazou @ 2011-06-03 11:06 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: gcc-patches

> Hmm...  Maybe it could, I'm not sure.  The problem is that DCE removes
> insns, and then DF associates remaining uses in debug insns to earlier
> DEFs.  Adjusting debug insns in DCE is right per the VTA design motto:
> decide as if debug insns weren't there, adjust them as you would adjust
> non-debug insns.  This code borrowed from DF into DCE is the “adjust”
> bit.

But DCE isn't the only pass that removes insns.  Does the same logic need to be 
replicated in all passes that do?  On the other hand, these passes call into 
DF when they remove insns, so DF is a central place here.

> Err...  These depend on the interface changes of functions defined
> within DF to work.

No, they don't, I can compile them independently.

-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: fix left-over debug insns in DCE
  2011-06-03 11:06     ` Eric Botcazou
@ 2011-06-06  5:38       ` Alexandre Oliva
  2011-06-06  6:05         ` Jakub Jelinek
  2011-06-06  8:00         ` Eric Botcazou
  2011-06-06  7:11       ` fix left-over debug insns in DCE Alexandre Oliva
  1 sibling, 2 replies; 23+ messages in thread
From: Alexandre Oliva @ 2011-06-06  5:38 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

On Jun  3, 2011, Eric Botcazou <ebotcazou@adacore.com> wrote:

>> Hmm...  Maybe it could, I'm not sure.  The problem is that DCE removes
>> insns, and then DF associates remaining uses in debug insns to earlier
>> DEFs.  Adjusting debug insns in DCE is right per the VTA design motto:
>> decide as if debug insns weren't there, adjust them as you would adjust
>> non-debug insns.  This code borrowed from DF into DCE is the “adjust”
>> bit.

> But DCE isn't the only pass that removes insns.

Yup.

> Does the same logic need to be replicated in all passes that do?

Those that remove sets whose DESTs may still be receved by debug insns
ought to adjust debug insns, yeah.  I think (hope) we have them all
covered.  Do you know of any we missed?

> On the other hand, these passes call into DF when they remove insns,
> so DF is a central place here.

It might be too late for DF to do anything sensible to preserve the
debug info rather than just throw it away, as your partial approval
suggests.

>> Err...  These depend on the interface changes of functions defined
>> within DF to work.

> No, they don't, I can compile them independently.

Indeed, sorry, I misread it.

-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: fix left-over debug insns in DCE
  2011-06-06  5:38       ` Alexandre Oliva
@ 2011-06-06  6:05         ` Jakub Jelinek
  2011-06-06  8:00         ` Eric Botcazou
  1 sibling, 0 replies; 23+ messages in thread
From: Jakub Jelinek @ 2011-06-06  6:05 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: Eric Botcazou, gcc-patches

On Mon, Jun 06, 2011 at 02:32:29AM -0300, Alexandre Oliva wrote:
> Those that remove sets whose DESTs may still be receved by debug insns
> ought to adjust debug insns, yeah.  I think (hope) we have them all
> covered.  Do you know of any we missed?

delete_trivially_dead_insns is already covered, fast DCE is what I've
encountered to be missing in the past (see PR48886), but I guess your
patch covers both fast DCE and ud DCE.

	Jakub

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: fix left-over debug insns in DCE
  2011-06-03 11:06     ` Eric Botcazou
  2011-06-06  5:38       ` Alexandre Oliva
@ 2011-06-06  7:11       ` Alexandre Oliva
  1 sibling, 0 replies; 23+ messages in thread
From: Alexandre Oliva @ 2011-06-06  7:11 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

On Jun  3, 2011, Eric Botcazou <ebotcazou@adacore.com> wrote:

> Does the same logic need to be replicated in all passes that do?  On
> the other hand, these passes call into DF when they remove insns, so
> DF is a central place here.

I went over (again?) a number of passes that call delete_insn directly
or indirectly.  The problem is that, at that point, it's not possible to
tell whether:

- equivalent DEFs are going to be re-emitted at the same place (say, a
split), so debug insns are to be left alone,

- whether they're being re-emitted elsewhere (say a move), so *some*
debug insns may have to be adjusted

- whether DEFs are really being removed, in which case debug temps may
have to be emitted; and the uses, adjusted; and other reaching defs also
followed by binding of the same debug temps

- whether many chained insns are being deleted (say an entire BB, or
part of a BB found to be shared between multiple BBs), in which case
debug insns may have to be left alone or adjusted depending on the
CFG adjustments to be made later

Given this analysis, I'm still of the opinion that passes that remove
insns that may be used by debug insns ought to decide on their own how
to make the adjustments (or not), as they currently do, but that we
should have better shared infrastructure to propagate
defs-to-be-removed, which is currently done in an ad hoc manner in
several passes.

-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: fix left-over debug insns in DCE
  2011-06-06  5:38       ` Alexandre Oliva
  2011-06-06  6:05         ` Jakub Jelinek
@ 2011-06-06  8:00         ` Eric Botcazou
  2011-06-06 10:07           ` Alexandre Oliva
  2011-06-06 13:28           ` Alexandre Oliva
  1 sibling, 2 replies; 23+ messages in thread
From: Eric Botcazou @ 2011-06-06  8:00 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: gcc-patches

> It might be too late for DF to do anything sensible to preserve the
> debug info rather than just throw it away, as your partial approval
> suggests.

OK, let me think about this a little more.

> Indeed, sorry, I misread it.

Mind installing these bits separately?  My understanding is that they are 
independent correctness fixes.

-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: fix left-over debug insns in DCE
  2011-06-06  8:00         ` Eric Botcazou
@ 2011-06-06 10:07           ` Alexandre Oliva
  2011-06-06 13:28           ` Alexandre Oliva
  1 sibling, 0 replies; 23+ messages in thread
From: Alexandre Oliva @ 2011-06-06 10:07 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 521 bytes --]

On Jun  6, 2011, Eric Botcazou <ebotcazou@adacore.com> wrote:

>> Indeed, sorry, I misread it.

> Mind installing these bits separately?

Nope.  Testing it separately now.

> My understanding is that they are 
> independent correctness fixes.

Yeah, I guess they are.  They won't make debug info any worse, although
they may remove incorrect debug info, and they won't preserve debug info
that we currently discard, which the other part of the patch retains.

Here's what I'm going to check in if regstrapping succeeds.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: vta-dce-reset-removed-debug-uses-pr48866.patch --]
[-- Type: text/x-diff, Size: 4638 bytes --]

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

	* dce.c (reset_unmarked_insns_debug_uses): New.
	(delete_unmarked_insns): Skip debug insns.
	(prescan_insns_for_dce): Likewise.
	(rest_of_handle_ud_dce): Reset debug uses of removed sets.
	* reg-stack.c (subst_stack_regs_in_debug_insn): Signal when no
	active reg can be found.
	(subst_all_stack_regs_in_debug_insn): New.  Reset debug insn then.
	(convert_regs_1): Use it.

Index: gcc/dce.c
===================================================================
--- gcc/dce.c.orig	2011-05-26 05:03:08.552989108 -0300
+++ gcc/dce.c	2011-05-27 03:09:07.711388764 -0300
@@ -493,6 +493,43 @@ remove_reg_equal_equiv_notes_for_defs (r
     remove_reg_equal_equiv_notes_for_regno (DF_REF_REGNO (*def_rec));
 }
 
+/* Scan all BBs for debug insns and reset those that reference values
+   defined in unmarked insns.  */
+
+static void
+reset_unmarked_insns_debug_uses (void)
+{
+  basic_block bb;
+  rtx insn, next;
+
+  FOR_EACH_BB_REVERSE (bb)
+    FOR_BB_INSNS_REVERSE_SAFE (bb, insn, next)
+      if (DEBUG_INSN_P (insn))
+	{
+	  df_ref *use_rec;
+
+	  for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
+	    {
+	      df_ref use = *use_rec;
+	      struct df_link *defs;
+	      for (defs = DF_REF_CHAIN (use); defs; defs = defs->next)
+		{
+		  rtx insn;
+		  if (DF_REF_IS_ARTIFICIAL (defs->ref))
+		    continue;
+		  insn = DF_REF_INSN (defs->ref);
+		  if (!marked_insn_p (insn))
+		    break;
+		}
+	      if (!defs)
+		continue;
+	      /* ??? FIXME could we propagate the values assigned to
+		 each of the DEFs?  */
+	      INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
+	      df_insn_rescan_debug_internal (insn);
+	    }
+	}
+}
 
 /* Delete every instruction that hasn't been marked.  */
 
@@ -505,7 +542,7 @@ delete_unmarked_insns (void)
 
   FOR_EACH_BB_REVERSE (bb)
     FOR_BB_INSNS_REVERSE_SAFE (bb, insn, next)
-      if (INSN_P (insn))
+      if (NONDEBUG_INSN_P (insn))
 	{
 	  /* Always delete no-op moves.  */
 	  if (noop_move_p (insn))
@@ -579,7 +616,7 @@ prescan_insns_for_dce (bool fast)
   FOR_EACH_BB (bb)
     {
       FOR_BB_INSNS_REVERSE_SAFE (bb, insn, prev)
-	if (INSN_P (insn))
+	if (NONDEBUG_INSN_P (insn))
 	  {
 	    /* Don't mark argument stores now.  They will be marked
 	       if needed when the associated CALL is marked.  */
@@ -713,6 +750,9 @@ rest_of_handle_ud_dce (void)
     }
   VEC_free (rtx, heap, worklist);
 
+  if (MAY_HAVE_DEBUG_INSNS)
+    reset_unmarked_insns_debug_uses ();
+
   /* Before any insns are deleted, we must remove the chains since
      they are not bidirectional.  */
   df_remove_problem (df_chain);
Index: gcc/reg-stack.c
===================================================================
--- gcc/reg-stack.c.orig	2011-05-26 05:03:08.554989112 -0300
+++ gcc/reg-stack.c	2011-05-27 03:11:23.756589358 -0300
@@ -1333,6 +1333,11 @@ subst_stack_regs_in_debug_insn (rtx *loc
     return 0;
 
   hard_regno = get_hard_regnum (regstack, *loc);
+
+  /* If we can't find an active register, reset this debug insn.  */
+  if (hard_regno == -1)
+    return 1;
+
   gcc_assert (hard_regno >= FIRST_STACK_REG);
 
   replace_reg (loc, hard_regno);
@@ -1340,6 +1345,23 @@ subst_stack_regs_in_debug_insn (rtx *loc
   return -1;
 }
 
+/* Substitute hardware stack regs in debug insn INSN, using stack
+   layout REGSTACK.  If we can't find a hardware stack reg for any of
+   the REGs in it, reset the debug insn.  */
+
+static void
+subst_all_stack_regs_in_debug_insn (rtx insn, struct stack_def *regstack)
+{
+  int ret = for_each_rtx (&INSN_VAR_LOCATION_LOC (insn),
+			  subst_stack_regs_in_debug_insn,
+			  regstack);
+
+  if (ret == 1)
+    INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
+  else
+    gcc_checking_assert (ret == 0);
+}
+
 /* Substitute new registers in PAT, which is part of INSN.  REGSTACK
    is the current register layout.  Return whether a control flow insn
    was deleted in the process.  */
@@ -2947,8 +2969,7 @@ convert_regs_1 (basic_block block)
 	    debug_insns_with_starting_stack++;
 	  else
 	    {
-	      for_each_rtx (&PATTERN (insn), subst_stack_regs_in_debug_insn,
-			    &regstack);
+	      subst_all_stack_regs_in_debug_insn (insn, &regstack);
 
 	      /* Nothing must ever die at a debug insn.  If something
 		 is referenced in it that becomes dead, it should have
@@ -2986,8 +3007,7 @@ convert_regs_1 (basic_block block)
 	    continue;
 
 	  debug_insns_with_starting_stack--;
-	  for_each_rtx (&PATTERN (insn), subst_stack_regs_in_debug_insn,
-			&bi->stack_in);
+	  subst_all_stack_regs_in_debug_insn (insn, &bi->stack_in);
 	}
     }
 

[-- Attachment #3: Type: text/plain, Size: 258 bytes --]



-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: fix left-over debug insns in DCE
  2011-06-06  8:00         ` Eric Botcazou
  2011-06-06 10:07           ` Alexandre Oliva
@ 2011-06-06 13:28           ` Alexandre Oliva
  2011-06-07 12:25             ` [PATCH] Fix ICE in reset_unmarked_insns_debug_uses (PR middle-end/49308) Jakub Jelinek
  2012-04-09  6:14             ` fix left-over debug insns in DCE Alexandre Oliva
  1 sibling, 2 replies; 23+ messages in thread
From: Alexandre Oliva @ 2011-06-06 13:28 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 459 bytes --]

On Jun  6, 2011, Eric Botcazou <ebotcazou@adacore.com> wrote:

>> It might be too late for DF to do anything sensible to preserve the
>> debug info rather than just throw it away, as your partial approval
>> suggests.

> OK, let me think about this a little more.

>> Indeed, sorry, I misread it.

> Mind installing these bits separately?  My understanding is that they are 
> independent correctness fixes.

Tested, installed.

Here are the remaining bits.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: vta-dce-df-preserve-unused-dead-in-debug-temp-pr48866.patch --]
[-- Type: text/x-diff, Size: 16972 bytes --]

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

	* df.h (enum debug_temp_where): New.
	(dead_debug_init, dead_debug_finish) Declare.
	(dead_debug_add, dead_debug_insert_temp): Declare.
	(struct dead_debug_use, struct dead_debug): Moved from...
	* df-problems.c: ... here.
	(df_set_unused_notes_for_mw): Bind debug uses of unused regno
	to a debug temp.
	(df_create_unused_note): Likewise.
	(df_set_dead_notes_for_mw): Move comment where it belongs.
	(dead_debug_init): Export.
	(dead_debug_reset_uses): New, factored out of...
	(dead_debug_finish): ...this.  Export.
	(dead_debug_reset): Remove.
	(dead_debug_add): Export.
	(dead_debug_insert_before): Rename to...
	(dead_debug_insert_temp): ... this.  Add where argument.  Export.
	Locate stored value for BEFORE_WITH_VALUE.  Avoid repeat inserts.
	Return insertion count.
	(df_note_bb_compute): Adjust.
	* dce.c (word_dce_process_block): Adjust dead debug uses.
	(dce_process_block): Likewise.

Index: gcc/df-problems.c
===================================================================
--- gcc/df-problems.c.orig	2011-06-03 11:41:26.507743096 -0300
+++ gcc/df-problems.c	2011-06-06 10:25:17.854030387 -0300
@@ -2842,25 +2842,6 @@ df_whole_mw_reg_unused_p (struct df_mw_h
 }
 
 
-/* Node of a linked list of uses of dead REGs in debug insns.  */
-struct dead_debug_use
-{
-  df_ref use;
-  struct dead_debug_use *next;
-};
-
-/* Linked list of the above, with a bitmap of the REGs in the
-   list.  */
-struct dead_debug
-{
-  struct dead_debug_use *head;
-  bitmap used;
-  bitmap to_rescan;
-};
-
-static void dead_debug_reset (struct dead_debug *, unsigned int);
-
-
 /* Set the REG_UNUSED notes for the multiword hardreg defs in INSN
    based on the bits in LIVE.  Do not generate notes for registers in
    artificial uses.  DO_NOT_GEN is updated so that REG_DEAD notes are
@@ -2886,7 +2867,7 @@ df_set_unused_notes_for_mw (rtx insn, st
     {
       unsigned int regno = mws->start_regno;
       df_set_note (REG_UNUSED, insn, mws->mw_reg);
-      dead_debug_reset (debug, regno);
+      dead_debug_insert_temp (debug, regno, insn, DEBUG_TEMP_AFTER_WITH_REG);
 
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 1: ", insn, REG_NOTES (insn));
@@ -2901,7 +2882,7 @@ df_set_unused_notes_for_mw (rtx insn, st
 	    && !bitmap_bit_p (artificial_uses, r))
 	  {
 	    df_set_note (REG_UNUSED, insn, regno_reg_rtx[r]);
-	    dead_debug_reset (debug, r);
+	    dead_debug_insert_temp (debug, r, insn, DEBUG_TEMP_AFTER_WITH_REG);
 #ifdef REG_DEAD_DEBUGGING
 	    df_print_note ("adding 2: ", insn, REG_NOTES (insn));
 #endif
@@ -2969,12 +2950,12 @@ df_set_dead_notes_for_mw (rtx insn, stru
 
   if (df_whole_mw_reg_dead_p (mws, live, artificial_uses, do_not_gen))
     {
-      /* Add a dead note for the entire multi word register.  */
       if (is_debug)
 	{
 	  *added_notes_p = true;
 	  return;
 	}
+      /* Add a dead note for the entire multi word register.  */
       df_set_note (REG_DEAD, insn, mws->mw_reg);
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 1: ", insn, REG_NOTES (insn));
@@ -3028,7 +3009,7 @@ df_create_unused_note (rtx insn, df_ref 
       rtx reg = (DF_REF_LOC (def))
                 ? *DF_REF_REAL_LOC (def): DF_REF_REG (def);
       df_set_note (REG_UNUSED, insn, reg);
-      dead_debug_reset (debug, dregno);
+      dead_debug_insert_temp (debug, dregno, insn, DEBUG_TEMP_AFTER_WITH_REG);
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 3: ", insn, REG_NOTES (insn));
 #endif
@@ -3039,7 +3020,7 @@ df_create_unused_note (rtx insn, df_ref 
 
 
 /* Initialize DEBUG to an empty list, and clear USED, if given.  */
-static inline void
+void
 dead_debug_init (struct dead_debug *debug, bitmap used)
 {
   debug->head = NULL;
@@ -3049,32 +3030,83 @@ dead_debug_init (struct dead_debug *debu
     bitmap_clear (used);
 }
 
-/* Reset all debug insns with pending uses.  Release the bitmap in it,
-   unless it is USED.  USED must be the same bitmap passed to
-   dead_debug_init.  */
-static inline void
-dead_debug_finish (struct dead_debug *debug, bitmap used)
+/* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of
+   each reset insn.  DEBUG is not otherwise modified.  If HEAD is
+   DEBUG->head, DEBUG->head will be set to NULL at the end.
+   Otherwise, entries from DEBUG->head that pertain to reset insns
+   will be removed, and only then rescanned.  */
+
+static void
+dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
 {
-  struct dead_debug_use *head;
-  rtx insn = NULL;
+  bool got_head = (debug->head == head);
+  bitmap rescan;
+  struct dead_debug_use **tailp = &debug->head;
+  struct dead_debug_use *cur;
+  bitmap_iterator bi;
+  unsigned int uid;
 
-  if (debug->used != used)
-    BITMAP_FREE (debug->used);
+  if (got_head)
+    rescan = NULL;
+  else
+    rescan = BITMAP_ALLOC (NULL);
 
-  while ((head = debug->head))
+  while (head)
     {
+      struct dead_debug_use *next = head->next;
+      rtx insn;
+
       insn = DF_REF_INSN (head->use);
-      if (!head->next || DF_REF_INSN (head->next->use) != insn)
+      if (!next || DF_REF_INSN (next->use) != insn)
 	{
 	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-	  df_insn_rescan_debug_internal (insn);
+	  if (got_head)
+	    df_insn_rescan_debug_internal (insn);
+	  else
+	    bitmap_set_bit (rescan, INSN_UID (insn));
 	  if (debug->to_rescan)
 	    bitmap_clear_bit (debug->to_rescan, INSN_UID (insn));
 	}
-      debug->head = head->next;
       XDELETE (head);
+      head = next;
     }
 
+  if (got_head)
+    {
+      debug->head = NULL;
+      return;
+    }
+
+  while ((cur = *tailp))
+    if (bitmap_bit_p (rescan, INSN_UID (DF_REF_INSN (cur->use))))
+      {
+	*tailp = cur->next;
+	XDELETE (cur);
+      }
+    else
+      tailp = &cur->next;
+
+  EXECUTE_IF_SET_IN_BITMAP (rescan, 0, uid, bi)
+    {
+      struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
+      if (insn_info)
+	df_insn_rescan_debug_internal (insn_info->insn);
+    }
+
+  BITMAP_FREE (rescan);
+}
+
+/* Reset all debug insns with pending uses.  Release the bitmap in it,
+   unless it is USED.  USED must be the same bitmap passed to
+   dead_debug_init.  */
+void
+dead_debug_finish (struct dead_debug *debug, bitmap used)
+{
+  if (debug->used != used)
+    BITMAP_FREE (debug->used);
+
+  dead_debug_reset_uses (debug, debug->head);
+
   if (debug->to_rescan)
     {
       bitmap_iterator bi;
@@ -3090,37 +3122,9 @@ dead_debug_finish (struct dead_debug *de
     }
 }
 
-/* Reset DEBUG_INSNs with pending uses of DREGNO.  */
-static void
-dead_debug_reset (struct dead_debug *debug, unsigned int dregno)
-{
-  struct dead_debug_use **tailp = &debug->head;
-  struct dead_debug_use *cur;
-  rtx insn;
-
-  if (!debug->used || !bitmap_clear_bit (debug->used, dregno))
-    return;
-
-  while ((cur = *tailp))
-    {
-      if (DF_REF_REGNO (cur->use) == dregno)
-	{
-	  *tailp = cur->next;
-	  insn = DF_REF_INSN (cur->use);
-	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-	  if (debug->to_rescan == NULL)
-	    debug->to_rescan = BITMAP_ALLOC (NULL);
-	  bitmap_set_bit (debug->to_rescan, INSN_UID (insn));
-	  XDELETE (cur);
-	}
-      else
-	tailp = &(*tailp)->next;
-    }
-}
-
 /* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
    insn.  Create a bitmap for DEBUG as needed.  */
-static inline void
+void
 dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
 {
   struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
@@ -3136,12 +3140,15 @@ dead_debug_add (struct dead_debug *debug
 }
 
 /* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
-   before INSN that binds the REG to a debug temp, and replace all
-   uses of UREGNO in DEBUG with uses of the debug temp.  INSN must be
-   the insn where UREGNO dies.  */
-static inline void
-dead_debug_insert_before (struct dead_debug *debug, unsigned int uregno,
-			  rtx insn)
+   before or after INSN (depending on WHERE), that binds a debug temp
+   to the widest-mode use of UREGNO, if WHERE is _WITH_REG, or
+   _WITH_VALUE otherwise, and replace all uses of UREGNO in DEBUG with
+   uses of the debug temp.  INSN must be where UREGNO dies, if WHERE
+   is DEAD_DEBUG_BEFORE_WITH_REG, or where it is set otherwise.
+   Return the number of debug insns emitted.  */
+int
+dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
+			rtx insn, enum debug_temp_where where)
 {
   struct dead_debug_use **tailp = &debug->head;
   struct dead_debug_use *cur;
@@ -3152,7 +3159,7 @@ dead_debug_insert_before (struct dead_de
   rtx bind;
 
   if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
-    return;
+    return 0;
 
   /* Move all uses of uregno from debug->head to uses, setting mode to
      the widest referenced mode.  */
@@ -3173,17 +3180,100 @@ dead_debug_insert_before (struct dead_de
 	tailp = &(*tailp)->next;
     }
 
-  gcc_assert (reg);
+  if (!reg)
+    {
+      gcc_checking_assert (!uses);
+      return 0;
+    }
+
+  gcc_checking_assert (uses);
+
+  bind = reg;
+  if (where == DEBUG_TEMP_BEFORE_WITH_VALUE)
+    {
+      rtx set = single_set (insn);
+      rtx dest, src;
+
+      if (set)
+	{
+	  dest = SET_DEST (set);
+	  src = SET_SRC (set);
+	  if (GET_CODE (src) == CALL)
+	    {
+	      while (uses)
+		{
+		  cur = uses->next;
+		  XDELETE (uses);
+		  uses = cur;
+		}
+	      return 0;
+	    }
+	}
+      else
+	set = NULL;
+
+      if (!set)
+	bind = NULL;
+      else if (dest == reg)
+	bind = copy_rtx (src);
+      else if (REG_P (dest))
+	{
+	  if (REGNO (dest) != REGNO (reg))
+	    bind = NULL;
+	  else
+	    bind = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      else if (GET_CODE (dest) == SUBREG)
+	{
+	  if (REGNO (SUBREG_REG (dest)) != REGNO (reg))
+	    bind = NULL;
+	  else if (!subreg_lowpart_p (dest))
+	    bind = NULL;
+	  else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
+		   && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
+		       == hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
+	    bind = NULL;
+	  else
+	    bind = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      else
+	bind = NULL;
+
+      if (!bind)
+	{
+	  dead_debug_reset_uses (debug, uses);
+	  return 0;
+	}
+    }
+
+  /* If there's a single (debug) use of an otherwise unused REG, and
+     the debug use is not part of a larger expression, then it
+     probably doesn't make sense to introduce a new debug temp.  */
+  if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next)
+    {
+      rtx next = DF_REF_INSN (uses->use);
+
+      if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
+	{
+	  XDELETE (uses);
+	  return 0;
+	}
+    }
 
   /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
   dval = make_debug_expr_from_rtl (reg);
 
   /* Emit a debug bind insn before the insn in which reg dies.  */
   bind = gen_rtx_VAR_LOCATION (GET_MODE (reg),
-			       DEBUG_EXPR_TREE_DECL (dval), reg,
+			       DEBUG_EXPR_TREE_DECL (dval), bind,
 			       VAR_INIT_STATUS_INITIALIZED);
 
-  bind = emit_debug_insn_before (bind, insn);
+  if (where == DEBUG_TEMP_AFTER_WITH_REG)
+    bind = emit_debug_insn_after (bind, insn);
+  else
+    bind = emit_debug_insn_before (bind, insn);
   df_insn_rescan (bind);
 
   /* Adjust all uses.  */
@@ -3201,6 +3291,8 @@ dead_debug_insert_before (struct dead_de
       uses = cur->next;
       XDELETE (cur);
     }
+
+  return 1;
 }
 
 /* Recompute the REG_DEAD and REG_UNUSED notes and compute register
@@ -3395,7 +3487,8 @@ df_note_bb_compute (unsigned int bb_inde
 		  break;
 		}
 	      else
-		dead_debug_insert_before (&debug, uregno, insn);
+		dead_debug_insert_temp (&debug, uregno, insn,
+					DEBUG_TEMP_BEFORE_WITH_REG);
 
 	      if ( (!(DF_REF_FLAGS (use)
 		      & (DF_REF_MW_HARDREG | DF_REF_READ_WRITE)))
Index: gcc/dce.c
===================================================================
--- gcc/dce.c.orig	2011-06-06 10:24:15.683249129 -0300
+++ gcc/dce.c	2011-06-06 10:25:17.919030159 -0300
@@ -807,6 +807,7 @@ word_dce_process_block (basic_block bb, 
   bitmap local_live = BITMAP_ALLOC (&dce_tmp_bitmap_obstack);
   rtx insn;
   bool block_changed;
+  struct dead_debug debug;
 
   if (redo_out)
     {
@@ -828,11 +829,24 @@ word_dce_process_block (basic_block bb, 
     }
 
   bitmap_copy (local_live, DF_WORD_LR_OUT (bb));
+  dead_debug_init (&debug, NULL);
 
   FOR_BB_INSNS_REVERSE (bb, insn)
-    if (NONDEBUG_INSN_P (insn))
+    if (DEBUG_INSN_P (insn))
+      {
+	df_ref *use_rec;
+	for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
+	  if (DF_REF_REGNO (*use_rec) >= FIRST_PSEUDO_REGISTER
+	      && (GET_MODE_SIZE (GET_MODE (DF_REF_REAL_REG (*use_rec)))
+		  == 2 * UNITS_PER_WORD)
+	      && !bitmap_bit_p (local_live, 2 * DF_REF_REGNO (*use_rec))
+	      && !bitmap_bit_p (local_live, 2 * DF_REF_REGNO (*use_rec) + 1))
+	    dead_debug_add (&debug, *use_rec, DF_REF_REGNO (*use_rec));
+      }
+    else if (INSN_P (insn))
       {
 	bool any_changed;
+
 	/* No matter if the instruction is needed or not, we remove
 	   any regno in the defs from the live set.  */
 	any_changed = df_word_lr_simulate_defs (insn, local_live);
@@ -844,6 +858,15 @@ word_dce_process_block (basic_block bb, 
 	if (marked_insn_p (insn))
 	  df_word_lr_simulate_uses (insn, local_live);
 
+	if (debug.used && !bitmap_empty_p (debug.used))
+	  {
+	    df_ref *def_rec;
+
+	    for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
+	      dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn,
+				      DEBUG_TEMP_BEFORE_WITH_VALUE);
+	  }
+
 	if (dump_file)
 	  {
 	    fprintf (dump_file, "finished processing insn %d live out = ",
@@ -856,6 +879,7 @@ word_dce_process_block (basic_block bb, 
   if (block_changed)
     bitmap_copy (DF_WORD_LR_IN (bb), local_live);
 
+  dead_debug_finish (&debug, NULL);
   BITMAP_FREE (local_live);
   return block_changed;
 }
@@ -873,6 +897,7 @@ dce_process_block (basic_block bb, bool 
   rtx insn;
   bool block_changed;
   df_ref *def_rec;
+  struct dead_debug debug;
 
   if (redo_out)
     {
@@ -896,22 +921,36 @@ dce_process_block (basic_block bb, bool 
   bitmap_copy (local_live, DF_LR_OUT (bb));
 
   df_simulate_initialize_backwards (bb, local_live);
+  dead_debug_init (&debug, NULL);
 
   FOR_BB_INSNS_REVERSE (bb, insn)
-    if (INSN_P (insn))
+    if (DEBUG_INSN_P (insn))
+      {
+	df_ref *use_rec;
+	for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
+	  if (!bitmap_bit_p (local_live, DF_REF_REGNO (*use_rec))
+	      && !bitmap_bit_p (au, DF_REF_REGNO (*use_rec)))
+	    dead_debug_add (&debug, *use_rec, DF_REF_REGNO (*use_rec));
+      }
+    else if (INSN_P (insn))
       {
 	bool needed = marked_insn_p (insn);
 
 	/* The insn is needed if there is someone who uses the output.  */
 	if (!needed)
 	  for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
-	    if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec))
-		|| bitmap_bit_p (au, DF_REF_REGNO (*def_rec)))
-	      {
-		needed = true;
-		mark_insn (insn, true);
-		break;
-	      }
+	    {
+	      dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn,
+				      DEBUG_TEMP_BEFORE_WITH_VALUE);
+
+	      if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec))
+		  || bitmap_bit_p (au, DF_REF_REGNO (*def_rec)))
+		{
+		  needed = true;
+		  mark_insn (insn, true);
+		  break;
+		}
+	    }
 
 	/* No matter if the instruction is needed or not, we remove
 	   any regno in the defs from the live set.  */
@@ -923,6 +962,7 @@ dce_process_block (basic_block bb, bool 
 	  df_simulate_uses (insn, local_live);
       }
 
+  dead_debug_finish (&debug, NULL);
   df_simulate_finalize_backwards (bb, local_live);
 
   block_changed = !bitmap_equal_p (local_live, DF_LR_IN (bb));
Index: gcc/df.h
===================================================================
--- gcc/df.h.orig	2011-06-03 11:41:26.946741443 -0300
+++ gcc/df.h	2011-06-06 10:25:17.981029941 -0300
@@ -1101,4 +1101,35 @@ extern void union_defs (df_ref, struct w
 			unsigned int *used, struct web_entry *,
 			bool (*fun) (struct web_entry *, struct web_entry *));
 
+/* Debug uses of dead regs.  */
+
+/* Node of a linked list of uses of dead REGs in debug insns.  */
+struct dead_debug_use
+{
+  df_ref use;
+  struct dead_debug_use *next;
+};
+
+/* Linked list of the above, with a bitmap of the REGs in the
+   list.  */
+struct dead_debug
+{
+  struct dead_debug_use *head;
+  bitmap used;
+  bitmap to_rescan;
+};
+
+enum debug_temp_where
+  {
+    DEBUG_TEMP_BEFORE_WITH_REG = -1,
+    DEBUG_TEMP_BEFORE_WITH_VALUE = 0,
+    DEBUG_TEMP_AFTER_WITH_REG = 1
+  };
+
+void dead_debug_init (struct dead_debug *, bitmap);
+void dead_debug_finish (struct dead_debug *, bitmap);
+void dead_debug_add (struct dead_debug *, df_ref, unsigned int);
+int dead_debug_insert_temp (struct dead_debug *, unsigned int, rtx,
+			    enum debug_temp_where);
+
 #endif /* GCC_DF_H */

[-- Attachment #3: Type: text/plain, Size: 258 bytes --]



-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH] Fix ICE in reset_unmarked_insns_debug_uses (PR middle-end/49308)
  2011-06-06 13:28           ` Alexandre Oliva
@ 2011-06-07 12:25             ` Jakub Jelinek
  2011-06-09  8:04               ` Jakub Jelinek
  2012-04-09  6:14             ` fix left-over debug insns in DCE Alexandre Oliva
  1 sibling, 1 reply; 23+ messages in thread
From: Jakub Jelinek @ 2011-06-07 12:25 UTC (permalink / raw)
  To: Eric Botcazou, Alexandre Oliva; +Cc: gcc-patches

On Mon, Jun 06, 2011 at 10:27:51AM -0300, Alexandre Oliva wrote:
> On Jun  6, 2011, Eric Botcazou <ebotcazou@adacore.com> wrote:
> 
> >> It might be too late for DF to do anything sensible to preserve the
> >> debug info rather than just throw it away, as your partial approval
> >> suggests.
> 
> > OK, let me think about this a little more.
> 
> >> Indeed, sorry, I misread it.
> 
> > Mind installing these bits separately?  My understanding is that they are 
> > independent correctness fixes.
> 
> Tested, installed.

This broke e.g. the attached testcase.
The problem is that after resetting+rescanning a debug insn
reset_unmarked_insns_debug_uses continued walking the DF chains for that
debug insn, but those were all invalidated by the
df_insn_rescan_debug_internal call.

Fixed thusly.  Additionally I've renamed a variable that was shadowing
a variable of the same name, which confused me quite a bit when debugging
it - I was expecting insn to be a DEBUG_INSN, while it was something
completely different.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2011-06-07  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/49308
	* dce.c (reset_unmarked_insns_debug_uses): Avoid shadowing insn
	variable.  After resetting and rescanning insn continue with previous
	statement.

	* gfortran.dg/pr49308.f90: New test.

--- gcc/dce.c.jj	2011-06-06 19:07:08.000000000 +0200
+++ gcc/dce.c	2011-06-07 11:08:12.000000000 +0200
@@ -514,11 +514,11 @@ reset_unmarked_insns_debug_uses (void)
 	      struct df_link *defs;
 	      for (defs = DF_REF_CHAIN (use); defs; defs = defs->next)
 		{
-		  rtx insn;
+		  rtx ref_insn;
 		  if (DF_REF_IS_ARTIFICIAL (defs->ref))
 		    continue;
-		  insn = DF_REF_INSN (defs->ref);
-		  if (!marked_insn_p (insn))
+		  ref_insn = DF_REF_INSN (defs->ref);
+		  if (!marked_insn_p (ref_insn))
 		    break;
 		}
 	      if (!defs)
@@ -527,6 +527,7 @@ reset_unmarked_insns_debug_uses (void)
 		 each of the DEFs?  */
 	      INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
 	      df_insn_rescan_debug_internal (insn);
+	      break;
 	    }
 	}
 }
--- gcc/testsuite/gfortran.dg/pr49308.f90.jj	2011-06-07 11:39:39.000000000 +0200
+++ gcc/testsuite/gfortran.dg/pr49308.f90	2011-06-07 11:30:25.000000000 +0200
@@ -0,0 +1,28 @@
+! PR middle-end/49308
+! { dg-do compile }
+! { dg-options "-O2 -funroll-loops -g" }
+
+subroutine foo(n, b, d, e)
+  type t
+    integer :: f
+  end type t
+  type s
+    type(t), pointer :: g
+  end type s
+  type u
+    type(s), dimension(:), pointer :: h
+  end type
+  integer :: i, k, n
+  type(u), pointer :: a, e
+  character(len=250) :: b, c, d
+  logical :: l
+  do i = 1, n
+    j = i - 1
+    if (j/=0) c = trim(b) // adjustl(d(j))
+  end do
+  a => e
+  do k = 1, size(a%h)
+     l = (a%h(k)%g%f == a%h(1)%g%f)
+     if (.not.(l)) call bar()
+  enddo
+end subroutine foo


	Jakub

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] Fix ICE in reset_unmarked_insns_debug_uses (PR middle-end/49308)
  2011-06-07 12:25             ` [PATCH] Fix ICE in reset_unmarked_insns_debug_uses (PR middle-end/49308) Jakub Jelinek
@ 2011-06-09  8:04               ` Jakub Jelinek
  0 siblings, 0 replies; 23+ messages in thread
From: Jakub Jelinek @ 2011-06-09  8:04 UTC (permalink / raw)
  To: Eric Botcazou, Alexandre Oliva; +Cc: gcc-patches

On Tue, Jun 07, 2011 at 02:24:49PM +0200, Jakub Jelinek wrote:
> Fixed thusly.  Additionally I've renamed a variable that was shadowing
> a variable of the same name, which confused me quite a bit when debugging
> it - I was expecting insn to be a DEBUG_INSN, while it was something
> completely different.
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

Alex said on IRC this looks obvious to him and I agree, so I've installed
it to trunk as such.

> 2011-06-07  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR middle-end/49308
> 	* dce.c (reset_unmarked_insns_debug_uses): Avoid shadowing insn
> 	variable.  After resetting and rescanning insn continue with previous
> 	statement.
> 
> 	* gfortran.dg/pr49308.f90: New test.
> 
> --- gcc/dce.c.jj	2011-06-06 19:07:08.000000000 +0200
> +++ gcc/dce.c	2011-06-07 11:08:12.000000000 +0200
> @@ -514,11 +514,11 @@ reset_unmarked_insns_debug_uses (void)
>  	      struct df_link *defs;
>  	      for (defs = DF_REF_CHAIN (use); defs; defs = defs->next)
>  		{
> -		  rtx insn;
> +		  rtx ref_insn;
>  		  if (DF_REF_IS_ARTIFICIAL (defs->ref))
>  		    continue;
> -		  insn = DF_REF_INSN (defs->ref);
> -		  if (!marked_insn_p (insn))
> +		  ref_insn = DF_REF_INSN (defs->ref);
> +		  if (!marked_insn_p (ref_insn))
>  		    break;
>  		}
>  	      if (!defs)
> @@ -527,6 +527,7 @@ reset_unmarked_insns_debug_uses (void)
>  		 each of the DEFs?  */
>  	      INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
>  	      df_insn_rescan_debug_internal (insn);
> +	      break;
>  	    }
>  	}
>  }
> --- gcc/testsuite/gfortran.dg/pr49308.f90.jj	2011-06-07 11:39:39.000000000 +0200
> +++ gcc/testsuite/gfortran.dg/pr49308.f90	2011-06-07 11:30:25.000000000 +0200
> @@ -0,0 +1,28 @@
> +! PR middle-end/49308
> +! { dg-do compile }
> +! { dg-options "-O2 -funroll-loops -g" }
> +
> +subroutine foo(n, b, d, e)
> +  type t
> +    integer :: f
> +  end type t
> +  type s
> +    type(t), pointer :: g
> +  end type s
> +  type u
> +    type(s), dimension(:), pointer :: h
> +  end type
> +  integer :: i, k, n
> +  type(u), pointer :: a, e
> +  character(len=250) :: b, c, d
> +  logical :: l
> +  do i = 1, n
> +    j = i - 1
> +    if (j/=0) c = trim(b) // adjustl(d(j))
> +  end do
> +  a => e
> +  do k = 1, size(a%h)
> +     l = (a%h(k)%g%f == a%h(1)%g%f)
> +     if (.not.(l)) call bar()
> +  enddo
> +end subroutine foo

	Jakub

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: fix left-over debug insns in DCE
  2011-06-06 13:28           ` Alexandre Oliva
  2011-06-07 12:25             ` [PATCH] Fix ICE in reset_unmarked_insns_debug_uses (PR middle-end/49308) Jakub Jelinek
@ 2012-04-09  6:14             ` Alexandre Oliva
  2012-04-09 13:42               ` Eric Botcazou
  1 sibling, 1 reply; 23+ messages in thread
From: Alexandre Oliva @ 2012-04-09  6:14 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 769 bytes --]

On Jun  6, 2011, Alexandre Oliva <aoliva@redhat.com> wrote:

> On Jun  6, 2011, Eric Botcazou <ebotcazou@adacore.com> wrote:
>>> It might be too late for DF to do anything sensible to preserve the
>>> debug info rather than just throw it away, as your partial approval
>>> suggests.

>> OK, let me think about this a little more.

Ping?

> Here are the remaining bits.

Some more context here: this enables DCE to turn removed insns into
debug temps when they're useful for debug info.  It further improves
debug info quality when installed along with the patch I just posted for
PR 48866.  Without it, a number of chains of debug temps that lead to a
real insn that gets deleted end up useless.

Regstrapped on x86_64-linux-gnu and i686-pc-linux-gnu.  Ok to install?


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: vta-dce-df-preserve-unused-dead-in-debug-temp-pr48866.patch --]
[-- Type: text/x-diff, Size: 17836 bytes --]

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

	PR debug/48866
	* df.h (enum debug_temp_where): New.
	(dead_debug_init, dead_debug_finish) Declare.
	(dead_debug_add, dead_debug_insert_temp): Declare.
	(struct dead_debug_use, struct dead_debug): Moved from...
	* df-problems.c: ... here.
	(df_set_unused_notes_for_mw): Bind debug uses of unused regno
	to a debug temp.
	(df_create_unused_note): Likewise.
	(df_set_dead_notes_for_mw): Move comment where it belongs.
	(dead_debug_init): Export.
	(dead_debug_reset_uses): New, factored out of...
	(dead_debug_finish): ...this.  Export.
	(dead_debug_reset): Remove.
	(dead_debug_add): Export.
	(dead_debug_insert_before): Rename to...
	(dead_debug_insert_temp): ... this.  Add where argument.  Export.
	Locate stored value for BEFORE_WITH_VALUE.  Avoid repeat inserts.
	Return insertion count.
	(df_note_bb_compute): Adjust.
	* dce.c (word_dce_process_block): Adjust dead debug uses.
	(dce_process_block): Likewise.

Index: gcc/df.h
===================================================================
--- gcc/df.h.orig	2012-03-01 04:26:27.926134403 -0300
+++ gcc/df.h	2012-03-26 11:19:12.300584463 -0300
@@ -1101,4 +1101,35 @@ extern void union_defs (df_ref, struct w
 			unsigned int *used, struct web_entry *,
 			bool (*fun) (struct web_entry *, struct web_entry *));
 
+/* Debug uses of dead regs.  */
+
+/* Node of a linked list of uses of dead REGs in debug insns.  */
+struct dead_debug_use
+{
+  df_ref use;
+  struct dead_debug_use *next;
+};
+
+/* Linked list of the above, with a bitmap of the REGs in the
+   list.  */
+struct dead_debug
+{
+  struct dead_debug_use *head;
+  bitmap used;
+  bitmap to_rescan;
+};
+
+enum debug_temp_where
+  {
+    DEBUG_TEMP_BEFORE_WITH_REG = -1,
+    DEBUG_TEMP_BEFORE_WITH_VALUE = 0,
+    DEBUG_TEMP_AFTER_WITH_REG = 1
+  };
+
+void dead_debug_init (struct dead_debug *, bitmap);
+void dead_debug_finish (struct dead_debug *, bitmap);
+void dead_debug_add (struct dead_debug *, df_ref, unsigned int);
+int dead_debug_insert_temp (struct dead_debug *, unsigned int, rtx,
+			    enum debug_temp_where);
+
 #endif /* GCC_DF_H */
Index: gcc/df-problems.c
===================================================================
--- gcc/df-problems.c.orig	2012-01-30 16:47:05.000000000 -0200
+++ gcc/df-problems.c	2012-03-26 11:49:17.848542873 -0300
@@ -2886,25 +2886,6 @@ df_whole_mw_reg_unused_p (struct df_mw_h
 }
 
 
-/* Node of a linked list of uses of dead REGs in debug insns.  */
-struct dead_debug_use
-{
-  df_ref use;
-  struct dead_debug_use *next;
-};
-
-/* Linked list of the above, with a bitmap of the REGs in the
-   list.  */
-struct dead_debug
-{
-  struct dead_debug_use *head;
-  bitmap used;
-  bitmap to_rescan;
-};
-
-static void dead_debug_reset (struct dead_debug *, unsigned int);
-
-
 /* Set the REG_UNUSED notes for the multiword hardreg defs in INSN
    based on the bits in LIVE.  Do not generate notes for registers in
    artificial uses.  DO_NOT_GEN is updated so that REG_DEAD notes are
@@ -2930,7 +2911,7 @@ df_set_unused_notes_for_mw (rtx insn, st
     {
       unsigned int regno = mws->start_regno;
       df_set_note (REG_UNUSED, insn, mws->mw_reg);
-      dead_debug_reset (debug, regno);
+      dead_debug_insert_temp (debug, regno, insn, DEBUG_TEMP_AFTER_WITH_REG);
 
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 1: ", insn, REG_NOTES (insn));
@@ -2945,7 +2926,7 @@ df_set_unused_notes_for_mw (rtx insn, st
 	    && !bitmap_bit_p (artificial_uses, r))
 	  {
 	    df_set_note (REG_UNUSED, insn, regno_reg_rtx[r]);
-	    dead_debug_reset (debug, r);
+	    dead_debug_insert_temp (debug, r, insn, DEBUG_TEMP_AFTER_WITH_REG);
 #ifdef REG_DEAD_DEBUGGING
 	    df_print_note ("adding 2: ", insn, REG_NOTES (insn));
 #endif
@@ -3013,12 +2994,12 @@ df_set_dead_notes_for_mw (rtx insn, stru
 
   if (df_whole_mw_reg_dead_p (mws, live, artificial_uses, do_not_gen))
     {
-      /* Add a dead note for the entire multi word register.  */
       if (is_debug)
 	{
 	  *added_notes_p = true;
 	  return;
 	}
+      /* Add a dead note for the entire multi word register.  */
       df_set_note (REG_DEAD, insn, mws->mw_reg);
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 1: ", insn, REG_NOTES (insn));
@@ -3072,7 +3053,7 @@ df_create_unused_note (rtx insn, df_ref 
       rtx reg = (DF_REF_LOC (def))
                 ? *DF_REF_REAL_LOC (def): DF_REF_REG (def);
       df_set_note (REG_UNUSED, insn, reg);
-      dead_debug_reset (debug, dregno);
+      dead_debug_insert_temp (debug, dregno, insn, DEBUG_TEMP_AFTER_WITH_REG);
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 3: ", insn, REG_NOTES (insn));
 #endif
@@ -3083,7 +3064,7 @@ df_create_unused_note (rtx insn, df_ref 
 
 
 /* Initialize DEBUG to an empty list, and clear USED, if given.  */
-static inline void
+void
 dead_debug_init (struct dead_debug *debug, bitmap used)
 {
   debug->head = NULL;
@@ -3093,32 +3074,83 @@ dead_debug_init (struct dead_debug *debu
     bitmap_clear (used);
 }
 
-/* Reset all debug insns with pending uses.  Release the bitmap in it,
-   unless it is USED.  USED must be the same bitmap passed to
-   dead_debug_init.  */
-static inline void
-dead_debug_finish (struct dead_debug *debug, bitmap used)
+/* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of
+   each reset insn.  DEBUG is not otherwise modified.  If HEAD is
+   DEBUG->head, DEBUG->head will be set to NULL at the end.
+   Otherwise, entries from DEBUG->head that pertain to reset insns
+   will be removed, and only then rescanned.  */
+
+static void
+dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
 {
-  struct dead_debug_use *head;
-  rtx insn = NULL;
+  bool got_head = (debug->head == head);
+  bitmap rescan;
+  struct dead_debug_use **tailp = &debug->head;
+  struct dead_debug_use *cur;
+  bitmap_iterator bi;
+  unsigned int uid;
 
-  if (debug->used != used)
-    BITMAP_FREE (debug->used);
+  if (got_head)
+    rescan = NULL;
+  else
+    rescan = BITMAP_ALLOC (NULL);
 
-  while ((head = debug->head))
+  while (head)
     {
+      struct dead_debug_use *next = head->next;
+      rtx insn;
+
       insn = DF_REF_INSN (head->use);
-      if (!head->next || DF_REF_INSN (head->next->use) != insn)
+      if (!next || DF_REF_INSN (next->use) != insn)
 	{
 	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-	  df_insn_rescan_debug_internal (insn);
+	  if (got_head)
+	    df_insn_rescan_debug_internal (insn);
+	  else
+	    bitmap_set_bit (rescan, INSN_UID (insn));
 	  if (debug->to_rescan)
 	    bitmap_clear_bit (debug->to_rescan, INSN_UID (insn));
 	}
-      debug->head = head->next;
       XDELETE (head);
+      head = next;
     }
 
+  if (got_head)
+    {
+      debug->head = NULL;
+      return;
+    }
+
+  while ((cur = *tailp))
+    if (bitmap_bit_p (rescan, INSN_UID (DF_REF_INSN (cur->use))))
+      {
+	*tailp = cur->next;
+	XDELETE (cur);
+      }
+    else
+      tailp = &cur->next;
+
+  EXECUTE_IF_SET_IN_BITMAP (rescan, 0, uid, bi)
+    {
+      struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
+      if (insn_info)
+	df_insn_rescan_debug_internal (insn_info->insn);
+    }
+
+  BITMAP_FREE (rescan);
+}
+
+/* Reset all debug insns with pending uses.  Release the bitmap in it,
+   unless it is USED.  USED must be the same bitmap passed to
+   dead_debug_init.  */
+void
+dead_debug_finish (struct dead_debug *debug, bitmap used)
+{
+  if (debug->used != used)
+    BITMAP_FREE (debug->used);
+
+  dead_debug_reset_uses (debug, debug->head);
+
   if (debug->to_rescan)
     {
       bitmap_iterator bi;
@@ -3134,54 +3166,9 @@ dead_debug_finish (struct dead_debug *de
     }
 }
 
-/* Reset DEBUG_INSNs with pending uses of DREGNO.  */
-static void
-dead_debug_reset (struct dead_debug *debug, unsigned int dregno)
-{
-  struct dead_debug_use **tailp = &debug->head;
-  struct dead_debug_use **insnp = &debug->head;
-  struct dead_debug_use *cur;
-  rtx insn;
-
-  if (!debug->used || !bitmap_clear_bit (debug->used, dregno))
-    return;
-
-  while ((cur = *tailp))
-    {
-      if (DF_REF_REGNO (cur->use) == dregno)
-	{
-	  *tailp = cur->next;
-	  insn = DF_REF_INSN (cur->use);
-	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-	  if (debug->to_rescan == NULL)
-	    debug->to_rescan = BITMAP_ALLOC (NULL);
-	  bitmap_set_bit (debug->to_rescan, INSN_UID (insn));
-	  XDELETE (cur);
-	  /* If the current use isn't the first one attached to INSN, go back
-	     to this first use.  We assume that the uses attached to an insn
-	     are adjacent.  */                                                                       
-	  if (tailp != insnp && DF_REF_INSN ((*insnp)->use) == insn)
-	    tailp = insnp;
-	  /* Then remove all the other uses attached to INSN.  */
-	  while ((cur = *tailp) && DF_REF_INSN (cur->use) == insn)
-	    {
-	      *tailp = cur->next;
-	      XDELETE (cur);
-	    }
-	  insnp = tailp;
-	}
-      else
-	{
-	  if (DF_REF_INSN ((*insnp)->use) != DF_REF_INSN (cur->use))
-	    insnp = tailp;
-	  tailp = &(*tailp)->next;
-	}
-    }
-}
-
 /* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
    insn.  Create a bitmap for DEBUG as needed.  */
-static inline void
+void
 dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
 {
   struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
@@ -3197,23 +3184,27 @@ dead_debug_add (struct dead_debug *debug
 }
 
 /* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
-   before INSN that binds the REG to a debug temp, and replace all
-   uses of UREGNO in DEBUG with uses of the debug temp.  INSN must be
-   the insn where UREGNO dies.  */
-static inline void
-dead_debug_insert_before (struct dead_debug *debug, unsigned int uregno,
-			  rtx insn)
+   before or after INSN (depending on WHERE), that binds a debug temp
+   to the widest-mode use of UREGNO, if WHERE is _WITH_REG, or
+   _WITH_VALUE otherwise, and replace all uses of UREGNO in DEBUG with
+   uses of the debug temp.  INSN must be where UREGNO dies, if WHERE
+   is DEAD_DEBUG_BEFORE_WITH_REG, or where it is set otherwise.
+   Return the number of debug insns emitted.  */
+int
+dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
+			rtx insn, enum debug_temp_where where)
 {
   struct dead_debug_use **tailp = &debug->head;
   struct dead_debug_use *cur;
   struct dead_debug_use *uses = NULL;
   struct dead_debug_use **usesp = &uses;
   rtx reg = NULL;
+  rtx breg;
   rtx dval;
   rtx bind;
 
   if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
-    return;
+    return 0;
 
   /* Move all uses of uregno from debug->head to uses, setting mode to
      the widest referenced mode.  */
@@ -3237,17 +3228,99 @@ dead_debug_insert_before (struct dead_de
   /* We may have dangling bits in debug->used for registers that were part
      of a multi-register use, one component of which has been reset.  */
   if (reg == NULL)
-    return;
+    {
+      gcc_checking_assert (!uses);
+      return 0;
+    }
+
+  gcc_checking_assert (uses);
+
+  breg = reg;
+  if (where == DEBUG_TEMP_BEFORE_WITH_VALUE)
+    {
+      rtx set = single_set (insn);
+      rtx dest, src;
+
+      if (set)
+	{
+	  dest = SET_DEST (set);
+	  src = SET_SRC (set);
+	  if (GET_CODE (src) == CALL)
+	    {
+	      while (uses)
+		{
+		  cur = uses->next;
+		  XDELETE (uses);
+		  uses = cur;
+		}
+	      return 0;
+	    }
+	}
+      else
+	set = NULL;
+
+      if (!set)
+	breg = NULL;
+      else if (dest == reg)
+	breg = copy_rtx (src);
+      else if (REG_P (dest))
+	{
+	  if (REGNO (dest) != REGNO (reg))
+	    breg = NULL;
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      else if (GET_CODE (dest) == SUBREG)
+	{
+	  if (REGNO (SUBREG_REG (dest)) != REGNO (reg))
+	    breg = NULL;
+	  else if (!subreg_lowpart_p (dest))
+	    breg = NULL;
+	  else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
+		   && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
+		       == hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
+	    breg = NULL;
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      else
+	breg = NULL;
+
+      if (!breg)
+	{
+	  dead_debug_reset_uses (debug, uses);
+	  return 0;
+	}
+    }
+
+  /* If there's a single (debug) use of an otherwise unused REG, and
+     the debug use is not part of a larger expression, then it
+     probably doesn't make sense to introduce a new debug temp.  */
+  if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next)
+    {
+      rtx next = DF_REF_INSN (uses->use);
+
+      if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
+	{
+	  XDELETE (uses);
+	  return 0;
+	}
+    }
 
   /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
   dval = make_debug_expr_from_rtl (reg);
 
   /* Emit a debug bind insn before the insn in which reg dies.  */
   bind = gen_rtx_VAR_LOCATION (GET_MODE (reg),
-			       DEBUG_EXPR_TREE_DECL (dval), reg,
+			       DEBUG_EXPR_TREE_DECL (dval), breg,
 			       VAR_INIT_STATUS_INITIALIZED);
 
-  bind = emit_debug_insn_before (bind, insn);
+  if (where == DEBUG_TEMP_AFTER_WITH_REG)
+    bind = emit_debug_insn_after (bind, insn);
+  else
+    bind = emit_debug_insn_before (bind, insn);
   df_insn_rescan (bind);
 
   /* Adjust all uses.  */
@@ -3265,6 +3338,8 @@ dead_debug_insert_before (struct dead_de
       uses = cur->next;
       XDELETE (cur);
     }
+
+  return 1;
 }
 
 /* Recompute the REG_DEAD and REG_UNUSED notes and compute register
@@ -3459,7 +3534,8 @@ df_note_bb_compute (unsigned int bb_inde
 		  break;
 		}
 	      else
-		dead_debug_insert_before (&debug, uregno, insn);
+		dead_debug_insert_temp (&debug, uregno, insn,
+					DEBUG_TEMP_BEFORE_WITH_REG);
 
 	      if ( (!(DF_REF_FLAGS (use)
 		      & (DF_REF_MW_HARDREG | DF_REF_READ_WRITE)))
Index: gcc/dce.c
===================================================================
--- gcc/dce.c.orig	2011-09-11 14:43:40.000000000 -0300
+++ gcc/dce.c	2012-03-26 11:19:12.271584817 -0300
@@ -807,6 +807,7 @@ word_dce_process_block (basic_block bb, 
   bitmap local_live = BITMAP_ALLOC (&dce_tmp_bitmap_obstack);
   rtx insn;
   bool block_changed;
+  struct dead_debug debug;
 
   if (redo_out)
     {
@@ -828,11 +829,24 @@ word_dce_process_block (basic_block bb, 
     }
 
   bitmap_copy (local_live, DF_WORD_LR_OUT (bb));
+  dead_debug_init (&debug, NULL);
 
   FOR_BB_INSNS_REVERSE (bb, insn)
-    if (NONDEBUG_INSN_P (insn))
+    if (DEBUG_INSN_P (insn))
+      {
+	df_ref *use_rec;
+	for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
+	  if (DF_REF_REGNO (*use_rec) >= FIRST_PSEUDO_REGISTER
+	      && (GET_MODE_SIZE (GET_MODE (DF_REF_REAL_REG (*use_rec)))
+		  == 2 * UNITS_PER_WORD)
+	      && !bitmap_bit_p (local_live, 2 * DF_REF_REGNO (*use_rec))
+	      && !bitmap_bit_p (local_live, 2 * DF_REF_REGNO (*use_rec) + 1))
+	    dead_debug_add (&debug, *use_rec, DF_REF_REGNO (*use_rec));
+      }
+    else if (INSN_P (insn))
       {
 	bool any_changed;
+
 	/* No matter if the instruction is needed or not, we remove
 	   any regno in the defs from the live set.  */
 	any_changed = df_word_lr_simulate_defs (insn, local_live);
@@ -844,6 +858,15 @@ word_dce_process_block (basic_block bb, 
 	if (marked_insn_p (insn))
 	  df_word_lr_simulate_uses (insn, local_live);
 
+	if (debug.used && !bitmap_empty_p (debug.used))
+	  {
+	    df_ref *def_rec;
+
+	    for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
+	      dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn,
+				      DEBUG_TEMP_BEFORE_WITH_VALUE);
+	  }
+
 	if (dump_file)
 	  {
 	    fprintf (dump_file, "finished processing insn %d live out = ",
@@ -856,6 +879,7 @@ word_dce_process_block (basic_block bb, 
   if (block_changed)
     bitmap_copy (DF_WORD_LR_IN (bb), local_live);
 
+  dead_debug_finish (&debug, NULL);
   BITMAP_FREE (local_live);
   return block_changed;
 }
@@ -873,6 +897,7 @@ dce_process_block (basic_block bb, bool 
   rtx insn;
   bool block_changed;
   df_ref *def_rec;
+  struct dead_debug debug;
 
   if (redo_out)
     {
@@ -896,22 +921,36 @@ dce_process_block (basic_block bb, bool 
   bitmap_copy (local_live, DF_LR_OUT (bb));
 
   df_simulate_initialize_backwards (bb, local_live);
+  dead_debug_init (&debug, NULL);
 
   FOR_BB_INSNS_REVERSE (bb, insn)
-    if (INSN_P (insn))
+    if (DEBUG_INSN_P (insn))
+      {
+	df_ref *use_rec;
+	for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
+	  if (!bitmap_bit_p (local_live, DF_REF_REGNO (*use_rec))
+	      && !bitmap_bit_p (au, DF_REF_REGNO (*use_rec)))
+	    dead_debug_add (&debug, *use_rec, DF_REF_REGNO (*use_rec));
+      }
+    else if (INSN_P (insn))
       {
 	bool needed = marked_insn_p (insn);
 
 	/* The insn is needed if there is someone who uses the output.  */
 	if (!needed)
 	  for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
-	    if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec))
-		|| bitmap_bit_p (au, DF_REF_REGNO (*def_rec)))
-	      {
-		needed = true;
-		mark_insn (insn, true);
-		break;
-	      }
+	    {
+	      dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn,
+				      DEBUG_TEMP_BEFORE_WITH_VALUE);
+
+	      if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec))
+		  || bitmap_bit_p (au, DF_REF_REGNO (*def_rec)))
+		{
+		  needed = true;
+		  mark_insn (insn, true);
+		  break;
+		}
+	    }
 
 	/* No matter if the instruction is needed or not, we remove
 	   any regno in the defs from the live set.  */
@@ -923,6 +962,7 @@ dce_process_block (basic_block bb, bool 
 	  df_simulate_uses (insn, local_live);
       }
 
+  dead_debug_finish (&debug, NULL);
   df_simulate_finalize_backwards (bb, local_live);
 
   block_changed = !bitmap_equal_p (local_live, DF_LR_IN (bb));

[-- Attachment #3: Type: text/plain, Size: 258 bytes --]



-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: fix left-over debug insns in DCE
  2012-04-09  6:14             ` fix left-over debug insns in DCE Alexandre Oliva
@ 2012-04-09 13:42               ` Eric Botcazou
  2012-04-13 15:59                 ` Alexandre Oliva
  0 siblings, 1 reply; 23+ messages in thread
From: Eric Botcazou @ 2012-04-09 13:42 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: gcc-patches

> Some more context here: this enables DCE to turn removed insns into
> debug temps when they're useful for debug info.  It further improves
> debug info quality when installed along with the patch I just posted for
> PR 48866.  Without it, a number of chains of debug temps that lead to a
> real insn that gets deleted end up useless.

> Regstrapped on x86_64-linux-gnu and i686-pc-linux-gnu.  Ok to install?

OK for mainline, modulo the following nits:

+enum debug_temp_where
+  {
+    DEBUG_TEMP_BEFORE_WITH_REG = -1,
+    DEBUG_TEMP_BEFORE_WITH_VALUE = 0,
+    DEBUG_TEMP_AFTER_WITH_REG = 1
+  };

Could you add a comment for each value?


+void dead_debug_init (struct dead_debug *, bitmap);
+void dead_debug_finish (struct dead_debug *, bitmap);
+void dead_debug_add (struct dead_debug *, df_ref, unsigned int);
+int dead_debug_insert_temp (struct dead_debug *, unsigned int, rtx,
+			    enum debug_temp_where);

Missing "extern" for all declarations.


 /* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
+   before or after INSN (depending on WHERE), that binds a debug temp
+   to the widest-mode use of UREGNO, if WHERE is _WITH_REG, or
+   _WITH_VALUE otherwise, and replace all uses of UREGNO in DEBUG with
+   uses of the debug temp.  INSN must be where UREGNO dies, if WHERE
+   is DEAD_DEBUG_BEFORE_WITH_REG, or where it is set otherwise.
+   Return the number of debug insns emitted.  */

I don't understand the "or _WITH_VALUE otherwise" part of the comment.


+  if (where == DEBUG_TEMP_BEFORE_WITH_VALUE)
+    {
+      rtx set = single_set (insn);
+      rtx dest, src;
+
+      if (set)
+	{
+	  dest = SET_DEST (set);
+	  src = SET_SRC (set);
+	  if (GET_CODE (src) == CALL)
+	    {
+	      while (uses)
+		{
+		  cur = uses->next;
+		  XDELETE (uses);
+		  uses = cur;
+		}
+	      return 0;
+	    }
+	}
+      else
+	set = NULL;
+
+      if (!set)
+	breg = NULL;
+      else if (dest == reg)
+	breg = copy_rtx (src);
+      else if (REG_P (dest))
+	{
+	  if (REGNO (dest) != REGNO (reg))
+	    breg = NULL;
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      else if (GET_CODE (dest) == SUBREG)
+	{
+	  if (REGNO (SUBREG_REG (dest)) != REGNO (reg))
+	    breg = NULL;
+	  else if (!subreg_lowpart_p (dest))
+	    breg = NULL;
+	  else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
+		   && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
+		       == hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
+	    breg = NULL;
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      else
+	breg = NULL;
+
+      if (!breg)
+	{
+	  dead_debug_reset_uses (debug, uses);
+	  return 0;
+	}
+    }

Please add a comment explaining what this is doing.

-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: fix left-over debug insns in DCE
  2012-04-09 13:42               ` Eric Botcazou
@ 2012-04-13 15:59                 ` Alexandre Oliva
  2012-04-13 16:45                   ` Paolo Bonzini
  0 siblings, 1 reply; 23+ messages in thread
From: Alexandre Oliva @ 2012-04-13 15:59 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 493 bytes --]

On Apr  9, 2012, Eric Botcazou <ebotcazou@adacore.com> wrote:

> Could you add a comment for each value?

Done

> Missing "extern" for all declarations.

Thanks, added.

> I don't understand the "or _WITH_VALUE otherwise" part of the comment.

Sorry, my bad.  It didn't make sense.  Fixed.

> Please add a comment explaining what this is doing.

How's this?

I've just installed the patch, but if you find the need for any further
improvement, let me know and I'll do it right away.

Thanks,


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: vta-dce-df-preserve-unused-dead-in-debug-temp-pr48866.patch --]
[-- Type: text/x-diff, Size: 19279 bytes --]

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

	PR debug/48866
	* df.h (enum debug_temp_where): New.
	(dead_debug_init, dead_debug_finish) Declare.
	(dead_debug_add, dead_debug_insert_temp): Declare.
	(struct dead_debug_use, struct dead_debug): Moved from...
	* df-problems.c: ... here.
	(df_set_unused_notes_for_mw): Bind debug uses of unused regno
	to a debug temp.
	(df_create_unused_note): Likewise.
	(df_set_dead_notes_for_mw): Move comment where it belongs.
	(dead_debug_init): Export.
	(dead_debug_reset_uses): New, factored out of...
	(dead_debug_finish): ...this.  Export.
	(dead_debug_reset): Remove.
	(dead_debug_add): Export.
	(dead_debug_insert_before): Rename to...
	(dead_debug_insert_temp): ... this.  Add where argument.  Export.
	Locate stored value for BEFORE_WITH_VALUE.  Avoid repeat inserts.
	Return insertion count.
	(df_note_bb_compute): Adjust.
	* dce.c (word_dce_process_block): Adjust dead debug uses.
	(dce_process_block): Likewise.

Index: gcc/df.h
===================================================================
--- gcc/df.h.orig	2012-04-13 05:18:29.140781640 -0300
+++ gcc/df.h	2012-04-13 07:13:18.000000000 -0300
@@ -1101,4 +1101,46 @@ extern void union_defs (df_ref, struct w
 			unsigned int *used, struct web_entry *,
 			bool (*fun) (struct web_entry *, struct web_entry *));
 
+/* Debug uses of dead regs.  */
+
+/* Node of a linked list of uses of dead REGs in debug insns.  */
+struct dead_debug_use
+{
+  df_ref use;
+  struct dead_debug_use *next;
+};
+
+/* Linked list of the above, with a bitmap of the REGs in the
+   list.  */
+struct dead_debug
+{
+  struct dead_debug_use *head;
+  bitmap used;
+  bitmap to_rescan;
+};
+
+/* This type controls the behavior of dead_debug_insert_temp WRT
+   UREGNO and INSN.  */
+enum debug_temp_where
+  {
+    /* Bind a newly-created debug temporary to a REG for UREGNO, and
+       insert the debug insn before INSN.  REG is expected to die at
+       INSN.  */
+    DEBUG_TEMP_BEFORE_WITH_REG = -1,
+    /* Bind a newly-created debug temporary to the value INSN stores
+       in REG, and insert the debug insn before INSN.  */
+    DEBUG_TEMP_BEFORE_WITH_VALUE = 0,
+    /* Bind a newly-created debug temporary to a REG for UREGNO, and
+       insert the debug insn after INSN.  REG is expected to be set at
+       INSN.  */
+    DEBUG_TEMP_AFTER_WITH_REG = 1
+  };
+
+extern void dead_debug_init (struct dead_debug *, bitmap);
+extern void dead_debug_finish (struct dead_debug *, bitmap);
+extern void dead_debug_add (struct dead_debug *, df_ref, unsigned int);
+extern int dead_debug_insert_temp (struct dead_debug *,
+				   unsigned int uregno, rtx insn,
+				   enum debug_temp_where);
+
 #endif /* GCC_DF_H */
Index: gcc/df-problems.c
===================================================================
--- gcc/df-problems.c.orig	2012-04-13 06:58:42.053258184 -0300
+++ gcc/df-problems.c	2012-04-13 07:29:33.000000000 -0300
@@ -2886,25 +2886,6 @@ df_whole_mw_reg_unused_p (struct df_mw_h
 }
 
 
-/* Node of a linked list of uses of dead REGs in debug insns.  */
-struct dead_debug_use
-{
-  df_ref use;
-  struct dead_debug_use *next;
-};
-
-/* Linked list of the above, with a bitmap of the REGs in the
-   list.  */
-struct dead_debug
-{
-  struct dead_debug_use *head;
-  bitmap used;
-  bitmap to_rescan;
-};
-
-static void dead_debug_reset (struct dead_debug *, unsigned int);
-
-
 /* Set the REG_UNUSED notes for the multiword hardreg defs in INSN
    based on the bits in LIVE.  Do not generate notes for registers in
    artificial uses.  DO_NOT_GEN is updated so that REG_DEAD notes are
@@ -2930,7 +2911,7 @@ df_set_unused_notes_for_mw (rtx insn, st
     {
       unsigned int regno = mws->start_regno;
       df_set_note (REG_UNUSED, insn, mws->mw_reg);
-      dead_debug_reset (debug, regno);
+      dead_debug_insert_temp (debug, regno, insn, DEBUG_TEMP_AFTER_WITH_REG);
 
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 1: ", insn, REG_NOTES (insn));
@@ -2945,7 +2926,7 @@ df_set_unused_notes_for_mw (rtx insn, st
 	    && !bitmap_bit_p (artificial_uses, r))
 	  {
 	    df_set_note (REG_UNUSED, insn, regno_reg_rtx[r]);
-	    dead_debug_reset (debug, r);
+	    dead_debug_insert_temp (debug, r, insn, DEBUG_TEMP_AFTER_WITH_REG);
 #ifdef REG_DEAD_DEBUGGING
 	    df_print_note ("adding 2: ", insn, REG_NOTES (insn));
 #endif
@@ -3013,12 +2994,12 @@ df_set_dead_notes_for_mw (rtx insn, stru
 
   if (df_whole_mw_reg_dead_p (mws, live, artificial_uses, do_not_gen))
     {
-      /* Add a dead note for the entire multi word register.  */
       if (is_debug)
 	{
 	  *added_notes_p = true;
 	  return;
 	}
+      /* Add a dead note for the entire multi word register.  */
       df_set_note (REG_DEAD, insn, mws->mw_reg);
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 1: ", insn, REG_NOTES (insn));
@@ -3072,7 +3053,7 @@ df_create_unused_note (rtx insn, df_ref 
       rtx reg = (DF_REF_LOC (def))
                 ? *DF_REF_REAL_LOC (def): DF_REF_REG (def);
       df_set_note (REG_UNUSED, insn, reg);
-      dead_debug_reset (debug, dregno);
+      dead_debug_insert_temp (debug, dregno, insn, DEBUG_TEMP_AFTER_WITH_REG);
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 3: ", insn, REG_NOTES (insn));
 #endif
@@ -3083,7 +3064,7 @@ df_create_unused_note (rtx insn, df_ref 
 
 
 /* Initialize DEBUG to an empty list, and clear USED, if given.  */
-static inline void
+void
 dead_debug_init (struct dead_debug *debug, bitmap used)
 {
   debug->head = NULL;
@@ -3093,32 +3074,83 @@ dead_debug_init (struct dead_debug *debu
     bitmap_clear (used);
 }
 
-/* Reset all debug insns with pending uses.  Release the bitmap in it,
-   unless it is USED.  USED must be the same bitmap passed to
-   dead_debug_init.  */
-static inline void
-dead_debug_finish (struct dead_debug *debug, bitmap used)
+/* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of
+   each reset insn.  DEBUG is not otherwise modified.  If HEAD is
+   DEBUG->head, DEBUG->head will be set to NULL at the end.
+   Otherwise, entries from DEBUG->head that pertain to reset insns
+   will be removed, and only then rescanned.  */
+
+static void
+dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
 {
-  struct dead_debug_use *head;
-  rtx insn = NULL;
+  bool got_head = (debug->head == head);
+  bitmap rescan;
+  struct dead_debug_use **tailp = &debug->head;
+  struct dead_debug_use *cur;
+  bitmap_iterator bi;
+  unsigned int uid;
 
-  if (debug->used != used)
-    BITMAP_FREE (debug->used);
+  if (got_head)
+    rescan = NULL;
+  else
+    rescan = BITMAP_ALLOC (NULL);
 
-  while ((head = debug->head))
+  while (head)
     {
+      struct dead_debug_use *next = head->next;
+      rtx insn;
+
       insn = DF_REF_INSN (head->use);
-      if (!head->next || DF_REF_INSN (head->next->use) != insn)
+      if (!next || DF_REF_INSN (next->use) != insn)
 	{
 	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-	  df_insn_rescan_debug_internal (insn);
+	  if (got_head)
+	    df_insn_rescan_debug_internal (insn);
+	  else
+	    bitmap_set_bit (rescan, INSN_UID (insn));
 	  if (debug->to_rescan)
 	    bitmap_clear_bit (debug->to_rescan, INSN_UID (insn));
 	}
-      debug->head = head->next;
       XDELETE (head);
+      head = next;
     }
 
+  if (got_head)
+    {
+      debug->head = NULL;
+      return;
+    }
+
+  while ((cur = *tailp))
+    if (bitmap_bit_p (rescan, INSN_UID (DF_REF_INSN (cur->use))))
+      {
+	*tailp = cur->next;
+	XDELETE (cur);
+      }
+    else
+      tailp = &cur->next;
+
+  EXECUTE_IF_SET_IN_BITMAP (rescan, 0, uid, bi)
+    {
+      struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
+      if (insn_info)
+	df_insn_rescan_debug_internal (insn_info->insn);
+    }
+
+  BITMAP_FREE (rescan);
+}
+
+/* Reset all debug insns with pending uses.  Release the bitmap in it,
+   unless it is USED.  USED must be the same bitmap passed to
+   dead_debug_init.  */
+void
+dead_debug_finish (struct dead_debug *debug, bitmap used)
+{
+  if (debug->used != used)
+    BITMAP_FREE (debug->used);
+
+  dead_debug_reset_uses (debug, debug->head);
+
   if (debug->to_rescan)
     {
       bitmap_iterator bi;
@@ -3134,54 +3166,9 @@ dead_debug_finish (struct dead_debug *de
     }
 }
 
-/* Reset DEBUG_INSNs with pending uses of DREGNO.  */
-static void
-dead_debug_reset (struct dead_debug *debug, unsigned int dregno)
-{
-  struct dead_debug_use **tailp = &debug->head;
-  struct dead_debug_use **insnp = &debug->head;
-  struct dead_debug_use *cur;
-  rtx insn;
-
-  if (!debug->used || !bitmap_clear_bit (debug->used, dregno))
-    return;
-
-  while ((cur = *tailp))
-    {
-      if (DF_REF_REGNO (cur->use) == dregno)
-	{
-	  *tailp = cur->next;
-	  insn = DF_REF_INSN (cur->use);
-	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-	  if (debug->to_rescan == NULL)
-	    debug->to_rescan = BITMAP_ALLOC (NULL);
-	  bitmap_set_bit (debug->to_rescan, INSN_UID (insn));
-	  XDELETE (cur);
-	  /* If the current use isn't the first one attached to INSN, go back
-	     to this first use.  We assume that the uses attached to an insn
-	     are adjacent.  */                                                                       
-	  if (tailp != insnp && DF_REF_INSN ((*insnp)->use) == insn)
-	    tailp = insnp;
-	  /* Then remove all the other uses attached to INSN.  */
-	  while ((cur = *tailp) && DF_REF_INSN (cur->use) == insn)
-	    {
-	      *tailp = cur->next;
-	      XDELETE (cur);
-	    }
-	  insnp = tailp;
-	}
-      else
-	{
-	  if (DF_REF_INSN ((*insnp)->use) != DF_REF_INSN (cur->use))
-	    insnp = tailp;
-	  tailp = &(*tailp)->next;
-	}
-    }
-}
-
 /* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
    insn.  Create a bitmap for DEBUG as needed.  */
-static inline void
+void
 dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
 {
   struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
@@ -3197,23 +3184,27 @@ dead_debug_add (struct dead_debug *debug
 }
 
 /* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
-   before INSN that binds the REG to a debug temp, and replace all
-   uses of UREGNO in DEBUG with uses of the debug temp.  INSN must be
-   the insn where UREGNO dies.  */
-static inline void
-dead_debug_insert_before (struct dead_debug *debug, unsigned int uregno,
-			  rtx insn)
+   before or after INSN (depending on WHERE), that binds a debug temp
+   to the widest-mode use of UREGNO, if WHERE is *_WITH_REG, or the
+   value stored in UREGNO by INSN otherwise, and replace all uses of
+   UREGNO in DEBUG with uses of the debug temp.  INSN must be where
+   UREGNO dies, if WHERE is *_BEFORE_*, or where it is set otherwise.
+   Return the number of debug insns emitted.  */
+int
+dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
+			rtx insn, enum debug_temp_where where)
 {
   struct dead_debug_use **tailp = &debug->head;
   struct dead_debug_use *cur;
   struct dead_debug_use *uses = NULL;
   struct dead_debug_use **usesp = &uses;
   rtx reg = NULL;
+  rtx breg;
   rtx dval;
   rtx bind;
 
   if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
-    return;
+    return 0;
 
   /* Move all uses of uregno from debug->head to uses, setting mode to
      the widest referenced mode.  */
@@ -3237,17 +3228,114 @@ dead_debug_insert_before (struct dead_de
   /* We may have dangling bits in debug->used for registers that were part
      of a multi-register use, one component of which has been reset.  */
   if (reg == NULL)
-    return;
+    {
+      gcc_checking_assert (!uses);
+      return 0;
+    }
+
+  gcc_checking_assert (uses);
+
+  breg = reg;
+  /* Recover the expression INSN stores in REG.  */
+  if (where == DEBUG_TEMP_BEFORE_WITH_VALUE)
+    {
+      rtx set = single_set (insn);
+      rtx dest, src;
+
+      if (set)
+	{
+	  dest = SET_DEST (set);
+	  src = SET_SRC (set);
+	  /* Lose if the REG-setting insn is a CALL.  */
+	  if (GET_CODE (src) == CALL)
+	    {
+	      while (uses)
+		{
+		  cur = uses->next;
+		  XDELETE (uses);
+		  uses = cur;
+		}
+	      return 0;
+	    }
+	}
+
+      /* ??? Should we try to extract it from a PARALLEL?  */
+      if (!set)
+	breg = NULL;
+      /* Cool, it's the same REG, we can use SRC.  */
+      else if (dest == reg)
+	breg = copy_rtx (src);
+      else if (REG_P (dest))
+	{
+	  /* Hmm...  Something's fishy, we should be setting REG here.  */
+	  if (REGNO (dest) != REGNO (reg))
+	    breg = NULL;
+	  /* Ok, it's the same (hardware) REG, but with a different
+	     mode, so SUBREG it.  */
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      else if (GET_CODE (dest) == SUBREG)
+	{
+	  /* We should be setting REG here.  Lose.  */
+	  if (REGNO (SUBREG_REG (dest)) != REGNO (reg))
+	    breg = NULL;
+	  /* Lose if we're setting something other than the lowpart of
+	     REG.  */
+	  else if (!subreg_lowpart_p (dest))
+	    breg = NULL;
+	  /* If we're not overwriting all the hardware registers that
+	     setting REG in its mode would, we won't know what to bind
+	     the debug temp to.  */
+	  else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
+		   && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
+		       != hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
+	    breg = NULL;
+	  /* Yay, we can use SRC, just adjust its mode.  */
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      /* Oh well, we're out of luck.  */
+      else
+	breg = NULL;
+
+      /* We couldn't figure out the value stored in REG, so reset all
+	 of its pending debug uses.  */
+      if (!breg)
+	{
+	  dead_debug_reset_uses (debug, uses);
+	  return 0;
+	}
+    }
+
+  /* If there's a single (debug) use of an otherwise unused REG, and
+     the debug use is not part of a larger expression, then it
+     probably doesn't make sense to introduce a new debug temp.  */
+  if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next)
+    {
+      rtx next = DF_REF_INSN (uses->use);
+
+      if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
+	{
+	  XDELETE (uses);
+	  return 0;
+	}
+    }
 
   /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
   dval = make_debug_expr_from_rtl (reg);
 
   /* Emit a debug bind insn before the insn in which reg dies.  */
   bind = gen_rtx_VAR_LOCATION (GET_MODE (reg),
-			       DEBUG_EXPR_TREE_DECL (dval), reg,
+			       DEBUG_EXPR_TREE_DECL (dval), breg,
 			       VAR_INIT_STATUS_INITIALIZED);
 
-  bind = emit_debug_insn_before (bind, insn);
+  if (where == DEBUG_TEMP_AFTER_WITH_REG)
+    bind = emit_debug_insn_after (bind, insn);
+  else
+    bind = emit_debug_insn_before (bind, insn);
   df_insn_rescan (bind);
 
   /* Adjust all uses.  */
@@ -3265,6 +3353,8 @@ dead_debug_insert_before (struct dead_de
       uses = cur->next;
       XDELETE (cur);
     }
+
+  return 1;
 }
 
 /* Recompute the REG_DEAD and REG_UNUSED notes and compute register
@@ -3464,7 +3554,8 @@ df_note_bb_compute (unsigned int bb_inde
 		  break;
 		}
 	      else
-		dead_debug_insert_before (&debug, uregno, insn);
+		dead_debug_insert_temp (&debug, uregno, insn,
+					DEBUG_TEMP_BEFORE_WITH_REG);
 
 	      if ( (!(DF_REF_FLAGS (use)
 		      & (DF_REF_MW_HARDREG | DF_REF_READ_WRITE)))
Index: gcc/dce.c
===================================================================
--- gcc/dce.c.orig	2012-04-13 05:18:28.604788011 -0300
+++ gcc/dce.c	2012-04-13 07:00:50.056647951 -0300
@@ -807,6 +807,7 @@ word_dce_process_block (basic_block bb, 
   bitmap local_live = BITMAP_ALLOC (&dce_tmp_bitmap_obstack);
   rtx insn;
   bool block_changed;
+  struct dead_debug debug;
 
   if (redo_out)
     {
@@ -828,11 +829,24 @@ word_dce_process_block (basic_block bb, 
     }
 
   bitmap_copy (local_live, DF_WORD_LR_OUT (bb));
+  dead_debug_init (&debug, NULL);
 
   FOR_BB_INSNS_REVERSE (bb, insn)
-    if (NONDEBUG_INSN_P (insn))
+    if (DEBUG_INSN_P (insn))
+      {
+	df_ref *use_rec;
+	for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
+	  if (DF_REF_REGNO (*use_rec) >= FIRST_PSEUDO_REGISTER
+	      && (GET_MODE_SIZE (GET_MODE (DF_REF_REAL_REG (*use_rec)))
+		  == 2 * UNITS_PER_WORD)
+	      && !bitmap_bit_p (local_live, 2 * DF_REF_REGNO (*use_rec))
+	      && !bitmap_bit_p (local_live, 2 * DF_REF_REGNO (*use_rec) + 1))
+	    dead_debug_add (&debug, *use_rec, DF_REF_REGNO (*use_rec));
+      }
+    else if (INSN_P (insn))
       {
 	bool any_changed;
+
 	/* No matter if the instruction is needed or not, we remove
 	   any regno in the defs from the live set.  */
 	any_changed = df_word_lr_simulate_defs (insn, local_live);
@@ -844,6 +858,15 @@ word_dce_process_block (basic_block bb, 
 	if (marked_insn_p (insn))
 	  df_word_lr_simulate_uses (insn, local_live);
 
+	if (debug.used && !bitmap_empty_p (debug.used))
+	  {
+	    df_ref *def_rec;
+
+	    for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
+	      dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn,
+				      DEBUG_TEMP_BEFORE_WITH_VALUE);
+	  }
+
 	if (dump_file)
 	  {
 	    fprintf (dump_file, "finished processing insn %d live out = ",
@@ -856,6 +879,7 @@ word_dce_process_block (basic_block bb, 
   if (block_changed)
     bitmap_copy (DF_WORD_LR_IN (bb), local_live);
 
+  dead_debug_finish (&debug, NULL);
   BITMAP_FREE (local_live);
   return block_changed;
 }
@@ -873,6 +897,7 @@ dce_process_block (basic_block bb, bool 
   rtx insn;
   bool block_changed;
   df_ref *def_rec;
+  struct dead_debug debug;
 
   if (redo_out)
     {
@@ -896,22 +921,36 @@ dce_process_block (basic_block bb, bool 
   bitmap_copy (local_live, DF_LR_OUT (bb));
 
   df_simulate_initialize_backwards (bb, local_live);
+  dead_debug_init (&debug, NULL);
 
   FOR_BB_INSNS_REVERSE (bb, insn)
-    if (INSN_P (insn))
+    if (DEBUG_INSN_P (insn))
+      {
+	df_ref *use_rec;
+	for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
+	  if (!bitmap_bit_p (local_live, DF_REF_REGNO (*use_rec))
+	      && !bitmap_bit_p (au, DF_REF_REGNO (*use_rec)))
+	    dead_debug_add (&debug, *use_rec, DF_REF_REGNO (*use_rec));
+      }
+    else if (INSN_P (insn))
       {
 	bool needed = marked_insn_p (insn);
 
 	/* The insn is needed if there is someone who uses the output.  */
 	if (!needed)
 	  for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
-	    if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec))
-		|| bitmap_bit_p (au, DF_REF_REGNO (*def_rec)))
-	      {
-		needed = true;
-		mark_insn (insn, true);
-		break;
-	      }
+	    {
+	      dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn,
+				      DEBUG_TEMP_BEFORE_WITH_VALUE);
+
+	      if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec))
+		  || bitmap_bit_p (au, DF_REF_REGNO (*def_rec)))
+		{
+		  needed = true;
+		  mark_insn (insn, true);
+		  break;
+		}
+	    }
 
 	/* No matter if the instruction is needed or not, we remove
 	   any regno in the defs from the live set.  */
@@ -923,6 +962,7 @@ dce_process_block (basic_block bb, bool 
 	  df_simulate_uses (insn, local_live);
       }
 
+  dead_debug_finish (&debug, NULL);
   df_simulate_finalize_backwards (bb, local_live);
 
   block_changed = !bitmap_equal_p (local_live, DF_LR_IN (bb));

[-- Attachment #3: Type: text/plain, Size: 258 bytes --]



-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: fix left-over debug insns in DCE
  2012-04-13 15:59                 ` Alexandre Oliva
@ 2012-04-13 16:45                   ` Paolo Bonzini
  2012-05-03 18:27                     ` [PR52983] eliminate autoinc from debug_insn locs (was: Re: fix left-over debug insns in DCE) Alexandre Oliva
  0 siblings, 1 reply; 23+ messages in thread
From: Paolo Bonzini @ 2012-04-13 16:45 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: Eric Botcazou, gcc-patches

Il 13/04/2012 17:58, Alexandre Oliva ha scritto:
> 
> I've just installed the patch, but if you find the need for any further
> improvement, let me know and I'll do it right away.

I wonder if it makes any sense to move the dead_debug_* stuff to its own
file...

Paolo

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PR52983] eliminate autoinc from debug_insn locs         (was: Re: fix left-over debug insns in DCE)
  2012-04-13 16:45                   ` Paolo Bonzini
@ 2012-05-03 18:27                     ` Alexandre Oliva
  2012-05-03 22:40                       ` [PR52983] eliminate autoinc from debug_insn locs Alexandre Oliva
  2012-05-03 22:46                       ` [PR52983, PR48866] " Alexandre Oliva
  0 siblings, 2 replies; 23+ messages in thread
From: Alexandre Oliva @ 2012-05-03 18:27 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: Eric Botcazou, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 816 bytes --]

My recent patch for PR48866, that introduced dead_debug_insert_temp()
with DEBUG_TEMP_BEFORE_WITH_VALUE as a possibility for keeping
expressions about to be DCE'd, caused regressions on ppc because it
would take MEMs with autoinc addressing modes, which would be rejected
down the road.

This patch arranges for locs to have autoinc addressing modes eliminated
while copying them for use in the debug insns, fixing the problem.

On Apr 13, 2012, Paolo Bonzini <bonzini@gnu.org> wrote:

> I wonder if it makes any sense to move the dead_debug_* stuff to its own
> file...

Yeah, that sounds like a reasonable idea, so this patch moves all this
value-tracking stuff to valtrack.[ch].

Regstrapped on x86_64-linux-gnu and i686-linux-gnu; verified to fix the
reported ppc problem with a cross compiler.  Ok to install?


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: vta-valtrack-pr52983.patch --]
[-- Type: text/x-diff, Size: 42000 bytes --]

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

	PR debug/52983
	* valtrack.h, valtrack.c: New.
	* Makefile.in (VALTRACK_H): New.
	(OBJS): Add valtrack.o.
	(valtrack.o): New.
	(cselib.o, dce.o, df-problems.o, combine.o): Add VALTRACK_H.
	* combine.c: Include valtrack.h.
	(make_compound_operation): Publish.
	(cleanup_auto_inc_dec): Move to valtrack.c.  Implement
	unconditionally, falling back to copy_rtx on non-autoinc machines.
	(struct rtx_subst_pair, propagate_for_debug_subst): Move to
	valtrack.c.
	(propagate_for_debug): Likewise.  Add this_basic_block parameter.
	Adjust all callers.
	* cselib.c: Include valtrack.h.
	* dce.c: Likewise.
	* df-problems.c: Likewise.
	(dead_debug_init, dead_debug_reset_uses): Move to valtrack.c.
	(dead_debug_finish, dead_debug_add): Likewise.
	(dead_debug_insert_temp): Likewise.  Use cleanup_auto_inc_dec.
	* df.h (struct dead_debug_use): Move to valtrack.h.
	(struct dead_debug, enum debug_temp_where): Likewise.
	(dead_debug_init, dead_debug_reset_uses): Move to valtrack.h.
	(dead_debug_finish, dead_debug_add): Likewise.
	(dead_debug_insert_temp): Likewise.
	* rtl.h (make_compound_operation): Declare.

Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in.orig	2012-04-30 08:16:19.496079627 -0300
+++ gcc/Makefile.in	2012-04-30 08:41:04.512982838 -0300
@@ -894,6 +894,7 @@ CGRAPH_H = cgraph.h $(VEC_H) $(TREE_H) $
 	cif-code.def ipa-ref.h ipa-ref-inline.h $(LINKER_PLUGIN_API_H)
 DF_H = df.h $(BITMAP_H) $(REGSET_H) sbitmap.h $(BASIC_BLOCK_H) \
 	alloc-pool.h $(TIMEVAR_H)
+VALTRACK_H = valtrack.h $(BITMAP_H) $(DF_H) $(RTL_H) $(BASIC_BLOCK_H)
 RESOURCE_H = resource.h hard-reg-set.h $(DF_H)
 DDG_H = ddg.h sbitmap.h $(DF_H)
 GCC_H = gcc.h version.h $(DIAGNOSTIC_CORE_H)
@@ -1437,6 +1438,7 @@ OBJS = \
 	tree-vectorizer.o \
 	tree-vrp.o \
 	tree.o \
+	valtrack.o \
 	value-prof.o \
 	var-tracking.o \
 	varasm.o \
@@ -3002,8 +3004,8 @@ coverage.o : coverage.c $(GCOV_IO_H) $(C
 cselib.o : cselib.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h $(RECOG_H) \
    $(EMIT_RTL_H) $(DIAGNOSTIC_CORE_H) output.h $(FUNCTION_H) $(TREE_PASS_H) \
-   cselib.h gt-cselib.h $(GGC_H) $(TM_P_H) $(PARAMS_H) alloc-pool.h \
-   $(HASHTAB_H) $(TARGET_H) $(BITMAP_H)
+   cselib.h gt-cselib.h $(GGC_H) $(TM_P_H) $(VALTRACK_H) $(PARAMS_H) \
+   alloc-pool.h $(HASHTAB_H) $(TARGET_H) $(BITMAP_H)
 cse.o : cse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \
    hard-reg-set.h $(FLAGS_H) insn-config.h $(RECOG_H) $(EXPR_H) toplev.h $(DIAGNOSTIC_CORE_H) \
    output.h $(FUNCTION_H) $(BASIC_BLOCK_H) $(GGC_H) $(TM_P_H) $(TIMEVAR_H) \
@@ -3011,8 +3013,8 @@ cse.o : cse.c $(CONFIG_H) $(SYSTEM_H) co
    $(DF_H) $(DBGCNT_H)
 dce.o : dce.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(TREE_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) $(EXCEPT_H) $(DF_H) cselib.h \
-   $(DBGCNT_H) dce.h $(TIMEVAR_H) $(TREE_PASS_H) $(DBGCNT_H) $(TM_P_H) \
-   $(EMIT_RTL_H)
+   $(DBGCNT_H) dce.h $(VALTRACK_H) $(TIMEVAR_H) $(TREE_PASS_H) \
+   $(DBGCNT_H) $(TM_P_H) $(EMIT_RTL_H)
 dse.o : dse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(TREE_H) $(TM_P_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h \
    $(RECOG_H) $(EXPR_H) $(DF_H) cselib.h $(DBGCNT_H) $(TIMEVAR_H) \
@@ -3104,7 +3106,8 @@ df-core.o : df-core.c $(CONFIG_H) $(SYST
 df-problems.o : df-problems.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(RTL_H) insn-config.h $(RECOG_H) $(FUNCTION_H) $(REGS_H) alloc-pool.h \
    hard-reg-set.h $(BASIC_BLOCK_H) $(DF_H) $(BITMAP_H) sbitmap.h $(TIMEVAR_H) \
-   $(TM_P_H) $(TARGET_H) $(FLAGS_H) output.h $(EXCEPT_H) dce.h vecprim.h
+   $(TM_P_H) $(TARGET_H) $(FLAGS_H) output.h $(EXCEPT_H) dce.h \
+   vecprim.h $(VALTRACK_H)
 df-scan.o : df-scan.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    insn-config.h $(RECOG_H) $(FUNCTION_H) $(REGS_H) alloc-pool.h \
    hard-reg-set.h $(BASIC_BLOCK_H) $(DF_H) $(BITMAP_H) sbitmap.h $(TIMEVAR_H) \
@@ -3113,6 +3116,8 @@ df-scan.o : df-scan.c $(CONFIG_H) $(SYST
 regstat.o : regstat.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(TM_P_H) $(FLAGS_H) $(REGS_H) output.h $(EXCEPT_H) hard-reg-set.h \
    $(BASIC_BLOCK_H) $(TIMEVAR_H) $(DF_H)
+valtrack.o : valtrack.c $(VALTRACK_H) $(CONFIG_H) $(SYSTEM_H) \
+   coretypes.h $(TM_H) $(FUNCTION_H) $(REGS_H) $(EMIT_RTL_H)
 var-tracking.o : var-tracking.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(RTL_H) $(TREE_H) hard-reg-set.h insn-config.h reload.h $(FLAGS_H) \
    $(BASIC_BLOCK_H) output.h sbitmap.h alloc-pool.h $(FIBHEAP_H) $(HASHTAB_H) \
@@ -3212,9 +3217,10 @@ et-forest.o : et-forest.c $(CONFIG_H) $(
 combine.o : combine.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(FLAGS_H) $(FUNCTION_H) insn-config.h $(INSN_ATTR_H) $(REGS_H) $(EXPR_H) \
    rtlhooks-def.h $(BASIC_BLOCK_H) $(RECOG_H) hard-reg-set.h \
-   $(DIAGNOSTIC_CORE_H) $(TM_P_H) $(TREE_H) $(TARGET_H) output.h $(PARAMS_H) $(OPTABS_H) \
-   insn-codes.h $(TIMEVAR_H) $(TREE_PASS_H) $(DF_H) vecprim.h $(CGRAPH_H) \
-   $(OBSTACK_H)
+   $(DIAGNOSTIC_CORE_H) $(TM_P_H) $(TREE_H) $(TARGET_H) \
+   output.h $(PARAMS_H) $(OPTABS_H) \
+   insn-codes.h $(TIMEVAR_H) $(TREE_PASS_H) $(DF_H) $(VALTRACK_H) \
+   vecprim.h $(CGRAPH_H) $(OBSTACK_H)
 reginfo.o : reginfo.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    hard-reg-set.h $(FLAGS_H) $(BASIC_BLOCK_H) addresses.h $(REGS_H) \
    insn-config.h $(RECOG_H) reload.h $(DIAGNOSTIC_CORE_H) \
Index: gcc/combine.c
===================================================================
--- gcc/combine.c.orig	2012-04-30 08:06:34.341809142 -0300
+++ gcc/combine.c	2012-04-30 08:42:48.167436366 -0300
@@ -103,6 +103,7 @@ along with GCC; see the file COPYING3.  
 #include "timevar.h"
 #include "tree-pass.h"
 #include "df.h"
+#include "valtrack.h"
 #include "cgraph.h"
 #include "obstack.h"
 
@@ -427,7 +428,6 @@ static const_rtx expand_field_assignment
 static rtx make_extraction (enum machine_mode, rtx, HOST_WIDE_INT,
 			    rtx, unsigned HOST_WIDE_INT, int, int, int);
 static rtx extract_left_shift (rtx, int);
-static rtx make_compound_operation (rtx, enum rtx_code);
 static int get_pos_from_mask (unsigned HOST_WIDE_INT,
 			      unsigned HOST_WIDE_INT *);
 static rtx canon_reg_for_combine (rtx, rtx);
@@ -2360,161 +2360,6 @@ reg_subword_p (rtx x, rtx reg)
 	 && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT;
 }
 
-#ifdef AUTO_INC_DEC
-/* Replace auto-increment addressing modes with explicit operations to access
-   the same addresses without modifying the corresponding registers.  */
-
-static rtx
-cleanup_auto_inc_dec (rtx src, enum machine_mode mem_mode)
-{
-  rtx x = src;
-  const RTX_CODE code = GET_CODE (x);
-  int i;
-  const char *fmt;
-
-  switch (code)
-    {
-    case REG:
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case CONST_FIXED:
-    case CONST_VECTOR:
-    case SYMBOL_REF:
-    case CODE_LABEL:
-    case PC:
-    case CC0:
-    case SCRATCH:
-      /* SCRATCH must be shared because they represent distinct values.  */
-      return x;
-    case CLOBBER:
-      if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)
-	return x;
-      break;
-
-    case CONST:
-      if (shared_const_p (x))
-	return x;
-      break;
-
-    case MEM:
-      mem_mode = GET_MODE (x);
-      break;
-
-    case PRE_INC:
-    case PRE_DEC:
-      gcc_assert (mem_mode != VOIDmode && mem_mode != BLKmode);
-      return gen_rtx_PLUS (GET_MODE (x),
-			   cleanup_auto_inc_dec (XEXP (x, 0), mem_mode),
-			   GEN_INT (code == PRE_INC
-				    ? GET_MODE_SIZE (mem_mode)
-				    : -GET_MODE_SIZE (mem_mode)));
-
-    case POST_INC:
-    case POST_DEC:
-    case PRE_MODIFY:
-    case POST_MODIFY:
-      return cleanup_auto_inc_dec (code == PRE_MODIFY
-				   ? XEXP (x, 1) : XEXP (x, 0),
-				   mem_mode);
-
-    default:
-      break;
-    }
-
-  /* Copy the various flags, fields, and other information.  We assume
-     that all fields need copying, and then clear the fields that should
-     not be copied.  That is the sensible default behavior, and forces
-     us to explicitly document why we are *not* copying a flag.  */
-  x = shallow_copy_rtx (x);
-
-  /* We do not copy the USED flag, which is used as a mark bit during
-     walks over the RTL.  */
-  RTX_FLAG (x, used) = 0;
-
-  /* We do not copy FRAME_RELATED for INSNs.  */
-  if (INSN_P (x))
-    RTX_FLAG (x, frame_related) = 0;
-
-  fmt = GET_RTX_FORMAT (code);
-  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
-    if (fmt[i] == 'e')
-      XEXP (x, i) = cleanup_auto_inc_dec (XEXP (x, i), mem_mode);
-    else if (fmt[i] == 'E' || fmt[i] == 'V')
-      {
-	int j;
-	XVEC (x, i) = rtvec_alloc (XVECLEN (x, i));
-	for (j = 0; j < XVECLEN (x, i); j++)
-	  XVECEXP (x, i, j)
-	    = cleanup_auto_inc_dec (XVECEXP (src, i, j), mem_mode);
-      }
-
-  return x;
-}
-#endif
-
-/* Auxiliary data structure for propagate_for_debug_stmt.  */
-
-struct rtx_subst_pair
-{
-  rtx to;
-  bool adjusted;
-};
-
-/* DATA points to an rtx_subst_pair.  Return the value that should be
-   substituted.  */
-
-static rtx
-propagate_for_debug_subst (rtx from, const_rtx old_rtx, void *data)
-{
-  struct rtx_subst_pair *pair = (struct rtx_subst_pair *)data;
-
-  if (!rtx_equal_p (from, old_rtx))
-    return NULL_RTX;
-  if (!pair->adjusted)
-    {
-      pair->adjusted = true;
-#ifdef AUTO_INC_DEC
-      pair->to = cleanup_auto_inc_dec (pair->to, VOIDmode);
-#else
-      pair->to = copy_rtx (pair->to);
-#endif
-      pair->to = make_compound_operation (pair->to, SET);
-      return pair->to;
-    }
-  return copy_rtx (pair->to);
-}
-
-/* Replace all the occurrences of DEST with SRC in DEBUG_INSNs between INSN
-   and LAST, not including INSN, but including LAST.  Also stop at the end
-   of THIS_BASIC_BLOCK.  */
-
-static void
-propagate_for_debug (rtx insn, rtx last, rtx dest, rtx src)
-{
-  rtx next, loc, end = NEXT_INSN (BB_END (this_basic_block));
-
-  struct rtx_subst_pair p;
-  p.to = src;
-  p.adjusted = false;
-
-  next = NEXT_INSN (insn);
-  last = NEXT_INSN (last);
-  while (next != last && next != end)
-    {
-      insn = next;
-      next = NEXT_INSN (insn);
-      if (DEBUG_INSN_P (insn))
-	{
-	  loc = simplify_replace_fn_rtx (INSN_VAR_LOCATION_LOC (insn),
-					 dest, propagate_for_debug_subst, &p);
-	  if (loc == INSN_VAR_LOCATION_LOC (insn))
-	    continue;
-	  INSN_VAR_LOCATION_LOC (insn) = loc;
-	  df_insn_rescan (insn);
-	}
-    }
-}
-
 /* Delete the unconditional jump INSN and adjust the CFG correspondingly.
    Note that the INSN should be deleted *after* removing dead edges, so
    that the kept edge is the fallthrough edge for a (set (pc) (pc))
@@ -3974,7 +3819,8 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
 		   i2src while its original mode is temporarily
 		   restored, and then clear i2scratch so that we don't
 		   do it again later.  */
-		propagate_for_debug (i2, last_combined_insn, reg, i2src);
+		propagate_for_debug (i2, last_combined_insn, reg, i2src,
+				     this_basic_block);
 		i2scratch = false;
 		/* Put back the new mode.  */
 		adjust_reg_mode (reg, new_mode);
@@ -4008,10 +3854,12 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
 		   with this copy we have created; then, replace the
 		   copy with the SUBREG of the original shared reg,
 		   once again changed to the new mode.  */
-		propagate_for_debug (first, last, reg, tempreg);
+		propagate_for_debug (first, last, reg, tempreg,
+				     this_basic_block);
 		adjust_reg_mode (reg, new_mode);
 		propagate_for_debug (first, last, tempreg,
-				     lowpart_subreg (old_mode, reg, new_mode));
+				     lowpart_subreg (old_mode, reg, new_mode),
+				     this_basic_block);
 	      }
 	  }
     }
@@ -4223,14 +4071,16 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
     if (newi2pat)
       {
 	if (MAY_HAVE_DEBUG_INSNS && i2scratch)
-	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src);
+	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src,
+			       this_basic_block);
 	INSN_CODE (i2) = i2_code_number;
 	PATTERN (i2) = newi2pat;
       }
     else
       {
 	if (MAY_HAVE_DEBUG_INSNS && i2src)
-	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src);
+	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src,
+			       this_basic_block);
 	SET_INSN_DELETED (i2);
       }
 
@@ -4239,7 +4089,8 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
 	LOG_LINKS (i1) = NULL;
 	REG_NOTES (i1) = 0;
 	if (MAY_HAVE_DEBUG_INSNS)
-	  propagate_for_debug (i1, last_combined_insn, i1dest, i1src);
+	  propagate_for_debug (i1, last_combined_insn, i1dest, i1src,
+			       this_basic_block);
 	SET_INSN_DELETED (i1);
       }
 
@@ -4248,7 +4099,8 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
 	LOG_LINKS (i0) = NULL;
 	REG_NOTES (i0) = 0;
 	if (MAY_HAVE_DEBUG_INSNS)
-	  propagate_for_debug (i0, last_combined_insn, i0dest, i0src);
+	  propagate_for_debug (i0, last_combined_insn, i0dest, i0src,
+			       this_basic_block);
 	SET_INSN_DELETED (i0);
       }
 
@@ -7600,7 +7452,7 @@ extract_left_shift (rtx x, int count)
    being kludges), it is MEM.  When processing the arguments of a comparison
    or a COMPARE against zero, it is COMPARE.  */
 
-static rtx
+rtx
 make_compound_operation (rtx x, enum rtx_code in_code)
 {
   enum rtx_code code = GET_CODE (x);
Index: gcc/cselib.c
===================================================================
--- gcc/cselib.c.orig	2012-04-30 08:00:48.174996299 -0300
+++ gcc/cselib.c	2012-04-30 08:01:49.085083851 -0300
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  
 #include "hashtab.h"
 #include "tree-pass.h"
 #include "cselib.h"
+#include "valtrack.h"
 #include "params.h"
 #include "alloc-pool.h"
 #include "target.h"
Index: gcc/dce.c
===================================================================
--- gcc/dce.c.orig	2012-04-30 08:00:47.349008672 -0300
+++ gcc/dce.c	2012-04-30 08:01:30.658359900 -0300
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  
 #include "df.h"
 #include "cselib.h"
 #include "dce.h"
+#include "valtrack.h"
 #include "timevar.h"
 #include "tree-pass.h"
 #include "dbgcnt.h"
Index: gcc/df-problems.c
===================================================================
--- gcc/df-problems.c.orig	2012-04-30 08:00:47.823001572 -0300
+++ gcc/df-problems.c	2012-04-30 08:13:55.575214401 -0300
@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  
 #include "except.h"
 #include "dce.h"
 #include "vecprim.h"
+#include "valtrack.h"
 
 /* Note that turning REG_DEAD_DEBUGGING on will cause
    gcc.c-torture/unsorted/dump-noaddr.c to fail because it prints
@@ -3063,300 +3064,6 @@ df_create_unused_note (rtx insn, df_ref 
 }
 
 
-/* Initialize DEBUG to an empty list, and clear USED, if given.  */
-void
-dead_debug_init (struct dead_debug *debug, bitmap used)
-{
-  debug->head = NULL;
-  debug->used = used;
-  debug->to_rescan = NULL;
-  if (used)
-    bitmap_clear (used);
-}
-
-/* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of
-   each reset insn.  DEBUG is not otherwise modified.  If HEAD is
-   DEBUG->head, DEBUG->head will be set to NULL at the end.
-   Otherwise, entries from DEBUG->head that pertain to reset insns
-   will be removed, and only then rescanned.  */
-
-static void
-dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
-{
-  bool got_head = (debug->head == head);
-  bitmap rescan;
-  struct dead_debug_use **tailp = &debug->head;
-  struct dead_debug_use *cur;
-  bitmap_iterator bi;
-  unsigned int uid;
-
-  if (got_head)
-    rescan = NULL;
-  else
-    rescan = BITMAP_ALLOC (NULL);
-
-  while (head)
-    {
-      struct dead_debug_use *next = head->next;
-      rtx insn;
-
-      insn = DF_REF_INSN (head->use);
-      if (!next || DF_REF_INSN (next->use) != insn)
-	{
-	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-	  if (got_head)
-	    df_insn_rescan_debug_internal (insn);
-	  else
-	    bitmap_set_bit (rescan, INSN_UID (insn));
-	  if (debug->to_rescan)
-	    bitmap_clear_bit (debug->to_rescan, INSN_UID (insn));
-	}
-      XDELETE (head);
-      head = next;
-    }
-
-  if (got_head)
-    {
-      debug->head = NULL;
-      return;
-    }
-
-  while ((cur = *tailp))
-    if (bitmap_bit_p (rescan, INSN_UID (DF_REF_INSN (cur->use))))
-      {
-	*tailp = cur->next;
-	XDELETE (cur);
-      }
-    else
-      tailp = &cur->next;
-
-  EXECUTE_IF_SET_IN_BITMAP (rescan, 0, uid, bi)
-    {
-      struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
-      if (insn_info)
-	df_insn_rescan_debug_internal (insn_info->insn);
-    }
-
-  BITMAP_FREE (rescan);
-}
-
-/* Reset all debug insns with pending uses.  Release the bitmap in it,
-   unless it is USED.  USED must be the same bitmap passed to
-   dead_debug_init.  */
-void
-dead_debug_finish (struct dead_debug *debug, bitmap used)
-{
-  if (debug->used != used)
-    BITMAP_FREE (debug->used);
-
-  dead_debug_reset_uses (debug, debug->head);
-
-  if (debug->to_rescan)
-    {
-      bitmap_iterator bi;
-      unsigned int uid;
-
-      EXECUTE_IF_SET_IN_BITMAP (debug->to_rescan, 0, uid, bi)
-	{
-	  struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
-	  if (insn_info)
-	    df_insn_rescan (insn_info->insn);
-	}
-      BITMAP_FREE (debug->to_rescan);
-    }
-}
-
-/* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
-   insn.  Create a bitmap for DEBUG as needed.  */
-void
-dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
-{
-  struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
-
-  newddu->use = use;
-  newddu->next = debug->head;
-  debug->head = newddu;
-
-  if (!debug->used)
-    debug->used = BITMAP_ALLOC (NULL);
-
-  bitmap_set_bit (debug->used, uregno);
-}
-
-/* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
-   before or after INSN (depending on WHERE), that binds a debug temp
-   to the widest-mode use of UREGNO, if WHERE is *_WITH_REG, or the
-   value stored in UREGNO by INSN otherwise, and replace all uses of
-   UREGNO in DEBUG with uses of the debug temp.  INSN must be where
-   UREGNO dies, if WHERE is *_BEFORE_*, or where it is set otherwise.
-   Return the number of debug insns emitted.  */
-int
-dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
-			rtx insn, enum debug_temp_where where)
-{
-  struct dead_debug_use **tailp = &debug->head;
-  struct dead_debug_use *cur;
-  struct dead_debug_use *uses = NULL;
-  struct dead_debug_use **usesp = &uses;
-  rtx reg = NULL;
-  rtx breg;
-  rtx dval;
-  rtx bind;
-
-  if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
-    return 0;
-
-  /* Move all uses of uregno from debug->head to uses, setting mode to
-     the widest referenced mode.  */
-  while ((cur = *tailp))
-    {
-      if (DF_REF_REGNO (cur->use) == uregno)
-	{
-	  *usesp = cur;
-	  usesp = &cur->next;
-	  *tailp = cur->next;
-	  cur->next = NULL;
-	  if (!reg
-	      || (GET_MODE_BITSIZE (GET_MODE (reg))
-		  < GET_MODE_BITSIZE (GET_MODE (*DF_REF_REAL_LOC (cur->use)))))
-	    reg = *DF_REF_REAL_LOC (cur->use);
-	}
-      else
-	tailp = &(*tailp)->next;
-    }
-
-  /* We may have dangling bits in debug->used for registers that were part
-     of a multi-register use, one component of which has been reset.  */
-  if (reg == NULL)
-    {
-      gcc_checking_assert (!uses);
-      return 0;
-    }
-
-  gcc_checking_assert (uses);
-
-  breg = reg;
-  /* Recover the expression INSN stores in REG.  */
-  if (where == DEBUG_TEMP_BEFORE_WITH_VALUE)
-    {
-      rtx set = single_set (insn);
-      rtx dest, src;
-
-      if (set)
-	{
-	  dest = SET_DEST (set);
-	  src = SET_SRC (set);
-	  /* Lose if the REG-setting insn is a CALL.  */
-	  if (GET_CODE (src) == CALL)
-	    {
-	      while (uses)
-		{
-		  cur = uses->next;
-		  XDELETE (uses);
-		  uses = cur;
-		}
-	      return 0;
-	    }
-	}
-
-      /* ??? Should we try to extract it from a PARALLEL?  */
-      if (!set)
-	breg = NULL;
-      /* Cool, it's the same REG, we can use SRC.  */
-      else if (dest == reg)
-	breg = copy_rtx (src);
-      else if (REG_P (dest))
-	{
-	  /* Hmm...  Something's fishy, we should be setting REG here.  */
-	  if (REGNO (dest) != REGNO (reg))
-	    breg = NULL;
-	  /* Ok, it's the same (hardware) REG, but with a different
-	     mode, so SUBREG it.  */
-	  else
-	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
-				   GET_MODE (dest));
-	}
-      else if (GET_CODE (dest) == SUBREG)
-	{
-	  /* We should be setting REG here.  Lose.  */
-	  if (REGNO (SUBREG_REG (dest)) != REGNO (reg))
-	    breg = NULL;
-	  /* Lose if we're setting something other than the lowpart of
-	     REG.  */
-	  else if (!subreg_lowpart_p (dest))
-	    breg = NULL;
-	  /* If we're not overwriting all the hardware registers that
-	     setting REG in its mode would, we won't know what to bind
-	     the debug temp to.  */
-	  else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
-		   && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
-		       != hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
-	    breg = NULL;
-	  /* Yay, we can use SRC, just adjust its mode.  */
-	  else
-	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
-				   GET_MODE (dest));
-	}
-      /* Oh well, we're out of luck.  */
-      else
-	breg = NULL;
-
-      /* We couldn't figure out the value stored in REG, so reset all
-	 of its pending debug uses.  */
-      if (!breg)
-	{
-	  dead_debug_reset_uses (debug, uses);
-	  return 0;
-	}
-    }
-
-  /* If there's a single (debug) use of an otherwise unused REG, and
-     the debug use is not part of a larger expression, then it
-     probably doesn't make sense to introduce a new debug temp.  */
-  if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next)
-    {
-      rtx next = DF_REF_INSN (uses->use);
-
-      if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
-	{
-	  XDELETE (uses);
-	  return 0;
-	}
-    }
-
-  /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
-  dval = make_debug_expr_from_rtl (reg);
-
-  /* Emit a debug bind insn before the insn in which reg dies.  */
-  bind = gen_rtx_VAR_LOCATION (GET_MODE (reg),
-			       DEBUG_EXPR_TREE_DECL (dval), breg,
-			       VAR_INIT_STATUS_INITIALIZED);
-
-  if (where == DEBUG_TEMP_AFTER_WITH_REG)
-    bind = emit_debug_insn_after (bind, insn);
-  else
-    bind = emit_debug_insn_before (bind, insn);
-  df_insn_rescan (bind);
-
-  /* Adjust all uses.  */
-  while ((cur = uses))
-    {
-      if (GET_MODE (*DF_REF_REAL_LOC (cur->use)) == GET_MODE (reg))
-	*DF_REF_REAL_LOC (cur->use) = dval;
-      else
-	*DF_REF_REAL_LOC (cur->use)
-	  = gen_lowpart_SUBREG (GET_MODE (*DF_REF_REAL_LOC (cur->use)), dval);
-      /* ??? Should we simplify subreg of subreg?  */
-      if (debug->to_rescan == NULL)
-	debug->to_rescan = BITMAP_ALLOC (NULL);
-      bitmap_set_bit (debug->to_rescan, INSN_UID (DF_REF_INSN (cur->use)));
-      uses = cur->next;
-      XDELETE (cur);
-    }
-
-  return 1;
-}
-
 /* Recompute the REG_DEAD and REG_UNUSED notes and compute register
    info: lifetime, bb, and number of defs and uses for basic block
    BB.  The three bitvectors are scratch regs used here.  */
Index: gcc/df.h
===================================================================
--- gcc/df.h.orig	2012-04-30 08:00:48.910985275 -0300
+++ gcc/df.h	2012-04-30 08:05:34.547705410 -0300
@@ -1101,46 +1101,4 @@ extern void union_defs (df_ref, struct w
 			unsigned int *used, struct web_entry *,
 			bool (*fun) (struct web_entry *, struct web_entry *));
 
-/* Debug uses of dead regs.  */
-
-/* Node of a linked list of uses of dead REGs in debug insns.  */
-struct dead_debug_use
-{
-  df_ref use;
-  struct dead_debug_use *next;
-};
-
-/* Linked list of the above, with a bitmap of the REGs in the
-   list.  */
-struct dead_debug
-{
-  struct dead_debug_use *head;
-  bitmap used;
-  bitmap to_rescan;
-};
-
-/* This type controls the behavior of dead_debug_insert_temp WRT
-   UREGNO and INSN.  */
-enum debug_temp_where
-  {
-    /* Bind a newly-created debug temporary to a REG for UREGNO, and
-       insert the debug insn before INSN.  REG is expected to die at
-       INSN.  */
-    DEBUG_TEMP_BEFORE_WITH_REG = -1,
-    /* Bind a newly-created debug temporary to the value INSN stores
-       in REG, and insert the debug insn before INSN.  */
-    DEBUG_TEMP_BEFORE_WITH_VALUE = 0,
-    /* Bind a newly-created debug temporary to a REG for UREGNO, and
-       insert the debug insn after INSN.  REG is expected to be set at
-       INSN.  */
-    DEBUG_TEMP_AFTER_WITH_REG = 1
-  };
-
-extern void dead_debug_init (struct dead_debug *, bitmap);
-extern void dead_debug_finish (struct dead_debug *, bitmap);
-extern void dead_debug_add (struct dead_debug *, df_ref, unsigned int);
-extern int dead_debug_insert_temp (struct dead_debug *,
-				   unsigned int uregno, rtx insn,
-				   enum debug_temp_where);
-
 #endif /* GCC_DF_H */
Index: gcc/rtl.h
===================================================================
--- gcc/rtl.h.orig	2012-04-30 08:32:47.328393723 -0300
+++ gcc/rtl.h	2012-04-30 08:41:52.935260461 -0300
@@ -2461,6 +2461,7 @@ extern unsigned int extended_count (cons
 extern rtx remove_death (unsigned int, rtx);
 extern void dump_combine_stats (FILE *);
 extern void dump_combine_total_stats (FILE *);
+extern rtx make_compound_operation (rtx, enum rtx_code);
 
 /* In cfgcleanup.c  */
 extern void delete_dead_jumptables (void);
Index: gcc/valtrack.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc/valtrack.c	2012-04-30 08:46:35.021037902 -0300
@@ -0,0 +1,481 @@
+/* Infrastructure for tracking user variable locations and values
+   throughout compilation.
+   Copyright (C) 2010, 2011, 2012  Free Software Foundation, Inc.
+   Contributed by Alexandre Oliva <aoliva@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "valtrack.h"
+#include "function.h"
+#include "regs.h"
+#include "emit-rtl.h"
+
+/* Replace auto-increment addressing modes with explicit operations to access
+   the same addresses without modifying the corresponding registers.  */
+
+rtx
+cleanup_auto_inc_dec (rtx src, enum machine_mode mem_mode ATTRIBUTE_UNUSED)
+{
+  rtx x = src;
+#ifdef AUTO_INC_DEC
+  const RTX_CODE code = GET_CODE (x);
+  int i;
+  const char *fmt;
+
+  switch (code)
+    {
+    case REG:
+    case CONST_INT:
+    case CONST_DOUBLE:
+    case CONST_FIXED:
+    case CONST_VECTOR:
+    case SYMBOL_REF:
+    case CODE_LABEL:
+    case PC:
+    case CC0:
+    case SCRATCH:
+      /* SCRATCH must be shared because they represent distinct values.  */
+      return x;
+    case CLOBBER:
+      if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)
+	return x;
+      break;
+
+    case CONST:
+      if (shared_const_p (x))
+	return x;
+      break;
+
+    case MEM:
+      mem_mode = GET_MODE (x);
+      break;
+
+    case PRE_INC:
+    case PRE_DEC:
+      gcc_assert (mem_mode != VOIDmode && mem_mode != BLKmode);
+      return gen_rtx_PLUS (GET_MODE (x),
+			   cleanup_auto_inc_dec (XEXP (x, 0), mem_mode),
+			   GEN_INT (code == PRE_INC
+				    ? GET_MODE_SIZE (mem_mode)
+				    : -GET_MODE_SIZE (mem_mode)));
+
+    case POST_INC:
+    case POST_DEC:
+    case PRE_MODIFY:
+    case POST_MODIFY:
+      return cleanup_auto_inc_dec (code == PRE_MODIFY
+				   ? XEXP (x, 1) : XEXP (x, 0),
+				   mem_mode);
+
+    default:
+      break;
+    }
+
+  /* Copy the various flags, fields, and other information.  We assume
+     that all fields need copying, and then clear the fields that should
+     not be copied.  That is the sensible default behavior, and forces
+     us to explicitly document why we are *not* copying a flag.  */
+  x = shallow_copy_rtx (x);
+
+  /* We do not copy the USED flag, which is used as a mark bit during
+     walks over the RTL.  */
+  RTX_FLAG (x, used) = 0;
+
+  /* We do not copy FRAME_RELATED for INSNs.  */
+  if (INSN_P (x))
+    RTX_FLAG (x, frame_related) = 0;
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    if (fmt[i] == 'e')
+      XEXP (x, i) = cleanup_auto_inc_dec (XEXP (x, i), mem_mode);
+    else if (fmt[i] == 'E' || fmt[i] == 'V')
+      {
+	int j;
+	XVEC (x, i) = rtvec_alloc (XVECLEN (x, i));
+	for (j = 0; j < XVECLEN (x, i); j++)
+	  XVECEXP (x, i, j)
+	    = cleanup_auto_inc_dec (XVECEXP (src, i, j), mem_mode);
+      }
+
+#else /* !AUTO_INC_DEC */
+  x = copy_rtx (x);
+#endif /* !AUTO_INC_DEC */
+
+  return x;
+}
+
+/* Auxiliary data structure for propagate_for_debug_stmt.  */
+
+struct rtx_subst_pair
+{
+  rtx to;
+  bool adjusted;
+};
+
+/* DATA points to an rtx_subst_pair.  Return the value that should be
+   substituted.  */
+
+static rtx
+propagate_for_debug_subst (rtx from, const_rtx old_rtx, void *data)
+{
+  struct rtx_subst_pair *pair = (struct rtx_subst_pair *)data;
+
+  if (!rtx_equal_p (from, old_rtx))
+    return NULL_RTX;
+  if (!pair->adjusted)
+    {
+      pair->adjusted = true;
+      pair->to = cleanup_auto_inc_dec (pair->to, VOIDmode);
+      pair->to = make_compound_operation (pair->to, SET);
+      return pair->to;
+    }
+  return copy_rtx (pair->to);
+}
+
+/* Replace all the occurrences of DEST with SRC in DEBUG_INSNs between INSN
+   and LAST, not including INSN, but including LAST.  Also stop at the end
+   of THIS_BASIC_BLOCK.  */
+
+void
+propagate_for_debug (rtx insn, rtx last, rtx dest, rtx src,
+		     basic_block this_basic_block)
+{
+  rtx next, loc, end = NEXT_INSN (BB_END (this_basic_block));
+
+  struct rtx_subst_pair p;
+  p.to = src;
+  p.adjusted = false;
+
+  next = NEXT_INSN (insn);
+  last = NEXT_INSN (last);
+  while (next != last && next != end)
+    {
+      insn = next;
+      next = NEXT_INSN (insn);
+      if (DEBUG_INSN_P (insn))
+	{
+	  loc = simplify_replace_fn_rtx (INSN_VAR_LOCATION_LOC (insn),
+					 dest, propagate_for_debug_subst, &p);
+	  if (loc == INSN_VAR_LOCATION_LOC (insn))
+	    continue;
+	  INSN_VAR_LOCATION_LOC (insn) = loc;
+	  df_insn_rescan (insn);
+	}
+    }
+}
+
+/* Initialize DEBUG to an empty list, and clear USED, if given.  */
+void
+dead_debug_init (struct dead_debug *debug, bitmap used)
+{
+  debug->head = NULL;
+  debug->used = used;
+  debug->to_rescan = NULL;
+  if (used)
+    bitmap_clear (used);
+}
+
+/* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of
+   each reset insn.  DEBUG is not otherwise modified.  If HEAD is
+   DEBUG->head, DEBUG->head will be set to NULL at the end.
+   Otherwise, entries from DEBUG->head that pertain to reset insns
+   will be removed, and only then rescanned.  */
+
+static void
+dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
+{
+  bool got_head = (debug->head == head);
+  bitmap rescan;
+  struct dead_debug_use **tailp = &debug->head;
+  struct dead_debug_use *cur;
+  bitmap_iterator bi;
+  unsigned int uid;
+
+  if (got_head)
+    rescan = NULL;
+  else
+    rescan = BITMAP_ALLOC (NULL);
+
+  while (head)
+    {
+      struct dead_debug_use *next = head->next;
+      rtx insn;
+
+      insn = DF_REF_INSN (head->use);
+      if (!next || DF_REF_INSN (next->use) != insn)
+	{
+	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
+	  if (got_head)
+	    df_insn_rescan_debug_internal (insn);
+	  else
+	    bitmap_set_bit (rescan, INSN_UID (insn));
+	  if (debug->to_rescan)
+	    bitmap_clear_bit (debug->to_rescan, INSN_UID (insn));
+	}
+      XDELETE (head);
+      head = next;
+    }
+
+  if (got_head)
+    {
+      debug->head = NULL;
+      return;
+    }
+
+  while ((cur = *tailp))
+    if (bitmap_bit_p (rescan, INSN_UID (DF_REF_INSN (cur->use))))
+      {
+	*tailp = cur->next;
+	XDELETE (cur);
+      }
+    else
+      tailp = &cur->next;
+
+  EXECUTE_IF_SET_IN_BITMAP (rescan, 0, uid, bi)
+    {
+      struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
+      if (insn_info)
+	df_insn_rescan_debug_internal (insn_info->insn);
+    }
+
+  BITMAP_FREE (rescan);
+}
+
+/* Reset all debug insns with pending uses.  Release the bitmap in it,
+   unless it is USED.  USED must be the same bitmap passed to
+   dead_debug_init.  */
+void
+dead_debug_finish (struct dead_debug *debug, bitmap used)
+{
+  if (debug->used != used)
+    BITMAP_FREE (debug->used);
+
+  dead_debug_reset_uses (debug, debug->head);
+
+  if (debug->to_rescan)
+    {
+      bitmap_iterator bi;
+      unsigned int uid;
+
+      EXECUTE_IF_SET_IN_BITMAP (debug->to_rescan, 0, uid, bi)
+	{
+	  struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
+	  if (insn_info)
+	    df_insn_rescan (insn_info->insn);
+	}
+      BITMAP_FREE (debug->to_rescan);
+    }
+}
+
+/* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
+   insn.  Create a bitmap for DEBUG as needed.  */
+void
+dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
+{
+  struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
+
+  newddu->use = use;
+  newddu->next = debug->head;
+  debug->head = newddu;
+
+  if (!debug->used)
+    debug->used = BITMAP_ALLOC (NULL);
+
+  bitmap_set_bit (debug->used, uregno);
+}
+
+/* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
+   before or after INSN (depending on WHERE), that binds a debug temp
+   to the widest-mode use of UREGNO, if WHERE is *_WITH_REG, or the
+   value stored in UREGNO by INSN otherwise, and replace all uses of
+   UREGNO in DEBUG with uses of the debug temp.  INSN must be where
+   UREGNO dies, if WHERE is *_BEFORE_*, or where it is set otherwise.
+   Return the number of debug insns emitted.  */
+int
+dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
+			rtx insn, enum debug_temp_where where)
+{
+  struct dead_debug_use **tailp = &debug->head;
+  struct dead_debug_use *cur;
+  struct dead_debug_use *uses = NULL;
+  struct dead_debug_use **usesp = &uses;
+  rtx reg = NULL;
+  rtx breg;
+  rtx dval;
+  rtx bind;
+
+  if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
+    return 0;
+
+  /* Move all uses of uregno from debug->head to uses, setting mode to
+     the widest referenced mode.  */
+  while ((cur = *tailp))
+    {
+      if (DF_REF_REGNO (cur->use) == uregno)
+	{
+	  *usesp = cur;
+	  usesp = &cur->next;
+	  *tailp = cur->next;
+	  cur->next = NULL;
+	  if (!reg
+	      || (GET_MODE_BITSIZE (GET_MODE (reg))
+		  < GET_MODE_BITSIZE (GET_MODE (*DF_REF_REAL_LOC (cur->use)))))
+	    reg = *DF_REF_REAL_LOC (cur->use);
+	}
+      else
+	tailp = &(*tailp)->next;
+    }
+
+  /* We may have dangling bits in debug->used for registers that were part
+     of a multi-register use, one component of which has been reset.  */
+  if (reg == NULL)
+    {
+      gcc_checking_assert (!uses);
+      return 0;
+    }
+
+  gcc_checking_assert (uses);
+
+  breg = reg;
+  /* Recover the expression INSN stores in REG.  */
+  if (where == DEBUG_TEMP_BEFORE_WITH_VALUE)
+    {
+      rtx set = single_set (insn);
+      rtx dest, src;
+
+      if (set)
+	{
+	  dest = SET_DEST (set);
+	  src = SET_SRC (set);
+	  /* Lose if the REG-setting insn is a CALL.  */
+	  if (GET_CODE (src) == CALL)
+	    {
+	      while (uses)
+		{
+		  cur = uses->next;
+		  XDELETE (uses);
+		  uses = cur;
+		}
+	      return 0;
+	    }
+	}
+
+      /* ??? Should we try to extract it from a PARALLEL?  */
+      if (!set)
+	breg = NULL;
+      /* Cool, it's the same REG, we can use SRC.  */
+      else if (dest == reg)
+	breg = cleanup_auto_inc_dec (src, VOIDmode);
+      else if (REG_P (dest))
+	{
+	  /* Hmm...  Something's fishy, we should be setting REG here.  */
+	  if (REGNO (dest) != REGNO (reg))
+	    breg = NULL;
+	  /* Ok, it's the same (hardware) REG, but with a different
+	     mode, so SUBREG it.  */
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg),
+				   cleanup_auto_inc_dec (src, VOIDmode),
+				   GET_MODE (dest));
+	}
+      else if (GET_CODE (dest) == SUBREG)
+	{
+	  /* We should be setting REG here.  Lose.  */
+	  if (REGNO (SUBREG_REG (dest)) != REGNO (reg))
+	    breg = NULL;
+	  /* Lose if we're setting something other than the lowpart of
+	     REG.  */
+	  else if (!subreg_lowpart_p (dest))
+	    breg = NULL;
+	  /* If we're not overwriting all the hardware registers that
+	     setting REG in its mode would, we won't know what to bind
+	     the debug temp to.  */
+	  else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
+		   && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
+		       != hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
+	    breg = NULL;
+	  /* Yay, we can use SRC, just adjust its mode.  */
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg),
+				   cleanup_auto_inc_dec (src, VOIDmode),
+				   GET_MODE (dest));
+	}
+      /* Oh well, we're out of luck.  */
+      else
+	breg = NULL;
+
+      /* We couldn't figure out the value stored in REG, so reset all
+	 of its pending debug uses.  */
+      if (!breg)
+	{
+	  dead_debug_reset_uses (debug, uses);
+	  return 0;
+	}
+    }
+
+  /* If there's a single (debug) use of an otherwise unused REG, and
+     the debug use is not part of a larger expression, then it
+     probably doesn't make sense to introduce a new debug temp.  */
+  if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next)
+    {
+      rtx next = DF_REF_INSN (uses->use);
+
+      if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
+	{
+	  XDELETE (uses);
+	  return 0;
+	}
+    }
+
+  /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
+  dval = make_debug_expr_from_rtl (reg);
+
+  /* Emit a debug bind insn before the insn in which reg dies.  */
+  bind = gen_rtx_VAR_LOCATION (GET_MODE (reg),
+			       DEBUG_EXPR_TREE_DECL (dval), breg,
+			       VAR_INIT_STATUS_INITIALIZED);
+
+  if (where == DEBUG_TEMP_AFTER_WITH_REG)
+    bind = emit_debug_insn_after (bind, insn);
+  else
+    bind = emit_debug_insn_before (bind, insn);
+  df_insn_rescan (bind);
+
+  /* Adjust all uses.  */
+  while ((cur = uses))
+    {
+      if (GET_MODE (*DF_REF_REAL_LOC (cur->use)) == GET_MODE (reg))
+	*DF_REF_REAL_LOC (cur->use) = dval;
+      else
+	*DF_REF_REAL_LOC (cur->use)
+	  = gen_lowpart_SUBREG (GET_MODE (*DF_REF_REAL_LOC (cur->use)), dval);
+      /* ??? Should we simplify subreg of subreg?  */
+      if (debug->to_rescan == NULL)
+	debug->to_rescan = BITMAP_ALLOC (NULL);
+      bitmap_set_bit (debug->to_rescan, INSN_UID (DF_REF_INSN (cur->use)));
+      uses = cur->next;
+      XDELETE (cur);
+    }
+
+  return 1;
+}
Index: gcc/valtrack.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc/valtrack.h	2012-04-30 08:40:50.209196206 -0300
@@ -0,0 +1,76 @@
+/* Infrastructure for tracking user variable locations and values
+   throughout compilation.
+   Copyright (C) 2010, 2011, 2012  Free Software Foundation, Inc.
+   Contributed by Alexandre Oliva <aoliva@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_VALTRACK_H
+#define GCC_VALTRACK_H
+
+#include "bitmap.h"
+#include "df.h"
+#include "rtl.h"
+#include "basic-block.h"
+
+/* Debug uses of dead regs.  */
+
+/* Node of a linked list of uses of dead REGs in debug insns.  */
+struct dead_debug_use
+{
+  df_ref use;
+  struct dead_debug_use *next;
+};
+
+/* Linked list of the above, with a bitmap of the REGs in the
+   list.  */
+struct dead_debug
+{
+  struct dead_debug_use *head;
+  bitmap used;
+  bitmap to_rescan;
+};
+
+/* This type controls the behavior of dead_debug_insert_temp WRT
+   UREGNO and INSN.  */
+enum debug_temp_where
+  {
+    /* Bind a newly-created debug temporary to a REG for UREGNO, and
+       insert the debug insn before INSN.  REG is expected to die at
+       INSN.  */
+    DEBUG_TEMP_BEFORE_WITH_REG = -1,
+    /* Bind a newly-created debug temporary to the value INSN stores
+       in REG, and insert the debug insn before INSN.  */
+    DEBUG_TEMP_BEFORE_WITH_VALUE = 0,
+    /* Bind a newly-created debug temporary to a REG for UREGNO, and
+       insert the debug insn after INSN.  REG is expected to be set at
+       INSN.  */
+    DEBUG_TEMP_AFTER_WITH_REG = 1
+  };
+
+extern void dead_debug_init (struct dead_debug *, bitmap);
+extern void dead_debug_finish (struct dead_debug *, bitmap);
+extern void dead_debug_add (struct dead_debug *, df_ref, unsigned int);
+extern int dead_debug_insert_temp (struct dead_debug *,
+				   unsigned int uregno, rtx insn,
+				   enum debug_temp_where);
+
+extern rtx cleanup_auto_inc_dec (rtx, enum machine_mode);
+extern void propagate_for_debug (rtx, rtx, rtx, rtx, basic_block);
+
+
+#endif /* GCC_VALTRACK_H */

[-- Attachment #3: Type: text/plain, Size: 258 bytes --]



-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PR52983] eliminate autoinc from debug_insn locs
  2012-05-03 18:27                     ` [PR52983] eliminate autoinc from debug_insn locs (was: Re: fix left-over debug insns in DCE) Alexandre Oliva
@ 2012-05-03 22:40                       ` Alexandre Oliva
  2012-06-13  8:01                         ` Alexandre Oliva
  2012-05-03 22:46                       ` [PR52983, PR48866] " Alexandre Oliva
  1 sibling, 1 reply; 23+ messages in thread
From: Alexandre Oliva @ 2012-05-03 22:40 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: Eric Botcazou, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 995 bytes --]

On May  3, 2012, Alexandre Oliva <aoliva@redhat.com> wrote:

> My recent patch for PR48866, that introduced dead_debug_insert_temp()
> with DEBUG_TEMP_BEFORE_WITH_VALUE as a possibility for keeping
> expressions about to be DCE'd, caused regressions on ppc because it
> would take MEMs with autoinc addressing modes, which would be rejected
> down the road.

> This patch arranges for locs to have autoinc addressing modes eliminated
> while copying them for use in the debug insns, fixing the problem.

Of course, a moment after posting the patch, it occurred to me that it
would be better to break it up into separate parts: moving code about,
changing interfaces, and making the actual change.  I was further
motivated to do this by the realization that cleanup_auto_inc_dec was
only used within valtrack.c, so I made it static rather than globally
exported.

Here are the 3 patches that, together, are equivalent to the one posted
before, except for the visibility of cleanup_auto_inc_dec.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: vta-valtrack-move-pr52983.patch --]
[-- Type: text/x-diff, Size: 41754 bytes --]

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

	PR debug/52983
	* valtrack.h, valtrack.c: New.
	* Makefile.in (VALTRACK_H): New.
	(OBJS): Add valtrack.o.
	(valtrack.o): New.
	(cselib.o, dce.o, df-problems.o, combine.o): Add VALTRACK_H.
	* combine.c: Include valtrack.h.
	(make_compound_operation): Publish.
	(cleanup_auto_inc_dec): Move to valtrack.c.
	(struct rtx_subst_pair, propagate_for_debug_subst): Likewise.
	(propagate_for_debug): Likewise.  Add this_basic_block parameter.
	Adjust all callers.
	* cselib.c: Include valtrack.h.
	* dce.c: Likewise.
	* df-problems.c: Likewise.
	(dead_debug_init, dead_debug_reset_uses): Move to valtrack.c.
	(dead_debug_finish, dead_debug_add): Likewise.
	(dead_debug_insert_temp): Likewise.
	* df.h (struct dead_debug_use): Move to valtrack.h.
	(struct dead_debug, enum debug_temp_where): Likewise.
	(dead_debug_init, dead_debug_reset_uses): Move to valtrack.h.
	(dead_debug_finish, dead_debug_add): Likewise.
	(dead_debug_insert_temp): Likewise.
	* rtl.h (make_compound_operation): Declare.

Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in.orig	2012-05-02 18:05:05.809736324 -0300
+++ gcc/Makefile.in	2012-05-03 02:14:44.795546132 -0300
@@ -894,6 +894,7 @@ CGRAPH_H = cgraph.h $(VEC_H) $(TREE_H) $
 	cif-code.def ipa-ref.h ipa-ref-inline.h $(LINKER_PLUGIN_API_H)
 DF_H = df.h $(BITMAP_H) $(REGSET_H) sbitmap.h $(BASIC_BLOCK_H) \
 	alloc-pool.h $(TIMEVAR_H)
+VALTRACK_H = valtrack.h $(BITMAP_H) $(DF_H) $(RTL_H) $(BASIC_BLOCK_H)
 RESOURCE_H = resource.h hard-reg-set.h $(DF_H)
 DDG_H = ddg.h sbitmap.h $(DF_H)
 GCC_H = gcc.h version.h $(DIAGNOSTIC_CORE_H)
@@ -1437,6 +1438,7 @@ OBJS = \
 	tree-vectorizer.o \
 	tree-vrp.o \
 	tree.o \
+	valtrack.o \
 	value-prof.o \
 	var-tracking.o \
 	varasm.o \
@@ -3002,8 +3004,8 @@ coverage.o : coverage.c $(GCOV_IO_H) $(C
 cselib.o : cselib.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h $(RECOG_H) \
    $(EMIT_RTL_H) $(DIAGNOSTIC_CORE_H) output.h $(FUNCTION_H) $(TREE_PASS_H) \
-   cselib.h gt-cselib.h $(GGC_H) $(TM_P_H) $(PARAMS_H) alloc-pool.h \
-   $(HASHTAB_H) $(TARGET_H) $(BITMAP_H)
+   cselib.h gt-cselib.h $(GGC_H) $(TM_P_H) $(VALTRACK_H) $(PARAMS_H) \
+   alloc-pool.h $(HASHTAB_H) $(TARGET_H) $(BITMAP_H)
 cse.o : cse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \
    hard-reg-set.h $(FLAGS_H) insn-config.h $(RECOG_H) $(EXPR_H) toplev.h $(DIAGNOSTIC_CORE_H) \
    output.h $(FUNCTION_H) $(BASIC_BLOCK_H) $(GGC_H) $(TM_P_H) $(TIMEVAR_H) \
@@ -3011,8 +3013,8 @@ cse.o : cse.c $(CONFIG_H) $(SYSTEM_H) co
    $(DF_H) $(DBGCNT_H)
 dce.o : dce.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(TREE_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) $(EXCEPT_H) $(DF_H) cselib.h \
-   $(DBGCNT_H) dce.h $(TIMEVAR_H) $(TREE_PASS_H) $(DBGCNT_H) $(TM_P_H) \
-   $(EMIT_RTL_H)
+   $(DBGCNT_H) dce.h $(VALTRACK_H) $(TIMEVAR_H) $(TREE_PASS_H) \
+   $(DBGCNT_H) $(TM_P_H) $(EMIT_RTL_H)
 dse.o : dse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(TREE_H) $(TM_P_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h \
    $(RECOG_H) $(EXPR_H) $(DF_H) cselib.h $(DBGCNT_H) $(TIMEVAR_H) \
@@ -3104,7 +3106,8 @@ df-core.o : df-core.c $(CONFIG_H) $(SYST
 df-problems.o : df-problems.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(RTL_H) insn-config.h $(RECOG_H) $(FUNCTION_H) $(REGS_H) alloc-pool.h \
    hard-reg-set.h $(BASIC_BLOCK_H) $(DF_H) $(BITMAP_H) sbitmap.h $(TIMEVAR_H) \
-   $(TM_P_H) $(TARGET_H) $(FLAGS_H) output.h $(EXCEPT_H) dce.h vecprim.h
+   $(TM_P_H) $(TARGET_H) $(FLAGS_H) output.h $(EXCEPT_H) dce.h \
+   vecprim.h $(VALTRACK_H)
 df-scan.o : df-scan.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    insn-config.h $(RECOG_H) $(FUNCTION_H) $(REGS_H) alloc-pool.h \
    hard-reg-set.h $(BASIC_BLOCK_H) $(DF_H) $(BITMAP_H) sbitmap.h $(TIMEVAR_H) \
@@ -3113,6 +3116,8 @@ df-scan.o : df-scan.c $(CONFIG_H) $(SYST
 regstat.o : regstat.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(TM_P_H) $(FLAGS_H) $(REGS_H) output.h $(EXCEPT_H) hard-reg-set.h \
    $(BASIC_BLOCK_H) $(TIMEVAR_H) $(DF_H)
+valtrack.o : valtrack.c $(VALTRACK_H) $(CONFIG_H) $(SYSTEM_H) \
+   coretypes.h $(TM_H) $(FUNCTION_H) $(REGS_H) $(EMIT_RTL_H)
 var-tracking.o : var-tracking.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(RTL_H) $(TREE_H) hard-reg-set.h insn-config.h reload.h $(FLAGS_H) \
    $(BASIC_BLOCK_H) output.h sbitmap.h alloc-pool.h $(FIBHEAP_H) $(HASHTAB_H) \
@@ -3212,9 +3217,10 @@ et-forest.o : et-forest.c $(CONFIG_H) $(
 combine.o : combine.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(FLAGS_H) $(FUNCTION_H) insn-config.h $(INSN_ATTR_H) $(REGS_H) $(EXPR_H) \
    rtlhooks-def.h $(BASIC_BLOCK_H) $(RECOG_H) hard-reg-set.h \
-   $(DIAGNOSTIC_CORE_H) $(TM_P_H) $(TREE_H) $(TARGET_H) output.h $(PARAMS_H) $(OPTABS_H) \
-   insn-codes.h $(TIMEVAR_H) $(TREE_PASS_H) $(DF_H) vecprim.h $(CGRAPH_H) \
-   $(OBSTACK_H)
+   $(DIAGNOSTIC_CORE_H) $(TM_P_H) $(TREE_H) $(TARGET_H) \
+   output.h $(PARAMS_H) $(OPTABS_H) \
+   insn-codes.h $(TIMEVAR_H) $(TREE_PASS_H) $(DF_H) $(VALTRACK_H) \
+   vecprim.h $(CGRAPH_H) $(OBSTACK_H)
 reginfo.o : reginfo.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    hard-reg-set.h $(FLAGS_H) $(BASIC_BLOCK_H) addresses.h $(REGS_H) \
    insn-config.h $(RECOG_H) reload.h $(DIAGNOSTIC_CORE_H) \
Index: gcc/combine.c
===================================================================
--- gcc/combine.c.orig	2012-05-02 18:01:48.846610990 -0300
+++ gcc/combine.c	2012-05-03 02:14:45.040542653 -0300
@@ -103,6 +103,7 @@ along with GCC; see the file COPYING3.  
 #include "timevar.h"
 #include "tree-pass.h"
 #include "df.h"
+#include "valtrack.h"
 #include "cgraph.h"
 #include "obstack.h"
 
@@ -427,7 +428,6 @@ static const_rtx expand_field_assignment
 static rtx make_extraction (enum machine_mode, rtx, HOST_WIDE_INT,
 			    rtx, unsigned HOST_WIDE_INT, int, int, int);
 static rtx extract_left_shift (rtx, int);
-static rtx make_compound_operation (rtx, enum rtx_code);
 static int get_pos_from_mask (unsigned HOST_WIDE_INT,
 			      unsigned HOST_WIDE_INT *);
 static rtx canon_reg_for_combine (rtx, rtx);
@@ -2360,161 +2360,6 @@ reg_subword_p (rtx x, rtx reg)
 	 && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT;
 }
 
-#ifdef AUTO_INC_DEC
-/* Replace auto-increment addressing modes with explicit operations to access
-   the same addresses without modifying the corresponding registers.  */
-
-static rtx
-cleanup_auto_inc_dec (rtx src, enum machine_mode mem_mode)
-{
-  rtx x = src;
-  const RTX_CODE code = GET_CODE (x);
-  int i;
-  const char *fmt;
-
-  switch (code)
-    {
-    case REG:
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case CONST_FIXED:
-    case CONST_VECTOR:
-    case SYMBOL_REF:
-    case CODE_LABEL:
-    case PC:
-    case CC0:
-    case SCRATCH:
-      /* SCRATCH must be shared because they represent distinct values.  */
-      return x;
-    case CLOBBER:
-      if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)
-	return x;
-      break;
-
-    case CONST:
-      if (shared_const_p (x))
-	return x;
-      break;
-
-    case MEM:
-      mem_mode = GET_MODE (x);
-      break;
-
-    case PRE_INC:
-    case PRE_DEC:
-      gcc_assert (mem_mode != VOIDmode && mem_mode != BLKmode);
-      return gen_rtx_PLUS (GET_MODE (x),
-			   cleanup_auto_inc_dec (XEXP (x, 0), mem_mode),
-			   GEN_INT (code == PRE_INC
-				    ? GET_MODE_SIZE (mem_mode)
-				    : -GET_MODE_SIZE (mem_mode)));
-
-    case POST_INC:
-    case POST_DEC:
-    case PRE_MODIFY:
-    case POST_MODIFY:
-      return cleanup_auto_inc_dec (code == PRE_MODIFY
-				   ? XEXP (x, 1) : XEXP (x, 0),
-				   mem_mode);
-
-    default:
-      break;
-    }
-
-  /* Copy the various flags, fields, and other information.  We assume
-     that all fields need copying, and then clear the fields that should
-     not be copied.  That is the sensible default behavior, and forces
-     us to explicitly document why we are *not* copying a flag.  */
-  x = shallow_copy_rtx (x);
-
-  /* We do not copy the USED flag, which is used as a mark bit during
-     walks over the RTL.  */
-  RTX_FLAG (x, used) = 0;
-
-  /* We do not copy FRAME_RELATED for INSNs.  */
-  if (INSN_P (x))
-    RTX_FLAG (x, frame_related) = 0;
-
-  fmt = GET_RTX_FORMAT (code);
-  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
-    if (fmt[i] == 'e')
-      XEXP (x, i) = cleanup_auto_inc_dec (XEXP (x, i), mem_mode);
-    else if (fmt[i] == 'E' || fmt[i] == 'V')
-      {
-	int j;
-	XVEC (x, i) = rtvec_alloc (XVECLEN (x, i));
-	for (j = 0; j < XVECLEN (x, i); j++)
-	  XVECEXP (x, i, j)
-	    = cleanup_auto_inc_dec (XVECEXP (src, i, j), mem_mode);
-      }
-
-  return x;
-}
-#endif
-
-/* Auxiliary data structure for propagate_for_debug_stmt.  */
-
-struct rtx_subst_pair
-{
-  rtx to;
-  bool adjusted;
-};
-
-/* DATA points to an rtx_subst_pair.  Return the value that should be
-   substituted.  */
-
-static rtx
-propagate_for_debug_subst (rtx from, const_rtx old_rtx, void *data)
-{
-  struct rtx_subst_pair *pair = (struct rtx_subst_pair *)data;
-
-  if (!rtx_equal_p (from, old_rtx))
-    return NULL_RTX;
-  if (!pair->adjusted)
-    {
-      pair->adjusted = true;
-#ifdef AUTO_INC_DEC
-      pair->to = cleanup_auto_inc_dec (pair->to, VOIDmode);
-#else
-      pair->to = copy_rtx (pair->to);
-#endif
-      pair->to = make_compound_operation (pair->to, SET);
-      return pair->to;
-    }
-  return copy_rtx (pair->to);
-}
-
-/* Replace all the occurrences of DEST with SRC in DEBUG_INSNs between INSN
-   and LAST, not including INSN, but including LAST.  Also stop at the end
-   of THIS_BASIC_BLOCK.  */
-
-static void
-propagate_for_debug (rtx insn, rtx last, rtx dest, rtx src)
-{
-  rtx next, loc, end = NEXT_INSN (BB_END (this_basic_block));
-
-  struct rtx_subst_pair p;
-  p.to = src;
-  p.adjusted = false;
-
-  next = NEXT_INSN (insn);
-  last = NEXT_INSN (last);
-  while (next != last && next != end)
-    {
-      insn = next;
-      next = NEXT_INSN (insn);
-      if (DEBUG_INSN_P (insn))
-	{
-	  loc = simplify_replace_fn_rtx (INSN_VAR_LOCATION_LOC (insn),
-					 dest, propagate_for_debug_subst, &p);
-	  if (loc == INSN_VAR_LOCATION_LOC (insn))
-	    continue;
-	  INSN_VAR_LOCATION_LOC (insn) = loc;
-	  df_insn_rescan (insn);
-	}
-    }
-}
-
 /* Delete the unconditional jump INSN and adjust the CFG correspondingly.
    Note that the INSN should be deleted *after* removing dead edges, so
    that the kept edge is the fallthrough edge for a (set (pc) (pc))
@@ -3974,7 +3819,8 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
 		   i2src while its original mode is temporarily
 		   restored, and then clear i2scratch so that we don't
 		   do it again later.  */
-		propagate_for_debug (i2, last_combined_insn, reg, i2src);
+		propagate_for_debug (i2, last_combined_insn, reg, i2src,
+				     this_basic_block);
 		i2scratch = false;
 		/* Put back the new mode.  */
 		adjust_reg_mode (reg, new_mode);
@@ -4008,10 +3854,12 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
 		   with this copy we have created; then, replace the
 		   copy with the SUBREG of the original shared reg,
 		   once again changed to the new mode.  */
-		propagate_for_debug (first, last, reg, tempreg);
+		propagate_for_debug (first, last, reg, tempreg,
+				     this_basic_block);
 		adjust_reg_mode (reg, new_mode);
 		propagate_for_debug (first, last, tempreg,
-				     lowpart_subreg (old_mode, reg, new_mode));
+				     lowpart_subreg (old_mode, reg, new_mode),
+				     this_basic_block);
 	      }
 	  }
     }
@@ -4223,14 +4071,16 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
     if (newi2pat)
       {
 	if (MAY_HAVE_DEBUG_INSNS && i2scratch)
-	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src);
+	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src,
+			       this_basic_block);
 	INSN_CODE (i2) = i2_code_number;
 	PATTERN (i2) = newi2pat;
       }
     else
       {
 	if (MAY_HAVE_DEBUG_INSNS && i2src)
-	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src);
+	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src,
+			       this_basic_block);
 	SET_INSN_DELETED (i2);
       }
 
@@ -4239,7 +4089,8 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
 	LOG_LINKS (i1) = NULL;
 	REG_NOTES (i1) = 0;
 	if (MAY_HAVE_DEBUG_INSNS)
-	  propagate_for_debug (i1, last_combined_insn, i1dest, i1src);
+	  propagate_for_debug (i1, last_combined_insn, i1dest, i1src,
+			       this_basic_block);
 	SET_INSN_DELETED (i1);
       }
 
@@ -4248,7 +4099,8 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
 	LOG_LINKS (i0) = NULL;
 	REG_NOTES (i0) = 0;
 	if (MAY_HAVE_DEBUG_INSNS)
-	  propagate_for_debug (i0, last_combined_insn, i0dest, i0src);
+	  propagate_for_debug (i0, last_combined_insn, i0dest, i0src,
+			       this_basic_block);
 	SET_INSN_DELETED (i0);
       }
 
@@ -7600,7 +7452,7 @@ extract_left_shift (rtx x, int count)
    being kludges), it is MEM.  When processing the arguments of a comparison
    or a COMPARE against zero, it is COMPARE.  */
 
-static rtx
+rtx
 make_compound_operation (rtx x, enum rtx_code in_code)
 {
   enum rtx_code code = GET_CODE (x);
Index: gcc/cselib.c
===================================================================
--- gcc/cselib.c.orig	2012-05-02 18:01:48.945609546 -0300
+++ gcc/cselib.c	2012-05-03 02:14:45.084542027 -0300
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  
 #include "hashtab.h"
 #include "tree-pass.h"
 #include "cselib.h"
+#include "valtrack.h"
 #include "params.h"
 #include "alloc-pool.h"
 #include "target.h"
Index: gcc/dce.c
===================================================================
--- gcc/dce.c.orig	2012-05-02 18:01:49.052607983 -0300
+++ gcc/dce.c	2012-05-03 02:14:45.130541377 -0300
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  
 #include "df.h"
 #include "cselib.h"
 #include "dce.h"
+#include "valtrack.h"
 #include "timevar.h"
 #include "tree-pass.h"
 #include "dbgcnt.h"
Index: gcc/df-problems.c
===================================================================
--- gcc/df-problems.c.orig	2012-05-02 18:01:49.127606889 -0300
+++ gcc/df-problems.c	2012-05-03 02:14:45.251539658 -0300
@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  
 #include "except.h"
 #include "dce.h"
 #include "vecprim.h"
+#include "valtrack.h"
 
 /* Note that turning REG_DEAD_DEBUGGING on will cause
    gcc.c-torture/unsorted/dump-noaddr.c to fail because it prints
@@ -3063,300 +3064,6 @@ df_create_unused_note (rtx insn, df_ref 
 }
 
 
-/* Initialize DEBUG to an empty list, and clear USED, if given.  */
-void
-dead_debug_init (struct dead_debug *debug, bitmap used)
-{
-  debug->head = NULL;
-  debug->used = used;
-  debug->to_rescan = NULL;
-  if (used)
-    bitmap_clear (used);
-}
-
-/* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of
-   each reset insn.  DEBUG is not otherwise modified.  If HEAD is
-   DEBUG->head, DEBUG->head will be set to NULL at the end.
-   Otherwise, entries from DEBUG->head that pertain to reset insns
-   will be removed, and only then rescanned.  */
-
-static void
-dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
-{
-  bool got_head = (debug->head == head);
-  bitmap rescan;
-  struct dead_debug_use **tailp = &debug->head;
-  struct dead_debug_use *cur;
-  bitmap_iterator bi;
-  unsigned int uid;
-
-  if (got_head)
-    rescan = NULL;
-  else
-    rescan = BITMAP_ALLOC (NULL);
-
-  while (head)
-    {
-      struct dead_debug_use *next = head->next;
-      rtx insn;
-
-      insn = DF_REF_INSN (head->use);
-      if (!next || DF_REF_INSN (next->use) != insn)
-	{
-	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-	  if (got_head)
-	    df_insn_rescan_debug_internal (insn);
-	  else
-	    bitmap_set_bit (rescan, INSN_UID (insn));
-	  if (debug->to_rescan)
-	    bitmap_clear_bit (debug->to_rescan, INSN_UID (insn));
-	}
-      XDELETE (head);
-      head = next;
-    }
-
-  if (got_head)
-    {
-      debug->head = NULL;
-      return;
-    }
-
-  while ((cur = *tailp))
-    if (bitmap_bit_p (rescan, INSN_UID (DF_REF_INSN (cur->use))))
-      {
-	*tailp = cur->next;
-	XDELETE (cur);
-      }
-    else
-      tailp = &cur->next;
-
-  EXECUTE_IF_SET_IN_BITMAP (rescan, 0, uid, bi)
-    {
-      struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
-      if (insn_info)
-	df_insn_rescan_debug_internal (insn_info->insn);
-    }
-
-  BITMAP_FREE (rescan);
-}
-
-/* Reset all debug insns with pending uses.  Release the bitmap in it,
-   unless it is USED.  USED must be the same bitmap passed to
-   dead_debug_init.  */
-void
-dead_debug_finish (struct dead_debug *debug, bitmap used)
-{
-  if (debug->used != used)
-    BITMAP_FREE (debug->used);
-
-  dead_debug_reset_uses (debug, debug->head);
-
-  if (debug->to_rescan)
-    {
-      bitmap_iterator bi;
-      unsigned int uid;
-
-      EXECUTE_IF_SET_IN_BITMAP (debug->to_rescan, 0, uid, bi)
-	{
-	  struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
-	  if (insn_info)
-	    df_insn_rescan (insn_info->insn);
-	}
-      BITMAP_FREE (debug->to_rescan);
-    }
-}
-
-/* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
-   insn.  Create a bitmap for DEBUG as needed.  */
-void
-dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
-{
-  struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
-
-  newddu->use = use;
-  newddu->next = debug->head;
-  debug->head = newddu;
-
-  if (!debug->used)
-    debug->used = BITMAP_ALLOC (NULL);
-
-  bitmap_set_bit (debug->used, uregno);
-}
-
-/* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
-   before or after INSN (depending on WHERE), that binds a debug temp
-   to the widest-mode use of UREGNO, if WHERE is *_WITH_REG, or the
-   value stored in UREGNO by INSN otherwise, and replace all uses of
-   UREGNO in DEBUG with uses of the debug temp.  INSN must be where
-   UREGNO dies, if WHERE is *_BEFORE_*, or where it is set otherwise.
-   Return the number of debug insns emitted.  */
-int
-dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
-			rtx insn, enum debug_temp_where where)
-{
-  struct dead_debug_use **tailp = &debug->head;
-  struct dead_debug_use *cur;
-  struct dead_debug_use *uses = NULL;
-  struct dead_debug_use **usesp = &uses;
-  rtx reg = NULL;
-  rtx breg;
-  rtx dval;
-  rtx bind;
-
-  if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
-    return 0;
-
-  /* Move all uses of uregno from debug->head to uses, setting mode to
-     the widest referenced mode.  */
-  while ((cur = *tailp))
-    {
-      if (DF_REF_REGNO (cur->use) == uregno)
-	{
-	  *usesp = cur;
-	  usesp = &cur->next;
-	  *tailp = cur->next;
-	  cur->next = NULL;
-	  if (!reg
-	      || (GET_MODE_BITSIZE (GET_MODE (reg))
-		  < GET_MODE_BITSIZE (GET_MODE (*DF_REF_REAL_LOC (cur->use)))))
-	    reg = *DF_REF_REAL_LOC (cur->use);
-	}
-      else
-	tailp = &(*tailp)->next;
-    }
-
-  /* We may have dangling bits in debug->used for registers that were part
-     of a multi-register use, one component of which has been reset.  */
-  if (reg == NULL)
-    {
-      gcc_checking_assert (!uses);
-      return 0;
-    }
-
-  gcc_checking_assert (uses);
-
-  breg = reg;
-  /* Recover the expression INSN stores in REG.  */
-  if (where == DEBUG_TEMP_BEFORE_WITH_VALUE)
-    {
-      rtx set = single_set (insn);
-      rtx dest, src;
-
-      if (set)
-	{
-	  dest = SET_DEST (set);
-	  src = SET_SRC (set);
-	  /* Lose if the REG-setting insn is a CALL.  */
-	  if (GET_CODE (src) == CALL)
-	    {
-	      while (uses)
-		{
-		  cur = uses->next;
-		  XDELETE (uses);
-		  uses = cur;
-		}
-	      return 0;
-	    }
-	}
-
-      /* ??? Should we try to extract it from a PARALLEL?  */
-      if (!set)
-	breg = NULL;
-      /* Cool, it's the same REG, we can use SRC.  */
-      else if (dest == reg)
-	breg = copy_rtx (src);
-      else if (REG_P (dest))
-	{
-	  /* Hmm...  Something's fishy, we should be setting REG here.  */
-	  if (REGNO (dest) != REGNO (reg))
-	    breg = NULL;
-	  /* Ok, it's the same (hardware) REG, but with a different
-	     mode, so SUBREG it.  */
-	  else
-	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
-				   GET_MODE (dest));
-	}
-      else if (GET_CODE (dest) == SUBREG)
-	{
-	  /* We should be setting REG here.  Lose.  */
-	  if (REGNO (SUBREG_REG (dest)) != REGNO (reg))
-	    breg = NULL;
-	  /* Lose if we're setting something other than the lowpart of
-	     REG.  */
-	  else if (!subreg_lowpart_p (dest))
-	    breg = NULL;
-	  /* If we're not overwriting all the hardware registers that
-	     setting REG in its mode would, we won't know what to bind
-	     the debug temp to.  */
-	  else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
-		   && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
-		       != hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
-	    breg = NULL;
-	  /* Yay, we can use SRC, just adjust its mode.  */
-	  else
-	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
-				   GET_MODE (dest));
-	}
-      /* Oh well, we're out of luck.  */
-      else
-	breg = NULL;
-
-      /* We couldn't figure out the value stored in REG, so reset all
-	 of its pending debug uses.  */
-      if (!breg)
-	{
-	  dead_debug_reset_uses (debug, uses);
-	  return 0;
-	}
-    }
-
-  /* If there's a single (debug) use of an otherwise unused REG, and
-     the debug use is not part of a larger expression, then it
-     probably doesn't make sense to introduce a new debug temp.  */
-  if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next)
-    {
-      rtx next = DF_REF_INSN (uses->use);
-
-      if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
-	{
-	  XDELETE (uses);
-	  return 0;
-	}
-    }
-
-  /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
-  dval = make_debug_expr_from_rtl (reg);
-
-  /* Emit a debug bind insn before the insn in which reg dies.  */
-  bind = gen_rtx_VAR_LOCATION (GET_MODE (reg),
-			       DEBUG_EXPR_TREE_DECL (dval), breg,
-			       VAR_INIT_STATUS_INITIALIZED);
-
-  if (where == DEBUG_TEMP_AFTER_WITH_REG)
-    bind = emit_debug_insn_after (bind, insn);
-  else
-    bind = emit_debug_insn_before (bind, insn);
-  df_insn_rescan (bind);
-
-  /* Adjust all uses.  */
-  while ((cur = uses))
-    {
-      if (GET_MODE (*DF_REF_REAL_LOC (cur->use)) == GET_MODE (reg))
-	*DF_REF_REAL_LOC (cur->use) = dval;
-      else
-	*DF_REF_REAL_LOC (cur->use)
-	  = gen_lowpart_SUBREG (GET_MODE (*DF_REF_REAL_LOC (cur->use)), dval);
-      /* ??? Should we simplify subreg of subreg?  */
-      if (debug->to_rescan == NULL)
-	debug->to_rescan = BITMAP_ALLOC (NULL);
-      bitmap_set_bit (debug->to_rescan, INSN_UID (DF_REF_INSN (cur->use)));
-      uses = cur->next;
-      XDELETE (cur);
-    }
-
-  return 1;
-}
-
 /* Recompute the REG_DEAD and REG_UNUSED notes and compute register
    info: lifetime, bb, and number of defs and uses for basic block
    BB.  The three bitvectors are scratch regs used here.  */
Index: gcc/df.h
===================================================================
--- gcc/df.h.orig	2012-05-02 18:01:49.202605795 -0300
+++ gcc/df.h	2012-05-03 02:14:45.277539289 -0300
@@ -1101,46 +1101,4 @@ extern void union_defs (df_ref, struct w
 			unsigned int *used, struct web_entry *,
 			bool (*fun) (struct web_entry *, struct web_entry *));
 
-/* Debug uses of dead regs.  */
-
-/* Node of a linked list of uses of dead REGs in debug insns.  */
-struct dead_debug_use
-{
-  df_ref use;
-  struct dead_debug_use *next;
-};
-
-/* Linked list of the above, with a bitmap of the REGs in the
-   list.  */
-struct dead_debug
-{
-  struct dead_debug_use *head;
-  bitmap used;
-  bitmap to_rescan;
-};
-
-/* This type controls the behavior of dead_debug_insert_temp WRT
-   UREGNO and INSN.  */
-enum debug_temp_where
-  {
-    /* Bind a newly-created debug temporary to a REG for UREGNO, and
-       insert the debug insn before INSN.  REG is expected to die at
-       INSN.  */
-    DEBUG_TEMP_BEFORE_WITH_REG = -1,
-    /* Bind a newly-created debug temporary to the value INSN stores
-       in REG, and insert the debug insn before INSN.  */
-    DEBUG_TEMP_BEFORE_WITH_VALUE = 0,
-    /* Bind a newly-created debug temporary to a REG for UREGNO, and
-       insert the debug insn after INSN.  REG is expected to be set at
-       INSN.  */
-    DEBUG_TEMP_AFTER_WITH_REG = 1
-  };
-
-extern void dead_debug_init (struct dead_debug *, bitmap);
-extern void dead_debug_finish (struct dead_debug *, bitmap);
-extern void dead_debug_add (struct dead_debug *, df_ref, unsigned int);
-extern int dead_debug_insert_temp (struct dead_debug *,
-				   unsigned int uregno, rtx insn,
-				   enum debug_temp_where);
-
 #endif /* GCC_DF_H */
Index: gcc/rtl.h
===================================================================
--- gcc/rtl.h.orig	2012-05-02 18:05:06.870720834 -0300
+++ gcc/rtl.h	2012-05-03 02:14:45.350538254 -0300
@@ -2461,6 +2461,7 @@ extern unsigned int extended_count (cons
 extern rtx remove_death (unsigned int, rtx);
 extern void dump_combine_stats (FILE *);
 extern void dump_combine_total_stats (FILE *);
+extern rtx make_compound_operation (rtx, enum rtx_code);
 
 /* In cfgcleanup.c  */
 extern void delete_dead_jumptables (void);
Index: gcc/valtrack.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc/valtrack.c	2012-05-03 15:39:16.356330424 -0300
@@ -0,0 +1,480 @@
+/* Infrastructure for tracking user variable locations and values
+   throughout compilation.
+   Copyright (C) 2010, 2011, 2012  Free Software Foundation, Inc.
+   Contributed by Alexandre Oliva <aoliva@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "valtrack.h"
+#include "function.h"
+#include "regs.h"
+#include "emit-rtl.h"
+
+/* Replace auto-increment addressing modes with explicit operations to access
+   the same addresses without modifying the corresponding registers.  */
+
+#ifdef AUTO_INC_DEC
+static rtx
+cleanup_auto_inc_dec (rtx src, enum machine_mode mem_mode ATTRIBUTE_UNUSED)
+{
+  rtx x = src;
+  const RTX_CODE code = GET_CODE (x);
+  int i;
+  const char *fmt;
+
+  switch (code)
+    {
+    case REG:
+    case CONST_INT:
+    case CONST_DOUBLE:
+    case CONST_FIXED:
+    case CONST_VECTOR:
+    case SYMBOL_REF:
+    case CODE_LABEL:
+    case PC:
+    case CC0:
+    case SCRATCH:
+      /* SCRATCH must be shared because they represent distinct values.  */
+      return x;
+    case CLOBBER:
+      if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)
+	return x;
+      break;
+
+    case CONST:
+      if (shared_const_p (x))
+	return x;
+      break;
+
+    case MEM:
+      mem_mode = GET_MODE (x);
+      break;
+
+    case PRE_INC:
+    case PRE_DEC:
+      gcc_assert (mem_mode != VOIDmode && mem_mode != BLKmode);
+      return gen_rtx_PLUS (GET_MODE (x),
+			   cleanup_auto_inc_dec (XEXP (x, 0), mem_mode),
+			   GEN_INT (code == PRE_INC
+				    ? GET_MODE_SIZE (mem_mode)
+				    : -GET_MODE_SIZE (mem_mode)));
+
+    case POST_INC:
+    case POST_DEC:
+    case PRE_MODIFY:
+    case POST_MODIFY:
+      return cleanup_auto_inc_dec (code == PRE_MODIFY
+				   ? XEXP (x, 1) : XEXP (x, 0),
+				   mem_mode);
+
+    default:
+      break;
+    }
+
+  /* Copy the various flags, fields, and other information.  We assume
+     that all fields need copying, and then clear the fields that should
+     not be copied.  That is the sensible default behavior, and forces
+     us to explicitly document why we are *not* copying a flag.  */
+  x = shallow_copy_rtx (x);
+
+  /* We do not copy the USED flag, which is used as a mark bit during
+     walks over the RTL.  */
+  RTX_FLAG (x, used) = 0;
+
+  /* We do not copy FRAME_RELATED for INSNs.  */
+  if (INSN_P (x))
+    RTX_FLAG (x, frame_related) = 0;
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    if (fmt[i] == 'e')
+      XEXP (x, i) = cleanup_auto_inc_dec (XEXP (x, i), mem_mode);
+    else if (fmt[i] == 'E' || fmt[i] == 'V')
+      {
+	int j;
+	XVEC (x, i) = rtvec_alloc (XVECLEN (x, i));
+	for (j = 0; j < XVECLEN (x, i); j++)
+	  XVECEXP (x, i, j)
+	    = cleanup_auto_inc_dec (XVECEXP (src, i, j), mem_mode);
+      }
+
+  return x;
+}
+#endif
+
+/* Auxiliary data structure for propagate_for_debug_stmt.  */
+
+struct rtx_subst_pair
+{
+  rtx to;
+  bool adjusted;
+};
+
+/* DATA points to an rtx_subst_pair.  Return the value that should be
+   substituted.  */
+
+static rtx
+propagate_for_debug_subst (rtx from, const_rtx old_rtx, void *data)
+{
+  struct rtx_subst_pair *pair = (struct rtx_subst_pair *)data;
+
+  if (!rtx_equal_p (from, old_rtx))
+    return NULL_RTX;
+  if (!pair->adjusted)
+    {
+      pair->adjusted = true;
+#ifdef AUTO_INC_DEC
+      pair->to = cleanup_auto_inc_dec (pair->to, VOIDmode);
+#else
+      pair->to = copy_rtx (pair->to);
+#endif
+      pair->to = make_compound_operation (pair->to, SET);
+      return pair->to;
+    }
+  return copy_rtx (pair->to);
+}
+
+/* Replace all the occurrences of DEST with SRC in DEBUG_INSNs between INSN
+   and LAST, not including INSN, but including LAST.  Also stop at the end
+   of THIS_BASIC_BLOCK.  */
+
+void
+propagate_for_debug (rtx insn, rtx last, rtx dest, rtx src,
+		     basic_block this_basic_block)
+{
+  rtx next, loc, end = NEXT_INSN (BB_END (this_basic_block));
+
+  struct rtx_subst_pair p;
+  p.to = src;
+  p.adjusted = false;
+
+  next = NEXT_INSN (insn);
+  last = NEXT_INSN (last);
+  while (next != last && next != end)
+    {
+      insn = next;
+      next = NEXT_INSN (insn);
+      if (DEBUG_INSN_P (insn))
+	{
+	  loc = simplify_replace_fn_rtx (INSN_VAR_LOCATION_LOC (insn),
+					 dest, propagate_for_debug_subst, &p);
+	  if (loc == INSN_VAR_LOCATION_LOC (insn))
+	    continue;
+	  INSN_VAR_LOCATION_LOC (insn) = loc;
+	  df_insn_rescan (insn);
+	}
+    }
+}
+
+/* Initialize DEBUG to an empty list, and clear USED, if given.  */
+void
+dead_debug_init (struct dead_debug *debug, bitmap used)
+{
+  debug->head = NULL;
+  debug->used = used;
+  debug->to_rescan = NULL;
+  if (used)
+    bitmap_clear (used);
+}
+
+/* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of
+   each reset insn.  DEBUG is not otherwise modified.  If HEAD is
+   DEBUG->head, DEBUG->head will be set to NULL at the end.
+   Otherwise, entries from DEBUG->head that pertain to reset insns
+   will be removed, and only then rescanned.  */
+
+static void
+dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
+{
+  bool got_head = (debug->head == head);
+  bitmap rescan;
+  struct dead_debug_use **tailp = &debug->head;
+  struct dead_debug_use *cur;
+  bitmap_iterator bi;
+  unsigned int uid;
+
+  if (got_head)
+    rescan = NULL;
+  else
+    rescan = BITMAP_ALLOC (NULL);
+
+  while (head)
+    {
+      struct dead_debug_use *next = head->next;
+      rtx insn;
+
+      insn = DF_REF_INSN (head->use);
+      if (!next || DF_REF_INSN (next->use) != insn)
+	{
+	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
+	  if (got_head)
+	    df_insn_rescan_debug_internal (insn);
+	  else
+	    bitmap_set_bit (rescan, INSN_UID (insn));
+	  if (debug->to_rescan)
+	    bitmap_clear_bit (debug->to_rescan, INSN_UID (insn));
+	}
+      XDELETE (head);
+      head = next;
+    }
+
+  if (got_head)
+    {
+      debug->head = NULL;
+      return;
+    }
+
+  while ((cur = *tailp))
+    if (bitmap_bit_p (rescan, INSN_UID (DF_REF_INSN (cur->use))))
+      {
+	*tailp = cur->next;
+	XDELETE (cur);
+      }
+    else
+      tailp = &cur->next;
+
+  EXECUTE_IF_SET_IN_BITMAP (rescan, 0, uid, bi)
+    {
+      struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
+      if (insn_info)
+	df_insn_rescan_debug_internal (insn_info->insn);
+    }
+
+  BITMAP_FREE (rescan);
+}
+
+/* Reset all debug insns with pending uses.  Release the bitmap in it,
+   unless it is USED.  USED must be the same bitmap passed to
+   dead_debug_init.  */
+void
+dead_debug_finish (struct dead_debug *debug, bitmap used)
+{
+  if (debug->used != used)
+    BITMAP_FREE (debug->used);
+
+  dead_debug_reset_uses (debug, debug->head);
+
+  if (debug->to_rescan)
+    {
+      bitmap_iterator bi;
+      unsigned int uid;
+
+      EXECUTE_IF_SET_IN_BITMAP (debug->to_rescan, 0, uid, bi)
+	{
+	  struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
+	  if (insn_info)
+	    df_insn_rescan (insn_info->insn);
+	}
+      BITMAP_FREE (debug->to_rescan);
+    }
+}
+
+/* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
+   insn.  Create a bitmap for DEBUG as needed.  */
+void
+dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
+{
+  struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
+
+  newddu->use = use;
+  newddu->next = debug->head;
+  debug->head = newddu;
+
+  if (!debug->used)
+    debug->used = BITMAP_ALLOC (NULL);
+
+  bitmap_set_bit (debug->used, uregno);
+}
+
+/* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
+   before or after INSN (depending on WHERE), that binds a debug temp
+   to the widest-mode use of UREGNO, if WHERE is *_WITH_REG, or the
+   value stored in UREGNO by INSN otherwise, and replace all uses of
+   UREGNO in DEBUG with uses of the debug temp.  INSN must be where
+   UREGNO dies, if WHERE is *_BEFORE_*, or where it is set otherwise.
+   Return the number of debug insns emitted.  */
+int
+dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
+			rtx insn, enum debug_temp_where where)
+{
+  struct dead_debug_use **tailp = &debug->head;
+  struct dead_debug_use *cur;
+  struct dead_debug_use *uses = NULL;
+  struct dead_debug_use **usesp = &uses;
+  rtx reg = NULL;
+  rtx breg;
+  rtx dval;
+  rtx bind;
+
+  if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
+    return 0;
+
+  /* Move all uses of uregno from debug->head to uses, setting mode to
+     the widest referenced mode.  */
+  while ((cur = *tailp))
+    {
+      if (DF_REF_REGNO (cur->use) == uregno)
+	{
+	  *usesp = cur;
+	  usesp = &cur->next;
+	  *tailp = cur->next;
+	  cur->next = NULL;
+	  if (!reg
+	      || (GET_MODE_BITSIZE (GET_MODE (reg))
+		  < GET_MODE_BITSIZE (GET_MODE (*DF_REF_REAL_LOC (cur->use)))))
+	    reg = *DF_REF_REAL_LOC (cur->use);
+	}
+      else
+	tailp = &(*tailp)->next;
+    }
+
+  /* We may have dangling bits in debug->used for registers that were part
+     of a multi-register use, one component of which has been reset.  */
+  if (reg == NULL)
+    {
+      gcc_checking_assert (!uses);
+      return 0;
+    }
+
+  gcc_checking_assert (uses);
+
+  breg = reg;
+  /* Recover the expression INSN stores in REG.  */
+  if (where == DEBUG_TEMP_BEFORE_WITH_VALUE)
+    {
+      rtx set = single_set (insn);
+      rtx dest, src;
+
+      if (set)
+	{
+	  dest = SET_DEST (set);
+	  src = SET_SRC (set);
+	  /* Lose if the REG-setting insn is a CALL.  */
+	  if (GET_CODE (src) == CALL)
+	    {
+	      while (uses)
+		{
+		  cur = uses->next;
+		  XDELETE (uses);
+		  uses = cur;
+		}
+	      return 0;
+	    }
+	}
+
+      /* ??? Should we try to extract it from a PARALLEL?  */
+      if (!set)
+	breg = NULL;
+      /* Cool, it's the same REG, we can use SRC.  */
+      else if (dest == reg)
+	breg = copy_rtx (src);
+      else if (REG_P (dest))
+	{
+	  /* Hmm...  Something's fishy, we should be setting REG here.  */
+	  if (REGNO (dest) != REGNO (reg))
+	    breg = NULL;
+	  /* Ok, it's the same (hardware) REG, but with a different
+	     mode, so SUBREG it.  */
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      else if (GET_CODE (dest) == SUBREG)
+	{
+	  /* We should be setting REG here.  Lose.  */
+	  if (REGNO (SUBREG_REG (dest)) != REGNO (reg))
+	    breg = NULL;
+	  /* Lose if we're setting something other than the lowpart of
+	     REG.  */
+	  else if (!subreg_lowpart_p (dest))
+	    breg = NULL;
+	  /* If we're not overwriting all the hardware registers that
+	     setting REG in its mode would, we won't know what to bind
+	     the debug temp to.  */
+	  else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
+		   && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
+		       != hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
+	    breg = NULL;
+	  /* Yay, we can use SRC, just adjust its mode.  */
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      /* Oh well, we're out of luck.  */
+      else
+	breg = NULL;
+
+      /* We couldn't figure out the value stored in REG, so reset all
+	 of its pending debug uses.  */
+      if (!breg)
+	{
+	  dead_debug_reset_uses (debug, uses);
+	  return 0;
+	}
+    }
+
+  /* If there's a single (debug) use of an otherwise unused REG, and
+     the debug use is not part of a larger expression, then it
+     probably doesn't make sense to introduce a new debug temp.  */
+  if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next)
+    {
+      rtx next = DF_REF_INSN (uses->use);
+
+      if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
+	{
+	  XDELETE (uses);
+	  return 0;
+	}
+    }
+
+  /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
+  dval = make_debug_expr_from_rtl (reg);
+
+  /* Emit a debug bind insn before the insn in which reg dies.  */
+  bind = gen_rtx_VAR_LOCATION (GET_MODE (reg),
+			       DEBUG_EXPR_TREE_DECL (dval), breg,
+			       VAR_INIT_STATUS_INITIALIZED);
+
+  if (where == DEBUG_TEMP_AFTER_WITH_REG)
+    bind = emit_debug_insn_after (bind, insn);
+  else
+    bind = emit_debug_insn_before (bind, insn);
+  df_insn_rescan (bind);
+
+  /* Adjust all uses.  */
+  while ((cur = uses))
+    {
+      if (GET_MODE (*DF_REF_REAL_LOC (cur->use)) == GET_MODE (reg))
+	*DF_REF_REAL_LOC (cur->use) = dval;
+      else
+	*DF_REF_REAL_LOC (cur->use)
+	  = gen_lowpart_SUBREG (GET_MODE (*DF_REF_REAL_LOC (cur->use)), dval);
+      /* ??? Should we simplify subreg of subreg?  */
+      if (debug->to_rescan == NULL)
+	debug->to_rescan = BITMAP_ALLOC (NULL);
+      bitmap_set_bit (debug->to_rescan, INSN_UID (DF_REF_INSN (cur->use)));
+      uses = cur->next;
+      XDELETE (cur);
+    }
+
+  return 1;
+}
Index: gcc/valtrack.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc/valtrack.h	2012-05-03 15:34:07.763637575 -0300
@@ -0,0 +1,75 @@
+/* Infrastructure for tracking user variable locations and values
+   throughout compilation.
+   Copyright (C) 2010, 2011, 2012  Free Software Foundation, Inc.
+   Contributed by Alexandre Oliva <aoliva@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_VALTRACK_H
+#define GCC_VALTRACK_H
+
+#include "bitmap.h"
+#include "df.h"
+#include "rtl.h"
+#include "basic-block.h"
+
+/* Debug uses of dead regs.  */
+
+/* Node of a linked list of uses of dead REGs in debug insns.  */
+struct dead_debug_use
+{
+  df_ref use;
+  struct dead_debug_use *next;
+};
+
+/* Linked list of the above, with a bitmap of the REGs in the
+   list.  */
+struct dead_debug
+{
+  struct dead_debug_use *head;
+  bitmap used;
+  bitmap to_rescan;
+};
+
+/* This type controls the behavior of dead_debug_insert_temp WRT
+   UREGNO and INSN.  */
+enum debug_temp_where
+  {
+    /* Bind a newly-created debug temporary to a REG for UREGNO, and
+       insert the debug insn before INSN.  REG is expected to die at
+       INSN.  */
+    DEBUG_TEMP_BEFORE_WITH_REG = -1,
+    /* Bind a newly-created debug temporary to the value INSN stores
+       in REG, and insert the debug insn before INSN.  */
+    DEBUG_TEMP_BEFORE_WITH_VALUE = 0,
+    /* Bind a newly-created debug temporary to a REG for UREGNO, and
+       insert the debug insn after INSN.  REG is expected to be set at
+       INSN.  */
+    DEBUG_TEMP_AFTER_WITH_REG = 1
+  };
+
+extern void dead_debug_init (struct dead_debug *, bitmap);
+extern void dead_debug_finish (struct dead_debug *, bitmap);
+extern void dead_debug_add (struct dead_debug *, df_ref, unsigned int);
+extern int dead_debug_insert_temp (struct dead_debug *,
+				   unsigned int uregno, rtx insn,
+				   enum debug_temp_where);
+
+extern void propagate_for_debug (rtx, rtx, rtx, rtx, basic_block);
+
+
+#endif /* GCC_VALTRACK_H */

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: vta-valtrack-refactor-pr52983.patch --]
[-- Type: text/x-diff, Size: 1552 bytes --]

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

	PR debug/52983
	* valtrack.c (cleanup_auto_inc_dec): Implement unconditionally,
	falling back to copy_rtx on non-autoinc machines.
	(propagate_for_debug_subst): Always use cleanup_auto_inc_dec.

Index: gcc/valtrack.c
===================================================================
--- gcc/valtrack.c.orig	2012-05-03 15:43:36.889686894 -0300
+++ gcc/valtrack.c	2012-05-03 15:44:47.934692987 -0300
@@ -32,11 +32,11 @@ along with GCC; see the file COPYING3.  
 /* Replace auto-increment addressing modes with explicit operations to access
    the same addresses without modifying the corresponding registers.  */
 
-#ifdef AUTO_INC_DEC
 static rtx
 cleanup_auto_inc_dec (rtx src, enum machine_mode mem_mode ATTRIBUTE_UNUSED)
 {
   rtx x = src;
+#ifdef AUTO_INC_DEC
   const RTX_CODE code = GET_CODE (x);
   int i;
   const char *fmt;
@@ -117,9 +117,12 @@ cleanup_auto_inc_dec (rtx src, enum mach
 	    = cleanup_auto_inc_dec (XVECEXP (src, i, j), mem_mode);
       }
 
+#else /* !AUTO_INC_DEC */
+  x = copy_rtx (x);
+#endif /* !AUTO_INC_DEC */
+
   return x;
 }
-#endif
 
 /* Auxiliary data structure for propagate_for_debug_stmt.  */
 
@@ -142,11 +145,7 @@ propagate_for_debug_subst (rtx from, con
   if (!pair->adjusted)
     {
       pair->adjusted = true;
-#ifdef AUTO_INC_DEC
       pair->to = cleanup_auto_inc_dec (pair->to, VOIDmode);
-#else
-      pair->to = copy_rtx (pair->to);
-#endif
       pair->to = make_compound_operation (pair->to, SET);
       return pair->to;
     }

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: vta-valtrack-move-pr52983.patch --]
[-- Type: text/x-diff, Size: 41754 bytes --]

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

	PR debug/52983
	* valtrack.h, valtrack.c: New.
	* Makefile.in (VALTRACK_H): New.
	(OBJS): Add valtrack.o.
	(valtrack.o): New.
	(cselib.o, dce.o, df-problems.o, combine.o): Add VALTRACK_H.
	* combine.c: Include valtrack.h.
	(make_compound_operation): Publish.
	(cleanup_auto_inc_dec): Move to valtrack.c.
	(struct rtx_subst_pair, propagate_for_debug_subst): Likewise.
	(propagate_for_debug): Likewise.  Add this_basic_block parameter.
	Adjust all callers.
	* cselib.c: Include valtrack.h.
	* dce.c: Likewise.
	* df-problems.c: Likewise.
	(dead_debug_init, dead_debug_reset_uses): Move to valtrack.c.
	(dead_debug_finish, dead_debug_add): Likewise.
	(dead_debug_insert_temp): Likewise.
	* df.h (struct dead_debug_use): Move to valtrack.h.
	(struct dead_debug, enum debug_temp_where): Likewise.
	(dead_debug_init, dead_debug_reset_uses): Move to valtrack.h.
	(dead_debug_finish, dead_debug_add): Likewise.
	(dead_debug_insert_temp): Likewise.
	* rtl.h (make_compound_operation): Declare.

Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in.orig	2012-05-02 18:05:05.809736324 -0300
+++ gcc/Makefile.in	2012-05-03 02:14:44.795546132 -0300
@@ -894,6 +894,7 @@ CGRAPH_H = cgraph.h $(VEC_H) $(TREE_H) $
 	cif-code.def ipa-ref.h ipa-ref-inline.h $(LINKER_PLUGIN_API_H)
 DF_H = df.h $(BITMAP_H) $(REGSET_H) sbitmap.h $(BASIC_BLOCK_H) \
 	alloc-pool.h $(TIMEVAR_H)
+VALTRACK_H = valtrack.h $(BITMAP_H) $(DF_H) $(RTL_H) $(BASIC_BLOCK_H)
 RESOURCE_H = resource.h hard-reg-set.h $(DF_H)
 DDG_H = ddg.h sbitmap.h $(DF_H)
 GCC_H = gcc.h version.h $(DIAGNOSTIC_CORE_H)
@@ -1437,6 +1438,7 @@ OBJS = \
 	tree-vectorizer.o \
 	tree-vrp.o \
 	tree.o \
+	valtrack.o \
 	value-prof.o \
 	var-tracking.o \
 	varasm.o \
@@ -3002,8 +3004,8 @@ coverage.o : coverage.c $(GCOV_IO_H) $(C
 cselib.o : cselib.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h $(RECOG_H) \
    $(EMIT_RTL_H) $(DIAGNOSTIC_CORE_H) output.h $(FUNCTION_H) $(TREE_PASS_H) \
-   cselib.h gt-cselib.h $(GGC_H) $(TM_P_H) $(PARAMS_H) alloc-pool.h \
-   $(HASHTAB_H) $(TARGET_H) $(BITMAP_H)
+   cselib.h gt-cselib.h $(GGC_H) $(TM_P_H) $(VALTRACK_H) $(PARAMS_H) \
+   alloc-pool.h $(HASHTAB_H) $(TARGET_H) $(BITMAP_H)
 cse.o : cse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \
    hard-reg-set.h $(FLAGS_H) insn-config.h $(RECOG_H) $(EXPR_H) toplev.h $(DIAGNOSTIC_CORE_H) \
    output.h $(FUNCTION_H) $(BASIC_BLOCK_H) $(GGC_H) $(TM_P_H) $(TIMEVAR_H) \
@@ -3011,8 +3013,8 @@ cse.o : cse.c $(CONFIG_H) $(SYSTEM_H) co
    $(DF_H) $(DBGCNT_H)
 dce.o : dce.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(TREE_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) $(EXCEPT_H) $(DF_H) cselib.h \
-   $(DBGCNT_H) dce.h $(TIMEVAR_H) $(TREE_PASS_H) $(DBGCNT_H) $(TM_P_H) \
-   $(EMIT_RTL_H)
+   $(DBGCNT_H) dce.h $(VALTRACK_H) $(TIMEVAR_H) $(TREE_PASS_H) \
+   $(DBGCNT_H) $(TM_P_H) $(EMIT_RTL_H)
 dse.o : dse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(TREE_H) $(TM_P_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h \
    $(RECOG_H) $(EXPR_H) $(DF_H) cselib.h $(DBGCNT_H) $(TIMEVAR_H) \
@@ -3104,7 +3106,8 @@ df-core.o : df-core.c $(CONFIG_H) $(SYST
 df-problems.o : df-problems.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(RTL_H) insn-config.h $(RECOG_H) $(FUNCTION_H) $(REGS_H) alloc-pool.h \
    hard-reg-set.h $(BASIC_BLOCK_H) $(DF_H) $(BITMAP_H) sbitmap.h $(TIMEVAR_H) \
-   $(TM_P_H) $(TARGET_H) $(FLAGS_H) output.h $(EXCEPT_H) dce.h vecprim.h
+   $(TM_P_H) $(TARGET_H) $(FLAGS_H) output.h $(EXCEPT_H) dce.h \
+   vecprim.h $(VALTRACK_H)
 df-scan.o : df-scan.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    insn-config.h $(RECOG_H) $(FUNCTION_H) $(REGS_H) alloc-pool.h \
    hard-reg-set.h $(BASIC_BLOCK_H) $(DF_H) $(BITMAP_H) sbitmap.h $(TIMEVAR_H) \
@@ -3113,6 +3116,8 @@ df-scan.o : df-scan.c $(CONFIG_H) $(SYST
 regstat.o : regstat.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(TM_P_H) $(FLAGS_H) $(REGS_H) output.h $(EXCEPT_H) hard-reg-set.h \
    $(BASIC_BLOCK_H) $(TIMEVAR_H) $(DF_H)
+valtrack.o : valtrack.c $(VALTRACK_H) $(CONFIG_H) $(SYSTEM_H) \
+   coretypes.h $(TM_H) $(FUNCTION_H) $(REGS_H) $(EMIT_RTL_H)
 var-tracking.o : var-tracking.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(RTL_H) $(TREE_H) hard-reg-set.h insn-config.h reload.h $(FLAGS_H) \
    $(BASIC_BLOCK_H) output.h sbitmap.h alloc-pool.h $(FIBHEAP_H) $(HASHTAB_H) \
@@ -3212,9 +3217,10 @@ et-forest.o : et-forest.c $(CONFIG_H) $(
 combine.o : combine.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(FLAGS_H) $(FUNCTION_H) insn-config.h $(INSN_ATTR_H) $(REGS_H) $(EXPR_H) \
    rtlhooks-def.h $(BASIC_BLOCK_H) $(RECOG_H) hard-reg-set.h \
-   $(DIAGNOSTIC_CORE_H) $(TM_P_H) $(TREE_H) $(TARGET_H) output.h $(PARAMS_H) $(OPTABS_H) \
-   insn-codes.h $(TIMEVAR_H) $(TREE_PASS_H) $(DF_H) vecprim.h $(CGRAPH_H) \
-   $(OBSTACK_H)
+   $(DIAGNOSTIC_CORE_H) $(TM_P_H) $(TREE_H) $(TARGET_H) \
+   output.h $(PARAMS_H) $(OPTABS_H) \
+   insn-codes.h $(TIMEVAR_H) $(TREE_PASS_H) $(DF_H) $(VALTRACK_H) \
+   vecprim.h $(CGRAPH_H) $(OBSTACK_H)
 reginfo.o : reginfo.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    hard-reg-set.h $(FLAGS_H) $(BASIC_BLOCK_H) addresses.h $(REGS_H) \
    insn-config.h $(RECOG_H) reload.h $(DIAGNOSTIC_CORE_H) \
Index: gcc/combine.c
===================================================================
--- gcc/combine.c.orig	2012-05-02 18:01:48.846610990 -0300
+++ gcc/combine.c	2012-05-03 02:14:45.040542653 -0300
@@ -103,6 +103,7 @@ along with GCC; see the file COPYING3.  
 #include "timevar.h"
 #include "tree-pass.h"
 #include "df.h"
+#include "valtrack.h"
 #include "cgraph.h"
 #include "obstack.h"
 
@@ -427,7 +428,6 @@ static const_rtx expand_field_assignment
 static rtx make_extraction (enum machine_mode, rtx, HOST_WIDE_INT,
 			    rtx, unsigned HOST_WIDE_INT, int, int, int);
 static rtx extract_left_shift (rtx, int);
-static rtx make_compound_operation (rtx, enum rtx_code);
 static int get_pos_from_mask (unsigned HOST_WIDE_INT,
 			      unsigned HOST_WIDE_INT *);
 static rtx canon_reg_for_combine (rtx, rtx);
@@ -2360,161 +2360,6 @@ reg_subword_p (rtx x, rtx reg)
 	 && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT;
 }
 
-#ifdef AUTO_INC_DEC
-/* Replace auto-increment addressing modes with explicit operations to access
-   the same addresses without modifying the corresponding registers.  */
-
-static rtx
-cleanup_auto_inc_dec (rtx src, enum machine_mode mem_mode)
-{
-  rtx x = src;
-  const RTX_CODE code = GET_CODE (x);
-  int i;
-  const char *fmt;
-
-  switch (code)
-    {
-    case REG:
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case CONST_FIXED:
-    case CONST_VECTOR:
-    case SYMBOL_REF:
-    case CODE_LABEL:
-    case PC:
-    case CC0:
-    case SCRATCH:
-      /* SCRATCH must be shared because they represent distinct values.  */
-      return x;
-    case CLOBBER:
-      if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)
-	return x;
-      break;
-
-    case CONST:
-      if (shared_const_p (x))
-	return x;
-      break;
-
-    case MEM:
-      mem_mode = GET_MODE (x);
-      break;
-
-    case PRE_INC:
-    case PRE_DEC:
-      gcc_assert (mem_mode != VOIDmode && mem_mode != BLKmode);
-      return gen_rtx_PLUS (GET_MODE (x),
-			   cleanup_auto_inc_dec (XEXP (x, 0), mem_mode),
-			   GEN_INT (code == PRE_INC
-				    ? GET_MODE_SIZE (mem_mode)
-				    : -GET_MODE_SIZE (mem_mode)));
-
-    case POST_INC:
-    case POST_DEC:
-    case PRE_MODIFY:
-    case POST_MODIFY:
-      return cleanup_auto_inc_dec (code == PRE_MODIFY
-				   ? XEXP (x, 1) : XEXP (x, 0),
-				   mem_mode);
-
-    default:
-      break;
-    }
-
-  /* Copy the various flags, fields, and other information.  We assume
-     that all fields need copying, and then clear the fields that should
-     not be copied.  That is the sensible default behavior, and forces
-     us to explicitly document why we are *not* copying a flag.  */
-  x = shallow_copy_rtx (x);
-
-  /* We do not copy the USED flag, which is used as a mark bit during
-     walks over the RTL.  */
-  RTX_FLAG (x, used) = 0;
-
-  /* We do not copy FRAME_RELATED for INSNs.  */
-  if (INSN_P (x))
-    RTX_FLAG (x, frame_related) = 0;
-
-  fmt = GET_RTX_FORMAT (code);
-  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
-    if (fmt[i] == 'e')
-      XEXP (x, i) = cleanup_auto_inc_dec (XEXP (x, i), mem_mode);
-    else if (fmt[i] == 'E' || fmt[i] == 'V')
-      {
-	int j;
-	XVEC (x, i) = rtvec_alloc (XVECLEN (x, i));
-	for (j = 0; j < XVECLEN (x, i); j++)
-	  XVECEXP (x, i, j)
-	    = cleanup_auto_inc_dec (XVECEXP (src, i, j), mem_mode);
-      }
-
-  return x;
-}
-#endif
-
-/* Auxiliary data structure for propagate_for_debug_stmt.  */
-
-struct rtx_subst_pair
-{
-  rtx to;
-  bool adjusted;
-};
-
-/* DATA points to an rtx_subst_pair.  Return the value that should be
-   substituted.  */
-
-static rtx
-propagate_for_debug_subst (rtx from, const_rtx old_rtx, void *data)
-{
-  struct rtx_subst_pair *pair = (struct rtx_subst_pair *)data;
-
-  if (!rtx_equal_p (from, old_rtx))
-    return NULL_RTX;
-  if (!pair->adjusted)
-    {
-      pair->adjusted = true;
-#ifdef AUTO_INC_DEC
-      pair->to = cleanup_auto_inc_dec (pair->to, VOIDmode);
-#else
-      pair->to = copy_rtx (pair->to);
-#endif
-      pair->to = make_compound_operation (pair->to, SET);
-      return pair->to;
-    }
-  return copy_rtx (pair->to);
-}
-
-/* Replace all the occurrences of DEST with SRC in DEBUG_INSNs between INSN
-   and LAST, not including INSN, but including LAST.  Also stop at the end
-   of THIS_BASIC_BLOCK.  */
-
-static void
-propagate_for_debug (rtx insn, rtx last, rtx dest, rtx src)
-{
-  rtx next, loc, end = NEXT_INSN (BB_END (this_basic_block));
-
-  struct rtx_subst_pair p;
-  p.to = src;
-  p.adjusted = false;
-
-  next = NEXT_INSN (insn);
-  last = NEXT_INSN (last);
-  while (next != last && next != end)
-    {
-      insn = next;
-      next = NEXT_INSN (insn);
-      if (DEBUG_INSN_P (insn))
-	{
-	  loc = simplify_replace_fn_rtx (INSN_VAR_LOCATION_LOC (insn),
-					 dest, propagate_for_debug_subst, &p);
-	  if (loc == INSN_VAR_LOCATION_LOC (insn))
-	    continue;
-	  INSN_VAR_LOCATION_LOC (insn) = loc;
-	  df_insn_rescan (insn);
-	}
-    }
-}
-
 /* Delete the unconditional jump INSN and adjust the CFG correspondingly.
    Note that the INSN should be deleted *after* removing dead edges, so
    that the kept edge is the fallthrough edge for a (set (pc) (pc))
@@ -3974,7 +3819,8 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
 		   i2src while its original mode is temporarily
 		   restored, and then clear i2scratch so that we don't
 		   do it again later.  */
-		propagate_for_debug (i2, last_combined_insn, reg, i2src);
+		propagate_for_debug (i2, last_combined_insn, reg, i2src,
+				     this_basic_block);
 		i2scratch = false;
 		/* Put back the new mode.  */
 		adjust_reg_mode (reg, new_mode);
@@ -4008,10 +3854,12 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
 		   with this copy we have created; then, replace the
 		   copy with the SUBREG of the original shared reg,
 		   once again changed to the new mode.  */
-		propagate_for_debug (first, last, reg, tempreg);
+		propagate_for_debug (first, last, reg, tempreg,
+				     this_basic_block);
 		adjust_reg_mode (reg, new_mode);
 		propagate_for_debug (first, last, tempreg,
-				     lowpart_subreg (old_mode, reg, new_mode));
+				     lowpart_subreg (old_mode, reg, new_mode),
+				     this_basic_block);
 	      }
 	  }
     }
@@ -4223,14 +4071,16 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
     if (newi2pat)
       {
 	if (MAY_HAVE_DEBUG_INSNS && i2scratch)
-	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src);
+	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src,
+			       this_basic_block);
 	INSN_CODE (i2) = i2_code_number;
 	PATTERN (i2) = newi2pat;
       }
     else
       {
 	if (MAY_HAVE_DEBUG_INSNS && i2src)
-	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src);
+	  propagate_for_debug (i2, last_combined_insn, i2dest, i2src,
+			       this_basic_block);
 	SET_INSN_DELETED (i2);
       }
 
@@ -4239,7 +4089,8 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
 	LOG_LINKS (i1) = NULL;
 	REG_NOTES (i1) = 0;
 	if (MAY_HAVE_DEBUG_INSNS)
-	  propagate_for_debug (i1, last_combined_insn, i1dest, i1src);
+	  propagate_for_debug (i1, last_combined_insn, i1dest, i1src,
+			       this_basic_block);
 	SET_INSN_DELETED (i1);
       }
 
@@ -4248,7 +4099,8 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx
 	LOG_LINKS (i0) = NULL;
 	REG_NOTES (i0) = 0;
 	if (MAY_HAVE_DEBUG_INSNS)
-	  propagate_for_debug (i0, last_combined_insn, i0dest, i0src);
+	  propagate_for_debug (i0, last_combined_insn, i0dest, i0src,
+			       this_basic_block);
 	SET_INSN_DELETED (i0);
       }
 
@@ -7600,7 +7452,7 @@ extract_left_shift (rtx x, int count)
    being kludges), it is MEM.  When processing the arguments of a comparison
    or a COMPARE against zero, it is COMPARE.  */
 
-static rtx
+rtx
 make_compound_operation (rtx x, enum rtx_code in_code)
 {
   enum rtx_code code = GET_CODE (x);
Index: gcc/cselib.c
===================================================================
--- gcc/cselib.c.orig	2012-05-02 18:01:48.945609546 -0300
+++ gcc/cselib.c	2012-05-03 02:14:45.084542027 -0300
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  
 #include "hashtab.h"
 #include "tree-pass.h"
 #include "cselib.h"
+#include "valtrack.h"
 #include "params.h"
 #include "alloc-pool.h"
 #include "target.h"
Index: gcc/dce.c
===================================================================
--- gcc/dce.c.orig	2012-05-02 18:01:49.052607983 -0300
+++ gcc/dce.c	2012-05-03 02:14:45.130541377 -0300
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  
 #include "df.h"
 #include "cselib.h"
 #include "dce.h"
+#include "valtrack.h"
 #include "timevar.h"
 #include "tree-pass.h"
 #include "dbgcnt.h"
Index: gcc/df-problems.c
===================================================================
--- gcc/df-problems.c.orig	2012-05-02 18:01:49.127606889 -0300
+++ gcc/df-problems.c	2012-05-03 02:14:45.251539658 -0300
@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  
 #include "except.h"
 #include "dce.h"
 #include "vecprim.h"
+#include "valtrack.h"
 
 /* Note that turning REG_DEAD_DEBUGGING on will cause
    gcc.c-torture/unsorted/dump-noaddr.c to fail because it prints
@@ -3063,300 +3064,6 @@ df_create_unused_note (rtx insn, df_ref 
 }
 
 
-/* Initialize DEBUG to an empty list, and clear USED, if given.  */
-void
-dead_debug_init (struct dead_debug *debug, bitmap used)
-{
-  debug->head = NULL;
-  debug->used = used;
-  debug->to_rescan = NULL;
-  if (used)
-    bitmap_clear (used);
-}
-
-/* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of
-   each reset insn.  DEBUG is not otherwise modified.  If HEAD is
-   DEBUG->head, DEBUG->head will be set to NULL at the end.
-   Otherwise, entries from DEBUG->head that pertain to reset insns
-   will be removed, and only then rescanned.  */
-
-static void
-dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
-{
-  bool got_head = (debug->head == head);
-  bitmap rescan;
-  struct dead_debug_use **tailp = &debug->head;
-  struct dead_debug_use *cur;
-  bitmap_iterator bi;
-  unsigned int uid;
-
-  if (got_head)
-    rescan = NULL;
-  else
-    rescan = BITMAP_ALLOC (NULL);
-
-  while (head)
-    {
-      struct dead_debug_use *next = head->next;
-      rtx insn;
-
-      insn = DF_REF_INSN (head->use);
-      if (!next || DF_REF_INSN (next->use) != insn)
-	{
-	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-	  if (got_head)
-	    df_insn_rescan_debug_internal (insn);
-	  else
-	    bitmap_set_bit (rescan, INSN_UID (insn));
-	  if (debug->to_rescan)
-	    bitmap_clear_bit (debug->to_rescan, INSN_UID (insn));
-	}
-      XDELETE (head);
-      head = next;
-    }
-
-  if (got_head)
-    {
-      debug->head = NULL;
-      return;
-    }
-
-  while ((cur = *tailp))
-    if (bitmap_bit_p (rescan, INSN_UID (DF_REF_INSN (cur->use))))
-      {
-	*tailp = cur->next;
-	XDELETE (cur);
-      }
-    else
-      tailp = &cur->next;
-
-  EXECUTE_IF_SET_IN_BITMAP (rescan, 0, uid, bi)
-    {
-      struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
-      if (insn_info)
-	df_insn_rescan_debug_internal (insn_info->insn);
-    }
-
-  BITMAP_FREE (rescan);
-}
-
-/* Reset all debug insns with pending uses.  Release the bitmap in it,
-   unless it is USED.  USED must be the same bitmap passed to
-   dead_debug_init.  */
-void
-dead_debug_finish (struct dead_debug *debug, bitmap used)
-{
-  if (debug->used != used)
-    BITMAP_FREE (debug->used);
-
-  dead_debug_reset_uses (debug, debug->head);
-
-  if (debug->to_rescan)
-    {
-      bitmap_iterator bi;
-      unsigned int uid;
-
-      EXECUTE_IF_SET_IN_BITMAP (debug->to_rescan, 0, uid, bi)
-	{
-	  struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
-	  if (insn_info)
-	    df_insn_rescan (insn_info->insn);
-	}
-      BITMAP_FREE (debug->to_rescan);
-    }
-}
-
-/* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
-   insn.  Create a bitmap for DEBUG as needed.  */
-void
-dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
-{
-  struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
-
-  newddu->use = use;
-  newddu->next = debug->head;
-  debug->head = newddu;
-
-  if (!debug->used)
-    debug->used = BITMAP_ALLOC (NULL);
-
-  bitmap_set_bit (debug->used, uregno);
-}
-
-/* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
-   before or after INSN (depending on WHERE), that binds a debug temp
-   to the widest-mode use of UREGNO, if WHERE is *_WITH_REG, or the
-   value stored in UREGNO by INSN otherwise, and replace all uses of
-   UREGNO in DEBUG with uses of the debug temp.  INSN must be where
-   UREGNO dies, if WHERE is *_BEFORE_*, or where it is set otherwise.
-   Return the number of debug insns emitted.  */
-int
-dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
-			rtx insn, enum debug_temp_where where)
-{
-  struct dead_debug_use **tailp = &debug->head;
-  struct dead_debug_use *cur;
-  struct dead_debug_use *uses = NULL;
-  struct dead_debug_use **usesp = &uses;
-  rtx reg = NULL;
-  rtx breg;
-  rtx dval;
-  rtx bind;
-
-  if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
-    return 0;
-
-  /* Move all uses of uregno from debug->head to uses, setting mode to
-     the widest referenced mode.  */
-  while ((cur = *tailp))
-    {
-      if (DF_REF_REGNO (cur->use) == uregno)
-	{
-	  *usesp = cur;
-	  usesp = &cur->next;
-	  *tailp = cur->next;
-	  cur->next = NULL;
-	  if (!reg
-	      || (GET_MODE_BITSIZE (GET_MODE (reg))
-		  < GET_MODE_BITSIZE (GET_MODE (*DF_REF_REAL_LOC (cur->use)))))
-	    reg = *DF_REF_REAL_LOC (cur->use);
-	}
-      else
-	tailp = &(*tailp)->next;
-    }
-
-  /* We may have dangling bits in debug->used for registers that were part
-     of a multi-register use, one component of which has been reset.  */
-  if (reg == NULL)
-    {
-      gcc_checking_assert (!uses);
-      return 0;
-    }
-
-  gcc_checking_assert (uses);
-
-  breg = reg;
-  /* Recover the expression INSN stores in REG.  */
-  if (where == DEBUG_TEMP_BEFORE_WITH_VALUE)
-    {
-      rtx set = single_set (insn);
-      rtx dest, src;
-
-      if (set)
-	{
-	  dest = SET_DEST (set);
-	  src = SET_SRC (set);
-	  /* Lose if the REG-setting insn is a CALL.  */
-	  if (GET_CODE (src) == CALL)
-	    {
-	      while (uses)
-		{
-		  cur = uses->next;
-		  XDELETE (uses);
-		  uses = cur;
-		}
-	      return 0;
-	    }
-	}
-
-      /* ??? Should we try to extract it from a PARALLEL?  */
-      if (!set)
-	breg = NULL;
-      /* Cool, it's the same REG, we can use SRC.  */
-      else if (dest == reg)
-	breg = copy_rtx (src);
-      else if (REG_P (dest))
-	{
-	  /* Hmm...  Something's fishy, we should be setting REG here.  */
-	  if (REGNO (dest) != REGNO (reg))
-	    breg = NULL;
-	  /* Ok, it's the same (hardware) REG, but with a different
-	     mode, so SUBREG it.  */
-	  else
-	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
-				   GET_MODE (dest));
-	}
-      else if (GET_CODE (dest) == SUBREG)
-	{
-	  /* We should be setting REG here.  Lose.  */
-	  if (REGNO (SUBREG_REG (dest)) != REGNO (reg))
-	    breg = NULL;
-	  /* Lose if we're setting something other than the lowpart of
-	     REG.  */
-	  else if (!subreg_lowpart_p (dest))
-	    breg = NULL;
-	  /* If we're not overwriting all the hardware registers that
-	     setting REG in its mode would, we won't know what to bind
-	     the debug temp to.  */
-	  else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
-		   && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
-		       != hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
-	    breg = NULL;
-	  /* Yay, we can use SRC, just adjust its mode.  */
-	  else
-	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
-				   GET_MODE (dest));
-	}
-      /* Oh well, we're out of luck.  */
-      else
-	breg = NULL;
-
-      /* We couldn't figure out the value stored in REG, so reset all
-	 of its pending debug uses.  */
-      if (!breg)
-	{
-	  dead_debug_reset_uses (debug, uses);
-	  return 0;
-	}
-    }
-
-  /* If there's a single (debug) use of an otherwise unused REG, and
-     the debug use is not part of a larger expression, then it
-     probably doesn't make sense to introduce a new debug temp.  */
-  if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next)
-    {
-      rtx next = DF_REF_INSN (uses->use);
-
-      if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
-	{
-	  XDELETE (uses);
-	  return 0;
-	}
-    }
-
-  /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
-  dval = make_debug_expr_from_rtl (reg);
-
-  /* Emit a debug bind insn before the insn in which reg dies.  */
-  bind = gen_rtx_VAR_LOCATION (GET_MODE (reg),
-			       DEBUG_EXPR_TREE_DECL (dval), breg,
-			       VAR_INIT_STATUS_INITIALIZED);
-
-  if (where == DEBUG_TEMP_AFTER_WITH_REG)
-    bind = emit_debug_insn_after (bind, insn);
-  else
-    bind = emit_debug_insn_before (bind, insn);
-  df_insn_rescan (bind);
-
-  /* Adjust all uses.  */
-  while ((cur = uses))
-    {
-      if (GET_MODE (*DF_REF_REAL_LOC (cur->use)) == GET_MODE (reg))
-	*DF_REF_REAL_LOC (cur->use) = dval;
-      else
-	*DF_REF_REAL_LOC (cur->use)
-	  = gen_lowpart_SUBREG (GET_MODE (*DF_REF_REAL_LOC (cur->use)), dval);
-      /* ??? Should we simplify subreg of subreg?  */
-      if (debug->to_rescan == NULL)
-	debug->to_rescan = BITMAP_ALLOC (NULL);
-      bitmap_set_bit (debug->to_rescan, INSN_UID (DF_REF_INSN (cur->use)));
-      uses = cur->next;
-      XDELETE (cur);
-    }
-
-  return 1;
-}
-
 /* Recompute the REG_DEAD and REG_UNUSED notes and compute register
    info: lifetime, bb, and number of defs and uses for basic block
    BB.  The three bitvectors are scratch regs used here.  */
Index: gcc/df.h
===================================================================
--- gcc/df.h.orig	2012-05-02 18:01:49.202605795 -0300
+++ gcc/df.h	2012-05-03 02:14:45.277539289 -0300
@@ -1101,46 +1101,4 @@ extern void union_defs (df_ref, struct w
 			unsigned int *used, struct web_entry *,
 			bool (*fun) (struct web_entry *, struct web_entry *));
 
-/* Debug uses of dead regs.  */
-
-/* Node of a linked list of uses of dead REGs in debug insns.  */
-struct dead_debug_use
-{
-  df_ref use;
-  struct dead_debug_use *next;
-};
-
-/* Linked list of the above, with a bitmap of the REGs in the
-   list.  */
-struct dead_debug
-{
-  struct dead_debug_use *head;
-  bitmap used;
-  bitmap to_rescan;
-};
-
-/* This type controls the behavior of dead_debug_insert_temp WRT
-   UREGNO and INSN.  */
-enum debug_temp_where
-  {
-    /* Bind a newly-created debug temporary to a REG for UREGNO, and
-       insert the debug insn before INSN.  REG is expected to die at
-       INSN.  */
-    DEBUG_TEMP_BEFORE_WITH_REG = -1,
-    /* Bind a newly-created debug temporary to the value INSN stores
-       in REG, and insert the debug insn before INSN.  */
-    DEBUG_TEMP_BEFORE_WITH_VALUE = 0,
-    /* Bind a newly-created debug temporary to a REG for UREGNO, and
-       insert the debug insn after INSN.  REG is expected to be set at
-       INSN.  */
-    DEBUG_TEMP_AFTER_WITH_REG = 1
-  };
-
-extern void dead_debug_init (struct dead_debug *, bitmap);
-extern void dead_debug_finish (struct dead_debug *, bitmap);
-extern void dead_debug_add (struct dead_debug *, df_ref, unsigned int);
-extern int dead_debug_insert_temp (struct dead_debug *,
-				   unsigned int uregno, rtx insn,
-				   enum debug_temp_where);
-
 #endif /* GCC_DF_H */
Index: gcc/rtl.h
===================================================================
--- gcc/rtl.h.orig	2012-05-02 18:05:06.870720834 -0300
+++ gcc/rtl.h	2012-05-03 02:14:45.350538254 -0300
@@ -2461,6 +2461,7 @@ extern unsigned int extended_count (cons
 extern rtx remove_death (unsigned int, rtx);
 extern void dump_combine_stats (FILE *);
 extern void dump_combine_total_stats (FILE *);
+extern rtx make_compound_operation (rtx, enum rtx_code);
 
 /* In cfgcleanup.c  */
 extern void delete_dead_jumptables (void);
Index: gcc/valtrack.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc/valtrack.c	2012-05-03 15:39:16.356330424 -0300
@@ -0,0 +1,480 @@
+/* Infrastructure for tracking user variable locations and values
+   throughout compilation.
+   Copyright (C) 2010, 2011, 2012  Free Software Foundation, Inc.
+   Contributed by Alexandre Oliva <aoliva@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "valtrack.h"
+#include "function.h"
+#include "regs.h"
+#include "emit-rtl.h"
+
+/* Replace auto-increment addressing modes with explicit operations to access
+   the same addresses without modifying the corresponding registers.  */
+
+#ifdef AUTO_INC_DEC
+static rtx
+cleanup_auto_inc_dec (rtx src, enum machine_mode mem_mode ATTRIBUTE_UNUSED)
+{
+  rtx x = src;
+  const RTX_CODE code = GET_CODE (x);
+  int i;
+  const char *fmt;
+
+  switch (code)
+    {
+    case REG:
+    case CONST_INT:
+    case CONST_DOUBLE:
+    case CONST_FIXED:
+    case CONST_VECTOR:
+    case SYMBOL_REF:
+    case CODE_LABEL:
+    case PC:
+    case CC0:
+    case SCRATCH:
+      /* SCRATCH must be shared because they represent distinct values.  */
+      return x;
+    case CLOBBER:
+      if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)
+	return x;
+      break;
+
+    case CONST:
+      if (shared_const_p (x))
+	return x;
+      break;
+
+    case MEM:
+      mem_mode = GET_MODE (x);
+      break;
+
+    case PRE_INC:
+    case PRE_DEC:
+      gcc_assert (mem_mode != VOIDmode && mem_mode != BLKmode);
+      return gen_rtx_PLUS (GET_MODE (x),
+			   cleanup_auto_inc_dec (XEXP (x, 0), mem_mode),
+			   GEN_INT (code == PRE_INC
+				    ? GET_MODE_SIZE (mem_mode)
+				    : -GET_MODE_SIZE (mem_mode)));
+
+    case POST_INC:
+    case POST_DEC:
+    case PRE_MODIFY:
+    case POST_MODIFY:
+      return cleanup_auto_inc_dec (code == PRE_MODIFY
+				   ? XEXP (x, 1) : XEXP (x, 0),
+				   mem_mode);
+
+    default:
+      break;
+    }
+
+  /* Copy the various flags, fields, and other information.  We assume
+     that all fields need copying, and then clear the fields that should
+     not be copied.  That is the sensible default behavior, and forces
+     us to explicitly document why we are *not* copying a flag.  */
+  x = shallow_copy_rtx (x);
+
+  /* We do not copy the USED flag, which is used as a mark bit during
+     walks over the RTL.  */
+  RTX_FLAG (x, used) = 0;
+
+  /* We do not copy FRAME_RELATED for INSNs.  */
+  if (INSN_P (x))
+    RTX_FLAG (x, frame_related) = 0;
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    if (fmt[i] == 'e')
+      XEXP (x, i) = cleanup_auto_inc_dec (XEXP (x, i), mem_mode);
+    else if (fmt[i] == 'E' || fmt[i] == 'V')
+      {
+	int j;
+	XVEC (x, i) = rtvec_alloc (XVECLEN (x, i));
+	for (j = 0; j < XVECLEN (x, i); j++)
+	  XVECEXP (x, i, j)
+	    = cleanup_auto_inc_dec (XVECEXP (src, i, j), mem_mode);
+      }
+
+  return x;
+}
+#endif
+
+/* Auxiliary data structure for propagate_for_debug_stmt.  */
+
+struct rtx_subst_pair
+{
+  rtx to;
+  bool adjusted;
+};
+
+/* DATA points to an rtx_subst_pair.  Return the value that should be
+   substituted.  */
+
+static rtx
+propagate_for_debug_subst (rtx from, const_rtx old_rtx, void *data)
+{
+  struct rtx_subst_pair *pair = (struct rtx_subst_pair *)data;
+
+  if (!rtx_equal_p (from, old_rtx))
+    return NULL_RTX;
+  if (!pair->adjusted)
+    {
+      pair->adjusted = true;
+#ifdef AUTO_INC_DEC
+      pair->to = cleanup_auto_inc_dec (pair->to, VOIDmode);
+#else
+      pair->to = copy_rtx (pair->to);
+#endif
+      pair->to = make_compound_operation (pair->to, SET);
+      return pair->to;
+    }
+  return copy_rtx (pair->to);
+}
+
+/* Replace all the occurrences of DEST with SRC in DEBUG_INSNs between INSN
+   and LAST, not including INSN, but including LAST.  Also stop at the end
+   of THIS_BASIC_BLOCK.  */
+
+void
+propagate_for_debug (rtx insn, rtx last, rtx dest, rtx src,
+		     basic_block this_basic_block)
+{
+  rtx next, loc, end = NEXT_INSN (BB_END (this_basic_block));
+
+  struct rtx_subst_pair p;
+  p.to = src;
+  p.adjusted = false;
+
+  next = NEXT_INSN (insn);
+  last = NEXT_INSN (last);
+  while (next != last && next != end)
+    {
+      insn = next;
+      next = NEXT_INSN (insn);
+      if (DEBUG_INSN_P (insn))
+	{
+	  loc = simplify_replace_fn_rtx (INSN_VAR_LOCATION_LOC (insn),
+					 dest, propagate_for_debug_subst, &p);
+	  if (loc == INSN_VAR_LOCATION_LOC (insn))
+	    continue;
+	  INSN_VAR_LOCATION_LOC (insn) = loc;
+	  df_insn_rescan (insn);
+	}
+    }
+}
+
+/* Initialize DEBUG to an empty list, and clear USED, if given.  */
+void
+dead_debug_init (struct dead_debug *debug, bitmap used)
+{
+  debug->head = NULL;
+  debug->used = used;
+  debug->to_rescan = NULL;
+  if (used)
+    bitmap_clear (used);
+}
+
+/* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of
+   each reset insn.  DEBUG is not otherwise modified.  If HEAD is
+   DEBUG->head, DEBUG->head will be set to NULL at the end.
+   Otherwise, entries from DEBUG->head that pertain to reset insns
+   will be removed, and only then rescanned.  */
+
+static void
+dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
+{
+  bool got_head = (debug->head == head);
+  bitmap rescan;
+  struct dead_debug_use **tailp = &debug->head;
+  struct dead_debug_use *cur;
+  bitmap_iterator bi;
+  unsigned int uid;
+
+  if (got_head)
+    rescan = NULL;
+  else
+    rescan = BITMAP_ALLOC (NULL);
+
+  while (head)
+    {
+      struct dead_debug_use *next = head->next;
+      rtx insn;
+
+      insn = DF_REF_INSN (head->use);
+      if (!next || DF_REF_INSN (next->use) != insn)
+	{
+	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
+	  if (got_head)
+	    df_insn_rescan_debug_internal (insn);
+	  else
+	    bitmap_set_bit (rescan, INSN_UID (insn));
+	  if (debug->to_rescan)
+	    bitmap_clear_bit (debug->to_rescan, INSN_UID (insn));
+	}
+      XDELETE (head);
+      head = next;
+    }
+
+  if (got_head)
+    {
+      debug->head = NULL;
+      return;
+    }
+
+  while ((cur = *tailp))
+    if (bitmap_bit_p (rescan, INSN_UID (DF_REF_INSN (cur->use))))
+      {
+	*tailp = cur->next;
+	XDELETE (cur);
+      }
+    else
+      tailp = &cur->next;
+
+  EXECUTE_IF_SET_IN_BITMAP (rescan, 0, uid, bi)
+    {
+      struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
+      if (insn_info)
+	df_insn_rescan_debug_internal (insn_info->insn);
+    }
+
+  BITMAP_FREE (rescan);
+}
+
+/* Reset all debug insns with pending uses.  Release the bitmap in it,
+   unless it is USED.  USED must be the same bitmap passed to
+   dead_debug_init.  */
+void
+dead_debug_finish (struct dead_debug *debug, bitmap used)
+{
+  if (debug->used != used)
+    BITMAP_FREE (debug->used);
+
+  dead_debug_reset_uses (debug, debug->head);
+
+  if (debug->to_rescan)
+    {
+      bitmap_iterator bi;
+      unsigned int uid;
+
+      EXECUTE_IF_SET_IN_BITMAP (debug->to_rescan, 0, uid, bi)
+	{
+	  struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
+	  if (insn_info)
+	    df_insn_rescan (insn_info->insn);
+	}
+      BITMAP_FREE (debug->to_rescan);
+    }
+}
+
+/* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
+   insn.  Create a bitmap for DEBUG as needed.  */
+void
+dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
+{
+  struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
+
+  newddu->use = use;
+  newddu->next = debug->head;
+  debug->head = newddu;
+
+  if (!debug->used)
+    debug->used = BITMAP_ALLOC (NULL);
+
+  bitmap_set_bit (debug->used, uregno);
+}
+
+/* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
+   before or after INSN (depending on WHERE), that binds a debug temp
+   to the widest-mode use of UREGNO, if WHERE is *_WITH_REG, or the
+   value stored in UREGNO by INSN otherwise, and replace all uses of
+   UREGNO in DEBUG with uses of the debug temp.  INSN must be where
+   UREGNO dies, if WHERE is *_BEFORE_*, or where it is set otherwise.
+   Return the number of debug insns emitted.  */
+int
+dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
+			rtx insn, enum debug_temp_where where)
+{
+  struct dead_debug_use **tailp = &debug->head;
+  struct dead_debug_use *cur;
+  struct dead_debug_use *uses = NULL;
+  struct dead_debug_use **usesp = &uses;
+  rtx reg = NULL;
+  rtx breg;
+  rtx dval;
+  rtx bind;
+
+  if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
+    return 0;
+
+  /* Move all uses of uregno from debug->head to uses, setting mode to
+     the widest referenced mode.  */
+  while ((cur = *tailp))
+    {
+      if (DF_REF_REGNO (cur->use) == uregno)
+	{
+	  *usesp = cur;
+	  usesp = &cur->next;
+	  *tailp = cur->next;
+	  cur->next = NULL;
+	  if (!reg
+	      || (GET_MODE_BITSIZE (GET_MODE (reg))
+		  < GET_MODE_BITSIZE (GET_MODE (*DF_REF_REAL_LOC (cur->use)))))
+	    reg = *DF_REF_REAL_LOC (cur->use);
+	}
+      else
+	tailp = &(*tailp)->next;
+    }
+
+  /* We may have dangling bits in debug->used for registers that were part
+     of a multi-register use, one component of which has been reset.  */
+  if (reg == NULL)
+    {
+      gcc_checking_assert (!uses);
+      return 0;
+    }
+
+  gcc_checking_assert (uses);
+
+  breg = reg;
+  /* Recover the expression INSN stores in REG.  */
+  if (where == DEBUG_TEMP_BEFORE_WITH_VALUE)
+    {
+      rtx set = single_set (insn);
+      rtx dest, src;
+
+      if (set)
+	{
+	  dest = SET_DEST (set);
+	  src = SET_SRC (set);
+	  /* Lose if the REG-setting insn is a CALL.  */
+	  if (GET_CODE (src) == CALL)
+	    {
+	      while (uses)
+		{
+		  cur = uses->next;
+		  XDELETE (uses);
+		  uses = cur;
+		}
+	      return 0;
+	    }
+	}
+
+      /* ??? Should we try to extract it from a PARALLEL?  */
+      if (!set)
+	breg = NULL;
+      /* Cool, it's the same REG, we can use SRC.  */
+      else if (dest == reg)
+	breg = copy_rtx (src);
+      else if (REG_P (dest))
+	{
+	  /* Hmm...  Something's fishy, we should be setting REG here.  */
+	  if (REGNO (dest) != REGNO (reg))
+	    breg = NULL;
+	  /* Ok, it's the same (hardware) REG, but with a different
+	     mode, so SUBREG it.  */
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      else if (GET_CODE (dest) == SUBREG)
+	{
+	  /* We should be setting REG here.  Lose.  */
+	  if (REGNO (SUBREG_REG (dest)) != REGNO (reg))
+	    breg = NULL;
+	  /* Lose if we're setting something other than the lowpart of
+	     REG.  */
+	  else if (!subreg_lowpart_p (dest))
+	    breg = NULL;
+	  /* If we're not overwriting all the hardware registers that
+	     setting REG in its mode would, we won't know what to bind
+	     the debug temp to.  */
+	  else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
+		   && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
+		       != hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
+	    breg = NULL;
+	  /* Yay, we can use SRC, just adjust its mode.  */
+	  else
+	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+				   GET_MODE (dest));
+	}
+      /* Oh well, we're out of luck.  */
+      else
+	breg = NULL;
+
+      /* We couldn't figure out the value stored in REG, so reset all
+	 of its pending debug uses.  */
+      if (!breg)
+	{
+	  dead_debug_reset_uses (debug, uses);
+	  return 0;
+	}
+    }
+
+  /* If there's a single (debug) use of an otherwise unused REG, and
+     the debug use is not part of a larger expression, then it
+     probably doesn't make sense to introduce a new debug temp.  */
+  if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next)
+    {
+      rtx next = DF_REF_INSN (uses->use);
+
+      if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
+	{
+	  XDELETE (uses);
+	  return 0;
+	}
+    }
+
+  /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
+  dval = make_debug_expr_from_rtl (reg);
+
+  /* Emit a debug bind insn before the insn in which reg dies.  */
+  bind = gen_rtx_VAR_LOCATION (GET_MODE (reg),
+			       DEBUG_EXPR_TREE_DECL (dval), breg,
+			       VAR_INIT_STATUS_INITIALIZED);
+
+  if (where == DEBUG_TEMP_AFTER_WITH_REG)
+    bind = emit_debug_insn_after (bind, insn);
+  else
+    bind = emit_debug_insn_before (bind, insn);
+  df_insn_rescan (bind);
+
+  /* Adjust all uses.  */
+  while ((cur = uses))
+    {
+      if (GET_MODE (*DF_REF_REAL_LOC (cur->use)) == GET_MODE (reg))
+	*DF_REF_REAL_LOC (cur->use) = dval;
+      else
+	*DF_REF_REAL_LOC (cur->use)
+	  = gen_lowpart_SUBREG (GET_MODE (*DF_REF_REAL_LOC (cur->use)), dval);
+      /* ??? Should we simplify subreg of subreg?  */
+      if (debug->to_rescan == NULL)
+	debug->to_rescan = BITMAP_ALLOC (NULL);
+      bitmap_set_bit (debug->to_rescan, INSN_UID (DF_REF_INSN (cur->use)));
+      uses = cur->next;
+      XDELETE (cur);
+    }
+
+  return 1;
+}
Index: gcc/valtrack.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc/valtrack.h	2012-05-03 15:34:07.763637575 -0300
@@ -0,0 +1,75 @@
+/* Infrastructure for tracking user variable locations and values
+   throughout compilation.
+   Copyright (C) 2010, 2011, 2012  Free Software Foundation, Inc.
+   Contributed by Alexandre Oliva <aoliva@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_VALTRACK_H
+#define GCC_VALTRACK_H
+
+#include "bitmap.h"
+#include "df.h"
+#include "rtl.h"
+#include "basic-block.h"
+
+/* Debug uses of dead regs.  */
+
+/* Node of a linked list of uses of dead REGs in debug insns.  */
+struct dead_debug_use
+{
+  df_ref use;
+  struct dead_debug_use *next;
+};
+
+/* Linked list of the above, with a bitmap of the REGs in the
+   list.  */
+struct dead_debug
+{
+  struct dead_debug_use *head;
+  bitmap used;
+  bitmap to_rescan;
+};
+
+/* This type controls the behavior of dead_debug_insert_temp WRT
+   UREGNO and INSN.  */
+enum debug_temp_where
+  {
+    /* Bind a newly-created debug temporary to a REG for UREGNO, and
+       insert the debug insn before INSN.  REG is expected to die at
+       INSN.  */
+    DEBUG_TEMP_BEFORE_WITH_REG = -1,
+    /* Bind a newly-created debug temporary to the value INSN stores
+       in REG, and insert the debug insn before INSN.  */
+    DEBUG_TEMP_BEFORE_WITH_VALUE = 0,
+    /* Bind a newly-created debug temporary to a REG for UREGNO, and
+       insert the debug insn after INSN.  REG is expected to be set at
+       INSN.  */
+    DEBUG_TEMP_AFTER_WITH_REG = 1
+  };
+
+extern void dead_debug_init (struct dead_debug *, bitmap);
+extern void dead_debug_finish (struct dead_debug *, bitmap);
+extern void dead_debug_add (struct dead_debug *, df_ref, unsigned int);
+extern int dead_debug_insert_temp (struct dead_debug *,
+				   unsigned int uregno, rtx insn,
+				   enum debug_temp_where);
+
+extern void propagate_for_debug (rtx, rtx, rtx, rtx, basic_block);
+
+
+#endif /* GCC_VALTRACK_H */

[-- Attachment #5: Type: text/plain, Size: 257 bytes --]


-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PR52983, PR48866] eliminate autoinc from debug_insn locs
  2012-05-03 18:27                     ` [PR52983] eliminate autoinc from debug_insn locs (was: Re: fix left-over debug insns in DCE) Alexandre Oliva
  2012-05-03 22:40                       ` [PR52983] eliminate autoinc from debug_insn locs Alexandre Oliva
@ 2012-05-03 22:46                       ` Alexandre Oliva
  2012-06-13  8:56                         ` Alexandre Oliva
  1 sibling, 1 reply; 23+ messages in thread
From: Alexandre Oliva @ 2012-05-03 22:46 UTC (permalink / raw)
  To: jakub; +Cc: gcc-patches, Eric Botcazou, Paolo Bonzini

[-- Attachment #1: Type: text/plain, Size: 491 bytes --]

On May  3, 2012, Alexandre Oliva <aoliva@redhat.com> wrote:

> My recent patch for PR48866

... had some inconsistency in behavior between dce and word_dce, as you
pointed out.  I couldn't find any reason for that, so I made them the
same.

Regstrapped on x86_64-linux-gnu and i686-linux-gnu, no regressions
detected by the testsuite, but I'm concerned about potential debug info
regressions, which I'm yet to test.

Once I'm convinced it doesn't regress debug info, is this ok to install?


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: vta-dce-regularize-pr48866-pr52983.patch --]
[-- Type: text/x-diff, Size: 2218 bytes --]

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

	PR debug/52983
	PR debug/48866
	* dce.c (word_dce_process_block): Insert debug temps only if the
	insn is not marked.
	(dce_process_block): Likewise, and if debug.used is not empty,
	and only after iterating over all DEFs that might mark the insn.

Index: gcc/dce.c
===================================================================
--- gcc/dce.c.orig	2012-05-03 15:49:35.324670988 -0300
+++ gcc/dce.c	2012-05-03 15:57:33.000000000 -0300
@@ -858,8 +858,9 @@ word_dce_process_block (basic_block bb, 
 	   anything in local_live.  */
 	if (marked_insn_p (insn))
 	  df_word_lr_simulate_uses (insn, local_live);
-
-	if (debug.used && !bitmap_empty_p (debug.used))
+	/* Insert debug temps for dead REGs used in subsequent debug
+	   insns.  */
+	else if (debug.used && !bitmap_empty_p (debug.used))
 	  {
 	    df_ref *def_rec;
 
@@ -940,18 +941,13 @@ dce_process_block (basic_block bb, bool 
 	/* The insn is needed if there is someone who uses the output.  */
 	if (!needed)
 	  for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
-	    {
-	      dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn,
-				      DEBUG_TEMP_BEFORE_WITH_VALUE);
-
-	      if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec))
-		  || bitmap_bit_p (au, DF_REF_REGNO (*def_rec)))
-		{
-		  needed = true;
-		  mark_insn (insn, true);
-		  break;
-		}
-	    }
+	    if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec))
+		|| bitmap_bit_p (au, DF_REF_REGNO (*def_rec)))
+	      {
+		needed = true;
+		mark_insn (insn, true);
+		break;
+	      }
 
 	/* No matter if the instruction is needed or not, we remove
 	   any regno in the defs from the live set.  */
@@ -961,6 +957,12 @@ dce_process_block (basic_block bb, bool 
 	   anything in local_live.  */
 	if (needed)
 	  df_simulate_uses (insn, local_live);
+	/* Insert debug temps for dead REGs used in subsequent debug
+	   insns.  */
+	else if (debug.used && !bitmap_empty_p (debug.used))
+	  for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
+	    dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn,
+				    DEBUG_TEMP_BEFORE_WITH_VALUE);
       }
 
   dead_debug_finish (&debug, NULL);

[-- Attachment #3: Type: text/plain, Size: 258 bytes --]



-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PR52983] eliminate autoinc from debug_insn locs
  2012-05-03 22:40                       ` [PR52983] eliminate autoinc from debug_insn locs Alexandre Oliva
@ 2012-06-13  8:01                         ` Alexandre Oliva
  2012-07-02 12:29                           ` Alexandre Oliva
  0 siblings, 1 reply; 23+ messages in thread
From: Alexandre Oliva @ 2012-06-13  8:01 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: Eric Botcazou, gcc-patches

On May  3, 2012, Alexandre Oliva <aoliva@redhat.com> wrote:

> Here are the 3 patches that, together, are equivalent to the one posted
> before, except for the visibility of cleanup_auto_inc_dec.

Ping?

http://gcc.gnu.org/ml/gcc-patches/2012-05/msg00300.html

-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PR52983, PR48866] eliminate autoinc from debug_insn locs
  2012-05-03 22:46                       ` [PR52983, PR48866] " Alexandre Oliva
@ 2012-06-13  8:56                         ` Alexandre Oliva
  2012-06-13  9:15                           ` Jakub Jelinek
  0 siblings, 1 reply; 23+ messages in thread
From: Alexandre Oliva @ 2012-06-13  8:56 UTC (permalink / raw)
  To: jakub; +Cc: gcc-patches, Eric Botcazou, Paolo Bonzini

On May  3, 2012, Alexandre Oliva <aoliva@redhat.com> wrote:

> On May  3, 2012, Alexandre Oliva <aoliva@redhat.com> wrote:
>> My recent patch for PR48866

> ... had some inconsistency in behavior between dce and word_dce, as you
> pointed out.  I couldn't find any reason for that, so I made them the
> same.

> Regstrapped on x86_64-linux-gnu and i686-linux-gnu

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

> 	PR debug/52983
> 	PR debug/48866
> 	* dce.c (word_dce_process_block): Insert debug temps only if the
> 	insn is not marked.
> 	(dce_process_block): Likewise, and if debug.used is not empty,
> 	and only after iterating over all DEFs that might mark the insn.

Ping?  http://gcc.gnu.org/ml/gcc-patches/2012-05/msg00301.html

-- 
Alexandre Oliva   home: +55 19 32435233  cell: +55 19 97143658
OS Tools Compiler Engineer                 I am Red Hat Brazil
You must be the change you wish to see in the world. -- Gandhi
         Be Free! -- FSFLA.org  FSF Latin America board member

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PR52983, PR48866] eliminate autoinc from debug_insn locs
  2012-06-13  8:56                         ` Alexandre Oliva
@ 2012-06-13  9:15                           ` Jakub Jelinek
  0 siblings, 0 replies; 23+ messages in thread
From: Jakub Jelinek @ 2012-06-13  9:15 UTC (permalink / raw)
  To: Alexandre Oliva; +Cc: gcc-patches, Eric Botcazou, Paolo Bonzini

On Wed, Jun 13, 2012 at 05:46:47AM -0300, Alexandre Oliva wrote:
> On May  3, 2012, Alexandre Oliva <aoliva@redhat.com> wrote:
> 
> > On May  3, 2012, Alexandre Oliva <aoliva@redhat.com> wrote:
> >> My recent patch for PR48866
> 
> > ... had some inconsistency in behavior between dce and word_dce, as you
> > pointed out.  I couldn't find any reason for that, so I made them the
> > same.
> 
> > Regstrapped on x86_64-linux-gnu and i686-linux-gnu
> 
> > for  gcc/ChangeLog
> > from  Alexandre Oliva  <aoliva@redhat.com>
> 
> > 	PR debug/52983
> > 	PR debug/48866
> > 	* dce.c (word_dce_process_block): Insert debug temps only if the
> > 	insn is not marked.
> > 	(dce_process_block): Likewise, and if debug.used is not empty,
> > 	and only after iterating over all DEFs that might mark the insn.
> 
> Ping?  http://gcc.gnu.org/ml/gcc-patches/2012-05/msg00301.html

Ok.  Thanks.

	Jakub

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PR52983] eliminate autoinc from debug_insn locs
  2012-06-13  8:01                         ` Alexandre Oliva
@ 2012-07-02 12:29                           ` Alexandre Oliva
  0 siblings, 0 replies; 23+ messages in thread
From: Alexandre Oliva @ 2012-07-02 12:29 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: Eric Botcazou, gcc-patches, Uros Bizjak

[-- Attachment #1: Type: text/plain, Size: 1830 bytes --]

On Jun 13, 2012, Alexandre Oliva <aoliva@redhat.com> wrote:

> On May  3, 2012, Alexandre Oliva <aoliva@redhat.com> wrote:
>> Here are the 3 patches that, together, are equivalent to the one posted
>> before, except for the visibility of cleanup_auto_inc_dec.

> Ping?

> http://gcc.gnu.org/ml/gcc-patches/2012-05/msg00300.html

Ping²?

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

	PR debug/52983
	* valtrack.h, valtrack.c: New.
	* Makefile.in (VALTRACK_H): New.
	(OBJS): Add valtrack.o.
	(valtrack.o): New.
	(cselib.o, dce.o, df-problems.o, combine.o): Add VALTRACK_H.
	* combine.c: Include valtrack.h.
	(make_compound_operation): Publish.
	(cleanup_auto_inc_dec): Move to valtrack.c.
	(struct rtx_subst_pair, propagate_for_debug_subst): Likewise.
	(propagate_for_debug): Likewise.  Add this_basic_block parameter.
	Adjust all callers.
	* cselib.c: Include valtrack.h.
	* dce.c: Likewise.
	* df-problems.c: Likewise.
	(dead_debug_init, dead_debug_reset_uses): Move to valtrack.c.
	(dead_debug_finish, dead_debug_add): Likewise.
	(dead_debug_insert_temp): Likewise.
	* df.h (struct dead_debug_use): Move to valtrack.h.
	(struct dead_debug, enum debug_temp_where): Likewise.
	(dead_debug_init, dead_debug_reset_uses): Move to valtrack.h.
	(dead_debug_finish, dead_debug_add): Likewise.
	(dead_debug_insert_temp): Likewise.
	* rtl.h (make_compound_operation): Declare.

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

	PR debug/52983
	* valtrack.c (cleanup_auto_inc_dec): Implement unconditionally,
	falling back to copy_rtx on non-autoinc machines.
	(propagate_for_debug_subst): Always use cleanup_auto_inc_dec.

Ugh, the 3rd patch was a mistaken repeat of the 1st.  Here is the 3rd
patch I was supposed to have posted, that completes the fix.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: vta-valtrack-fix-pr52983.patch --]
[-- Type: text/x-diff, Size: 1441 bytes --]

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

	PR debug/52983
	* valtrack.c (dead_debug_insert_temp): Use cleanup_auto_inc_dec.

Index: gcc/valtrack.c
===================================================================
--- gcc/valtrack.c.orig	2012-05-03 15:44:47.934692987 -0300
+++ gcc/valtrack.c	2012-05-03 15:44:52.540628548 -0300
@@ -385,7 +385,7 @@ dead_debug_insert_temp (struct dead_debu
 	breg = NULL;
       /* Cool, it's the same REG, we can use SRC.  */
       else if (dest == reg)
-	breg = copy_rtx (src);
+	breg = cleanup_auto_inc_dec (src, VOIDmode);
       else if (REG_P (dest))
 	{
 	  /* Hmm...  Something's fishy, we should be setting REG here.  */
@@ -394,7 +394,8 @@ dead_debug_insert_temp (struct dead_debu
 	  /* Ok, it's the same (hardware) REG, but with a different
 	     mode, so SUBREG it.  */
 	  else
-	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+	    breg = lowpart_subreg (GET_MODE (reg),
+				   cleanup_auto_inc_dec (src, VOIDmode),
 				   GET_MODE (dest));
 	}
       else if (GET_CODE (dest) == SUBREG)
@@ -415,7 +416,8 @@ dead_debug_insert_temp (struct dead_debu
 	    breg = NULL;
 	  /* Yay, we can use SRC, just adjust its mode.  */
 	  else
-	    breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src),
+	    breg = lowpart_subreg (GET_MODE (reg),
+				   cleanup_auto_inc_dec (src, VOIDmode),
 				   GET_MODE (dest));
 	}
       /* Oh well, we're out of luck.  */

[-- Attachment #3: Type: text/plain, Size: 258 bytes --]



-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist      Red Hat Brazil Compiler Engineer

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2012-07-02 12:29 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-30 12:15 fix left-over debug insns in DCE Alexandre Oliva
2011-06-02 15:26 ` Eric Botcazou
2011-06-03  1:16   ` Alexandre Oliva
2011-06-03 11:06     ` Eric Botcazou
2011-06-06  5:38       ` Alexandre Oliva
2011-06-06  6:05         ` Jakub Jelinek
2011-06-06  8:00         ` Eric Botcazou
2011-06-06 10:07           ` Alexandre Oliva
2011-06-06 13:28           ` Alexandre Oliva
2011-06-07 12:25             ` [PATCH] Fix ICE in reset_unmarked_insns_debug_uses (PR middle-end/49308) Jakub Jelinek
2011-06-09  8:04               ` Jakub Jelinek
2012-04-09  6:14             ` fix left-over debug insns in DCE Alexandre Oliva
2012-04-09 13:42               ` Eric Botcazou
2012-04-13 15:59                 ` Alexandre Oliva
2012-04-13 16:45                   ` Paolo Bonzini
2012-05-03 18:27                     ` [PR52983] eliminate autoinc from debug_insn locs (was: Re: fix left-over debug insns in DCE) Alexandre Oliva
2012-05-03 22:40                       ` [PR52983] eliminate autoinc from debug_insn locs Alexandre Oliva
2012-06-13  8:01                         ` Alexandre Oliva
2012-07-02 12:29                           ` Alexandre Oliva
2012-05-03 22:46                       ` [PR52983, PR48866] " Alexandre Oliva
2012-06-13  8:56                         ` Alexandre Oliva
2012-06-13  9:15                           ` Jakub Jelinek
2011-06-06  7:11       ` fix left-over debug insns in DCE Alexandre Oliva

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).