/* Variable tracking routines for the GNU compiler.
- Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010
+ Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011
Free Software Foundation, Inc.
This file is part of GCC.
#include "tm.h"
#include "rtl.h"
#include "tree.h"
+#include "tm_p.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "flags.h"
#include "tree-flow.h"
#include "cselib.h"
#include "target.h"
-#include "toplev.h"
#include "params.h"
#include "diagnostic.h"
+#include "tree-pretty-print.h"
#include "pointer-set.h"
#include "recog.h"
static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
HOST_WIDE_INT *);
static bool vt_stack_adjustments (void);
-static rtx compute_cfa_pointer (HOST_WIDE_INT);
static hashval_t variable_htab_hash (const void *);
static int variable_htab_eq (const void *, const void *);
static void variable_htab_free (void *);
static void **unshare_variable (dataflow_set *set, void **slot, variable var,
enum var_init_status);
-static int vars_copy_1 (void **, void *);
static void vars_copy (htab_t, htab_t);
static tree var_debug_decl (tree);
static void var_reg_set (dataflow_set *, rtx, enum var_init_status, rtx);
static void dataflow_set_clear (dataflow_set *);
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 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 bool variable_part_different_p (variable_part *, variable_part *);
static bool onepart_variable_different_p (variable, variable);
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 true;
}
+/* arg_pointer_rtx resp. frame_pointer_rtx if stack_pointer_rtx or
+ hard_frame_pointer_rtx is being mapped to it and offset for it. */
+static rtx cfa_base_rtx;
+static HOST_WIDE_INT cfa_base_offset;
+
/* Compute a CFA-based value for the stack pointer. */
-static rtx
+static inline rtx
compute_cfa_pointer (HOST_WIDE_INT adjustment)
{
- rtx cfa;
-
-#ifdef FRAME_POINTER_CFA_OFFSET
- adjustment -= FRAME_POINTER_CFA_OFFSET (current_function_decl);
- cfa = plus_constant (frame_pointer_rtx, adjustment);
-#else
- adjustment -= ARG_POINTER_CFA_OFFSET (current_function_decl);
- cfa = plus_constant (arg_pointer_rtx, adjustment);
-#endif
-
- return cfa;
+ return plus_constant (cfa_base_rtx, adjustment + cfa_base_offset);
}
/* Adjustment for hard_frame_pointer_rtx to cfa base reg,
rtx side_effects;
};
+/* Helper for adjust_mems. Return 1 if *loc is unsuitable for
+ transformation of wider mode arithmetics to narrower mode,
+ -1 if it is suitable and subexpressions shouldn't be
+ traversed and 0 if it is suitable and subexpressions should
+ be traversed. Called through for_each_rtx. */
+
+static int
+use_narrower_mode_test (rtx *loc, void *data)
+{
+ rtx subreg = (rtx) data;
+
+ if (CONSTANT_P (*loc))
+ return -1;
+ switch (GET_CODE (*loc))
+ {
+ case REG:
+ if (cselib_lookup (*loc, GET_MODE (SUBREG_REG (subreg)), 0, VOIDmode))
+ return 1;
+ if (!validate_subreg (GET_MODE (subreg), GET_MODE (*loc),
+ *loc, subreg_lowpart_offset (GET_MODE (subreg),
+ GET_MODE (*loc))))
+ return 1;
+ return -1;
+ case PLUS:
+ case MINUS:
+ case MULT:
+ return 0;
+ case ASHIFT:
+ if (for_each_rtx (&XEXP (*loc, 0), use_narrower_mode_test, data))
+ return 1;
+ else
+ return -1;
+ default:
+ return 1;
+ }
+}
+
+/* Transform X into narrower mode MODE from wider mode WMODE. */
+
+static rtx
+use_narrower_mode (rtx x, enum machine_mode mode, enum machine_mode wmode)
+{
+ rtx op0, op1;
+ if (CONSTANT_P (x))
+ return lowpart_subreg (mode, x, wmode);
+ switch (GET_CODE (x))
+ {
+ case REG:
+ return lowpart_subreg (mode, x, wmode);
+ case PLUS:
+ case MINUS:
+ case MULT:
+ op0 = use_narrower_mode (XEXP (x, 0), mode, wmode);
+ op1 = use_narrower_mode (XEXP (x, 1), mode, wmode);
+ return simplify_gen_binary (GET_CODE (x), mode, op0, op1);
+ case ASHIFT:
+ op0 = use_narrower_mode (XEXP (x, 0), mode, wmode);
+ return simplify_gen_binary (ASHIFT, mode, op0, XEXP (x, 1));
+ default:
+ gcc_unreachable ();
+ }
+}
+
/* Helper function for adjusting used MEMs. */
static rtx
switch (GET_CODE (loc))
{
case REG:
- /* Don't do any sp or fp replacements outside of MEM addresses. */
- if (amd->mem_mode == VOIDmode)
+ /* Don't do any sp or fp replacements outside of MEM addresses
+ on the LHS. */
+ if (amd->mem_mode == VOIDmode && amd->store)
return loc;
if (loc == stack_pointer_rtx
- && !frame_pointer_needed)
+ && !frame_pointer_needed
+ && cfa_base_rtx)
return compute_cfa_pointer (amd->stack_adjust);
else if (loc == hard_frame_pointer_rtx
&& frame_pointer_needed
- && hard_frame_pointer_adjustment != -1)
+ && hard_frame_pointer_adjustment != -1
+ && cfa_base_rtx)
return compute_cfa_pointer (hard_frame_pointer_adjustment);
+ gcc_checking_assert (loc != virtual_incoming_args_rtx);
return loc;
case MEM:
mem = loc;
amd->store = store_save;
mem = simplify_replace_fn_rtx (addr, old_rtx, adjust_mems, data);
if (mem == SUBREG_REG (loc))
- return loc;
+ {
+ tem = loc;
+ goto finish_subreg;
+ }
tem = simplify_gen_subreg (GET_MODE (loc), mem,
GET_MODE (SUBREG_REG (loc)),
SUBREG_BYTE (loc));
if (tem)
- return tem;
+ goto finish_subreg;
tem = simplify_gen_subreg (GET_MODE (loc), addr,
GET_MODE (SUBREG_REG (loc)),
SUBREG_BYTE (loc));
- if (tem)
- return tem;
- return gen_rtx_raw_SUBREG (GET_MODE (loc), addr, SUBREG_BYTE (loc));
+ if (tem == NULL_RTX)
+ tem = gen_rtx_raw_SUBREG (GET_MODE (loc), addr, SUBREG_BYTE (loc));
+ finish_subreg:
+ if (MAY_HAVE_DEBUG_INSNS
+ && GET_CODE (tem) == SUBREG
+ && (GET_CODE (SUBREG_REG (tem)) == PLUS
+ || GET_CODE (SUBREG_REG (tem)) == MINUS
+ || GET_CODE (SUBREG_REG (tem)) == MULT
+ || GET_CODE (SUBREG_REG (tem)) == ASHIFT)
+ && GET_MODE_CLASS (GET_MODE (tem)) == MODE_INT
+ && GET_MODE_CLASS (GET_MODE (SUBREG_REG (tem))) == MODE_INT
+ && GET_MODE_SIZE (GET_MODE (tem))
+ < GET_MODE_SIZE (GET_MODE (SUBREG_REG (tem)))
+ && subreg_lowpart_p (tem)
+ && !for_each_rtx (&SUBREG_REG (tem), use_narrower_mode_test, tem))
+ return use_narrower_mode (SUBREG_REG (tem), GET_MODE (tem),
+ GET_MODE (SUBREG_REG (tem)));
+ return tem;
+ case ASM_OPERANDS:
+ /* Don't do any replacements in second and following
+ ASM_OPERANDS of inline-asm with multiple sets.
+ ASM_OPERANDS_INPUT_VEC, ASM_OPERANDS_INPUT_CONSTRAINT_VEC
+ and ASM_OPERANDS_LABEL_VEC need to be equal between
+ all the ASM_OPERANDs in the insn and adjust_insn will
+ fix this up. */
+ if (ASM_OPERANDS_OUTPUT_IDX (loc) != 0)
+ return loc;
+ break;
default:
break;
}
note_stores (PATTERN (insn), adjust_mem_stores, &amd);
amd.store = false;
- note_uses (&PATTERN (insn), adjust_mem_uses, &amd);
+ if (GET_CODE (PATTERN (insn)) == PARALLEL
+ && asm_noperands (PATTERN (insn)) > 0
+ && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
+ {
+ rtx body, set0;
+ int i;
+
+ /* inline-asm with multiple sets is tiny bit more complicated,
+ because the 3 vectors in ASM_OPERANDS need to be shared between
+ all ASM_OPERANDS in the instruction. adjust_mems will
+ not touch ASM_OPERANDS other than the first one, asm_noperands
+ test above needs to be called before that (otherwise it would fail)
+ and afterwards this code fixes it up. */
+ note_uses (&PATTERN (insn), adjust_mem_uses, &amd);
+ body = PATTERN (insn);
+ set0 = XVECEXP (body, 0, 0);
+ gcc_checking_assert (GET_CODE (set0) == SET
+ && GET_CODE (SET_SRC (set0)) == ASM_OPERANDS
+ && ASM_OPERANDS_OUTPUT_IDX (SET_SRC (set0)) == 0);
+ for (i = 1; i < XVECLEN (body, 0); i++)
+ if (GET_CODE (XVECEXP (body, 0, i)) != SET)
+ break;
+ else
+ {
+ set = XVECEXP (body, 0, i);
+ gcc_checking_assert (GET_CODE (SET_SRC (set)) == ASM_OPERANDS
+ && ASM_OPERANDS_OUTPUT_IDX (SET_SRC (set))
+ == i);
+ if (ASM_OPERANDS_INPUT_VEC (SET_SRC (set))
+ != ASM_OPERANDS_INPUT_VEC (SET_SRC (set0))
+ || ASM_OPERANDS_INPUT_CONSTRAINT_VEC (SET_SRC (set))
+ != ASM_OPERANDS_INPUT_CONSTRAINT_VEC (SET_SRC (set0))
+ || ASM_OPERANDS_LABEL_VEC (SET_SRC (set))
+ != ASM_OPERANDS_LABEL_VEC (SET_SRC (set0)))
+ {
+ rtx newsrc = shallow_copy_rtx (SET_SRC (set));
+ ASM_OPERANDS_INPUT_VEC (newsrc)
+ = ASM_OPERANDS_INPUT_VEC (SET_SRC (set0));
+ ASM_OPERANDS_INPUT_CONSTRAINT_VEC (newsrc)
+ = ASM_OPERANDS_INPUT_CONSTRAINT_VEC (SET_SRC (set0));
+ ASM_OPERANDS_LABEL_VEC (newsrc)
+ = ASM_OPERANDS_LABEL_VEC (SET_SRC (set0));
+ validate_change (NULL_RTX, &SET_SRC (set), newsrc, true);
+ }
+ }
+ }
+ else
+ note_uses (&PATTERN (insn), adjust_mem_uses, &amd);
/* For read-only MEMs containing some constant, prefer those
constants. */
static inline tree
dv_as_decl (decl_or_value dv)
{
-#ifdef ENABLE_CHECKING
- gcc_assert (dv_is_decl_p (dv));
-#endif
+ gcc_checking_assert (dv_is_decl_p (dv));
return (tree) dv;
}
static inline rtx
dv_as_value (decl_or_value dv)
{
-#ifdef ENABLE_CHECKING
- gcc_assert (dv_is_value_p (dv));
-#endif
+ gcc_checking_assert (dv_is_value_p (dv));
return (rtx)dv;
}
{
decl_or_value dv;
dv = decl;
-#ifdef ENABLE_CHECKING
- gcc_assert (dv_is_decl_p (dv));
-#endif
+ gcc_checking_assert (dv_is_decl_p (dv));
return dv;
}
{
decl_or_value dv;
dv = value;
-#ifdef ENABLE_CHECKING
- gcc_assert (dv_is_value_p (dv));
-#endif
+ gcc_checking_assert (dv_is_value_p (dv));
return dv;
}
extern void debug_dv (decl_or_value dv);
-void
+DEBUG_FUNCTION void
debug_dv (decl_or_value dv)
{
if (dv_is_value_p (dv))
variable var = (variable) elem;
location_chain node, next;
- gcc_assert (var->refcount > 0);
+ gcc_checking_assert (var->refcount > 0);
var->refcount--;
if (var->refcount > 0)
static void
shared_hash_destroy (shared_hash vars)
{
- gcc_assert (vars->refcount > 0);
+ gcc_checking_assert (vars->refcount > 0);
if (--vars->refcount == 0)
{
htab_delete (vars->htab);
return slot;
}
-/* Add a variable from *SLOT to hash table DATA and increase its reference
- count. */
-
-static int
-vars_copy_1 (void **slot, void *data)
-{
- htab_t dst = (htab_t) data;
- variable src;
- void **dstp;
-
- src = (variable) *slot;
- src->refcount++;
-
- dstp = htab_find_slot_with_hash (dst, src->dv,
- dv_htab_hash (src->dv),
- INSERT);
- *dstp = src;
-
- /* Continue traversing the hash table. */
- return 1;
-}
-
/* Copy all variables from hash table SRC to hash table DST. */
static void
vars_copy (htab_t dst, htab_t src)
{
- htab_traverse_noresize (src, vars_copy_1, dst);
+ htab_iterator hi;
+ variable var;
+
+ FOR_EACH_HTAB_ELEMENT (src, var, variable, hi)
+ {
+ void **dstp;
+ var->refcount++;
+ dstp = htab_find_slot_with_hash (dst, var->dv,
+ dv_htab_hash (var->dv),
+ INSERT);
+ *dstp = var;
+ }
}
/* Map a decl to its main debug decl. */
var_debug_decl (tree decl)
{
if (decl && DECL_P (decl)
- && DECL_DEBUG_EXPR_IS_FROM (decl) && DECL_DEBUG_EXPR (decl)
- && DECL_P (DECL_DEBUG_EXPR (decl)))
- decl = DECL_DEBUG_EXPR (decl);
+ && DECL_DEBUG_EXPR_IS_FROM (decl))
+ {
+ tree debugdecl = DECL_DEBUG_EXPR (decl);
+ if (debugdecl && DECL_P (debugdecl))
+ decl = debugdecl;
+ }
return decl;
}
we keep the newest locations in the beginning. */
static int
-variable_union (void **slot, void *data)
+variable_union (variable src, dataflow_set *set)
{
- variable src, dst;
+ variable dst;
void **dstp;
- dataflow_set *set = (dataflow_set *) data;
int i, j, k;
- src = (variable) *slot;
dstp = shared_hash_find_slot (set->vars, src->dv);
if (!dstp || !*dstp)
{
{
location_chain *nodep, dnode, snode;
- gcc_assert (src->n_var_parts == 1);
- gcc_assert (dst->n_var_parts == 1);
+ gcc_assert (src->n_var_parts == 1
+ && dst->n_var_parts == 1);
snode = src->var_part[0].loc_chain;
gcc_assert (snode);
nnode->next = dnode;
dnode = nnode;
}
-#ifdef ENABLE_CHECKING
else if (r == 0)
- gcc_assert (rtx_equal_p (dnode->loc, snode->loc));
-#endif
+ gcc_checking_assert (rtx_equal_p (dnode->loc, snode->loc));
if (r >= 0)
snode = snode->next;
dst->vars = shared_hash_copy (src->vars);
}
else
- htab_traverse (shared_hash_htab (src->vars), variable_union, dst);
+ {
+ htab_iterator hi;
+ variable var;
+
+ FOR_EACH_HTAB_ELEMENT (shared_hash_htab (src->vars), var, variable, hi)
+ variable_union (var, dst);
+ }
}
/* Whether the value is currently being expanded. */
/* Return a location list node whose loc is rtx_equal to LOC, in the
location list of a one-part variable or value VAR, or in that of
- any values recursively mentioned in the location lists. */
+ any values recursively mentioned in the location lists. VARS must
+ be in star-canonical form. */
static location_chain
find_loc_in_1pdv (rtx loc, variable var, htab_t vars)
{
location_chain node;
+ enum rtx_code loc_code;
if (!var)
return NULL;
- gcc_assert (dv_onepart_p (var->dv));
+ gcc_checking_assert (dv_onepart_p (var->dv));
if (!var->n_var_parts)
return NULL;
- gcc_assert (var->var_part[0].offset == 0);
+ gcc_checking_assert (var->var_part[0].offset == 0);
+ gcc_checking_assert (loc != dv_as_opaque (var->dv));
+ loc_code = GET_CODE (loc);
for (node = var->var_part[0].loc_chain; node; node = node->next)
- if (rtx_equal_p (loc, node->loc))
- return node;
- else if (GET_CODE (node->loc) == VALUE
- && !VALUE_RECURSED_INTO (node->loc))
- {
- decl_or_value dv = dv_from_value (node->loc);
- variable var = (variable)
- htab_find_with_hash (vars, dv, dv_htab_hash (dv));
+ {
+ decl_or_value dv;
+ variable rvar;
- if (var)
- {
- location_chain where;
- VALUE_RECURSED_INTO (node->loc) = true;
- if ((where = find_loc_in_1pdv (loc, var, vars)))
- {
- VALUE_RECURSED_INTO (node->loc) = false;
- return where;
- }
- VALUE_RECURSED_INTO (node->loc) = false;
- }
- }
+ if (GET_CODE (node->loc) != loc_code)
+ {
+ if (GET_CODE (node->loc) != VALUE)
+ continue;
+ }
+ else if (loc == node->loc)
+ return node;
+ else if (loc_code != VALUE)
+ {
+ if (rtx_equal_p (loc, node->loc))
+ return node;
+ continue;
+ }
+
+ /* Since we're in star-canonical form, we don't need to visit
+ non-canonical nodes: one-part variables and non-canonical
+ values would only point back to the canonical node. */
+ if (dv_is_value_p (var->dv)
+ && !canon_value_cmp (node->loc, dv_as_value (var->dv)))
+ {
+ /* Skip all subsequent VALUEs. */
+ while (node->next && GET_CODE (node->next->loc) == VALUE)
+ {
+ node = node->next;
+ gcc_checking_assert (!canon_value_cmp (node->loc,
+ dv_as_value (var->dv)));
+ if (loc == node->loc)
+ return node;
+ }
+ continue;
+ }
+
+ gcc_checking_assert (node == var->var_part[0].loc_chain);
+ gcc_checking_assert (!node->next);
+
+ dv = dv_from_value (node->loc);
+ rvar = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
+ return find_loc_in_1pdv (loc, rvar, vars);
+ }
return NULL;
}
dataflow_set *s2set = dsm->src;
location_chain found;
+ if (s2var)
+ {
+ location_chain s2node;
+
+ gcc_checking_assert (dv_onepart_p (s2var->dv));
+
+ if (s2var->n_var_parts)
+ {
+ gcc_checking_assert (s2var->var_part[0].offset == 0);
+ s2node = s2var->var_part[0].loc_chain;
+
+ for (; s1node && s2node;
+ s1node = s1node->next, s2node = s2node->next)
+ if (s1node->loc != s2node->loc)
+ break;
+ else if (s1node->loc == val)
+ continue;
+ else
+ insert_into_intersection (dest, s1node->loc,
+ MIN (s1node->init, s2node->init));
+ }
+ }
+
for (; s1node; s1node = s1node->next)
{
if (s1node->loc == val)
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
+ gcc_checking_assert (DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x))
+ > DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (y)));
return 1;
}
if (!dv_is_value_p (dv))
return 1;
- gcc_assert (var->n_var_parts == 1);
+ gcc_checking_assert (var->n_var_parts == 1);
val = dv_as_value (dv);
if (!dv_onepart_p (dv))
return 1;
- gcc_assert (var->n_var_parts == 1);
+ gcc_checking_assert (var->n_var_parts == 1);
if (dv_is_value_p (dv))
{
/* Variable may have been unshared. */
var = (variable)*slot;
- gcc_assert (var->n_var_parts && var->var_part[0].loc_chain->loc == cval
- && var->var_part[0].loc_chain->next == NULL);
+ gcc_checking_assert (var->n_var_parts && var->var_part[0].loc_chain->loc == cval
+ && var->var_part[0].loc_chain->next == NULL);
if (VALUE_RECURSED_INTO (cval))
goto restart_with_cval;
intersection. */
static int
-variable_merge_over_cur (void **s1slot, void *data)
+variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
{
- struct dfset_merge *dsm = (struct dfset_merge *)data;
dataflow_set *dst = dsm->dst;
void **dstslot;
- variable s1var = (variable) *s1slot;
variable s2var, dvar = NULL;
decl_or_value dv = s1var->dv;
bool onepart = dv_onepart_p (dv);
/* If the incoming onepart variable has an empty location list, then
the intersection will be just as empty. For other variables,
it's always union. */
- gcc_assert (s1var->n_var_parts);
- gcc_assert (s1var->var_part[0].loc_chain);
+ gcc_checking_assert (s1var->n_var_parts
+ && s1var->var_part[0].loc_chain);
if (!onepart)
- return variable_union (s1slot, dst);
+ return variable_union (s1var, dst);
- gcc_assert (s1var->n_var_parts == 1);
- gcc_assert (s1var->var_part[0].offset == 0);
+ gcc_checking_assert (s1var->n_var_parts == 1
+ && s1var->var_part[0].offset == 0);
dvhash = dv_htab_hash (dv);
if (dv_is_value_p (dv))
}
dsm->src_onepart_cnt--;
- gcc_assert (s2var->var_part[0].loc_chain);
- gcc_assert (s2var->n_var_parts == 1);
- gcc_assert (s2var->var_part[0].offset == 0);
+ gcc_assert (s2var->var_part[0].loc_chain
+ && s2var->n_var_parts == 1
+ && s2var->var_part[0].offset == 0);
dstslot = shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash);
if (dstslot)
{
dvar = (variable)*dstslot;
- gcc_assert (dvar->refcount == 1);
- gcc_assert (dvar->n_var_parts == 1);
- gcc_assert (dvar->var_part[0].offset == 0);
+ gcc_assert (dvar->refcount == 1
+ && dvar->n_var_parts == 1
+ && dvar->var_part[0].offset == 0);
nodep = &dvar->var_part[0].loc_chain;
}
else
dstslot = shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash);
gcc_assert (*dstslot == dvar);
canonicalize_values_star (dstslot, dst);
-#ifdef ENABLE_CHECKING
- gcc_assert (dstslot
- == shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash));
-#endif
+ gcc_checking_assert (dstslot
+ == shared_hash_find_slot_noinsert_1 (dst->vars,
+ dv, dvhash));
dvar = (variable)*dstslot;
}
else
dstslot = shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash);
gcc_assert (*dstslot == dvar);
canonicalize_values_star (dstslot, dst);
-#ifdef ENABLE_CHECKING
- gcc_assert (dstslot
- == shared_hash_find_slot_noinsert_1 (dst->vars,
- dv, dvhash));
-#endif
+ gcc_checking_assert (dstslot
+ == shared_hash_find_slot_noinsert_1 (dst->vars,
+ dv, dvhash));
dvar = (variable)*dstslot;
}
}
variable_merge_over_cur(). */
static int
-variable_merge_over_src (void **s2slot, void *data)
+variable_merge_over_src (variable s2var, struct dfset_merge *dsm)
{
- struct dfset_merge *dsm = (struct dfset_merge *)data;
dataflow_set *dst = dsm->dst;
- variable s2var = (variable) *s2slot;
decl_or_value dv = s2var->dv;
bool onepart = dv_onepart_p (dv);
struct dfset_merge dsm;
int i;
size_t src1_elems, src2_elems;
+ htab_iterator hi;
+ variable var;
src1_elems = htab_elements (shared_hash_htab (src1->vars));
src2_elems = htab_elements (shared_hash_htab (src2->vars));
dsm.cur = src1;
dsm.src_onepart_cnt = 0;
- htab_traverse (shared_hash_htab (dsm.src->vars), variable_merge_over_src,
- &dsm);
- htab_traverse (shared_hash_htab (dsm.cur->vars), variable_merge_over_cur,
- &dsm);
+ FOR_EACH_HTAB_ELEMENT (shared_hash_htab (dsm.src->vars), var, variable, hi)
+ variable_merge_over_src (var, &dsm);
+ FOR_EACH_HTAB_ELEMENT (shared_hash_htab (dsm.cur->vars), var, variable, hi)
+ variable_merge_over_cur (var, &dsm);
if (dsm.src_onepart_cnt)
dst_can_be_shared = false;
{
rtx canon[NUM_MACHINE_MODES];
+ /* If the list is empty or one entry, no need to canonicalize
+ anything. */
+ if (set->regs[i] == NULL || set->regs[i]->next == NULL)
+ continue;
+
memset (canon, 0, sizeof (canon));
for (list = set->regs[i]; list; list = list->next)
att; att = att->next)
if (GET_MODE (att->loc) == GET_MODE (node->loc))
{
- gcc_assert (att->offset == 0);
- gcc_assert (dv_is_value_p (att->dv));
+ gcc_assert (att->offset == 0
+ && dv_is_value_p (att->dv));
val_reset (set, att->dv);
break;
}
subsequent rounds. */
cselib_val *v;
gcc_assert (!cselib_lookup (node->loc,
- GET_MODE (node->loc), 0));
- v = cselib_lookup (node->loc, GET_MODE (node->loc), 1);
+ GET_MODE (node->loc), 0,
+ VOIDmode));
+ v = cselib_lookup (node->loc, GET_MODE (node->loc), 1,
+ VOIDmode);
cselib_preserve_value (v);
cselib_invalidate_rtx (node->loc);
cval = v->val_rtx;
decl_or_value dv;
attrs att;
- gcc_assert (dv_is_value_p (pvar->dv));
- gcc_assert (pvar->n_var_parts == 1);
+ gcc_assert (dv_is_value_p (pvar->dv)
+ && pvar->n_var_parts == 1);
pnode = pvar->var_part[0].loc_chain;
- gcc_assert (pnode);
- gcc_assert (!pnode->next);
- gcc_assert (REG_P (pnode->loc));
+ gcc_assert (pnode
+ && !pnode->next
+ && REG_P (pnode->loc));
dv = pvar->dv;
var = shared_hash_find (set->vars, dv);
if (var)
{
+ /* Although variable_post_merge_new_vals may have made decls
+ non-star-canonical, values that pre-existed in canonical form
+ remain canonical, and newly-created values reference a single
+ REG, so they are canonical as well. Since VAR has the
+ location list for a VALUE, using find_loc_in_1pdv for it is
+ fine, since VALUEs don't map back to DECLs. */
if (find_loc_in_1pdv (pnode->loc, var, shared_hash_htab (set->vars)))
return 1;
val_reset (set, dv);
{
attrs_list_insert (&set->regs[REGNO (pnode->loc)],
dv, 0, pnode->loc);
- variable_union (pslot, set);
+ variable_union (pvar, set);
}
return 1;
if (!val)
return NULL;
- gcc_assert (GET_CODE (val) == VALUE);
-
- gcc_assert (!VALUE_RECURSED_INTO (val));
+ gcc_assert (GET_CODE (val) == VALUE
+ && !VALUE_RECURSED_INTO (val));
dv = dv_from_value (val);
var = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
VALUE_RECURSED_INTO (val) = true;
for (node = var->var_part[0].loc_chain; node; node = node->next)
- if (MEM_P (node->loc) && MEM_EXPR (node->loc) == expr
- && MEM_OFFSET (node->loc) == 0)
+ if (MEM_P (node->loc)
+ && MEM_EXPR (node->loc) == expr
+ && INT_MEM_OFFSET (node->loc) == 0)
{
where = node;
break;
{
for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
{
- /* We want to remove dying MEMs that doesn't refer to
- DECL. */
+ /* We want to remove dying MEMs that doesn't refer to DECL. */
if (GET_CODE (loc->loc) == MEM
&& (MEM_EXPR (loc->loc) != decl
- || MEM_OFFSET (loc->loc))
+ || INT_MEM_OFFSET (loc->loc) != 0)
&& !mem_dies_at_call (loc->loc))
break;
/* We want to move here MEMs that do refer to DECL. */
if (GET_CODE (loc->loc) != MEM
|| (MEM_EXPR (loc->loc) == decl
- && MEM_OFFSET (loc->loc) == 0)
+ && INT_MEM_OFFSET (loc->loc) == 0)
|| !mem_dies_at_call (loc->loc))
{
if (old_loc != loc->loc && emit_notes)
int r;
for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
- if (TEST_HARD_REG_BIT (call_used_reg_set, r))
+ if (TEST_HARD_REG_BIT (regs_invalidated_by_call, r))
var_regno_delete (set, r);
if (MAY_HAVE_DEBUG_INSNS)
}
}
-/* Flag whether two dataflow sets being compared contain different data. */
-static bool
-dataflow_set_different_value;
-
static bool
variable_part_different_p (variable_part *vp1, variable_part *vp2)
{
if (var1 == var2)
return false;
- gcc_assert (var1->n_var_parts == 1);
- gcc_assert (var2->n_var_parts == 1);
+ gcc_assert (var1->n_var_parts == 1
+ && var2->n_var_parts == 1);
lc1 = var1->var_part[0].loc_chain;
lc2 = var2->var_part[0].loc_chain;
- gcc_assert (lc1);
- gcc_assert (lc2);
+ gcc_assert (lc1 && lc2);
while (lc1 && lc2)
{
/* 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);
- gcc_assert (dv_as_opaque (var1->dv) == dv_as_opaque (var2->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 false;
}
-/* Compare variable *SLOT with the same variable in hash table DATA
- and set DATAFLOW_SET_DIFFERENT_VALUE if they are different. */
-
-static int
-dataflow_set_different_1 (void **slot, void *data)
-{
- htab_t htab = (htab_t) data;
- variable var1, var2;
-
- var1 = (variable) *slot;
- var2 = (variable) htab_find_with_hash (htab, var1->dv,
- dv_htab_hash (var1->dv));
- if (!var2)
- {
- dataflow_set_different_value = true;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "dataflow difference found: removal of:\n");
- dump_var (var1);
- }
-
- /* Stop traversing the hash table. */
- return 0;
- }
-
- if (variable_different_p (var1, var2))
- {
- dataflow_set_different_value = true;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "dataflow difference found: old and new follow:\n");
- dump_var (var1);
- dump_var (var2);
- }
-
- /* Stop traversing the hash table. */
- return 0;
- }
-
- /* Continue traversing the hash table. */
- return 1;
-}
-
/* Return true if dataflow sets OLD_SET and NEW_SET differ. */
static bool
dataflow_set_different (dataflow_set *old_set, dataflow_set *new_set)
{
+ htab_iterator hi;
+ variable var1;
+
if (old_set->vars == new_set->vars)
return false;
!= htab_elements (shared_hash_htab (new_set->vars)))
return true;
- dataflow_set_different_value = false;
+ FOR_EACH_HTAB_ELEMENT (shared_hash_htab (old_set->vars), var1, variable, hi)
+ {
+ htab_t htab = shared_hash_htab (new_set->vars);
+ variable var2 = (variable) htab_find_with_hash (htab, var1->dv,
+ dv_htab_hash (var1->dv));
+ if (!var2)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "dataflow difference found: removal of:\n");
+ dump_var (var1);
+ }
+ return true;
+ }
+
+ if (variable_different_p (var1, var2))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "dataflow difference found: "
+ "old and new follow:\n");
+ dump_var (var1);
+ dump_var (var2);
+ }
+ return true;
+ }
+ }
- htab_traverse (shared_hash_htab (old_set->vars), dataflow_set_different_1,
- shared_hash_htab (new_set->vars));
/* No need to traverse the second hashtab, if both have the same number
of elements and the second one had all entries found in the first one,
then it can't have any extra entries. */
- return dataflow_set_different_value;
+ return false;
}
/* Free the contents of dataflow set SET. */
don't need to track this expression if the ultimate declaration is
ignored. */
realdecl = expr;
- if (DECL_DEBUG_EXPR_IS_FROM (realdecl) && DECL_DEBUG_EXPR (realdecl))
+ if (DECL_DEBUG_EXPR_IS_FROM (realdecl))
{
realdecl = DECL_DEBUG_EXPR (realdecl);
- /* ??? We don't yet know how to emit DW_OP_piece for variable
- that has been SRA'ed. */
- if (!DECL_P (realdecl))
- return 0;
+ if (realdecl == NULL_TREE)
+ realdecl = expr;
+ else if (!DECL_P (realdecl))
+ {
+ if (handled_component_p (realdecl))
+ {
+ HOST_WIDE_INT bitsize, bitpos, maxsize;
+ tree innerdecl
+ = get_ref_base_and_extent (realdecl, &bitpos, &bitsize,
+ &maxsize);
+ if (!DECL_P (innerdecl)
+ || DECL_IGNORED_P (innerdecl)
+ || TREE_STATIC (innerdecl)
+ || bitsize <= 0
+ || bitpos + bitsize > 256
+ || bitsize != maxsize)
+ return 0;
+ else
+ realdecl = expr;
+ }
+ else
+ return 0;
+ }
}
/* Do not track EXPR if REALDECL it should be ignored for debugging
return gen_rtx_REG_offset (loc, mode, regno, offset);
}
-/* arg_pointer_rtx resp. frame_pointer_rtx if stack_pointer_rtx or
- hard_frame_pointer_rtx is being mapped to it. */
-static rtx cfa_base_rtx;
-
/* Carry information about uses and stores while walking rtx. */
struct count_use_info
if (cui->sets)
{
/* This is called after uses are set up and before stores are
- processed bycselib, so it's safe to look up srcs, but not
+ processed by cselib, so it's safe to look up srcs, but not
dsts. So we look up expressions that appear in srcs or in
dest expressions, but we search the sets array for dests of
stores. */
if (cui->store_p)
{
+ /* Some targets represent memset and memcpy patterns
+ by (set (mem:BLK ...) (reg:[QHSD]I ...)) or
+ (set (mem:BLK ...) (const_int ...)) or
+ (set (mem:BLK ...) (mem:BLK ...)). Don't return anything
+ in that case, otherwise we end up with mode mismatches. */
+ if (mode == BLKmode && MEM_P (x))
+ return NULL;
for (i = 0; i < cui->n_sets; i++)
if (cui->sets[i].dest == x)
return cui->sets[i].src_elt;
}
else
- return cselib_lookup (x, mode, 0);
+ return cselib_lookup (x, mode, 0, VOIDmode);
}
return NULL;
else if (MEM_P (loc))
{
cselib_val *addr = cselib_lookup (XEXP (loc, 0),
- get_address_mode (loc), 0);
+ get_address_mode (loc), 0,
+ GET_MODE (loc));
if (addr)
return replace_equiv_address_nv (loc, addr->val_rtx);
else
return NULL;
}
else
- return cselib_subst_to_values (loc);
+ return cselib_subst_to_values (loc, VOIDmode);
}
/* Determine what kind of micro operation to choose for a USE. Return
rtx ploc = PAT_VAR_LOCATION_LOC (loc);
if (! VAR_LOC_UNKNOWN_P (ploc))
{
- cselib_val *val = cselib_lookup (ploc, GET_MODE (loc), 1);
+ cselib_val *val = cselib_lookup (ploc, GET_MODE (loc), 1,
+ VOIDmode);
/* ??? flag_float_store and volatile mems are never
given values, but we could in theory use them for
if (REG_P (loc)
|| (find_use_val (loc, GET_MODE (loc), cui)
&& cselib_lookup (XEXP (loc, 0),
- get_address_mode (loc), 0)))
+ get_address_mode (loc), 0,
+ GET_MODE (loc))))
return MO_VAL_SET;
}
else
rtx mloc = vloc;
enum machine_mode address_mode = get_address_mode (mloc);
cselib_val *val
- = cselib_lookup (XEXP (mloc, 0), address_mode, 0);
+ = cselib_lookup (XEXP (mloc, 0), address_mode, 0,
+ GET_MODE (mloc));
if (val && !cselib_preserved_value_p (val))
{
micro_operation moa;
preserve_value (val);
- mloc = cselib_subst_to_values (XEXP (mloc, 0));
+ 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,
rtx mloc = oloc;
enum machine_mode address_mode = get_address_mode (mloc);
cselib_val *val
- = cselib_lookup (XEXP (mloc, 0), address_mode, 0);
+ = cselib_lookup (XEXP (mloc, 0), address_mode, 0,
+ GET_MODE (mloc));
if (val && !cselib_preserved_value_p (val))
{
micro_operation moa;
preserve_value (val);
- mloc = cselib_subst_to_values (XEXP (mloc, 0));
+ 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,
case XOR:
case NOT:
case NEG:
+ if (!REG_P (XEXP (src, 0)))
+ return NULL_RTX;
+ break;
case SIGN_EXTEND:
case ZERO_EXTEND:
+ if (!REG_P (XEXP (src, 0)) && !MEM_P (XEXP (src, 0)))
+ return NULL_RTX;
break;
default:
return NULL_RTX;
}
- if (!REG_P (XEXP (src, 0)) || !SCALAR_INT_MODE_P (GET_MODE (src)))
+ if (!SCALAR_INT_MODE_P (GET_MODE (src)) || XEXP (src, 0) == cfa_base_rtx)
return NULL_RTX;
- v = cselib_lookup (XEXP (src, 0), GET_MODE (XEXP (src, 0)), 0);
+ v = cselib_lookup (XEXP (src, 0), GET_MODE (XEXP (src, 0)), 0, VOIDmode);
if (!v || !cselib_preserved_value_p (v))
return NULL_RTX;
rtx mloc = loc;
enum machine_mode address_mode = get_address_mode (mloc);
cselib_val *val = cselib_lookup (XEXP (mloc, 0),
- address_mode, 0);
+ address_mode, 0,
+ GET_MODE (mloc));
if (val && !cselib_preserved_value_p (val))
{
preserve_value (val);
mo.type = MO_VAL_USE;
- mloc = cselib_subst_to_values (XEXP (mloc, 0));
+ 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))
if (GET_CODE (PATTERN (cui->insn)) == COND_EXEC)
{
- cselib_val *oval = cselib_lookup (oloc, GET_MODE (oloc), 0);
+ cselib_val *oval = cselib_lookup (oloc, GET_MODE (oloc), 0, VOIDmode);
gcc_assert (oval != v);
gcc_assert (REG_P (oloc) || MEM_P (oloc));
dataflow_set_copy (&old_out, out);
dataflow_set_copy (out, in);
- for (i = 0; VEC_iterate (micro_operation, VTI (bb)->mos, i, mo); i++)
+ FOR_EACH_VEC_ELT (micro_operation, VTI (bb)->mos, i, mo)
{
rtx insn = mo->insn;
int htabmax = PARAM_VALUE (PARAM_MAX_VARTRACK_SIZE);
bool success = true;
+ timevar_push (TV_VAR_TRACKING_DATAFLOW);
/* Compute reverse completion order of depth first search of the CFG
so that the data-flow runs faster. */
rc_order = XNEWVEC (int, n_basic_blocks - NUM_FIXED_BLOCKS);
{
bb = (basic_block) fibheap_extract_min (worklist);
RESET_BIT (in_worklist, bb->index);
+ gcc_assert (!TEST_BIT (visited, bb->index));
if (!TEST_BIT (visited, bb->index))
{
bool changed;
sbitmap_free (in_worklist);
sbitmap_free (in_pending);
+ timevar_pop (TV_VAR_TRACKING_DATAFLOW);
return success;
}
result = pc_rtx;
break;
}
- else
- {
- result = cselib_expand_value_rtx_cb (loc->loc, regs, max_depth,
- vt_expand_loc_callback,
- data);
- if (result)
- 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;
data.vars = vars;
data.dummy = false;
data.cur_loc_changed = false;
- loc = cselib_expand_value_rtx_cb (loc, scratch_regs, 5,
+ loc = cselib_expand_value_rtx_cb (loc, scratch_regs, 8,
vt_expand_loc_callback, &data);
if (loc && MEM_P (loc))
data.vars = vars;
data.dummy = true;
data.cur_loc_changed = false;
- ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, 5,
+ ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, 8,
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. */
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;
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);
NOTE_DURING_CALL_P (note) = true;
}
else
- note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn);
+ {
+ /* Make sure that the call related notes come first. */
+ while (NEXT_INSN (insn)
+ && NOTE_P (insn)
+ && NOTE_DURING_CALL_P (insn))
+ insn = NEXT_INSN (insn);
+ if (NOTE_P (insn) && NOTE_DURING_CALL_P (insn))
+ note = emit_note_after (NOTE_INSN_VAR_LOCATION, insn);
+ else
+ note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn);
+ }
NOTE_VAR_LOCATION (note) = note_vl;
clear:
{
location_chain lc1, lc2;
- gcc_assert (old_var->n_var_parts == 1);
- gcc_assert (new_var->n_var_parts == 1);
+ 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
dataflow_set_clear (set);
dataflow_set_copy (set, &VTI (bb)->in);
- for (i = 0; VEC_iterate (micro_operation, VTI (bb)->mos, i, mo); i++)
+ FOR_EACH_VEC_ELT (micro_operation, VTI (bb)->mos, i, mo)
{
rtx insn = mo->insn;
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
unsigned int i;
rtx val;
- for (i = 0; VEC_iterate (rtx, preserved_values, i, val); i++)
+ 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);
unsigned int i;
rtx val;
- for (i = 0; VEC_iterate (rtx, preserved_values, i, val); i++)
+ FOR_EACH_VEC_ELT (rtx, preserved_values, i, val)
remove_cselib_value_chains (dv_from_value (val));
gcc_assert (htab_elements (value_chains) == 0);
}
VEC_free (rtx, heap, changed_values_stack);
}
-#ifdef ENABLE_RTL_CHECKING
- pointer_map_destroy (emitted_notes);
-#endif
emit_notes = false;
}
return false;
}
-/* Insert function parameters to IN and OUT sets of ENTRY_BLOCK. */
+/* Insert function parameter PARM in IN and OUT sets of ENTRY_BLOCK. */
static void
-vt_add_function_parameters (void)
+vt_add_function_parameter (tree parm)
{
- tree parm;
-
- for (parm = DECL_ARGUMENTS (current_function_decl);
- parm; parm = TREE_CHAIN (parm))
- {
- rtx decl_rtl = DECL_RTL_IF_SET (parm);
- rtx incoming = DECL_INCOMING_RTL (parm);
- tree decl;
- enum machine_mode mode;
- HOST_WIDE_INT offset;
- dataflow_set *out;
- decl_or_value dv;
-
- if (TREE_CODE (parm) != PARM_DECL)
- continue;
+ rtx decl_rtl = DECL_RTL_IF_SET (parm);
+ rtx incoming = DECL_INCOMING_RTL (parm);
+ tree decl;
+ enum machine_mode mode;
+ HOST_WIDE_INT offset;
+ dataflow_set *out;
+ decl_or_value dv;
- if (!DECL_NAME (parm))
- continue;
+ if (TREE_CODE (parm) != PARM_DECL)
+ return;
- if (!decl_rtl || !incoming)
- continue;
+ if (!decl_rtl || !incoming)
+ return;
- if (GET_MODE (decl_rtl) == BLKmode || GET_MODE (incoming) == BLKmode)
- continue;
+ if (GET_MODE (decl_rtl) == BLKmode || GET_MODE (incoming) == BLKmode)
+ return;
- if (!vt_get_decl_and_offset (incoming, &decl, &offset))
+ if (!vt_get_decl_and_offset (incoming, &decl, &offset))
+ {
+ if (REG_P (incoming) || MEM_P (incoming))
{
- if (REG_P (incoming) || MEM_P (incoming))
- {
- /* This means argument is passed by invisible reference. */
- offset = 0;
- decl = parm;
- incoming = gen_rtx_MEM (GET_MODE (decl_rtl), incoming);
- }
- else
- {
- if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset))
- continue;
- offset += byte_lowpart_offset (GET_MODE (incoming),
- GET_MODE (decl_rtl));
- }
+ /* This means argument is passed by invisible reference. */
+ offset = 0;
+ decl = parm;
+ incoming = gen_rtx_MEM (GET_MODE (decl_rtl), incoming);
}
-
- if (!decl)
- continue;
-
- if (parm != decl)
+ else
{
- /* Assume that DECL_RTL was a pseudo that got spilled to
- memory. The spill slot sharing code will force the
- memory to reference spill_slot_decl (%sfp), so we don't
- match above. That's ok, the pseudo must have referenced
- the entire parameter, so just reset OFFSET. */
- gcc_assert (decl == get_spill_slot_decl (false));
- offset = 0;
+ if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset))
+ return;
+ offset += byte_lowpart_offset (GET_MODE (incoming),
+ GET_MODE (decl_rtl));
}
+ }
- if (!track_loc_p (incoming, parm, offset, false, &mode, &offset))
- continue;
+ if (!decl)
+ return;
- out = &VTI (ENTRY_BLOCK_PTR)->out;
+ if (parm != decl)
+ {
+ /* Assume that DECL_RTL was a pseudo that got spilled to
+ memory. The spill slot sharing code will force the
+ memory to reference spill_slot_decl (%sfp), so we don't
+ match above. That's ok, the pseudo must have referenced
+ the entire parameter, so just reset OFFSET. */
+ gcc_assert (decl == get_spill_slot_decl (false));
+ offset = 0;
+ }
- dv = dv_from_decl (parm);
+ if (!track_loc_p (incoming, parm, offset, false, &mode, &offset))
+ return;
- if (target_for_debug_bind (parm)
- /* We can't deal with these right now, because this kind of
- variable is single-part. ??? We could handle parallels
- that describe multiple locations for the same single
- value, but ATM we don't. */
- && GET_CODE (incoming) != PARALLEL)
- {
- cselib_val *val;
+ out = &VTI (ENTRY_BLOCK_PTR)->out;
- /* ??? We shouldn't ever hit this, but it may happen because
- arguments passed by invisible reference aren't dealt with
- above: incoming-rtl will have Pmode rather than the
- expected mode for the type. */
- if (offset)
- continue;
+ dv = dv_from_decl (parm);
- val = cselib_lookup (var_lowpart (mode, incoming), mode, true);
+ if (target_for_debug_bind (parm)
+ /* We can't deal with these right now, because this kind of
+ variable is single-part. ??? We could handle parallels
+ that describe multiple locations for the same single
+ value, but ATM we don't. */
+ && GET_CODE (incoming) != PARALLEL)
+ {
+ cselib_val *val;
- /* ??? Float-typed values in memory are not handled by
- cselib. */
- if (val)
- {
- preserve_value (val);
- set_variable_part (out, val->val_rtx, dv, offset,
- VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
- dv = dv_from_value (val->val_rtx);
- }
- }
+ /* ??? We shouldn't ever hit this, but it may happen because
+ arguments passed by invisible reference aren't dealt with
+ above: incoming-rtl will have Pmode rather than the
+ expected mode for the type. */
+ if (offset)
+ return;
- if (REG_P (incoming))
- {
- incoming = var_lowpart (mode, incoming);
- gcc_assert (REGNO (incoming) < FIRST_PSEUDO_REGISTER);
- attrs_list_insert (&out->regs[REGNO (incoming)], dv, offset,
- incoming);
- set_variable_part (out, incoming, dv, offset,
- VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
- }
- else if (MEM_P (incoming))
+ val = cselib_lookup (var_lowpart (mode, incoming), mode, true,
+ VOIDmode);
+
+ /* ??? Float-typed values in memory are not handled by
+ cselib. */
+ if (val)
{
- incoming = var_lowpart (mode, incoming);
- set_variable_part (out, incoming, dv, offset,
+ preserve_value (val);
+ set_variable_part (out, val->val_rtx, dv, offset,
VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
+ dv = dv_from_value (val->val_rtx);
}
}
+ if (REG_P (incoming))
+ {
+ incoming = var_lowpart (mode, incoming);
+ gcc_assert (REGNO (incoming) < FIRST_PSEUDO_REGISTER);
+ attrs_list_insert (&out->regs[REGNO (incoming)], dv, offset,
+ incoming);
+ set_variable_part (out, incoming, dv, offset,
+ VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
+ }
+ else if (MEM_P (incoming))
+ {
+ incoming = var_lowpart (mode, incoming);
+ set_variable_part (out, incoming, dv, offset,
+ VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
+ }
+}
+
+/* Insert function parameters to IN and OUT sets of ENTRY_BLOCK. */
+
+static void
+vt_add_function_parameters (void)
+{
+ tree parm;
+
+ for (parm = DECL_ARGUMENTS (current_function_decl);
+ parm; parm = DECL_CHAIN (parm))
+ vt_add_function_parameter (parm);
+
+ if (DECL_HAS_VALUE_EXPR_P (DECL_RESULT (current_function_decl)))
+ {
+ tree vexpr = DECL_VALUE_EXPR (DECL_RESULT (current_function_decl));
+
+ if (TREE_CODE (vexpr) == INDIRECT_REF)
+ vexpr = TREE_OPERAND (vexpr, 0);
+
+ if (TREE_CODE (vexpr) == PARM_DECL
+ && DECL_ARTIFICIAL (vexpr)
+ && !DECL_IGNORED_P (vexpr)
+ && DECL_NAMELESS (vexpr))
+ vt_add_function_parameter (vexpr);
+ }
+
if (MAY_HAVE_DEBUG_INSNS)
{
cselib_preserve_only_values ();
#ifdef FRAME_POINTER_CFA_OFFSET
cfa_base_rtx = frame_pointer_rtx;
+ cfa_base_offset = -FRAME_POINTER_CFA_OFFSET (current_function_decl);
#else
cfa_base_rtx = arg_pointer_rtx;
+ cfa_base_offset = -ARG_POINTER_CFA_OFFSET (current_function_decl);
#endif
if (cfa_base_rtx == hard_frame_pointer_rtx
|| !fixed_regs[REGNO (cfa_base_rtx)])
if (!MAY_HAVE_DEBUG_INSNS)
return;
- val = cselib_lookup (cfa_base_rtx, GET_MODE (cfa_base_rtx), 1);
+ /* Tell alias analysis that cfa_base_rtx should share
+ find_base_term value with stack pointer or hard frame pointer. */
+ vt_equate_reg_base_value (cfa_base_rtx,
+ frame_pointer_needed
+ ? hard_frame_pointer_rtx : stack_pointer_rtx);
+ val = cselib_lookup_from_insn (cfa_base_rtx, GET_MODE (cfa_base_rtx), 1,
+ VOIDmode, get_insns ());
preserve_value (val);
- cselib_preserve_cfa_base_value (val);
- val->locs->setting_insn = get_insns ();
+ cselib_preserve_cfa_base_value (val, REGNO (cfa_base_rtx));
var_reg_decl_set (&VTI (ENTRY_BLOCK_PTR)->out, cfa_base_rtx,
VAR_INIT_STATUS_INITIALIZED, dv_from_value (val->val_rtx),
0, NULL_RTX, INSERT);
dump_flow_info (dump_file, dump_flags);
}
+ timevar_push (TV_VAR_TRACKING_EMIT);
vt_emit_notes ();
+ timevar_pop (TV_VAR_TRACKING_EMIT);
vt_finalize ();
vt_debug_insns_local (false);
static bool
gate_handle_var_tracking (void)
{
- return (flag_var_tracking);
+ return (flag_var_tracking && !targetm.delay_vartrack);
}