diff mbox

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

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

Commit Message

Alexandre Oliva Oct. 2, 2012, 7:05 p.m. UTC
On Sep 25, 2012, Jakub Jelinek <jakub@redhat.com> wrote:

> 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.

Here's the revised version with all the nits fixed.

Regstrapped on x86_64-linux-gnu and i686-linux-gnu.  I'm checking it
in momentarily.
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 (df_set_unused_notes_for_mw): Adjusted.
	(df_create_unused_notes, df_note_bb_compute): Likewise.
	(df_note_compute): Justify local-only dead debug analysis.

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                         |  220 +++++++++++++++++++++++++++++---
 gcc/valtrack.h                         |   84 +++++++++++-
 6 files changed, 340 insertions(+), 45 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/guality/pr54551.c


diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 94ac3b5..77ba4df 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -888,7 +888,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..52f5ed6 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,57 @@  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));
+
+      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.  */
+   dead_debug_local_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 +421,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 +461,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 +523,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 +639,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..303ffa4 100644
--- a/gcc/valtrack.h
+++ b/gcc/valtrack.h
@@ -26,10 +26,71 @@  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);
+  /* Entries are identical if they refer to the same pseudo.  */
+  static inline bool equal (T const *my, T const *other);
+  /* Release entries when they're removed.  */
+  static inline void remove (T *p);
+};
+
+/* Hash on the pseudo number.  */
+inline hashval_t
+dead_debug_hash_descr::hash (T const *my)
+{
+  return REGNO (my->reg);
+}
+
+/* Entries are identical if they refer to the same pseudo.  */
+inline bool
+dead_debug_hash_descr::equal (T const *my, T const *other)
+{
+  return my->reg == other->reg;
+}
+
+/* Release entries when they're removed.  */
+inline void
+dead_debug_hash_descr::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 +99,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 +133,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);