/* Decompose multiword subregs.
- Copyright (C) 2007-2013 Free Software Foundation, Inc.
+ Copyright (C) 2007-2019 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>
Ian Lance Taylor <iant@google.com>
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "machmode.h"
-#include "tm.h"
-#include "tree.h"
+#include "backend.h"
#include "rtl.h"
+#include "tree.h"
+#include "cfghooks.h"
+#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
-#include "flags.h"
+#include "expmed.h"
#include "insn-config.h"
-#include "obstack.h"
-#include "basic-block.h"
+#include "emit-rtl.h"
#include "recog.h"
-#include "bitmap.h"
+#include "cfgrtl.h"
+#include "cfgbuild.h"
#include "dce.h"
#include "expr.h"
-#include "except.h"
-#include "regs.h"
#include "tree-pass.h"
-#include "df.h"
#include "lower-subreg.h"
-
-#ifdef STACK_GROWS_DOWNWARD
-# undef STACK_GROWS_DOWNWARD
-# define STACK_GROWS_DOWNWARD 1
-#else
-# define STACK_GROWS_DOWNWARD 0
-#endif
+#include "rtl-iter.h"
+#include "target.h"
/* Decompose multi-word pseudo-registers into individual
static bitmap decomposable_context;
/* Bit N in this bitmap is set if regno N is used in a context in
- which it can not be decomposed. */
+ which it cannot be decomposed. */
static bitmap non_decomposable_context;
/* Bit N in this bitmap is set if regno N is used in a subreg
#define choices \
this_target_lower_subreg->x_choices
+/* Return true if MODE is a mode we know how to lower. When returning true,
+ store its byte size in *BYTES and its word size in *WORDS. */
+
+static inline bool
+interesting_mode_p (machine_mode mode, unsigned int *bytes,
+ unsigned int *words)
+{
+ if (!GET_MODE_SIZE (mode).is_constant (bytes))
+ return false;
+ *words = CEIL (*bytes, UNITS_PER_WORD);
+ return true;
+}
+
/* RTXes used while computing costs. */
struct cost_rtxes {
/* Source and target registers. */
static int
shift_cost (bool speed_p, struct cost_rtxes *rtxes, enum rtx_code code,
- enum machine_mode mode, int op1)
+ machine_mode mode, int op1)
{
PUT_CODE (rtxes->shift, code);
PUT_MODE (rtxes->shift, mode);
PUT_MODE (rtxes->source, mode);
- XEXP (rtxes->shift, 1) = GEN_INT (op1);
- return set_src_cost (rtxes->shift, speed_p);
+ XEXP (rtxes->shift, 1) = gen_int_shift_amount (mode, op1);
+ return set_src_cost (rtxes->shift, mode, speed_p);
}
/* For each X in the range [0, BITS_PER_WORD), set SPLITTING[X]
for (i = 0; i < MAX_MACHINE_MODE; i++)
{
- enum machine_mode mode = (enum machine_mode) i;
- int factor = GET_MODE_SIZE (mode) / UNITS_PER_WORD;
- if (factor > 1)
+ machine_mode mode = (machine_mode) i;
+ unsigned int size, factor;
+ if (interesting_mode_p (mode, &size, &factor) && factor > 1)
{
- int mode_move_cost;
+ unsigned int mode_move_cost;
PUT_MODE (rtxes->target, mode);
PUT_MODE (rtxes->source, mode);
/* The only case here to check to see if moving the upper part with a
zero is cheaper than doing the zext itself. */
PUT_MODE (rtxes->source, word_mode);
- zext_cost = set_src_cost (rtxes->zext, speed_p);
+ zext_cost = set_src_cost (rtxes->zext, twice_word_mode, speed_p);
if (LOG_COSTS)
fprintf (stderr, "%s %s: original cost %d, split cost %d + %d\n",
memset (this_target_lower_subreg, 0, sizeof (*this_target_lower_subreg));
- twice_word_mode = GET_MODE_2XWIDER_MODE (word_mode);
+ twice_word_mode = GET_MODE_2XWIDER_MODE (word_mode).require ();
- rtxes.target = gen_rtx_REG (word_mode, FIRST_PSEUDO_REGISTER);
- rtxes.source = gen_rtx_REG (word_mode, FIRST_PSEUDO_REGISTER + 1);
- rtxes.set = gen_rtx_SET (VOIDmode, rtxes.target, rtxes.source);
+ rtxes.target = gen_rtx_REG (word_mode, LAST_VIRTUAL_REGISTER + 1);
+ rtxes.source = gen_rtx_REG (word_mode, LAST_VIRTUAL_REGISTER + 2);
+ rtxes.set = gen_rtx_SET (rtxes.target, rtxes.source);
rtxes.zext = gen_rtx_ZERO_EXTEND (twice_word_mode, rtxes.source);
rtxes.shift = gen_rtx_ASHIFT (twice_word_mode, rtxes.source, const0_rtx);
return true;
}
+/* If X is an operator that can be treated as a simple move that we
+ can split, then return the operand that is operated on. */
+
+static rtx
+operand_for_swap_move_operator (rtx x)
+{
+ /* A word sized rotate of a register pair is equivalent to swapping
+ the registers in the register pair. */
+ if (GET_CODE (x) == ROTATE
+ && GET_MODE (x) == twice_word_mode
+ && simple_move_operand (XEXP (x, 0))
+ && CONST_INT_P (XEXP (x, 1))
+ && INTVAL (XEXP (x, 1)) == BITS_PER_WORD)
+ return XEXP (x, 0);
+
+ return NULL_RTX;
+}
+
/* If INSN is a single set between two objects that we want to split,
return the single set. SPEED_P says whether we are optimizing
INSN for speed or size.
is called. */
static rtx
-simple_move (rtx insn, bool speed_p)
+simple_move (rtx_insn *insn, bool speed_p)
{
- rtx x;
+ rtx x, op;
rtx set;
- enum machine_mode mode;
+ machine_mode mode;
if (recog_data.n_operands != 2)
return NULL_RTX;
return NULL_RTX;
x = SET_SRC (set);
+ if ((op = operand_for_swap_move_operator (x)) != NULL_RTX)
+ x = op;
+
if (x != recog_data.operand[0] && x != recog_data.operand[1])
return NULL_RTX;
/* For the src we can handle ASM_OPERANDS, and it is beneficial for
size. */
mode = GET_MODE (SET_DEST (set));
if (!SCALAR_INT_MODE_P (mode)
- && (mode_for_size (GET_MODE_SIZE (mode) * BITS_PER_UNIT, MODE_INT, 0)
- == BLKmode))
+ && !int_mode_for_size (GET_MODE_BITSIZE (mode), 0).exists ())
return NULL_RTX;
/* Reject PARTIAL_INT modes. They are used for processor specific
{
rtx dest = SET_DEST (set);
rtx src = SET_SRC (set);
+ rtx op;
unsigned int rd, rs;
bitmap b;
+ if ((op = operand_for_swap_move_operator (src)) != NULL_RTX)
+ src = op;
+
if (!REG_P (dest) || !REG_P (src))
return false;
static void
propagate_pseudo_copies (void)
{
- bitmap queue, propagate;
-
- queue = BITMAP_ALLOC (NULL);
- propagate = BITMAP_ALLOC (NULL);
+ auto_bitmap queue, propagate;
bitmap_copy (queue, decomposable_context);
do
bitmap_ior_into (decomposable_context, propagate);
}
while (!bitmap_empty_p (queue));
-
- BITMAP_FREE (queue);
- BITMAP_FREE (propagate);
}
/* A pointer to one of these values is passed to
- find_decomposable_subregs via for_each_rtx. */
+ find_decomposable_subregs. */
enum classify_move_insn
{
SIMPLE_MOVE
};
-/* This is called via for_each_rtx. If we find a SUBREG which we
- could use to decompose a pseudo-register, set a bit in
- DECOMPOSABLE_CONTEXT. If we find an unadorned register which is
- not a simple pseudo-register copy, DATA will point at the type of
- move, and we set a bit in DECOMPOSABLE_CONTEXT or
- NON_DECOMPOSABLE_CONTEXT as appropriate. */
+/* If we find a SUBREG in *LOC which we could use to decompose a
+ pseudo-register, set a bit in DECOMPOSABLE_CONTEXT. If we find an
+ unadorned register which is not a simple pseudo-register copy,
+ DATA will point at the type of move, and we set a bit in
+ DECOMPOSABLE_CONTEXT or NON_DECOMPOSABLE_CONTEXT as appropriate. */
-static int
-find_decomposable_subregs (rtx *px, void *data)
+static void
+find_decomposable_subregs (rtx *loc, enum classify_move_insn *pcmi)
{
- enum classify_move_insn *pcmi = (enum classify_move_insn *) data;
- rtx x = *px;
-
- if (x == NULL_RTX)
- return 0;
-
- if (GET_CODE (x) == SUBREG)
+ subrtx_var_iterator::array_type array;
+ FOR_EACH_SUBRTX_VAR (iter, array, *loc, NONCONST)
{
- rtx inner = SUBREG_REG (x);
- unsigned int regno, outer_size, inner_size, outer_words, inner_words;
-
- if (!REG_P (inner))
- return 0;
-
- regno = REGNO (inner);
- if (HARD_REGISTER_NUM_P (regno))
- return -1;
+ rtx x = *iter;
+ if (GET_CODE (x) == SUBREG)
+ {
+ rtx inner = SUBREG_REG (x);
+ unsigned int regno, outer_size, inner_size, outer_words, inner_words;
- outer_size = GET_MODE_SIZE (GET_MODE (x));
- inner_size = GET_MODE_SIZE (GET_MODE (inner));
- outer_words = (outer_size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
- inner_words = (inner_size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+ if (!REG_P (inner))
+ continue;
- /* We only try to decompose single word subregs of multi-word
- registers. When we find one, we return -1 to avoid iterating
- over the inner register.
+ regno = REGNO (inner);
+ if (HARD_REGISTER_NUM_P (regno))
+ {
+ iter.skip_subrtxes ();
+ continue;
+ }
- ??? This doesn't allow, e.g., DImode subregs of TImode values
- on 32-bit targets. We would need to record the way the
- pseudo-register was used, and only decompose if all the uses
- were the same number and size of pieces. Hopefully this
- doesn't happen much. */
+ if (!interesting_mode_p (GET_MODE (x), &outer_size, &outer_words)
+ || !interesting_mode_p (GET_MODE (inner), &inner_size,
+ &inner_words))
+ continue;
- if (outer_words == 1 && inner_words > 1)
- {
- bitmap_set_bit (decomposable_context, regno);
- return -1;
- }
+ /* We only try to decompose single word subregs of multi-word
+ registers. When we find one, we return -1 to avoid iterating
+ over the inner register.
+
+ ??? This doesn't allow, e.g., DImode subregs of TImode values
+ on 32-bit targets. We would need to record the way the
+ pseudo-register was used, and only decompose if all the uses
+ were the same number and size of pieces. Hopefully this
+ doesn't happen much. */
+
+ if (outer_words == 1
+ && inner_words > 1
+ /* Don't allow to decompose floating point subregs of
+ multi-word pseudos if the floating point mode does
+ not have word size, because otherwise we'd generate
+ a subreg with that floating mode from a different
+ sized integral pseudo which is not allowed by
+ validate_subreg. */
+ && (!FLOAT_MODE_P (GET_MODE (x))
+ || outer_size == UNITS_PER_WORD))
+ {
+ bitmap_set_bit (decomposable_context, regno);
+ iter.skip_subrtxes ();
+ continue;
+ }
- /* If this is a cast from one mode to another, where the modes
- have the same size, and they are not tieable, then mark this
- register as non-decomposable. If we decompose it we are
- likely to mess up whatever the backend is trying to do. */
- if (outer_words > 1
- && outer_size == inner_size
- && !MODES_TIEABLE_P (GET_MODE (x), GET_MODE (inner)))
- {
- bitmap_set_bit (non_decomposable_context, regno);
- bitmap_set_bit (subreg_context, regno);
- return -1;
+ /* If this is a cast from one mode to another, where the modes
+ have the same size, and they are not tieable, then mark this
+ register as non-decomposable. If we decompose it we are
+ likely to mess up whatever the backend is trying to do. */
+ if (outer_words > 1
+ && outer_size == inner_size
+ && !targetm.modes_tieable_p (GET_MODE (x), GET_MODE (inner)))
+ {
+ bitmap_set_bit (non_decomposable_context, regno);
+ bitmap_set_bit (subreg_context, regno);
+ iter.skip_subrtxes ();
+ continue;
+ }
}
- }
- else if (REG_P (x))
- {
- unsigned int regno;
-
- /* We will see an outer SUBREG before we see the inner REG, so
- when we see a plain REG here it means a direct reference to
- the register.
-
- If this is not a simple copy from one location to another,
- then we can not decompose this register. If this is a simple
- copy we want to decompose, and the mode is right,
- then we mark the register as decomposable.
- Otherwise we don't say anything about this register --
- it could be decomposed, but whether that would be
- profitable depends upon how it is used elsewhere.
-
- We only set bits in the bitmap for multi-word
- pseudo-registers, since those are the only ones we care about
- and it keeps the size of the bitmaps down. */
-
- regno = REGNO (x);
- if (!HARD_REGISTER_NUM_P (regno)
- && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
+ else if (REG_P (x))
{
- switch (*pcmi)
+ unsigned int regno, size, words;
+
+ /* We will see an outer SUBREG before we see the inner REG, so
+ when we see a plain REG here it means a direct reference to
+ the register.
+
+ If this is not a simple copy from one location to another,
+ then we cannot decompose this register. If this is a simple
+ copy we want to decompose, and the mode is right,
+ then we mark the register as decomposable.
+ Otherwise we don't say anything about this register --
+ it could be decomposed, but whether that would be
+ profitable depends upon how it is used elsewhere.
+
+ We only set bits in the bitmap for multi-word
+ pseudo-registers, since those are the only ones we care about
+ and it keeps the size of the bitmaps down. */
+
+ regno = REGNO (x);
+ if (!HARD_REGISTER_NUM_P (regno)
+ && interesting_mode_p (GET_MODE (x), &size, &words)
+ && words > 1)
{
- case NOT_SIMPLE_MOVE:
- bitmap_set_bit (non_decomposable_context, regno);
- break;
- case DECOMPOSABLE_SIMPLE_MOVE:
- if (MODES_TIEABLE_P (GET_MODE (x), word_mode))
- bitmap_set_bit (decomposable_context, regno);
- break;
- case SIMPLE_MOVE:
- break;
- default:
- gcc_unreachable ();
+ switch (*pcmi)
+ {
+ case NOT_SIMPLE_MOVE:
+ bitmap_set_bit (non_decomposable_context, regno);
+ break;
+ case DECOMPOSABLE_SIMPLE_MOVE:
+ if (targetm.modes_tieable_p (GET_MODE (x), word_mode))
+ bitmap_set_bit (decomposable_context, regno);
+ break;
+ case SIMPLE_MOVE:
+ break;
+ default:
+ gcc_unreachable ();
+ }
}
}
- }
- else if (MEM_P (x))
- {
- enum classify_move_insn cmi_mem = NOT_SIMPLE_MOVE;
+ else if (MEM_P (x))
+ {
+ enum classify_move_insn cmi_mem = NOT_SIMPLE_MOVE;
- /* Any registers used in a MEM do not participate in a
- SIMPLE_MOVE or DECOMPOSABLE_SIMPLE_MOVE. Do our own recursion
- here, and return -1 to block the parent's recursion. */
- for_each_rtx (&XEXP (x, 0), find_decomposable_subregs, &cmi_mem);
- return -1;
+ /* Any registers used in a MEM do not participate in a
+ SIMPLE_MOVE or DECOMPOSABLE_SIMPLE_MOVE. Do our own recursion
+ here, and return -1 to block the parent's recursion. */
+ find_decomposable_subregs (&XEXP (x, 0), &cmi_mem);
+ iter.skip_subrtxes ();
+ }
}
-
- return 0;
}
/* Decompose REGNO into word-sized components. We smash the REG node
decompose_register (unsigned int regno)
{
rtx reg;
- unsigned int words, i;
+ unsigned int size, words, i;
rtvec v;
reg = regno_reg_rtx[regno];
regno_reg_rtx[regno] = NULL_RTX;
- words = GET_MODE_SIZE (GET_MODE (reg));
- words = (words + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+ if (!interesting_mode_p (GET_MODE (reg), &size, &words))
+ gcc_unreachable ();
v = rtvec_alloc (words);
for (i = 0; i < words; ++i)
/* Get a SUBREG of a CONCATN. */
static rtx
-simplify_subreg_concatn (enum machine_mode outermode, rtx op,
- unsigned int byte)
+simplify_subreg_concatn (machine_mode outermode, rtx op, poly_uint64 orig_byte)
{
- unsigned int inner_size;
- enum machine_mode innermode, partmode;
+ unsigned int outer_size, outer_words, inner_size, inner_words;
+ machine_mode innermode, partmode;
rtx part;
unsigned int final_offset;
+ unsigned int byte;
+
+ innermode = GET_MODE (op);
+ if (!interesting_mode_p (outermode, &outer_size, &outer_words)
+ || !interesting_mode_p (innermode, &inner_size, &inner_words))
+ gcc_unreachable ();
+ /* Must be constant if interesting_mode_p passes. */
+ byte = orig_byte.to_constant ();
gcc_assert (GET_CODE (op) == CONCATN);
- gcc_assert (byte % GET_MODE_SIZE (outermode) == 0);
+ gcc_assert (byte % outer_size == 0);
- innermode = GET_MODE (op);
- gcc_assert (byte < GET_MODE_SIZE (innermode));
- gcc_assert (GET_MODE_SIZE (outermode) <= GET_MODE_SIZE (innermode));
+ gcc_assert (byte < inner_size);
+ if (outer_size > inner_size)
+ return NULL_RTX;
- inner_size = GET_MODE_SIZE (innermode) / XVECLEN (op, 0);
+ inner_size /= XVECLEN (op, 0);
part = XVECEXP (op, 0, byte / inner_size);
partmode = GET_MODE (part);
+ final_offset = byte % inner_size;
+ if (final_offset + outer_size > inner_size)
+ return NULL_RTX;
+
/* VECTOR_CSTs in debug expressions are expanded into CONCATN instead of
regular CONST_VECTORs. They have vector or integer modes, depending
on the capabilities of the target. Cope with them. */
if (partmode == VOIDmode && VECTOR_MODE_P (innermode))
partmode = GET_MODE_INNER (innermode);
else if (partmode == VOIDmode)
- {
- enum mode_class mclass = GET_MODE_CLASS (innermode);
- partmode = mode_for_size (inner_size * BITS_PER_UNIT, mclass, 0);
- }
-
- final_offset = byte % inner_size;
- if (final_offset + GET_MODE_SIZE (outermode) > inner_size)
- return NULL_RTX;
+ partmode = mode_for_size (inner_size * BITS_PER_UNIT,
+ GET_MODE_CLASS (innermode), 0).require ();
return simplify_gen_subreg (outermode, part, partmode, final_offset);
}
/* Wrapper around simplify_gen_subreg which handles CONCATN. */
static rtx
-simplify_gen_subreg_concatn (enum machine_mode outermode, rtx op,
- enum machine_mode innermode, unsigned int byte)
+simplify_gen_subreg_concatn (machine_mode outermode, rtx op,
+ machine_mode innermode, unsigned int byte)
{
rtx ret;
{
rtx op2;
- if ((GET_MODE_SIZE (GET_MODE (op))
- == GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))))
- && SUBREG_BYTE (op) == 0)
+ if (known_eq (GET_MODE_SIZE (GET_MODE (op)),
+ GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))))
+ && known_eq (SUBREG_BYTE (op), 0))
return simplify_gen_subreg_concatn (outermode, SUBREG_REG (op),
GET_MODE (SUBREG_REG (op)), byte);
if (op2 == NULL_RTX)
{
/* We don't handle paradoxical subregs here. */
- gcc_assert (GET_MODE_SIZE (outermode)
- <= GET_MODE_SIZE (GET_MODE (op)));
- gcc_assert (GET_MODE_SIZE (GET_MODE (op))
- <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))));
+ gcc_assert (!paradoxical_subreg_p (outermode, GET_MODE (op)));
+ gcc_assert (!paradoxical_subreg_p (op));
op2 = simplify_subreg_concatn (outermode, SUBREG_REG (op),
byte + SUBREG_BYTE (op));
gcc_assert (op2 != NULL_RTX);
resolve_simple_move will ask for the high part of the paradoxical
subreg, which does not have a value. Just return a zero. */
if (ret == NULL_RTX
- && GET_CODE (op) == SUBREG
- && SUBREG_BYTE (op) == 0
- && (GET_MODE_SIZE (innermode)
- > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op)))))
+ && paradoxical_subreg_p (op))
return CONST0_RTX (outermode);
gcc_assert (ret != NULL_RTX);
return resolve_reg_p (SUBREG_REG (x));
}
-/* This is called via for_each_rtx. Look for SUBREGs which need to be
- decomposed. */
+/* Look for SUBREGs in *LOC which need to be decomposed. */
-static int
-resolve_subreg_use (rtx *px, void *data)
+static bool
+resolve_subreg_use (rtx *loc, rtx insn)
{
- rtx insn = (rtx) data;
- rtx x = *px;
-
- if (x == NULL_RTX)
- return 0;
-
- if (resolve_subreg_p (x))
+ subrtx_ptr_iterator::array_type array;
+ FOR_EACH_SUBRTX_PTR (iter, array, loc, NONCONST)
{
- x = simplify_subreg_concatn (GET_MODE (x), SUBREG_REG (x),
- SUBREG_BYTE (x));
-
- /* It is possible for a note to contain a reference which we can
- decompose. In this case, return 1 to the caller to indicate
- that the note must be removed. */
- if (!x)
+ rtx *loc = *iter;
+ rtx x = *loc;
+ if (resolve_subreg_p (x))
{
- gcc_assert (!insn);
- return 1;
- }
+ x = simplify_subreg_concatn (GET_MODE (x), SUBREG_REG (x),
+ SUBREG_BYTE (x));
- validate_change (insn, px, x, 1);
- return -1;
- }
-
- if (resolve_reg_p (x))
- {
- /* Return 1 to the caller to indicate that we found a direct
- reference to a register which is being decomposed. This can
- happen inside notes, multiword shift or zero-extend
- instructions. */
- return 1;
- }
-
- return 0;
-}
-
-/* This is called via for_each_rtx. Look for SUBREGs which can be
- decomposed and decomposed REGs that need copying. */
-
-static int
-adjust_decomposed_uses (rtx *px, void *data ATTRIBUTE_UNUSED)
-{
- rtx x = *px;
-
- if (x == NULL_RTX)
- return 0;
-
- if (resolve_subreg_p (x))
- {
- x = simplify_subreg_concatn (GET_MODE (x), SUBREG_REG (x),
- SUBREG_BYTE (x));
+ /* It is possible for a note to contain a reference which we can
+ decompose. In this case, return 1 to the caller to indicate
+ that the note must be removed. */
+ if (!x)
+ {
+ gcc_assert (!insn);
+ return true;
+ }
- if (x)
- *px = x;
- else
- x = copy_rtx (*px);
+ validate_change (insn, loc, x, 1);
+ iter.skip_subrtxes ();
+ }
+ else if (resolve_reg_p (x))
+ /* Return 1 to the caller to indicate that we found a direct
+ reference to a register which is being decomposed. This can
+ happen inside notes, multiword shift or zero-extend
+ instructions. */
+ return true;
}
- if (resolve_reg_p (x))
- *px = copy_rtx (x);
-
- return 0;
+ return false;
}
/* Resolve any decomposed registers which appear in register notes on
INSN. */
static void
-resolve_reg_notes (rtx insn)
+resolve_reg_notes (rtx_insn *insn)
{
rtx *pnote, note;
if (note)
{
int old_count = num_validated_changes ();
- if (for_each_rtx (&XEXP (note, 0), resolve_subreg_use, NULL))
+ if (resolve_subreg_use (&XEXP (note, 0), NULL_RTX))
remove_note (insn, note);
else
if (old_count != num_validated_changes ())
if (HARD_REGISTER_NUM_P (regno))
{
- unsigned int byte, num_bytes;
+ unsigned int byte, num_bytes, num_words;
- num_bytes = GET_MODE_SIZE (GET_MODE (x));
+ if (!interesting_mode_p (GET_MODE (x), &num_bytes, &num_words))
+ return false;
for (byte = 0; byte < num_bytes; byte += UNITS_PER_WORD)
if (simplify_subreg_regno (regno, GET_MODE (x), byte, word_mode) < 0)
return false;
return true;
}
+/* OPND is a concatn operand this is used with a simple move operator.
+ Return a new rtx with the concatn's operands swapped. */
+
+static rtx
+resolve_operand_for_swap_move_operator (rtx opnd)
+{
+ gcc_assert (GET_CODE (opnd) == CONCATN);
+ rtx concatn = copy_rtx (opnd);
+ rtx op0 = XVECEXP (concatn, 0, 0);
+ rtx op1 = XVECEXP (concatn, 0, 1);
+ XVECEXP (concatn, 0, 0) = op1;
+ XVECEXP (concatn, 0, 1) = op0;
+ return concatn;
+}
+
/* Decompose the registers used in a simple move SET within INSN. If
we don't change anything, return INSN, otherwise return the start
of the sequence of moves. */
-static rtx
-resolve_simple_move (rtx set, rtx insn)
+static rtx_insn *
+resolve_simple_move (rtx set, rtx_insn *insn)
{
- rtx src, dest, real_dest, insns;
- enum machine_mode orig_mode, dest_mode;
- unsigned int words;
+ rtx src, dest, real_dest, src_op;
+ rtx_insn *insns;
+ machine_mode orig_mode, dest_mode;
+ unsigned int orig_size, words;
bool pushing;
src = SET_SRC (set);
dest = SET_DEST (set);
orig_mode = GET_MODE (dest);
- words = (GET_MODE_SIZE (orig_mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+ if (!interesting_mode_p (orig_mode, &orig_size, &words))
+ gcc_unreachable ();
gcc_assert (words > 1);
start_sequence ();
real_dest = NULL_RTX;
+ if ((src_op = operand_for_swap_move_operator (src)) != NULL_RTX)
+ {
+ if (resolve_reg_p (dest))
+ {
+ /* DEST is a CONCATN, so swap its operands and strip
+ SRC's operator. */
+ dest = resolve_operand_for_swap_move_operator (dest);
+ src = src_op;
+ }
+ else if (resolve_reg_p (src_op))
+ {
+ /* SRC is an operation on a CONCATN, so strip the operator and
+ swap the CONCATN's operands. */
+ src = resolve_operand_for_swap_move_operator (src_op);
+ }
+ }
+
if (GET_CODE (src) == SUBREG
&& resolve_reg_p (SUBREG_REG (src))
- && (SUBREG_BYTE (src) != 0
- || (GET_MODE_SIZE (orig_mode)
- != GET_MODE_SIZE (GET_MODE (SUBREG_REG (src))))))
+ && (maybe_ne (SUBREG_BYTE (src), 0)
+ || maybe_ne (orig_size, GET_MODE_SIZE (GET_MODE (SUBREG_REG (src))))))
{
real_dest = dest;
dest = gen_reg_rtx (orig_mode);
if (GET_CODE (dest) == SUBREG
&& resolve_reg_p (SUBREG_REG (dest))
- && (SUBREG_BYTE (dest) != 0
- || (GET_MODE_SIZE (orig_mode)
- != GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))))
+ && (maybe_ne (SUBREG_BYTE (dest), 0)
+ || maybe_ne (orig_size,
+ GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))))
{
- rtx reg, minsn, smove;
+ rtx reg, smove;
+ rtx_insn *minsn;
reg = gen_reg_rtx (orig_mode);
minsn = emit_move_insn (reg, src);
/* It's possible for the code to use a subreg of a decomposed
register while forming an address. We need to handle that before
passing the address to emit_move_insn. We pass NULL_RTX as the
- insn parameter to resolve_subreg_use because we can not validate
+ insn parameter to resolve_subreg_use because we cannot validate
the insn yet. */
if (MEM_P (src) || MEM_P (dest))
{
int acg;
if (MEM_P (src))
- for_each_rtx (&XEXP (src, 0), resolve_subreg_use, NULL_RTX);
+ resolve_subreg_use (&XEXP (src, 0), NULL_RTX);
if (MEM_P (dest))
- for_each_rtx (&XEXP (dest, 0), resolve_subreg_use, NULL_RTX);
+ resolve_subreg_use (&XEXP (dest, 0), NULL_RTX);
acg = apply_change_group ();
gcc_assert (acg);
}
reg = gen_reg_rtx (orig_mode);
-#ifdef AUTO_INC_DEC
- {
- rtx move = emit_move_insn (reg, src);
- if (MEM_P (src))
- {
- rtx note = find_reg_note (insn, REG_INC, NULL_RTX);
- if (note)
- add_reg_note (move, REG_INC, XEXP (note, 0));
- }
- }
-#else
- emit_move_insn (reg, src);
-#endif
+ if (AUTO_INC_DEC)
+ {
+ rtx_insn *move = emit_move_insn (reg, src);
+ if (MEM_P (src))
+ {
+ rtx note = find_reg_note (insn, REG_INC, NULL_RTX);
+ if (note)
+ add_reg_note (move, REG_INC, XEXP (note, 0));
+ }
+ }
+ else
+ emit_move_insn (reg, src);
+
src = reg;
}
if (real_dest == NULL_RTX)
real_dest = dest;
if (!SCALAR_INT_MODE_P (dest_mode))
- {
- dest_mode = mode_for_size (GET_MODE_SIZE (dest_mode) * BITS_PER_UNIT,
- MODE_INT, 0);
- gcc_assert (dest_mode != BLKmode);
- }
+ dest_mode = int_mode_for_mode (dest_mode).require ();
dest = gen_reg_rtx (dest_mode);
if (REG_P (real_dest))
REG_ATTRS (dest) = REG_ATTRS (real_dest);
{
unsigned int i, j, jinc;
- gcc_assert (GET_MODE_SIZE (orig_mode) % UNITS_PER_WORD == 0);
+ gcc_assert (orig_size % UNITS_PER_WORD == 0);
gcc_assert (GET_CODE (XEXP (dest, 0)) != PRE_MODIFY);
gcc_assert (GET_CODE (XEXP (dest, 0)) != POST_MODIFY);
if (real_dest != NULL_RTX)
{
- rtx mdest, minsn, smove;
+ rtx mdest, smove;
+ rtx_insn *minsn;
if (dest_mode == orig_mode)
mdest = dest;
mdest = simplify_gen_subreg (orig_mode, dest, GET_MODE (dest), 0);
minsn = emit_move_insn (real_dest, mdest);
-#ifdef AUTO_INC_DEC
- if (MEM_P (real_dest)
+ if (AUTO_INC_DEC && MEM_P (real_dest)
&& !(resolve_reg_p (real_dest) || resolve_subreg_p (real_dest)))
{
rtx note = find_reg_note (insn, REG_INC, NULL_RTX);
if (note)
add_reg_note (minsn, REG_INC, XEXP (note, 0));
}
-#endif
smove = single_set (minsn);
gcc_assert (smove != NULL_RTX);
component registers. Return whether we changed something. */
static bool
-resolve_clobber (rtx pat, rtx insn)
+resolve_clobber (rtx pat, rtx_insn *insn)
{
rtx reg;
- enum machine_mode orig_mode;
- unsigned int words, i;
+ machine_mode orig_mode;
+ unsigned int orig_size, words, i;
int ret;
reg = XEXP (pat, 0);
return false;
orig_mode = GET_MODE (reg);
- words = GET_MODE_SIZE (orig_mode);
- words = (words + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+ if (!interesting_mode_p (orig_mode, &orig_size, &words))
+ gcc_unreachable ();
ret = validate_change (NULL_RTX, &XEXP (pat, 0),
simplify_gen_subreg_concatn (word_mode, reg,
whether we changed something. */
static bool
-resolve_use (rtx pat, rtx insn)
+resolve_use (rtx pat, rtx_insn *insn)
{
if (resolve_reg_p (XEXP (pat, 0)) || resolve_subreg_p (XEXP (pat, 0)))
{
/* A VAR_LOCATION can be simplified. */
static void
-resolve_debug (rtx insn)
+resolve_debug (rtx_insn *insn)
{
- for_each_rtx (&PATTERN (insn), adjust_decomposed_uses, NULL_RTX);
+ subrtx_ptr_iterator::array_type array;
+ FOR_EACH_SUBRTX_PTR (iter, array, &PATTERN (insn), NONCONST)
+ {
+ rtx *loc = *iter;
+ rtx x = *loc;
+ if (resolve_subreg_p (x))
+ {
+ x = simplify_subreg_concatn (GET_MODE (x), SUBREG_REG (x),
+ SUBREG_BYTE (x));
+
+ if (x)
+ *loc = x;
+ else
+ x = copy_rtx (*loc);
+ }
+ if (resolve_reg_p (x))
+ *loc = copy_rtx (x);
+ }
df_insn_rescan (insn);
if INSN is decomposable. */
static bool
-find_decomposable_shift_zext (rtx insn, bool speed_p)
+find_decomposable_shift_zext (rtx_insn *insn, bool speed_p)
{
rtx set;
rtx op;
and 'set to zero' insn. Return a pointer to the new insn when a
replacement was done. */
-static rtx
-resolve_shift_zext (rtx insn)
+static rtx_insn *
+resolve_shift_zext (rtx_insn *insn)
{
rtx set;
rtx op;
rtx op_operand;
- rtx insns;
+ rtx_insn *insns;
rtx src_reg, dest_reg, dest_upper, upper_src = NULL_RTX;
int src_reg_num, dest_reg_num, offset1, offset2, src_offset;
+ scalar_int_mode inner_mode;
set = single_set (insn);
if (!set)
- return NULL_RTX;
+ return NULL;
op = SET_SRC (set);
if (GET_CODE (op) != ASHIFT
&& GET_CODE (op) != LSHIFTRT
&& GET_CODE (op) != ASHIFTRT
&& GET_CODE (op) != ZERO_EXTEND)
- return NULL_RTX;
+ return NULL;
op_operand = XEXP (op, 0);
+ if (!is_a <scalar_int_mode> (GET_MODE (op_operand), &inner_mode))
+ return NULL;
/* We can tear this operation apart only if the regs were already
torn apart. */
if (!resolve_reg_p (SET_DEST (set)) && !resolve_reg_p (op_operand))
- return NULL_RTX;
+ return NULL;
/* src_reg_num is the number of the word mode register which we
are operating on. For a left shift and a zero_extend on little
src_reg_num = (GET_CODE (op) == LSHIFTRT || GET_CODE (op) == ASHIFTRT)
? 1 : 0;
- if (WORDS_BIG_ENDIAN
- && GET_MODE_SIZE (GET_MODE (op_operand)) > UNITS_PER_WORD)
+ if (WORDS_BIG_ENDIAN && GET_MODE_SIZE (inner_mode) > UNITS_PER_WORD)
src_reg_num = 1 - src_reg_num;
if (GET_CODE (op) == ZERO_EXTEND)
if (dump_file)
{
- rtx in;
+ rtx_insn *in;
fprintf (dump_file, "; Replacing insn: %d with insns: ", INSN_UID (insn));
for (in = insns; in != insn; in = NEXT_INSN (in))
fprintf (dump_file, "%d ", INSN_UID (in));
static void
dump_choices (bool speed_p, const char *description)
{
- unsigned int i;
+ unsigned int size, factor, i;
fprintf (dump_file, "Choices when optimizing for %s:\n", description);
for (i = 0; i < MAX_MACHINE_MODE; i++)
- if (GET_MODE_SIZE (i) > UNITS_PER_WORD)
+ if (interesting_mode_p ((machine_mode) i, &size, &factor)
+ && factor > 1)
fprintf (dump_file, " %s mode %s for copy lowering.\n",
choices[speed_p].move_modes_to_split[i]
? "Splitting"
: "Skipping",
- GET_MODE_NAME ((enum machine_mode) i));
+ GET_MODE_NAME ((machine_mode) i));
fprintf (dump_file, " %s mode %s for zero_extend lowering.\n",
choices[speed_p].splitting_zext ? "Splitting" : "Skipping",
for (i = FIRST_PSEUDO_REGISTER; i < max; ++i)
if (regno_reg_rtx[i] != NULL)
{
- enum machine_mode mode = GET_MODE (regno_reg_rtx[i]);
+ machine_mode mode = GET_MODE (regno_reg_rtx[i]);
if (choices[false].move_modes_to_split[(int) mode]
|| choices[true].move_modes_to_split[(int) mode])
{
memset (reg_copy_graph.address (), 0, sizeof (bitmap) * max);
speed_p = optimize_function_for_speed_p (cfun);
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
{
- rtx insn;
+ rtx_insn *insn;
FOR_BB_INSNS (bb, insn)
{
n = recog_data.n_operands;
for (i = 0; i < n; ++i)
{
- for_each_rtx (&recog_data.operand[i],
- find_decomposable_subregs,
- &cmi);
+ find_decomposable_subregs (&recog_data.operand[i], &cmi);
/* We handle ASM_OPERANDS as a special case to support
things like x86 rdtsc which returns a DImode value.
bitmap_and_compl_into (decomposable_context, non_decomposable_context);
if (!bitmap_empty_p (decomposable_context))
{
- sbitmap sub_blocks;
unsigned int i;
sbitmap_iterator sbi;
bitmap_iterator iter;
propagate_pseudo_copies ();
- sub_blocks = sbitmap_alloc (last_basic_block_for_fn (cfun));
+ auto_sbitmap sub_blocks (last_basic_block_for_fn (cfun));
bitmap_clear (sub_blocks);
EXECUTE_IF_SET_IN_BITMAP (decomposable_context, 0, regno, iter)
decompose_register (regno);
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
{
- rtx insn;
+ rtx_insn *insn;
FOR_BB_INSNS (bb, insn)
{
set = simple_move (insn, speed_p);
if (set)
{
- rtx orig_insn = insn;
+ rtx_insn *orig_insn = insn;
bool cfi = control_flow_insn_p (insn);
/* We can end up splitting loads to multi-word pseudos
}
else
{
- rtx decomposed_shift;
+ rtx_insn *decomposed_shift;
decomposed_shift = resolve_shift_zext (insn);
if (decomposed_shift != NULL_RTX)
}
for (i = recog_data.n_operands - 1; i >= 0; --i)
- for_each_rtx (recog_data.operand_loc[i],
- resolve_subreg_use,
- insn);
+ resolve_subreg_use (recog_data.operand_loc[i], insn);
resolve_reg_notes (insn);
loads to appear. */
EXECUTE_IF_SET_IN_BITMAP (sub_blocks, 0, i, sbi)
{
- rtx insn, end;
+ rtx_insn *insn, *end;
edge fallthru;
bb = BASIC_BLOCK_FOR_FN (cfun, i);
insn = NEXT_INSN (insn);
}
}
-
- sbitmap_free (sub_blocks);
}
{
BITMAP_FREE (subreg_context);
}
\f
-/* Gate function for lower subreg pass. */
-
-static bool
-gate_handle_lower_subreg (void)
-{
- return flag_split_wide_types != 0;
-}
-
/* Implement first lower subreg pass. */
-static unsigned int
-rest_of_handle_lower_subreg (void)
-{
- decompose_multiword_subregs (false);
- return 0;
-}
-
-/* Implement second lower subreg pass. */
-
-static unsigned int
-rest_of_handle_lower_subreg2 (void)
-{
- decompose_multiword_subregs (true);
- return 0;
-}
-
namespace {
const pass_data pass_data_lower_subreg =
RTL_PASS, /* type */
"subreg1", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_gate */
- true, /* has_execute */
TV_LOWER_SUBREG, /* tv_id */
0, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_verify_flow, /* todo_flags_finish */
+ 0, /* todo_flags_finish */
};
class pass_lower_subreg : public rtl_opt_pass
{}
/* opt_pass methods: */
- bool gate () { return gate_handle_lower_subreg (); }
- unsigned int execute () { return rest_of_handle_lower_subreg (); }
+ virtual bool gate (function *) { return flag_split_wide_types != 0; }
+ virtual unsigned int execute (function *)
+ {
+ decompose_multiword_subregs (false);
+ return 0;
+ }
}; // class pass_lower_subreg
return new pass_lower_subreg (ctxt);
}
+/* Implement second lower subreg pass. */
+
namespace {
const pass_data pass_data_lower_subreg2 =
RTL_PASS, /* type */
"subreg2", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_gate */
- true, /* has_execute */
TV_LOWER_SUBREG, /* tv_id */
0, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- ( TODO_df_finish | TODO_verify_rtl_sharing
- | TODO_verify_flow ), /* todo_flags_finish */
+ TODO_df_finish, /* todo_flags_finish */
};
class pass_lower_subreg2 : public rtl_opt_pass
{}
/* opt_pass methods: */
- bool gate () { return gate_handle_lower_subreg (); }
- unsigned int execute () { return rest_of_handle_lower_subreg2 (); }
+ virtual bool gate (function *) { return flag_split_wide_types != 0; }
+ virtual unsigned int execute (function *)
+ {
+ decompose_multiword_subregs (true);
+ return 0;
+ }
}; // class pass_lower_subreg2