#include "toplev.h"
#include "params.h"
#include "diagnostic.h"
+#include "pointer-set.h"
/* var-tracking.c assumes that tree code with the same value as VALUE rtx code
has no chance to appear in REG_EXPR/MEM_EXPRs and isn't a decl.
int refcount;
/* Number of variable parts. */
- int n_var_parts;
+ char n_var_parts;
+
+ /* True if this variable changed (any of its) cur_loc fields
+ during the current emit_notes_for_changes resp.
+ emit_notes_for_differences call. */
+ bool cur_loc_changed;
+
+ /* True if this variable_def struct is currently in the
+ changed_variables hash table. */
+ bool in_changed_variables;
/* The variable parts. */
variable_part var_part[1];
static void dataflow_set_copy (dataflow_set *, dataflow_set *);
static int variable_union_info_cmp_pos (const void *, const void *);
static int variable_union (void **, void *);
-static int variable_canonicalize (void **, void *);
static void dataflow_set_union (dataflow_set *, dataflow_set *);
static location_chain find_loc_in_1pdv (rtx, variable, htab_t);
static bool canon_value_cmp (rtx, rtx);
static int loc_cmp (rtx, rtx);
static bool variable_part_different_p (variable_part *, variable_part *);
static bool onepart_variable_different_p (variable, variable);
-static bool variable_different_p (variable, variable, bool);
+static bool variable_different_p (variable, variable);
static int dataflow_set_different_1 (void **, void *);
static bool dataflow_set_different (dataflow_set *, dataflow_set *);
static void dataflow_set_destroy (dataflow_set *);
return vars->htab;
}
+/* Return true if VAR is shared, or maybe because VARS is shared. */
+
+static inline bool
+shared_var_p (variable var, shared_hash vars)
+{
+ /* Don't count an entry in the changed_variables table as a duplicate. */
+ return ((var->refcount > 1 + (int) var->in_changed_variables)
+ || shared_hash_shared (vars));
+}
+
/* Copy variables into a new hash table. */
static shared_hash
new_var->refcount = 1;
var->refcount--;
new_var->n_var_parts = var->n_var_parts;
+ new_var->cur_loc_changed = var->cur_loc_changed;
+ var->cur_loc_changed = false;
+ new_var->in_changed_variables = false;
if (! flag_var_tracking_uninit)
initialized = VAR_INIT_STATUS_INITIALIZED;
nextp = &new_lc->next;
}
- /* We are at the basic block boundary when copying variable description
- so set the CUR_LOC to be the first element of the chain. */
- if (new_var->var_part[i].loc_chain)
- new_var->var_part[i].cur_loc = new_var->var_part[i].loc_chain->loc;
- else
- new_var->var_part[i].cur_loc = NULL;
+ new_var->var_part[i].cur_loc = var->var_part[i].cur_loc;
}
dst_can_be_shared = false;
else if (set->traversed_vars && set->vars != set->traversed_vars)
slot = shared_hash_find_slot_noinsert (set->vars, var->dv);
*slot = new_var;
+ if (var->in_changed_variables)
+ {
+ void **cslot
+ = htab_find_slot_with_hash (changed_variables, var->dv,
+ dv_htab_hash (var->dv), NO_INSERT);
+ gcc_assert (*cslot == (void *) var);
+ var->in_changed_variables = false;
+ variable_htab_free (var);
+ *cslot = new_var;
+ new_var->in_changed_variables = true;
+ }
return slot;
}
*dstp = src;
- /* If CUR_LOC of some variable part is not the first element of
- the location chain we are going to change it so we have to make
- a copy of the variable. */
- for (k = 0; k < src->n_var_parts; k++)
- {
- gcc_assert (!src->var_part[k].loc_chain
- == !src->var_part[k].cur_loc);
- if (src->var_part[k].loc_chain)
- {
- gcc_assert (src->var_part[k].cur_loc);
- if (src->var_part[k].cur_loc != src->var_part[k].loc_chain->loc)
- break;
- }
- }
- if (k < src->n_var_parts)
- dstp = unshare_variable (set, dstp, src, VAR_INIT_STATUS_UNKNOWN);
-
/* Continue traversing the hash table. */
return 1;
}
{
location_chain nnode;
- if (dst->refcount != 1 || shared_hash_shared (set->vars))
+ if (shared_var_p (dst, set->vars))
{
dstp = unshare_variable (set, dstp, dst,
VAR_INIT_STATUS_INITIALIZED);
dnode = *nodep;
}
- dst->var_part[0].cur_loc = dst->var_part[0].loc_chain->loc;
-
return 1;
}
thus there are at most MAX_VAR_PARTS different offsets. */
gcc_assert (dv_onepart_p (dst->dv) ? k == 1 : k <= MAX_VAR_PARTS);
- if ((dst->refcount > 1 || shared_hash_shared (set->vars))
- && dst->n_var_parts != k)
+ if (dst->n_var_parts != k && shared_var_p (dst, set->vars))
{
dstp = unshare_variable (set, dstp, dst, VAR_INIT_STATUS_UNKNOWN);
dst = (variable)*dstp;
/* If DST is shared compare the location chains.
If they are different we will modify the chain in DST with
high probability so make a copy of DST. */
- if (dst->refcount > 1 || shared_hash_shared (set->vars))
+ if (shared_var_p (dst, set->vars))
{
for (node = src->var_part[i].loc_chain,
node2 = dst->var_part[j].loc_chain; node && node2;
dst->var_part[k].offset = src->var_part[i].offset;
i--;
}
-
- /* We are at the basic block boundary when computing union
- so set the CUR_LOC to be the first element of the chain. */
- if (dst->var_part[k].loc_chain)
- dst->var_part[k].cur_loc = dst->var_part[k].loc_chain->loc;
- else
- dst->var_part[k].cur_loc = NULL;
+ dst->var_part[k].cur_loc = NULL;
}
if (flag_var_tracking_uninit)
return 1;
}
-/* Like variable_union, but only used when doing dataflow_set_union
- into an empty hashtab. To allow sharing, dst is initially shared
- with src (so all variables are "copied" from src to dst hashtab),
- so only unshare_variable for variables that need canonicalization
- are needed. */
-
-static int
-variable_canonicalize (void **slot, void *data)
-{
- variable src;
- dataflow_set *set = (dataflow_set *) data;
- int k;
-
- src = *(variable *) slot;
-
- /* If CUR_LOC of some variable part is not the first element of
- the location chain we are going to change it so we have to make
- a copy of the variable. */
- for (k = 0; k < src->n_var_parts; k++)
- {
- gcc_assert (!src->var_part[k].loc_chain == !src->var_part[k].cur_loc);
- if (src->var_part[k].loc_chain)
- {
- gcc_assert (src->var_part[k].cur_loc);
- if (src->var_part[k].cur_loc != src->var_part[k].loc_chain->loc)
- break;
- }
- }
- if (k < src->n_var_parts)
- slot = unshare_variable (set, slot, src, VAR_INIT_STATUS_UNKNOWN);
- return 1;
-}
-
/* Compute union of dataflow sets SRC and DST and store it to DST. */
static void
{
shared_hash_destroy (dst->vars);
dst->vars = shared_hash_copy (src->vars);
- dst->traversed_vars = dst->vars;
- htab_traverse (shared_hash_htab (dst->vars), variable_canonicalize, dst);
- dst->traversed_vars = NULL;
}
else
htab_traverse (shared_hash_htab (src->vars), variable_union, dst);
gcc_assert (GET_MODE (x) == GET_MODE (y));
+ if (GET_CODE (x) == DEBUG_EXPR)
+ {
+ if (DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x))
+ < DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (y)))
+ return -1;
+#ifdef ENABLE_CHECKING
+ gcc_assert (DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x))
+ > DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (y)));
+#endif
+ return 1;
+ }
+
fmt = GET_RTX_FORMAT (code);
for (i = 0; i < GET_RTX_LENGTH (code); i++)
switch (fmt[i])
decl_or_value dv = var->dv;
location_chain node, next;
+#ifdef ENABLE_RTL_CHECKING
+ int i;
+ for (i = 0; i < var->n_var_parts; i++)
+ gcc_assert (var->var_part[0].cur_loc == NULL);
+ gcc_assert (!var->cur_loc_changed && !var->in_changed_variables);
+#endif
+
if (!dv_onepart_p (dv))
return 1;
dvar->dv = dv;
dvar->refcount = 1;
dvar->n_var_parts = 1;
+ dvar->cur_loc_changed = false;
+ dvar->in_changed_variables = false;
dvar->var_part[0].offset = 0;
dvar->var_part[0].loc_chain = node;
- dvar->var_part[0].cur_loc = node->loc;
+ dvar->var_part[0].cur_loc = NULL;
dstslot
= shared_hash_find_slot_unshare_1 (&dst->vars, dv, dvhash,
var->dv = dv;
var->refcount = 1;
var->n_var_parts = 1;
+ var->cur_loc_changed = false;
+ var->in_changed_variables = false;
var->var_part[0].offset = 0;
var->var_part[0].loc_chain = NULL;
var->var_part[0].cur_loc = NULL;
dst_can_be_shared = false;
}
else
- {
- if (dvar->refcount == 1)
- dvar->var_part[0].cur_loc = dvar->var_part[0].loc_chain->loc;
- dst_can_be_shared = false;
- }
+ dst_can_be_shared = false;
return 1;
}
void **dstp = shared_hash_find_slot (dst->vars, dv);
*dstp = s2var;
s2var->refcount++;
- return variable_canonicalize (dstp, dst);
+ return 1;
}
dsm->src_onepart_cnt++;
{
tree decl = dv_as_decl (var->dv);
location_chain loc, *locp;
+ bool changed = false;
if (!var->n_var_parts)
return 1;
gcc_assert (var->n_var_parts == 1);
- if (var->refcount > 1 || shared_hash_shared (set->vars))
+ if (shared_var_p (var, set->vars))
{
for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
{
{
if (old_loc != loc->loc && emit_notes)
{
+ if (old_loc == var->var_part[0].cur_loc)
+ {
+ changed = true;
+ var->var_part[0].cur_loc = NULL;
+ var->cur_loc_changed = true;
+ }
add_value_chains (var->dv, loc->loc);
remove_value_chains (var->dv, old_loc);
}
}
if (emit_notes)
- remove_value_chains (var->dv, old_loc);
+ {
+ remove_value_chains (var->dv, old_loc);
+ if (old_loc == var->var_part[0].cur_loc)
+ {
+ changed = true;
+ var->var_part[0].cur_loc = NULL;
+ var->cur_loc_changed = true;
+ }
+ }
*locp = loc->next;
pool_free (loc_chain_pool, loc);
}
var->n_var_parts--;
if (emit_notes && dv_is_value_p (var->dv))
remove_cselib_value_chains (var->dv);
- variable_was_changed (var, set);
+ changed = true;
}
+ if (changed)
+ variable_was_changed (var, set);
}
return 1;
gcc_assert (var->n_var_parts == 1);
- if (var->refcount > 1 || shared_hash_shared (set->vars))
+ if (shared_var_p (var, set->vars))
{
for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
if (GET_CODE (loc->loc) == MEM
/* If we have deleted the location which was last emitted
we have to emit new location so add the variable to set
of changed variables. */
- if (var->var_part[0].cur_loc
- && rtx_equal_p (loc->loc, var->var_part[0].cur_loc))
- changed = true;
+ if (var->var_part[0].cur_loc == loc->loc)
+ {
+ changed = true;
+ var->var_part[0].cur_loc = NULL;
+ var->cur_loc_changed = true;
+ }
pool_free (loc_chain_pool, loc);
}
var->n_var_parts--;
if (emit_notes && dv_is_value_p (var->dv))
remove_cselib_value_chains (var->dv);
- gcc_assert (changed);
+ changed = true;
}
if (changed)
- {
- if (var->n_var_parts && var->var_part[0].loc_chain)
- var->var_part[0].cur_loc = var->var_part[0].loc_chain->loc;
- variable_was_changed (var, set);
- }
+ variable_was_changed (var, set);
}
return 1;
return lc1 != lc2;
}
-/* Return true if variables VAR1 and VAR2 are different.
- If COMPARE_CURRENT_LOCATION is true compare also the cur_loc of each
- variable part. */
+/* Return true if variables VAR1 and VAR2 are different. */
static bool
-variable_different_p (variable var1, variable var2,
- bool compare_current_location)
+variable_different_p (variable var1, variable var2)
{
int i;
{
if (var1->var_part[i].offset != var2->var_part[i].offset)
return true;
- if (compare_current_location)
- {
- if (!((REG_P (var1->var_part[i].cur_loc)
- && REG_P (var2->var_part[i].cur_loc)
- && (REGNO (var1->var_part[i].cur_loc)
- == REGNO (var2->var_part[i].cur_loc)))
- || rtx_equal_p (var1->var_part[i].cur_loc,
- var2->var_part[i].cur_loc)))
- return true;
- }
/* One-part values have locations in a canonical order. */
if (i == 0 && var1->var_part[i].offset == 0 && dv_onepart_p (var1->dv))
{
return 0;
}
- if (variable_different_p (var1, var2, false))
+ if (variable_different_p (var1, var2))
{
dataflow_set_different_value = true;
if (emit_notes)
{
void **slot;
+ bool old_cur_loc_changed = false;
/* Remember this decl or VALUE has been added to changed_variables. */
set_dv_changed (var->dv, true);
var->dv,
hash, INSERT);
+ if (*slot)
+ {
+ variable old_var = (variable) *slot;
+ gcc_assert (old_var->in_changed_variables);
+ old_var->in_changed_variables = false;
+ old_cur_loc_changed = old_var->cur_loc_changed;
+ variable_htab_free (*slot);
+ }
if (set && var->n_var_parts == 0)
{
variable empty_var;
empty_var->dv = var->dv;
empty_var->refcount = 1;
empty_var->n_var_parts = 0;
+ empty_var->cur_loc_changed = true;
+ empty_var->in_changed_variables = true;
*slot = empty_var;
goto drop_var;
}
else
{
var->refcount++;
+ var->in_changed_variables = true;
+ /* If within processing one uop a variable is deleted
+ and then readded, we need to assume it has changed. */
+ if (old_cur_loc_changed)
+ var->cur_loc_changed = true;
*slot = var;
}
}
var->dv = dv;
var->refcount = 1;
var->n_var_parts = 1;
+ var->cur_loc_changed = false;
+ var->in_changed_variables = false;
var->var_part[0].offset = offset;
var->var_part[0].loc_chain = NULL;
var->var_part[0].cur_loc = NULL;
if (r == 0)
return slot;
- if (var->refcount > 1 || shared_hash_shared (set->vars))
+ if (shared_var_p (var, set->vars))
{
slot = unshare_variable (set, slot, var, initialized);
var = (variable)*slot;
else
{
/* We have to make a copy of a shared variable. */
- if (var->refcount > 1 || shared_hash_shared (set->vars))
+ if (shared_var_p (var, set->vars))
{
slot = unshare_variable (set, slot, var, initialized);
var = (variable)*slot;
/* We have not found the location part, new one will be created. */
/* We have to make a copy of the shared variable. */
- if (var->refcount > 1 || shared_hash_shared (set->vars))
+ if (shared_var_p (var, set->vars))
{
slot = unshare_variable (set, slot, var, initialized);
var = (variable)*slot;
initialized = node->init;
if (node->set_src != NULL && set_src == NULL)
set_src = node->set_src;
+ if (var->var_part[pos].cur_loc == node->loc)
+ {
+ var->var_part[pos].cur_loc = NULL;
+ var->cur_loc_changed = true;
+ }
pool_free (loc_chain_pool, node);
*nextp = next;
break;
/* If no location was emitted do so. */
if (var->var_part[pos].cur_loc == NULL)
- {
- var->var_part[pos].cur_loc = loc;
- variable_was_changed (var, set);
- }
+ variable_was_changed (var, set);
return slot;
}
location_chain *nextp;
bool changed;
- if (var->refcount > 1 || shared_hash_shared (set->vars))
+ if (shared_var_p (var, set->vars))
{
/* If the variable contains the location part we have to
make a copy of the variable. */
}
/* Delete the location part. */
+ changed = false;
nextp = &var->var_part[pos].loc_chain;
for (node = *nextp; node; node = next)
{
{
if (emit_notes && pos == 0 && dv_onepart_p (var->dv))
remove_value_chains (var->dv, node->loc);
+ /* If we have deleted the location which was last emitted
+ we have to emit new location so add the variable to set
+ of changed variables. */
+ if (var->var_part[pos].cur_loc == node->loc)
+ {
+ changed = true;
+ var->var_part[pos].cur_loc = NULL;
+ var->cur_loc_changed = true;
+ }
pool_free (loc_chain_pool, node);
*nextp = next;
break;
nextp = &node->next;
}
- /* If we have deleted the location which was last emitted
- we have to emit new location so add the variable to set
- of changed variables. */
- if (var->var_part[pos].cur_loc
- && ((REG_P (loc)
- && REG_P (var->var_part[pos].cur_loc)
- && REGNO (loc) == REGNO (var->var_part[pos].cur_loc))
- || rtx_equal_p (loc, var->var_part[pos].cur_loc)))
- {
- changed = true;
- if (var->var_part[pos].loc_chain)
- var->var_part[pos].cur_loc = var->var_part[pos].loc_chain->loc;
- }
- else
- changed = false;
-
if (var->var_part[pos].loc_chain == NULL)
{
- gcc_assert (changed);
+ changed = true;
var->n_var_parts--;
- if (emit_notes && var->n_var_parts == 0 && dv_is_value_p (var->dv))
- remove_cselib_value_chains (var->dv);
+ if (emit_notes)
+ {
+ var->cur_loc_changed = true;
+ if (var->n_var_parts == 0 && dv_is_value_p (var->dv))
+ remove_cselib_value_chains (var->dv);
+ }
while (pos < var->n_var_parts)
{
var->var_part[pos] = var->var_part[pos + 1];
slot = delete_slot_part (set, loc, slot, offset);
}
+/* Structure for passing some other parameters to function
+ vt_expand_loc_callback. */
+struct expand_loc_callback_data
+{
+ /* The variables and values active at this point. */
+ htab_t vars;
+
+ /* True in vt_expand_loc_dummy calls, no rtl should be allocated.
+ Non-NULL should be returned if vt_expand_loc would return
+ non-NULL in that case, NULL otherwise. cur_loc_changed should be
+ computed and cur_loc recomputed when possible (but just once
+ per emit_notes_for_changes call). */
+ bool dummy;
+
+ /* True if expansion of subexpressions had to recompute some
+ VALUE/DEBUG_EXPR_DECL's cur_loc or used a VALUE/DEBUG_EXPR_DECL
+ whose cur_loc has been already recomputed during current
+ emit_notes_for_changes call. */
+ bool cur_loc_changed;
+};
+
/* Callback for cselib_expand_value, that looks for expressions
holding the value in the var-tracking hash tables. Return X for
standard processing, anything else is to be used as-is. */
static rtx
vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
{
- htab_t vars = (htab_t)data;
+ struct expand_loc_callback_data *elcd
+ = (struct expand_loc_callback_data *) data;
+ bool dummy = elcd->dummy;
+ bool cur_loc_changed = elcd->cur_loc_changed;
decl_or_value dv;
variable var;
location_chain loc;
switch (GET_CODE (x))
{
case SUBREG:
- subreg = SUBREG_REG (x);
-
- if (GET_CODE (SUBREG_REG (x)) != VALUE)
- return x;
-
subreg = cselib_expand_value_rtx_cb (SUBREG_REG (x), regs,
max_depth - 1,
vt_expand_loc_callback, data);
if (!subreg)
return NULL;
+ if (dummy)
+ return pc_rtx;
+
result = simplify_gen_subreg (GET_MODE (x), subreg,
GET_MODE (SUBREG_REG (x)),
SUBREG_BYTE (x));
/* Invalid SUBREGs are ok in debug info. ??? We could try
alternate expansions for the VALUE as well. */
- if (!result && (REG_P (subreg) || MEM_P (subreg)))
+ if (!result)
result = gen_rtx_raw_SUBREG (GET_MODE (x), subreg, SUBREG_BYTE (x));
return result;
if (VALUE_RECURSED_INTO (x))
return NULL;
- var = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
+ var = (variable) htab_find_with_hash (elcd->vars, dv, dv_htab_hash (dv));
if (!var)
- return xret;
+ {
+ if (dummy && dv_changed_p (dv))
+ elcd->cur_loc_changed = true;
+ return xret;
+ }
if (var->n_var_parts == 0)
- return xret;
+ {
+ if (dummy)
+ elcd->cur_loc_changed = true;
+ return xret;
+ }
gcc_assert (var->n_var_parts == 1);
VALUE_RECURSED_INTO (x) = true;
result = NULL;
- for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
+ if (var->var_part[0].cur_loc)
{
- result = cselib_expand_value_rtx_cb (loc->loc, regs, max_depth,
- vt_expand_loc_callback, vars);
+ if (dummy)
+ {
+ if (cselib_dummy_expand_value_rtx_cb (var->var_part[0].cur_loc, regs,
+ max_depth,
+ vt_expand_loc_callback, data))
+ result = pc_rtx;
+ }
+ else
+ result = cselib_expand_value_rtx_cb (var->var_part[0].cur_loc, regs,
+ max_depth,
+ vt_expand_loc_callback, data);
if (result)
- break;
+ set_dv_changed (dv, false);
+ }
+ if (!result && dv_changed_p (dv))
+ {
+ set_dv_changed (dv, false);
+ for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
+ if (loc->loc == var->var_part[0].cur_loc)
+ continue;
+ else if (dummy)
+ {
+ elcd->cur_loc_changed = cur_loc_changed;
+ if (cselib_dummy_expand_value_rtx_cb (loc->loc, regs, max_depth,
+ vt_expand_loc_callback,
+ data))
+ {
+ result = pc_rtx;
+ break;
+ }
+ else
+ {
+ result = cselib_expand_value_rtx_cb (loc->loc, regs, max_depth,
+ vt_expand_loc_callback,
+ data);
+ if (result)
+ break;
+ }
+ }
+ if (dummy && (result || var->var_part[0].cur_loc))
+ var->cur_loc_changed = true;
+ var->var_part[0].cur_loc = loc ? loc->loc : NULL_RTX;
+ }
+ if (dummy)
+ {
+ if (var->cur_loc_changed)
+ elcd->cur_loc_changed = true;
+ else if (!result && var->var_part[0].cur_loc == NULL_RTX)
+ elcd->cur_loc_changed = cur_loc_changed;
}
VALUE_RECURSED_INTO (x) = false;
static rtx
vt_expand_loc (rtx loc, htab_t vars)
{
+ struct expand_loc_callback_data data;
+
if (!MAY_HAVE_DEBUG_INSNS)
return loc;
+ data.vars = vars;
+ data.dummy = false;
+ data.cur_loc_changed = false;
loc = cselib_expand_value_rtx_cb (loc, scratch_regs, 5,
- vt_expand_loc_callback, vars);
+ vt_expand_loc_callback, &data);
if (loc && MEM_P (loc))
loc = targetm.delegitimize_address (loc);
-
return loc;
}
+/* Like vt_expand_loc, but only return true/false (whether vt_expand_loc
+ would succeed or not, without actually allocating new rtxes. */
+
+static bool
+vt_expand_loc_dummy (rtx loc, htab_t vars, bool *pcur_loc_changed)
+{
+ struct expand_loc_callback_data data;
+ bool ret;
+
+ gcc_assert (MAY_HAVE_DEBUG_INSNS);
+ data.vars = vars;
+ data.dummy = true;
+ data.cur_loc_changed = false;
+ ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, 5,
+ vt_expand_loc_callback, &data);
+ *pcur_loc_changed = data.cur_loc_changed;
+ return ret;
+}
+
+#ifdef ENABLE_RTL_CHECKING
+/* Used to verify that cur_loc_changed updating is safe. */
+static struct pointer_map_t *emitted_notes;
+#endif
+
/* Emit the NOTE_INSN_VAR_LOCATION for variable *VARP. DATA contains
additional parameters: WHERE specifies whether the note shall be emitted
before or after instruction INSN. */
rtx insn = ((emit_note_data *)data)->insn;
enum emit_note_where where = ((emit_note_data *)data)->where;
htab_t vars = ((emit_note_data *)data)->vars;
- rtx note;
+ rtx note, note_vl;
int i, j, n_var_parts;
bool complete;
enum var_init_status initialized = VAR_INIT_STATUS_UNINITIALIZED;
HOST_WIDE_INT offsets[MAX_VAR_PARTS];
rtx loc[MAX_VAR_PARTS];
tree decl;
+ location_chain lc;
if (dv_is_value_p (var->dv))
- goto clear;
+ goto value_or_debug_decl;
decl = dv_as_decl (var->dv);
if (TREE_CODE (decl) == DEBUG_EXPR_DECL)
- goto clear;
-
- gcc_assert (decl);
+ goto value_or_debug_decl;
complete = true;
last_limit = 0;
n_var_parts = 0;
+ if (!MAY_HAVE_DEBUG_STMTS)
+ {
+ for (i = 0; i < var->n_var_parts; i++)
+ if (var->var_part[i].cur_loc == NULL && var->var_part[i].loc_chain)
+ {
+ var->var_part[i].cur_loc = var->var_part[i].loc_chain->loc;
+ var->cur_loc_changed = true;
+ }
+ if (var->n_var_parts == 0)
+ var->cur_loc_changed = true;
+ }
+#ifndef ENABLE_RTL_CHECKING
+ if (!var->cur_loc_changed)
+ goto clear;
+#endif
for (i = 0; i < var->n_var_parts; i++)
{
enum machine_mode mode, wider_mode;
else if (last_limit > var->var_part[i].offset)
continue;
offsets[n_var_parts] = var->var_part[i].offset;
- loc2 = vt_expand_loc (var->var_part[i].loc_chain->loc, vars);
+ if (!var->var_part[i].cur_loc)
+ {
+ complete = false;
+ continue;
+ }
+ loc2 = vt_expand_loc (var->var_part[i].cur_loc, vars);
if (!loc2)
{
complete = false;
continue;
}
loc[n_var_parts] = loc2;
- mode = GET_MODE (var->var_part[i].loc_chain->loc);
- initialized = var->var_part[i].loc_chain->init;
+ mode = GET_MODE (var->var_part[i].cur_loc);
+ for (lc = var->var_part[i].loc_chain; lc; lc = lc->next)
+ if (var->var_part[i].cur_loc == lc->loc)
+ {
+ initialized = lc->init;
+ break;
+ }
+ gcc_assert (lc);
last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode);
/* Attempt to merge adjacent registers or memory. */
break;
if (j < var->n_var_parts
&& wider_mode != VOIDmode
- && mode == GET_MODE (var->var_part[j].loc_chain->loc)
+ && 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]))
- && (loc2 = vt_expand_loc (var->var_part[j].loc_chain->loc, vars))
- && GET_CODE (loc[n_var_parts]) == GET_CODE (loc2)
- && last_limit == var->var_part[j].offset)
+ && last_limit == var->var_part[j].offset
+ && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars))
+ && GET_CODE (loc[n_var_parts]) == GET_CODE (loc2))
{
rtx new_loc = NULL;
if ((unsigned HOST_WIDE_INT) last_limit < TREE_INT_CST_LOW (type_size_unit))
complete = false;
- if (where != EMIT_NOTE_BEFORE_INSN)
- {
- note = emit_note_after (NOTE_INSN_VAR_LOCATION, insn);
- if (where == EMIT_NOTE_AFTER_CALL_INSN)
- NOTE_DURING_CALL_P (note) = true;
- }
- else
- note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn);
-
if (! flag_var_tracking_uninit)
initialized = VAR_INIT_STATUS_INITIALIZED;
+ note_vl = NULL_RTX;
if (!complete)
- {
- NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, decl,
- NULL_RTX, (int) initialized);
- }
+ note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl, NULL_RTX,
+ (int) initialized);
else if (n_var_parts == 1)
{
rtx expr_list
= gen_rtx_EXPR_LIST (VOIDmode, loc[0], GEN_INT (offsets[0]));
- NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, decl,
- expr_list,
- (int) initialized);
+ note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl, expr_list,
+ (int) initialized);
}
else if (n_var_parts)
{
parallel = gen_rtx_PARALLEL (VOIDmode,
gen_rtvec_v (n_var_parts, loc));
- NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, decl,
- parallel,
- (int) initialized);
+ note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl,
+ parallel, (int) initialized);
}
+#ifdef ENABLE_RTL_CHECKING
+ if (note_vl)
+ {
+ void **note_slot = pointer_map_insert (emitted_notes, decl);
+ rtx pnote = (rtx) *note_slot;
+ if (!var->cur_loc_changed && (pnote || PAT_VAR_LOCATION_LOC (note_vl)))
+ {
+ gcc_assert (pnote);
+ gcc_assert (rtx_equal_p (PAT_VAR_LOCATION_LOC (pnote),
+ PAT_VAR_LOCATION_LOC (note_vl)));
+ }
+ *note_slot = (void *) note_vl;
+ }
+ if (!var->cur_loc_changed)
+ goto clear;
+#endif
+
+ if (where != EMIT_NOTE_BEFORE_INSN)
+ {
+ note = emit_note_after (NOTE_INSN_VAR_LOCATION, insn);
+ if (where == EMIT_NOTE_AFTER_CALL_INSN)
+ NOTE_DURING_CALL_P (note) = true;
+ }
+ else
+ note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn);
+ NOTE_VAR_LOCATION (note) = note_vl;
+
clear:
set_dv_changed (var->dv, false);
+ var->cur_loc_changed = false;
+ gcc_assert (var->in_changed_variables);
+ var->in_changed_variables = false;
htab_clear_slot (changed_variables, varp);
/* Continue traversing the hash table. */
return 1;
+
+ value_or_debug_decl:
+ if (dv_changed_p (var->dv) && var->n_var_parts)
+ {
+ location_chain lc;
+ bool cur_loc_changed;
+
+ if (var->var_part[0].cur_loc
+ && vt_expand_loc_dummy (var->var_part[0].cur_loc, vars,
+ &cur_loc_changed))
+ goto clear;
+ for (lc = var->var_part[0].loc_chain; lc; lc = lc->next)
+ if (lc->loc != var->var_part[0].cur_loc
+ && vt_expand_loc_dummy (lc->loc, vars, &cur_loc_changed))
+ break;
+ var->var_part[0].cur_loc = lc ? lc->loc : NULL_RTX;
+ }
+ goto clear;
}
DEF_VEC_P (variable);
}
}
+/* For each changed decl (except DEBUG_EXPR_DECLs) recompute
+ cur_loc if needed (and cur_loc of all VALUEs and DEBUG_EXPR_DECLs
+ it needs and are also in changed variables) and track whether
+ cur_loc (or anything it uses to compute location) had to change
+ during the current emit_notes_for_changes call. */
+
+static int
+check_changed_vars_3 (void **slot, void *data)
+{
+ variable var = (variable) *slot;
+ htab_t vars = (htab_t) data;
+ int i;
+ location_chain lc;
+ bool cur_loc_changed;
+
+ if (dv_is_value_p (var->dv)
+ || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL)
+ return 1;
+
+ for (i = 0; i < var->n_var_parts; i++)
+ {
+ if (var->var_part[i].cur_loc
+ && vt_expand_loc_dummy (var->var_part[i].cur_loc, vars,
+ &cur_loc_changed))
+ {
+ if (cur_loc_changed)
+ var->cur_loc_changed = true;
+ continue;
+ }
+ for (lc = var->var_part[i].loc_chain; lc; lc = lc->next)
+ if (lc->loc != var->var_part[i].cur_loc
+ && vt_expand_loc_dummy (lc->loc, vars, &cur_loc_changed))
+ break;
+ if (lc || var->var_part[i].cur_loc)
+ var->cur_loc_changed = true;
+ var->var_part[i].cur_loc = lc ? lc->loc : NULL_RTX;
+ }
+ if (var->n_var_parts == 0)
+ var->cur_loc_changed = true;
+ return 1;
+}
+
/* Emit NOTE_INSN_VAR_LOCATION note for each variable from a chain
CHANGED_VARIABLES and delete this chain. WHERE specifies whether the notes
shall be emitted before of after instruction INSN. */
while (VEC_length (variable, changed_variables_stack) > 0)
check_changed_vars_2 (VEC_pop (variable, changed_variables_stack),
htab);
+ htab_traverse (changed_variables, check_changed_vars_3, htab);
}
data.insn = insn;
empty_var->dv = old_var->dv;
empty_var->refcount = 0;
empty_var->n_var_parts = 0;
+ empty_var->cur_loc_changed = false;
+ empty_var->in_changed_variables = false;
if (dv_onepart_p (old_var->dv))
{
location_chain lc;
remove_cselib_value_chains (old_var->dv);
}
variable_was_changed (empty_var, NULL);
+ /* Continue traversing the hash table. */
+ return 1;
}
- else if (variable_different_p (old_var, new_var, true))
+ if (variable_different_p (old_var, new_var))
{
if (dv_onepart_p (old_var->dv))
{
}
variable_was_changed (new_var, NULL);
}
+ /* Update cur_loc. */
+ if (old_var != new_var)
+ {
+ int i;
+ for (i = 0; i < new_var->n_var_parts; i++)
+ {
+ new_var->var_part[i].cur_loc = NULL;
+ if (old_var->n_var_parts != new_var->n_var_parts
+ || old_var->var_part[i].offset != new_var->var_part[i].offset)
+ new_var->cur_loc_changed = true;
+ else if (old_var->var_part[i].cur_loc != NULL)
+ {
+ location_chain lc;
+ rtx cur_loc = old_var->var_part[i].cur_loc;
+
+ for (lc = new_var->var_part[i].loc_chain; lc; lc = lc->next)
+ if (lc->loc == cur_loc
+ || rtx_equal_p (cur_loc, lc->loc))
+ {
+ new_var->var_part[i].cur_loc = lc->loc;
+ break;
+ }
+ if (lc == NULL)
+ new_var->cur_loc_changed = true;
+ }
+ }
+ }
/* Continue traversing the hash table. */
return 1;
dv_htab_hash (new_var->dv));
if (!old_var)
{
+ int i;
/* Variable has appeared. */
if (dv_onepart_p (new_var->dv))
{
if (dv_is_value_p (new_var->dv))
add_cselib_value_chains (new_var->dv);
}
+ for (i = 0; i < new_var->n_var_parts; i++)
+ new_var->var_part[i].cur_loc = NULL;
variable_was_changed (new_var, NULL);
}
basic_block bb;
dataflow_set cur;
+#ifdef ENABLE_RTL_CHECKING
+ emitted_notes = pointer_map_create ();
+#endif
gcc_assert (!htab_elements (changed_variables));
/* Free memory occupied by the out hash tables, as they aren't used
if (MAY_HAVE_DEBUG_INSNS)
VEC_free (variable, heap, changed_variables_stack);
+#ifdef ENABLE_RTL_CHECKING
+ pointer_map_destroy (emitted_notes);
+#endif
emit_notes = false;
}