mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-15 16:00:46 +08:00
re PR debug/52983 (internal compiler error: in df_uses_record, at df-scan.c:3243)
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. From-SVN: r190061
This commit is contained in:
parent
57e8ecc3ba
commit
08df6c0db1
@ -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>
|
||||
|
||||
|
@ -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) \
|
||||
|
180
gcc/combine.c
180
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);
|
||||
|
@ -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"
|
||||
|
@ -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. */
|
||||
|
42
gcc/df.h
42
gcc/df.h
@ -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);
|
||||
|
492
gcc/valtrack.c
Normal file
492
gcc/valtrack.c
Normal file
@ -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;
|
||||
}
|
75
gcc/valtrack.h
Normal file
75
gcc/valtrack.h
Normal file
@ -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 */
|
Loading…
x
Reference in New Issue
Block a user