#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 "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 *);
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,
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);
return loc;
case MEM:
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);
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;
/* 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;
/* 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
- && s1var->var_part[0].loc_chain);
+ gcc_checking_assert (s1var->n_var_parts
+ && s1var->var_part[0].loc_chain);
if (!onepart)
return variable_union (s1var, dst);
- gcc_assert (s1var->n_var_parts == 1
- && 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))
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;
}
}
{
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)
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);
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
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);
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;
}
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;
+
+/* Strip REG_POINTER from REGs and MEM_POINTER from MEMs in order to
+ avoid differences in commutative operand simplification. */
+static rtx
+strip_pointer_flags (rtx x, const_rtx old_rtx ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED)
+{
+ if (REG_P (x) && REG_POINTER (x))
+ return gen_rtx_REG (GET_MODE (x), REGNO (x));
+ if (MEM_P (x) && MEM_POINTER (x))
+ return gen_rtx_MEM (GET_MODE (x), XEXP (x, 0));
+ return NULL_RTX;
+}
#endif
/* Emit the NOTE_INSN_VAR_LOCATION for variable *VARP. DATA contains
rtx pnote = (rtx) *note_slot;
if (!var->cur_loc_changed && (pnote || PAT_VAR_LOCATION_LOC (note_vl)))
{
+ rtx old_vl, new_vl;
gcc_assert (pnote);
- gcc_assert (rtx_equal_p (PAT_VAR_LOCATION_LOC (pnote),
- PAT_VAR_LOCATION_LOC (note_vl)));
+ old_vl = PAT_VAR_LOCATION_LOC (pnote);
+ new_vl = PAT_VAR_LOCATION_LOC (note_vl);
+ if (!rtx_equal_p (old_vl, new_vl))
+ {
+ /* There might be differences caused by REG_POINTER
+ differences. REG_POINTER affects
+ swap_commutative_operands_p. */
+ old_vl = simplify_replace_fn_rtx (old_vl, NULL_RTX,
+ strip_pointer_flags, NULL);
+ new_vl = simplify_replace_fn_rtx (new_vl, NULL_RTX,
+ strip_pointer_flags, NULL);
+ gcc_assert (rtx_equal_p (old_vl, new_vl));
+ PAT_VAR_LOCATION_LOC (note_vl) = new_vl;
+ }
}
*note_slot = (void *) note_vl;
}
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:
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;
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);
}
tree parm;
for (parm = DECL_ARGUMENTS (current_function_decl);
- parm; parm = TREE_CHAIN (parm))
+ parm; parm = DECL_CHAIN (parm))
{
rtx decl_rtl = DECL_RTL_IF_SET (parm);
rtx incoming = DECL_INCOMING_RTL (parm);
#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;
+ /* 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,
get_insns ());
preserve_value (val);
- cselib_preserve_cfa_base_value (val);
+ 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);