/* RTL dead store elimination.
- Copyright (C) 2005-2014 Free Software Foundation, Inc.
+ Copyright (C) 2005-2015 Free Software Foundation, Inc.
Contributed by Richard Sandiford <rsandifor@codesourcery.com>
and Kenneth Zadeck <zadeck@naturalbridge.com>
#include "hash-table.h"
#include "tm.h"
#include "rtl.h"
+#include "hash-set.h"
+#include "machmode.h"
+#include "vec.h"
+#include "double-int.h"
+#include "input.h"
+#include "alias.h"
+#include "symtab.h"
+#include "wide-int.h"
+#include "inchash.h"
+#include "real.h"
#include "tree.h"
+#include "fold-const.h"
#include "stor-layout.h"
#include "tm_p.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "regset.h"
#include "flags.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "cfgrtl.h"
+#include "predict.h"
+#include "basic-block.h"
#include "df.h"
#include "cselib.h"
#include "tree-pass.h"
#include "alloc-pool.h"
-#include "alias.h"
#include "insn-config.h"
+#include "hashtab.h"
+#include "function.h"
+#include "statistics.h"
+#include "fixed-value.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
#include "expr.h"
#include "recog.h"
+#include "insn-codes.h"
#include "optabs.h"
#include "dbgcnt.h"
#include "target.h"
#include "is-a.h"
#include "gimple.h"
#include "gimple-ssa.h"
+#include "rtl-iter.h"
+#include "cfgcleanup.h"
/* This file contains three techniques for performing Dead Store
Elimination (dse).
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. */
+ This field is set after reload for const function calls and before
+ reload for const tail function calls on targets where arg pointer
+ is the frame pointer. 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.
static insn_info_t active_local_stores;
static int active_local_stores_len;
-struct bb_info
+struct dse_bb_info
{
/* Pointer to the insn info for the last insn in the block. These
bitmap regs_live;
};
-typedef struct bb_info *bb_info_t;
+typedef struct dse_bb_info *bb_info_t;
static alloc_pool bb_info_pool;
/* Table to hold all bb_infos. */
struct clear_alias_mode_holder
{
alias_set_type alias_set;
- enum machine_mode mode;
+ machine_mode mode;
};
/* This is true except if cfun->stdarg -- i.e. we cannot do
sizeof (struct insn_info), 100);
bb_info_pool
= create_alloc_pool ("bb_info_pool",
- sizeof (struct bb_info), 100);
+ sizeof (struct dse_bb_info), 100);
rtx_group_info_pool
= create_alloc_pool ("rtx_group_info_pool",
sizeof (struct group_info), 100);
typedef struct
{
- rtx first, current;
+ rtx_insn *first, *current;
regset fixed_regs_live;
bool failure;
} note_add_store_info;
static void
note_add_store (rtx loc, const_rtx expr ATTRIBUTE_UNUSED, void *data)
{
- rtx insn;
+ rtx_insn *insn;
note_add_store_info *info = (note_add_store_info *) data;
int r, n;
rtx dest, rtx src, rtx srcoff, void *arg)
{
insn_info_t insn_info = (insn_info_t) arg;
- rtx insn = insn_info->insn, new_insn, cur;
+ rtx_insn *insn = insn_info->insn, *new_insn, *cur;
note_add_store_info info;
/* We can reuse all operands without copying, because we are about
end_sequence ();
}
else
- new_insn = gen_move_insn (dest, src);
+ new_insn = as_a <rtx_insn *> (gen_move_insn (dest, src));
info.first = new_insn;
info.fixed_regs_live = insn_info->fixed_regs_live;
info.failure = false;
emit_insn_before (new_insn, insn);
- return -1;
+ return 0;
}
/* Before we delete INSN_INFO->INSN, make sure that the auto inc/dec, if it
rtx_insn *insn = insn_info->insn;
rtx note = find_reg_note (insn, REG_INC, NULL_RTX);
if (note)
- return for_each_inc_dec (&insn, emit_inc_dec_insn_before, insn_info) == 0;
+ return for_each_inc_dec (PATTERN (insn), emit_inc_dec_insn_before,
+ insn_info) == 0;
return true;
}
insn_info.fixed_regs_live = NULL;
note = find_reg_note (insn, REG_INC, NULL_RTX);
if (note)
- return for_each_inc_dec (&insn, emit_inc_dec_insn_before, &insn_info) == 0;
+ return for_each_inc_dec (PATTERN (insn), emit_inc_dec_insn_before,
+ &insn_info) == 0;
return true;
}
HOST_WIDE_INT *offset,
cselib_val **base)
{
- enum machine_mode address_mode = get_address_mode (mem);
+ machine_mode address_mode = get_address_mode (mem);
rtx mem_address = XEXP (mem, 0);
rtx expanded_address, address;
int expanded;
}
-static rtx get_stored_val (store_info_t, enum machine_mode, HOST_WIDE_INT,
+static rtx get_stored_val (store_info_t, machine_mode, HOST_WIDE_INT,
HOST_WIDE_INT, basic_block, bool);
static rtx
find_shift_sequence (int access_size,
store_info_t store_info,
- enum machine_mode read_mode,
+ machine_mode read_mode,
int shift, bool speed, bool require_cst)
{
- enum machine_mode store_mode = GET_MODE (store_info->mem);
- enum machine_mode new_mode;
+ machine_mode store_mode = GET_MODE (store_info->mem);
+ machine_mode new_mode;
rtx read_reg = NULL;
/* Some machines like the x86 have shift insns for each size of
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;
+ rtx target, new_reg, new_lhs;
+ rtx_insn *shift_seq, *insn;
int cost;
/* If a constant was stored into memory, try to simplify it here,
if not successful. If REQUIRE_CST is true, return always constant. */
static rtx
-get_stored_val (store_info_t store_info, enum machine_mode read_mode,
+get_stored_val (store_info_t store_info, machine_mode read_mode,
HOST_WIDE_INT read_begin, HOST_WIDE_INT read_end,
basic_block bb, bool require_cst)
{
- enum machine_mode store_mode = GET_MODE (store_info->mem);
+ machine_mode store_mode = GET_MODE (store_info->mem);
int shift;
int access_size; /* In bytes. */
rtx read_reg;
read_info_t read_info, insn_info_t read_insn, rtx *loc,
bitmap regs_live)
{
- enum machine_mode store_mode = GET_MODE (store_info->mem);
- enum machine_mode read_mode = GET_MODE (read_info->mem);
- rtx insns, this_insn, read_reg;
+ machine_mode store_mode = GET_MODE (store_info->mem);
+ machine_mode read_mode = GET_MODE (read_info->mem);
+ rtx_insn *insns, *this_insn;
+ rtx read_reg;
basic_block bb;
if (!dbg_cnt (dse))
}
}
-/* 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. */
+/* Check the address of MEM *LOC and kill any appropriate stores that may
+ be active. */
-static int
-check_mem_read_rtx (rtx *loc, void *data)
+static void
+check_mem_read_rtx (rtx *loc, bb_info_t bb_info)
{
rtx mem = *loc, mem_addr;
- bb_info_t bb_info;
insn_info_t insn_info;
HOST_WIDE_INT offset = 0;
HOST_WIDE_INT width = 0;
int group_id;
read_info_t read_info;
- if (!mem || !MEM_P (mem))
- return 0;
-
- bb_info = (bb_info_t) data;
insn_info = bb_info->last_insn;
if ((MEM_ALIAS_SET (mem) == ALIAS_SET_MEMORY_BARRIER)
fprintf (dump_file, " adding wild read, volatile or barrier.\n");
add_wild_read (bb_info);
insn_info->cannot_delete = true;
- return 0;
+ return;
}
/* If it is reading readonly mem, then there can be no conflict with
another write. */
if (MEM_READONLY_P (mem))
- return 0;
+ return;
if (!canon_address (mem, &spill_alias_set, &group_id, &offset, &base))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " adding wild read, canon_address failure.\n");
add_wild_read (bb_info);
- return 0;
+ return;
}
if (GET_MODE (mem) == BLKmode)
width)
&& replace_read (store_info, i_ptr, read_info,
insn_info, loc, bb_info->regs_live))
- return 0;
+ return;
/* The bases are the same, just see if the offsets
overlap. */
offset - store_info->begin, width)
&& replace_read (store_info, i_ptr, read_info, insn_info, loc,
bb_info->regs_live))
- return 0;
+ return;
if (!store_info->alias_set)
remove = canon_true_dependence (store_info->mem,
i_ptr = i_ptr->next_local_store;
}
}
- return 0;
}
-/* A for_each_rtx callback in which DATA points the INSN_INFO for
+/* A note_uses callback in which DATA points the INSN_INFO for
as check_mem_read_rtx. Nullify the pointer if i_m_r_m_r returns
true for any part of *LOC. */
static void
check_mem_read_use (rtx *loc, void *data)
{
- for_each_rtx (loc, check_mem_read_rtx, data);
+ subrtx_ptr_iterator::array_type array;
+ FOR_EACH_SUBRTX_PTR (iter, array, loc, NONCONST)
+ {
+ rtx *loc = *iter;
+ if (MEM_P (*loc))
+ check_mem_read_rtx (loc, (bb_info_t) data);
+ }
}
arg != void_list_node && idx < nargs;
arg = TREE_CHAIN (arg), idx++)
{
- enum machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
+ machine_mode mode = TYPE_MODE (TREE_VALUE (arg));
rtx reg, link, tmp;
reg = targetm.calls.function_arg (args_so_far, mode, NULL_TREE, true);
if (!reg || !REG_P (reg) || GET_MODE (reg) != mode
const_call ? "const" : "memset", INSN_UID (insn));
/* See the head comment of the frame_read field. */
- if (reload_completed)
+ if (reload_completed
+ /* Tail calls are storing their arguments using
+ arg pointer. If it is a frame pointer on the target,
+ even before reload we need to kill frame pointer based
+ stores. */
+ || (SIBLING_CALL_P (insn)
+ && HARD_FRAME_POINTER_IS_ARG_POINTER))
insn_info->frame_read = true;
/* Loop over the active stores and remove those which are
}
}
}
-
+ else if (SIBLING_CALL_P (insn) && reload_completed)
+ /* Arguments for a sibling call that are pushed to memory are passed
+ using the incoming argument pointer of the current function. After
+ reload that might be (and likely is) frame pointer based. */
+ add_wild_read (bb_info);
else
/* Every other call, including pure functions, may read any memory
that is not relative to the frame. */
insn_info_t ptr;
bb_info_t bb_info = (bb_info_t) pool_alloc (bb_info_pool);
- memset (bb_info, 0, sizeof (struct bb_info));
+ memset (bb_info, 0, sizeof (struct dse_bb_info));
bitmap_set_bit (all_blocks, bb->index);
bb_info->regs_live = regs_live;
if (dump_file)
fprintf (dump_file, "dse: local deletions = %d, global deletions = %d, spill deletions = %d\n",
locally_deleted, globally_deleted, spill_deleted);
+
+ /* DSE can eliminate potentially-trapping MEMs.
+ Remove any EH edges associated with them. */
+ if ((locally_deleted || globally_deleted)
+ && cfun->can_throw_non_call_exceptions
+ && purge_all_dead_edges ())
+ cleanup_cfg (0);
+
return 0;
}