summaryrefslogtreecommitdiff
path: root/gcc/var-tracking.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/var-tracking.c')
-rw-r--r--gcc/var-tracking.c2150
1 files changed, 1278 insertions, 872 deletions
diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c
index 214f58a8ac2..8a64000b616 100644
--- a/gcc/var-tracking.c
+++ b/gcc/var-tracking.c
@@ -301,6 +301,48 @@ typedef struct location_chain_def
enum var_init_status init;
} *location_chain;
+/* A vector of loc_exp_dep holds the active dependencies of a one-part
+ DV on VALUEs, i.e., the VALUEs expanded so as to form the current
+ location of DV. Each entry is also part of VALUE' s linked-list of
+ backlinks back to DV. */
+typedef struct loc_exp_dep_s
+{
+ /* The dependent DV. */
+ decl_or_value dv;
+ /* The dependency VALUE or DECL_DEBUG. */
+ rtx value;
+ /* The next entry in VALUE's backlinks list. */
+ struct loc_exp_dep_s *next;
+ /* A pointer to the pointer to this entry (head or prev's next) in
+ the doubly-linked list. */
+ struct loc_exp_dep_s **pprev;
+} loc_exp_dep;
+
+DEF_VEC_O (loc_exp_dep);
+
+/* This data structure is allocated for one-part variables at the time
+ of emitting notes. */
+struct onepart_aux
+{
+ /* Doubly-linked list of dependent DVs. These are DVs whose cur_loc
+ computation used the expansion of this variable, and that ought
+ to be notified should this variable change. If the DV's cur_loc
+ expanded to NULL, all components of the loc list are regarded as
+ active, so that any changes in them give us a chance to get a
+ location. Otherwise, only components of the loc that expanded to
+ non-NULL are regarded as active dependencies. */
+ loc_exp_dep *backlinks;
+ /* This holds the LOC that was expanded into cur_loc. We need only
+ mark a one-part variable as changed if the FROM loc is removed,
+ or if it has no known location and a loc is added, or if it gets
+ a change notification from any of its active dependencies. */
+ rtx from;
+ /* The depth of the cur_loc expression. */
+ int depth;
+ /* Dependencies actively used when expand FROM into cur_loc. */
+ VEC (loc_exp_dep, none) deps;
+};
+
/* Structure describing one part of variable. */
typedef struct variable_part_def
{
@@ -310,13 +352,33 @@ typedef struct variable_part_def
/* Location which was last emitted to location list. */
rtx cur_loc;
- /* The offset in the variable. */
- HOST_WIDE_INT offset;
+ union variable_aux
+ {
+ /* The offset in the variable, if !var->onepart. */
+ HOST_WIDE_INT offset;
+
+ /* Pointer to auxiliary data, if var->onepart and emit_notes. */
+ struct onepart_aux *onepaux;
+ } aux;
} variable_part;
/* Maximum number of location parts. */
#define MAX_VAR_PARTS 16
+/* Enumeration type used to discriminate various types of one-part
+ variables. */
+typedef enum onepart_enum
+{
+ /* Not a one-part variable. */
+ NOT_ONEPART = 0,
+ /* A one-part DECL that is not a DEBUG_EXPR_DECL. */
+ ONEPART_VDECL = 1,
+ /* A DEBUG_EXPR_DECL. */
+ ONEPART_DEXPR = 2,
+ /* A VALUE. */
+ ONEPART_VALUE = 3
+} onepart_enum_t;
+
/* Structure describing where the variable is located. */
typedef struct variable_def
{
@@ -330,10 +392,8 @@ typedef struct variable_def
/* Number of variable 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;
+ /* What type of DV this is, according to enum onepart_enum. */
+ ENUM_BITFIELD (onepart_enum) onepart : CHAR_BIT;
/* True if this variable_def struct is currently in the
changed_variables hash table. */
@@ -344,29 +404,48 @@ typedef struct variable_def
} *variable;
typedef const struct variable_def *const_variable;
-/* Structure for chaining backlinks from referenced VALUEs to
- DVs that are referencing them. */
-typedef struct value_chain_def
-{
- /* Next value_chain entry. */
- struct value_chain_def *next;
-
- /* The declaration of the variable, or an RTL value
- being handled like a declaration, whose var_parts[0].loc_chain
- references the VALUE owning this value_chain. */
- decl_or_value dv;
-
- /* Reference count. */
- int refcount;
-} *value_chain;
-typedef const struct value_chain_def *const_value_chain;
-
/* 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)
+#if ENABLE_CHECKING && (GCC_VERSION >= 2007)
+
+/* Access VAR's Ith part's offset, checking that it's not a one-part
+ variable. */
+#define VAR_PART_OFFSET(var, i) __extension__ \
+(*({ variable const __v = (var); \
+ gcc_checking_assert (!__v->onepart); \
+ &__v->var_part[(i)].aux.offset; }))
+
+/* Access VAR's one-part auxiliary data, checking that it is a
+ one-part variable. */
+#define VAR_LOC_1PAUX(var) __extension__ \
+(*({ variable const __v = (var); \
+ gcc_checking_assert (__v->onepart); \
+ &__v->var_part[0].aux.onepaux; }))
+
+#else
+#define VAR_PART_OFFSET(var, i) ((var)->var_part[(i)].aux.offset)
+#define VAR_LOC_1PAUX(var) ((var)->var_part[0].aux.onepaux)
+#endif
+
+/* These are accessor macros for the one-part auxiliary data. When
+ convenient for users, they're guarded by tests that the data was
+ allocated. */
+#define VAR_LOC_DEP_LST(var) (VAR_LOC_1PAUX (var) \
+ ? VAR_LOC_1PAUX (var)->backlinks \
+ : NULL)
+#define VAR_LOC_DEP_LSTP(var) (VAR_LOC_1PAUX (var) \
+ ? &VAR_LOC_1PAUX (var)->backlinks \
+ : NULL)
+#define VAR_LOC_FROM(var) (VAR_LOC_1PAUX (var)->from)
+#define VAR_LOC_DEPTH(var) (VAR_LOC_1PAUX (var)->depth)
+#define VAR_LOC_DEP_VEC(var) (VAR_LOC_1PAUX (var) \
+ ? &VAR_LOC_1PAUX (var)->deps \
+ : NULL)
+
/* Alloc pool for struct attrs_def. */
static alloc_pool attrs_pool;
@@ -382,24 +461,24 @@ static alloc_pool loc_chain_pool;
/* Alloc pool for struct shared_hash_def. */
static alloc_pool shared_hash_pool;
-/* Alloc pool for struct value_chain_def. */
-static alloc_pool value_chain_pool;
-
/* Changed variables, notes will be emitted for them. */
static htab_t changed_variables;
-/* Links from VALUEs to DVs referencing them in their current loc_chains. */
-static htab_t value_chains;
-
/* Shall notes be emitted? */
static bool emit_notes;
+/* Values whose dynamic location lists have gone empty, but whose
+ cselib location lists are still usable. Use this to hold the
+ current location, the backlinks, etc, during emit_notes. */
+static htab_t dropped_values;
+
/* Empty shared hashtable. */
static shared_hash empty_shared_hash;
/* Scratch register bitmap used by cselib_expand_value_rtx. */
static bitmap scratch_regs = NULL;
+#ifdef HAVE_window_save
typedef struct GTY(()) parm_reg {
rtx outgoing;
rtx incoming;
@@ -410,6 +489,7 @@ DEF_VEC_ALLOC_O(parm_reg_t, gc);
/* Vector of windowed parameter registers, if any. */
static VEC(parm_reg_t, gc) *windowed_parm_regs = NULL;
+#endif
/* Variable used to tell whether cselib_process_insn called our hook. */
static bool cselib_hook_called;
@@ -420,7 +500,6 @@ static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
HOST_WIDE_INT *);
static bool vt_stack_adjustments (void);
-static void note_register_arguments (rtx);
static hashval_t variable_htab_hash (const void *);
static int variable_htab_eq (const void *, const void *);
static void variable_htab_free (void *);
@@ -476,6 +555,7 @@ static void dump_vars (htab_t);
static void dump_dataflow_set (dataflow_set *);
static void dump_dataflow_sets (void);
+static void set_dv_changed (decl_or_value, bool);
static void variable_was_changed (variable, dataflow_set *);
static void **set_slot_part (dataflow_set *, rtx, void **,
decl_or_value, HOST_WIDE_INT,
@@ -672,15 +752,11 @@ vt_stack_adjustments (void)
for (insn = BB_HEAD (dest);
insn != NEXT_INSN (BB_END (dest));
insn = NEXT_INSN (insn))
- {
- if (INSN_P (insn))
- {
- insn_stack_adjust_offset_pre_post (insn, &pre, &post);
- offset += pre + post;
- }
- if (CALL_P (insn))
- note_register_arguments (insn);
- }
+ if (INSN_P (insn))
+ {
+ insn_stack_adjust_offset_pre_post (insn, &pre, &post);
+ offset += pre + post;
+ }
VTI (dest)->out.stack_adjust = offset;
@@ -1133,6 +1209,21 @@ dv_as_value (decl_or_value dv)
return (rtx)dv;
}
+/* Return the DEBUG_EXPR of a DEBUG_EXPR_DECL or the VALUE in DV. */
+static inline rtx
+dv_as_rtx (decl_or_value dv)
+{
+ tree decl;
+
+ if (dv_is_value_p (dv))
+ return dv_as_value (dv);
+
+ decl = dv_as_decl (dv);
+
+ gcc_checking_assert (TREE_CODE (decl) == DEBUG_EXPR_DECL);
+ return DECL_RTL_KNOWN_SET (decl);
+}
+
/* Return the opaque pointer in the decl_or_value. */
static inline void *
dv_as_opaque (decl_or_value dv)
@@ -1140,36 +1231,36 @@ dv_as_opaque (decl_or_value dv)
return dv;
}
-/* Return true if a decl_or_value must not have more than one variable
- part. */
-static inline bool
+/* Return nonzero if a decl_or_value must not have more than one
+ variable part. The returned value discriminates among various
+ kinds of one-part DVs ccording to enum onepart_enum. */
+static inline onepart_enum_t
dv_onepart_p (decl_or_value dv)
{
tree decl;
if (!MAY_HAVE_DEBUG_INSNS)
- return false;
+ return NOT_ONEPART;
if (dv_is_value_p (dv))
- return true;
+ return ONEPART_VALUE;
decl = dv_as_decl (dv);
- if (!decl)
- return true;
-
if (TREE_CODE (decl) == DEBUG_EXPR_DECL)
- return true;
+ return ONEPART_DEXPR;
- return (target_for_debug_bind (decl) != NULL_TREE);
+ if (target_for_debug_bind (decl) != NULL_TREE)
+ return ONEPART_VDECL;
+
+ return NOT_ONEPART;
}
-/* Return the variable pool to be used for dv, depending on whether it
- can have multiple parts or not. */
+/* Return the variable pool to be used for a dv of type ONEPART. */
static inline alloc_pool
-dv_pool (decl_or_value dv)
+onepart_pool (onepart_enum_t onepart)
{
- return dv_onepart_p (dv) ? valvar_pool : var_pool;
+ return onepart ? valvar_pool : var_pool;
}
/* Build a decl_or_value out of a decl. */
@@ -1192,6 +1283,30 @@ dv_from_value (rtx value)
return dv;
}
+/* Return a value or the decl of a debug_expr as a decl_or_value. */
+static inline decl_or_value
+dv_from_rtx (rtx x)
+{
+ decl_or_value dv;
+
+ switch (GET_CODE (x))
+ {
+ case DEBUG_EXPR:
+ dv = dv_from_decl (DEBUG_EXPR_TREE_DECL (x));
+ gcc_checking_assert (DECL_RTL_KNOWN_SET (DEBUG_EXPR_TREE_DECL (x)) == x);
+ break;
+
+ case VALUE:
+ dv = dv_from_value (x);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ return dv;
+}
+
extern void debug_dv (decl_or_value dv);
DEBUG_FUNCTION void
@@ -1254,6 +1369,8 @@ variable_htab_eq (const void *x, const void *y)
return (dv_as_opaque (v->dv) == dv_as_opaque (dv));
}
+static void loc_exp_dep_clear (variable var);
+
/* Free the element of VARIABLE_HTAB (its type is struct variable_def). */
static void
@@ -1278,29 +1395,18 @@ variable_htab_free (void *elem)
}
var->var_part[i].loc_chain = NULL;
}
- pool_free (dv_pool (var->dv), var);
-}
-
-/* The hash function for value_chains htab, computes the hash value
- from the VALUE. */
-
-static hashval_t
-value_chain_htab_hash (const void *x)
-{
- const_value_chain const v = (const_value_chain) x;
-
- return dv_htab_hash (v->dv);
-}
-
-/* Compare the VALUE X with VALUE Y. */
-
-static int
-value_chain_htab_eq (const void *x, const void *y)
-{
- const_value_chain const v = (const_value_chain) x;
- decl_or_value dv = CONST_CAST2 (decl_or_value, const void *, y);
-
- return dv_as_opaque (v->dv) == dv_as_opaque (dv);
+ if (var->onepart && VAR_LOC_1PAUX (var))
+ {
+ loc_exp_dep_clear (var);
+ if (VAR_LOC_DEP_LST (var))
+ VAR_LOC_DEP_LST (var)->pprev = NULL;
+ XDELETE (VAR_LOC_1PAUX (var));
+ /* These may be reused across functions, so reset
+ e.g. NO_LOC_P. */
+ if (var->onepart == ONEPART_DEXPR)
+ set_dv_changed (var->dv, true);
+ }
+ pool_free (onepart_pool (var->onepart), var);
}
/* Initialize the set (array) SET of attrs to empty lists. */
@@ -1569,13 +1675,12 @@ unshare_variable (dataflow_set *set, void **slot, variable var,
variable new_var;
int i;
- new_var = (variable) pool_alloc (dv_pool (var->dv));
+ new_var = (variable) pool_alloc (onepart_pool (var->onepart));
new_var->dv = var->dv;
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->onepart = var->onepart;
new_var->in_changed_variables = false;
if (! flag_var_tracking_uninit)
@@ -1586,7 +1691,18 @@ unshare_variable (dataflow_set *set, void **slot, variable var,
location_chain node;
location_chain *nextp;
- new_var->var_part[i].offset = var->var_part[i].offset;
+ if (i == 0 && var->onepart)
+ {
+ /* One-part auxiliary data is only used while emitting
+ notes, so propagate it to the new variable in the active
+ dataflow set. If we're not emitting notes, this will be
+ a no-op. */
+ gcc_checking_assert (!VAR_LOC_1PAUX (var) || emit_notes);
+ VAR_LOC_1PAUX (new_var) = VAR_LOC_1PAUX (var);
+ VAR_LOC_1PAUX (var) = NULL;
+ }
+ else
+ VAR_PART_OFFSET (new_var, i) = VAR_PART_OFFSET (var, i);
nextp = &new_var->var_part[i].loc_chain;
for (node = var->var_part[i].loc_chain; node; node = node->next)
{
@@ -1891,6 +2007,26 @@ var_mem_delete (dataflow_set *set, rtx loc, bool clobber)
delete_variable_part (set, loc, dv_from_decl (decl), offset);
}
+/* Return true if LOC should not be expanded for location expressions,
+ or used in them. */
+
+static inline bool
+unsuitable_loc (rtx loc)
+{
+ switch (GET_CODE (loc))
+ {
+ case PC:
+ case SCRATCH:
+ case CC0:
+ case ASM_INPUT:
+ case ASM_OPERANDS:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
/* Bind a value to a location it was just stored in. If MODIFIED
holds, assume the location was modified, detaching it from any
values bound to it. */
@@ -1904,10 +2040,10 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified)
if (dump_file)
{
- fprintf (dump_file, "%i: ", INSN_UID (insn));
- print_inline_rtx (dump_file, val, 0);
- fprintf (dump_file, " stored in ");
+ fprintf (dump_file, "%i: ", insn ? INSN_UID (insn) : 0);
print_inline_rtx (dump_file, loc, 0);
+ fprintf (dump_file, " evaluates to ");
+ print_inline_rtx (dump_file, val, 0);
if (v->locs)
{
struct elt_loc_list *l;
@@ -1920,6 +2056,8 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified)
fprintf (dump_file, "\n");
}
+ gcc_checking_assert (!unsuitable_loc (loc));
+
if (REG_P (loc))
{
if (modified)
@@ -1931,6 +2069,8 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified)
var_mem_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED,
dv_from_value (val), 0, NULL_RTX, INSERT);
else
+ /* ??? Ideally we wouldn't get these, and use them from the static
+ cselib loc list. */
set_variable_part (set, loc, dv_from_value (val), 0,
VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
}
@@ -1997,13 +2137,6 @@ val_reset (dataflow_set *set, decl_or_value dv)
delete_variable_part (set, dv_as_value (dv), dv_from_value (cval), 0);
clobber_variable_part (set, NULL, dv, 0, NULL);
-
- /* ??? Should we make sure there aren't other available values or
- variables whose values involve this one other than by
- equivalence? E.g., at the very least we should reset MEMs, those
- shouldn't be too hard to find cselib-looking up the value as an
- address, then locating the resulting value in our own hash
- table. */
}
/* Find the values in a given location and map the val to another
@@ -2029,6 +2162,8 @@ val_resolve (dataflow_set *set, rtx val, rtx loc, rtx insn)
val_reset (set, dv);
+ gcc_checking_assert (!unsuitable_loc (loc));
+
if (REG_P (loc))
{
attrs node, found = NULL;
@@ -2061,6 +2196,8 @@ val_resolve (dataflow_set *set, rtx val, rtx loc, rtx insn)
var_mem_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED,
dv_from_value (val), 0, NULL_RTX, INSERT);
else
+ /* ??? Ideally we wouldn't get these, and use them from the static
+ cselib loc list. */
/* ??? Merge equivalent expressions. */
set_variable_part (set, loc, dv_from_value (val), 0,
VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
@@ -2176,10 +2313,11 @@ variable_union (variable src, dataflow_set *set)
dst = (variable) *dstp;
gcc_assert (src->n_var_parts);
+ gcc_checking_assert (src->onepart == dst->onepart);
/* We can combine one-part variables very efficiently, because their
entries are in canonical order. */
- if (dv_onepart_p (src->dv))
+ if (src->onepart)
{
location_chain *nodep, dnode, snode;
@@ -2233,16 +2371,18 @@ variable_union (variable src, dataflow_set *set)
return 1;
}
+ gcc_checking_assert (!src->onepart);
+
/* Count the number of location parts, result is K. */
for (i = 0, j = 0, k = 0;
i < src->n_var_parts && j < dst->n_var_parts; k++)
{
- if (src->var_part[i].offset == dst->var_part[j].offset)
+ if (VAR_PART_OFFSET (src, i) == VAR_PART_OFFSET (dst, j))
{
i++;
j++;
}
- else if (src->var_part[i].offset < dst->var_part[j].offset)
+ else if (VAR_PART_OFFSET (src, i) < VAR_PART_OFFSET (dst, j))
i++;
else
j++;
@@ -2252,7 +2392,7 @@ variable_union (variable src, dataflow_set *set)
/* We track only variables whose size is <= MAX_VAR_PARTS bytes
thus there are at most MAX_VAR_PARTS different offsets. */
- gcc_assert (dv_onepart_p (dst->dv) ? k == 1 : k <= MAX_VAR_PARTS);
+ gcc_checking_assert (dst->onepart ? k == 1 : k <= MAX_VAR_PARTS);
if (dst->n_var_parts != k && shared_var_p (dst, set->vars))
{
@@ -2269,7 +2409,7 @@ variable_union (variable src, dataflow_set *set)
location_chain node, node2;
if (i >= 0 && j >= 0
- && src->var_part[i].offset == dst->var_part[j].offset)
+ && VAR_PART_OFFSET (src, i) == VAR_PART_OFFSET (dst, j))
{
/* Compute the "sorted" union of the chains, i.e. the locations which
are in both chains go first, they are sorted by the sum of
@@ -2317,7 +2457,7 @@ variable_union (variable src, dataflow_set *set)
/* The most common case, much simpler, no qsort is needed. */
location_chain dstnode = dst->var_part[j].loc_chain;
dst->var_part[k].loc_chain = dstnode;
- dst->var_part[k].offset = dst->var_part[j].offset;
+ VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET(dst, j);
node2 = dstnode;
for (node = src->var_part[i].loc_chain; node; node = node->next)
if (!((REG_P (dstnode->loc)
@@ -2455,20 +2595,20 @@ variable_union (variable src, dataflow_set *set)
dst->var_part[k].loc_chain = vui[0].lc;
}
- dst->var_part[k].offset = dst->var_part[j].offset;
+ VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET (dst, j);
}
i--;
j--;
}
else if ((i >= 0 && j >= 0
- && src->var_part[i].offset < dst->var_part[j].offset)
+ && VAR_PART_OFFSET (src, i) < VAR_PART_OFFSET (dst, j))
|| i < 0)
{
dst->var_part[k] = dst->var_part[j];
j--;
}
else if ((i >= 0 && j >= 0
- && src->var_part[i].offset > dst->var_part[j].offset)
+ && VAR_PART_OFFSET (src, i) > VAR_PART_OFFSET (dst, j))
|| j < 0)
{
location_chain *nextp;
@@ -2492,7 +2632,7 @@ variable_union (variable src, dataflow_set *set)
nextp = &new_lc->next;
}
- dst->var_part[k].offset = src->var_part[i].offset;
+ VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET (src, i);
i--;
}
dst->var_part[k].cur_loc = NULL;
@@ -2543,25 +2683,46 @@ dataflow_set_union (dataflow_set *dst, dataflow_set *src)
/* Whether the value is currently being expanded. */
#define VALUE_RECURSED_INTO(x) \
(RTL_FLAG_CHECK2 ("VALUE_RECURSED_INTO", (x), VALUE, DEBUG_EXPR)->used)
-/* Whether the value is in changed_variables hash table. */
+
+/* Whether no expansion was found, saving useless lookups.
+ It must only be set when VALUE_CHANGED is clear. */
+#define NO_LOC_P(x) \
+ (RTL_FLAG_CHECK2 ("NO_LOC_P", (x), VALUE, DEBUG_EXPR)->return_val)
+
+/* Whether cur_loc in the value needs to be (re)computed. */
#define VALUE_CHANGED(x) \
(RTL_FLAG_CHECK1 ("VALUE_CHANGED", (x), VALUE)->frame_related)
-/* Whether the decl is in changed_variables hash table. */
+/* Whether cur_loc in the decl needs to be (re)computed. */
#define DECL_CHANGED(x) TREE_VISITED (x)
-/* Record that DV has been added into resp. removed from changed_variables
- hashtable. */
+/* Record (if NEWV) that DV needs to have its cur_loc recomputed. For
+ user DECLs, this means they're in changed_variables. Values and
+ debug exprs may be left with this flag set if no user variable
+ requires them to be evaluated. */
static inline void
set_dv_changed (decl_or_value dv, bool newv)
{
- if (dv_is_value_p (dv))
- VALUE_CHANGED (dv_as_value (dv)) = newv;
- else
- DECL_CHANGED (dv_as_decl (dv)) = newv;
+ switch (dv_onepart_p (dv))
+ {
+ case ONEPART_VALUE:
+ if (newv)
+ NO_LOC_P (dv_as_value (dv)) = false;
+ VALUE_CHANGED (dv_as_value (dv)) = newv;
+ break;
+
+ case ONEPART_DEXPR:
+ if (newv)
+ NO_LOC_P (DECL_RTL_KNOWN_SET (dv_as_decl (dv))) = false;
+ /* Fall through... */
+
+ default:
+ DECL_CHANGED (dv_as_decl (dv)) = newv;
+ break;
+ }
}
-/* Return true if DV is present in changed_variables hash table. */
+/* Return true if DV needs to have its cur_loc recomputed. */
static inline bool
dv_changed_p (decl_or_value dv)
@@ -2585,12 +2746,11 @@ find_loc_in_1pdv (rtx loc, variable var, htab_t vars)
if (!var)
return NULL;
- gcc_checking_assert (dv_onepart_p (var->dv));
+ gcc_checking_assert (var->onepart);
if (!var->n_var_parts)
return NULL;
- gcc_checking_assert (var->var_part[0].offset == 0);
gcc_checking_assert (loc != dv_as_opaque (var->dv));
loc_code = GET_CODE (loc);
@@ -2639,6 +2799,8 @@ find_loc_in_1pdv (rtx loc, variable var, htab_t vars)
return find_loc_in_1pdv (loc, rvar, vars);
}
+ /* ??? Gotta look in cselib_val locations too. */
+
return NULL;
}
@@ -2683,7 +2845,7 @@ insert_into_intersection (location_chain *nodep, rtx loc,
*nodep = node;
}
-/* Insert in DEST the intersection the locations present in both
+/* Insert in DEST the intersection of the locations present in both
S1NODE and S2VAR, directly or indirectly. S1NODE is from a
variable in DSM->cur, whereas S2VAR is from DSM->src. dvar is in
DSM->dst. */
@@ -2700,11 +2862,10 @@ intersect_loc_chains (rtx val, location_chain *dest, struct dfset_merge *dsm,
{
location_chain s2node;
- gcc_checking_assert (dv_onepart_p (s2var->dv));
+ gcc_checking_assert (s2var->onepart);
if (s2var->n_var_parts)
{
- gcc_checking_assert (s2var->var_part[0].offset == 0);
s2node = s2var->var_part[0].loc_chain;
for (; s1node && s2node;
@@ -2750,6 +2911,8 @@ intersect_loc_chains (rtx val, location_chain *dest, struct dfset_merge *dsm,
}
}
+ /* ??? gotta look in cselib_val locations too. */
+
/* ??? if the location is equivalent to any location in src,
searched recursively
@@ -2839,6 +3002,18 @@ loc_cmp (rtx x, rtx y)
if (GET_CODE (y) == VALUE)
return 1;
+ /* Entry value is the least preferable kind of expression. */
+ if (GET_CODE (x) == ENTRY_VALUE)
+ {
+ if (GET_CODE (y) != ENTRY_VALUE)
+ return 1;
+ gcc_assert (GET_MODE (x) == GET_MODE (y));
+ return loc_cmp (XEXP (x, 0), XEXP (y, 0));
+ }
+
+ if (GET_CODE (y) == ENTRY_VALUE)
+ return -1;
+
if (GET_CODE (x) == GET_CODE (y))
/* Compare operands below. */;
else if (GET_CODE (x) < GET_CODE (y))
@@ -2933,186 +3108,23 @@ loc_cmp (rtx x, rtx y)
return 0;
}
-/* If decl or value DVP refers to VALUE from *LOC, add backlinks
- from VALUE to DVP. */
-
-static int
-add_value_chain (rtx *loc, void *dvp)
-{
- decl_or_value dv, ldv;
- value_chain vc, nvc;
- void **slot;
-
- if (GET_CODE (*loc) == VALUE)
- ldv = dv_from_value (*loc);
- else if (GET_CODE (*loc) == DEBUG_EXPR)
- ldv = dv_from_decl (DEBUG_EXPR_TREE_DECL (*loc));
- else
- return 0;
-
- if (dv_as_opaque (ldv) == dvp)
- return 0;
-
- dv = (decl_or_value) dvp;
- slot = htab_find_slot_with_hash (value_chains, ldv, dv_htab_hash (ldv),
- INSERT);
- if (!*slot)
- {
- vc = (value_chain) pool_alloc (value_chain_pool);
- vc->dv = ldv;
- vc->next = NULL;
- vc->refcount = 0;
- *slot = (void *) vc;
- }
- else
- {
- for (vc = ((value_chain) *slot)->next; vc; vc = vc->next)
- if (dv_as_opaque (vc->dv) == dv_as_opaque (dv))
- break;
- if (vc)
- {
- vc->refcount++;
- return 0;
- }
- }
- vc = (value_chain) *slot;
- nvc = (value_chain) pool_alloc (value_chain_pool);
- nvc->dv = dv;
- nvc->next = vc->next;
- nvc->refcount = 1;
- vc->next = nvc;
- return 0;
-}
-
-/* If decl or value DVP refers to VALUEs from within LOC, add backlinks
- from those VALUEs to DVP. */
-
-static void
-add_value_chains (decl_or_value dv, rtx loc)
-{
- if (GET_CODE (loc) == VALUE || GET_CODE (loc) == DEBUG_EXPR)
- {
- add_value_chain (&loc, dv_as_opaque (dv));
- return;
- }
- if (REG_P (loc))
- return;
- if (MEM_P (loc))
- loc = XEXP (loc, 0);
- for_each_rtx (&loc, add_value_chain, dv_as_opaque (dv));
-}
-
-/* If CSELIB_VAL_PTR of value DV refer to VALUEs, add backlinks from those
- VALUEs to DV. Add the same time get rid of ASM_OPERANDS from locs list,
- that is something we never can express in .debug_info and can prevent
- reverse ops from being used. */
-
-static void
-add_cselib_value_chains (decl_or_value dv)
-{
- struct elt_loc_list **l;
-
- for (l = &CSELIB_VAL_PTR (dv_as_value (dv))->locs; *l;)
- if (GET_CODE ((*l)->loc) == ASM_OPERANDS)
- *l = (*l)->next;
- else
- {
- for_each_rtx (&(*l)->loc, add_value_chain, dv_as_opaque (dv));
- l = &(*l)->next;
- }
-}
-
-/* If decl or value DVP refers to VALUE from *LOC, remove backlinks
- from VALUE to DVP. */
-
-static int
-remove_value_chain (rtx *loc, void *dvp)
-{
- decl_or_value dv, ldv;
- value_chain vc;
- void **slot;
-
- if (GET_CODE (*loc) == VALUE)
- ldv = dv_from_value (*loc);
- else if (GET_CODE (*loc) == DEBUG_EXPR)
- ldv = dv_from_decl (DEBUG_EXPR_TREE_DECL (*loc));
- else
- return 0;
-
- if (dv_as_opaque (ldv) == dvp)
- return 0;
-
- dv = (decl_or_value) dvp;
- slot = htab_find_slot_with_hash (value_chains, ldv, dv_htab_hash (ldv),
- NO_INSERT);
- for (vc = (value_chain) *slot; vc->next; vc = vc->next)
- if (dv_as_opaque (vc->next->dv) == dv_as_opaque (dv))
- {
- value_chain dvc = vc->next;
- gcc_assert (dvc->refcount > 0);
- if (--dvc->refcount == 0)
- {
- vc->next = dvc->next;
- pool_free (value_chain_pool, dvc);
- if (vc->next == NULL && vc == (value_chain) *slot)
- {
- pool_free (value_chain_pool, vc);
- htab_clear_slot (value_chains, slot);
- }
- }
- return 0;
- }
- gcc_unreachable ();
-}
-
-/* If decl or value DVP refers to VALUEs from within LOC, remove backlinks
- from those VALUEs to DVP. */
-
-static void
-remove_value_chains (decl_or_value dv, rtx loc)
-{
- if (GET_CODE (loc) == VALUE || GET_CODE (loc) == DEBUG_EXPR)
- {
- remove_value_chain (&loc, dv_as_opaque (dv));
- return;
- }
- if (REG_P (loc))
- return;
- if (MEM_P (loc))
- loc = XEXP (loc, 0);
- for_each_rtx (&loc, remove_value_chain, dv_as_opaque (dv));
-}
-
#if ENABLE_CHECKING
-/* If CSELIB_VAL_PTR of value DV refer to VALUEs, remove backlinks from those
- VALUEs to DV. */
-
-static void
-remove_cselib_value_chains (decl_or_value dv)
-{
- struct elt_loc_list *l;
-
- for (l = CSELIB_VAL_PTR (dv_as_value (dv))->locs; l; l = l->next)
- for_each_rtx (&l->loc, remove_value_chain, dv_as_opaque (dv));
-}
-
/* Check the order of entries in one-part variables. */
static int
canonicalize_loc_order_check (void **slot, void *data ATTRIBUTE_UNUSED)
{
variable var = (variable) *slot;
- 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);
+ gcc_assert (!var->in_changed_variables);
#endif
- if (!dv_onepart_p (dv))
+ if (!var->onepart)
return 1;
gcc_assert (var->n_var_parts == 1);
@@ -3186,7 +3198,7 @@ canonicalize_values_star (void **slot, void *data)
bool has_value;
bool has_marks;
- if (!dv_onepart_p (dv))
+ if (!var->onepart)
return 1;
gcc_checking_assert (var->n_var_parts == 1);
@@ -3408,7 +3420,7 @@ canonicalize_vars_star (void **slot, void *data)
variable cvar;
location_chain cnode;
- if (!dv_onepart_p (dv) || dv_is_value_p (dv))
+ if (!var->onepart || var->onepart == ONEPART_VALUE)
return 1;
gcc_assert (var->n_var_parts == 1);
@@ -3461,7 +3473,7 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
void **dstslot;
variable s2var, dvar = NULL;
decl_or_value dv = s1var->dv;
- bool onepart = dv_onepart_p (dv);
+ onepart_enum_t onepart = s1var->onepart;
rtx val;
hashval_t dvhash;
location_chain node, *nodep;
@@ -3475,8 +3487,7 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
if (!onepart)
return variable_union (s1var, dst);
- gcc_checking_assert (s1var->n_var_parts == 1
- && s1var->var_part[0].offset == 0);
+ gcc_checking_assert (s1var->n_var_parts == 1);
dvhash = dv_htab_hash (dv);
if (dv_is_value_p (dv))
@@ -3493,16 +3504,16 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
dsm->src_onepart_cnt--;
gcc_assert (s2var->var_part[0].loc_chain
- && s2var->n_var_parts == 1
- && s2var->var_part[0].offset == 0);
+ && s2var->onepart == onepart
+ && s2var->n_var_parts == 1);
dstslot = shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash);
if (dstslot)
{
dvar = (variable)*dstslot;
gcc_assert (dvar->refcount == 1
- && dvar->n_var_parts == 1
- && dvar->var_part[0].offset == 0);
+ && dvar->onepart == onepart
+ && dvar->n_var_parts == 1);
nodep = &dvar->var_part[0].loc_chain;
}
else
@@ -3529,15 +3540,18 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
{
if (node)
{
- dvar = (variable) pool_alloc (dv_pool (dv));
+ dvar = (variable) pool_alloc (onepart_pool (onepart));
dvar->dv = dv;
dvar->refcount = 1;
dvar->n_var_parts = 1;
- dvar->cur_loc_changed = false;
+ dvar->onepart = onepart;
dvar->in_changed_variables = false;
- dvar->var_part[0].offset = 0;
dvar->var_part[0].loc_chain = node;
dvar->var_part[0].cur_loc = NULL;
+ if (onepart)
+ VAR_LOC_1PAUX (dvar) = NULL;
+ else
+ VAR_PART_OFFSET (dvar, 0) = 0;
dstslot
= shared_hash_find_slot_unshare_1 (&dst->vars, dv, dvhash,
@@ -3662,15 +3676,16 @@ variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
INSERT);
if (!*slot)
{
- variable var = (variable) pool_alloc (dv_pool (dv));
+ variable var = (variable) pool_alloc (onepart_pool
+ (ONEPART_VALUE));
var->dv = dv;
var->refcount = 1;
var->n_var_parts = 1;
- var->cur_loc_changed = false;
+ var->onepart = ONEPART_VALUE;
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;
+ VAR_LOC_1PAUX (var) = NULL;
*slot = var;
}
@@ -3717,9 +3732,8 @@ variable_merge_over_src (variable s2var, struct dfset_merge *dsm)
{
dataflow_set *dst = dsm->dst;
decl_or_value dv = s2var->dv;
- bool onepart = dv_onepart_p (dv);
- if (!onepart)
+ if (!s2var->onepart)
{
void **dstp = shared_hash_find_slot (dst->vars, dv);
*dstp = s2var;
@@ -3864,7 +3878,7 @@ remove_duplicate_values (variable var)
{
location_chain node, *nodep;
- gcc_assert (dv_onepart_p (var->dv));
+ gcc_assert (var->onepart);
gcc_assert (var->n_var_parts == 1);
gcc_assert (var->refcount == 1);
@@ -3915,7 +3929,7 @@ variable_post_merge_new_vals (void **slot, void *info)
variable var = (variable)*slot;
location_chain node;
- if (!dv_onepart_p (var->dv) || !var->n_var_parts)
+ if (!var->onepart || !var->n_var_parts)
return 1;
gcc_assert (var->n_var_parts == 1);
@@ -4146,13 +4160,11 @@ find_mem_expr_in_1pdv (tree expr, rtx val, htab_t vars)
if (!var)
return NULL;
- gcc_assert (dv_onepart_p (var->dv));
+ gcc_assert (var->onepart);
if (!var->n_var_parts)
return NULL;
- gcc_assert (var->var_part[0].offset == 0);
-
VALUE_RECURSED_INTO (val) = true;
for (node = var->var_part[0].loc_chain; node; node = node->next)
@@ -4206,7 +4218,7 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
dataflow_set *set = (dataflow_set *) data;
variable var = (variable) *slot;
- if (dv_is_decl_p (var->dv) && dv_onepart_p (var->dv))
+ if (var->onepart == ONEPART_VDECL || var->onepart == ONEPART_DEXPR)
{
tree decl = dv_as_decl (var->dv);
location_chain loc, *locp;
@@ -4277,10 +4289,7 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
{
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);
}
locp = &loc->next;
continue;
@@ -4288,12 +4297,10 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
if (emit_notes)
{
- 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;
@@ -4321,10 +4328,11 @@ dataflow_set_remove_mem_locs (void **slot, void *data)
dataflow_set *set = (dataflow_set *) data;
variable var = (variable) *slot;
- if (dv_is_value_p (var->dv))
+ if (var->onepart == ONEPART_VALUE)
{
location_chain loc, *locp;
bool changed = false;
+ rtx cur_loc;
gcc_assert (var->n_var_parts == 1);
@@ -4343,6 +4351,11 @@ dataflow_set_remove_mem_locs (void **slot, void *data)
gcc_assert (var->n_var_parts == 1);
}
+ if (VAR_LOC_1PAUX (var))
+ cur_loc = VAR_LOC_FROM (var);
+ else
+ cur_loc = var->var_part[0].cur_loc;
+
for (locp = &var->var_part[0].loc_chain, loc = *locp;
loc; loc = *locp)
{
@@ -4353,17 +4366,16 @@ dataflow_set_remove_mem_locs (void **slot, void *data)
continue;
}
- if (emit_notes)
- remove_value_chains (var->dv, loc->loc);
*locp = loc->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[0].cur_loc == loc->loc)
+ if (cur_loc == loc->loc)
{
changed = true;
var->var_part[0].cur_loc = NULL;
- var->cur_loc_changed = true;
+ if (VAR_LOC_1PAUX (var))
+ VAR_LOC_FROM (var) = NULL;
}
pool_free (loc_chain_pool, loc);
}
@@ -4467,20 +4479,24 @@ variable_different_p (variable var1, variable var2)
if (var1 == var2)
return false;
+ if (var1->onepart != var2->onepart)
+ return true;
+
if (var1->n_var_parts != var2->n_var_parts)
return true;
+ if (var1->onepart && var1->n_var_parts)
+ {
+ gcc_checking_assert (dv_as_opaque (var1->dv) == dv_as_opaque (var2->dv)
+ && var1->n_var_parts == 1);
+ /* One-part values have locations in a canonical order. */
+ return onepart_variable_different_p (var1, var2);
+ }
+
for (i = 0; i < var1->n_var_parts; i++)
{
- if (var1->var_part[i].offset != var2->var_part[i].offset)
+ if (VAR_PART_OFFSET (var1, i) != VAR_PART_OFFSET (var2, i))
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))
- {
- gcc_assert (var1->n_var_parts == 1
- && dv_as_opaque (var1->dv) == dv_as_opaque (var2->dv));
- return onepart_variable_different_p (var1, var2);
- }
if (variable_part_different_p (&var1->var_part[i], &var2->var_part[i]))
return true;
if (variable_part_different_p (&var2->var_part[i], &var1->var_part[i]))
@@ -5020,9 +5036,6 @@ log_op_type (rtx x, basic_block bb, rtx insn,
/* All preserved VALUEs. */
static VEC (rtx, heap) *preserved_values;
-/* Registers used in the current function for passing parameters. */
-static HARD_REG_SET argument_reg_set;
-
/* Ensure VAL is preserved and remember it in a vector for vt_emit_notes. */
static void
@@ -5089,11 +5102,7 @@ add_uses (rtx *ploc, void *data)
if (MEM_P (vloc)
&& !REG_P (XEXP (vloc, 0))
- && !MEM_P (XEXP (vloc, 0))
- && GET_CODE (XEXP (vloc, 0)) != ENTRY_VALUE
- && (GET_CODE (XEXP (vloc, 0)) != PLUS
- || XEXP (XEXP (vloc, 0), 0) != cfa_base_rtx
- || !CONST_INT_P (XEXP (XEXP (vloc, 0), 1))))
+ && !MEM_P (XEXP (vloc, 0)))
{
rtx mloc = vloc;
enum machine_mode address_mode = get_address_mode (mloc);
@@ -5105,16 +5114,24 @@ add_uses (rtx *ploc, void *data)
{
micro_operation moa;
preserve_value (val);
- mloc = cselib_subst_to_values (XEXP (mloc, 0),
- GET_MODE (mloc));
- moa.type = MO_VAL_USE;
- moa.insn = cui->insn;
- moa.u.loc = gen_rtx_CONCAT (address_mode,
- val->val_rtx, mloc);
- if (dump_file && (dump_flags & TDF_DETAILS))
- log_op_type (moa.u.loc, cui->bb, cui->insn,
- moa.type, dump_file);
- VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &moa);
+
+ if (GET_CODE (XEXP (mloc, 0)) != ENTRY_VALUE
+ && (GET_CODE (XEXP (mloc, 0)) != PLUS
+ || XEXP (XEXP (mloc, 0), 0) != cfa_base_rtx
+ || !CONST_INT_P (XEXP (XEXP (mloc, 0), 1))))
+ {
+ mloc = cselib_subst_to_values (XEXP (mloc, 0),
+ GET_MODE (mloc));
+ moa.type = MO_VAL_USE;
+ moa.insn = cui->insn;
+ moa.u.loc = gen_rtx_CONCAT (address_mode,
+ val->val_rtx, mloc);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ log_op_type (moa.u.loc, cui->bb, cui->insn,
+ moa.type, dump_file);
+ VEC_safe_push (micro_operation, heap, VTI (bb)->mos,
+ &moa);
+ }
}
}
@@ -5122,7 +5139,7 @@ add_uses (rtx *ploc, void *data)
&& (GET_CODE (vloc) != CONST
|| for_each_rtx (&vloc, non_suitable_const, NULL)))
/* For constants don't look up any value. */;
- else if (!VAR_LOC_UNKNOWN_P (vloc)
+ else if (!VAR_LOC_UNKNOWN_P (vloc) && !unsuitable_loc (vloc)
&& (val = find_use_val (vloc, GET_MODE (oloc), cui)))
{
enum machine_mode mode2;
@@ -5168,32 +5185,36 @@ add_uses (rtx *ploc, void *data)
if (MEM_P (oloc)
&& !REG_P (XEXP (oloc, 0))
- && !MEM_P (XEXP (oloc, 0))
- && GET_CODE (XEXP (oloc, 0)) != ENTRY_VALUE
- && (GET_CODE (XEXP (oloc, 0)) != PLUS
- || XEXP (XEXP (oloc, 0), 0) != cfa_base_rtx
- || !CONST_INT_P (XEXP (XEXP (oloc, 0), 1))))
+ && !MEM_P (XEXP (oloc, 0)))
{
rtx mloc = oloc;
enum machine_mode address_mode = get_address_mode (mloc);
cselib_val *val
= cselib_lookup (XEXP (mloc, 0), address_mode, 0,
- GET_MODE (mloc));
+ GET_MODE (mloc));
if (val && !cselib_preserved_value_p (val))
{
micro_operation moa;
preserve_value (val);
- mloc = cselib_subst_to_values (XEXP (mloc, 0),
- GET_MODE (mloc));
- moa.type = MO_VAL_USE;
- moa.insn = cui->insn;
- moa.u.loc = gen_rtx_CONCAT (address_mode,
- val->val_rtx, mloc);
- if (dump_file && (dump_flags & TDF_DETAILS))
- log_op_type (moa.u.loc, cui->bb, cui->insn,
- moa.type, dump_file);
- VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &moa);
+
+ if (GET_CODE (XEXP (mloc, 0)) != ENTRY_VALUE
+ && (GET_CODE (XEXP (mloc, 0)) != PLUS
+ || XEXP (XEXP (mloc, 0), 0) != cfa_base_rtx
+ || !CONST_INT_P (XEXP (XEXP (mloc, 0), 1))))
+ {
+ mloc = cselib_subst_to_values (XEXP (mloc, 0),
+ GET_MODE (mloc));
+ moa.type = MO_VAL_USE;
+ moa.insn = cui->insn;
+ moa.u.loc = gen_rtx_CONCAT (address_mode,
+ val->val_rtx, mloc);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ log_op_type (moa.u.loc, cui->bb, cui->insn,
+ moa.type, dump_file);
+ VEC_safe_push (micro_operation, heap, VTI (bb)->mos,
+ &moa);
+ }
}
}
@@ -5255,7 +5276,18 @@ add_uses_1 (rtx *x, void *cui)
for_each_rtx (x, add_uses, cui);
}
-#define EXPR_DEPTH (PARAM_VALUE (PARAM_MAX_VARTRACK_EXPR_DEPTH))
+/* This is the value used during expansion of locations. We want it
+ to be unbounded, so that variables expanded deep in a recursion
+ nest are fully evaluated, so that their values are cached
+ correctly. We avoid recursion cycles through other means, and we
+ don't unshare RTL, so excess complexity is not a problem. */
+#define EXPR_DEPTH (INT_MAX)
+/* We use this to keep too-complex expressions from being emitted as
+ location notes, and then to debug information. Users can trade
+ compile time for ridiculously complex expressions, although they're
+ seldom useful, and they may often have to be discarded as not
+ representable anyway. */
+#define EXPR_USE_DEPTH (PARAM_VALUE (PARAM_MAX_VARTRACK_EXPR_DEPTH))
/* Attempt to reverse the EXPR operation in the debug info. Say for
reg1 = reg2 + 6 even when reg2 is no longer live we
@@ -5382,10 +5414,8 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
mo.u.loc = loc;
if (GET_CODE (expr) == SET
&& SET_DEST (expr) == loc
- && REGNO (loc) < FIRST_PSEUDO_REGISTER
- && TEST_HARD_REG_BIT (argument_reg_set, REGNO (loc))
- && find_use_val (loc, mode, cui)
- && GET_CODE (SET_SRC (expr)) != ASM_OPERANDS)
+ && !unsuitable_loc (SET_SRC (expr))
+ && find_use_val (loc, mode, cui))
{
gcc_checking_assert (type == MO_VAL_SET);
mo.u.loc = gen_rtx_SET (VOIDmode, loc, SET_SRC (expr));
@@ -5422,11 +5452,7 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
{
if (MEM_P (loc) && type == MO_VAL_SET
&& !REG_P (XEXP (loc, 0))
- && !MEM_P (XEXP (loc, 0))
- && GET_CODE (XEXP (loc, 0)) != ENTRY_VALUE
- && (GET_CODE (XEXP (loc, 0)) != PLUS
- || XEXP (XEXP (loc, 0), 0) != cfa_base_rtx
- || !CONST_INT_P (XEXP (XEXP (loc, 0), 1))))
+ && !MEM_P (XEXP (loc, 0)))
{
rtx mloc = loc;
enum machine_mode address_mode = get_address_mode (mloc);
@@ -5437,15 +5463,23 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
if (val && !cselib_preserved_value_p (val))
{
preserve_value (val);
- mo.type = MO_VAL_USE;
- mloc = cselib_subst_to_values (XEXP (mloc, 0),
- GET_MODE (mloc));
- mo.u.loc = gen_rtx_CONCAT (address_mode, val->val_rtx, mloc);
- mo.insn = cui->insn;
- if (dump_file && (dump_flags & TDF_DETAILS))
- log_op_type (mo.u.loc, cui->bb, cui->insn,
- mo.type, dump_file);
- VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo);
+
+ if (GET_CODE (XEXP (mloc, 0)) != ENTRY_VALUE
+ && (GET_CODE (XEXP (mloc, 0)) != PLUS
+ || XEXP (XEXP (mloc, 0), 0) != cfa_base_rtx
+ || !CONST_INT_P (XEXP (XEXP (mloc, 0), 1))))
+ {
+ mloc = cselib_subst_to_values (XEXP (mloc, 0),
+ GET_MODE (mloc));
+ mo.type = MO_VAL_USE;
+ mo.insn = cui->insn;
+ mo.u.loc = gen_rtx_CONCAT (address_mode,
+ val->val_rtx, mloc);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ log_op_type (mo.u.loc, cui->bb, cui->insn,
+ mo.type, dump_file);
+ VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo);
+ }
}
}
@@ -5893,7 +5927,7 @@ prepare_call_arguments (basic_block bb, rtx insn)
tree dtemp = VEC_index (tree, *debug_args, ix + 1);
enum machine_mode mode = DECL_MODE (dtemp);
item = gen_rtx_DEBUG_PARAMETER_REF (mode, param);
- item = gen_rtx_CONCAT (mode, item, DECL_RTL (dtemp));
+ item = gen_rtx_CONCAT (mode, item, DECL_RTL_KNOWN_SET (dtemp));
call_arguments = gen_rtx_EXPR_LIST (VOIDmode, item,
call_arguments);
}
@@ -6705,7 +6739,7 @@ dump_var (variable var)
for (i = 0; i < var->n_var_parts; i++)
{
fprintf (dump_file, " offset %ld\n",
- (long) var->var_part[i].offset);
+ (long)(var->onepart ? 0 : VAR_PART_OFFSET (var, i)));
for (node = var->var_part[i].loc_chain; node; node = node->next)
{
fprintf (dump_file, " ");
@@ -6766,6 +6800,73 @@ dump_dataflow_sets (void)
}
}
+/* Return the variable for DV in dropped_values, inserting one if
+ requested with INSERT. */
+
+static inline variable
+variable_from_dropped (decl_or_value dv, enum insert_option insert)
+{
+ void **slot;
+ variable empty_var;
+ onepart_enum_t onepart;
+
+ slot = htab_find_slot_with_hash (dropped_values, dv, dv_htab_hash (dv),
+ insert);
+
+ if (!slot)
+ return NULL;
+
+ if (*slot)
+ return (variable) *slot;
+
+ gcc_checking_assert (insert == INSERT);
+
+ onepart = dv_onepart_p (dv);
+
+ gcc_checking_assert (onepart == ONEPART_VALUE || onepart == ONEPART_DEXPR);
+
+ empty_var = (variable) pool_alloc (onepart_pool (onepart));
+ empty_var->dv = dv;
+ empty_var->refcount = 1;
+ empty_var->n_var_parts = 0;
+ empty_var->onepart = onepart;
+ empty_var->in_changed_variables = false;
+ empty_var->var_part[0].loc_chain = NULL;
+ empty_var->var_part[0].cur_loc = NULL;
+ VAR_LOC_1PAUX (empty_var) = NULL;
+ set_dv_changed (dv, true);
+
+ *slot = empty_var;
+
+ return empty_var;
+}
+
+/* Recover the one-part aux from dropped_values. */
+
+static struct onepart_aux *
+recover_dropped_1paux (variable var)
+{
+ variable dvar;
+
+ gcc_checking_assert (var->onepart);
+
+ if (VAR_LOC_1PAUX (var))
+ return VAR_LOC_1PAUX (var);
+
+ if (var->onepart == ONEPART_VDECL)
+ return NULL;
+
+ dvar = variable_from_dropped (var->dv, NO_INSERT);
+
+ if (!dvar)
+ return NULL;
+
+ VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (dvar);
+ VAR_LOC_1PAUX (dvar) = NULL;
+
+ return VAR_LOC_1PAUX (var);
+}
+
/* Add variable VAR to the hash table of changed variables and
if it has no locations delete it from SET's hash table. */
@@ -6777,7 +6878,6 @@ variable_was_changed (variable var, dataflow_set *set)
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);
@@ -6791,30 +6891,76 @@ variable_was_changed (variable var, dataflow_set *set)
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;
+ if (var != old_var && var->onepart)
+ {
+ /* Restore the auxiliary info from an empty variable
+ previously created for changed_variables, so it is
+ not lost. */
+ gcc_checking_assert (!VAR_LOC_1PAUX (var));
+ VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (old_var);
+ VAR_LOC_1PAUX (old_var) = NULL;
+ }
variable_htab_free (*slot);
}
+
if (set && var->n_var_parts == 0)
{
- variable empty_var;
+ onepart_enum_t onepart = var->onepart;
+ variable empty_var = NULL;
+ void **dslot = NULL;
- empty_var = (variable) pool_alloc (dv_pool (var->dv));
- empty_var->dv = var->dv;
- empty_var->refcount = 1;
- empty_var->n_var_parts = 0;
- empty_var->cur_loc_changed = true;
+ if (onepart == ONEPART_VALUE || onepart == ONEPART_DEXPR)
+ {
+ dslot = htab_find_slot_with_hash (dropped_values, var->dv,
+ dv_htab_hash (var->dv),
+ INSERT);
+ empty_var = (variable) *dslot;
+
+ if (empty_var)
+ {
+ gcc_checking_assert (!empty_var->in_changed_variables);
+ if (!VAR_LOC_1PAUX (var))
+ {
+ VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (empty_var);
+ VAR_LOC_1PAUX (empty_var) = NULL;
+ }
+ else
+ gcc_checking_assert (!VAR_LOC_1PAUX (empty_var));
+ }
+ }
+
+ if (!empty_var)
+ {
+ empty_var = (variable) pool_alloc (onepart_pool (onepart));
+ empty_var->dv = var->dv;
+ empty_var->refcount = 1;
+ empty_var->n_var_parts = 0;
+ empty_var->onepart = onepart;
+ if (dslot)
+ {
+ empty_var->refcount++;
+ *dslot = empty_var;
+ }
+ }
+ else
+ empty_var->refcount++;
empty_var->in_changed_variables = true;
*slot = empty_var;
+ if (onepart)
+ {
+ empty_var->var_part[0].loc_chain = NULL;
+ empty_var->var_part[0].cur_loc = NULL;
+ VAR_LOC_1PAUX (empty_var) = VAR_LOC_1PAUX (var);
+ VAR_LOC_1PAUX (var) = NULL;
+ }
goto drop_var;
}
else
{
+ if (var->onepart && !VAR_LOC_1PAUX (var))
+ recover_dropped_1paux (var);
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;
}
}
@@ -6849,13 +6995,24 @@ find_variable_location_part (variable var, HOST_WIDE_INT offset,
{
int pos, low, high;
+ if (var->onepart)
+ {
+ if (offset != 0)
+ return -1;
+
+ if (insertion_point)
+ *insertion_point = 0;
+
+ return var->n_var_parts - 1;
+ }
+
/* Find the location part. */
low = 0;
high = var->n_var_parts;
while (low != high)
{
pos = (low + high) / 2;
- if (var->var_part[pos].offset < offset)
+ if (VAR_PART_OFFSET (var, pos) < offset)
low = pos + 1;
else
high = pos;
@@ -6865,7 +7022,7 @@ find_variable_location_part (variable var, HOST_WIDE_INT offset,
if (insertion_point)
*insertion_point = pos;
- if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
+ if (pos < var->n_var_parts && VAR_PART_OFFSET (var, pos) == offset)
return pos;
return -1;
@@ -6880,26 +7037,34 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
location_chain node, next;
location_chain *nextp;
variable var;
- bool onepart = dv_onepart_p (dv);
-
- gcc_assert (offset == 0 || !onepart);
- gcc_assert (loc != dv_as_opaque (dv));
+ onepart_enum_t onepart;
var = (variable) *slot;
+ if (var)
+ onepart = var->onepart;
+ else
+ onepart = dv_onepart_p (dv);
+
+ gcc_checking_assert (offset == 0 || !onepart);
+ gcc_checking_assert (loc != dv_as_opaque (dv));
+
if (! flag_var_tracking_uninit)
initialized = VAR_INIT_STATUS_INITIALIZED;
if (!var)
{
/* Create new variable information. */
- var = (variable) pool_alloc (dv_pool (dv));
+ var = (variable) pool_alloc (onepart_pool (onepart));
var->dv = dv;
var->refcount = 1;
var->n_var_parts = 1;
- var->cur_loc_changed = false;
+ var->onepart = onepart;
var->in_changed_variables = false;
- var->var_part[0].offset = offset;
+ if (var->onepart)
+ VAR_LOC_1PAUX (var) = NULL;
+ else
+ VAR_PART_OFFSET (var, 0) = offset;
var->var_part[0].loc_chain = NULL;
var->var_part[0].cur_loc = NULL;
*slot = var;
@@ -7054,7 +7219,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
/* We track only variables whose size is <= MAX_VAR_PARTS bytes
thus there are at most MAX_VAR_PARTS different offsets. */
gcc_assert (var->n_var_parts < MAX_VAR_PARTS
- && (!var->n_var_parts || !dv_onepart_p (var->dv)));
+ && (!var->n_var_parts || !onepart));
/* We have to move the elements of array starting at index
inspos to the next position. */
@@ -7062,7 +7227,8 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
var->var_part[pos] = var->var_part[pos - 1];
var->n_var_parts++;
- var->var_part[pos].offset = offset;
+ gcc_checking_assert (!onepart);
+ VAR_PART_OFFSET (var, pos) = offset;
var->var_part[pos].loc_chain = NULL;
var->var_part[pos].cur_loc = NULL;
}
@@ -7083,10 +7249,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
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;
- }
+ var->var_part[pos].cur_loc = NULL;
pool_free (loc_chain_pool, node);
*nextp = next;
break;
@@ -7106,9 +7269,6 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
node->next = *nextp;
*nextp = node;
- if (onepart && emit_notes)
- add_value_chains (var->dv, loc);
-
/* If no location was emitted do so. */
if (var->var_part[pos].cur_loc == NULL)
variable_was_changed (var, set);
@@ -7238,6 +7398,7 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
location_chain node, next;
location_chain *nextp;
bool changed;
+ rtx cur_loc;
if (shared_var_p (var, set->vars))
{
@@ -7258,6 +7419,11 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
}
}
+ if (pos == 0 && var->onepart && VAR_LOC_1PAUX (var))
+ cur_loc = VAR_LOC_FROM (var);
+ else
+ cur_loc = var->var_part[pos].cur_loc;
+
/* Delete the location part. */
changed = false;
nextp = &var->var_part[pos].loc_chain;
@@ -7268,16 +7434,15 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
&& REGNO (node->loc) == REGNO (loc))
|| rtx_equal_p (node->loc, loc))
{
- 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)
+ if (cur_loc == node->loc)
{
changed = true;
var->var_part[pos].cur_loc = NULL;
- var->cur_loc_changed = true;
+ if (pos == 0 && var->onepart && VAR_LOC_1PAUX (var))
+ VAR_LOC_FROM (var) = NULL;
}
pool_free (loc_chain_pool, node);
*nextp = next;
@@ -7291,8 +7456,6 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
{
changed = true;
var->n_var_parts--;
- if (emit_notes)
- var->cur_loc_changed = true;
while (pos < var->n_var_parts)
{
var->var_part[pos] = var->var_part[pos + 1];
@@ -7321,6 +7484,12 @@ delete_variable_part (dataflow_set *set, rtx loc, decl_or_value dv,
delete_slot_part (set, loc, slot, offset);
}
+DEF_VEC_P (variable);
+DEF_VEC_ALLOC_P (variable, heap);
+
+DEF_VEC_ALLOC_P_STACK (rtx);
+#define VEC_rtx_stack_alloc(alloc) VEC_stack_alloc (rtx, alloc)
+
/* Structure for passing some other parameters to function
vt_expand_loc_callback. */
struct expand_loc_callback_data
@@ -7328,56 +7497,368 @@ 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;
-
- /* True if cur_loc should be ignored and any possible location
- returned. */
- bool ignore_cur_loc;
+ /* Stack of values and debug_exprs under expansion, and their
+ children. */
+ VEC (rtx, stack) *expanding;
+
+ /* Stack of values and debug_exprs whose expansion hit recursion
+ cycles. They will have VALUE_RECURSED_INTO marked when added to
+ this list. This flag will be cleared if any of its dependencies
+ resolves to a valid location. So, if the flag remains set at the
+ end of the search, we know no valid location for this one can
+ possibly exist. */
+ VEC (rtx, stack) *pending;
+
+ /* The maximum depth among the sub-expressions under expansion.
+ Zero indicates no expansion so far. */
+ int depth;
};
+/* Allocate the one-part auxiliary data structure for VAR, with enough
+ room for COUNT dependencies. */
+
+static void
+loc_exp_dep_alloc (variable var, int count)
+{
+ size_t allocsize;
+
+ gcc_checking_assert (var->onepart);
+
+ /* We can be called with COUNT == 0 to allocate the data structure
+ without any dependencies, e.g. for the backlinks only. However,
+ if we are specifying a COUNT, then the dependency list must have
+ been emptied before. It would be possible to adjust pointers or
+ force it empty here, but this is better done at an earlier point
+ in the algorithm, so we instead leave an assertion to catch
+ errors. */
+ gcc_checking_assert (!count
+ || VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var)));
+
+ if (VAR_LOC_1PAUX (var)
+ && VEC_space (loc_exp_dep, VAR_LOC_DEP_VEC (var), count))
+ return;
+
+ allocsize = offsetof (struct onepart_aux, deps)
+ + VEC_embedded_size (loc_exp_dep, count);
+
+ if (VAR_LOC_1PAUX (var))
+ {
+ VAR_LOC_1PAUX (var) = XRESIZEVAR (struct onepart_aux,
+ VAR_LOC_1PAUX (var), allocsize);
+ /* If the reallocation moves the onepaux structure, the
+ back-pointer to BACKLINKS in the first list member will still
+ point to its old location. Adjust it. */
+ if (VAR_LOC_DEP_LST (var))
+ VAR_LOC_DEP_LST (var)->pprev = VAR_LOC_DEP_LSTP (var);
+ }
+ else
+ {
+ VAR_LOC_1PAUX (var) = XNEWVAR (struct onepart_aux, allocsize);
+ *VAR_LOC_DEP_LSTP (var) = NULL;
+ VAR_LOC_FROM (var) = NULL;
+ VAR_LOC_DEPTH (var) = 0;
+ }
+ VEC_embedded_init (loc_exp_dep, VAR_LOC_DEP_VEC (var), count);
+}
+
+/* Remove all entries from the vector of active dependencies of VAR,
+ removing them from the back-links lists too. */
+
+static void
+loc_exp_dep_clear (variable var)
+{
+ while (!VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var)))
+ {
+ loc_exp_dep *led = VEC_last (loc_exp_dep, VAR_LOC_DEP_VEC (var));
+ if (led->next)
+ led->next->pprev = led->pprev;
+ if (led->pprev)
+ *led->pprev = led->next;
+ VEC_pop (loc_exp_dep, VAR_LOC_DEP_VEC (var));
+ }
+}
+
+/* Insert an active dependency from VAR on X to the vector of
+ dependencies, and add the corresponding back-link to X's list of
+ back-links in VARS. */
+
+static void
+loc_exp_insert_dep (variable var, rtx x, htab_t vars)
+{
+ decl_or_value dv;
+ variable xvar;
+ loc_exp_dep *led;
+
+ dv = dv_from_rtx (x);
+
+ /* ??? Build a vector of variables parallel to EXPANDING, to avoid
+ an additional look up? */
+ xvar = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
+
+ if (!xvar)
+ {
+ xvar = variable_from_dropped (dv, NO_INSERT);
+ gcc_checking_assert (xvar);
+ }
+
+ /* No point in adding the same backlink more than once. This may
+ arise if say the same value appears in two complex expressions in
+ the same loc_list, or even more than once in a single
+ expression. */
+ if (VAR_LOC_DEP_LST (xvar) && VAR_LOC_DEP_LST (xvar)->dv == var->dv)
+ return;
+
+ VEC_quick_push (loc_exp_dep, VAR_LOC_DEP_VEC (var), NULL);
+ led = VEC_last (loc_exp_dep, VAR_LOC_DEP_VEC (var));
+ led->dv = var->dv;
+ led->value = x;
+
+ loc_exp_dep_alloc (xvar, 0);
+ led->pprev = VAR_LOC_DEP_LSTP (xvar);
+ led->next = *led->pprev;
+ if (led->next)
+ led->next->pprev = &led->next;
+ *led->pprev = led;
+}
+
+/* Create active dependencies of VAR on COUNT values starting at
+ VALUE, and corresponding back-links to the entries in VARS. Return
+ true if we found any pending-recursion results. */
+
+static bool
+loc_exp_dep_set (variable var, rtx result, rtx *value, int count, htab_t vars)
+{
+ bool pending_recursion = false;
+
+ gcc_checking_assert (VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var)));
+
+ /* Set up all dependencies from last_child (as set up at the end of
+ the loop above) to the end. */
+ loc_exp_dep_alloc (var, count);
+
+ while (count--)
+ {
+ rtx x = *value++;
+
+ if (!pending_recursion)
+ pending_recursion = !result && VALUE_RECURSED_INTO (x);
+
+ loc_exp_insert_dep (var, x, vars);
+ }
+
+ return pending_recursion;
+}
+
+/* Notify the back-links of IVAR that are pending recursion that we
+ have found a non-NIL value for it, so they are cleared for another
+ attempt to compute a current location. */
+
+static void
+notify_dependents_of_resolved_value (variable ivar, htab_t vars)
+{
+ loc_exp_dep *led, *next;
+
+ for (led = VAR_LOC_DEP_LST (ivar); led; led = next)
+ {
+ decl_or_value dv = led->dv;
+ variable var;
+
+ next = led->next;
+
+ if (dv_is_value_p (dv))
+ {
+ rtx value = dv_as_value (dv);
+
+ /* If we have already resolved it, leave it alone. */
+ if (!VALUE_RECURSED_INTO (value))
+ continue;
+
+ /* Check that VALUE_RECURSED_INTO, true from the test above,
+ implies NO_LOC_P. */
+ gcc_checking_assert (NO_LOC_P (value));
+
+ /* We won't notify variables that are being expanded,
+ because their dependency list is cleared before
+ recursing. */
+ VALUE_RECURSED_INTO (value) = false;
+
+ gcc_checking_assert (dv_changed_p (dv));
+ }
+ else if (!dv_changed_p (dv))
+ continue;
+
+ var = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
+
+ if (!var)
+ var = variable_from_dropped (dv, NO_INSERT);
+
+ if (var)
+ notify_dependents_of_resolved_value (var, vars);
+
+ if (next)
+ next->pprev = led->pprev;
+ if (led->pprev)
+ *led->pprev = next;
+ led->next = NULL;
+ led->pprev = NULL;
+ }
+}
+
+static rtx vt_expand_loc_callback (rtx x, bitmap regs,
+ int max_depth, void *data);
+
+/* Return the combined depth, when one sub-expression evaluated to
+ BEST_DEPTH and the previous known depth was SAVED_DEPTH. */
+
+static inline int
+update_depth (int saved_depth, int best_depth)
+{
+ /* If we didn't find anything, stick with what we had. */
+ if (!best_depth)
+ return saved_depth;
+
+ /* If we found hadn't found anything, use the depth of the current
+ expression. Do NOT add one extra level, we want to compute the
+ maximum depth among sub-expressions. We'll increment it later,
+ if appropriate. */
+ if (!saved_depth)
+ return best_depth;
+
+ if (saved_depth < best_depth)
+ return best_depth;
+ else
+ return saved_depth;
+}
+
+/* Expand VAR to a location RTX, updating its cur_loc. Use REGS and
+ DATA for cselib expand callback. If PENDRECP is given, indicate in
+ it whether any sub-expression couldn't be fully evaluated because
+ it is pending recursion resolution. */
+
+static inline rtx
+vt_expand_var_loc_chain (variable var, bitmap regs, void *data, bool *pendrecp)
+{
+ struct expand_loc_callback_data *elcd
+ = (struct expand_loc_callback_data *) data;
+ location_chain loc, next;
+ rtx result = NULL;
+ int first_child, result_first_child, last_child;
+ bool pending_recursion;
+ rtx loc_from = NULL;
+ struct elt_loc_list *cloc = NULL;
+ int depth, saved_depth = elcd->depth;
+
+ /* Clear all backlinks pointing at this, so that we're not notified
+ while we're active. */
+ loc_exp_dep_clear (var);
+
+ if (var->onepart == ONEPART_VALUE)
+ {
+ cselib_val *val = CSELIB_VAL_PTR (dv_as_value (var->dv));
+
+ gcc_checking_assert (cselib_preserved_value_p (val));
+
+ cloc = val->locs;
+ }
+
+ first_child = result_first_child = last_child
+ = VEC_length (rtx, elcd->expanding);
+
+ /* Attempt to expand each available location in turn. */
+ for (next = loc = var->n_var_parts ? var->var_part[0].loc_chain : NULL;
+ loc || cloc; loc = next)
+ {
+ result_first_child = last_child;
+
+ if (!loc || (GET_CODE (loc->loc) == ENTRY_VALUE && cloc))
+ {
+ loc_from = cloc->loc;
+ next = loc;
+ cloc = cloc->next;
+ if (unsuitable_loc (loc_from))
+ continue;
+ }
+ else
+ {
+ loc_from = loc->loc;
+ next = loc->next;
+ }
+
+ gcc_checking_assert (!unsuitable_loc (loc_from));
+
+ elcd->depth = 0;
+ result = cselib_expand_value_rtx_cb (loc_from, regs, EXPR_DEPTH,
+ vt_expand_loc_callback, data);
+ last_child = VEC_length (rtx, elcd->expanding);
+
+ if (result)
+ {
+ depth = elcd->depth;
+
+ gcc_checking_assert (depth || result_first_child == last_child);
+
+ if (last_child - result_first_child != 1)
+ depth++;
+
+ if (depth <= EXPR_USE_DEPTH)
+ break;
+
+ result = NULL;
+ }
+
+ /* Set it up in case we leave the loop. */
+ depth = 0;
+ loc_from = NULL;
+ result_first_child = first_child;
+ }
+
+ /* Register all encountered dependencies as active. */
+ pending_recursion = loc_exp_dep_set
+ (var, result, VEC_address (rtx, elcd->expanding) + result_first_child,
+ last_child - result_first_child, elcd->vars);
+
+ VEC_truncate (rtx, elcd->expanding, first_child);
+
+ /* Record where the expansion came from. */
+ gcc_checking_assert (!result || !pending_recursion);
+ VAR_LOC_FROM (var) = loc_from;
+ VAR_LOC_DEPTH (var) = depth;
+
+ elcd->depth = update_depth (saved_depth, depth);
+
+ /* Indicate whether any of the dependencies are pending recursion
+ resolution. */
+ if (pendrecp)
+ *pendrecp = pending_recursion;
+
+ if (!pendrecp || !pending_recursion)
+ var->var_part[0].cur_loc = result;
+
+ return result;
+}
+
/* 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)
+vt_expand_loc_callback (rtx x, bitmap regs,
+ int max_depth ATTRIBUTE_UNUSED,
+ void *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;
- rtx cur_loc;
decl_or_value dv;
variable var;
- location_chain loc;
- rtx result, subreg, xret;
+ rtx result, subreg;
+ bool pending_recursion = false;
+ bool from_empty = false;
switch (GET_CODE (x))
{
case SUBREG:
- if (dummy)
- {
- if (cselib_dummy_expand_value_rtx_cb (SUBREG_REG (x), regs,
- max_depth - 1,
- vt_expand_loc_callback, data))
- return pc_rtx;
- else
- return NULL;
- }
-
subreg = cselib_expand_value_rtx_cb (SUBREG_REG (x), regs,
- max_depth - 1,
+ EXPR_DEPTH,
vt_expand_loc_callback, data);
if (!subreg)
@@ -7395,148 +7876,172 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
return result;
case DEBUG_EXPR:
- dv = dv_from_decl (DEBUG_EXPR_TREE_DECL (x));
- xret = NULL;
- break;
-
case VALUE:
- dv = dv_from_value (x);
- xret = x;
+ dv = dv_from_rtx (x);
break;
default:
return x;
}
- if (VALUE_RECURSED_INTO (x))
+ VEC_safe_push (rtx, stack, elcd->expanding, x);
+
+ /* Check that VALUE_RECURSED_INTO implies NO_LOC_P. */
+ gcc_checking_assert (!VALUE_RECURSED_INTO (x) || NO_LOC_P (x));
+
+ if (NO_LOC_P (x))
return NULL;
var = (variable) htab_find_with_hash (elcd->vars, dv, dv_htab_hash (dv));
if (!var)
{
- if (dummy && dv_changed_p (dv))
- elcd->cur_loc_changed = true;
- return xret;
+ from_empty = true;
+ var = variable_from_dropped (dv, INSERT);
}
- if (var->n_var_parts == 0)
+ gcc_checking_assert (var);
+
+ if (!dv_changed_p (dv))
{
- if (dummy)
- elcd->cur_loc_changed = true;
- return xret;
- }
+ gcc_checking_assert (!NO_LOC_P (x));
+ gcc_checking_assert (var->var_part[0].cur_loc);
+ gcc_checking_assert (VAR_LOC_1PAUX (var));
+ gcc_checking_assert (VAR_LOC_1PAUX (var)->depth);
- gcc_assert (var->n_var_parts == 1);
+ elcd->depth = update_depth (elcd->depth, VAR_LOC_1PAUX (var)->depth);
+
+ return var->var_part[0].cur_loc;
+ }
VALUE_RECURSED_INTO (x) = true;
- result = NULL;
+ /* This is tentative, but it makes some tests simpler. */
+ NO_LOC_P (x) = true;
+
+ gcc_checking_assert (var->n_var_parts == 1 || from_empty);
+
+ result = vt_expand_var_loc_chain (var, regs, data, &pending_recursion);
- if (var->var_part[0].cur_loc && !elcd->ignore_cur_loc)
+ if (pending_recursion)
{
- 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)
- set_dv_changed (dv, false);
- cur_loc = var->var_part[0].cur_loc;
+ gcc_checking_assert (!result);
+ VEC_safe_push (rtx, stack, elcd->pending, x);
}
else
- cur_loc = NULL_RTX;
- if (!result && (dv_changed_p (dv) || elcd->ignore_cur_loc))
- {
- if (!elcd->ignore_cur_loc)
- set_dv_changed (dv, false);
- for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
- if (loc->loc == 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;
- if (!elcd->ignore_cur_loc)
- 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;
+ NO_LOC_P (x) = !result;
+ VALUE_RECURSED_INTO (x) = false;
+ set_dv_changed (dv, false);
+
+ if (result)
+ notify_dependents_of_resolved_value (var, elcd->vars);
}
- VALUE_RECURSED_INTO (x) = false;
- if (result)
- return result;
- else
- return xret;
+ return result;
}
-/* Expand VALUEs in LOC, using VARS as well as cselib's equivalence
- tables. */
+/* While expanding variables, we may encounter recursion cycles
+ because of mutual (possibly indirect) dependencies between two
+ particular variables (or values), say A and B. If we're trying to
+ expand A when we get to B, which in turn attempts to expand A, if
+ we can't find any other expansion for B, we'll add B to this
+ pending-recursion stack, and tentatively return NULL for its
+ location. This tentative value will be used for any other
+ occurrences of B, unless A gets some other location, in which case
+ it will notify B that it is worth another try at computing a
+ location for it, and it will use the location computed for A then.
+ At the end of the expansion, the tentative NULL locations become
+ final for all members of PENDING that didn't get a notification.
+ This function performs this finalization of NULL locations. */
+
+static void
+resolve_expansions_pending_recursion (VEC (rtx, stack) *pending)
+{
+ while (!VEC_empty (rtx, pending))
+ {
+ rtx x = VEC_pop (rtx, pending);
+ decl_or_value dv;
+
+ if (!VALUE_RECURSED_INTO (x))
+ continue;
+
+ gcc_checking_assert (NO_LOC_P (x));
+ VALUE_RECURSED_INTO (x) = false;
+ dv = dv_from_rtx (x);
+ gcc_checking_assert (dv_changed_p (dv));
+ set_dv_changed (dv, false);
+ }
+}
+
+/* Initialize expand_loc_callback_data D with variable hash table V.
+ It must be a macro because of alloca (VEC stack). */
+#define INIT_ELCD(d, v) \
+ do \
+ { \
+ (d).vars = (v); \
+ (d).expanding = VEC_alloc (rtx, stack, 4); \
+ (d).pending = VEC_alloc (rtx, stack, 4); \
+ (d).depth = 0; \
+ } \
+ while (0)
+/* Finalize expand_loc_callback_data D, resolved to location L. */
+#define FINI_ELCD(d, l) \
+ do \
+ { \
+ resolve_expansions_pending_recursion ((d).pending); \
+ VEC_free (rtx, stack, (d).pending); \
+ VEC_free (rtx, stack, (d).expanding); \
+ \
+ if ((l) && MEM_P (l)) \
+ (l) = targetm.delegitimize_address (l); \
+ } \
+ while (0)
+
+/* Expand VALUEs and DEBUG_EXPRs in LOC to a location, using the
+ equivalences in VARS, updating their CUR_LOCs in the process. */
static rtx
-vt_expand_loc (rtx loc, htab_t vars, bool ignore_cur_loc)
+vt_expand_loc (rtx loc, htab_t vars)
{
struct expand_loc_callback_data data;
+ rtx result;
if (!MAY_HAVE_DEBUG_INSNS)
return loc;
- data.vars = vars;
- data.dummy = false;
- data.cur_loc_changed = false;
- data.ignore_cur_loc = ignore_cur_loc;
- loc = cselib_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH,
- vt_expand_loc_callback, &data);
+ INIT_ELCD (data, vars);
- if (loc && MEM_P (loc))
- loc = targetm.delegitimize_address (loc);
- return loc;
+ result = cselib_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH,
+ vt_expand_loc_callback, &data);
+
+ FINI_ELCD (data, result);
+
+ return result;
}
-/* Like vt_expand_loc, but only return true/false (whether vt_expand_loc
- would succeed or not, without actually allocating new rtxes. */
+/* Expand the one-part VARiable to a location, using the equivalences
+ in VARS, updating their CUR_LOCs in the process. */
-static bool
-vt_expand_loc_dummy (rtx loc, htab_t vars, bool *pcur_loc_changed)
+static rtx
+vt_expand_1pvar (variable var, htab_t vars)
{
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;
- data.ignore_cur_loc = false;
- ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH,
- vt_expand_loc_callback, &data);
- *pcur_loc_changed = data.cur_loc_changed;
- return ret;
+ rtx loc;
+
+ gcc_checking_assert (var->onepart && var->n_var_parts == 1);
+
+ if (!dv_changed_p (var->dv))
+ return var->var_part[0].cur_loc;
+
+ INIT_ELCD (data, vars);
+
+ loc = vt_expand_var_loc_chain (var, scratch_regs, &data, NULL);
+
+ gcc_checking_assert (VEC_empty (rtx, data.expanding));
+
+ FINI_ELCD (data, loc);
+
+ return loc;
}
/* Emit the NOTE_INSN_VAR_LOCATION for variable *VARP. DATA contains
@@ -7561,49 +8066,57 @@ emit_note_insn_var_location (void **varp, void *data)
tree decl;
location_chain lc;
- if (dv_is_value_p (var->dv))
- goto value_or_debug_decl;
+ gcc_checking_assert (var->onepart == NOT_ONEPART
+ || var->onepart == ONEPART_VDECL);
decl = dv_as_decl (var->dv);
- if (TREE_CODE (decl) == DEBUG_EXPR_DECL)
- goto value_or_debug_decl;
-
complete = true;
last_limit = 0;
n_var_parts = 0;
- if (!MAY_HAVE_DEBUG_INSNS)
- {
- 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;
- }
- if (!var->cur_loc_changed)
- goto clear;
+ if (!var->onepart)
+ 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;
for (i = 0; i < var->n_var_parts; i++)
{
enum machine_mode mode, wider_mode;
rtx loc2;
+ HOST_WIDE_INT offset;
- if (last_limit < var->var_part[i].offset)
+ if (i == 0 && var->onepart)
{
- complete = false;
- break;
+ gcc_checking_assert (var->n_var_parts == 1);
+ offset = 0;
+ initialized = VAR_INIT_STATUS_INITIALIZED;
+ loc2 = vt_expand_1pvar (var, vars);
}
- else if (last_limit > var->var_part[i].offset)
- continue;
- offsets[n_var_parts] = var->var_part[i].offset;
- if (!var->var_part[i].cur_loc)
+ else
{
- complete = false;
- continue;
+ if (last_limit < VAR_PART_OFFSET (var, i))
+ {
+ complete = false;
+ break;
+ }
+ else if (last_limit > VAR_PART_OFFSET (var, i))
+ continue;
+ offset = VAR_PART_OFFSET (var, i);
+ if (!var->var_part[i].cur_loc)
+ {
+ complete = false;
+ continue;
+ }
+ 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);
+ loc2 = var->var_part[i].cur_loc;
}
- loc2 = vt_expand_loc (var->var_part[i].cur_loc, vars, false);
+
+ offsets[n_var_parts] = offset;
if (!loc2)
{
complete = false;
@@ -7611,29 +8124,22 @@ emit_note_insn_var_location (void **varp, void *data)
}
loc[n_var_parts] = loc2;
mode = GET_MODE (var->var_part[i].cur_loc);
- if (mode == VOIDmode && dv_onepart_p (var->dv))
+ if (mode == VOIDmode && var->onepart)
mode = DECL_MODE (decl);
- 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. */
wider_mode = GET_MODE_WIDER_MODE (mode);
for (j = i + 1; j < var->n_var_parts; j++)
- if (last_limit <= var->var_part[j].offset)
+ if (last_limit <= VAR_PART_OFFSET (var, j))
break;
if (j < var->n_var_parts
&& wider_mode != VOIDmode
&& 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]))
- && last_limit == var->var_part[j].offset
- && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars, false))
+ && last_limit == (var->onepart ? 0 : VAR_PART_OFFSET (var, j))
+ && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars))
&& GET_CODE (loc[n_var_parts]) == GET_CODE (loc2))
{
rtx new_loc = NULL;
@@ -7746,152 +8252,152 @@ emit_note_insn_var_location (void **varp, void *data)
}
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);
-DEF_VEC_ALLOC_P (variable, heap);
-
-/* Stack of variable_def pointers that need processing with
- check_changed_vars_2. */
+/* While traversing changed_variables, push onto DATA (a stack of RTX
+ values) entries that aren't user variables. */
-static VEC (variable, heap) *changed_variables_stack;
-
-/* VALUEs with no variables that need set_dv_changed (val, false)
- called before check_changed_vars_3. */
+static int
+values_to_stack (void **slot, void *data)
+{
+ VEC (rtx, stack) **changed_values_stack = (VEC (rtx, stack) **)data;
+ variable var = (variable) *slot;
-static VEC (rtx, heap) *changed_values_stack;
+ if (var->onepart == ONEPART_VALUE)
+ VEC_safe_push (rtx, stack, *changed_values_stack, dv_as_value (var->dv));
+ else if (var->onepart == ONEPART_DEXPR)
+ VEC_safe_push (rtx, stack, *changed_values_stack,
+ DECL_RTL_KNOWN_SET (dv_as_decl (var->dv)));
-/* Helper function for check_changed_vars_1 and check_changed_vars_2. */
+ return 1;
+}
+/* Remove from changed_variables the entry whose DV corresponds to
+ value or debug_expr VAL. */
static void
-check_changed_vars_0 (decl_or_value dv, htab_t htab)
+remove_value_from_changed_variables (rtx val)
{
- value_chain vc
- = (value_chain) htab_find_with_hash (value_chains, dv, dv_htab_hash (dv));
+ decl_or_value dv = dv_from_rtx (val);
+ void **slot;
+ variable var;
- if (vc == NULL)
- return;
- for (vc = vc->next; vc; vc = vc->next)
- if (!dv_changed_p (vc->dv))
- {
- variable vcvar
- = (variable) htab_find_with_hash (htab, vc->dv,
- dv_htab_hash (vc->dv));
- if (vcvar)
- {
- set_dv_changed (vc->dv, true);
- VEC_safe_push (variable, heap, changed_variables_stack, vcvar);
- }
- else if (dv_is_value_p (vc->dv))
- {
- set_dv_changed (vc->dv, true);
- VEC_safe_push (rtx, heap, changed_values_stack,
- dv_as_value (vc->dv));
- check_changed_vars_0 (vc->dv, htab);
- }
- }
+ slot = htab_find_slot_with_hash (changed_variables,
+ dv, dv_htab_hash (dv), NO_INSERT);
+ var = (variable) *slot;
+ var->in_changed_variables = false;
+ htab_clear_slot (changed_variables, slot);
}
-/* Populate changed_variables_stack with variable_def pointers
- that need variable_was_changed called on them. */
+/* If VAL (a value or debug_expr) has backlinks to variables actively
+ dependent on it in HTAB or in CHANGED_VARIABLES, mark them as
+ changed, adding to CHANGED_VALUES_STACK any dependencies that may
+ have dependencies of their own to notify. */
-static int
-check_changed_vars_1 (void **slot, void *data)
+static void
+notify_dependents_of_changed_value (rtx val, htab_t htab,
+ VEC (rtx, stack) **changed_values_stack)
{
- variable var = (variable) *slot;
- htab_t htab = (htab_t) data;
+ void **slot;
+ variable var;
+ loc_exp_dep *led;
+ decl_or_value dv = dv_from_rtx (val);
- if (dv_is_value_p (var->dv)
- || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL)
- check_changed_vars_0 (var->dv, htab);
- return 1;
-}
+ slot = htab_find_slot_with_hash (changed_variables,
+ dv, dv_htab_hash (dv), NO_INSERT);
+ if (!slot)
+ slot = htab_find_slot_with_hash (htab,
+ dv, dv_htab_hash (dv), NO_INSERT);
+ if (!slot)
+ slot = htab_find_slot_with_hash (dropped_values,
+ dv, dv_htab_hash (dv), NO_INSERT);
+ var = (variable) *slot;
-/* Add VAR to changed_variables and also for VALUEs add recursively
- all DVs that aren't in changed_variables yet but reference the
- VALUE from its loc_chain. */
+ while ((led = VAR_LOC_DEP_LST (var)))
+ {
+ decl_or_value ldv = led->dv;
+ void **islot;
+ variable ivar;
-static void
-check_changed_vars_2 (variable var, htab_t htab)
-{
- variable_was_changed (var, NULL);
- if (dv_is_value_p (var->dv)
- || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL)
- check_changed_vars_0 (var->dv, htab);
+ /* Deactivate and remove the backlink, as it was “used up”. It
+ makes no sense to attempt to notify the same entity again:
+ either it will be recomputed and re-register an active
+ dependency, or it will still have the changed mark. */
+ if (led->next)
+ led->next->pprev = led->pprev;
+ if (led->pprev)
+ *led->pprev = led->next;
+ led->next = NULL;
+ led->pprev = NULL;
+
+ if (dv_changed_p (ldv))
+ continue;
+
+ switch (dv_onepart_p (ldv))
+ {
+ case ONEPART_VALUE:
+ case ONEPART_DEXPR:
+ set_dv_changed (ldv, true);
+ VEC_safe_push (rtx, stack, *changed_values_stack, dv_as_rtx (ldv));
+ break;
+
+ default:
+ islot = htab_find_slot_with_hash (htab, ldv, dv_htab_hash (ldv),
+ NO_INSERT);
+ ivar = (variable) *islot;
+ gcc_checking_assert (!VAR_LOC_DEP_LST (ivar));
+ variable_was_changed (ivar, NULL);
+ break;
+ }
+ }
}
-/* 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. */
+/* Take out of changed_variables any entries that don't refer to use
+ variables. Back-propagate change notifications from values and
+ debug_exprs to their active dependencies in HTAB or in
+ CHANGED_VARIABLES. */
-static int
-check_changed_vars_3 (void **slot, void *data)
+static void
+process_changed_values (htab_t htab)
{
- variable var = (variable) *slot;
- htab_t vars = (htab_t) data;
- int i;
- location_chain lc;
- bool cur_loc_changed;
+ int i, n;
+ rtx val;
+ VEC (rtx, stack) *changed_values_stack = VEC_alloc (rtx, stack, 20);
- if (dv_is_value_p (var->dv)
- || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL)
- return 1;
+ /* Move values from changed_variables to changed_values_stack. */
+ htab_traverse (changed_variables, values_to_stack, &changed_values_stack);
- for (i = 0; i < var->n_var_parts; i++)
+ /* Back-propagate change notifications in values while popping
+ them from the stack. */
+ for (n = i = VEC_length (rtx, changed_values_stack);
+ i > 0; i = VEC_length (rtx, changed_values_stack))
{
- if (var->var_part[i].cur_loc
- && vt_expand_loc_dummy (var->var_part[i].cur_loc, vars,
- &cur_loc_changed))
+ val = VEC_pop (rtx, changed_values_stack);
+ notify_dependents_of_changed_value (val, htab, &changed_values_stack);
+
+ /* This condition will hold when visiting each of the entries
+ originally in changed_variables. We can't remove them
+ earlier because this could drop the backlinks before we got a
+ chance to use them. */
+ if (i == n)
{
- if (cur_loc_changed)
- var->cur_loc_changed = true;
- continue;
+ remove_value_from_changed_variables (val);
+ n--;
}
- 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;
+
+ VEC_free (rtx, stack, changed_values_stack);
}
/* 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. */
+ CHANGED_VARIABLES and delete this chain. WHERE specifies whether
+ the notes shall be emitted before of after instruction INSN. */
static void
emit_notes_for_changes (rtx insn, enum emit_note_where where,
@@ -7904,19 +8410,7 @@ emit_notes_for_changes (rtx insn, enum emit_note_where where,
return;
if (MAY_HAVE_DEBUG_INSNS)
- {
- /* Unfortunately this has to be done in two steps, because
- we can't traverse a hashtab into which we are inserting
- through variable_was_changed. */
- htab_traverse (changed_variables, check_changed_vars_1, htab);
- while (VEC_length (variable, changed_variables_stack) > 0)
- check_changed_vars_2 (VEC_pop (variable, changed_variables_stack),
- htab);
- while (VEC_length (rtx, changed_values_stack) > 0)
- set_dv_changed (dv_from_value (VEC_pop (rtx, changed_values_stack)),
- false);
- htab_traverse (changed_variables, check_changed_vars_3, htab);
- }
+ process_changed_values (htab);
data.insn = insn;
data.where = where;
@@ -7941,78 +8435,59 @@ emit_notes_for_differences_1 (void **slot, void *data)
if (!new_var)
{
/* Variable has disappeared. */
- variable empty_var;
-
- empty_var = (variable) pool_alloc (dv_pool (old_var->dv));
- 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;
+ variable empty_var = NULL;
- gcc_assert (old_var->n_var_parts == 1);
- for (lc = old_var->var_part[0].loc_chain; lc; lc = lc->next)
- remove_value_chains (old_var->dv, lc->loc);
- }
- variable_was_changed (empty_var, NULL);
- /* Continue traversing the hash table. */
- return 1;
- }
- if (variable_different_p (old_var, new_var))
- {
- if (dv_onepart_p (old_var->dv))
+ if (old_var->onepart == ONEPART_VALUE
+ || old_var->onepart == ONEPART_DEXPR)
{
- location_chain lc1, lc2;
-
- gcc_assert (old_var->n_var_parts == 1
- && new_var->n_var_parts == 1);
- lc1 = old_var->var_part[0].loc_chain;
- lc2 = new_var->var_part[0].loc_chain;
- while (lc1
- && lc2
- && ((REG_P (lc1->loc) && REG_P (lc2->loc))
- || rtx_equal_p (lc1->loc, lc2->loc)))
+ empty_var = variable_from_dropped (old_var->dv, NO_INSERT);
+ if (empty_var)
{
- lc1 = lc1->next;
- lc2 = lc2->next;
+ gcc_checking_assert (!empty_var->in_changed_variables);
+ if (!VAR_LOC_1PAUX (old_var))
+ {
+ VAR_LOC_1PAUX (old_var) = VAR_LOC_1PAUX (empty_var);
+ VAR_LOC_1PAUX (empty_var) = NULL;
+ }
+ else
+ gcc_checking_assert (!VAR_LOC_1PAUX (empty_var));
}
- for (; lc2; lc2 = lc2->next)
- add_value_chains (old_var->dv, lc2->loc);
- for (; lc1; lc1 = lc1->next)
- remove_value_chains (old_var->dv, lc1->loc);
}
- 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++)
+
+ if (!empty_var)
{
- 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;
+ empty_var = (variable) pool_alloc (onepart_pool (old_var->onepart));
+ empty_var->dv = old_var->dv;
+ empty_var->refcount = 0;
+ empty_var->n_var_parts = 0;
+ empty_var->onepart = old_var->onepart;
+ empty_var->in_changed_variables = false;
+ }
- 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;
- }
+ if (empty_var->onepart)
+ {
+ /* Propagate the auxiliary data to (ultimately)
+ changed_variables. */
+ empty_var->var_part[0].loc_chain = NULL;
+ empty_var->var_part[0].cur_loc = NULL;
+ VAR_LOC_1PAUX (empty_var) = VAR_LOC_1PAUX (old_var);
+ VAR_LOC_1PAUX (old_var) = NULL;
}
+ variable_was_changed (empty_var, NULL);
+ /* Continue traversing the hash table. */
+ return 1;
+ }
+ /* Update cur_loc and one-part auxiliary data, before new_var goes
+ through variable_was_changed. */
+ if (old_var != new_var && new_var->onepart)
+ {
+ gcc_checking_assert (VAR_LOC_1PAUX (new_var) == NULL);
+ VAR_LOC_1PAUX (new_var) = VAR_LOC_1PAUX (old_var);
+ VAR_LOC_1PAUX (old_var) = NULL;
+ new_var->var_part[0].cur_loc = old_var->var_part[0].cur_loc;
}
+ if (variable_different_p (old_var, new_var))
+ variable_was_changed (new_var, NULL);
/* Continue traversing the hash table. */
return 1;
@@ -8033,15 +8508,6 @@ emit_notes_for_differences_2 (void **slot, void *data)
if (!old_var)
{
int i;
- /* Variable has appeared. */
- if (dv_onepart_p (new_var->dv))
- {
- location_chain lc;
-
- gcc_assert (new_var->n_var_parts == 1);
- for (lc = new_var->var_part[0].loc_chain; lc; lc = lc->next)
- add_value_chains (new_var->dv, lc->loc);
- }
for (i = 0; i < new_var->n_var_parts; i++)
new_var->var_part[i].cur_loc = NULL;
variable_was_changed (new_var, NULL);
@@ -8111,7 +8577,7 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
{
XEXP (XEXP (*p, 0), 1)
= vt_expand_loc (XEXP (XEXP (*p, 0), 1),
- shared_hash_htab (set->vars), true);
+ shared_hash_htab (set->vars));
/* If expansion is successful, keep it in the list. */
if (XEXP (XEXP (*p, 0), 1))
p = &XEXP (*p, 1);
@@ -8412,15 +8878,9 @@ vt_emit_notes (void)
emit_notes = true;
if (MAY_HAVE_DEBUG_INSNS)
- {
- unsigned int i;
- rtx val;
-
- FOR_EACH_VEC_ELT (rtx, preserved_values, i, val)
- add_cselib_value_chains (dv_from_value (val));
- changed_variables_stack = VEC_alloc (variable, heap, 40);
- changed_values_stack = VEC_alloc (rtx, heap, 40);
- }
+ dropped_values = htab_create (cselib_get_next_uid () * 2,
+ variable_htab_hash, variable_htab_eq,
+ variable_htab_free);
dataflow_set_init (&cur);
@@ -8441,23 +8901,11 @@ vt_emit_notes (void)
htab_traverse (shared_hash_htab (cur.vars),
emit_notes_for_differences_1,
shared_hash_htab (empty_shared_hash));
- if (MAY_HAVE_DEBUG_INSNS)
- {
- unsigned int i;
- rtx val;
-
- FOR_EACH_VEC_ELT (rtx, preserved_values, i, val)
- remove_cselib_value_chains (dv_from_value (val));
- gcc_assert (htab_elements (value_chains) == 0);
- }
#endif
dataflow_set_destroy (&cur);
if (MAY_HAVE_DEBUG_INSNS)
- {
- VEC_free (variable, heap, changed_variables_stack);
- VEC_free (rtx, heap, changed_values_stack);
- }
+ htab_delete (dropped_values);
emit_notes = false;
}
@@ -8489,36 +8937,27 @@ vt_get_decl_and_offset (rtx rtl, tree *declp, HOST_WIDE_INT *offsetp)
return false;
}
-/* Helper function for vt_add_function_parameter. RTL is
- the expression and VAL corresponding cselib_val pointer
- for which ENTRY_VALUE should be created. */
+/* Mark the value for the ENTRY_VALUE of RTL as equivalent to EQVAL in
+ OUT. */
static void
-create_entry_value (rtx rtl, cselib_val *val)
-{
- cselib_val *val2;
- struct elt_loc_list *el;
- el = (struct elt_loc_list *) ggc_alloc_cleared_atomic (sizeof (*el));
- el->loc = gen_rtx_ENTRY_VALUE (GET_MODE (rtl));
- ENTRY_VALUE_EXP (el->loc) = rtl;
- val2 = cselib_lookup_from_insn (el->loc, GET_MODE (rtl), true,
- VOIDmode, get_insns ());
- el->next = val->locs;
- el->setting_insn = get_insns ();
- val->locs = el;
- if (val2
- && val2 != val
- && val2->locs
- && rtx_equal_p (val2->locs->loc, el->loc))
- {
- struct elt_loc_list *el2;
-
- preserve_value (val2);
- el2 = (struct elt_loc_list *) ggc_alloc_cleared_atomic (sizeof (*el2));
- el2->next = val2->locs;
- el2->loc = val->val_rtx;
- el2->setting_insn = get_insns ();
- val2->locs = el2;
+create_entry_value (dataflow_set *out, rtx eqval, rtx rtl)
+{
+ rtx ev = gen_rtx_ENTRY_VALUE (GET_MODE (rtl));
+ cselib_val *val;
+
+ ENTRY_VALUE_EXP (ev) = rtl;
+
+ val = cselib_lookup_from_insn (ev, GET_MODE (ev), true,
+ VOIDmode, get_insns ());
+
+ if (val->val_rtx != eqval)
+ {
+ preserve_value (val);
+ set_variable_part (out, val->val_rtx, dv_from_value (eqval), 0,
+ VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
+ set_variable_part (out, eqval, dv_from_value (val->val_rtx), 0,
+ VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
}
}
@@ -8678,20 +9117,22 @@ vt_add_function_parameter (tree parm)
VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
if (dv_is_value_p (dv))
{
- cselib_val *val = CSELIB_VAL_PTR (dv_as_value (dv));
- create_entry_value (incoming, val);
+ create_entry_value (out, dv_as_value (dv), incoming);
if (TREE_CODE (TREE_TYPE (parm)) == REFERENCE_TYPE
&& INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (parm))))
{
enum machine_mode indmode
= TYPE_MODE (TREE_TYPE (TREE_TYPE (parm)));
rtx mem = gen_rtx_MEM (indmode, incoming);
- val = cselib_lookup_from_insn (mem, indmode, true,
- VOIDmode, get_insns ());
+ cselib_val *val = cselib_lookup_from_insn (mem, indmode, true,
+ VOIDmode,
+ get_insns ());
if (val)
{
preserve_value (val);
- create_entry_value (mem, val);
+ set_variable_part (out, mem, dv_from_value (val->val_rtx), 0,
+ VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
+ create_entry_value (out, val->val_rtx, mem);
}
}
}
@@ -8755,23 +9196,6 @@ fp_setter (rtx insn)
return false;
}
-/* Gather all registers used for passing arguments to other functions
- called from the current routine. */
-
-static void
-note_register_arguments (rtx insn)
-{
- rtx link, x;
-
- for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
- if (GET_CODE (XEXP (link, 0)) == USE)
- {
- x = XEXP (XEXP (link, 0), 0);
- if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER)
- SET_HARD_REG_BIT (argument_reg_set, REGNO (x));
- }
-}
-
/* Initialize cfa_base_rtx, create a preserved VALUE for it and
ensure it isn't flushed during cselib_reset_table.
Can be called only if frame_pointer_rtx resp. arg_pointer_rtx
@@ -8843,14 +9267,6 @@ vt_initialize (void)
variable_htab_free);
changed_variables = htab_create (10, variable_htab_hash, variable_htab_eq,
variable_htab_free);
- if (MAY_HAVE_DEBUG_INSNS)
- {
- value_chain_pool = create_alloc_pool ("value_chain_def pool",
- sizeof (struct value_chain_def),
- 1024);
- value_chains = htab_create (32, value_chain_htab_hash,
- value_chain_htab_eq, NULL);
- }
/* Init the IN and OUT sets. */
FOR_ALL_BB (bb)
@@ -8876,8 +9292,6 @@ vt_initialize (void)
valvar_pool = NULL;
}
- CLEAR_HARD_REG_SET (argument_reg_set);
-
/* In order to factor out the adjustments made to the stack pointer or to
the hard frame pointer and thus be able to use DW_OP_fbreg operations
instead of individual location lists, we're going to rewrite MEMs based
@@ -8962,14 +9376,6 @@ vt_initialize (void)
}
}
- if (frame_pointer_needed)
- {
- rtx insn;
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
- if (CALL_P (insn))
- note_register_arguments (insn);
- }
-
hard_frame_pointer_adjustment = -1;
vt_add_function_parameters ();
@@ -9155,8 +9561,6 @@ vt_finalize (void)
if (MAY_HAVE_DEBUG_INSNS)
{
- htab_delete (value_chains);
- free_alloc_pool (value_chain_pool);
free_alloc_pool (valvar_pool);
VEC_free (rtx, heap, preserved_values);
cselib_finish ();
@@ -9164,7 +9568,9 @@ vt_finalize (void)
scratch_regs = NULL;
}
+#ifdef HAVE_window_save
VEC_free (parm_reg_t, gc, windowed_parm_regs);
+#endif
if (vui_vec)
XDELETEVEC (vui_vec);