diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 9388c159ad4..a8ad41cb28d 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,37 @@ +2012-10-02 Alexandre Oliva + + 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. + 2012-10-02 Alexandre Oliva PR debug/53135 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 94ac3b536fe..77ba4df2958 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 c951865f765..11f8edb7b70 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 abeb118dd7e..a1a0e71422d 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/ChangeLog b/gcc/testsuite/ChangeLog index c202c89c129..1d2cac58a8c 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2012-10-02 Alexandre Oliva + + PR debug/54551 + * gcc.dg/guality/pr54551.c: New. + 2012-10-02 Pat Haugen * gcc.target/powerpc/pr46728-1.c: Accept xssqrtdp. diff --git a/gcc/testsuite/gcc.dg/guality/pr54551.c b/gcc/testsuite/gcc.dg/guality/pr54551.c new file mode 100644 index 00000000000..4235f78ce73 --- /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 2cdb06b174e..52f5ed65313 100644 --- a/gcc/valtrack.c +++ b/gcc/valtrack.c @@ -182,16 +182,110 @@ 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_init (struct dead_debug *debug, bitmap used) +dead_debug_global_init (struct dead_debug_global *debug, bitmap used) { - debug->head = NULL; debug->used = used; - debug->to_rescan = NULL; 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_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) + { + 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 each reset insn. DEBUG is not otherwise modified. If HEAD is DEBUG->head, DEBUG->head will be set to NULL at the end. @@ -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_add (struct dead_debug *debug, df_ref use, unsigned int uregno) +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_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; } - gcc_checking_assert (uses); + 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 || 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 9f96f210b1a..303ffa43a8f 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 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);