/* RTL dead store elimination.
- Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
Contributed by Richard Sandiford <rsandifor@codesourcery.com>
and Kenneth Zadeck <zadeck@naturalbridge.com>
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#undef BASELINE
#include "tm.h"
#include "rtl.h"
#include "tree.h"
+#include "tm_p.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "expr.h"
#include "recog.h"
#include "dse.h"
+#include "optabs.h"
#include "dbgcnt.h"
/* This file contains three techniques for performing Dead Store
rtx mem_addr;
/* If this is non-zero, it is the alias set of a spill location. */
- HOST_WIDE_INT alias_set;
+ alias_set_type alias_set;
/* The offset of the first and byte before the last byte associated
with the operation. */
/* An bitmask as wide as the number of bytes in the word that
contains a 1 if the byte may be needed. The store is unused if
all of the bits are 0. */
- long positions_needed;
+ unsigned HOST_WIDE_INT positions_needed;
/* The next store info for this insn. */
struct store_info *next;
rtx rhs;
};
+/* Return a bitmask with the first N low bits set. */
+
+static unsigned HOST_WIDE_INT
+lowpart_bitmask (int n)
+{
+ unsigned HOST_WIDE_INT mask = ~(unsigned HOST_WIDE_INT) 0;
+ return mask >> (HOST_BITS_PER_WIDE_INT - n);
+}
+
typedef struct store_info *store_info_t;
static alloc_pool cse_store_info_pool;
static alloc_pool rtx_store_info_pool;
int group_id;
/* If this is non-zero, it is the alias set of a spill location. */
- HOST_WIDE_INT alias_set;
+ alias_set_type alias_set;
/* The offset of the first and byte after the last byte associated
with the operation. If begin == end == 0, the read did not have
contains a wild read, the use_rec will be null. */
bool wild_read;
- /* This field is set for const function calls. Const functions
- cannot read memory, but they can read the stack because that is
- where they may get their parms. So having this set is less
- severe than a wild read, it just means that all of the stores to
- the stack are killed rather than all stores. */
- bool stack_read;
+ /* This field is only used for the processing of const functions.
+ These functions cannot read memory, but they can read the stack
+ because that is where they may get their parms. We need to be
+ this conservative because, like the store motion pass, we don't
+ consider CALL_INSN_FUNCTION_USAGE when processing call insns.
+ Moreover, we need to distinguish two cases:
+ 1. Before reload (register elimination), the stores related to
+ outgoing arguments are stack pointer based and thus deemed
+ of non-constant base in this pass. This requires special
+ handling but also means that the frame pointer based stores
+ need not be killed upon encountering a const function call.
+ 2. After reload, the stores related to outgoing arguments can be
+ either stack pointer or hard frame pointer based. This means
+ that we have no other choice than also killing all the frame
+ pointer based stores upon encountering a const function call.
+ This field is set after reload for const function calls. Having
+ this set is less severe than a wild read, it just means that all
+ the frame related stores are killed rather than all the stores. */
+ bool frame_read;
+
+ /* This field is only used for the processing of const functions.
+ It is set if the insn may contain a stack pointer based store. */
+ bool stack_pointer_based;
/* This is true if any of the sets within the store contains a
cselib base. Such stores can only be deleted by the local
/* The linked list of insns that are in consideration for removal in
the forwards pass thru the basic block. This pointer may be
trash as it is not cleared when a wild read occurs. The only
- time it is guaranteed to be correct is when the traveral starts
+ time it is guaranteed to be correct is when the traversal starts
at active_local_stores. */
struct insn_info * next_local_store;
};
int offset_map_size_n, offset_map_size_p;
};
typedef struct group_info *group_info_t;
+typedef const struct group_info *const_group_info_t;
static alloc_pool rtx_group_info_pool;
/* Tables of group_info structures, hashed by base value. */
/* Hash table element to look up the mode for an alias set. */
struct clear_alias_mode_holder
{
- HOST_WIDE_INT alias_set;
+ alias_set_type alias_set;
enum machine_mode mode;
};
static alloc_pool clear_alias_mode_pool;
-/* This is true except for two cases:
- (1) current_function_stdarg -- i.e. we cannot do this
- for vararg functions because they play games with the frame.
- (2) In ada, it is sometimes not safe to do assume that any stores
- based off the stack frame go dead at the exit to a function. */
+/* This is true except if cfun->stdarg -- i.e. we cannot do
+ this for vararg functions because they play games with the frame. */
static bool stores_off_frame_dead_at_return;
/* Counter for stats. */
static bool gate_dse (void);
+static bool gate_dse1 (void);
+static bool gate_dse2 (void);
\f
/*----------------------------------------------------------------------------
/* Find the entry associated with ALIAS_SET. */
static struct clear_alias_mode_holder *
-clear_alias_set_lookup (HOST_WIDE_INT alias_set)
+clear_alias_set_lookup (alias_set_type alias_set)
{
struct clear_alias_mode_holder tmp_holder;
void **slot;
slot = htab_find_slot (clear_alias_mode_table, &tmp_holder, NO_INSERT);
gcc_assert (*slot);
- return *slot;
+ return (struct clear_alias_mode_holder *) *slot;
}
static int
invariant_group_base_eq (const void *p1, const void *p2)
{
- const group_info_t gi1 = (const group_info_t) p1;
- const group_info_t gi2 = (const group_info_t) p2;
+ const_group_info_t gi1 = (const_group_info_t) p1;
+ const_group_info_t gi2 = (const_group_info_t) p2;
return rtx_equal_p (gi1->rtx_base, gi2->rtx_base);
}
static hashval_t
invariant_group_base_hash (const void *p)
{
- const group_info_t gi = (const group_info_t) p;
+ const_group_info_t gi = (const_group_info_t) p;
int do_not_record;
return hash_rtx (gi->rtx_base, Pmode, &do_not_record, NULL, false);
}
{
if (!clear_alias_group)
{
- clear_alias_group = gi = pool_alloc (rtx_group_info_pool);
+ clear_alias_group = gi =
+ (group_info_t) pool_alloc (rtx_group_info_pool);
memset (gi, 0, sizeof (struct group_info));
gi->id = rtx_group_next_id++;
gi->store1_n = BITMAP_ALLOC (NULL);
if (gi == NULL)
{
- *slot = gi = pool_alloc (rtx_group_info_pool);
+ *slot = gi = (group_info_t) pool_alloc (rtx_group_info_pool);
gi->rtx_base = base;
gi->id = rtx_group_next_id++;
gi->base_mem = gen_rtx_MEM (QImode, base);
bb_table = XCNEWVEC (bb_info_t, last_basic_block);
rtx_group_next_id = 0;
- stores_off_frame_dead_at_return =
- (!(TREE_CODE (TREE_TYPE (current_function_decl)) == FUNCTION_TYPE
- && (TYPE_RETURNS_STACK_DEPRESSED (TREE_TYPE (current_function_decl)))))
- && (!current_function_stdarg);
+ stores_off_frame_dead_at_return = !cfun->stdarg;
init_alias_analysis ();
{
rtx r1 = XEXP (x, 0);
rtx c = gen_int_mode (Pmode, data->size);
- add_insn_before (data->insn,
- gen_rtx_SET (Pmode, r1,
- gen_rtx_PLUS (Pmode, r1, c)),
- NULL);
+ emit_insn_before (gen_rtx_SET (Pmode, r1,
+ gen_rtx_PLUS (Pmode, r1, c)),
+ data->insn);
return -1;
}
{
rtx r1 = XEXP (x, 0);
rtx c = gen_int_mode (Pmode, -data->size);
- add_insn_before (data->insn,
- gen_rtx_SET (Pmode, r1,
- gen_rtx_PLUS (Pmode, r1, c)),
- NULL);
+ emit_insn_before (gen_rtx_SET (Pmode, r1,
+ gen_rtx_PLUS (Pmode, r1, c)),
+ data->insn);
return -1;
}
insn that contained it. */
rtx add = XEXP (x, 0);
rtx r1 = XEXP (add, 0);
- add_insn_before (data->insn,
- gen_rtx_SET (Pmode, r1, add), NULL);
+ emit_insn_before (gen_rtx_SET (Pmode, r1, add), data->insn);
return -1;
}
replace_inc_dec_mem (rtx *r, void *d)
{
rtx x = *r;
- if (GET_CODE (x) == MEM)
+ if (x != NULL_RTX && MEM_P (x))
{
struct insn_size data;
data.size = GET_MODE_SIZE (GET_MODE (x));
- data.insn = (rtx)d;
+ data.insn = (rtx) d;
for_each_rtx (&XEXP (x, 0), replace_inc_dec, &data);
INSN_UID (insn_info->insn));
if (insn_info->store_rec->alias_set)
fprintf (dump_file, "alias set %d\n",
- (int)insn_info->store_rec->alias_set);
+ (int) insn_info->store_rec->alias_set);
else
fprintf (dump_file, "\n");
}
while (*ptr)
{
read_info_t next = (*ptr)->next;
- if ( (*ptr)->alias_set == 0 )
+ if ((*ptr)->alias_set == 0)
{
pool_free (read_info_pool, *ptr);
*ptr = next;
}
-/* Return true if X is a constant or one of the registers that behaves
- as a constant over the life of a function. */
+/* Return true if X is a constant or one of the registers that behave
+ as a constant over the life of a function. This is equivalent to
+ !rtx_varies_p for memory addresses. */
static bool
const_or_frame_p (rtx x)
static bool
canon_address (rtx mem,
- HOST_WIDE_INT *alias_set_out,
+ alias_set_type *alias_set_out,
int *group_id,
HOST_WIDE_INT *offset,
cselib_val **base)
if (clear_alias_sets)
{
/* If this is a spill, do not do any further processing. */
- HOST_WIDE_INT alias_set = MEM_ALIAS_SET (mem);
+ alias_set_type alias_set = MEM_ALIAS_SET (mem);
if (dump_file)
- fprintf (dump_file, "found alias set %d\n", (int)alias_set);
+ fprintf (dump_file, "found alias set %d\n", (int) alias_set);
if (bitmap_bit_p (clear_alias_sets, alias_set))
{
struct clear_alias_mode_holder *entry
if (dump_file)
fprintf (dump_file,
"disqualifying alias set %d, (%s) != (%s)\n",
- (int)alias_set, GET_MODE_NAME (entry->mode),
+ (int) alias_set, GET_MODE_NAME (entry->mode),
GET_MODE_NAME (GET_MODE (mem)));
bitmap_set_bit (disqualified_clear_alias_sets, alias_set);
rtx mem;
HOST_WIDE_INT offset = 0;
HOST_WIDE_INT width = 0;
- HOST_WIDE_INT spill_alias_set;
+ alias_set_type spill_alias_set;
insn_info_t insn_info = bb_info->last_insn;
store_info_t store_info = NULL;
int group_id;
if (clear_alias_group->offset_map_size_p < spill_alias_set)
clear_alias_group->offset_map_size_p = spill_alias_set;
- store_info = pool_alloc (rtx_store_info_pool);
+ store_info = (store_info_t) pool_alloc (rtx_store_info_pool);
if (dump_file)
fprintf (dump_file, " processing spill store %d(%s)\n",
- (int)spill_alias_set, GET_MODE_NAME (GET_MODE (mem)));
+ (int) spill_alias_set, GET_MODE_NAME (GET_MODE (mem)));
}
else if (group_id >= 0)
{
group_info_t group
= VEC_index (group_info_t, rtx_group_vec, group_id);
- store_info = pool_alloc (rtx_store_info_pool);
+ store_info = (store_info_t) pool_alloc (rtx_store_info_pool);
set_usage_bits (group, offset, width);
if (dump_file)
}
else
{
- store_info = pool_alloc (cse_store_info_pool);
+ rtx base_term = find_base_term (XEXP (mem, 0));
+ if (!base_term
+ || (GET_CODE (base_term) == ADDRESS
+ && GET_MODE (base_term) == Pmode
+ && XEXP (base_term, 0) == stack_pointer_rtx))
+ insn_info->stack_pointer_based = true;
insn_info->contains_cselib_groups = true;
+
+ store_info = (store_info_t) pool_alloc (cse_store_info_pool);
group_id = -1;
if (dump_file)
{
insn_info_t next = ptr->next_local_store;
store_info_t s_info = ptr->store_rec;
- bool delete = true;
+ bool del = true;
/* Skip the clobbers. We delete the active insn if this insn
shadows the set. To have been put on the active list, it
s_info = s_info->next;
if (s_info->alias_set != spill_alias_set)
- delete = false;
+ del = false;
else if (s_info->alias_set)
{
struct clear_alias_mode_holder *entry
if ((GET_MODE (mem) == GET_MODE (s_info->mem))
&& (GET_MODE (mem) == entry->mode))
{
- delete = true;
- s_info->positions_needed = 0;
+ del = true;
+ s_info->positions_needed = (unsigned HOST_WIDE_INT) 0;
}
if (dump_file)
fprintf (dump_file, " trying spill store in insn=%d alias_set=%d\n",
- INSN_UID (ptr->insn), (int)s_info->alias_set);
+ INSN_UID (ptr->insn), (int) s_info->alias_set);
}
else if ((s_info->group_id == group_id)
&& (s_info->cse_base == base))
(int)s_info->begin, (int)s_info->end);
for (i = offset; i < offset+width; i++)
if (i >= s_info->begin && i < s_info->end)
- s_info->positions_needed &= ~(1L << (i - s_info->begin));
+ s_info->positions_needed
+ &= ~(((unsigned HOST_WIDE_INT) 1) << (i - s_info->begin));
}
else if (s_info->rhs)
/* Need to see if it is possible for this store to overwrite
/* An insn can be deleted if every position of every one of
its s_infos is zero. */
- if (s_info->positions_needed != 0)
- delete = false;
+ if (s_info->positions_needed != (unsigned HOST_WIDE_INT) 0)
+ del = false;
- if (delete)
+ if (del)
{
insn_info_t insn_to_delete = ptr;
ptr = next;
}
- gcc_assert ((unsigned) width < sizeof (store_info->positions_needed) * CHAR_BIT);
+ gcc_assert ((unsigned) width <= HOST_BITS_PER_WIDE_INT);
/* Finish filling in the store_info. */
store_info->next = insn_info->store_rec;
store_info->alias_set = spill_alias_set;
store_info->mem_addr = get_addr (XEXP (mem, 0));
store_info->cse_base = base;
- store_info->positions_needed = (1L << width) - 1;
+ store_info->positions_needed = lowpart_bitmask (width);
store_info->group_id = group_id;
store_info->begin = offset;
store_info->end = offset + width;
if (store_info->is_set
/* No place to keep the value after ra. */
&& !reload_completed
- /* The careful reviewer may wish to comment my checking that the
- rhs of a store is always a reg. */
- && REG_P (SET_SRC (body))
+ && (REG_P (SET_SRC (body))
+ || GET_CODE (SET_SRC (body)) == SUBREG
+ || CONSTANT_P (SET_SRC (body)))
/* Sometimes the store and reload is used for truncation and
rounding. */
&& !(FLOAT_MODE_P (GET_MODE (mem)) && (flag_float_store)))
}
+/* If the modes are different and the value's source and target do not
+ line up, we need to extract the value from lower part of the rhs of
+ the store, shift it, and then put it into a form that can be shoved
+ into the read_insn. This function generates a right SHIFT of a
+ value that is at least ACCESS_SIZE bytes wide of READ_MODE. The
+ shift sequence is returned or NULL if we failed to find a
+ shift. */
+
+static rtx
+find_shift_sequence (int access_size,
+ store_info_t store_info,
+ read_info_t read_info,
+ int shift,
+ bool speed)
+{
+ enum machine_mode store_mode = GET_MODE (store_info->mem);
+ enum machine_mode read_mode = GET_MODE (read_info->mem);
+ enum machine_mode new_mode;
+ rtx read_reg = NULL;
+
+ /* Some machines like the x86 have shift insns for each size of
+ operand. Other machines like the ppc or the ia-64 may only have
+ shift insns that shift values within 32 or 64 bit registers.
+ This loop tries to find the smallest shift insn that will right
+ justify the value we want to read but is available in one insn on
+ the machine. */
+
+ for (new_mode = smallest_mode_for_size (access_size * BITS_PER_UNIT,
+ MODE_INT);
+ GET_MODE_BITSIZE (new_mode) <= BITS_PER_WORD;
+ new_mode = GET_MODE_WIDER_MODE (new_mode))
+ {
+ rtx target, new_reg, shift_seq, insn, new_lhs;
+ int cost;
+
+ /* Try a wider mode if truncating the store mode to NEW_MODE
+ requires a real instruction. */
+ if (GET_MODE_BITSIZE (new_mode) < GET_MODE_BITSIZE (store_mode)
+ && !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (new_mode),
+ GET_MODE_BITSIZE (store_mode)))
+ continue;
+
+ /* Also try a wider mode if the necessary punning is either not
+ desirable or not possible. */
+ if (!CONSTANT_P (store_info->rhs)
+ && !MODES_TIEABLE_P (new_mode, store_mode))
+ continue;
+
+ new_reg = gen_reg_rtx (new_mode);
+
+ start_sequence ();
+
+ /* In theory we could also check for an ashr. Ian Taylor knows
+ of one dsp where the cost of these two was not the same. But
+ this really is a rare case anyway. */
+ target = expand_binop (new_mode, lshr_optab, new_reg,
+ GEN_INT (shift), new_reg, 1, OPTAB_DIRECT);
+
+ shift_seq = get_insns ();
+ end_sequence ();
+
+ if (target != new_reg || shift_seq == NULL)
+ continue;
+
+ cost = 0;
+ for (insn = shift_seq; insn != NULL_RTX; insn = NEXT_INSN (insn))
+ if (INSN_P (insn))
+ cost += insn_rtx_cost (PATTERN (insn), speed);
+
+ /* The computation up to here is essentially independent
+ of the arguments and could be precomputed. It may
+ not be worth doing so. We could precompute if
+ worthwhile or at least cache the results. The result
+ technically depends on both SHIFT and ACCESS_SIZE,
+ but in practice the answer will depend only on ACCESS_SIZE. */
+
+ if (cost > COSTS_N_INSNS (1))
+ continue;
+
+ new_lhs = extract_low_bits (new_mode, store_mode,
+ copy_rtx (store_info->rhs));
+ if (new_lhs == NULL_RTX)
+ continue;
+
+ /* We found an acceptable shift. Generate a move to
+ take the value from the store and put it into the
+ shift pseudo, then shift it, then generate another
+ move to put in into the target of the read. */
+ emit_move_insn (new_reg, new_lhs);
+ emit_insn (shift_seq);
+ read_reg = extract_low_bits (read_mode, new_mode, new_reg);
+ break;
+ }
+
+ return read_reg;
+}
+
+
/* Take a sequence of:
A <- r1
...
...
... <- r2
- The STORE_INFO and STORE_INFO are for the store and the READ_INFO
+ or
+
+ r3 <- extract (r1)
+ r3 <- r3 >> shift
+ r2 <- extract (r3)
+ ... <- r2
+
+ or
+
+ r2 <- extract (r1)
+ ... <- r2
+
+ Depending on the alignment and the mode of the store and
+ subsequent load.
+
+
+ The STORE_INFO and STORE_INSN are for the store and READ_INFO
and READ_INSN are for the read. Return true if the replacement
went ok. */
replace_read (store_info_t store_info, insn_info_t store_insn,
read_info_t read_info, insn_info_t read_insn, rtx *loc)
{
+ enum machine_mode store_mode = GET_MODE (store_info->mem);
+ enum machine_mode read_mode = GET_MODE (read_info->mem);
+ int shift;
+ int access_size; /* In bytes. */
+ rtx insns, read_reg;
+
if (!dbg_cnt (dse))
return false;
+ /* To get here the read is within the boundaries of the write so
+ shift will never be negative. Start out with the shift being in
+ bytes. */
+ if (BYTES_BIG_ENDIAN)
+ shift = store_info->end - read_info->end;
+ else
+ shift = read_info->begin - store_info->begin;
+
+ access_size = shift + GET_MODE_SIZE (read_mode);
+
+ /* From now on it is bits. */
+ shift *= BITS_PER_UNIT;
+
+ /* Create a sequence of instructions to set up the read register.
+ This sequence goes immediately before the store and its result
+ is read by the load.
+
+ We need to keep this in perspective. We are replacing a read
+ with a sequence of insns, but the read will almost certainly be
+ in cache, so it is not going to be an expensive one. Thus, we
+ are not willing to do a multi insn shift or worse a subroutine
+ call to get rid of the read. */
if (dump_file)
- fprintf (dump_file, "generating move to replace load at %d from store at %d\n",
- INSN_UID (read_insn->insn), INSN_UID (store_insn->insn));
- if (GET_MODE (store_info->mem) == GET_MODE (read_info->mem))
+ fprintf (dump_file, "trying to replace %smode load in insn %d"
+ " from %smode store in insn %d\n",
+ GET_MODE_NAME (read_mode), INSN_UID (read_insn->insn),
+ GET_MODE_NAME (store_mode), INSN_UID (store_insn->insn));
+ start_sequence ();
+ if (shift)
+ read_reg = find_shift_sequence (access_size, store_info, read_info, shift,
+ optimize_bb_for_speed_p (BLOCK_FOR_INSN (read_insn->insn)));
+ else
+ read_reg = extract_low_bits (read_mode, store_mode,
+ copy_rtx (store_info->rhs));
+ if (read_reg == NULL_RTX)
{
- rtx new_reg = gen_reg_rtx (GET_MODE (store_info->mem));
- if (validate_change (read_insn->insn, loc, new_reg, 0))
- {
- rtx insns;
- deferred_change_t deferred_change = pool_alloc (deferred_change_pool);
-
- start_sequence ();
- emit_move_insn (new_reg, store_info->rhs);
- insns = get_insns ();
- end_sequence ();
- emit_insn_before (insns, store_insn->insn);
+ end_sequence ();
+ if (dump_file)
+ fprintf (dump_file, " -- could not extract bits of stored value\n");
+ return false;
+ }
+ /* Force the value into a new register so that it won't be clobbered
+ between the store and the load. */
+ read_reg = copy_to_mode_reg (read_mode, read_reg);
+ insns = get_insns ();
+ end_sequence ();
- if (dump_file)
- fprintf (dump_file, " -- adding move insn %d: r%d = r%d\n",
- INSN_UID (insns), REGNO (new_reg), REGNO (store_info->rhs));
-
- /* And now for the cludge part: cselib croaks if you just
- return at this point. There are two reasons for this:
-
- 1) Cselib has an idea of how many pseudos there are and
- that does not include the new one we just added.
-
- 2) Cselib does not know about the move insn we added
- above the store_info, and there is no way to tell it
- about it, because it has "moved on".
-
- So we are just going to have to lie. The move insn is
- not really an issue, cselib did not see it. But the use
- of the new pseudo read_insn is a real problem. The way
- that we solve this problem is that we are just going to
- put the mem back keep a table of mems to get rid of. At
- the end of the basic block we can put it back. */
-
- *loc = read_info->mem;
- deferred_change->next = deferred_change_list;
- deferred_change_list = deferred_change;
- deferred_change->loc = loc;
- deferred_change->reg = new_reg;
-
- /* Get rid of the read_info, from the point of view of the
- rest of dse, play like this read never happened. */
- read_insn->read_rec = read_info->next;
- pool_free (read_info_pool, read_info);
- return true;
- }
- else
+ if (validate_change (read_insn->insn, loc, read_reg, 0))
+ {
+ deferred_change_t deferred_change =
+ (deferred_change_t) pool_alloc (deferred_change_pool);
+
+ /* Insert this right before the store insn where it will be safe
+ from later insns that might change it before the read. */
+ emit_insn_before (insns, store_insn->insn);
+
+ /* And now for the kludge part: cselib croaks if you just
+ return at this point. There are two reasons for this:
+
+ 1) Cselib has an idea of how many pseudos there are and
+ that does not include the new ones we just added.
+
+ 2) Cselib does not know about the move insn we added
+ above the store_info, and there is no way to tell it
+ about it, because it has "moved on".
+
+ Problem (1) is fixable with a certain amount of engineering.
+ Problem (2) is requires starting the bb from scratch. This
+ could be expensive.
+
+ So we are just going to have to lie. The move/extraction
+ insns are not really an issue, cselib did not see them. But
+ the use of the new pseudo read_insn is a real problem because
+ cselib has not scanned this insn. The way that we solve this
+ problem is that we are just going to put the mem back for now
+ and when we are finished with the block, we undo this. We
+ keep a table of mems to get rid of. At the end of the basic
+ block we can put them back. */
+
+ *loc = read_info->mem;
+ deferred_change->next = deferred_change_list;
+ deferred_change_list = deferred_change;
+ deferred_change->loc = loc;
+ deferred_change->reg = read_reg;
+
+ /* Get rid of the read_info, from the point of view of the
+ rest of dse, play like this read never happened. */
+ read_insn->read_rec = read_info->next;
+ pool_free (read_info_pool, read_info);
+ if (dump_file)
{
- if (dump_file)
- fprintf (dump_file, " -- validation failure\n");
- return false;
+ fprintf (dump_file, " -- replaced the loaded MEM with ");
+ print_simple_rtl (dump_file, read_reg);
+ fprintf (dump_file, "\n");
}
+ return true;
}
- else
+ else
{
- /* Someone with excellent rtl skills needs to fill this in. You
- are guaranteed that the read is of the same size or smaller
- than the store, and that the read does not hang off one of
- the ends of the store. But the offsets of each must be
- checked because the read does not have to line up on either
- end of the store so the begin fields need to be examined in
- both the store_info and read_info. */
if (dump_file)
- fprintf (dump_file, " -- complex load, currently unsupported.\n");
+ {
+ fprintf (dump_file, " -- replacing the loaded MEM with ");
+ print_simple_rtl (dump_file, read_reg);
+ fprintf (dump_file, " led to an invalid instruction\n");
+ }
return false;
}
}
-
/* A for_each_rtx callback in which DATA is the bb_info. Check to see
if LOC is a mem and if it is look at the address and kill any
appropriate stores that may be active. */
insn_info_t insn_info;
HOST_WIDE_INT offset = 0;
HOST_WIDE_INT width = 0;
- HOST_WIDE_INT spill_alias_set = 0;
+ alias_set_type spill_alias_set = 0;
cselib_val *base = NULL;
int group_id;
read_info_t read_info;
else
width = GET_MODE_SIZE (GET_MODE (mem));
- read_info = pool_alloc (read_info_pool);
+ read_info = (read_info_t) pool_alloc (read_info_pool);
read_info->group_id = group_id;
read_info->mem = mem;
read_info->alias_set = spill_alias_set;
if (dump_file)
fprintf (dump_file, " processing spill load %d\n",
- (int)spill_alias_set);
+ (int) spill_alias_set);
while (i_ptr)
{
&& (offset >= store_info->begin)
&& (offset + width <= store_info->end))
{
- int mask = ((1L << width) - 1) << (offset - store_info->begin);
-
+ unsigned HOST_WIDE_INT mask
+ = (lowpart_bitmask (width)
+ << (offset - store_info->begin));
+
if ((store_info->positions_needed & mask) == mask
&& replace_read (store_info, i_ptr,
read_info, insn_info, loc))
&& (offset >= store_info->begin)
&& (offset + width <= store_info->end))
{
- int mask = ((1L << width) - 1) << (offset - store_info->begin);
-
+ unsigned HOST_WIDE_INT mask
+ = (lowpart_bitmask (width)
+ << (offset - store_info->begin));
+
if ((store_info->positions_needed & mask) == mask
&& replace_read (store_info, i_ptr,
read_info, insn_info, loc))
scan_insn (bb_info_t bb_info, rtx insn)
{
rtx body;
- insn_info_t insn_info = pool_alloc (insn_info_pool);
+ insn_info_t insn_info = (insn_info_t) pool_alloc (insn_info_pool);
int mems_found = 0;
memset (insn_info, 0, sizeof (struct insn_info));
if (CALL_P (insn))
{
insn_info->cannot_delete = true;
+
/* Const functions cannot do anything bad i.e. read memory,
- however, they can read their parameters which may have been
- pushed onto the stack. */
- if (CONST_OR_PURE_CALL_P (insn) && !pure_call_p (insn))
+ however, they can read their parameters which may have
+ been pushed onto the stack. */
+ if (RTL_CONST_CALL_P (insn))
{
insn_info_t i_ptr = active_local_stores;
insn_info_t last = NULL;
if (dump_file)
fprintf (dump_file, "const call %d\n", INSN_UID (insn));
+ /* See the head comment of the frame_read field. */
+ if (reload_completed)
+ insn_info->frame_read = true;
+
+ /* Loop over the active stores and remove those which are
+ killed by the const function call. */
while (i_ptr)
{
- store_info_t store_info = i_ptr->store_rec;
+ bool remove_store = false;
- /* Skip the clobbers. */
- while (!store_info->is_set)
- store_info = store_info->next;
+ /* The stack pointer based stores are always killed. */
+ if (i_ptr->stack_pointer_based)
+ remove_store = true;
- /* Remove the frame related stores. */
- if (store_info->group_id >= 0
- && VEC_index (group_info_t, rtx_group_vec, store_info->group_id)->frame_related)
+ /* If the frame is read, the frame related stores are killed. */
+ else if (insn_info->frame_read)
+ {
+ store_info_t store_info = i_ptr->store_rec;
+
+ /* Skip the clobbers. */
+ while (!store_info->is_set)
+ store_info = store_info->next;
+
+ if (store_info->group_id >= 0
+ && VEC_index (group_info_t, rtx_group_vec,
+ store_info->group_id)->frame_related)
+ remove_store = true;
+ }
+
+ if (remove_store)
{
if (dump_file)
dump_insn_info ("removing from active", i_ptr);
}
else
last = i_ptr;
+
i_ptr = i_ptr->next_local_store;
}
-
- insn_info->stack_read = true;
-
- return;
}
- /* Every other call, including pure functions may read memory. */
- add_wild_read (bb_info);
+ else
+ /* Every other call, including pure functions, may read memory. */
+ add_wild_read (bb_info);
+
return;
}
/* Assuming that there are sets in these insns, we cannot delete
them. */
if ((GET_CODE (PATTERN (insn)) == CLOBBER)
- || volatile_insn_p (PATTERN (insn))
+ || volatile_refs_p (PATTERN (insn))
|| (flag_non_call_exceptions && may_trap_p (PATTERN (insn)))
|| (RTX_FRAME_RELATED_P (insn))
|| find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX))
while (insn_info)
{
store_info_t store_info = insn_info->store_rec;
- bool delete = false;
+ bool del = false;
/* If ANY of the store_infos match the cselib group that is
being deleted, then the insn can not be deleted. */
if ((store_info->group_id == -1)
&& (store_info->cse_base == base))
{
- delete = true;
+ del = true;
break;
}
store_info = store_info->next;
}
- if (delete)
+ if (del)
{
if (last)
last->next_local_store = insn_info->next_local_store;
FOR_ALL_BB (bb)
{
insn_info_t ptr;
- bb_info_t bb_info = pool_alloc (bb_info_pool);
+ bb_info_t bb_info = (bb_info_t) pool_alloc (bb_info_pool);
memset (bb_info, 0, sizeof (struct bb_info));
bitmap_set_bit (all_blocks, bb->index);
&& (EDGE_COUNT (bb->succs) == 0
|| (single_succ_p (bb)
&& single_succ (bb) == EXIT_BLOCK_PTR
- && ! current_function_calls_eh_return)))
+ && ! crtl->calls_eh_return)))
{
insn_info_t i_ptr = active_local_stores;
while (i_ptr)
void
-dse_record_singleton_alias_set (HOST_WIDE_INT alias_set,
+dse_record_singleton_alias_set (alias_set_type alias_set,
enum machine_mode mode)
{
struct clear_alias_mode_holder tmp_holder;
slot = htab_find_slot (clear_alias_mode_table, &tmp_holder, INSERT);
gcc_assert (*slot == NULL);
- *slot = entry = pool_alloc (clear_alias_mode_pool);
+ *slot = entry =
+ (struct clear_alias_mode_holder *) pool_alloc (clear_alias_mode_pool);
entry->alias_set = alias_set;
entry->mode = mode;
}
/* Remove ALIAS_SET from the sets of stack slots being considered. */
void
-dse_invalidate_singleton_alias_set (HOST_WIDE_INT alias_set)
+dse_invalidate_singleton_alias_set (alias_set_type alias_set)
{
if ((!gate_dse()) || !alias_set)
return;
int i;
group_info_t group;
- /* For const function calls kill the stack related stores. */
- if (insn_info->stack_read)
+ /* If this insn reads the frame, kill all the frame related stores. */
+ if (insn_info->frame_read)
{
for (i = 0; VEC_iterate (group_info_t, rtx_group_vec, i, group); i++)
if (group->process_globally && group->frame_related)
group_info_t group;
basic_block bb;
- if (global_done)
- {
- for (i = 0; VEC_iterate (group_info_t, rtx_group_vec, i, group); i++)
- {
- free (group->offset_map_n);
- free (group->offset_map_p);
- BITMAP_FREE (group->store1_n);
- BITMAP_FREE (group->store1_p);
- BITMAP_FREE (group->store2_n);
- BITMAP_FREE (group->store2_p);
- BITMAP_FREE (group->group_kill);
- }
-
- FOR_ALL_BB (bb)
- {
- bb_info_t bb_info = bb_table[bb->index];
- BITMAP_FREE (bb_info->gen);
- if (bb_info->kill)
- BITMAP_FREE (bb_info->kill);
- if (bb_info->in)
- BITMAP_FREE (bb_info->in);
- if (bb_info->out)
- BITMAP_FREE (bb_info->out);
- }
- }
- else
+ for (i = 0; VEC_iterate (group_info_t, rtx_group_vec, i, group); i++)
{
- for (i = 0; VEC_iterate (group_info_t, rtx_group_vec, i, group); i++)
- {
- BITMAP_FREE (group->store1_n);
- BITMAP_FREE (group->store1_p);
- BITMAP_FREE (group->store2_n);
- BITMAP_FREE (group->store2_p);
- BITMAP_FREE (group->group_kill);
- }
+ free (group->offset_map_n);
+ free (group->offset_map_p);
+ BITMAP_FREE (group->store1_n);
+ BITMAP_FREE (group->store1_p);
+ BITMAP_FREE (group->store2_n);
+ BITMAP_FREE (group->store2_p);
+ BITMAP_FREE (group->group_kill);
}
+ if (global_done)
+ FOR_ALL_BB (bb)
+ {
+ bb_info_t bb_info = bb_table[bb->index];
+ BITMAP_FREE (bb_info->gen);
+ if (bb_info->kill)
+ BITMAP_FREE (bb_info->kill);
+ if (bb_info->in)
+ BITMAP_FREE (bb_info->in);
+ if (bb_info->out)
+ BITMAP_FREE (bb_info->out);
+ }
+
if (clear_alias_sets)
{
BITMAP_FREE (clear_alias_sets);
}
-
/* -------------------------------------------------------------------------
DSE
------------------------------------------------------------------------- */
static bool
gate_dse (void)
{
- return optimize > 0 && flag_dse;
+ return gate_dse1 () || gate_dse2 ();
+}
+
+static bool
+gate_dse1 (void)
+{
+ return optimize > 0 && flag_dse
+ && dbg_cnt (dse1);
+}
+
+static bool
+gate_dse2 (void)
+{
+ return optimize > 0 && flag_dse
+ && dbg_cnt (dse2);
}
-struct tree_opt_pass pass_rtl_dse1 =
+struct rtl_opt_pass pass_rtl_dse1 =
{
+ {
+ RTL_PASS,
"dse1", /* name */
- gate_dse, /* gate */
+ gate_dse1, /* gate */
rest_of_handle_dse, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* properties_destroyed */
0, /* todo_flags_start */
TODO_dump_func |
- TODO_df_finish |
- TODO_ggc_collect, /* todo_flags_finish */
- 'w' /* letter */
+ TODO_df_finish | TODO_verify_rtl_sharing |
+ TODO_ggc_collect /* todo_flags_finish */
+ }
};
-struct tree_opt_pass pass_rtl_dse2 =
+struct rtl_opt_pass pass_rtl_dse2 =
{
+ {
+ RTL_PASS,
"dse2", /* name */
- gate_dse, /* gate */
+ gate_dse2, /* gate */
rest_of_handle_dse, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* properties_destroyed */
0, /* todo_flags_start */
TODO_dump_func |
- TODO_df_finish |
- TODO_ggc_collect, /* todo_flags_finish */
- 'w' /* letter */
+ TODO_df_finish | TODO_verify_rtl_sharing |
+ TODO_ggc_collect /* todo_flags_finish */
+ }
};
-