/* RTL-based forward propagation pass for GNU compiler.
- Copyright (C) 2005-2015 Free Software Foundation, Inc.
+ Copyright (C) 2005-2020 Free Software Foundation, Inc.
Contributed by Paolo Bonzini and Steven Bosscher.
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
-#include "diagnostic-core.h"
-
-#include "sparseset.h"
+#include "backend.h"
+#include "target.h"
#include "rtl.h"
+#include "predict.h"
+#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
#include "insn-config.h"
+#include "emit-rtl.h"
#include "recog.h"
-#include "flags.h"
-#include "obstack.h"
-#include "predict.h"
-#include "hard-reg-set.h"
-#include "function.h"
-#include "dominance.h"
-#include "cfg.h"
+
+#include "sparseset.h"
#include "cfgrtl.h"
#include "cfgcleanup.h"
-#include "basic-block.h"
-#include "df.h"
-#include "target.h"
#include "cfgloop.h"
#include "tree-pass.h"
#include "domwalk.h"
-#include "emit-rtl.h"
#include "rtl-iter.h"
static vec<df_ref> reg_defs;
static vec<df_ref> reg_defs_stack;
+/* The maximum number of propagations that are still allowed. If we do
+ more propagations than originally we had uses, we must have ended up
+ in a propagation loop, as in PR79405. Until the algorithm fwprop
+ uses can obviously not get into such loops we need a workaround like
+ this. */
+static int propagations_left;
+
/* The MD bitmaps are trimmed to include only live registers to cut
memory usage on testcases like insn-recog.c. Track live registers
in the basic block and do not perform forward propagation if the
public:
single_def_use_dom_walker (cdi_direction direction)
: dom_walker (direction) {}
- virtual void before_dom_children (basic_block);
+ virtual edge before_dom_children (basic_block);
virtual void after_dom_children (basic_block);
};
-void
+edge
single_def_use_dom_walker::before_dom_children (basic_block bb)
{
int bb_index = bb->index;
- struct df_md_bb_info *md_bb_info = df_md_get_bb_info (bb_index);
- struct df_lr_bb_info *lr_bb_info = df_lr_get_bb_info (bb_index);
+ class df_md_bb_info *md_bb_info = df_md_get_bb_info (bb_index);
+ class df_lr_bb_info *lr_bb_info = df_lr_get_bb_info (bb_index);
rtx_insn *insn;
bitmap_copy (local_md, &md_bb_info->in);
process_uses (df_get_artificial_uses (bb_index), 0);
process_defs (df_get_artificial_defs (bb_index), 0);
+
+ return NULL;
}
/* Pop the definitions created in this basic block when leaving its
df_maybe_reorganize_use_refs (DF_REF_ORDER_BY_INSN_WITH_NOTES);
use_def_ref.create (DF_USES_TABLE_SIZE ());
- use_def_ref.safe_grow_cleared (DF_USES_TABLE_SIZE ());
+ use_def_ref.safe_grow_cleared (DF_USES_TABLE_SIZE (), true);
reg_defs.create (max_reg_num ());
- reg_defs.safe_grow_cleared (max_reg_num ());
+ reg_defs.safe_grow_cleared (max_reg_num (), true);
reg_defs_stack.create (n_basic_blocks_for_fn (cfun) * 10);
local_md = BITMAP_ALLOC (NULL);
{
case ASHIFT:
if (CONST_INT_P (XEXP (x, 1))
- && INTVAL (XEXP (x, 1)) < GET_MODE_BITSIZE (GET_MODE (x))
- && INTVAL (XEXP (x, 1)) >= 0)
+ && INTVAL (XEXP (x, 1)) < GET_MODE_UNIT_BITSIZE (GET_MODE (x))
+ && INTVAL (XEXP (x, 1)) >= 0)
{
HOST_WIDE_INT shift = INTVAL (XEXP (x, 1));
PUT_CODE (x, MULT);
- XEXP (x, 1) = gen_int_mode ((HOST_WIDE_INT) 1 << shift,
+ XEXP (x, 1) = gen_int_mode (HOST_WIDE_INT_1 << shift,
GET_MODE (x));
}
eliminating the most insns without additional costs, and it
is the same that cse.c used to do. */
if (gain == 0)
- gain = set_src_cost (new_rtx, speed) - set_src_cost (old_rtx, speed);
+ gain = (set_src_cost (new_rtx, VOIDmode, speed)
+ - set_src_cost (old_rtx, VOIDmode, speed));
return (gain > 0);
}
PR_OPTIMIZE_FOR_SPEED = 4
};
+/* Check that X has a single def. */
+
+static bool
+reg_single_def_p (rtx x)
+{
+ if (!REG_P (x))
+ return false;
+
+ int regno = REGNO (x);
+ return (DF_REG_DEF_COUNT (regno) == 1
+ && !bitmap_bit_p (DF_LR_OUT (ENTRY_BLOCK_PTR_FOR_FN (cfun)), regno));
+}
/* Replace all occurrences of OLD in *PX with NEW and try to simplify the
resulting expression. Replace *PX with a new RTL expression if an
tem = simplify_gen_subreg (mode, op0, GET_MODE (SUBREG_REG (x)),
SUBREG_BYTE (x));
}
+
+ else
+ {
+ rtvec vec;
+ rtvec newvec;
+ const char *fmt = GET_RTX_FORMAT (code);
+ rtx op;
+
+ for (int i = 0; fmt[i]; i++)
+ switch (fmt[i])
+ {
+ case 'E':
+ vec = XVEC (x, i);
+ newvec = vec;
+ for (int j = 0; j < GET_NUM_ELEM (vec); j++)
+ {
+ op = RTVEC_ELT (vec, j);
+ valid_ops &= propagate_rtx_1 (&op, old_rtx, new_rtx, flags);
+ if (op != RTVEC_ELT (vec, j))
+ {
+ if (newvec == vec)
+ {
+ newvec = shallow_copy_rtvec (vec);
+ if (!tem)
+ tem = shallow_copy_rtx (x);
+ XVEC (tem, i) = newvec;
+ }
+ RTVEC_ELT (newvec, j) = op;
+ }
+ }
+ break;
+
+ case 'e':
+ if (XEXP (x, i))
+ {
+ op = XEXP (x, i);
+ valid_ops &= propagate_rtx_1 (&op, old_rtx, new_rtx, flags);
+ if (op != XEXP (x, i))
+ {
+ if (!tem)
+ tem = shallow_copy_rtx (x);
+ XEXP (tem, i) = op;
+ }
+ }
+ break;
+ }
+ }
+
break;
case RTX_OBJ:
*px = tem;
+ /* Allow replacements that simplify operations on a vector or complex
+ value to a component. The most prominent case is
+ (subreg ([vec_]concat ...)). */
+ if (REG_P (tem) && !HARD_REGISTER_P (tem)
+ && (VECTOR_MODE_P (GET_MODE (new_rtx))
+ || COMPLEX_MODE_P (GET_MODE (new_rtx)))
+ && GET_MODE (tem) == GET_MODE_INNER (GET_MODE (new_rtx)))
+ return true;
+
/* The replacement we made so far is valid, if all of the recursive
replacements were valid, or we could simplify everything to
a constant. */
|| CONSTANT_P (new_rtx)
|| (GET_CODE (new_rtx) == SUBREG
&& REG_P (SUBREG_REG (new_rtx))
- && (GET_MODE_SIZE (mode)
- <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (new_rtx))))))
+ && !paradoxical_subreg_p (new_rtx)))
flags |= PR_CAN_APPEAR;
if (!varying_mem_p (new_rtx))
flags |= PR_HANDLE_MEM;
}
-/* Check if the given DEF is available in INSN. This would require full
- computation of available expressions; we check only restricted conditions:
- - if DEF is the sole definition of its register, go ahead;
- - in the same basic block, we check for no definitions killing the
- definition of DEF_INSN;
- - if USE's basic block has DEF's basic block as the sole predecessor,
- we check if the definition is killed after DEF_INSN or before
+/* Check if USE is killed between DEF_INSN and TARGET_INSN. This would
+ require full computation of available expressions; we check only a few
+ restricted conditions:
+ - if the reg in USE has only one definition, go ahead;
+ - in the same basic block, we check for no definitions killing the use;
+ - if TARGET_INSN's basic block has DEF_INSN's basic block as its sole
+ predecessor, we check if the use is killed after DEF_INSN or before
TARGET_INSN insn, in their respective basic blocks. */
+
static bool
use_killed_between (df_ref use, rtx_insn *def_insn, rtx_insn *target_insn)
{
know that this definition reaches use, or we wouldn't be here.
However, this is invalid for hard registers because if they are
live at the beginning of the function it does not mean that we
- have an uninitialized access. */
+ have an uninitialized access. And we have to check for the case
+ where a register may be used uninitialized in a loop as above. */
regno = DF_REF_REGNO (use);
def = DF_REG_DEF_CHAIN (regno);
if (def
&& DF_REF_NEXT_REG (def) == NULL
- && regno >= FIRST_PSEUDO_REGISTER)
+ && regno >= FIRST_PSEUDO_REGISTER
+ && (BLOCK_FOR_INSN (DF_REF_INSN (def)) == def_bb
+ ? DF_INSN_LUID (DF_REF_INSN (def)) < DF_INSN_LUID (def_insn)
+ : dominated_by_p (CDI_DOMINATORS,
+ def_bb, BLOCK_FOR_INSN (DF_REF_INSN (def)))))
return false;
/* Check locally if we are in the same basic block. */
\f
static df_ref *active_defs;
-#ifdef ENABLE_CHECKING
static sparseset active_defs_check;
-#endif
/* Fill the ACTIVE_DEFS array with the use->def link for the registers
mentioned in USE_REC. Register the valid entries in ACTIVE_DEFS_CHECK
df_ref def = get_def_for_use (use);
int regno = DF_REF_REGNO (use);
-#ifdef ENABLE_CHECKING
- sparseset_set_bit (active_defs_check, regno);
-#endif
+ if (flag_checking)
+ sparseset_set_bit (active_defs_check, regno);
active_defs[regno] = def;
}
}
static void
update_df_init (rtx_insn *def_insn, rtx_insn *insn)
{
-#ifdef ENABLE_CHECKING
- sparseset_clear (active_defs_check);
-#endif
+ if (flag_checking)
+ sparseset_clear (active_defs_check);
register_active_defs (DF_INSN_USES (def_insn));
register_active_defs (DF_INSN_USES (insn));
register_active_defs (DF_INSN_EQ_USES (insn));
/* Set up the use-def chain. */
if (DF_REF_ID (use) >= (int) use_def_ref.length ())
- use_def_ref.safe_grow_cleared (DF_REF_ID (use) + 1);
+ use_def_ref.safe_grow_cleared (DF_REF_ID (use) + 1, true);
-#ifdef ENABLE_CHECKING
- gcc_assert (sparseset_bit_p (active_defs_check, regno));
-#endif
+ if (flag_checking)
+ gcc_assert (sparseset_bit_p (active_defs_check, regno));
use_def_ref[DF_REF_ID (use)] = active_defs[regno];
}
}
multiple sets. If so, assume the cost of the new instruction is
not greater than the old one. */
if (set)
- old_cost = set_src_cost (SET_SRC (set), speed);
+ old_cost = set_src_cost (SET_SRC (set), GET_MODE (SET_DEST (set)), speed);
if (dump_file)
{
fprintf (dump_file, "\nIn insn %d, replacing\n ", INSN_UID (insn));
else if (DF_REF_TYPE (use) == DF_REF_REG_USE
&& set
- && set_src_cost (SET_SRC (set), speed) > old_cost)
+ && (set_src_cost (SET_SRC (set), GET_MODE (SET_DEST (set)), speed)
+ > old_cost))
{
if (dump_file)
fprintf (dump_file, "Changes to insn %d not profitable\n",
making a new one if one does not already exist. */
if (set_reg_equal)
{
- if (dump_file)
- fprintf (dump_file, " Setting REG_EQUAL note\n");
+ /* If there are any paradoxical SUBREGs, don't add REG_EQUAL note,
+ because the bits in there can be anything and so might not
+ match the REG_EQUAL note content. See PR70574. */
+ subrtx_var_iterator::array_type array;
+ FOR_EACH_SUBRTX_VAR (iter, array, *loc, NONCONST)
+ {
+ rtx x = *iter;
+ if (SUBREG_P (x) && paradoxical_subreg_p (x))
+ {
+ set_reg_equal = false;
+ break;
+ }
+ }
- note = set_unique_reg_note (insn, REG_EQUAL, copy_rtx (new_rtx));
+ if (set_reg_equal)
+ {
+ if (dump_file)
+ fprintf (dump_file, " Setting REG_EQUAL note\n");
+
+ note = set_unique_reg_note (insn, REG_EQUAL, copy_rtx (new_rtx));
+ }
}
}
df_ref def, use;
reg = XEXP (src, 0);
-#ifdef LOAD_EXTEND_OP
- if (LOAD_EXTEND_OP (GET_MODE (reg)) != GET_CODE (src))
-#endif
+ if (load_extend_op (GET_MODE (reg)) != GET_CODE (src))
return false;
FOR_EACH_INSN_USE (use, insn)
rtx use_reg = DF_REF_REG (use);
rtx_insn *use_insn;
rtx src;
+ scalar_int_mode int_use_mode, src_mode;
/* Only consider subregs... */
machine_mode use_mode = GET_MODE (use_reg);
|| !REG_P (SET_DEST (def_set)))
return false;
- /* If this is a paradoxical SUBREG... */
- if (GET_MODE_SIZE (use_mode)
- > GET_MODE_SIZE (GET_MODE (SUBREG_REG (use_reg))))
+ if (paradoxical_subreg_p (use_reg))
{
/* If this is a paradoxical SUBREG, we have no idea what value the
extra bits would have. However, if the operand is equivalent to
definition of Y or, failing that, allow A to be deleted after
reload through register tying. Introducing more uses of Y
prevents both optimisations. */
- else if (subreg_lowpart_p (use_reg))
+ else if (is_a <scalar_int_mode> (use_mode, &int_use_mode)
+ && subreg_lowpart_p (use_reg))
{
use_insn = DF_REF_INSN (use);
src = SET_SRC (def_set);
if ((GET_CODE (src) == ZERO_EXTEND
|| GET_CODE (src) == SIGN_EXTEND)
+ && is_a <scalar_int_mode> (GET_MODE (src), &src_mode)
&& REG_P (XEXP (src, 0))
&& REGNO (XEXP (src, 0)) >= FIRST_PSEUDO_REGISTER
&& GET_MODE (XEXP (src, 0)) == use_mode
&& !free_load_extend (src, def_insn)
- && (targetm.mode_rep_extended (use_mode, GET_MODE (src))
+ && (targetm.mode_rep_extended (int_use_mode, src_mode)
!= (int) GET_CODE (src))
&& all_uses_available_at (def_insn, use_insn))
return try_fwprop_subst (use, DF_REF_LOC (use), XEXP (src, 0),
reg = DF_REF_REG (use);
if (GET_CODE (reg) == SUBREG && GET_CODE (SET_DEST (def_set)) == SUBREG)
{
- if (SUBREG_BYTE (SET_DEST (def_set)) != SUBREG_BYTE (reg))
+ if (maybe_ne (SUBREG_BYTE (SET_DEST (def_set)), SUBREG_BYTE (reg)))
return false;
}
/* Check if the def had a subreg, but the use has the whole reg. */
that isn't mentioned in USE_SET, as the note would be invalid
otherwise. We also don't want to install a note if we are merely
propagating a pseudo since verifying that this pseudo isn't dead
- is a pain; moreover such a note won't help anything. */
+ is a pain; moreover such a note won't help anything.
+ If the use is a paradoxical subreg, make sure we don't add a
+ REG_EQUAL note for it, because it is not equivalent, it is one
+ possible value for it, but we can't rely on it holding that value.
+ See PR70574. */
set_reg_equal = (note == NULL_RTX
&& REG_P (SET_DEST (use_set))
&& !REG_P (src)
&& !(GET_CODE (src) == SUBREG
&& REG_P (SUBREG_REG (src)))
&& !reg_mentioned_p (SET_DEST (use_set),
- SET_SRC (use_set)));
+ SET_SRC (use_set))
+ && !paradoxical_subreg_p (DF_REF_REG (use)));
}
if (GET_MODE (*loc) == VOIDmode)
/* Given a use USE of an insn, if it has a single reaching
definition, try to forward propagate it into that insn.
- Return true if cfg cleanup will be needed. */
+ Return true if cfg cleanup will be needed.
+ REG_PROP_ONLY is true if we should only propagate register copies. */
static bool
-forward_propagate_into (df_ref use)
+forward_propagate_into (df_ref use, bool reg_prop_only = false)
{
df_ref def;
rtx_insn *def_insn, *use_insn;
if (DF_REF_IS_ARTIFICIAL (def))
return false;
- /* Do not propagate loop invariant definitions inside the loop. */
- if (DF_REF_BB (def)->loop_father != DF_REF_BB (use)->loop_father)
- return false;
-
/* Check if the use is still present in the insn! */
use_insn = DF_REF_INSN (use);
if (DF_REF_FLAGS (use) & DF_REF_IN_NOTE)
if (!def_set)
return false;
+ if (reg_prop_only
+ && (!reg_single_def_p (SET_SRC (def_set))
+ || !reg_single_def_p (SET_DEST (def_set))))
+ return false;
+
+ /* Allow propagations into a loop only for reg-to-reg copies, since
+ replacing one register by another shouldn't increase the cost. */
+
+ if (DF_REF_BB (def)->loop_father != DF_REF_BB (use)->loop_father
+ && (!reg_single_def_p (SET_SRC (def_set))
+ || !reg_single_def_p (SET_DEST (def_set))))
+ return false;
+
/* Only try one kind of propagation. If two are possible, we'll
do it on the following iterations. */
if (forward_propagate_and_simplify (use, def_insn, def_set)
|| forward_propagate_subreg (use, def_insn, def_set))
{
+ propagations_left--;
+
if (cfun->can_throw_non_call_exceptions
&& find_reg_note (use_insn, REG_EH_REGION, NULL_RTX)
&& purge_dead_edges (DF_REF_BB (use)))
df_set_flags (DF_DEFER_INSN_RESCAN);
active_defs = XNEWVEC (df_ref, max_reg_num ());
-#ifdef ENABLE_CHECKING
- active_defs_check = sparseset_alloc (max_reg_num ());
-#endif
+ if (flag_checking)
+ active_defs_check = sparseset_alloc (max_reg_num ());
+
+ propagations_left = DF_USES_TABLE_SIZE ();
}
static void
use_def_ref.release ();
free (active_defs);
-#ifdef ENABLE_CHECKING
- sparseset_free (active_defs_check);
-#endif
+ if (flag_checking)
+ sparseset_free (active_defs_check);
free_dominance_info (CDI_DOMINATORS);
cleanup_cfg (0);
}
static unsigned int
-fwprop (void)
+fwprop (bool fwprop_addr_p)
{
unsigned i;
- bool need_cleanup = false;
fwprop_init ();
for (i = 0; i < DF_USES_TABLE_SIZE (); i++)
{
+ if (!propagations_left)
+ break;
+
df_ref use = DF_USES_GET (i);
if (use)
- if (DF_REF_TYPE (use) == DF_REF_REG_USE
- || DF_REF_BB (use)->loop_father == NULL
- /* The outer most loop is not really a loop. */
- || loop_outer (DF_REF_BB (use)->loop_father) == NULL)
- need_cleanup |= forward_propagate_into (use);
+ {
+ if (DF_REF_TYPE (use) == DF_REF_REG_USE
+ || DF_REF_BB (use)->loop_father == NULL
+ /* The outer most loop is not really a loop. */
+ || loop_outer (DF_REF_BB (use)->loop_father) == NULL)
+ forward_propagate_into (use, fwprop_addr_p);
+
+ else if (fwprop_addr_p)
+ forward_propagate_into (use, false);
+ }
}
fwprop_done ();
- if (need_cleanup)
- cleanup_cfg (0);
return 0;
}
/* opt_pass methods: */
virtual bool gate (function *) { return gate_fwprop (); }
- virtual unsigned int execute (function *) { return fwprop (); }
+ virtual unsigned int execute (function *) { return fwprop (false); }
}; // class pass_rtl_fwprop
return new pass_rtl_fwprop (ctxt);
}
-static unsigned int
-fwprop_addr (void)
-{
- unsigned i;
- bool need_cleanup = false;
-
- fwprop_init ();
-
- /* Go through all the uses. df_uses_create will create new ones at the
- end, and we'll go through them as well. */
- for (i = 0; i < DF_USES_TABLE_SIZE (); i++)
- {
- df_ref use = DF_USES_GET (i);
- if (use)
- if (DF_REF_TYPE (use) != DF_REF_REG_USE
- && DF_REF_BB (use)->loop_father != NULL
- /* The outer most loop is not really a loop. */
- && loop_outer (DF_REF_BB (use)->loop_father) != NULL)
- need_cleanup |= forward_propagate_into (use);
- }
-
- fwprop_done ();
-
- if (need_cleanup)
- cleanup_cfg (0);
- return 0;
-}
-
namespace {
const pass_data pass_data_rtl_fwprop_addr =
/* opt_pass methods: */
virtual bool gate (function *) { return gate_fwprop (); }
- virtual unsigned int execute (function *) { return fwprop_addr (); }
+ virtual unsigned int execute (function *) { return fwprop (true); }
}; // class pass_rtl_fwprop_addr