/* Common subexpression elimination for GNU compiler.
- Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- 2011 Free Software Foundation, Inc.
+ Copyright (C) 1987-2013 Free Software Foundation, Inc.
This file is part of GCC.
#include "expr.h"
#include "diagnostic-core.h"
#include "toplev.h"
-#include "output.h"
#include "ggc.h"
-#include "timevar.h"
#include "except.h"
#include "target.h"
#include "params.h"
#include "tree-pass.h"
#include "df.h"
#include "dbgcnt.h"
+#include "pointer-set.h"
/* The basic idea of common subexpression elimination is to go
through the code, keeping a record of expressions that would
a cost of 2. Aside from these special cases, call `rtx_cost'. */
#define CHEAP_REGNO(N) \
- (REGNO_PTR_FRAME_P(N) \
+ (REGNO_PTR_FRAME_P (N) \
|| (HARD_REGISTER_NUM_P (N) \
&& FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS))
static void invalidate_from_sets_and_clobbers (rtx);
static rtx cse_process_notes (rtx, rtx, bool *);
static void cse_extended_basic_block (struct cse_basic_block_data *);
-static void count_reg_usage (rtx, int *, rtx, int);
static int check_for_label_ref (rtx *, void *);
extern void dump_class (struct table_elt*);
static void get_cse_reg_info_1 (unsigned int regno);
static const struct rtl_hooks cse_rtl_hooks = RTL_HOOKS_INITIALIZER;
\f
-/* Nonzero if X has the form (PLUS frame-pointer integer). We check for
- virtual regs here because the simplify_*_operation routines are called
- by integrate.c, which is called before virtual register instantiation. */
+/* Nonzero if X has the form (PLUS frame-pointer integer). */
static bool
fixed_base_plus_p (rtx x)
return true;
if (x == arg_pointer_rtx && fixed_regs[ARG_POINTER_REGNUM])
return true;
- if (REGNO (x) >= FIRST_VIRTUAL_REGISTER
- && REGNO (x) <= LAST_VIRTUAL_REGISTER)
- return true;
return false;
case PLUS:
rtx lower_exp = NULL_RTX, upper_exp = NULL_RTX;
unsigned lower_old, upper_old;
+ /* CONST_INT is used for CC modes, but we should leave those alone. */
+ if (GET_MODE_CLASS (mode) == MODE_CC)
+ return NULL_RTX;
+
+ gcc_assert (SCALAR_INT_MODE_P (mode));
if (!compute_const_anchors (src_const, &lower_base, &lower_offs,
&upper_base, &upper_offs))
return NULL_RTX;
}
}
\f
-/* Function called for each rtx to check whether true dependence exist. */
+/* Function called for each rtx to check whether an anti dependence exist. */
struct check_dependence_data
{
enum machine_mode mode;
{
struct check_dependence_data *d = (struct check_dependence_data *) data;
if (*x && MEM_P (*x))
- return canon_true_dependence (d->exp, d->mode, d->addr, *x, NULL_RTX);
+ return canon_anti_dependence (*x, true, d->exp, d->mode, d->addr);
else
return 0;
}
unsigned hash;
struct table_elt *p, *next;
int in_table = 0;
+ hard_reg_set_iterator hrsi;
/* Go through all the hard registers. For each that is clobbered in
a CALL_INSN, remove the register from quantity chains and update
reg_tick if defined. Also see if any of these registers is currently
in the table. */
-
- for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
- {
- delete_reg_equiv (regno);
- if (REG_TICK (regno) >= 0)
- {
- REG_TICK (regno)++;
- SUBREG_TICKED (regno) = -1;
- }
-
- in_table |= (TEST_HARD_REG_BIT (hard_regs_in_table, regno) != 0);
- }
+ EXECUTE_IF_SET_IN_HARD_REG_SET (regs_invalidated_by_call, 0, regno, hrsi)
+ {
+ delete_reg_equiv (regno);
+ if (REG_TICK (regno) >= 0)
+ {
+ REG_TICK (regno)++;
+ SUBREG_TICKED (regno) = -1;
+ }
+ in_table |= (TEST_HARD_REG_BIT (hard_regs_in_table, regno) != 0);
+ }
/* In the case where we have no call-clobbered hard registers in the
table, we are done. Otherwise, scan the table and remove any
+ (unsigned int) INTVAL (x));
return hash;
+ case CONST_WIDE_INT:
+ {
+ int i;
+ for (i = 0; i < CONST_WIDE_INT_NUNITS (x); i++)
+ hash += CONST_WIDE_INT_ELT (x, i);
+ }
+ return hash;
+
case CONST_DOUBLE:
/* This is like the general case, except that it only counts
the integers representing the constant. */
hash += (unsigned int) code + (unsigned int) GET_MODE (x);
- if (GET_MODE (x) != VOIDmode)
- hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
- else
+ if (TARGET_SUPPORTS_WIDE_INT == 0 && GET_MODE (x) == VOIDmode)
hash += ((unsigned int) CONST_DOUBLE_LOW (x)
+ (unsigned int) CONST_DOUBLE_HIGH (x));
+ else
+ hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
return hash;
case CONST_FIXED:
Store 1 in DO_NOT_RECORD_P if any subexpression is volatile.
If HASH_ARG_IN_MEMORY_P is not NULL, store 1 in it if X contains
- a MEM rtx which does not have the RTX_UNCHANGING_P bit set.
+ a MEM rtx which does not have the MEM_READONLY_P flag set.
Note that cse_insn knows that the hash code of a MEM expression
is just (int) MEM plus the hash code of the address. */
/* Hash an rtx X for cse via hash_rtx.
Stores 1 in do_not_record if any subexpression is volatile.
Stores 1 in hash_arg_in_memory if X contains a mem rtx which
- does not have the RTX_UNCHANGING_P bit set. */
+ does not have the MEM_READONLY_P flag set. */
static inline unsigned
canon_hash (rtx x, enum machine_mode mode)
{
case PC:
case CC0:
- case CONST_INT:
- case CONST_DOUBLE:
- case CONST_FIXED:
+ CASE_CONST_UNIQUE:
return x == y;
case LABEL_REF:
case PC:
case CC0:
case CONST:
- case CONST_INT:
- case CONST_DOUBLE:
- case CONST_FIXED:
- case CONST_VECTOR:
+ CASE_CONST_ANY:
case SYMBOL_REF:
case LABEL_REF:
case ADDR_VEC:
enum machine_mode *pmode1, enum machine_mode *pmode2)
{
rtx arg1, arg2;
+ struct pointer_set_t *visited = NULL;
+ /* Set nonzero when we find something of interest. */
+ rtx x = NULL;
arg1 = *parg1, arg2 = *parg2;
while (arg2 == CONST0_RTX (GET_MODE (arg1)))
{
- /* Set nonzero when we find something of interest. */
- rtx x = 0;
int reverse_code = 0;
struct table_elt *p = 0;
+ /* Remember state from previous iteration. */
+ if (x)
+ {
+ if (!visited)
+ visited = pointer_set_create ();
+ pointer_set_insert (visited, x);
+ x = 0;
+ }
+
/* If arg1 is a COMPARE, extract the comparison arguments from it.
On machines with CC0, this is the only case that can occur, since
fold_rtx will return the COMPARE or item being compared with zero
if (! exp_equiv_p (p->exp, p->exp, 1, false))
continue;
- /* If it's the same comparison we're already looking at, skip it. */
- if (COMPARISON_P (p->exp)
- && XEXP (p->exp, 0) == arg1
- && XEXP (p->exp, 1) == arg2)
+ /* If it's a comparison we've used before, skip it. */
+ if (visited && pointer_set_contains (visited, p->exp))
continue;
if (GET_CODE (p->exp) == COMPARE
*pmode1 = GET_MODE (arg1), *pmode2 = GET_MODE (arg2);
*parg1 = fold_rtx (arg1, 0), *parg2 = fold_rtx (arg2, 0);
+ if (visited)
+ pointer_set_destroy (visited);
return code;
}
\f
return x;
case CONST:
- case CONST_INT:
- case CONST_DOUBLE:
- case CONST_FIXED:
- case CONST_VECTOR:
+ CASE_CONST_ANY:
case SYMBOL_REF:
case LABEL_REF:
case REG:
break;
case CONST:
- case CONST_INT:
+ CASE_CONST_ANY:
case SYMBOL_REF:
case LABEL_REF:
- case CONST_DOUBLE:
- case CONST_FIXED:
- case CONST_VECTOR:
const_arg = folded_arg;
break;
break;
new_rtx = simplify_unary_operation (code, mode,
- const_arg0 ? const_arg0 : folded_arg0,
- mode_arg0);
+ const_arg0 ? const_arg0 : folded_arg0,
+ mode_arg0);
}
break;
}
{
- rtx op0 = const_arg0 ? const_arg0 : folded_arg0;
- rtx op1 = const_arg1 ? const_arg1 : folded_arg1;
- new_rtx = simplify_relational_operation (code, mode, mode_arg0, op0, op1);
+ rtx op0 = const_arg0 ? const_arg0 : copy_rtx (folded_arg0);
+ rtx op1 = const_arg1 ? const_arg1 : copy_rtx (folded_arg1);
+ new_rtx = simplify_relational_operation (code, mode, mode_arg0,
+ op0, op1);
}
break;
/* See if we previously assigned a constant value to this SUBREG. */
if ((new_rtx = lookup_as_function (x, CONST_INT)) != 0
+ || (new_rtx = lookup_as_function (x, CONST_WIDE_INT)) != 0
|| (new_rtx = lookup_as_function (x, CONST_DOUBLE)) != 0
|| (new_rtx = lookup_as_function (x, CONST_FIXED)) != 0)
return new_rtx;
/* Set what we are trying to extend and the operation it might
have been extended with. */
- memset (memory_extend_rtx, 0, sizeof(*memory_extend_rtx));
+ memset (memory_extend_rtx, 0, sizeof (*memory_extend_rtx));
PUT_CODE (memory_extend_rtx, LOAD_EXTEND_OP (mode));
XEXP (memory_extend_rtx, 0) = src;
}
/* If this is a single SET, we are setting a register, and we have an
- equivalent constant, we want to add a REG_NOTE. We don't want
- to write a REG_EQUAL note for a constant pseudo since verifying that
- that pseudo hasn't been eliminated is a pain. Such a note also
- won't help anything.
+ equivalent constant, we want to add a REG_EQUAL note if the constant
+ is different from the source. We don't want to do it for a constant
+ pseudo since verifying that this pseudo hasn't been eliminated is a
+ pain; moreover such a note won't help anything.
Avoid a REG_EQUAL note for (CONST (MINUS (LABEL_REF) (LABEL_REF)))
which can be created for a reference to a compile time computable
entry in a jump table. */
-
- if (n_sets == 1 && src_const && REG_P (dest)
+ if (n_sets == 1
+ && REG_P (dest)
+ && src_const
&& !REG_P (src_const)
- && ! (GET_CODE (src_const) == CONST
- && GET_CODE (XEXP (src_const, 0)) == MINUS
- && GET_CODE (XEXP (XEXP (src_const, 0), 0)) == LABEL_REF
- && GET_CODE (XEXP (XEXP (src_const, 0), 1)) == LABEL_REF))
+ && !(GET_CODE (src_const) == SUBREG
+ && REG_P (SUBREG_REG (src_const)))
+ && !(GET_CODE (src_const) == CONST
+ && GET_CODE (XEXP (src_const, 0)) == MINUS
+ && GET_CODE (XEXP (XEXP (src_const, 0), 0)) == LABEL_REF
+ && GET_CODE (XEXP (XEXP (src_const, 0), 1)) == LABEL_REF)
+ && !rtx_equal_p (src, src_const))
{
- /* We only want a REG_EQUAL note if src_const != src. */
- if (! rtx_equal_p (src, src_const))
- {
- /* Make sure that the rtx is not shared. */
- src_const = copy_rtx (src_const);
+ /* Make sure that the rtx is not shared. */
+ src_const = copy_rtx (src_const);
- /* Record the actual constant value in a REG_EQUAL note,
- making a new one if one does not already exist. */
- set_unique_reg_note (insn, REG_EQUAL, src_const);
- df_notes_rescan (insn);
- }
+ /* Record the actual constant value in a REG_EQUAL note,
+ making a new one if one does not already exist. */
+ set_unique_reg_note (insn, REG_EQUAL, src_const);
+ df_notes_rescan (insn);
}
/* Now deal with the destination. */
&& CONST_INT_P (width)
&& INTVAL (width) < HOST_BITS_PER_WIDE_INT
&& ! (INTVAL (src_const)
- & ((HOST_WIDE_INT) (-1) << INTVAL (width))))
+ & (HOST_WIDE_INT_M1U << INTVAL (width))))
/* Exception: if the value is constant,
and it won't be truncated, record it. */
;
invalidate (XEXP (dest, 0), GET_MODE (dest));
}
- /* A volatile ASM invalidates everything. */
+ /* A volatile ASM or an UNSPEC_VOLATILE invalidates everything. */
if (NONJUMP_INSN_P (insn)
- && GET_CODE (PATTERN (insn)) == ASM_OPERANDS
- && MEM_VOLATILE_P (PATTERN (insn)))
+ && volatile_insn_p (PATTERN (insn)))
flush_hash_table ();
/* Don't cse over a call to setjmp; on some machines (eg VAX)
switch (code)
{
- case CONST_INT:
case CONST:
case SYMBOL_REF:
case LABEL_REF:
- case CONST_DOUBLE:
- case CONST_FIXED:
- case CONST_VECTOR:
+ CASE_CONST_ANY:
case PC:
case CC0:
case LO_SUM:
return x;
case EXPR_LIST:
- case INSN_LIST:
if (REG_NOTE_KIND (x) == REG_EQUAL)
XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX, changed);
+ /* Fall through. */
+
+ case INSN_LIST:
+ case INT_LIST:
if (XEXP (x, 1))
XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX, changed);
return x;
edge e;
int path_size;
- SET_BIT (cse_visited_basic_blocks, first_bb->index);
+ bitmap_set_bit (cse_visited_basic_blocks, first_bb->index);
/* See if there is a previous path. */
path_size = data->path_size;
&& e == BRANCH_EDGE (previous_bb_in_path))
{
bb = FALLTHRU_EDGE (previous_bb_in_path)->dest;
- if (bb != EXIT_BLOCK_PTR
+ if (bb != EXIT_BLOCK_PTR_FOR_FN (cfun)
&& single_pred_p (bb)
/* We used to assert here that we would only see blocks
that we have not visited yet. But we may end up
We still want to visit each basic block only once, so
halt the path here if we have already visited BB. */
- && !TEST_BIT (cse_visited_basic_blocks, bb->index))
+ && !bitmap_bit_p (cse_visited_basic_blocks, bb->index))
{
- SET_BIT (cse_visited_basic_blocks, bb->index);
+ bitmap_set_bit (cse_visited_basic_blocks, bb->index);
data->path[path_size++].bb = bb;
break;
}
if (e
&& !((e->flags & EDGE_ABNORMAL_CALL) && cfun->has_nonlocal_label)
- && e->dest != EXIT_BLOCK_PTR
+ && e->dest != EXIT_BLOCK_PTR_FOR_FN (cfun)
&& single_pred_p (e->dest)
/* Avoid visiting basic blocks twice. The large comment
above explains why this can happen. */
- && !TEST_BIT (cse_visited_basic_blocks, e->dest->index))
+ && !bitmap_bit_p (cse_visited_basic_blocks, e->dest->index))
{
basic_block bb2 = e->dest;
- SET_BIT (cse_visited_basic_blocks, bb2->index);
+ bitmap_set_bit (cse_visited_basic_blocks, bb2->index);
data->path[path_size++].bb = bb2;
bb = bb2;
}
/* If we truncate the path, we must also reset the
visited bit on the remaining blocks in the path,
or we will never visit them at all. */
- RESET_BIT (cse_visited_basic_blocks,
+ bitmap_clear_bit (cse_visited_basic_blocks,
ebb_data->path[path_size].bb->index);
ebb_data->path[path_size].bb = NULL;
}
int i, n_blocks;
df_set_flags (DF_LR_RUN_DCE);
+ df_note_add_problem ();
df_analyze ();
df_set_flags (DF_DEFER_INSN_RESCAN);
/* Set up the table of already visited basic blocks. */
cse_visited_basic_blocks = sbitmap_alloc (last_basic_block);
- sbitmap_zero (cse_visited_basic_blocks);
+ bitmap_clear (cse_visited_basic_blocks);
/* Loop over basic blocks in reverse completion order (RPO),
excluding the ENTRY and EXIT blocks. */
{
bb = BASIC_BLOCK (rc_order[i++]);
}
- while (TEST_BIT (cse_visited_basic_blocks, bb->index)
+ while (bitmap_bit_p (cse_visited_basic_blocks, bb->index)
&& i < n_blocks);
/* Find all paths starting with BB, and process them. */
case PC:
case CC0:
case CONST:
- case CONST_INT:
- case CONST_DOUBLE:
- case CONST_FIXED:
- case CONST_VECTOR:
+ CASE_CONST_ANY:
case SYMBOL_REF:
case LABEL_REF:
return;
case CALL_INSN:
case INSN:
case JUMP_INSN:
- /* We expect dest to be NULL_RTX here. If the insn may trap,
+ /* We expect dest to be NULL_RTX here. If the insn may throw,
or if it cannot be deleted due to side-effects, mark this fact
by setting DEST to pc_rtx. */
- if (insn_could_throw_p (x) || side_effects_p (PATTERN (x)))
+ if ((!cfun->can_delete_dead_exceptions && !insn_nothrow_p (x))
+ || side_effects_p (PATTERN (x)))
dest = pc_rtx;
if (code == CALL_INSN)
count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, dest, incr);
return;
case INSN_LIST:
+ case INT_LIST:
gcc_unreachable ();
default:
insn_live_p (rtx insn, int *counts)
{
int i;
- if (insn_could_throw_p (insn))
+ if (!cfun->can_delete_dead_exceptions && !insn_nothrow_p (insn))
return true;
else if (GET_CODE (PATTERN (insn)) == SET)
return set_live_p (PATTERN (insn), insn, counts);
continue;
if (EDGE_COUNT (e->dest->preds) != 1
- || e->dest == EXIT_BLOCK_PTR
+ || e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun)
/* Avoid endless recursion on unreachable blocks. */
|| e->dest == orig_bb)
continue;
return 0;
}
-struct rtl_opt_pass pass_cse =
+namespace {
+
+const pass_data pass_data_cse =
{
- {
- RTL_PASS,
- "cse1", /* name */
- gate_handle_cse, /* gate */
- rest_of_handle_cse, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- TV_CSE, /* tv_id */
- 0, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_df_finish | TODO_verify_rtl_sharing |
- TODO_ggc_collect |
- TODO_verify_flow, /* todo_flags_finish */
- }
+ RTL_PASS, /* type */
+ "cse1", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ true, /* has_gate */
+ true, /* has_execute */
+ TV_CSE, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ ( TODO_df_finish | TODO_verify_rtl_sharing
+ | TODO_verify_flow ), /* todo_flags_finish */
};
+class pass_cse : public rtl_opt_pass
+{
+public:
+ pass_cse (gcc::context *ctxt)
+ : rtl_opt_pass (pass_data_cse, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ bool gate () { return gate_handle_cse (); }
+ unsigned int execute () { return rest_of_handle_cse (); }
+
+}; // class pass_cse
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_cse (gcc::context *ctxt)
+{
+ return new pass_cse (ctxt);
+}
+
static bool
gate_handle_cse2 (void)
}
-struct rtl_opt_pass pass_cse2 =
+namespace {
+
+const pass_data pass_data_cse2 =
{
- {
- RTL_PASS,
- "cse2", /* name */
- gate_handle_cse2, /* gate */
- rest_of_handle_cse2, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- TV_CSE2, /* tv_id */
- 0, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_df_finish | TODO_verify_rtl_sharing |
- TODO_ggc_collect |
- TODO_verify_flow /* todo_flags_finish */
- }
+ RTL_PASS, /* type */
+ "cse2", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ true, /* has_gate */
+ true, /* has_execute */
+ TV_CSE2, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ ( TODO_df_finish | TODO_verify_rtl_sharing
+ | TODO_verify_flow ), /* todo_flags_finish */
};
+class pass_cse2 : public rtl_opt_pass
+{
+public:
+ pass_cse2 (gcc::context *ctxt)
+ : rtl_opt_pass (pass_data_cse2, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ bool gate () { return gate_handle_cse2 (); }
+ unsigned int execute () { return rest_of_handle_cse2 (); }
+
+}; // class pass_cse2
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_cse2 (gcc::context *ctxt)
+{
+ return new pass_cse2 (ctxt);
+}
+
static bool
gate_handle_cse_after_global_opts (void)
{
return 0;
}
-struct rtl_opt_pass pass_cse_after_global_opts =
+namespace {
+
+const pass_data pass_data_cse_after_global_opts =
{
- {
- RTL_PASS,
- "cse_local", /* name */
- gate_handle_cse_after_global_opts, /* gate */
- rest_of_handle_cse_after_global_opts, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- TV_CSE, /* tv_id */
- 0, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_df_finish | TODO_verify_rtl_sharing |
- TODO_ggc_collect |
- TODO_verify_flow /* todo_flags_finish */
- }
+ RTL_PASS, /* type */
+ "cse_local", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ true, /* has_gate */
+ true, /* has_execute */
+ TV_CSE, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ ( TODO_df_finish | TODO_verify_rtl_sharing
+ | TODO_verify_flow ), /* todo_flags_finish */
};
+
+class pass_cse_after_global_opts : public rtl_opt_pass
+{
+public:
+ pass_cse_after_global_opts (gcc::context *ctxt)
+ : rtl_opt_pass (pass_data_cse_after_global_opts, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ bool gate () { return gate_handle_cse_after_global_opts (); }
+ unsigned int execute () {
+ return rest_of_handle_cse_after_global_opts ();
+ }
+
+}; // class pass_cse_after_global_opts
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_cse_after_global_opts (gcc::context *ctxt)
+{
+ return new pass_cse_after_global_opts (ctxt);
+}