[backport from gcc-4.8/trunk r186422 ] gcc/ 2012-04-13 Alexandre Oliva PR debug/48866 * df.h (enum debug_temp_where): New. (dead_debug_init, dead_debug_finish) Declare. (dead_debug_add, dead_debug_insert_temp): Declare. (struct dead_debug_use, struct dead_debug): Moved from... * df-problems.c: ... here. (df_set_unused_notes_for_mw): Bind debug uses of unused regno to a debug temp. (df_create_unused_note): Likewise. (df_set_dead_notes_for_mw): Move comment where it belongs. (dead_debug_init): Export. (dead_debug_reset_uses): New, factored out of... (dead_debug_finish): ...this. Export. (dead_debug_reset): Remove. (dead_debug_add): Export. (dead_debug_insert_before): Rename to... (dead_debug_insert_temp): ... this. Add where argument. Export. Locate stored value for BEFORE_WITH_VALUE. Avoid repeat inserts. Return insertion count. (df_note_bb_compute): Adjust. * dce.c (word_dce_process_block): Adjust dead debug uses. (dce_process_block): Likewise. --- gcc-4.7.1/gcc/dce.c.~1~ 2011-07-19 19:43:15.000000000 +0200 +++ gcc-4.7.1/gcc/dce.c 2012-06-16 13:09:28.000000000 +0200 @@ -807,6 +807,7 @@ word_dce_process_block (basic_block bb, bitmap local_live = BITMAP_ALLOC (&dce_tmp_bitmap_obstack); rtx insn; bool block_changed; + struct dead_debug debug; if (redo_out) { @@ -828,11 +829,24 @@ word_dce_process_block (basic_block bb, } bitmap_copy (local_live, DF_WORD_LR_OUT (bb)); + dead_debug_init (&debug, NULL); FOR_BB_INSNS_REVERSE (bb, insn) - if (NONDEBUG_INSN_P (insn)) + if (DEBUG_INSN_P (insn)) + { + df_ref *use_rec; + for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++) + if (DF_REF_REGNO (*use_rec) >= FIRST_PSEUDO_REGISTER + && (GET_MODE_SIZE (GET_MODE (DF_REF_REAL_REG (*use_rec))) + == 2 * UNITS_PER_WORD) + && !bitmap_bit_p (local_live, 2 * DF_REF_REGNO (*use_rec)) + && !bitmap_bit_p (local_live, 2 * DF_REF_REGNO (*use_rec) + 1)) + dead_debug_add (&debug, *use_rec, DF_REF_REGNO (*use_rec)); + } + else if (INSN_P (insn)) { bool any_changed; + /* No matter if the instruction is needed or not, we remove any regno in the defs from the live set. */ any_changed = df_word_lr_simulate_defs (insn, local_live); @@ -844,6 +858,15 @@ word_dce_process_block (basic_block bb, if (marked_insn_p (insn)) df_word_lr_simulate_uses (insn, local_live); + if (debug.used && !bitmap_empty_p (debug.used)) + { + df_ref *def_rec; + + for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++) + dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn, + DEBUG_TEMP_BEFORE_WITH_VALUE); + } + if (dump_file) { fprintf (dump_file, "finished processing insn %d live out = ", @@ -856,6 +879,7 @@ word_dce_process_block (basic_block bb, if (block_changed) bitmap_copy (DF_WORD_LR_IN (bb), local_live); + dead_debug_finish (&debug, NULL); BITMAP_FREE (local_live); return block_changed; } @@ -873,6 +897,7 @@ dce_process_block (basic_block bb, bool rtx insn; bool block_changed; df_ref *def_rec; + struct dead_debug debug; if (redo_out) { @@ -896,22 +921,36 @@ dce_process_block (basic_block bb, bool bitmap_copy (local_live, DF_LR_OUT (bb)); df_simulate_initialize_backwards (bb, local_live); + dead_debug_init (&debug, NULL); FOR_BB_INSNS_REVERSE (bb, insn) - if (INSN_P (insn)) + if (DEBUG_INSN_P (insn)) + { + df_ref *use_rec; + for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++) + if (!bitmap_bit_p (local_live, DF_REF_REGNO (*use_rec)) + && !bitmap_bit_p (au, DF_REF_REGNO (*use_rec))) + dead_debug_add (&debug, *use_rec, DF_REF_REGNO (*use_rec)); + } + else if (INSN_P (insn)) { bool needed = marked_insn_p (insn); /* The insn is needed if there is someone who uses the output. */ if (!needed) for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++) - if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec)) - || bitmap_bit_p (au, DF_REF_REGNO (*def_rec))) - { - needed = true; - mark_insn (insn, true); - break; - } + { + dead_debug_insert_temp (&debug, DF_REF_REGNO (*def_rec), insn, + DEBUG_TEMP_BEFORE_WITH_VALUE); + + if (bitmap_bit_p (local_live, DF_REF_REGNO (*def_rec)) + || bitmap_bit_p (au, DF_REF_REGNO (*def_rec))) + { + needed = true; + mark_insn (insn, true); + break; + } + } /* No matter if the instruction is needed or not, we remove any regno in the defs from the live set. */ @@ -923,6 +962,7 @@ dce_process_block (basic_block bb, bool df_simulate_uses (insn, local_live); } + dead_debug_finish (&debug, NULL); df_simulate_finalize_backwards (bb, local_live); block_changed = !bitmap_equal_p (local_live, DF_LR_IN (bb)); --- gcc-4.7.1/gcc/df-problems.c.~1~ 2012-01-30 13:56:17.000000000 +0100 +++ gcc-4.7.1/gcc/df-problems.c 2012-06-16 13:09:28.000000000 +0200 @@ -2886,25 +2886,6 @@ df_whole_mw_reg_unused_p (struct df_mw_h } -/* Node of a linked list of uses of dead REGs in debug insns. */ -struct dead_debug_use -{ - df_ref use; - struct dead_debug_use *next; -}; - -/* Linked list of the above, with a bitmap of the REGs in the - list. */ -struct dead_debug -{ - struct dead_debug_use *head; - bitmap used; - bitmap to_rescan; -}; - -static void dead_debug_reset (struct dead_debug *, unsigned int); - - /* Set the REG_UNUSED notes for the multiword hardreg defs in INSN based on the bits in LIVE. Do not generate notes for registers in artificial uses. DO_NOT_GEN is updated so that REG_DEAD notes are @@ -2930,7 +2911,7 @@ df_set_unused_notes_for_mw (rtx insn, st { unsigned int regno = mws->start_regno; df_set_note (REG_UNUSED, insn, mws->mw_reg); - dead_debug_reset (debug, regno); + dead_debug_insert_temp (debug, regno, insn, DEBUG_TEMP_AFTER_WITH_REG); #ifdef REG_DEAD_DEBUGGING df_print_note ("adding 1: ", insn, REG_NOTES (insn)); @@ -2945,7 +2926,7 @@ df_set_unused_notes_for_mw (rtx insn, st && !bitmap_bit_p (artificial_uses, r)) { df_set_note (REG_UNUSED, insn, regno_reg_rtx[r]); - dead_debug_reset (debug, r); + dead_debug_insert_temp (debug, r, insn, DEBUG_TEMP_AFTER_WITH_REG); #ifdef REG_DEAD_DEBUGGING df_print_note ("adding 2: ", insn, REG_NOTES (insn)); #endif @@ -3013,12 +2994,12 @@ df_set_dead_notes_for_mw (rtx insn, stru if (df_whole_mw_reg_dead_p (mws, live, artificial_uses, do_not_gen)) { - /* Add a dead note for the entire multi word register. */ if (is_debug) { *added_notes_p = true; return; } + /* Add a dead note for the entire multi word register. */ df_set_note (REG_DEAD, insn, mws->mw_reg); #ifdef REG_DEAD_DEBUGGING df_print_note ("adding 1: ", insn, REG_NOTES (insn)); @@ -3072,7 +3053,7 @@ df_create_unused_note (rtx insn, df_ref rtx reg = (DF_REF_LOC (def)) ? *DF_REF_REAL_LOC (def): DF_REF_REG (def); df_set_note (REG_UNUSED, insn, reg); - dead_debug_reset (debug, dregno); + dead_debug_insert_temp (debug, dregno, insn, DEBUG_TEMP_AFTER_WITH_REG); #ifdef REG_DEAD_DEBUGGING df_print_note ("adding 3: ", insn, REG_NOTES (insn)); #endif @@ -3083,7 +3064,7 @@ df_create_unused_note (rtx insn, df_ref /* Initialize DEBUG to an empty list, and clear USED, if given. */ -static inline void +void dead_debug_init (struct dead_debug *debug, bitmap used) { debug->head = NULL; @@ -3093,32 +3074,83 @@ dead_debug_init (struct dead_debug *debu bitmap_clear (used); } -/* 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. */ -static inline void -dead_debug_finish (struct dead_debug *debug, bitmap used) +/* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of + each reset insn. DEBUG is not otherwise modified. If HEAD is + DEBUG->head, DEBUG->head will be set to NULL at the end. + Otherwise, entries from DEBUG->head that pertain to reset insns + will be removed, and only then rescanned. */ + +static void +dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head) { - struct dead_debug_use *head; - rtx insn = NULL; + bool got_head = (debug->head == head); + bitmap rescan; + struct dead_debug_use **tailp = &debug->head; + struct dead_debug_use *cur; + bitmap_iterator bi; + unsigned int uid; - if (debug->used != used) - BITMAP_FREE (debug->used); + if (got_head) + rescan = NULL; + else + rescan = BITMAP_ALLOC (NULL); - while ((head = debug->head)) + while (head) { + struct dead_debug_use *next = head->next; + rtx insn; + insn = DF_REF_INSN (head->use); - if (!head->next || DF_REF_INSN (head->next->use) != insn) + if (!next || DF_REF_INSN (next->use) != insn) { INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC (); - df_insn_rescan_debug_internal (insn); + if (got_head) + df_insn_rescan_debug_internal (insn); + else + bitmap_set_bit (rescan, INSN_UID (insn)); if (debug->to_rescan) bitmap_clear_bit (debug->to_rescan, INSN_UID (insn)); } - debug->head = head->next; XDELETE (head); + head = next; } + if (got_head) + { + debug->head = NULL; + return; + } + + while ((cur = *tailp)) + if (bitmap_bit_p (rescan, INSN_UID (DF_REF_INSN (cur->use)))) + { + *tailp = cur->next; + XDELETE (cur); + } + else + tailp = &cur->next; + + EXECUTE_IF_SET_IN_BITMAP (rescan, 0, uid, bi) + { + struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid); + if (insn_info) + df_insn_rescan_debug_internal (insn_info->insn); + } + + BITMAP_FREE (rescan); +} + +/* Reset all debug insns with pending uses. Release the bitmap in it, + unless it is USED. USED must be the same bitmap passed to + dead_debug_init. */ +void +dead_debug_finish (struct dead_debug *debug, bitmap used) +{ + if (debug->used != used) + BITMAP_FREE (debug->used); + + dead_debug_reset_uses (debug, debug->head); + if (debug->to_rescan) { bitmap_iterator bi; @@ -3134,54 +3166,9 @@ dead_debug_finish (struct dead_debug *de } } -/* Reset DEBUG_INSNs with pending uses of DREGNO. */ -static void -dead_debug_reset (struct dead_debug *debug, unsigned int dregno) -{ - struct dead_debug_use **tailp = &debug->head; - struct dead_debug_use **insnp = &debug->head; - struct dead_debug_use *cur; - rtx insn; - - if (!debug->used || !bitmap_clear_bit (debug->used, dregno)) - return; - - while ((cur = *tailp)) - { - if (DF_REF_REGNO (cur->use) == dregno) - { - *tailp = cur->next; - insn = DF_REF_INSN (cur->use); - INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC (); - if (debug->to_rescan == NULL) - debug->to_rescan = BITMAP_ALLOC (NULL); - bitmap_set_bit (debug->to_rescan, INSN_UID (insn)); - XDELETE (cur); - /* If the current use isn't the first one attached to INSN, go back - to this first use. We assume that the uses attached to an insn - are adjacent. */ - if (tailp != insnp && DF_REF_INSN ((*insnp)->use) == insn) - tailp = insnp; - /* Then remove all the other uses attached to INSN. */ - while ((cur = *tailp) && DF_REF_INSN (cur->use) == insn) - { - *tailp = cur->next; - XDELETE (cur); - } - insnp = tailp; - } - else - { - if (DF_REF_INSN ((*insnp)->use) != DF_REF_INSN (cur->use)) - insnp = tailp; - tailp = &(*tailp)->next; - } - } -} - /* Add USE to DEBUG. It must be a dead reference to UREGNO in a debug insn. Create a bitmap for DEBUG as needed. */ -static inline void +void dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno) { struct dead_debug_use *newddu = XNEW (struct dead_debug_use); @@ -3197,23 +3184,27 @@ dead_debug_add (struct dead_debug *debug } /* If UREGNO is referenced by any entry in DEBUG, emit a debug insn - before INSN that binds the REG to a debug temp, and replace all - uses of UREGNO in DEBUG with uses of the debug temp. INSN must be - the insn where UREGNO dies. */ -static inline void -dead_debug_insert_before (struct dead_debug *debug, unsigned int uregno, - rtx insn) + before or after INSN (depending on WHERE), that binds a debug temp + to the widest-mode use of UREGNO, if WHERE is *_WITH_REG, or the + value stored in UREGNO by INSN otherwise, and replace all uses of + UREGNO in DEBUG with uses of the debug temp. INSN must be where + UREGNO dies, if WHERE is *_BEFORE_*, or where it is set otherwise. + Return the number of debug insns emitted. */ +int +dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno, + rtx insn, enum debug_temp_where where) { struct dead_debug_use **tailp = &debug->head; struct dead_debug_use *cur; struct dead_debug_use *uses = NULL; struct dead_debug_use **usesp = &uses; rtx reg = NULL; + rtx breg; rtx dval; rtx bind; if (!debug->used || !bitmap_clear_bit (debug->used, uregno)) - return; + return 0; /* Move all uses of uregno from debug->head to uses, setting mode to the widest referenced mode. */ @@ -3237,17 +3228,114 @@ dead_debug_insert_before (struct dead_de /* We may have dangling bits in debug->used for registers that were part of a multi-register use, one component of which has been reset. */ if (reg == NULL) - return; + { + gcc_checking_assert (!uses); + return 0; + } + + gcc_checking_assert (uses); + + breg = reg; + /* Recover the expression INSN stores in REG. */ + if (where == DEBUG_TEMP_BEFORE_WITH_VALUE) + { + rtx set = single_set (insn); + rtx dest, src; + + if (set) + { + dest = SET_DEST (set); + src = SET_SRC (set); + /* Lose if the REG-setting insn is a CALL. */ + if (GET_CODE (src) == CALL) + { + while (uses) + { + cur = uses->next; + XDELETE (uses); + uses = cur; + } + return 0; + } + } + + /* ??? Should we try to extract it from a PARALLEL? */ + if (!set) + breg = NULL; + /* Cool, it's the same REG, we can use SRC. */ + else if (dest == reg) + breg = copy_rtx (src); + else if (REG_P (dest)) + { + /* Hmm... Something's fishy, we should be setting REG here. */ + if (REGNO (dest) != REGNO (reg)) + breg = NULL; + /* Ok, it's the same (hardware) REG, but with a different + mode, so SUBREG it. */ + else + breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src), + GET_MODE (dest)); + } + else if (GET_CODE (dest) == SUBREG) + { + /* We should be setting REG here. Lose. */ + if (REGNO (SUBREG_REG (dest)) != REGNO (reg)) + breg = NULL; + /* Lose if we're setting something other than the lowpart of + REG. */ + else if (!subreg_lowpart_p (dest)) + breg = NULL; + /* If we're not overwriting all the hardware registers that + setting REG in its mode would, we won't know what to bind + the debug temp to. */ + else if (REGNO (reg) < FIRST_PSEUDO_REGISTER + && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)] + != hard_regno_nregs[REGNO (reg)][GET_MODE (dest)])) + breg = NULL; + /* Yay, we can use SRC, just adjust its mode. */ + else + breg = lowpart_subreg (GET_MODE (reg), copy_rtx (src), + GET_MODE (dest)); + } + /* Oh well, we're out of luck. */ + else + breg = NULL; + + /* We couldn't figure out the value stored in REG, so reset all + of its pending debug uses. */ + if (!breg) + { + dead_debug_reset_uses (debug, uses); + return 0; + } + } + + /* If there's a single (debug) use of an otherwise unused REG, and + the debug use is not part of a larger expression, then it + probably doesn't make sense to introduce a new debug temp. */ + if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next) + { + rtx next = DF_REF_INSN (uses->use); + + if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next)) + { + XDELETE (uses); + return 0; + } + } /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL). */ dval = make_debug_expr_from_rtl (reg); /* Emit a debug bind insn before the insn in which reg dies. */ bind = gen_rtx_VAR_LOCATION (GET_MODE (reg), - DEBUG_EXPR_TREE_DECL (dval), reg, + DEBUG_EXPR_TREE_DECL (dval), breg, VAR_INIT_STATUS_INITIALIZED); - bind = emit_debug_insn_before (bind, insn); + if (where == DEBUG_TEMP_AFTER_WITH_REG) + bind = emit_debug_insn_after (bind, insn); + else + bind = emit_debug_insn_before (bind, insn); df_insn_rescan (bind); /* Adjust all uses. */ @@ -3265,6 +3353,8 @@ dead_debug_insert_before (struct dead_de uses = cur->next; XDELETE (cur); } + + return 1; } /* Recompute the REG_DEAD and REG_UNUSED notes and compute register @@ -3459,7 +3549,8 @@ df_note_bb_compute (unsigned int bb_inde break; } else - dead_debug_insert_before (&debug, uregno, insn); + dead_debug_insert_temp (&debug, uregno, insn, + DEBUG_TEMP_BEFORE_WITH_REG); if ( (!(DF_REF_FLAGS (use) & (DF_REF_MW_HARDREG | DF_REF_READ_WRITE))) --- gcc-4.7.1/gcc/df.h.~1~ 2012-02-29 09:12:04.000000000 +0100 +++ gcc-4.7.1/gcc/df.h 2012-06-16 13:09:28.000000000 +0200 @@ -1101,4 +1101,46 @@ extern void union_defs (df_ref, struct w unsigned int *used, struct web_entry *, bool (*fun) (struct web_entry *, struct web_entry *)); +/* Debug uses of dead regs. */ + +/* Node of a linked list of uses of dead REGs in debug insns. */ +struct dead_debug_use +{ + df_ref use; + struct dead_debug_use *next; +}; + +/* Linked list of the above, with a bitmap of the REGs in the + list. */ +struct dead_debug +{ + struct dead_debug_use *head; + bitmap used; + bitmap to_rescan; +}; + +/* This type controls the behavior of dead_debug_insert_temp WRT + UREGNO and INSN. */ +enum debug_temp_where + { + /* Bind a newly-created debug temporary to a REG for UREGNO, and + insert the debug insn before INSN. REG is expected to die at + INSN. */ + DEBUG_TEMP_BEFORE_WITH_REG = -1, + /* Bind a newly-created debug temporary to the value INSN stores + in REG, and insert the debug insn before INSN. */ + DEBUG_TEMP_BEFORE_WITH_VALUE = 0, + /* Bind a newly-created debug temporary to a REG for UREGNO, and + insert the debug insn after INSN. REG is expected to be set at + INSN. */ + DEBUG_TEMP_AFTER_WITH_REG = 1 + }; + +extern void dead_debug_init (struct dead_debug *, bitmap); +extern void dead_debug_finish (struct dead_debug *, bitmap); +extern void dead_debug_add (struct dead_debug *, df_ref, unsigned int); +extern int dead_debug_insert_temp (struct dead_debug *, + unsigned int uregno, rtx insn, + enum debug_temp_where); + #endif /* GCC_DF_H */