/* Post reload partially redundant load elimination
- Copyright (C) 2004-2014 Free Software Foundation, Inc.
+ Copyright (C) 2004-2017 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
-#include "diagnostic-core.h"
-
-#include "hash-table.h"
+#include "backend.h"
+#include "target.h"
#include "rtl.h"
#include "tree.h"
+#include "predict.h"
+#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
-#include "regs.h"
-#include "hard-reg-set.h"
-#include "flags.h"
#include "insn-config.h"
+#include "emit-rtl.h"
#include "recog.h"
-#include "basic-block.h"
-#include "function.h"
+
+#include "cfgrtl.h"
+#include "profile.h"
#include "expr.h"
-#include "except.h"
-#include "intl.h"
-#include "obstack.h"
-#include "hashtab.h"
#include "params.h"
-#include "target.h"
#include "tree-pass.h"
#include "dbgcnt.h"
+#include "gcse-common.h"
/* The following code implements gcse after reload, the purpose of this
pass is to cleanup redundant loads generated by reload and other
/* The same hash for this entry. */
hashval_t hash;
+ /* Index in the transparent bitmaps. */
+ unsigned int bitmap_index;
+
/* List of available occurrence in basic blocks in the function. */
struct occr *avail_occr;
};
/* Hashtable helpers. */
-struct expr_hasher : typed_noop_remove <expr>
+struct expr_hasher : nofree_ptr_hash <expr>
{
- typedef expr value_type;
- typedef expr compare_type;
- static inline hashval_t hash (const value_type *);
- static inline bool equal (const value_type *, const compare_type *);
+ static inline hashval_t hash (const expr *);
+ static inline bool equal (const expr *, const expr *);
};
here, we just return the cached hash value. */
inline hashval_t
-expr_hasher::hash (const value_type *exp)
+expr_hasher::hash (const expr *exp)
{
return exp->hash;
}
Return nonzero if exp1 is equivalent to exp2. */
inline bool
-expr_hasher::equal (const value_type *exp1, const compare_type *exp2)
+expr_hasher::equal (const expr *exp1, const expr *exp2)
{
int equiv_p = exp_equiv_p (exp1->expr, exp2->expr, 0, true);
/* Next occurrence of this expression. */
struct occr *next;
/* The insn that computes the expression. */
- rtx insn;
+ rtx_insn *insn;
/* Nonzero if this [anticipatable] occurrence has been deleted. */
char deleted_p;
};
{
struct unoccr *next;
edge pred;
- rtx insn;
+ rtx_insn *insn;
};
static struct obstack unoccr_obstack;
/* A list of insns that may modify memory within the current basic block. */
struct modifies_mem
{
- rtx insn;
+ rtx_insn *insn;
struct modifies_mem *next;
};
static struct modifies_mem *modifies_mem_list;
block, have no gaps, and only apply to real insns. */
static int *uid_cuid;
#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)])
+
+/* Bitmap of blocks which have memory stores. */
+static bitmap modify_mem_list_set;
+
+/* Bitmap of blocks which have calls. */
+static bitmap blocks_with_calls;
+
+/* Vector indexed by block # with a list of all the insns that
+ modify memory within the block. */
+static vec<rtx_insn *> *modify_mem_list;
+
+/* Vector indexed by block # with a canonicalized list of insns
+ that modify memory in the block. */
+static vec<modify_pair> *canon_modify_mem_list;
+
+/* Vector of simple bitmaps indexed by block number. Each component sbitmap
+ indicates which expressions are transparent through the block. */
+static sbitmap *transp;
\f
/* Helpers for memory allocation/freeing. */
static void free_mem (void);
/* Support for hash table construction and transformations. */
-static bool oprs_unchanged_p (rtx, rtx, bool);
-static void record_last_reg_set_info (rtx, rtx);
-static void record_last_reg_set_info_regno (rtx, int);
-static void record_last_mem_set_info (rtx);
+static bool oprs_unchanged_p (rtx, rtx_insn *, bool);
+static void record_last_reg_set_info (rtx_insn *, rtx);
+static void record_last_reg_set_info_regno (rtx_insn *, int);
+static void record_last_mem_set_info (rtx_insn *);
static void record_last_set_info (rtx, const_rtx, void *);
-static void record_opr_changes (rtx);
+static void record_opr_changes (rtx_insn *);
static void find_mem_conflicts (rtx, const_rtx, void *);
static int load_killed_in_block_p (int, rtx, bool);
/* Hash table support. */
static hashval_t hash_expr (rtx, int *);
-static void insert_expr_in_table (rtx, rtx);
+static void insert_expr_in_table (rtx, rtx_insn *);
static struct expr *lookup_expr_in_table (rtx);
static void dump_hash_table (FILE *);
static bool reg_killed_on_edge (rtx, edge);
static bool reg_used_on_edge (rtx, edge);
-static rtx get_avail_load_store_reg (rtx);
+static rtx get_avail_load_store_reg (rtx_insn *);
static bool bb_has_well_behaved_predecessors (basic_block);
-static struct occr* get_bb_avail_insn (basic_block, struct occr *);
-static void hash_scan_set (rtx);
+static struct occr* get_bb_avail_insn (basic_block, struct occr *, int);
+static void hash_scan_set (rtx_insn *);
static void compute_hash_table (void);
/* The work horses of this pass. */
static void eliminate_partially_redundant_load (basic_block,
- rtx,
+ rtx_insn *,
struct expr *);
static void eliminate_partially_redundant_loads (void);
\f
{
int i;
basic_block bb;
- rtx insn;
+ rtx_insn *insn;
/* Find the largest UID and create a mapping from UIDs to CUIDs. */
uid_cuid = XCNEWVEC (int, get_max_uid () + 1);
modifies_mem_obstack_bottom =
(struct modifies_mem *) obstack_alloc (&modifies_mem_obstack,
sizeof (struct modifies_mem));
+
+ blocks_with_calls = BITMAP_ALLOC (NULL);
+ modify_mem_list_set = BITMAP_ALLOC (NULL);
+
+ modify_mem_list = (vec_rtx_heap *) xcalloc (last_basic_block_for_fn (cfun),
+ sizeof (vec_rtx_heap));
+ canon_modify_mem_list
+ = (vec_modify_pair_heap *) xcalloc (last_basic_block_for_fn (cfun),
+ sizeof (vec_modify_pair_heap));
}
/* Free memory allocated by alloc_mem. */
obstack_free (&unoccr_obstack, NULL);
obstack_free (&modifies_mem_obstack, NULL);
+ unsigned i;
+ bitmap_iterator bi;
+ EXECUTE_IF_SET_IN_BITMAP (modify_mem_list_set, 0, i, bi)
+ {
+ modify_mem_list[i].release ();
+ canon_modify_mem_list[i].release ();
+ }
+
+ BITMAP_FREE (blocks_with_calls);
+ BITMAP_FREE (modify_mem_list_set);
free (reg_avail_info);
+ free (modify_mem_list);
+ free (canon_modify_mem_list);
}
\f
basic block. */
static void
-insert_expr_in_table (rtx x, rtx insn)
+insert_expr_in_table (rtx x, rtx_insn *insn)
{
int do_not_record_p;
hashval_t hash;
slot = expr_table->find_slot_with_hash (cur_expr, hash, INSERT);
if (! (*slot))
- /* The expression isn't found, so insert it. */
- *slot = cur_expr;
+ {
+ /* The expression isn't found, so insert it. */
+ *slot = cur_expr;
+
+ /* Anytime we add an entry to the table, record the index
+ of the new entry. The bitmap index starts counting
+ at zero. */
+ cur_expr->bitmap_index = expr_table->elements () - 1;
+ }
else
{
/* The expression is already in the table, so roll back the
occr = exprs->avail_occr;
while (occr)
{
- rtx insn = occr->insn;
+ rtx_insn *insn = occr->insn;
print_rtl_single (file, insn);
fprintf (file, "\n");
occr = occr->next;
unsigned int regno, end_regno;
regno = REGNO (x);
- end_regno = END_HARD_REGNO (x);
+ end_regno = END_REGNO (x);
do
if (reg_avail_info[regno] > cuid)
return true;
2) from INSN to the end of INSN's basic block if AFTER_INSN is true. */
static bool
-oprs_unchanged_p (rtx x, rtx insn, bool after_insn)
+oprs_unchanged_p (rtx x, rtx_insn *insn, bool after_insn)
{
int i, j;
enum rtx_code code;
while (list_entry)
{
- rtx setter = list_entry->insn;
+ rtx_insn *setter = list_entry->insn;
/* Ignore entries in the list that do not apply. */
if ((after_insn
/* Record register first/last/block set information for REGNO in INSN. */
static inline void
-record_last_reg_set_info (rtx insn, rtx reg)
+record_last_reg_set_info (rtx_insn *insn, rtx reg)
{
unsigned int regno, end_regno;
regno = REGNO (reg);
- end_regno = END_HARD_REGNO (reg);
+ end_regno = END_REGNO (reg);
do
reg_avail_info[regno] = INSN_CUID (insn);
while (++regno < end_regno);
}
static inline void
-record_last_reg_set_info_regno (rtx insn, int regno)
+record_last_reg_set_info_regno (rtx_insn *insn, int regno)
{
reg_avail_info[regno] = INSN_CUID (insn);
}
a CALL_INSN). We merely need to record which insns modify memory. */
static void
-record_last_mem_set_info (rtx insn)
+record_last_mem_set_info (rtx_insn *insn)
{
struct modifies_mem *list_entry;
list_entry->insn = insn;
list_entry->next = modifies_mem_list;
modifies_mem_list = list_entry;
+
+ record_last_mem_set_info_common (insn, modify_mem_list,
+ canon_modify_mem_list,
+ modify_mem_list_set,
+ blocks_with_calls);
}
/* Called from compute_hash_table via note_stores to handle one
static void
record_last_set_info (rtx dest, const_rtx setter ATTRIBUTE_UNUSED, void *data)
{
- rtx last_set_insn = (rtx) data;
+ rtx_insn *last_set_insn = (rtx_insn *) data;
if (GET_CODE (dest) == SUBREG)
dest = SUBREG_REG (dest);
This data is used by oprs_unchanged_p. */
static void
-record_opr_changes (rtx insn)
+record_opr_changes (rtx_insn *insn)
{
rtx note;
After reload we are interested in loads/stores only. */
static void
-hash_scan_set (rtx insn)
+hash_scan_set (rtx_insn *insn)
{
rtx pat = PATTERN (insn);
rtx src = SET_SRC (pat);
FOR_EACH_BB_FN (bb, cfun)
{
- rtx insn;
+ rtx_insn *insn;
/* First pass over the instructions records information used to
determine when registers and memory are last set.
static bool
reg_killed_on_edge (rtx reg, edge e)
{
- rtx insn;
+ rtx_insn *insn;
for (insn = e->insns.r; insn; insn = NEXT_INSN (insn))
if (INSN_P (insn) && reg_set_p (reg, insn))
static bool
reg_used_on_edge (rtx reg, edge e)
{
- rtx insn;
+ rtx_insn *insn;
for (insn = e->insns.r; insn; insn = NEXT_INSN (insn))
if (INSN_P (insn) && reg_overlap_mentioned_p (reg, PATTERN (insn)))
/* Return the loaded/stored register of a load/store instruction. */
static rtx
-get_avail_load_store_reg (rtx insn)
+get_avail_load_store_reg (rtx_insn *insn)
{
if (REG_P (SET_DEST (PATTERN (insn))))
/* A load. */
FOR_EACH_EDGE (pred, ei, bb->preds)
{
- if ((pred->flags & EDGE_ABNORMAL) && EDGE_CRITICAL_P (pred))
+ /* commit_one_edge_insertion refuses to insert on abnormal edges even if
+ the source has only one successor so EDGE_CRITICAL_P is too weak. */
+ if ((pred->flags & EDGE_ABNORMAL) && !single_pred_p (pred->dest))
return false;
if ((pred->flags & EDGE_ABNORMAL_CALL) && cfun->has_nonlocal_label)
/* Search for the occurrences of expression in BB. */
static struct occr*
-get_bb_avail_insn (basic_block bb, struct occr *occr)
+get_bb_avail_insn (basic_block bb, struct occr *orig_occr, int bitmap_index)
{
+ struct occr *occr = orig_occr;
+
for (; occr != NULL; occr = occr->next)
if (BLOCK_FOR_INSN (occr->insn) == bb)
return occr;
+
+ /* If we could not find an occurrence in BB, see if BB
+ has a single predecessor with an occurrence that is
+ transparent through BB. */
+ if (single_pred_p (bb)
+ && bitmap_bit_p (transp[bb->index], bitmap_index)
+ && (occr = get_bb_avail_insn (single_pred (bb), orig_occr, bitmap_index)))
+ {
+ rtx avail_reg = get_avail_load_store_reg (occr->insn);
+ if (!reg_set_between_p (avail_reg,
+ PREV_INSN (BB_HEAD (bb)),
+ NEXT_INSN (BB_END (bb)))
+ && !reg_killed_on_edge (avail_reg, single_pred_edge (bb)))
+ return occr;
+ }
+
return NULL;
}
+/* This helper is called via htab_traverse. */
+int
+compute_expr_transp (expr **slot, FILE *dump_file ATTRIBUTE_UNUSED)
+{
+ struct expr *expr = *slot;
+
+ compute_transp (expr->expr, expr->bitmap_index, transp,
+ blocks_with_calls, modify_mem_list_set,
+ canon_modify_mem_list);
+ return 1;
+}
+
/* This handles the case where several stores feed a partially redundant
load. It checks if the redundancy elimination is possible and if it's
worth it.
a redundancy is also worth doing, assuming it is possible. */
static void
-eliminate_partially_redundant_load (basic_block bb, rtx insn,
+eliminate_partially_redundant_load (basic_block bb, rtx_insn *insn,
struct expr *expr)
{
edge pred;
- rtx avail_insn = NULL_RTX;
+ rtx_insn *avail_insn = NULL;
rtx avail_reg;
rtx dest, pat;
struct occr *a_occr;
/* Check potential for replacing load with copy for predecessors. */
FOR_EACH_EDGE (pred, ei, bb->preds)
{
- rtx next_pred_bb_end;
+ rtx_insn *next_pred_bb_end;
- avail_insn = NULL_RTX;
+ avail_insn = NULL;
avail_reg = NULL_RTX;
pred_bb = pred->src;
- next_pred_bb_end = NEXT_INSN (BB_END (pred_bb));
- for (a_occr = get_bb_avail_insn (pred_bb, expr->avail_occr); a_occr;
- a_occr = get_bb_avail_insn (pred_bb, a_occr->next))
+ for (a_occr = get_bb_avail_insn (pred_bb,
+ expr->avail_occr,
+ expr->bitmap_index);
+ a_occr;
+ a_occr = get_bb_avail_insn (pred_bb,
+ a_occr->next,
+ expr->bitmap_index))
{
/* Check if the loaded register is not used. */
avail_insn = a_occr->insn;
/* Make sure we can generate a move from register avail_reg to
dest. */
- extract_insn (gen_move_insn (copy_rtx (dest),
- copy_rtx (avail_reg)));
- if (! constrain_operands (1)
+ rtx_insn *move = gen_move_insn (copy_rtx (dest),
+ copy_rtx (avail_reg));
+ extract_insn (move);
+ if (! constrain_operands (1, get_preferred_alternatives (insn,
+ pred_bb))
|| reg_killed_on_edge (avail_reg, pred)
|| reg_used_on_edge (dest, pred))
{
avail_insn = NULL;
continue;
}
+ next_pred_bb_end = NEXT_INSN (BB_END (BLOCK_FOR_INSN (avail_insn)));
if (!reg_set_between_p (avail_reg, avail_insn, next_pred_bb_end))
/* AVAIL_INSN remains non-null. */
break;
not_ok_count += pred->count;
unoccr = (struct unoccr *) obstack_alloc (&unoccr_obstack,
sizeof (struct unoccr));
- unoccr->insn = NULL_RTX;
+ unoccr->insn = NULL;
unoccr->pred = pred;
unoccr->next = unavail_occrs;
unavail_occrs = unoccr;
/* Delete the insn if it is not available in this block and mark it
for deletion if it is available. If insn is available it may help
discover additional redundancies, so mark it for later deletion. */
- for (a_occr = get_bb_avail_insn (bb, expr->avail_occr);
+ for (a_occr = get_bb_avail_insn (bb, expr->avail_occr, expr->bitmap_index);
a_occr && (a_occr->insn != insn);
- a_occr = get_bb_avail_insn (bb, a_occr->next))
+ a_occr = get_bb_avail_insn (bb, a_occr->next, expr->bitmap_index))
;
if (!a_occr)
static void
eliminate_partially_redundant_loads (void)
{
- rtx insn;
+ rtx_insn *insn;
basic_block bb;
/* Note we start at block 1. */
if (expr_table->elements () > 0)
{
+ /* Knowing which MEMs are transparent through a block can signifiantly
+ increase the number of redundant loads found. So compute transparency
+ information for each memory expression in the hash table. */
+ df_analyze ();
+ /* This can not be part of the normal allocation routine because
+ we have to know the number of elements in the hash table. */
+ transp = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
+ expr_table->elements ());
+ bitmap_vector_ones (transp, last_basic_block_for_fn (cfun));
+ expr_table->traverse <FILE *, compute_expr_transp> (dump_file);
eliminate_partially_redundant_loads ();
delete_redundant_insns ();
+ sbitmap_vector_free (transp);
if (dump_file)
{
RTL_PASS, /* type */
"gcse2", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_execute */
TV_GCSE_AFTER_RELOAD, /* tv_id */
0, /* properties_required */
0, /* properties_provided */