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
@@ -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)
@@ -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);
@@ -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);
}
new file mode 100644
@@ -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;
+}
@@ -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),
@@ -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);