/* Post reload partially redundant load elimination
- Copyright (C) 2004-2015 Free Software Foundation, Inc.
+ Copyright (C) 2004-2021 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 "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 "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 "predict.h"
-#include "function.h"
-#include "dominance.h"
-#include "cfg.h"
+
#include "cfgrtl.h"
-#include "basic-block.h"
#include "profile.h"
-#include "hashtab.h"
-#include "statistics.h"
-#include "real.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 "except.h"
-#include "intl.h"
-#include "obstack.h"
-#include "params.h"
-#include "target.h"
#include "tree-pass.h"
#include "dbgcnt.h"
-#include "df.h"
+#include "intl.h"
#include "gcse-common.h"
+#include "gcse.h"
+#include "regs.h"
+#include "function-abi.h"
/* The following code implements gcse after reload, the purpose of this
pass is to cleanup redundant loads generated by reload and other
/* 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);
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
int do_not_record_p;
hashval_t hash;
struct expr *cur_expr, **slot;
- struct occr *avail_occr, *last_occr = NULL;
+ struct occr *avail_occr;
hash = hash_expr (x, &do_not_record_p);
cur_expr = *slot;
}
- /* Search for another occurrence in the same basic block. */
+ /* Search for another occurrence in the same basic block. We insert
+ insns blockwise from start to end, so keep appending to the
+ start of the list so we have to check only a single element. */
avail_occr = cur_expr->avail_occr;
- while (avail_occr
- && BLOCK_FOR_INSN (avail_occr->insn) != BLOCK_FOR_INSN (insn))
- {
- /* If an occurrence isn't found, save a pointer to the end of
- the list. */
- last_occr = avail_occr;
- avail_occr = avail_occr->next;
- }
-
- if (avail_occr)
- /* Found another instance of the expression in the same basic block.
- Prefer this occurrence to the currently recorded one. We want
- the last one in the block and the block is scanned from start
- to end. */
+ if (avail_occr
+ && BLOCK_FOR_INSN (avail_occr->insn) == BLOCK_FOR_INSN (insn))
avail_occr->insn = insn;
else
{
/* First occurrence of this expression in this basic block. */
avail_occr = (struct occr *) obstack_alloc (&occr_obstack,
sizeof (struct occr));
-
- /* First occurrence of this expression in any block? */
- if (cur_expr->avail_occr == NULL)
- cur_expr->avail_occr = avail_occr;
- else
- last_occr->next = avail_occr;
-
avail_occr->insn = insn;
- avail_occr->next = NULL;
+ avail_occr->next = cur_expr->avail_occr;
avail_occr->deleted_p = 0;
+ cur_expr->avail_occr = avail_occr;
}
}
\f
(long) expr_table->size (),
(long) expr_table->elements (),
expr_table->collisions ());
- if (expr_table->elements () > 0)
+ if (!expr_table->is_empty ())
{
fprintf (file, "\n\ntable entries:\n");
expr_table->traverse <FILE *, dump_expr_hash_table_entry> (file);
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;
return oprs_unchanged_p (XEXP (x, 0), insn, after_insn);
case PC:
- case CC0: /*FIXME*/
case CONST:
CASE_CONST_ANY:
case SYMBOL_REF:
It will set mems_conflict_p to nonzero if there may be a
conflict between X and SETTER. */
mems_conflict_p = 0;
- note_stores (PATTERN (setter), find_mem_conflicts, x);
+ note_stores (setter, find_mem_conflicts, x);
if (mems_conflict_p)
return 1;
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);
rtx note;
/* Find all stores and record them. */
- note_stores (PATTERN (insn), record_last_set_info, insn);
+ note_stores (insn, record_last_set_info, insn);
/* Also record autoincremented REGs for this insn as changed. */
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
if (CALL_P (insn))
{
unsigned int regno;
- rtx link, x;
hard_reg_set_iterator hrsi;
- EXECUTE_IF_SET_IN_HARD_REG_SET (regs_invalidated_by_call, 0, regno, hrsi)
+ /* We don't track modes of hard registers, so we need to be
+ conservative and assume that partial kills are full kills. */
+ HARD_REG_SET callee_clobbers
+ = insn_callee_abi (insn).full_and_partial_reg_clobbers ();
+ EXECUTE_IF_SET_IN_HARD_REG_SET (callee_clobbers, 0, regno, hrsi)
record_last_reg_set_info_regno (insn, regno);
- for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
- if (GET_CODE (XEXP (link, 0)) == CLOBBER)
- {
- x = XEXP (XEXP (link, 0), 0);
- if (REG_P (x))
- {
- gcc_assert (HARD_REGISTER_P (x));
- record_last_reg_set_info (insn, x);
- }
- }
-
- if (! RTL_CONST_OR_PURE_CALL_P (insn))
+ if (! RTL_CONST_OR_PURE_CALL_P (insn)
+ || RTL_LOOPING_CONST_OR_PURE_CALL_P (insn)
+ || can_throw_external (insn))
record_last_mem_set_info (insn);
}
}
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)
/* 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)
+ if (transp
+ && single_pred_p (bb)
&& bitmap_bit_p (transp[bb->index], bitmap_index)
&& (occr = get_bb_avail_insn (single_pred (bb), orig_occr, bitmap_index)))
{
struct unoccr *occr, *avail_occrs = NULL;
struct unoccr *unoccr, *unavail_occrs = NULL, *rollback_unoccr = NULL;
int npred_ok = 0;
- gcov_type ok_count = 0; /* Redundant load execution count. */
- gcov_type critical_count = 0; /* Execution count of critical edges. */
+ profile_count ok_count = profile_count::zero ();
+ /* Redundant load execution count. */
+ profile_count critical_count = profile_count::zero ();
+ /* Execution count of critical edges. */
edge_iterator ei;
bool critical_edge_split = false;
/* The execution count of the loads to be added to make the
load fully redundant. */
- gcov_type not_ok_count = 0;
+ profile_count not_ok_count = profile_count::zero ();
basic_block pred_bb;
pat = PATTERN (insn);
/* Make sure we can generate a move from register avail_reg to
dest. */
- rtx_insn *move = as_a <rtx_insn *>
- (gen_move_insn (copy_rtx (dest), copy_rtx (avail_reg)));
+ 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))
avail_insn = NULL;
}
- if (EDGE_CRITICAL_P (pred))
- critical_count += pred->count;
+ if (EDGE_CRITICAL_P (pred) && pred->count ().initialized_p ())
+ critical_count += pred->count ();
if (avail_insn != NULL_RTX)
{
npred_ok++;
- ok_count += pred->count;
+ if (pred->count ().initialized_p ())
+ ok_count = ok_count + pred->count ();
if (! set_noop_p (PATTERN (gen_move_insn (copy_rtx (dest),
copy_rtx (avail_reg)))))
{
/* Adding a load on a critical edge will cause a split. */
if (EDGE_CRITICAL_P (pred))
critical_edge_split = true;
- not_ok_count += pred->count;
+ if (pred->count ().initialized_p ())
+ not_ok_count = not_ok_count + pred->count ();
unoccr = (struct unoccr *) obstack_alloc (&unoccr_obstack,
sizeof (struct unoccr));
unoccr->insn = NULL;
|| (optimize_bb_for_size_p (bb) && npred_ok > 1)
/* If we don't have profile information we cannot tell if splitting
a critical edge is profitable or not so don't do it. */
- || ((! profile_info || ! flag_branch_probabilities
+ || ((!profile_info || profile_status_for_fn (cfun) != PROFILE_READ
|| targetm.cannot_modify_jumps_p ())
&& critical_edge_split))
goto cleanup;
/* Check if it's worth applying the partial redundancy elimination. */
- if (ok_count < GCSE_AFTER_RELOAD_PARTIAL_FRACTION * not_ok_count)
+ if (ok_count.to_gcov_type ()
+ < param_gcse_after_reload_partial_fraction * not_ok_count.to_gcov_type ())
goto cleanup;
- if (ok_count < GCSE_AFTER_RELOAD_CRITICAL_FRACTION * critical_count)
+
+ gcov_type threshold;
+#if (GCC_VERSION >= 5000)
+ if (__builtin_mul_overflow (param_gcse_after_reload_critical_fraction,
+ critical_count.to_gcov_type (), &threshold))
+ threshold = profile_count::max_count;
+#else
+ threshold
+ = (param_gcse_after_reload_critical_fraction
+ * critical_count.to_gcov_type ());
+#endif
+
+ if (ok_count.to_gcov_type () < threshold)
goto cleanup;
/* Generate moves to the loaded register from where
static void
gcse_after_reload_main (rtx f ATTRIBUTE_UNUSED)
{
+ /* Disable computing transparentness if it is too expensive. */
+ bool do_transp
+ = !gcse_or_cprop_is_too_expensive (_("using simple load CSE after register "
+ "allocation"));
memset (&stats, 0, sizeof (stats));
if (dump_file)
dump_hash_table (dump_file);
- if (expr_table->elements () > 0)
+ if (!expr_table->is_empty ())
{
/* 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);
+ if (do_transp)
+ {
+ /* This cannot 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);
+ }
+ else
+ transp = NULL;
eliminate_partially_redundant_loads ();
delete_redundant_insns ();
- sbitmap_vector_free (transp);
+ if (do_transp)
+ sbitmap_vector_free (transp);
if (dump_file)
{