/* Variable tracking routines for the GNU compiler.
- Copyright (C) 2002-2015 Free Software Foundation, Inc.
+ Copyright (C) 2002-2017 Free Software Foundation, Inc.
This file is part of GCC.
#include "cfghooks.h"
#include "alloc-pool.h"
#include "tree-pass.h"
+#include "memmodel.h"
#include "tm_p.h"
#include "insn-config.h"
#include "regs.h"
/* Pointer to the BB's information specific to variable tracking pass. */
#define VTI(BB) ((variable_tracking_info *) (BB)->aux)
-/* Macro to access MEM_OFFSET as an HOST_WIDE_INT. Evaluates MEM twice. */
-#define INT_MEM_OFFSET(mem) (MEM_OFFSET_KNOWN_P (mem) ? MEM_OFFSET (mem) : 0)
+/* Return MEM_OFFSET (MEM) as a HOST_WIDE_INT, or 0 if we can't. */
+
+static inline HOST_WIDE_INT
+int_mem_offset (const_rtx mem)
+{
+ HOST_WIDE_INT offset;
+ if (MEM_OFFSET_KNOWN_P (mem) && MEM_OFFSET (mem).is_constant (&offset))
+ return offset;
+ return 0;
+}
#if CHECKING_P && (GCC_VERSION >= 2007)
static void dataflow_set_destroy (dataflow_set *);
static bool track_expr_p (tree, bool);
-static bool same_variable_part_p (rtx, tree, HOST_WIDE_INT);
static void add_uses_1 (rtx *, void *);
static void add_stores (rtx, const_rtx, void *);
static bool compute_bb_dataflow (basic_block);
static void emit_notes_in_bb (basic_block, dataflow_set *);
static void vt_emit_notes (void);
-static bool vt_get_decl_and_offset (rtx, tree *, HOST_WIDE_INT *);
static void vt_add_function_parameters (void);
static bool vt_initialize (void);
static void vt_finalize (void);
pointer is often restored via a load-multiple instruction
and so no stack_adjust offset is recorded for it. This means
that the stack offset at the end of the epilogue block is the
- the same as the offset before the epilogue, whereas other paths
+ same as the offset before the epilogue, whereas other paths
to the exit block will have the correct stack_adjust.
It is safe to ignore these differences because (a) we never
bool store;
machine_mode mem_mode;
HOST_WIDE_INT stack_adjust;
- rtx_expr_list *side_effects;
+ auto_vec<rtx> side_effects;
};
/* Helper for adjust_mems. Return true if X is suitable for
/* Transform X into narrower mode MODE from wider mode WMODE. */
static rtx
-use_narrower_mode (rtx x, machine_mode mode, machine_mode wmode)
+use_narrower_mode (rtx x, scalar_int_mode mode, scalar_int_mode wmode)
{
rtx op0, op1;
if (CONSTANT_P (x))
/* Ensure shift amount is not wider than mode. */
if (GET_MODE (op1) == VOIDmode)
op1 = lowpart_subreg (mode, op1, wmode);
- else if (GET_MODE_PRECISION (mode) < GET_MODE_PRECISION (GET_MODE (op1)))
+ else if (GET_MODE_PRECISION (mode)
+ < GET_MODE_PRECISION (as_a <scalar_int_mode> (GET_MODE (op1))))
op1 = lowpart_subreg (mode, op1, GET_MODE (op1));
return simplify_gen_binary (ASHIFT, mode, op0, op1);
default:
rtx mem, addr = loc, tem;
machine_mode mem_mode_save;
bool store_save;
+ scalar_int_mode tem_mode, tem_subreg_mode;
switch (GET_CODE (loc))
{
case REG:
? GET_MODE_SIZE (amd->mem_mode)
: -GET_MODE_SIZE (amd->mem_mode),
GET_MODE (loc)));
+ /* FALLTHRU */
case POST_INC:
case POST_DEC:
if (addr == loc)
amd->store = false;
tem = simplify_replace_fn_rtx (tem, old_rtx, adjust_mems, data);
amd->store = store_save;
- amd->side_effects = alloc_EXPR_LIST (0,
- gen_rtx_SET (XEXP (loc, 0), tem),
- amd->side_effects);
+ amd->side_effects.safe_push (gen_rtx_SET (XEXP (loc, 0), tem));
return addr;
case PRE_MODIFY:
addr = XEXP (loc, 1);
+ /* FALLTHRU */
case POST_MODIFY:
if (addr == loc)
addr = XEXP (loc, 0);
tem = simplify_replace_fn_rtx (XEXP (loc, 1), old_rtx,
adjust_mems, data);
amd->store = store_save;
- amd->side_effects = alloc_EXPR_LIST (0,
- gen_rtx_SET (XEXP (loc, 0), tem),
- amd->side_effects);
+ amd->side_effects.safe_push (gen_rtx_SET (XEXP (loc, 0), tem));
return addr;
case SUBREG:
/* First try without delegitimization of whole MEMs and
if (tem == NULL_RTX)
tem = gen_rtx_raw_SUBREG (GET_MODE (loc), addr, SUBREG_BYTE (loc));
finish_subreg:
- if (MAY_HAVE_DEBUG_INSNS
+ if (MAY_HAVE_DEBUG_BIND_INSNS
&& GET_CODE (tem) == SUBREG
&& (GET_CODE (SUBREG_REG (tem)) == PLUS
|| GET_CODE (SUBREG_REG (tem)) == MINUS
|| GET_CODE (SUBREG_REG (tem)) == MULT
|| GET_CODE (SUBREG_REG (tem)) == ASHIFT)
- && (GET_MODE_CLASS (GET_MODE (tem)) == MODE_INT
- || GET_MODE_CLASS (GET_MODE (tem)) == MODE_PARTIAL_INT)
- && (GET_MODE_CLASS (GET_MODE (SUBREG_REG (tem))) == MODE_INT
- || GET_MODE_CLASS (GET_MODE (SUBREG_REG (tem))) == MODE_PARTIAL_INT)
- && GET_MODE_PRECISION (GET_MODE (tem))
- < GET_MODE_PRECISION (GET_MODE (SUBREG_REG (tem)))
+ && is_a <scalar_int_mode> (GET_MODE (tem), &tem_mode)
+ && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (tem)),
+ &tem_subreg_mode)
+ && (GET_MODE_PRECISION (tem_mode)
+ < GET_MODE_PRECISION (tem_subreg_mode))
&& subreg_lowpart_p (tem)
&& use_narrower_mode_test (SUBREG_REG (tem), tem))
- return use_narrower_mode (SUBREG_REG (tem), GET_MODE (tem),
- GET_MODE (SUBREG_REG (tem)));
+ return use_narrower_mode (SUBREG_REG (tem), tem_mode, tem_subreg_mode);
return tem;
case ASM_OPERANDS:
/* Don't do any replacements in second and following
static void
adjust_insn (basic_block bb, rtx_insn *insn)
{
- struct adjust_mem_data amd;
rtx set;
#ifdef HAVE_window_save
}
#endif
+ adjust_mem_data amd;
amd.mem_mode = VOIDmode;
amd.stack_adjust = -VTI (bb)->out.stack_adjust;
- amd.side_effects = NULL;
amd.store = true;
note_stores (PATTERN (insn), adjust_mem_stores, &amd);
validate_change (NULL_RTX, &SET_SRC (set), XEXP (note, 0), true);
}
- if (amd.side_effects)
+ if (!amd.side_effects.is_empty ())
{
- rtx *pat, new_pat, s;
- int i, oldn, newn;
+ rtx *pat, new_pat;
+ int i, oldn;
pat = &PATTERN (insn);
if (GET_CODE (*pat) == COND_EXEC)
oldn = XVECLEN (*pat, 0);
else
oldn = 1;
- for (s = amd.side_effects, newn = 0; s; newn++)
- s = XEXP (s, 1);
+ unsigned int newn = amd.side_effects.length ();
new_pat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (oldn + newn));
if (GET_CODE (*pat) == PARALLEL)
for (i = 0; i < oldn; i++)
XVECEXP (new_pat, 0, i) = XVECEXP (*pat, 0, i);
else
XVECEXP (new_pat, 0, 0) = *pat;
- for (s = amd.side_effects, i = oldn; i < oldn + newn; i++, s = XEXP (s, 1))
- XVECEXP (new_pat, 0, i) = XEXP (s, 0);
- free_EXPR_LIST_list (&amd.side_effects);
+
+ rtx effect;
+ unsigned int j;
+ FOR_EACH_VEC_ELT_REVERSE (amd.side_effects, j, effect)
+ XVECEXP (new_pat, 0, j + oldn) = effect;
validate_change (NULL_RTX, pat, new_pat, true);
}
}
{
tree decl;
- if (!MAY_HAVE_DEBUG_INSNS)
+ if (!MAY_HAVE_DEBUG_BIND_INSNS)
return NOT_ONEPART;
if (dv_is_value_p (dv))
static inline tree
var_debug_decl (tree decl)
{
- if (decl && TREE_CODE (decl) == VAR_DECL
- && DECL_HAS_DEBUG_EXPR_P (decl))
+ if (decl && VAR_P (decl) && DECL_HAS_DEBUG_EXPR_P (decl))
{
tree debugdecl = DECL_DEBUG_EXPR (decl);
if (DECL_P (debugdecl))
set_variable_part (set, loc, dv, offset, initialized, set_src, iopt);
}
+/* Return true if we should track a location that is OFFSET bytes from
+ a variable. Store the constant offset in *OFFSET_OUT if so. */
+
+static bool
+track_offset_p (poly_int64 offset, HOST_WIDE_INT *offset_out)
+{
+ HOST_WIDE_INT const_offset;
+ if (!offset.is_constant (&const_offset)
+ || !IN_RANGE (const_offset, 0, MAX_VAR_PARTS - 1))
+ return false;
+ *offset_out = const_offset;
+ return true;
+}
+
+/* Return the offset of a register that track_offset_p says we
+ should track. */
+
+static HOST_WIDE_INT
+get_tracked_reg_offset (rtx loc)
+{
+ HOST_WIDE_INT offset;
+ if (!track_offset_p (REG_OFFSET (loc), &offset))
+ gcc_unreachable ();
+ return offset;
+}
+
/* Set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC). */
static void
rtx set_src)
{
tree decl = REG_EXPR (loc);
- HOST_WIDE_INT offset = REG_OFFSET (loc);
+ HOST_WIDE_INT offset = get_tracked_reg_offset (loc);
var_reg_decl_set (set, loc, initialized,
dv_from_decl (decl), offset, set_src, INSERT);
enum var_init_status initialized, rtx set_src)
{
tree decl = REG_EXPR (loc);
- HOST_WIDE_INT offset = REG_OFFSET (loc);
+ HOST_WIDE_INT offset = get_tracked_reg_offset (loc);
attrs *node, *next;
attrs **nextp;
attrs **nextp = &set->regs[REGNO (loc)];
attrs *node, *next;
- if (clobber)
+ HOST_WIDE_INT offset;
+ if (clobber && track_offset_p (REG_OFFSET (loc), &offset))
{
tree decl = REG_EXPR (loc);
- HOST_WIDE_INT offset = REG_OFFSET (loc);
decl = var_debug_decl (decl);
negative_power_of_two_p (HOST_WIDE_INT i)
{
unsigned HOST_WIDE_INT x = -(unsigned HOST_WIDE_INT)i;
- return x == (x & -x);
+ return pow2_or_zerop (x);
}
/* Strip constant offsets and alignments off of LOC. Return the base
};
/* Remove all MEMs that overlap with COMS->LOC from the location list
- of a hash table entry for a value. COMS->ADDR must be a
+ of a hash table entry for a onepart variable. COMS->ADDR must be a
canonicalized form of COMS->LOC's address, and COMS->LOC must be
canonicalized itself. */
rtx mloc = coms->loc, addr = coms->addr;
variable *var = *slot;
- if (var->onepart == ONEPART_VALUE)
+ if (var->onepart != NOT_ONEPART)
{
location_chain *loc, **locp;
bool changed = false;
rtx set_src)
{
tree decl = MEM_EXPR (loc);
- HOST_WIDE_INT offset = INT_MEM_OFFSET (loc);
+ HOST_WIDE_INT offset = int_mem_offset (loc);
var_mem_decl_set (set, loc, initialized,
dv_from_decl (decl), offset, set_src, INSERT);
enum var_init_status initialized, rtx set_src)
{
tree decl = MEM_EXPR (loc);
- HOST_WIDE_INT offset = INT_MEM_OFFSET (loc);
+ HOST_WIDE_INT offset = int_mem_offset (loc);
clobber_overlapping_mems (set, loc);
decl = var_debug_decl (decl);
var_mem_delete (dataflow_set *set, rtx loc, bool clobber)
{
tree decl = MEM_EXPR (loc);
- HOST_WIDE_INT offset = INT_MEM_OFFSET (loc);
+ HOST_WIDE_INT offset = int_mem_offset (loc);
clobber_overlapping_mems (set, loc);
decl = var_debug_decl (decl);
{
decl_or_value cdv = dv_from_value (cval);
- /* Keep the remaining values connected, accummulating links
+ /* Keep the remaining values connected, accumulating links
in the canonical value. */
for (node = var->var_part[0].loc_chain; node; node = node->next)
{
case ONEPART_DEXPR:
if (newv)
NO_LOC_P (DECL_RTL_KNOWN_SET (dv_as_decl (dv))) = false;
- /* Fall through... */
+ /* Fall through. */
default:
DECL_CHANGED (dv_as_decl (dv)) = newv;
else
return 1;
+ case 'p':
+ r = compare_sizes_for_sort (SUBREG_BYTE (x), SUBREG_BYTE (y));
+ if (r != 0)
+ return r;
+ break;
+
case 'V':
case 'E':
/* Compare the vector length first. */
for (node = var->var_part[0].loc_chain; node; node = node->next)
if (MEM_P (node->loc)
&& MEM_EXPR (node->loc) == expr
- && INT_MEM_OFFSET (node->loc) == 0)
+ && int_mem_offset (node->loc) == 0)
{
where = node;
break;
{
for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
{
- /* We want to remove dying MEMs that doesn't refer to DECL. */
+ /* We want to remove dying MEMs that don't refer to DECL. */
if (GET_CODE (loc->loc) == MEM
&& (MEM_EXPR (loc->loc) != decl
- || INT_MEM_OFFSET (loc->loc) != 0)
- && !mem_dies_at_call (loc->loc))
+ || int_mem_offset (loc->loc) != 0)
+ && mem_dies_at_call (loc->loc))
break;
/* We want to move here MEMs that do refer to DECL. */
else if (GET_CODE (loc->loc) == VALUE
if (GET_CODE (loc->loc) != MEM
|| (MEM_EXPR (loc->loc) == decl
- && INT_MEM_OFFSET (loc->loc) == 0)
+ && int_mem_offset (loc->loc) == 0)
|| !mem_dies_at_call (loc->loc))
{
if (old_loc != loc->loc && emit_notes)
}
/* Remove all MEMs from the location list of a hash table entry for a
- value. */
+ onepart variable. */
int
dataflow_set_remove_mem_locs (variable **slot, dataflow_set *set)
{
variable *var = *slot;
- if (var->onepart == ONEPART_VALUE)
+ if (var->onepart != NOT_ONEPART)
{
location_chain *loc, **locp;
bool changed = false;
EXECUTE_IF_SET_IN_HARD_REG_SET (invalidated_regs, 0, r, hrsi)
var_regno_delete (set, r);
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
{
set->traversed_vars = set->vars;
shared_hash_htab (set->vars)
return lc1 != lc2;
}
+/* Return true if one-part variables VAR1 and VAR2 are different.
+ They must be in canonical order. */
+
+static void
+dump_onepart_variable_differences (variable *var1, variable *var2)
+{
+ location_chain *lc1, *lc2;
+
+ gcc_assert (var1 != var2);
+ gcc_assert (dump_file);
+ gcc_assert (dv_as_opaque (var1->dv) == dv_as_opaque (var2->dv));
+ gcc_assert (var1->n_var_parts == 1
+ && var2->n_var_parts == 1);
+
+ lc1 = var1->var_part[0].loc_chain;
+ lc2 = var2->var_part[0].loc_chain;
+
+ gcc_assert (lc1 && lc2);
+
+ while (lc1 && lc2)
+ {
+ switch (loc_cmp (lc1->loc, lc2->loc))
+ {
+ case -1:
+ fprintf (dump_file, "removed: ");
+ print_rtl_single (dump_file, lc1->loc);
+ lc1 = lc1->next;
+ continue;
+ case 0:
+ break;
+ case 1:
+ fprintf (dump_file, "added: ");
+ print_rtl_single (dump_file, lc2->loc);
+ lc2 = lc2->next;
+ continue;
+ default:
+ gcc_unreachable ();
+ }
+ lc1 = lc1->next;
+ lc2 = lc2->next;
+ }
+
+ while (lc1)
+ {
+ fprintf (dump_file, "removed: ");
+ print_rtl_single (dump_file, lc1->loc);
+ lc1 = lc1->next;
+ }
+
+ while (lc2)
+ {
+ fprintf (dump_file, "added: ");
+ print_rtl_single (dump_file, lc2->loc);
+ lc2 = lc2->next;
+ }
+}
+
/* Return true if variables VAR1 and VAR2 are different. */
static bool
{
variable_iterator_type hi;
variable *var1;
+ bool diffound = false;
+ bool details = (dump_file && (dump_flags & TDF_DETAILS));
+
+#define RETRUE \
+ do \
+ { \
+ if (!details) \
+ return true; \
+ else \
+ diffound = true; \
+ } \
+ while (0)
if (old_set->vars == new_set->vars)
return false;
if (shared_hash_htab (old_set->vars)->elements ()
!= shared_hash_htab (new_set->vars)->elements ())
- return true;
+ RETRUE;
FOR_EACH_HASH_TABLE_ELEMENT (*shared_hash_htab (old_set->vars),
var1, variable, hi)
{
variable_table_type *htab = shared_hash_htab (new_set->vars);
variable *var2 = htab->find_with_hash (var1->dv, dv_htab_hash (var1->dv));
+
if (!var2)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "dataflow difference found: removal of:\n");
dump_var (var1);
}
- return true;
+ RETRUE;
}
-
- if (variable_different_p (var1, var2))
+ else if (variable_different_p (var1, var2))
{
- if (dump_file && (dump_flags & TDF_DETAILS))
+ if (details)
{
fprintf (dump_file, "dataflow difference found: "
"old and new follow:\n");
dump_var (var1);
+ if (dv_onepart_p (var1->dv))
+ dump_onepart_variable_differences (var1, var2);
dump_var (var2);
}
- return true;
+ RETRUE;
}
}
- /* No need to traverse the second hashtab, if both have the same number
- of elements and the second one had all entries found in the first one,
- then it can't have any extra entries. */
- return false;
+ /* There's no need to traverse the second hashtab unless we want to
+ print the details. If both have the same number of elements and
+ the second one had all entries found in the first one, then the
+ second can't have any extra entries. */
+ if (!details)
+ return diffound;
+
+ FOR_EACH_HASH_TABLE_ELEMENT (*shared_hash_htab (new_set->vars),
+ var1, variable, hi)
+ {
+ variable_table_type *htab = shared_hash_htab (old_set->vars);
+ variable *var2 = htab->find_with_hash (var1->dv, dv_htab_hash (var1->dv));
+ if (!var2)
+ {
+ if (details)
+ {
+ fprintf (dump_file, "dataflow difference found: addition of:\n");
+ dump_var (var1);
+ }
+ RETRUE;
+ }
+ }
+
+#undef RETRUE
+
+ return diffound;
}
/* Free the contents of dataflow set SET. */
set->vars = NULL;
}
+/* Return true if T is a tracked parameter with non-degenerate record type. */
+
+static bool
+tracked_record_parameter_p (tree t)
+{
+ if (TREE_CODE (t) != PARM_DECL)
+ return false;
+
+ if (DECL_MODE (t) == BLKmode)
+ return false;
+
+ tree type = TREE_TYPE (t);
+ if (TREE_CODE (type) != RECORD_TYPE)
+ return false;
+
+ if (TYPE_FIELDS (type) == NULL_TREE
+ || DECL_CHAIN (TYPE_FIELDS (type)) == NULL_TREE)
+ return false;
+
+ return true;
+}
+
/* Shall EXPR be tracked? */
static bool
return DECL_RTL_SET_P (expr);
/* If EXPR is not a parameter or a variable do not track it. */
- if (TREE_CODE (expr) != VAR_DECL && TREE_CODE (expr) != PARM_DECL)
+ if (!VAR_P (expr) && TREE_CODE (expr) != PARM_DECL)
return 0;
/* It also must have a name... */
don't need to track this expression if the ultimate declaration is
ignored. */
realdecl = expr;
- if (TREE_CODE (realdecl) == VAR_DECL && DECL_HAS_DEBUG_EXPR_P (realdecl))
+ if (VAR_P (realdecl) && DECL_HAS_DEBUG_EXPR_P (realdecl))
{
realdecl = DECL_DEBUG_EXPR (realdecl);
if (!DECL_P (realdecl))
|| (TREE_CODE (realdecl) == MEM_REF
&& TREE_CODE (TREE_OPERAND (realdecl, 0)) == ADDR_EXPR))
{
- HOST_WIDE_INT bitsize, bitpos, maxsize;
+ HOST_WIDE_INT bitsize, bitpos;
bool reverse;
tree innerdecl
- = get_ref_base_and_extent (realdecl, &bitpos, &bitsize,
- &maxsize, &reverse);
- if (!DECL_P (innerdecl)
+ = get_ref_base_and_extent_hwi (realdecl, &bitpos,
+ &bitsize, &reverse);
+ if (!innerdecl
+ || !DECL_P (innerdecl)
|| DECL_IGNORED_P (innerdecl)
- /* Do not track declarations for parts of tracked parameters
- since we want to track them as a whole instead. */
- || (TREE_CODE (innerdecl) == PARM_DECL
- && DECL_MODE (innerdecl) != BLKmode
- && TREE_CODE (TREE_TYPE (innerdecl)) != UNION_TYPE)
+ /* Do not track declarations for parts of tracked record
+ parameters since we want to track them as a whole. */
+ || tracked_record_parameter_p (innerdecl)
|| TREE_STATIC (innerdecl)
- || bitsize <= 0
- || bitpos + bitsize > 256
- || bitsize != maxsize)
+ || bitsize == 0
+ || bitpos + bitsize > 256)
return 0;
else
realdecl = expr;
if (decl_rtl && MEM_P (decl_rtl))
{
/* Do not track structures and arrays. */
- if (GET_MODE (decl_rtl) == BLKmode
- || AGGREGATE_TYPE_P (TREE_TYPE (realdecl)))
+ if ((GET_MODE (decl_rtl) == BLKmode
+ || AGGREGATE_TYPE_P (TREE_TYPE (realdecl)))
+ && !tracked_record_parameter_p (realdecl))
return 0;
if (MEM_SIZE_KNOWN_P (decl_rtl)
- && MEM_SIZE (decl_rtl) > MAX_VAR_PARTS)
+ && maybe_gt (MEM_SIZE (decl_rtl), MAX_VAR_PARTS))
return 0;
}
EXPR+OFFSET. */
static bool
-same_variable_part_p (rtx loc, tree expr, HOST_WIDE_INT offset)
+same_variable_part_p (rtx loc, tree expr, poly_int64 offset)
{
tree expr2;
- HOST_WIDE_INT offset2;
+ poly_int64 offset2;
if (! DECL_P (expr))
return false;
else if (MEM_P (loc))
{
expr2 = MEM_EXPR (loc);
- offset2 = INT_MEM_OFFSET (loc);
+ offset2 = int_mem_offset (loc);
}
else
return false;
expr = var_debug_decl (expr);
expr2 = var_debug_decl (expr2);
- return (expr == expr2 && offset == offset2);
+ return (expr == expr2 && known_eq (offset, offset2));
}
/* LOC is a REG or MEM that we would like to track if possible.
from EXPR in *OFFSET_OUT (if nonnull). */
static bool
-track_loc_p (rtx loc, tree expr, HOST_WIDE_INT offset, bool store_reg_p,
+track_loc_p (rtx loc, tree expr, poly_int64 offset, bool store_reg_p,
machine_mode *mode_out, HOST_WIDE_INT *offset_out)
{
machine_mode mode;
machine_mode pseudo_mode;
pseudo_mode = PSEUDO_REGNO_MODE (ORIGINAL_REGNO (loc));
- if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (pseudo_mode))
+ if (paradoxical_subreg_p (mode, pseudo_mode))
{
offset += byte_lowpart_offset (pseudo_mode, mode);
mode = pseudo_mode;
because the real and imaginary parts are represented as separate
pseudo registers, even if the whole complex value fits into one
hard register. */
- if ((GET_MODE_SIZE (mode) > GET_MODE_SIZE (DECL_MODE (expr))
+ if ((paradoxical_subreg_p (mode, DECL_MODE (expr))
|| (store_reg_p
&& !COMPLEX_MODE_P (DECL_MODE (expr))
- && hard_regno_nregs[REGNO (loc)][DECL_MODE (expr)] == 1))
- && offset + byte_lowpart_offset (DECL_MODE (expr), mode) == 0)
+ && hard_regno_nregs (REGNO (loc), DECL_MODE (expr)) == 1))
+ && known_eq (offset + byte_lowpart_offset (DECL_MODE (expr), mode), 0))
{
mode = DECL_MODE (expr);
offset = 0;
}
- if (offset < 0 || offset >= MAX_VAR_PARTS)
+ HOST_WIDE_INT const_offset;
+ if (!track_offset_p (offset, &const_offset))
return false;
if (mode_out)
*mode_out = mode;
if (offset_out)
- *offset_out = offset;
+ *offset_out = const_offset;
return true;
}
static rtx
var_lowpart (machine_mode mode, rtx loc)
{
- unsigned int offset, reg_offset, regno;
+ unsigned int regno;
if (GET_MODE (loc) == mode)
return loc;
if (!REG_P (loc) && !MEM_P (loc))
return NULL;
- offset = byte_lowpart_offset (mode, GET_MODE (loc));
+ poly_uint64 offset = byte_lowpart_offset (mode, GET_MODE (loc));
if (MEM_P (loc))
return adjust_address_nv (loc, mode, offset);
- reg_offset = subreg_lowpart_offset (mode, GET_MODE (loc));
+ poly_uint64 reg_offset = subreg_lowpart_offset (mode, GET_MODE (loc));
regno = REGNO (loc) + subreg_regno_offset (REGNO (loc), GET_MODE (loc),
reg_offset, mode);
return gen_rtx_REG_offset (loc, mode, regno, offset);
return MO_CLOBBER;
else if (target_for_debug_bind (var_debug_decl (expr)))
return MO_CLOBBER;
- else if (track_loc_p (loc, expr, INT_MEM_OFFSET (loc),
+ else if (track_loc_p (loc, expr, int_mem_offset (loc),
false, modep, NULL)
/* Multi-part variables shouldn't refer to one-part
variable names such as VALUEs (never happens) or
DEBUG_EXPRs (only happens in the presence of debug
insns). */
- && (!MAY_HAVE_DEBUG_INSNS
+ && (!MAY_HAVE_DEBUG_BIND_INSNS
|| !rtx_debug_expr_p (XEXP (loc, 0))))
return MO_USE;
else
return;
}
ret = simplify_gen_binary (code, GET_MODE (val), val, arg);
- if (ret == val)
- /* Ensure ret isn't VALUE itself (which can happen e.g. for
- (plus (reg1) (reg2)) when reg2 is known to be 0), as that
- breaks a lot of routines during var-tracking. */
- ret = gen_rtx_fmt_ee (PLUS, GET_MODE (val), val, const0_rtx);
break;
default:
gcc_unreachable ();
rtx xexpr = gen_rtx_SET (loc, src);
if (same_variable_part_p (SET_SRC (xexpr),
MEM_EXPR (loc),
- INT_MEM_OFFSET (loc)))
+ int_mem_offset (loc)))
mo.type = MO_COPY;
else
mo.type = MO_SET;
resolve = preserve = !cselib_preserved_value_p (v);
/* We cannot track values for multiple-part variables, so we track only
- locations for tracked parameters passed either by invisible reference
- or directly in multiple locations. */
+ locations for tracked record parameters. */
if (track_p
&& REG_P (loc)
&& REG_EXPR (loc)
- && TREE_CODE (REG_EXPR (loc)) == PARM_DECL
- && DECL_MODE (REG_EXPR (loc)) != BLKmode
- && TREE_CODE (TREE_TYPE (REG_EXPR (loc))) != UNION_TYPE
- && ((MEM_P (DECL_INCOMING_RTL (REG_EXPR (loc)))
- && XEXP (DECL_INCOMING_RTL (REG_EXPR (loc)), 0) != arg_pointer_rtx)
- || (GET_CODE (DECL_INCOMING_RTL (REG_EXPR (loc))) == PARALLEL
- && XVECLEN (DECL_INCOMING_RTL (REG_EXPR (loc)), 0) > 1)))
+ && tracked_record_parameter_p (REG_EXPR (loc)))
{
/* Although we don't use the value here, it could be used later by the
mere virtue of its existence as the operand of the reverse operation
else if (REG_P (x))
{
cselib_val *val = cselib_lookup (x, GET_MODE (x), 0, VOIDmode);
+ scalar_int_mode mode;
if (val && cselib_preserved_value_p (val))
item = val->val_rtx;
- else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
- || GET_MODE_CLASS (GET_MODE (x)) == MODE_PARTIAL_INT)
+ else if (is_a <scalar_int_mode> (GET_MODE (x), &mode))
{
- machine_mode mode = GET_MODE (x);
-
- while ((mode = GET_MODE_WIDER_MODE (mode)) != VOIDmode
- && GET_MODE_BITSIZE (mode) <= BITS_PER_WORD)
+ opt_scalar_int_mode mode_iter;
+ FOR_EACH_WIDER_MODE (mode_iter, mode)
{
- rtx reg = simplify_subreg (mode, x, GET_MODE (x), 0);
+ mode = mode_iter.require ();
+ if (GET_MODE_BITSIZE (mode) > BITS_PER_WORD)
+ break;
+ rtx reg = simplify_subreg (mode, x, GET_MODE (x), 0);
if (reg == NULL_RTX || !REG_P (reg))
continue;
val = cselib_lookup (reg, mode, 0, VOIDmode);
struct adjust_mem_data amd;
amd.mem_mode = VOIDmode;
amd.stack_adjust = -VTI (bb)->out.stack_adjust;
- amd.side_effects = NULL;
amd.store = true;
mem = simplify_replace_fn_rtx (mem, NULL_RTX, adjust_mems,
&amd);
- gcc_assert (amd.side_effects == NULL_RTX);
+ gcc_assert (amd.side_effects.is_empty ());
}
val = cselib_lookup (mem, GET_MODE (mem), 0, VOIDmode);
if (val && cselib_preserved_value_p (val))
{
/* For non-integer stack argument see also if they weren't
initialized by integers. */
- machine_mode imode = int_mode_for_mode (GET_MODE (mem));
- if (imode != GET_MODE (mem) && imode != BLKmode)
+ scalar_int_mode imode;
+ if (int_mode_for_mode (GET_MODE (mem)).exists (&imode)
+ && imode != GET_MODE (mem))
{
val = cselib_lookup (adjust_address_nv (mem, imode, 0),
imode, 0, VOIDmode);
dataflow_set_copy (&old_out, out);
dataflow_set_copy (out, in);
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
local_get_addr_cache = new hash_map<rtx, rtx>;
FOR_EACH_VEC_ELT (VTI (bb)->mos, i, mo)
}
}
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
{
delete local_get_addr_cache;
local_get_addr_cache = NULL;
{
bb_heap_t *worklist = new bb_heap_t (LONG_MIN);
bb_heap_t *pending = new bb_heap_t (LONG_MIN);
- sbitmap visited, in_worklist, in_pending;
+ sbitmap in_worklist, in_pending;
basic_block bb;
edge e;
int *bb_order;
bb_order[rc_order[i]] = i;
free (rc_order);
- visited = sbitmap_alloc (last_basic_block_for_fn (cfun));
+ auto_sbitmap visited (last_basic_block_for_fn (cfun));
in_worklist = sbitmap_alloc (last_basic_block_for_fn (cfun));
in_pending = sbitmap_alloc (last_basic_block_for_fn (cfun));
bitmap_clear (in_worklist);
else
oldinsz = oldoutsz = 0;
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
{
dataflow_set *in = &VTI (bb)->in, *first_out = NULL;
bool first = true, adjust = false;
if (htabmax && htabsz > htabmax)
{
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
inform (DECL_SOURCE_LOCATION (cfun->decl),
"variable tracking size limit exceeded with "
"-fvar-tracking-assignments, retrying without");
}
}
- if (success && MAY_HAVE_DEBUG_INSNS)
+ if (success && MAY_HAVE_DEBUG_BIND_INSNS)
FOR_EACH_BB_FN (bb, cfun)
gcc_assert (VTI (bb)->flooded);
free (bb_order);
delete worklist;
delete pending;
- sbitmap_free (visited);
sbitmap_free (in_worklist);
sbitmap_free (in_pending);
struct expand_loc_callback_data data;
rtx result;
- if (!MAY_HAVE_DEBUG_INSNS)
+ if (!MAY_HAVE_DEBUG_BIND_INSNS)
return loc;
INIT_ELCD (data, vars);
last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode);
/* Attempt to merge adjacent registers or memory. */
- wider_mode = GET_MODE_WIDER_MODE (mode);
for (j = i + 1; j < var->n_var_parts; j++)
if (last_limit <= VAR_PART_OFFSET (var, j))
break;
if (j < var->n_var_parts
- && wider_mode != VOIDmode
+ && GET_MODE_WIDER_MODE (mode).exists (&wider_mode)
&& var->var_part[j].cur_loc
&& mode == GET_MODE (var->var_part[j].cur_loc)
&& (REG_P (loc[n_var_parts]) || MEM_P (loc[n_var_parts]))
rtx new_loc = NULL;
if (REG_P (loc[n_var_parts])
- && hard_regno_nregs[REGNO (loc[n_var_parts])][mode] * 2
- == hard_regno_nregs[REGNO (loc[n_var_parts])][wider_mode]
+ && hard_regno_nregs (REGNO (loc[n_var_parts]), mode) * 2
+ == hard_regno_nregs (REGNO (loc[n_var_parts]), wider_mode)
&& end_hard_regno (mode, REGNO (loc[n_var_parts]))
== REGNO (loc2))
{
if (!changed_variables->elements ())
return;
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
process_changed_values (htab);
data.insn = insn;
delete_variable_part). */
emit_notes = true;
- if (MAY_HAVE_DEBUG_INSNS)
- {
- dropped_values = new variable_table_type (cselib_get_next_uid () * 2);
- }
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
+ dropped_values = new variable_table_type (cselib_get_next_uid () * 2);
dataflow_set_init (&cur);
subsequent basic blocks. */
emit_notes_for_differences (BB_HEAD (bb), &cur, &VTI (bb)->in);
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
local_get_addr_cache = new hash_map<rtx, rtx>;
/* Emit the notes for the changes in the basic block itself. */
emit_notes_in_bb (bb, &cur);
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
delete local_get_addr_cache;
local_get_addr_cache = NULL;
dataflow_set_destroy (&cur);
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
delete dropped_values;
dropped_values = NULL;
assign declaration to *DECLP and offset to *OFFSETP, and return true. */
static bool
-vt_get_decl_and_offset (rtx rtl, tree *declp, HOST_WIDE_INT *offsetp)
+vt_get_decl_and_offset (rtx rtl, tree *declp, poly_int64 *offsetp)
{
if (REG_P (rtl))
{
decl = REG_EXPR (reg);
if (REG_EXPR (reg) != decl)
break;
- if (REG_OFFSET (reg) < offset)
- offset = REG_OFFSET (reg);
+ HOST_WIDE_INT this_offset;
+ if (!track_offset_p (REG_OFFSET (reg), &this_offset))
+ break;
+ offset = MIN (offset, this_offset);
}
if (i == len)
if (MEM_ATTRS (rtl))
{
*declp = MEM_EXPR (rtl);
- *offsetp = INT_MEM_OFFSET (rtl);
+ *offsetp = int_mem_offset (rtl);
return true;
}
}
rtx incoming = DECL_INCOMING_RTL (parm);
tree decl;
machine_mode mode;
- HOST_WIDE_INT offset;
+ poly_int64 offset;
dataflow_set *out;
decl_or_value dv;
offset = 0;
}
- if (!track_loc_p (incoming, parm, offset, false, &mode, &offset))
+ HOST_WIDE_INT const_offset;
+ if (!track_loc_p (incoming, parm, offset, false, &mode, &const_offset))
return;
out = &VTI (ENTRY_BLOCK_PTR_FOR_FN (cfun))->out;
arguments passed by invisible reference aren't dealt with
above: incoming-rtl will have Pmode rather than the
expected mode for the type. */
- if (offset)
+ if (const_offset)
return;
lowpart = var_lowpart (mode, incoming);
if (val)
{
preserve_value (val);
- set_variable_part (out, val->val_rtx, dv, offset,
+ set_variable_part (out, val->val_rtx, dv, const_offset,
VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
dv = dv_from_value (val->val_rtx);
}
{
incoming = var_lowpart (mode, incoming);
gcc_assert (REGNO (incoming) < FIRST_PSEUDO_REGISTER);
- attrs_list_insert (&out->regs[REGNO (incoming)], dv, offset,
+ attrs_list_insert (&out->regs[REGNO (incoming)], dv, const_offset,
incoming);
- set_variable_part (out, incoming, dv, offset,
+ set_variable_part (out, incoming, dv, const_offset,
VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
if (dv_is_value_p (dv))
{
for (i = 0; i < XVECLEN (incoming, 0); i++)
{
rtx reg = XEXP (XVECEXP (incoming, 0, i), 0);
- offset = REG_OFFSET (reg);
+ /* vt_get_decl_and_offset has already checked that the offset
+ is a valid variable part. */
+ const_offset = get_tracked_reg_offset (reg);
gcc_assert (REGNO (reg) < FIRST_PSEUDO_REGISTER);
- attrs_list_insert (&out->regs[REGNO (reg)], dv, offset, reg);
- set_variable_part (out, reg, dv, offset,
+ attrs_list_insert (&out->regs[REGNO (reg)], dv, const_offset, reg);
+ set_variable_part (out, reg, dv, const_offset,
VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
}
}
else if (MEM_P (incoming))
{
incoming = var_lowpart (mode, incoming);
- set_variable_part (out, incoming, dv, offset,
+ set_variable_part (out, incoming, dv, const_offset,
VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
}
}
cfa_base_rtx = NULL_RTX;
return;
}
- if (!MAY_HAVE_DEBUG_INSNS)
+ if (!MAY_HAVE_DEBUG_BIND_INSNS)
return;
/* Tell alias analysis that cfa_base_rtx should share
cselib_preserve_cfa_base_value (val, REGNO (cfa_base_rtx));
}
+/* Reemit INSN, a MARKER_DEBUG_INSN, as a note. */
+
+static rtx_insn *
+reemit_marker_as_note (rtx_insn *insn)
+{
+ gcc_checking_assert (DEBUG_MARKER_INSN_P (insn));
+
+ enum insn_note kind = INSN_DEBUG_MARKER_KIND (insn);
+
+ switch (kind)
+ {
+ case NOTE_INSN_BEGIN_STMT:
+ {
+ rtx_insn *note = NULL;
+ if (cfun->debug_nonbind_markers)
+ {
+ note = emit_note_before (kind, insn);
+ NOTE_MARKER_LOCATION (note) = INSN_LOCATION (insn);
+ }
+ delete_insn (insn);
+ return note;
+ }
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
/* Allocate and initialize the data structures for variable tracking
and parse the RTL to get the micro operations. */
alloc_aux_for_blocks (sizeof (variable_tracking_info));
- empty_shared_hash = new shared_hash;
+ empty_shared_hash = shared_hash_pool.allocate ();
empty_shared_hash->refcount = 1;
empty_shared_hash->htab = new variable_table_type (1);
changed_variables = new variable_table_type (10);
VTI (bb)->permp = NULL;
}
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
{
cselib_init (CSELIB_RECORD_MEMORY | CSELIB_PRESERVE_CONSTANTS);
scratch_regs = BITMAP_ALLOC (NULL);
global_get_addr_cache = NULL;
}
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
{
rtx reg, expr;
int ofst;
HOST_WIDE_INT pre, post = 0;
basic_block first_bb, last_bb;
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
{
cselib_record_sets_hook = add_with_sets;
if (dump_file && (dump_flags & TDF_DETAILS))
{
HOST_WIDE_INT offset = VTI (bb)->out.stack_adjust;
VTI (bb)->out.stack_adjust = VTI (bb)->in.stack_adjust;
- for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
- insn = NEXT_INSN (insn))
+
+ rtx_insn *next;
+ FOR_BB_INSNS_SAFE (bb, insn, next)
{
if (INSN_P (insn))
{
cselib_hook_called = false;
adjust_insn (bb, insn);
- if (MAY_HAVE_DEBUG_INSNS)
+ if (DEBUG_MARKER_INSN_P (insn))
+ {
+ reemit_marker_as_note (insn);
+ continue;
+ }
+
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
{
if (CALL_P (insn))
prepare_call_arguments (bb, insn);
vt_init_cfa_base ();
hard_frame_pointer_adjustment = fp_cfa_offset;
/* Disassociate sp from fp now. */
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
{
cselib_val *v;
cselib_invalidate_rtx (stack_pointer_rtx);
bb = last_bb;
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
{
cselib_preserve_only_values ();
cselib_reset_table (cselib_get_next_uid ());
static int debug_label_num = 1;
-/* Get rid of all debug insns from the insn stream. */
+/* Remove from the insn stream all debug insns used for variable
+ tracking at assignments. */
static void
-delete_debug_insns (void)
+delete_vta_debug_insns (void)
{
basic_block bb;
rtx_insn *insn, *next;
FOR_BB_INSNS_SAFE (bb, insn, next)
if (DEBUG_INSN_P (insn))
{
+ if (DEBUG_MARKER_INSN_P (insn))
+ {
+ reemit_marker_as_note (insn);
+ continue;
+ }
+
tree decl = INSN_VAR_LOCATION_DECL (insn);
if (TREE_CODE (decl) == LABEL_DECL
&& DECL_NAME (decl)
vt_debug_insns_local (bool skipped ATTRIBUTE_UNUSED)
{
/* ??? Just skip it all for now. */
- delete_debug_insns ();
+ delete_vta_debug_insns ();
}
/* Free the data structures needed for variable tracking. */
location_chain_pool.release ();
shared_hash_pool.release ();
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
{
if (global_get_addr_cache)
delete global_get_addr_cache;
{
bool success;
- if (flag_var_tracking_assignments < 0
+ /* We won't be called as a separate pass if flag_var_tracking is not
+ set, but final may call us to turn debug markers into notes. */
+ if ((!flag_var_tracking && MAY_HAVE_DEBUG_INSNS)
+ || flag_var_tracking_assignments < 0
/* Var-tracking right now assumes the IR doesn't contain
any pseudos at this point. */
|| targetm.no_register_allocation)
{
- delete_debug_insns ();
+ delete_vta_debug_insns ();
return 0;
}
- if (n_basic_blocks_for_fn (cfun) > 500 &&
- n_edges_for_fn (cfun) / n_basic_blocks_for_fn (cfun) >= 20)
+ if (!flag_var_tracking)
+ return 0;
+
+ if (n_basic_blocks_for_fn (cfun) > 500
+ && n_edges_for_fn (cfun) / n_basic_blocks_for_fn (cfun) >= 20)
{
vt_debug_insns_local (true);
return 0;
{
vt_finalize ();
- delete_debug_insns ();
+ delete_vta_debug_insns ();
/* This is later restored by our caller. */
flag_var_tracking_assignments = 0;