/* Global common subexpression elimination/Partial redundancy elimination
and global constant/copy propagation for GNU compiler.
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
- 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+ 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
This file is part of GCC.
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
-#include "real.h"
#include "insn-config.h"
#include "recog.h"
#include "basic-block.h"
#include "hashtab.h"
#include "df.h"
#include "dbgcnt.h"
-
-/* Propagate flow information through back edges and thus enable PRE's
- moving loop invariant calculations out of loops.
-
- Originally this tended to create worse overall code, but several
- improvements during the development of PRE seem to have made following
- back edges generally a win.
-
- Note much of the loop invariant code motion done here would normally
- be done by loop.c, which has more heuristics for when to move invariants
- out of loops. At some point we might need to move some of those
- heuristics into gcse.c. */
+#include "target.h"
/* We support GCSE via Partial Redundancy Elimination. PRE optimizations
- are a superset of those done by GCSE.
+ are a superset of those done by classic GCSE.
We perform the following steps:
conditional jumps if the condition can be computed from a value of
an incoming edge.
- 5) Perform store motion.
-
Two passes of copy/constant propagation are done because the first one
enables more GCSE and the second one helps to clean up the copies that
GCSE creates. This is needed more for PRE than for Classic because Classic
(set (pseudo-reg) (expression)).
Function want_to_gcse_p says what these are.
- In addition, expressions in REG_EQUAL notes are candidates for GXSE-ing.
+ In addition, expressions in REG_EQUAL notes are candidates for GCSE-ing.
This allows PRE to hoist expressions that are expressed in multiple insns,
- such as comprex address calculations (e.g. for PIC code, or loads with a
- high part and as lowe part).
+ such as complex address calculations (e.g. for PIC code, or loads with a
+ high part and a low part).
PRE handles moving invariant expressions out of loops (by treating them as
partially redundant).
- Eventually it would be nice to replace cse.c/gcse.c with SSA (static single
- assignment) based GVN (global value numbering). L. T. Simpson's paper
- (Rice University) on value numbering is a useful reference for this.
-
**********************
We used to support multiple passes but there are diminishing returns in
argue it is not. The number of iterations for the algorithm to converge
is typically 2-4 so I don't view it as that expensive (relatively speaking).
- PRE GCSE depends heavily on the second CSE pass to clean up the copies
+ PRE GCSE depends heavily on the second CPROP pass to clean up the copies
we create. To make an expression reach the place where it's redundant,
the result of the expression is copied to a new register, and the redundant
expression is deleted by replacing it with this new register. Classic GCSE
static void record_last_mem_set_info (rtx);
static void record_last_set_info (rtx, const_rtx, void *);
static void compute_hash_table (struct hash_table_d *);
-static void alloc_hash_table (int, struct hash_table_d *, int);
+static void alloc_hash_table (struct hash_table_d *, int);
static void free_hash_table (struct hash_table_d *);
static void compute_hash_table_work (struct hash_table_d *);
static void dump_hash_table (FILE *, const char *, struct hash_table_d *);
alloc_gcse_mem (void)
{
/* Allocate vars to track sets of regs. */
- reg_set_bitmap = BITMAP_ALLOC (NULL);
+ reg_set_bitmap = ALLOC_REG_SET (NULL);
/* Allocate array to keep a list of insns which modify memory in each
basic block. */
if (antloc)
for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
{
- SET_BIT (antloc[BLOCK_NUM (occr->insn)], indx);
+ SET_BIT (antloc[BLOCK_FOR_INSN (occr->insn)->index], indx);
/* While we're scanning the table, this is a good place to
initialize this. */
if (comp)
for (occr = expr->avail_occr; occr != NULL; occr = occr->next)
{
- SET_BIT (comp[BLOCK_NUM (occr->insn)], indx);
+ SET_BIT (comp[BLOCK_FOR_INSN (occr->insn)->index], indx);
/* While we're scanning the table, this is a good place to
initialize this. */
/* Return true if we can assign X to a pseudo register such that the
resulting insn does not result in clobbering a hard register as a
side-effect.
+
+ Additionally, if the target requires it, check that the resulting insn
+ can be copied. If it cannot, this means that X is special and probably
+ has hidden side-effects we don't want to mess with.
+
This function is typically used by code motion passes, to verify
that it is safe to insert an insn without worrying about clobbering
maybe live hard regs. */
valid. */
PUT_MODE (SET_DEST (PATTERN (test_insn)), GET_MODE (x));
SET_SRC (PATTERN (test_insn)) = x;
- return ((icode = recog (PATTERN (test_insn), test_insn, &num_clobbers)) >= 0
- && (num_clobbers == 0 || ! added_clobbers_hard_reg_p (icode)));
+
+ icode = recog (PATTERN (test_insn), test_insn, &num_clobbers);
+ if (icode < 0)
+ return false;
+
+ if (num_clobbers > 0 && added_clobbers_hard_reg_p (icode))
+ return false;
+
+ if (targetm.cannot_copy_insn_p && targetm.cannot_copy_insn_p (test_insn))
+ return false;
+
+ return true;
}
/* Return nonzero if the operands of expression X are unchanged from the
{
antic_occr = cur_expr->antic_occr;
- if (antic_occr && BLOCK_NUM (antic_occr->insn) != BLOCK_NUM (insn))
+ if (antic_occr
+ && BLOCK_FOR_INSN (antic_occr->insn) != BLOCK_FOR_INSN (insn))
antic_occr = NULL;
if (antic_occr)
{
avail_occr = cur_expr->avail_occr;
- if (avail_occr && BLOCK_NUM (avail_occr->insn) == BLOCK_NUM (insn))
+ if (avail_occr
+ && BLOCK_FOR_INSN (avail_occr->insn) == BLOCK_FOR_INSN (insn))
{
/* Found another instance of the expression in the same basic block.
Prefer this occurrence to the currently recorded one. We want
/* Now record the occurrence. */
cur_occr = cur_expr->avail_occr;
- if (cur_occr && BLOCK_NUM (cur_occr->insn) == BLOCK_NUM (insn))
+ if (cur_occr
+ && BLOCK_FOR_INSN (cur_occr->insn) == BLOCK_FOR_INSN (insn))
{
/* Found another instance of the expression in the same basic block.
Prefer this occurrence to the currently recorded one. We want
{
/* Consider a COMPARE of two integers constant. */
if (GET_CODE (x) == COMPARE
- && GET_CODE (XEXP (x, 0)) == CONST_INT
- && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ && CONST_INT_P (XEXP (x, 0))
+ && CONST_INT_P (XEXP (x, 1)))
return true;
/* Consider a COMPARE of the same registers is a constant
/* Don't GCSE something if we can't do a reg/reg copy. */
&& can_copy_p (GET_MODE (dest))
/* GCSE commonly inserts instruction after the insn. We can't
- do that easily for EH_REGION notes so disable GCSE on these
- for now. */
- && !find_reg_note (insn, REG_EH_REGION, NULL_RTX)
+ do that easily for EH edges so disable GCSE on these for now. */
+ /* ??? We can now easily create new EH landing pads at the
+ gimple level, for splitting edges; there's no reason we
+ can't do the same thing at the rtl level. */
+ && !can_throw_internal (insn)
/* Is SET_SRC something we want to gcse? */
&& want_to_gcse_p (src)
/* Don't CSE a nop. */
/* Don't GCSE something if we can't do a reg/reg copy. */
&& can_copy_p (GET_MODE (src))
/* GCSE commonly inserts instruction after the insn. We can't
- do that easily for EH_REGION notes so disable GCSE on these
- for now. */
- && ! find_reg_note (insn, REG_EH_REGION, NULL_RTX)
+ do that easily for EH edges so disable GCSE on these for now. */
+ && !can_throw_internal (insn)
/* Is SET_DEST something we want to gcse? */
&& want_to_gcse_p (dest)
/* Don't CSE a nop. */
dest_addr = get_addr (XEXP (dest, 0));
dest_addr = canon_rtx (dest_addr);
insn = (rtx) v_insn;
- bb = BLOCK_NUM (insn);
+ bb = BLOCK_FOR_INSN (insn)->index;
canon_modify_mem_list[bb] =
alloc_EXPR_LIST (VOIDmode, dest_addr, canon_modify_mem_list[bb]);
static void
record_last_mem_set_info (rtx insn)
{
- int bb = BLOCK_NUM (insn);
+ int bb = BLOCK_FOR_INSN (insn)->index;
/* load_killed_in_block_p will handle the case of calls clobbering
everything. */
}
/* Allocate space for the set/expr hash TABLE.
- N_INSNS is the number of instructions in the function.
It is used to determine the number of buckets to use.
SET_P determines whether set or expression table will
be created. */
static void
-alloc_hash_table (int n_insns, struct hash_table_d *table, int set_p)
+alloc_hash_table (struct hash_table_d *table, int set_p)
{
int n;
- table->size = n_insns / 4;
+ n = get_max_insn_count ();
+
+ table->size = n / 4;
if (table->size < 11)
table->size = 11;
/* Now iterate over the blocks which have memory modifications
but which do not have any calls. */
- EXECUTE_IF_AND_COMPL_IN_BITMAP (modify_mem_list_set,
+ EXECUTE_IF_AND_COMPL_IN_BITMAP (modify_mem_list_set,
blocks_with_calls,
0, bb_index, bi)
{
with our replacement. */
if (note != 0 && REG_NOTE_KIND (note) == REG_EQUAL)
set_unique_reg_note (insn, REG_EQUAL,
- simplify_replace_rtx (XEXP (note, 0), from,
- copy_rtx (to)));
+ simplify_replace_rtx (XEXP (note, 0), from, to));
if (!success && set && reg_mentioned_p (from, SET_SRC (set)))
{
/* If above failed and this is a single set, try to simplify the source of
which contains INSN. */
while (set)
{
- if (TEST_BIT (cprop_avin[BLOCK_NUM (insn)], set->bitmap_index))
+ if (TEST_BIT (cprop_avin[BLOCK_FOR_INSN (insn)->index],
+ set->bitmap_index))
break;
set = next_set (regno, set);
}
}
}
+ if (changed && DEBUG_INSN_P (insn))
+ return 0;
+
return changed;
}
struct reg_use *reg_used;
bool changed = false;
- cselib_init (false);
+ cselib_init (0);
FOR_EACH_BB (bb)
{
FOR_BB_INSNS (bb, insn)
for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
{
removed_p = 0;
-
+
if (e->flags & EDGE_COMPLEX)
{
ei_next (&ei);
src = SET_SRC (pc_set (jump));
if (setcc != NULL)
- src = simplify_replace_rtx (src,
- SET_DEST (PATTERN (setcc)),
- SET_SRC (PATTERN (setcc)));
+ src = simplify_replace_rtx (src,
+ SET_DEST (PATTERN (setcc)),
+ SET_SRC (PATTERN (setcc)));
new_rtx = simplify_replace_rtx (src, reg_used->reg_rtx,
- SET_SRC (set->expr));
+ SET_SRC (set->expr));
/* Jump bypassing may have already placed instructions on
edges of the CFG. We can't bypass an outgoing edge that
{
setcc = NULL_RTX;
FOR_BB_INSNS (bb, insn)
- if (NONJUMP_INSN_P (insn))
+ if (DEBUG_INSN_P (insn))
+ continue;
+ else if (NONJUMP_INSN_P (insn))
{
if (setcc)
break;
{
edge pred;
edge_iterator ei;
-
+
FOR_EACH_EDGE (pred, ei, bb->preds)
{
basic_block pred_bb = pred->src;
if (insn_invalid_p (insn))
gcc_unreachable ();
}
-
+
pat = get_insns ();
end_sequence ();
&& (!single_succ_p (bb)
|| single_succ_edge (bb)->flags & EDGE_ABNORMAL))
{
- /* Keeping in mind SMALL_REGISTER_CLASSES and parameters in registers,
- we search backward and place the instructions before the first
- parameter is loaded. Do this for everyone for consistency and a
- presumption that we'll get better code elsewhere as well.
+ /* Keeping in mind targets with small register classes and parameters
+ in registers, we search backward and place the instructions before
+ the first parameter is loaded. Do this for everyone for consistency
+ and a presumption that we'll get better code elsewhere as well.
It should always be the case that we can put these instructions
anywhere in the basic block with performing PRE optimizations.
if (dump_file)
fprintf (dump_file,
"PRE: bb %d, insn %d, copy expression %d in insn %d to reg %d\n",
- BLOCK_NUM (insn), INSN_UID (new_insn), indx,
+ BLOCK_FOR_INSN (insn)->index, INSN_UID (new_insn), indx,
INSN_UID (insn), regno);
}
gcc_obstack_init (&gcse_obstack);
alloc_gcse_mem ();
- alloc_hash_table (get_max_uid (), &expr_hash_table, 0);
+ alloc_hash_table (&expr_hash_table, 0);
add_noreturn_fake_exit_edges ();
if (flag_gcse_lm)
compute_ld_motion_mems ();
gcc_obstack_init (&gcse_obstack);
alloc_gcse_mem ();
- alloc_hash_table (get_max_uid (), &expr_hash_table, 0);
+ alloc_hash_table (&expr_hash_table, 0);
compute_hash_table (&expr_hash_table);
if (dump_file)
dump_hash_table (dump_file, "Code Hosting Expressions", &expr_hash_table);
{
FOR_BB_INSNS (bb, insn)
{
- if (INSN_P (insn))
+ if (NONDEBUG_INSN_P (insn))
{
if (GET_CODE (PATTERN (insn)) == SET)
{
rtx pat = PATTERN (insn);
rtx src = SET_SRC (pat);
rtx reg = expr->reaching_reg;
- rtx copy, new_rtx;
+ rtx copy;
/* If we've already copied it, continue. */
if (expr->reaching_reg == src)
}
copy = gen_move_insn (reg, copy_rtx (SET_SRC (pat)));
- new_rtx = emit_insn_before (copy, insn);
+ emit_insn_before (copy, insn);
SET_SRC (pat) = reg;
df_insn_rescan (insn);
FIXME: This local pass should not be necessary after CSE (but for
some reason it still is). It is also (proven) not necessary
to run the local pass right after FWPWOP.
-
+
FIXME: The global analysis would not get into infinite loops if it
would use the DF solver (via df_simple_dataflow) instead of
the solver implemented in this file. */
implicit_sets = XCNEWVEC (rtx, last_basic_block);
find_implicit_sets ();
- alloc_hash_table (get_max_uid (), &set_hash_table, 1);
+ alloc_hash_table (&set_hash_table, 1);
compute_hash_table (&set_hash_table);
/* Free implicit_sets before peak usage. */
execute_rtl_cprop (void)
{
delete_unreachable_blocks ();
- df_note_add_problem ();
df_set_flags (DF_LR_RUN_DCE);
df_analyze ();
flag_rerun_cse_after_global_opts |= one_cprop_pass ();
execute_rtl_pre (void)
{
delete_unreachable_blocks ();
- df_note_add_problem ();
df_analyze ();
flag_rerun_cse_after_global_opts |= one_pre_gcse_pass ();
return 0;
execute_rtl_hoist (void)
{
delete_unreachable_blocks ();
- df_note_add_problem ();
df_analyze ();
flag_rerun_cse_after_global_opts |= one_code_hoisting_pass ();
return 0;
{
RTL_PASS,
"cprop", /* name */
- gate_rtl_cprop, /* gate */
- execute_rtl_cprop, /* execute */
+ gate_rtl_cprop, /* gate */
+ execute_rtl_cprop, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
{
{
RTL_PASS,
- "pre", /* name */
- gate_rtl_pre, /* gate */
- execute_rtl_pre, /* execute */
+ "rtl pre", /* name */
+ gate_rtl_pre, /* gate */
+ execute_rtl_pre, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
{
RTL_PASS,
"hoist", /* name */
- gate_rtl_hoist, /* gate */
- execute_rtl_hoist, /* execute */
+ gate_rtl_hoist, /* gate */
+ execute_rtl_hoist, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */