Patchwork [PR52983] eliminate autoinc from debug_insn locs

login
register
mail settings
Submitter Alexandre Oliva
Date May 3, 2012, 10:39 p.m.
Message ID <orsjfgyjnc.fsf@livre.localdomain>
Download mbox | patch
Permalink /patch/156781/
State New
Headers show

Comments

Alexandre Oliva - May 3, 2012, 10:39 p.m.
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.
Alexandre Oliva - June 13, 2012, 7:59 a.m.
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

Patch

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 */