diff mbox

[PR54551] global dead debug pseudo tracking in fast-dce

Message ID orpq5dj97q.fsf@livre.localdomain
State New
Headers show

Commit Message

Alexandre Oliva Sept. 23, 2012, 10:59 a.m. UTC
This patch introduces a global mode of dead_debug tracking for use in
fast DCE.  If a debug use reaches the top of a basic block before
finding its death point, the pending and subsequent uses of the pseudo
in debug insns will all be substituted with the same debug temp, and
death points will get the value bound to the debug temp.

Without this, we'd just reset the debug insn.

Regstrapped on x86_64-linux-gnu and i686-linux-gnu.  Ok?

Comments

Jakub Jelinek Sept. 25, 2012, 12:21 p.m. UTC | #1
On Sun, Sep 23, 2012 at 07:59:37AM -0300, Alexandre Oliva wrote:
> This patch introduces a global mode of dead_debug tracking for use in
> fast DCE.  If a debug use reaches the top of a basic block before
> finding its death point, the pending and subsequent uses of the pseudo
> in debug insns will all be substituted with the same debug temp, and
> death points will get the value bound to the debug temp.

Thanks for working on this.  The patch generally looks good, just some minor
nits below.
If it turns out to be too expensive to do global dead debug tracking in
every fast DCE, we could limit it to the first one via some new DF flag.

> 	* df-problems.c: Adjust.

I think you should list here what functions you've changed.

> --- a/gcc/valtrack.c
> +++ b/gcc/valtrack.c
...
> +static bool
> +dead_debug_global_replace_temp (struct dead_debug_global *global,
> +				df_ref use, unsigned int uregno,
> +				bitmap *pto_rescan)
> +{
> +  if (!global || uregno < FIRST_PSEUDO_REGISTER
> +      || !global->used
> +      || !bitmap_bit_p (global->used, uregno))
> +    return false;
> +
> +  gcc_checking_assert (REGNO (*DF_REF_REAL_LOC (use)) == uregno);
> +
> +  dead_debug_global_entry *entry
> +    = dead_debug_global_find (global, *DF_REF_REAL_LOC (use));
> +  gcc_checking_assert (GET_CODE (entry->reg) == REG
> +		       && REGNO (entry->reg) == uregno);

I think just for safety you should here:
  if (GET_MODE (*DF_REF_REAL_LOC (use)) != GET_MODE (entry->reg))
    return false;
(the other alternative would be to use mode in the hash function etc.,
but if usually the same pseudo has the same mode everywhere, then the above
should be good enough).

> +static void
> +dead_debug_promote_uses (struct dead_debug_local *debug)
> +{
> +  for (struct dead_debug_use *head = debug->head, **headp = &debug->head;
> +       head; head = *headp)
> +    {
> +      rtx reg = *DF_REF_REAL_LOC (head->use);
> +
> +      if (GET_CODE (reg) != REG
> +	  || REGNO (reg) < FIRST_PSEUDO_REGISTER)
> +	{
> +	  headp = &head->next;
> +	  continue;
> +	}
> +
> +      if (!debug->global->used)
> +	debug->global->used = BITMAP_ALLOC (NULL);
> +
> +      if (bitmap_set_bit (debug->global->used, REGNO (reg)))
> +	dead_debug_global_insert (debug->global, reg,
> +				  make_debug_expr_from_rtl (reg));
> +
> +      dead_debug_global_replace_temp (debug->global, head->use, REGNO (reg),
> +				      &debug->to_rescan);

Here you are ignoring the return value from dead_debug_global_replace_temp.
IMHO you should just do what you do a few lines above if it returns false,
something like
  if (!dead_debug_global_replace_temp (debug->global, head->use, REGNO (reg),
				       &debug->to_rescan))
    {
      headp = &head->next;
      continue;
    }

> +      
> +      *headp = head->next;
> +      XDELETE (head);
> +    }
> +}
> +
>  /* 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.  */

The above comment should be adjusted to dead_debug_local_init.

> -  rtx reg = NULL;
> +  rtx reg = NULL_RTX;
>    rtx breg;
> -  rtx dval;
> +  rtx dval = NULL_RTX;
>    rtx bind;
> +  bool global;
>  
> -  if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
> +  if (!debug->used)
> +    return 0;
> +
> +  global = debug->global && debug->global->used
> +    && bitmap_bit_p (debug->global->used, uregno);

I'd think here formatting should put && below debug in "= debug->global".
Perhaps put && debug->global->used onto its own line too?

> +/* Descriptor for hash_table to hash by dead_debug_global_entry's REG
> +   and map to DTEMP.  */
> +
> +struct dead_debug_hash_descr
> +{
> +  /* The hash table contains pointers to entries of this type.  */
> +  typedef struct dead_debug_global_entry T;
> +  /* Hash on the pseudo number.  */
> +  static inline hashval_t hash (T const *my)
> +  {
> +    return REGNO (my->reg);
> +  }
> +  /* Entries are identical if they refer to the same pseudo.  */
> +  static inline bool equal (T const *my, T const *other)
> +  {
> +    return my->reg == other->reg;
> +  }
> +  /* Release entries when they're removed.  */
> +  static inline void remove (T *p)
> +  {
> +    XDELETE (p);
> +  }
> +};

I believe the coding conventions ask to put the inlines outside of the
class body, see e.g. coverage.c.

> +
> +/* Maintain a global table of pseudos used in debug insns after their
> +   deaths in other blocks, and debug temps their deathpoint values are
> +   to be bound to.  */
> +
> +struct dead_debug_global
> +{
> +  /* This hash table that maps pseudos to debug temps.  */
> +  hash_table<dead_debug_hash_descr> htab;

I think all other hash_table uses put a space here before <.

BTW, the patch fixes most of the (non-LTO) fails left in the
http://gcc.gnu.org/ml/gcc-patches/2012-09/msg00711.html patch, good job!

	Jakub
Alexandre Oliva Sept. 25, 2012, 10:21 p.m. UTC | #2
On Sep 25, 2012, Jakub Jelinek <jakub@redhat.com> wrote:

> (the other alternative would be to use mode in the hash function etc.,
> but if usually the same pseudo has the same mode everywhere, then the above
> should be good enough).

AFAIK each pseudo is referenced everywhere using the same RTX; if so, it
follows that it has the same mode in all uses.


> I believe the coding conventions ask to put the inlines outside of the
> class body, see e.g. coverage.c.

I wasn't sure about one-liners; hash-table.h itself has inline
one-liners, one of which I used as the basis for the descriptor.  That
said, the braces were not in separate lines.

I'm going on a trip tomorrow morning, and I'll only return on Friday
evening.  I'll have a look at the C++ coding conventions and the other
issues you brought up when I return.  However, if you'd rather have the
fix in before that, I won't mind if you pick it up from where I left.

> BTW, the patch fixes most of the (non-LTO) fails left in the
> http://gcc.gnu.org/ml/gcc-patches/2012-09/msg00711.html patch, good job!

Cool!  Thanks for the good news, and for the review.
Jakub Jelinek Sept. 27, 2012, 9:18 a.m. UTC | #3
On Tue, Sep 25, 2012 at 07:21:04PM -0300, Alexandre Oliva wrote:
> On Sep 25, 2012, Jakub Jelinek <jakub@redhat.com> wrote:
> 
> > (the other alternative would be to use mode in the hash function etc.,
> > but if usually the same pseudo has the same mode everywhere, then the above
> > should be good enough).
> 
> AFAIK each pseudo is referenced everywhere using the same RTX; if so, it
> follows that it has the same mode in all uses.

Ok, leave the mode check out then.  But still checking the result of the
function is IMHO desirable.

> > I believe the coding conventions ask to put the inlines outside of the
> > class body, see e.g. coverage.c.
> 
> I wasn't sure about one-liners; hash-table.h itself has inline
> one-liners, one of which I used as the basis for the descriptor.  That
> said, the braces were not in separate lines.

Seems the C++ coding conventions are unfinished and vague and the codebase
is growing different styles in different spots :(.

> I'm going on a trip tomorrow morning, and I'll only return on Friday
> evening.  I'll have a look at the C++ coding conventions and the other
> issues you brought up when I return.  However, if you'd rather have the
> fix in before that, I won't mind if you pick it up from where I left.

I can wait, after all the related msg00711.html patch hasn't been reviewed
yet anyway.

	Jakub
diff mbox

Patch

Track dead pseudos used in debug insns globally in fast DCE.

From: Alexandre Oliva <aoliva@redhat.com>

for  gcc/ChangeLog

	PR debug/54551
	* Makefile.in (VALTRACK_H): Add hash-table.h.
	* valtrack.h: Include hash-table.h.
	(struct dead_debug_global_entry): New.
	(struct dead_debug_hash_descr): New.
	(struct dead_debug_global): New.
	(struct dead_debug): Rename to...
	(struct dead_debug_local): ... this.  Adjust all uses.
	(dead_debug_global_init, dead_debug_global_finish): New.
	(dead_debug_init): Rename to...
	(dead_debug_local_init): ... this.  Adjust all callers.
	(dead_debug_finish): Rename to...
	(dead_debug_local_finish): ... this.  Adjust all callers.
	* valtrack.c (dead_debug_global_init): New.
	(dead_debug_init): Rename to...
	(dead_debug_local_init): ... this.  Take global parameter.
	Save it and initialize used bitmap from it.
	(dead_debug_global_find, dead_debug_global_insert): New.
	(dead_debug_global_replace_temp): New.
	(dead_debug_promote_uses): New.
	(dead_debug_finish): Rename to...
	(dead_debug_local_finish): ... this.  Promote remaining uses.
	(dead_debug_global_finish): New.
	(dead_debug_add): Try to replace global temps first.
	(dead_debug_insert_temp): Support global replacements.
	* dce.c (word_dce_process_block, dce_process_block): Add
	global_debug parameter.  Pass it on.
	(fast_dce): Initialize, pass on and finalize global_debug.
	* df-problems.c: Adjust.

for  gcc/testsuite/ChangeLog

	PR debug/54551
	* gcc.dg/guality/pr54551.c: New.
---

 gcc/Makefile.in                        |    3 
 gcc/dce.c                              |   35 +++--
 gcc/df-problems.c                      |   15 +-
 gcc/testsuite/gcc.dg/guality/pr54551.c |   28 ++++
 gcc/valtrack.c                         |  214 +++++++++++++++++++++++++++++---
 gcc/valtrack.h                         |   72 ++++++++++-
 6 files changed, 323 insertions(+), 44 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/guality/pr54551.c


diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 4901b95..6ca30e5 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -883,7 +883,8 @@  CGRAPH_H = cgraph.h $(VEC_H) $(TREE_H) $(BASIC_BLOCK_H) $(FUNCTION_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)
+VALTRACK_H = valtrack.h $(BITMAP_H) $(DF_H) $(RTL_H) $(BASIC_BLOCK_H) \
+	$(HASH_TABLE_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)
diff --git a/gcc/dce.c b/gcc/dce.c
index c951865..11f8edb 100644
--- a/gcc/dce.c
+++ b/gcc/dce.c
@@ -806,15 +806,17 @@  struct rtl_opt_pass pass_ud_rtl_dce =
 /* Process basic block BB.  Return true if the live_in set has
    changed. REDO_OUT is true if the info at the bottom of the block
    needs to be recalculated before starting.  AU is the proper set of
-   artificial uses. */
+   artificial uses.  Track global substitution of uses of dead pseudos
+   in debug insns using GLOBAL_DEBUG.  */
 
 static bool
-word_dce_process_block (basic_block bb, bool redo_out)
+word_dce_process_block (basic_block bb, bool redo_out,
+			struct dead_debug_global *global_debug)
 {
   bitmap local_live = BITMAP_ALLOC (&dce_tmp_bitmap_obstack);
   rtx insn;
   bool block_changed;
-  struct dead_debug debug;
+  struct dead_debug_local debug;
 
   if (redo_out)
     {
@@ -836,7 +838,7 @@  word_dce_process_block (basic_block bb, bool redo_out)
     }
 
   bitmap_copy (local_live, DF_WORD_LR_OUT (bb));
-  dead_debug_init (&debug, NULL);
+  dead_debug_local_init (&debug, NULL, global_debug);
 
   FOR_BB_INSNS_REVERSE (bb, insn)
     if (DEBUG_INSN_P (insn))
@@ -890,7 +892,7 @@  word_dce_process_block (basic_block bb, bool redo_out)
   if (block_changed)
     bitmap_copy (DF_WORD_LR_IN (bb), local_live);
 
-  dead_debug_finish (&debug, NULL);
+  dead_debug_local_finish (&debug, NULL);
   BITMAP_FREE (local_live);
   return block_changed;
 }
@@ -899,16 +901,18 @@  word_dce_process_block (basic_block bb, bool redo_out)
 /* Process basic block BB.  Return true if the live_in set has
    changed. REDO_OUT is true if the info at the bottom of the block
    needs to be recalculated before starting.  AU is the proper set of
-   artificial uses. */
+   artificial uses.  Track global substitution of uses of dead pseudos
+   in debug insns using GLOBAL_DEBUG.  */
 
 static bool
-dce_process_block (basic_block bb, bool redo_out, bitmap au)
+dce_process_block (basic_block bb, bool redo_out, bitmap au,
+		   struct dead_debug_global *global_debug)
 {
   bitmap local_live = BITMAP_ALLOC (&dce_tmp_bitmap_obstack);
   rtx insn;
   bool block_changed;
   df_ref *def_rec;
-  struct dead_debug debug;
+  struct dead_debug_local debug;
 
   if (redo_out)
     {
@@ -932,7 +936,7 @@  dce_process_block (basic_block bb, bool redo_out, bitmap au)
   bitmap_copy (local_live, DF_LR_OUT (bb));
 
   df_simulate_initialize_backwards (bb, local_live);
-  dead_debug_init (&debug, NULL);
+  dead_debug_local_init (&debug, NULL, global_debug);
 
   FOR_BB_INSNS_REVERSE (bb, insn)
     if (DEBUG_INSN_P (insn))
@@ -977,7 +981,7 @@  dce_process_block (basic_block bb, bool redo_out, bitmap au)
 				    DEBUG_TEMP_BEFORE_WITH_VALUE);
       }
 
-  dead_debug_finish (&debug, NULL);
+  dead_debug_local_finish (&debug, NULL);
   df_simulate_finalize_backwards (bb, local_live);
 
   block_changed = !bitmap_equal_p (local_live, DF_LR_IN (bb));
@@ -1014,12 +1018,15 @@  fast_dce (bool word_level)
   bitmap au = &df->regular_block_artificial_uses;
   bitmap au_eh = &df->eh_block_artificial_uses;
   int i;
+  struct dead_debug_global global_debug;
 
   prescan_insns_for_dce (true);
 
   for (i = 0; i < n_blocks; i++)
     bitmap_set_bit (all_blocks, postorder[i]);
 
+  dead_debug_global_init (&global_debug, NULL);
+
   while (global_changed)
     {
       global_changed = false;
@@ -1038,11 +1045,13 @@  fast_dce (bool word_level)
 
 	  if (word_level)
 	    local_changed
-	      = word_dce_process_block (bb, bitmap_bit_p (redo_out, index));
+	      = word_dce_process_block (bb, bitmap_bit_p (redo_out, index),
+					&global_debug);
 	  else
 	    local_changed
 	      = dce_process_block (bb, bitmap_bit_p (redo_out, index),
-				   bb_has_eh_pred (bb) ? au_eh : au);
+				   bb_has_eh_pred (bb) ? au_eh : au,
+				   &global_debug);
 	  bitmap_set_bit (processed, index);
 
 	  if (local_changed)
@@ -1090,6 +1099,8 @@  fast_dce (bool word_level)
 	}
     }
 
+  dead_debug_global_finish (&global_debug, NULL);
+
   delete_unmarked_insns ();
 
   BITMAP_FREE (processed);
diff --git a/gcc/df-problems.c b/gcc/df-problems.c
index abeb118..a1a0e71 100644
--- a/gcc/df-problems.c
+++ b/gcc/df-problems.c
@@ -2892,7 +2892,7 @@  static void
 df_set_unused_notes_for_mw (rtx insn, struct df_mw_hardreg *mws,
 			    bitmap live, bitmap do_not_gen,
 			    bitmap artificial_uses,
-			    struct dead_debug *debug)
+			    struct dead_debug_local *debug)
 {
   unsigned int r;
 
@@ -3021,7 +3021,7 @@  df_set_dead_notes_for_mw (rtx insn, struct df_mw_hardreg *mws,
 static void
 df_create_unused_note (rtx insn, df_ref def,
 		       bitmap live, bitmap artificial_uses,
-		       struct dead_debug *debug)
+		       struct dead_debug_local *debug)
 {
   unsigned int dregno = DF_REF_REGNO (def);
 
@@ -3060,9 +3060,9 @@  df_note_bb_compute (unsigned int bb_index,
   rtx insn;
   df_ref *def_rec;
   df_ref *use_rec;
-  struct dead_debug debug;
+  struct dead_debug_local debug;
 
-  dead_debug_init (&debug, NULL);
+  dead_debug_local_init (&debug, NULL, NULL);
 
   bitmap_copy (live, df_get_live_out (bb));
   bitmap_clear (artificial_uses);
@@ -3268,7 +3268,7 @@  df_note_bb_compute (unsigned int bb_index,
 	}
     }
 
-  dead_debug_finish (&debug, NULL);
+  dead_debug_local_finish (&debug, NULL);
 }
 
 
@@ -3286,6 +3286,11 @@  df_note_compute (bitmap all_blocks)
 
   EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi)
   {
+    /* ??? Unlike fast DCE, we don't use global_debug for uses of dead
+       pseudos in debug insns because we don't always (re)visit blocks
+       with death points after visiting dead uses.  Even changing this
+       loop to postorder would still leave room for visiting a death
+       point before visiting a subsequent debug use.  */
     df_note_bb_compute (bb_index, &live, &do_not_gen, &artificial_uses);
   }
 
diff --git a/gcc/testsuite/gcc.dg/guality/pr54551.c b/gcc/testsuite/gcc.dg/guality/pr54551.c
new file mode 100644
index 0000000..4235f78
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/guality/pr54551.c
@@ -0,0 +1,28 @@ 
+/* PR debug/54551 */
+/* { dg-do run } */
+/* { dg-options "-g" } */
+
+void  __attribute__((__noinline__))
+bar (void)
+{
+  asm volatile ("");
+}
+
+int __attribute__((__noinline__))
+foo (int x, int y, int z)
+{
+  if (x != z)
+    {
+      int a = z + 1;
+      bar (); /* { dg-final { gdb-test 18 "a" "4" } } */
+      bar (); /* { dg-final { gdb-test 18 "z" "3" } } */
+    }
+  return y;
+}
+
+int
+main ()
+{
+  foo (1, 2, 3);
+  return 0;
+}
diff --git a/gcc/valtrack.c b/gcc/valtrack.c
index 2cdb06b..75017c2 100644
--- a/gcc/valtrack.c
+++ b/gcc/valtrack.c
@@ -182,14 +182,108 @@  propagate_for_debug (rtx insn, rtx last, rtx dest, rtx src,
 }
 
 /* Initialize DEBUG to an empty list, and clear USED, if given.  */
+
+void
+dead_debug_global_init (struct dead_debug_global *debug, bitmap used)
+{
+  debug->used = used;
+  if (used)
+    bitmap_clear (used);
+}
+
+/* Initialize DEBUG to an empty list, and clear USED, if given.  Link
+   back to GLOBAL, if given, and bring in used bits from it.  */
+
 void
-dead_debug_init (struct dead_debug *debug, bitmap used)
+dead_debug_local_init (struct dead_debug_local *debug, bitmap used,
+		       struct dead_debug_global *global)
 {
+  if (!used && global && global->used)
+    used = BITMAP_ALLOC (NULL);
+
   debug->head = NULL;
+  debug->global = global;
   debug->used = used;
   debug->to_rescan = NULL;
+
   if (used)
-    bitmap_clear (used);
+    {
+      if (global && global->used)
+	bitmap_copy (used, global->used);
+      else
+	bitmap_clear (used);
+    }
+}
+
+/* Locate the entry for REG in GLOBAL->htab.  */
+
+static dead_debug_global_entry *
+dead_debug_global_find (struct dead_debug_global *global, rtx reg)
+{
+  dead_debug_global_entry temp_entry;
+  temp_entry.reg = reg;
+
+  dead_debug_global_entry *entry = global->htab.find (&temp_entry);
+  gcc_checking_assert (entry && entry->reg == temp_entry.reg);
+  gcc_checking_assert (entry->dtemp);
+
+  return entry;
+}
+
+/* Insert an entry mapping REG to DTEMP in GLOBAL->htab.  */
+
+static void
+dead_debug_global_insert (struct dead_debug_global *global, rtx reg, rtx dtemp)
+{
+  dead_debug_global_entry temp_entry;
+  temp_entry.reg = reg;
+  temp_entry.dtemp = dtemp;
+
+  if (!global->htab.is_created ())
+    global->htab.create (31);
+
+  dead_debug_global_entry **slot = global->htab.find_slot (&temp_entry, INSERT);
+  gcc_checking_assert (!*slot);
+  *slot = XNEW (dead_debug_global_entry);
+  **slot = temp_entry;
+}
+
+/* If UREGNO, referenced by USE, is a pseudo marked as used in GLOBAL,
+   replace it with with a USE of the debug temp recorded for it, and
+   return TRUE.  Otherwise, just return FALSE.
+
+   If PTO_RESCAN is given, instead of rescanning modified INSNs right
+   away, add their UIDs to the bitmap, allocating one of *PTO_RESCAN
+   is NULL.  */
+
+static bool
+dead_debug_global_replace_temp (struct dead_debug_global *global,
+				df_ref use, unsigned int uregno,
+				bitmap *pto_rescan)
+{
+  if (!global || uregno < FIRST_PSEUDO_REGISTER
+      || !global->used
+      || !bitmap_bit_p (global->used, uregno))
+    return false;
+
+  gcc_checking_assert (REGNO (*DF_REF_REAL_LOC (use)) == uregno);
+
+  dead_debug_global_entry *entry
+    = dead_debug_global_find (global, *DF_REF_REAL_LOC (use));
+  gcc_checking_assert (GET_CODE (entry->reg) == REG
+		       && REGNO (entry->reg) == uregno);
+
+  *DF_REF_REAL_LOC (use) = entry->dtemp;
+  if (!pto_rescan)
+    df_insn_rescan (DF_REF_INSN (use));
+  else
+    {
+      if (!*pto_rescan)
+	*pto_rescan = BITMAP_ALLOC (NULL);
+      bitmap_set_bit (*pto_rescan, INSN_UID (DF_REF_INSN (use)));
+    }
+
+  return true;
 }
 
 /* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of
@@ -199,7 +293,8 @@  dead_debug_init (struct dead_debug *debug, bitmap used)
    will be removed, and only then rescanned.  */
 
 static void
-dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
+dead_debug_reset_uses (struct dead_debug_local *debug,
+		       struct dead_debug_use *head)
 {
   bool got_head = (debug->head == head);
   bitmap rescan;
@@ -258,15 +353,53 @@  dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
   BITMAP_FREE (rescan);
 }
 
+/* Promote pending local uses of pseudos in DEBUG to global
+   substitutions.  Uses of non-pseudos are left alone for
+   resetting.  */
+
+static void
+dead_debug_promote_uses (struct dead_debug_local *debug)
+{
+  for (struct dead_debug_use *head = debug->head, **headp = &debug->head;
+       head; head = *headp)
+    {
+      rtx reg = *DF_REF_REAL_LOC (head->use);
+
+      if (GET_CODE (reg) != REG
+	  || REGNO (reg) < FIRST_PSEUDO_REGISTER)
+	{
+	  headp = &head->next;
+	  continue;
+	}
+
+      if (!debug->global->used)
+	debug->global->used = BITMAP_ALLOC (NULL);
+
+      if (bitmap_set_bit (debug->global->used, REGNO (reg)))
+	dead_debug_global_insert (debug->global, reg,
+				  make_debug_expr_from_rtl (reg));
+
+      dead_debug_global_replace_temp (debug->global, head->use, REGNO (reg),
+				      &debug->to_rescan);
+      
+      *headp = head->next;
+      XDELETE (head);
+    }
+}
+
 /* 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)
+dead_debug_local_finish (struct dead_debug_local *debug, bitmap used)
 {
   if (debug->used != used)
     BITMAP_FREE (debug->used);
 
+  if (debug->global)
+    dead_debug_promote_uses (debug);
+
   dead_debug_reset_uses (debug, debug->head);
 
   if (debug->to_rescan)
@@ -284,11 +417,30 @@  dead_debug_finish (struct dead_debug *debug, bitmap used)
     }
 }
 
-/* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
-   insn.  Create a bitmap for DEBUG as needed.  */
+/* Release GLOBAL->used unless it is the same as USED.  Release the
+   mapping hash table if it was initialized.  */
+
+void
+dead_debug_global_finish (struct dead_debug_global *global, bitmap used)
+{
+  if (global->used != used)
+    BITMAP_FREE (global->used);
+
+  if (global->htab.is_created ())
+    global->htab.dispose ();
+}
+
+/* Add USE to DEBUG, or substitute it right away if it's a pseudo in
+   the global substitution list.  USE 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)
+dead_debug_add (struct dead_debug_local *debug, df_ref use, unsigned int uregno)
 {
+  if (dead_debug_global_replace_temp (debug->global, use, uregno,
+				      &debug->to_rescan))
+    return;
+
   struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
 
   newddu->use = use;
@@ -305,26 +457,34 @@  dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int 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.  */
+   before or after INSN (depending on WHERE), that binds a (possibly
+   global) 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,
+dead_debug_insert_temp (struct dead_debug_local *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 reg = NULL_RTX;
   rtx breg;
-  rtx dval;
+  rtx dval = NULL_RTX;
   rtx bind;
+  bool global;
 
-  if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
+  if (!debug->used)
+    return 0;
+
+  global = debug->global && debug->global->used
+    && bitmap_bit_p (debug->global->used, uregno);
+
+  if (!global && !bitmap_clear_bit (debug->used, uregno))
     return 0;
 
   /* Move all uses of uregno from debug->head to uses, setting mode to
@@ -359,10 +519,21 @@  dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
   if (reg == NULL)
     {
       gcc_checking_assert (!uses);
-      return 0;
+      if (!global)
+	return 0;
+    }
+
+  if (global)
+    {
+      if (!reg)
+	reg = regno_reg_rtx[uregno];
+      dead_debug_global_entry *entry
+	= dead_debug_global_find (debug->global, reg);
+      gcc_checking_assert (entry->reg == reg);
+      dval = entry->dtemp;
     }
 
-  gcc_checking_assert (uses);
+  gcc_checking_assert (uses || global);
 
   breg = reg;
   /* Recover the expression INSN stores in REG.  */
@@ -464,8 +635,9 @@  dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
 	}
     }
 
-  /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
-  dval = make_debug_expr_from_rtl (reg);
+  if (!global)
+    /* 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),
diff --git a/gcc/valtrack.h b/gcc/valtrack.h
index 9f96f21..9c32274 100644
--- a/gcc/valtrack.h
+++ b/gcc/valtrack.h
@@ -26,10 +26,59 @@  along with GCC; see the file COPYING3.  If not see
 #include "df.h"
 #include "rtl.h"
 #include "basic-block.h"
+#include "hash-table.h"
 
 /* Debug uses of dead regs.  */
 
+/* Entry that maps a dead pseudo (REG) used in a debug insns that dies
+   at different blocks to the debug temp (DTEMP) it was replaced
+   with.  */
+
+struct dead_debug_global_entry
+{
+  rtx reg;
+  rtx dtemp;
+};
+
+/* Descriptor for hash_table to hash by dead_debug_global_entry's REG
+   and map to DTEMP.  */
+
+struct dead_debug_hash_descr
+{
+  /* The hash table contains pointers to entries of this type.  */
+  typedef struct dead_debug_global_entry T;
+  /* Hash on the pseudo number.  */
+  static inline hashval_t hash (T const *my)
+  {
+    return REGNO (my->reg);
+  }
+  /* Entries are identical if they refer to the same pseudo.  */
+  static inline bool equal (T const *my, T const *other)
+  {
+    return my->reg == other->reg;
+  }
+  /* Release entries when they're removed.  */
+  static inline void remove (T *p)
+  {
+    XDELETE (p);
+  }
+};
+
+/* Maintain a global table of pseudos used in debug insns after their
+   deaths in other blocks, and debug temps their deathpoint values are
+   to be bound to.  */
+
+struct dead_debug_global
+{
+  /* This hash table that maps pseudos to debug temps.  */
+  hash_table<dead_debug_hash_descr> htab;
+  /* For each entry in htab, the bit corresponding to its REGNO will
+     be set.  */
+  bitmap used;
+};
+
 /* Node of a linked list of uses of dead REGs in debug insns.  */
+
 struct dead_debug_use
 {
   df_ref use;
@@ -38,15 +87,25 @@  struct dead_debug_use
 
 /* Linked list of the above, with a bitmap of the REGs in the
    list.  */
-struct dead_debug
+
+struct dead_debug_local
 {
+  /* The first dead_debug_use entry in the list.  */
   struct dead_debug_use *head;
+  /* A pointer to the global tracking data structure.  */
+  struct dead_debug_global *global;
+  /* A bitmap that has bits set for each REG used in the
+     dead_debug_use list, and for each entry in the global hash
+     table.  */
   bitmap used;
+  /* A bitmap that has bits set for each INSN that is to be
+     rescanned.  */
   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
@@ -62,10 +121,13 @@  enum debug_temp_where
     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 *,
+extern void dead_debug_global_init (struct dead_debug_global *, bitmap);
+extern void dead_debug_global_finish (struct dead_debug_global *, bitmap);
+extern void dead_debug_local_init (struct dead_debug_local *, bitmap,
+				   struct dead_debug_global *);
+extern void dead_debug_local_finish (struct dead_debug_local *, bitmap);
+extern void dead_debug_add (struct dead_debug_local *, df_ref, unsigned int);
+extern int dead_debug_insert_temp (struct dead_debug_local *,
 				   unsigned int uregno, rtx insn,
 				   enum debug_temp_where);