diff options
-rw-r--r-- | gcc/ChangeLog | 27 | ||||
-rw-r--r-- | gcc/Makefile.in | 15 | ||||
-rw-r--r-- | gcc/combine.c | 180 | ||||
-rw-r--r-- | gcc/cselib.c | 1 | ||||
-rw-r--r-- | gcc/dce.c | 1 | ||||
-rw-r--r-- | gcc/df-problems.c | 307 | ||||
-rw-r--r-- | gcc/df.h | 42 | ||||
-rw-r--r-- | gcc/rtl.h | 1 | ||||
-rw-r--r-- | gcc/valtrack.c | 492 | ||||
-rw-r--r-- | gcc/valtrack.h | 75 |
10 files changed, 624 insertions, 517 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 4d5bf54..ea9a7b1 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,30 @@ +2012-08-01 Alexandre Oliva <aoliva@redhat.com> + + PR debug/52983 + * valtrack.h, valtrack.c: New. + * Makefile.in (VALTRACK_H): New. + (OBJS): Add valtrack.o. + (valtrack.o): New. + (cselib.o, dce.o, df-problems.o, combine.o): Add VALTRACK_H. + * combine.c: Include valtrack.h. + (make_compound_operation): Publish. + (cleanup_auto_inc_dec): Move to valtrack.c. + (struct rtx_subst_pair, propagate_for_debug_subst): Likewise. + (propagate_for_debug): Likewise. Add this_basic_block parameter. + Adjust all callers. + * cselib.c: Include valtrack.h. + * dce.c: Likewise. + * df-problems.c: Likewise. + (dead_debug_init, dead_debug_reset_uses): Move to valtrack.c. + (dead_debug_finish, dead_debug_add): Likewise. + (dead_debug_insert_temp): Likewise. + * df.h (struct dead_debug_use): Move to valtrack.h. + (struct dead_debug, enum debug_temp_where): Likewise. + (dead_debug_init, dead_debug_reset_uses): Move to valtrack.h. + (dead_debug_finish, dead_debug_add): Likewise. + (dead_debug_insert_temp): Likewise. + * rtl.h (make_compound_operation): Declare. + 2012-08-01 Catherine Moore <clm@codesourcery.com> Sandra Loosemore <sandra@codesourcery.com> diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 7633282..b2c4d4f 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -904,6 +904,7 @@ 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) 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) @@ -1447,6 +1448,7 @@ OBJS = \ tree-vectorizer.o \ tree-vrp.o \ tree.o \ + valtrack.o \ value-prof.o \ var-tracking.o \ varasm.o \ @@ -2947,7 +2949,7 @@ cse.o : cse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \ $(DF_H) $(DBGCNT_H) dce.o : dce.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(TREE_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) $(EXCEPT_H) $(DF_H) cselib.h \ - $(DBGCNT_H) dce.h $(TREE_PASS_H) $(DBGCNT_H) $(TM_P_H) \ + $(DBGCNT_H) dce.h $(VALTRACK_H) $(TREE_PASS_H) $(DBGCNT_H) $(TM_P_H) \ $(EMIT_RTL_H) dse.o : dse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(TREE_H) $(TM_P_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) insn-config.h \ @@ -3039,7 +3041,7 @@ df-core.o : df-core.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ df-problems.o : df-problems.c $(CONFIG_H) $(SYSTEM_H) coretypes.h dumpfile.h $(TM_H) \ $(RTL_H) insn-config.h $(RECOG_H) $(FUNCTION_H) $(REGS_H) alloc-pool.h \ hard-reg-set.h $(BASIC_BLOCK_H) $(DF_H) $(BITMAP_H) sbitmap.h $(TIMEVAR_H) \ - $(TM_P_H) $(TARGET_H) $(FLAGS_H) $(EXCEPT_H) dce.h vecprim.h + $(TM_P_H) $(TARGET_H) $(FLAGS_H) $(EXCEPT_H) dce.h vecprim.h $(VALTRACK_H) df-scan.o : df-scan.c $(CONFIG_H) $(SYSTEM_H) coretypes.h dumpfile.h $(TM_H) $(RTL_H) \ insn-config.h $(RECOG_H) $(FUNCTION_H) $(REGS_H) alloc-pool.h \ hard-reg-set.h $(BASIC_BLOCK_H) $(DF_H) $(BITMAP_H) sbitmap.h \ @@ -3048,6 +3050,8 @@ df-scan.o : df-scan.c $(CONFIG_H) $(SYSTEM_H) coretypes.h dumpfile.h $(TM_H) $(R regstat.o : regstat.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(TM_P_H) $(FLAGS_H) $(REGS_H) $(EXCEPT_H) hard-reg-set.h \ $(BASIC_BLOCK_H) $(TIMEVAR_H) $(DF_H) +valtrack.o : valtrack.c $(VALTRACK_H) $(CONFIG_H) $(SYSTEM_H) \ + coretypes.h $(TM_H) $(FUNCTION_H) $(REGS_H) $(EMIT_RTL_H) var-tracking.o : var-tracking.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) $(TREE_H) hard-reg-set.h insn-config.h reload.h $(FLAGS_H) \ $(BASIC_BLOCK_H) bitmap.h alloc-pool.h $(FIBHEAP_H) $(HASHTAB_H) \ @@ -3144,9 +3148,10 @@ et-forest.o : et-forest.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ combine.o : combine.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(FLAGS_H) $(FUNCTION_H) insn-config.h $(INSN_ATTR_H) $(REGS_H) $(EXPR_H) \ rtlhooks-def.h $(BASIC_BLOCK_H) $(RECOG_H) hard-reg-set.h \ - $(DIAGNOSTIC_CORE_H) $(TM_P_H) $(TREE_H) $(TARGET_H) $(PARAMS_H) $(OPTABS_H) \ - insn-codes.h $(TREE_PASS_H) $(DF_H) vecprim.h $(CGRAPH_H) \ - $(OBSTACK_H) + $(DIAGNOSTIC_CORE_H) $(TM_P_H) $(TREE_H) $(TARGET_H) \ + output.h $(PARAMS_H) $(OPTABS_H) \ + insn-codes.h $(TREE_PASS_H) $(DF_H) $(VALTRACK_H) \ + vecprim.h $(CGRAPH_H) $(OBSTACK_H) reginfo.o : reginfo.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ hard-reg-set.h $(FLAGS_H) $(BASIC_BLOCK_H) addresses.h $(REGS_H) \ insn-config.h $(RECOG_H) reload.h $(DIAGNOSTIC_CORE_H) \ diff --git a/gcc/combine.c b/gcc/combine.c index a07c046..dbc6ff6 100644 --- a/gcc/combine.c +++ b/gcc/combine.c @@ -100,6 +100,7 @@ along with GCC; see the file COPYING3. If not see #include "params.h" #include "tree-pass.h" #include "df.h" +#include "valtrack.h" #include "cgraph.h" #include "obstack.h" @@ -424,7 +425,6 @@ static const_rtx expand_field_assignment (const_rtx); static rtx make_extraction (enum machine_mode, rtx, HOST_WIDE_INT, rtx, unsigned HOST_WIDE_INT, int, int, int); static rtx extract_left_shift (rtx, int); -static rtx make_compound_operation (rtx, enum rtx_code); static int get_pos_from_mask (unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT *); static rtx canon_reg_for_combine (rtx, rtx); @@ -2357,161 +2357,6 @@ reg_subword_p (rtx x, rtx reg) && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT; } -#ifdef AUTO_INC_DEC -/* Replace auto-increment addressing modes with explicit operations to access - the same addresses without modifying the corresponding registers. */ - -static rtx -cleanup_auto_inc_dec (rtx src, enum machine_mode mem_mode) -{ - rtx x = src; - const RTX_CODE code = GET_CODE (x); - int i; - const char *fmt; - - switch (code) - { - case REG: - case CONST_INT: - case CONST_DOUBLE: - case CONST_FIXED: - case CONST_VECTOR: - case SYMBOL_REF: - case CODE_LABEL: - case PC: - case CC0: - case SCRATCH: - /* SCRATCH must be shared because they represent distinct values. */ - return x; - case CLOBBER: - if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER) - return x; - break; - - case CONST: - if (shared_const_p (x)) - return x; - break; - - case MEM: - mem_mode = GET_MODE (x); - break; - - case PRE_INC: - case PRE_DEC: - gcc_assert (mem_mode != VOIDmode && mem_mode != BLKmode); - return gen_rtx_PLUS (GET_MODE (x), - cleanup_auto_inc_dec (XEXP (x, 0), mem_mode), - GEN_INT (code == PRE_INC - ? GET_MODE_SIZE (mem_mode) - : -GET_MODE_SIZE (mem_mode))); - - case POST_INC: - case POST_DEC: - case PRE_MODIFY: - case POST_MODIFY: - return cleanup_auto_inc_dec (code == PRE_MODIFY - ? XEXP (x, 1) : XEXP (x, 0), - mem_mode); - - default: - break; - } - - /* Copy the various flags, fields, and other information. We assume - that all fields need copying, and then clear the fields that should - not be copied. That is the sensible default behavior, and forces - us to explicitly document why we are *not* copying a flag. */ - x = shallow_copy_rtx (x); - - /* We do not copy the USED flag, which is used as a mark bit during - walks over the RTL. */ - RTX_FLAG (x, used) = 0; - - /* We do not copy FRAME_RELATED for INSNs. */ - if (INSN_P (x)) - RTX_FLAG (x, frame_related) = 0; - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - if (fmt[i] == 'e') - XEXP (x, i) = cleanup_auto_inc_dec (XEXP (x, i), mem_mode); - else if (fmt[i] == 'E' || fmt[i] == 'V') - { - int j; - XVEC (x, i) = rtvec_alloc (XVECLEN (x, i)); - for (j = 0; j < XVECLEN (x, i); j++) - XVECEXP (x, i, j) - = cleanup_auto_inc_dec (XVECEXP (src, i, j), mem_mode); - } - - return x; -} -#endif - -/* Auxiliary data structure for propagate_for_debug_stmt. */ - -struct rtx_subst_pair -{ - rtx to; - bool adjusted; -}; - -/* DATA points to an rtx_subst_pair. Return the value that should be - substituted. */ - -static rtx -propagate_for_debug_subst (rtx from, const_rtx old_rtx, void *data) -{ - struct rtx_subst_pair *pair = (struct rtx_subst_pair *)data; - - if (!rtx_equal_p (from, old_rtx)) - return NULL_RTX; - if (!pair->adjusted) - { - pair->adjusted = true; -#ifdef AUTO_INC_DEC - pair->to = cleanup_auto_inc_dec (pair->to, VOIDmode); -#else - pair->to = copy_rtx (pair->to); -#endif - pair->to = make_compound_operation (pair->to, SET); - return pair->to; - } - return copy_rtx (pair->to); -} - -/* Replace all the occurrences of DEST with SRC in DEBUG_INSNs between INSN - and LAST, not including INSN, but including LAST. Also stop at the end - of THIS_BASIC_BLOCK. */ - -static void -propagate_for_debug (rtx insn, rtx last, rtx dest, rtx src) -{ - rtx next, loc, end = NEXT_INSN (BB_END (this_basic_block)); - - struct rtx_subst_pair p; - p.to = src; - p.adjusted = false; - - next = NEXT_INSN (insn); - last = NEXT_INSN (last); - while (next != last && next != end) - { - insn = next; - next = NEXT_INSN (insn); - if (DEBUG_INSN_P (insn)) - { - loc = simplify_replace_fn_rtx (INSN_VAR_LOCATION_LOC (insn), - dest, propagate_for_debug_subst, &p); - if (loc == INSN_VAR_LOCATION_LOC (insn)) - continue; - INSN_VAR_LOCATION_LOC (insn) = loc; - df_insn_rescan (insn); - } - } -} - /* Delete the unconditional jump INSN and adjust the CFG correspondingly. Note that the INSN should be deleted *after* removing dead edges, so that the kept edge is the fallthrough edge for a (set (pc) (pc)) @@ -3971,7 +3816,8 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx i0, int *new_direct_jump_p, i2src while its original mode is temporarily restored, and then clear i2scratch so that we don't do it again later. */ - propagate_for_debug (i2, last_combined_insn, reg, i2src); + propagate_for_debug (i2, last_combined_insn, reg, i2src, + this_basic_block); i2scratch = false; /* Put back the new mode. */ adjust_reg_mode (reg, new_mode); @@ -4005,10 +3851,12 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx i0, int *new_direct_jump_p, with this copy we have created; then, replace the copy with the SUBREG of the original shared reg, once again changed to the new mode. */ - propagate_for_debug (first, last, reg, tempreg); + propagate_for_debug (first, last, reg, tempreg, + this_basic_block); adjust_reg_mode (reg, new_mode); propagate_for_debug (first, last, tempreg, - lowpart_subreg (old_mode, reg, new_mode)); + lowpart_subreg (old_mode, reg, new_mode), + this_basic_block); } } } @@ -4220,14 +4068,16 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx i0, int *new_direct_jump_p, if (newi2pat) { if (MAY_HAVE_DEBUG_INSNS && i2scratch) - propagate_for_debug (i2, last_combined_insn, i2dest, i2src); + propagate_for_debug (i2, last_combined_insn, i2dest, i2src, + this_basic_block); INSN_CODE (i2) = i2_code_number; PATTERN (i2) = newi2pat; } else { if (MAY_HAVE_DEBUG_INSNS && i2src) - propagate_for_debug (i2, last_combined_insn, i2dest, i2src); + propagate_for_debug (i2, last_combined_insn, i2dest, i2src, + this_basic_block); SET_INSN_DELETED (i2); } @@ -4236,7 +4086,8 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx i0, int *new_direct_jump_p, LOG_LINKS (i1) = NULL; REG_NOTES (i1) = 0; if (MAY_HAVE_DEBUG_INSNS) - propagate_for_debug (i1, last_combined_insn, i1dest, i1src); + propagate_for_debug (i1, last_combined_insn, i1dest, i1src, + this_basic_block); SET_INSN_DELETED (i1); } @@ -4245,7 +4096,8 @@ try_combine (rtx i3, rtx i2, rtx i1, rtx i0, int *new_direct_jump_p, LOG_LINKS (i0) = NULL; REG_NOTES (i0) = 0; if (MAY_HAVE_DEBUG_INSNS) - propagate_for_debug (i0, last_combined_insn, i0dest, i0src); + propagate_for_debug (i0, last_combined_insn, i0dest, i0src, + this_basic_block); SET_INSN_DELETED (i0); } @@ -7596,7 +7448,7 @@ extract_left_shift (rtx x, int count) being kludges), it is MEM. When processing the arguments of a comparison or a COMPARE against zero, it is COMPARE. */ -static rtx +rtx make_compound_operation (rtx x, enum rtx_code in_code) { enum rtx_code code = GET_CODE (x); diff --git a/gcc/cselib.c b/gcc/cselib.c index ece2240..88a74b4 100644 --- a/gcc/cselib.c +++ b/gcc/cselib.c @@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see #include "hashtab.h" #include "dumpfile.h" #include "cselib.h" +#include "valtrack.h" #include "params.h" #include "alloc-pool.h" #include "target.h" @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see #include "df.h" #include "cselib.h" #include "dce.h" +#include "valtrack.h" #include "tree-pass.h" #include "dbgcnt.h" #include "tm_p.h" diff --git a/gcc/df-problems.c b/gcc/df-problems.c index d572b0f..8699304 100644 --- a/gcc/df-problems.c +++ b/gcc/df-problems.c @@ -44,6 +44,7 @@ along with GCC; see the file COPYING3. If not see #include "except.h" #include "dce.h" #include "vecprim.h" +#include "valtrack.h" #include "dumpfile.h" /* Note that turning REG_DEAD_DEBUGGING on will cause @@ -3047,312 +3048,6 @@ df_create_unused_note (rtx insn, df_ref def, } -/* Initialize DEBUG to an empty list, and clear USED, if given. */ -void -dead_debug_init (struct dead_debug *debug, bitmap used) -{ - debug->head = NULL; - debug->used = used; - debug->to_rescan = NULL; - if (used) - bitmap_clear (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) -{ - 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 (got_head) - rescan = NULL; - else - rescan = BITMAP_ALLOC (NULL); - - while (head) - { - struct dead_debug_use *next = head->next; - rtx insn; - - insn = DF_REF_INSN (head->use); - if (!next || DF_REF_INSN (next->use) != insn) - { - INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC (); - 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)); - } - 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; - unsigned int uid; - - EXECUTE_IF_SET_IN_BITMAP (debug->to_rescan, 0, uid, bi) - { - struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid); - if (insn_info) - df_insn_rescan (insn_info->insn); - } - BITMAP_FREE (debug->to_rescan); - } -} - -/* Add USE to DEBUG. It 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) -{ - struct dead_debug_use *newddu = XNEW (struct dead_debug_use); - - newddu->use = use; - newddu->next = debug->head; - debug->head = newddu; - - if (!debug->used) - debug->used = BITMAP_ALLOC (NULL); - - /* ??? If we dealt with split multi-registers below, we should set - all registers for the used mode in case of hardware - registers. */ - bitmap_set_bit (debug->used, 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. */ -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 0; - - /* Move all uses of uregno from debug->head to uses, setting mode to - the widest referenced mode. */ - while ((cur = *tailp)) - { - if (DF_REF_REGNO (cur->use) == uregno) - { - *usesp = cur; - usesp = &cur->next; - *tailp = cur->next; - cur->next = NULL; - if (!reg - || (GET_MODE_BITSIZE (GET_MODE (reg)) - < GET_MODE_BITSIZE (GET_MODE (*DF_REF_REAL_LOC (cur->use))))) - reg = *DF_REF_REAL_LOC (cur->use); - } - else - tailp = &(*tailp)->next; - } - - /* 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) - { - 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; - /* 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. ??? We could bind the debug_expr to a - CONCAT or PARALLEL with the split multi-registers, and - replace them as we found the corresponding sets. */ - 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; - /* 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), breg, - VAR_INIT_STATUS_INITIALIZED); - - 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. */ - while ((cur = uses)) - { - if (GET_MODE (*DF_REF_REAL_LOC (cur->use)) == GET_MODE (reg)) - *DF_REF_REAL_LOC (cur->use) = dval; - else - *DF_REF_REAL_LOC (cur->use) - = gen_lowpart_SUBREG (GET_MODE (*DF_REF_REAL_LOC (cur->use)), dval); - /* ??? Should we simplify subreg of subreg? */ - if (debug->to_rescan == NULL) - debug->to_rescan = BITMAP_ALLOC (NULL); - bitmap_set_bit (debug->to_rescan, INSN_UID (DF_REF_INSN (cur->use))); - uses = cur->next; - XDELETE (cur); - } - - return 1; -} - /* Recompute the REG_DEAD and REG_UNUSED notes and compute register info: lifetime, bb, and number of defs and uses for basic block BB. The three bitvectors are scratch regs used here. */ @@ -1101,46 +1101,4 @@ extern void union_defs (df_ref, struct web_entry *, 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 */ @@ -2460,6 +2460,7 @@ extern unsigned int extended_count (const_rtx, enum machine_mode, int); extern rtx remove_death (unsigned int, rtx); extern void dump_combine_stats (FILE *); extern void dump_combine_total_stats (FILE *); +extern rtx make_compound_operation (rtx, enum rtx_code); /* In cfgcleanup.c */ extern void delete_dead_jumptables (void); diff --git a/gcc/valtrack.c b/gcc/valtrack.c new file mode 100644 index 0000000..3e03599 --- /dev/null +++ b/gcc/valtrack.c @@ -0,0 +1,492 @@ +/* Infrastructure for tracking user variable locations and values + throughout compilation. + Copyright (C) 2010, 2011, 2012 Free Software Foundation, Inc. + Contributed by Alexandre Oliva <aoliva@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "valtrack.h" +#include "function.h" +#include "regs.h" +#include "emit-rtl.h" + +/* Replace auto-increment addressing modes with explicit operations to access + the same addresses without modifying the corresponding registers. */ + +#ifdef AUTO_INC_DEC +static rtx +cleanup_auto_inc_dec (rtx src, enum machine_mode mem_mode ATTRIBUTE_UNUSED) +{ + rtx x = src; + const RTX_CODE code = GET_CODE (x); + int i; + const char *fmt; + + switch (code) + { + case REG: + case CONST_INT: + case CONST_DOUBLE: + case CONST_FIXED: + case CONST_VECTOR: + case SYMBOL_REF: + case CODE_LABEL: + case PC: + case CC0: + case SCRATCH: + /* SCRATCH must be shared because they represent distinct values. */ + return x; + case CLOBBER: + if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER) + return x; + break; + + case CONST: + if (shared_const_p (x)) + return x; + break; + + case MEM: + mem_mode = GET_MODE (x); + break; + + case PRE_INC: + case PRE_DEC: + gcc_assert (mem_mode != VOIDmode && mem_mode != BLKmode); + return gen_rtx_PLUS (GET_MODE (x), + cleanup_auto_inc_dec (XEXP (x, 0), mem_mode), + GEN_INT (code == PRE_INC + ? GET_MODE_SIZE (mem_mode) + : -GET_MODE_SIZE (mem_mode))); + + case POST_INC: + case POST_DEC: + case PRE_MODIFY: + case POST_MODIFY: + return cleanup_auto_inc_dec (code == PRE_MODIFY + ? XEXP (x, 1) : XEXP (x, 0), + mem_mode); + + default: + break; + } + + /* Copy the various flags, fields, and other information. We assume + that all fields need copying, and then clear the fields that should + not be copied. That is the sensible default behavior, and forces + us to explicitly document why we are *not* copying a flag. */ + x = shallow_copy_rtx (x); + + /* We do not copy the USED flag, which is used as a mark bit during + walks over the RTL. */ + RTX_FLAG (x, used) = 0; + + /* We do not copy FRAME_RELATED for INSNs. */ + if (INSN_P (x)) + RTX_FLAG (x, frame_related) = 0; + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (fmt[i] == 'e') + XEXP (x, i) = cleanup_auto_inc_dec (XEXP (x, i), mem_mode); + else if (fmt[i] == 'E' || fmt[i] == 'V') + { + int j; + XVEC (x, i) = rtvec_alloc (XVECLEN (x, i)); + for (j = 0; j < XVECLEN (x, i); j++) + XVECEXP (x, i, j) + = cleanup_auto_inc_dec (XVECEXP (src, i, j), mem_mode); + } + + return x; +} +#endif + +/* Auxiliary data structure for propagate_for_debug_stmt. */ + +struct rtx_subst_pair +{ + rtx to; + bool adjusted; +}; + +/* DATA points to an rtx_subst_pair. Return the value that should be + substituted. */ + +static rtx +propagate_for_debug_subst (rtx from, const_rtx old_rtx, void *data) +{ + struct rtx_subst_pair *pair = (struct rtx_subst_pair *)data; + + if (!rtx_equal_p (from, old_rtx)) + return NULL_RTX; + if (!pair->adjusted) + { + pair->adjusted = true; +#ifdef AUTO_INC_DEC + pair->to = cleanup_auto_inc_dec (pair->to, VOIDmode); +#else + pair->to = copy_rtx (pair->to); +#endif + pair->to = make_compound_operation (pair->to, SET); + return pair->to; + } + return copy_rtx (pair->to); +} + +/* Replace all the occurrences of DEST with SRC in DEBUG_INSNs between INSN + and LAST, not including INSN, but including LAST. Also stop at the end + of THIS_BASIC_BLOCK. */ + +void +propagate_for_debug (rtx insn, rtx last, rtx dest, rtx src, + basic_block this_basic_block) +{ + rtx next, loc, end = NEXT_INSN (BB_END (this_basic_block)); + + struct rtx_subst_pair p; + p.to = src; + p.adjusted = false; + + next = NEXT_INSN (insn); + last = NEXT_INSN (last); + while (next != last && next != end) + { + insn = next; + next = NEXT_INSN (insn); + if (DEBUG_INSN_P (insn)) + { + loc = simplify_replace_fn_rtx (INSN_VAR_LOCATION_LOC (insn), + dest, propagate_for_debug_subst, &p); + if (loc == INSN_VAR_LOCATION_LOC (insn)) + continue; + INSN_VAR_LOCATION_LOC (insn) = loc; + df_insn_rescan (insn); + } + } +} + +/* Initialize DEBUG to an empty list, and clear USED, if given. */ +void +dead_debug_init (struct dead_debug *debug, bitmap used) +{ + debug->head = NULL; + debug->used = used; + debug->to_rescan = NULL; + if (used) + bitmap_clear (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) +{ + 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 (got_head) + rescan = NULL; + else + rescan = BITMAP_ALLOC (NULL); + + while (head) + { + struct dead_debug_use *next = head->next; + rtx insn; + + insn = DF_REF_INSN (head->use); + if (!next || DF_REF_INSN (next->use) != insn) + { + INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC (); + 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)); + } + 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; + unsigned int uid; + + EXECUTE_IF_SET_IN_BITMAP (debug->to_rescan, 0, uid, bi) + { + struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid); + if (insn_info) + df_insn_rescan (insn_info->insn); + } + BITMAP_FREE (debug->to_rescan); + } +} + +/* Add USE to DEBUG. It 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) +{ + struct dead_debug_use *newddu = XNEW (struct dead_debug_use); + + newddu->use = use; + newddu->next = debug->head; + debug->head = newddu; + + if (!debug->used) + debug->used = BITMAP_ALLOC (NULL); + + /* ??? If we dealt with split multi-registers below, we should set + all registers for the used mode in case of hardware + registers. */ + bitmap_set_bit (debug->used, 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. */ +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 0; + + /* Move all uses of uregno from debug->head to uses, setting mode to + the widest referenced mode. */ + while ((cur = *tailp)) + { + if (DF_REF_REGNO (cur->use) == uregno) + { + *usesp = cur; + usesp = &cur->next; + *tailp = cur->next; + cur->next = NULL; + if (!reg + || (GET_MODE_BITSIZE (GET_MODE (reg)) + < GET_MODE_BITSIZE (GET_MODE (*DF_REF_REAL_LOC (cur->use))))) + reg = *DF_REF_REAL_LOC (cur->use); + } + else + tailp = &(*tailp)->next; + } + + /* 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) + { + 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; + /* 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. ??? We could bind the debug_expr to a + CONCAT or PARALLEL with the split multi-registers, and + replace them as we found the corresponding sets. */ + 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; + /* 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), breg, + VAR_INIT_STATUS_INITIALIZED); + + 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. */ + while ((cur = uses)) + { + if (GET_MODE (*DF_REF_REAL_LOC (cur->use)) == GET_MODE (reg)) + *DF_REF_REAL_LOC (cur->use) = dval; + else + *DF_REF_REAL_LOC (cur->use) + = gen_lowpart_SUBREG (GET_MODE (*DF_REF_REAL_LOC (cur->use)), dval); + /* ??? Should we simplify subreg of subreg? */ + if (debug->to_rescan == NULL) + debug->to_rescan = BITMAP_ALLOC (NULL); + bitmap_set_bit (debug->to_rescan, INSN_UID (DF_REF_INSN (cur->use))); + uses = cur->next; + XDELETE (cur); + } + + return 1; +} diff --git a/gcc/valtrack.h b/gcc/valtrack.h new file mode 100644 index 0000000..9f96f21 --- /dev/null +++ b/gcc/valtrack.h @@ -0,0 +1,75 @@ +/* Infrastructure for tracking user variable locations and values + throughout compilation. + Copyright (C) 2010, 2011, 2012 Free Software Foundation, Inc. + Contributed by Alexandre Oliva <aoliva@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_VALTRACK_H +#define GCC_VALTRACK_H + +#include "bitmap.h" +#include "df.h" +#include "rtl.h" +#include "basic-block.h" + +/* 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); + +extern void propagate_for_debug (rtx, rtx, rtx, rtx, basic_block); + + +#endif /* GCC_VALTRACK_H */ |