summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>2004-09-04 08:50:36 +0000
committerrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>2004-09-04 08:50:36 +0000
commitc49547c45490b7a0c0281d90f79848aa3191ba88 (patch)
tree4320b41f468e28f9439feab3f57dff953085081c /gcc
parentc068056a5c1064f44c6ad502c60981420b4a4562 (diff)
downloadgcc-c49547c45490b7a0c0281d90f79848aa3191ba88.tar.gz
* doc/md.texi (shift patterns): New anchor. Add reference to
TARGET_SHIFT_TRUNCATION_MASK. * doc/tm.texi (TARGET_SHIFT_TRUNCATION_MASK): Document. * target.h (shift_truncation_mask): New target hook. * targhook.h (default_shift_truncation_mask): Declare. * targhook.c (default_shift_truncation_mask): Define. * target-def.h (TARGET_SHIFT_TRUNCATION_MASK): Define. (TARGET_INITIALIZER): Include it. * simplify-rtx.c (simplify_binary_operation): Combine ASHIFT, ASHIFTRT and LSHIFTRT cases. Truncate arg1 if SHIFT_COUNT_TRUNCATED, otherwise reject all out-of-range values. Fix sign-extension code for modes whose width is smaller than HOST_BITS_PER_WIDE_INT. * optabs.c (simplify_expand_binop, force_expand_binop): New functions. (expand_superword_shift, expand_subword_shift): Likewise. (expand_doubleword_shift_condmove, expand_doubleword_shift): Likewise. (expand_binop): Use them to implement double-word shifts. * config/arm/arm.c (arm_shift_truncation_mask): New function. (TARGET_SHIFT_TRUNCATION_MASK): Define. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@87079 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog21
-rw-r--r--gcc/config/arm/arm.c15
-rw-r--r--gcc/doc/md.texi5
-rw-r--r--gcc/doc/tm.texi25
-rw-r--r--gcc/optabs.c490
-rw-r--r--gcc/simplify-rtx.c45
-rw-r--r--gcc/target-def.h5
-rw-r--r--gcc/target.h4
-rw-r--r--gcc/targhooks.c8
-rw-r--r--gcc/targhooks.h2
10 files changed, 494 insertions, 126 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 93ed7fde8d7..19d3faef681 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,24 @@
+2004-09-04 Richard Sandiford <rsandifo@redhat.com>
+
+ * doc/md.texi (shift patterns): New anchor. Add reference to
+ TARGET_SHIFT_TRUNCATION_MASK.
+ * doc/tm.texi (TARGET_SHIFT_TRUNCATION_MASK): Document.
+ * target.h (shift_truncation_mask): New target hook.
+ * targhook.h (default_shift_truncation_mask): Declare.
+ * targhook.c (default_shift_truncation_mask): Define.
+ * target-def.h (TARGET_SHIFT_TRUNCATION_MASK): Define.
+ (TARGET_INITIALIZER): Include it.
+ * simplify-rtx.c (simplify_binary_operation): Combine ASHIFT, ASHIFTRT
+ and LSHIFTRT cases. Truncate arg1 if SHIFT_COUNT_TRUNCATED, otherwise
+ reject all out-of-range values. Fix sign-extension code for modes
+ whose width is smaller than HOST_BITS_PER_WIDE_INT.
+ * optabs.c (simplify_expand_binop, force_expand_binop): New functions.
+ (expand_superword_shift, expand_subword_shift): Likewise.
+ (expand_doubleword_shift_condmove, expand_doubleword_shift): Likewise.
+ (expand_binop): Use them to implement double-word shifts.
+ * config/arm/arm.c (arm_shift_truncation_mask): New function.
+ (TARGET_SHIFT_TRUNCATION_MASK): Define.
+
2004-09-04 Jan Hubicka <jh@suse.cz>
* tree.c (iterate_hash_expr): Optimize, avoid use of iterative_hash_object.
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index c2617052d7c..38ead5d3c35 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -175,7 +175,7 @@ static bool arm_cxx_cdtor_returns_this (void);
static bool arm_cxx_key_method_may_be_inline (void);
static bool arm_cxx_export_class_data (void);
static void arm_init_libfuncs (void);
-
+static unsigned HOST_WIDE_INT arm_shift_truncation_mask (enum machine_mode);
/* Initialize the GCC target structure. */
#if TARGET_DLLIMPORT_DECL_ATTRIBUTES
@@ -248,6 +248,8 @@ static void arm_init_libfuncs (void);
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST arm_address_cost
+#undef TARGET_SHIFT_TRUNCATION_MASK
+#define TARGET_SHIFT_TRUNCATION_MASK arm_shift_truncation_mask
#undef TARGET_VECTOR_MODE_SUPPORTED_P
#define TARGET_VECTOR_MODE_SUPPORTED_P arm_vector_mode_supported_p
@@ -14334,3 +14336,14 @@ arm_vector_mode_supported_p (enum machine_mode mode)
return false;
}
+
+/* Implement TARGET_SHIFT_TRUNCATION_MASK. SImode shifts use normal
+ ARM insns and therefore guarantee that the shift count is modulo 256.
+ DImode shifts (those implemented by lib1funcs.asm or by optabs.c)
+ guarantee no particular behavior for out-of-range counts. */
+
+static unsigned HOST_WIDE_INT
+arm_shift_truncation_mask (enum machine_mode mode)
+{
+ return mode == SImode ? 255 : 0;
+}
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index 8404707db27..0fe7c50eb8d 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -2884,13 +2884,16 @@ quotient or remainder and generate the appropriate instruction.
@item @samp{udivmod@var{m}4}
Similar, but does unsigned division.
+@anchor{shift patterns}
@cindex @code{ashl@var{m}3} instruction pattern
@item @samp{ashl@var{m}3}
Arithmetic-shift operand 1 left by a number of bits specified by operand
2, and store the result in operand 0. Here @var{m} is the mode of
operand 0 and operand 1; operand 2's mode is specified by the
instruction pattern, and the compiler will convert the operand to that
-mode before generating the instruction.
+mode before generating the instruction. The meaning of out-of-range shift
+counts can optionally be specified by @code{TARGET_SHIFT_TRUNCATION_MASK}.
+@xref{TARGET_SHIFT_TRUNCATION_MASK}.
@cindex @code{ashr@var{m}3} instruction pattern
@cindex @code{lshr@var{m}3} instruction pattern
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 26724858e99..e21f72747ed 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -8749,6 +8749,31 @@ the implied truncation of the shift instructions.
You need not define this macro if it would always have the value of zero.
@end defmac
+@anchor{TARGET_SHIFT_TRUNCATION_MASK}
+@deftypefn {Target Hook} int TARGET_SHIFT_TRUNCATION_MASK (enum machine_mode @var{mode})
+This function describes how the standard shift patterns for @var{mode}
+deal with shifts by negative amounts or by more than the width of the mode.
+@xref{shift patterns}.
+
+On many machines, the shift patterns will apply a mask @var{m} to the
+shift count, meaning that a fixed-width shift of @var{x} by @var{y} is
+equivalent to an arbitrary-width shift of @var{x} by @var{y & m}. If
+this is true for mode @var{mode}, the function should return @var{m},
+otherwise it should return 0. A return value of 0 indicates that no
+particular behavior is guaranteed.
+
+Note that, unlike @code{SHIFT_COUNT_TRUNCATED}, this function does
+@emph{not} apply to general shift rtxes; it applies only to instructions
+that are generated by the named shift patterns.
+
+The default implementation of this function returns
+@code{GET_MODE_BITSIZE (@var{mode}) - 1} if @code{SHIFT_COUNT_TRUNCATED}
+and 0 otherwise. This definition is always safe, but if
+@code{SHIFT_COUNT_TRUNCATED} is false, and some shift patterns
+nevertheless truncate the shift count, you may get better code
+by overriding it.
+@end deftypefn
+
@defmac TRULY_NOOP_TRUNCATION (@var{outprec}, @var{inprec})
A C expression which is nonzero if on this machine it is safe to
``convert'' an integer of @var{inprec} bits to one of @var{outprec}
diff --git a/gcc/optabs.c b/gcc/optabs.c
index de7f4dc2caa..22c0b347946 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -312,7 +312,356 @@ optab_for_tree_code (enum tree_code code, tree type)
return NULL;
}
}
+
+/* Like expand_binop, but return a constant rtx if the result can be
+ calculated at compile time. The arguments and return value are
+ otherwise the same as for expand_binop. */
+
+static rtx
+simplify_expand_binop (enum machine_mode mode, optab binoptab,
+ rtx op0, rtx op1, rtx target, int unsignedp,
+ enum optab_methods methods)
+{
+ if (CONSTANT_P (op0) && CONSTANT_P (op1))
+ return simplify_gen_binary (binoptab->code, mode, op0, op1);
+ else
+ return expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods);
+}
+
+/* Like simplify_expand_binop, but always put the result in TARGET.
+ Return true if the expansion succeeded. */
+
+static bool
+force_expand_binop (enum machine_mode mode, optab binoptab,
+ rtx op0, rtx op1, rtx target, int unsignedp,
+ enum optab_methods methods)
+{
+ rtx x = simplify_expand_binop (mode, binoptab, op0, op1,
+ target, unsignedp, methods);
+ if (x == 0)
+ return false;
+ if (x != target)
+ emit_move_insn (target, x);
+ return true;
+}
+
+/* This subroutine of expand_doubleword_shift handles the cases in which
+ the effective shift value is >= BITS_PER_WORD. The arguments and return
+ value are the same as for the parent routine, except that SUPERWORD_OP1
+ is the shift count to use when shifting OUTOF_INPUT into INTO_TARGET.
+ INTO_TARGET may be null if the caller has decided to calculate it. */
+
+static bool
+expand_superword_shift (optab binoptab, rtx outof_input, rtx superword_op1,
+ rtx outof_target, rtx into_target,
+ int unsignedp, enum optab_methods methods)
+{
+ if (into_target != 0)
+ if (!force_expand_binop (word_mode, binoptab, outof_input, superword_op1,
+ into_target, unsignedp, methods))
+ return false;
+
+ if (outof_target != 0)
+ {
+ /* For a signed right shift, we must fill OUTOF_TARGET with copies
+ of the sign bit, otherwise we must fill it with zeros. */
+ if (binoptab != ashr_optab)
+ emit_move_insn (outof_target, CONST0_RTX (word_mode));
+ else
+ if (!force_expand_binop (word_mode, binoptab,
+ outof_input, GEN_INT (BITS_PER_WORD - 1),
+ outof_target, unsignedp, methods))
+ return false;
+ }
+ return true;
+}
+
+/* This subroutine of expand_doubleword_shift handles the cases in which
+ the effective shift value is < BITS_PER_WORD. The arguments and return
+ value are the same as for the parent routine. */
+
+static bool
+expand_subword_shift (enum machine_mode op1_mode, optab binoptab,
+ rtx outof_input, rtx into_input, rtx op1,
+ rtx outof_target, rtx into_target,
+ int unsignedp, enum optab_methods methods,
+ unsigned HOST_WIDE_INT shift_mask)
+{
+ optab reverse_unsigned_shift, unsigned_shift;
+ rtx tmp, carries;
+
+ reverse_unsigned_shift = (binoptab == ashl_optab ? lshr_optab : ashl_optab);
+ unsigned_shift = (binoptab == ashl_optab ? ashl_optab : lshr_optab);
+
+ /* The low OP1 bits of INTO_TARGET come from the high bits of OUTOF_INPUT.
+ We therefore need to shift OUTOF_INPUT by (BITS_PER_WORD - OP1) bits in
+ the opposite direction to BINOPTAB. */
+ if (CONSTANT_P (op1) || shift_mask >= BITS_PER_WORD)
+ {
+ carries = outof_input;
+ tmp = immed_double_const (BITS_PER_WORD, 0, op1_mode);
+ tmp = simplify_expand_binop (op1_mode, sub_optab, tmp, op1,
+ 0, true, methods);
+ }
+ else
+ {
+ /* We must avoid shifting by BITS_PER_WORD bits since that is either
+ the same as a zero shift (if shift_mask == BITS_PER_WORD - 1) or
+ has unknown behaviour. Do a single shift first, then shift by the
+ remainder. It's OK to use ~OP1 as the remainder if shift counts
+ are truncated to the mode size. */
+ carries = expand_binop (word_mode, reverse_unsigned_shift,
+ outof_input, const1_rtx, 0, unsignedp, methods);
+ if (shift_mask == BITS_PER_WORD - 1)
+ {
+ tmp = immed_double_const (-1, -1, op1_mode);
+ tmp = simplify_expand_binop (op1_mode, xor_optab, op1, tmp,
+ 0, true, methods);
+ }
+ else
+ {
+ tmp = immed_double_const (BITS_PER_WORD - 1, 0, op1_mode);
+ tmp = simplify_expand_binop (op1_mode, sub_optab, tmp, op1,
+ 0, true, methods);
+ }
+ }
+ if (tmp == 0 || carries == 0)
+ return false;
+ carries = expand_binop (word_mode, reverse_unsigned_shift,
+ carries, tmp, 0, unsignedp, methods);
+ if (carries == 0)
+ return false;
+
+ /* Shift INTO_INPUT logically by OP1. This is the last use of INTO_INPUT
+ so the result can go directly into INTO_TARGET if convenient. */
+ tmp = expand_binop (word_mode, unsigned_shift, into_input, op1,
+ into_target, unsignedp, methods);
+ if (tmp == 0)
+ return false;
+
+ /* Now OR in the bits carried over from OUTOF_INPUT. */
+ if (!force_expand_binop (word_mode, ior_optab, tmp, carries,
+ into_target, unsignedp, methods))
+ return false;
+
+ /* Use a standard word_mode shift for the out-of half. */
+ if (outof_target != 0)
+ if (!force_expand_binop (word_mode, binoptab, outof_input, op1,
+ outof_target, unsignedp, methods))
+ return false;
+
+ return true;
+}
+
+
+#ifdef HAVE_conditional_move
+/* Try implementing expand_doubleword_shift using conditional moves.
+ The shift is by < BITS_PER_WORD if (CMP_CODE CMP1 CMP2) is true,
+ otherwise it is by >= BITS_PER_WORD. SUBWORD_OP1 and SUPERWORD_OP1
+ are the shift counts to use in the former and latter case. All other
+ arguments are the same as the parent routine. */
+
+static bool
+expand_doubleword_shift_condmove (enum machine_mode op1_mode, optab binoptab,
+ enum rtx_code cmp_code, rtx cmp1, rtx cmp2,
+ rtx outof_input, rtx into_input,
+ rtx subword_op1, rtx superword_op1,
+ rtx outof_target, rtx into_target,
+ int unsignedp, enum optab_methods methods,
+ unsigned HOST_WIDE_INT shift_mask)
+{
+ rtx outof_superword, into_superword;
+
+ /* Put the superword version of the output into OUTOF_SUPERWORD and
+ INTO_SUPERWORD. */
+ outof_superword = outof_target != 0 ? gen_reg_rtx (word_mode) : 0;
+ if (outof_target != 0 && subword_op1 == superword_op1)
+ {
+ /* The value INTO_TARGET >> SUBWORD_OP1, which we later store in
+ OUTOF_TARGET, is the same as the value of INTO_SUPERWORD. */
+ into_superword = outof_target;
+ if (!expand_superword_shift (binoptab, outof_input, superword_op1,
+ outof_superword, 0, unsignedp, methods))
+ return false;
+ }
+ else
+ {
+ into_superword = gen_reg_rtx (word_mode);
+ if (!expand_superword_shift (binoptab, outof_input, superword_op1,
+ outof_superword, into_superword,
+ unsignedp, methods))
+ return false;
+ }
+
+ /* Put the subword version directly in OUTOF_TARGET and INTO_TARGET. */
+ if (!expand_subword_shift (op1_mode, binoptab,
+ outof_input, into_input, subword_op1,
+ outof_target, into_target,
+ unsignedp, methods, shift_mask))
+ return false;
+
+ /* Select between them. Do the INTO half first because INTO_SUPERWORD
+ might be the current value of OUTOF_TARGET. */
+ if (!emit_conditional_move (into_target, cmp_code, cmp1, cmp2, op1_mode,
+ into_target, into_superword, word_mode, false))
+ return false;
+
+ if (outof_target != 0)
+ if (!emit_conditional_move (outof_target, cmp_code, cmp1, cmp2, op1_mode,
+ outof_target, outof_superword,
+ word_mode, false))
+ return false;
+
+ return true;
+}
+#endif
+
+/* Expand a doubleword shift (ashl, ashr or lshr) using word-mode shifts.
+ OUTOF_INPUT and INTO_INPUT are the two word-sized halves of the first
+ input operand; the shift moves bits in the direction OUTOF_INPUT->
+ INTO_TARGET. OUTOF_TARGET and INTO_TARGET are the equivalent words
+ of the target. OP1 is the shift count and OP1_MODE is its mode.
+ If OP1 is constant, it will have been truncated as appropriate
+ and is known to be nonzero.
+
+ If SHIFT_MASK is zero, the result of word shifts is undefined when the
+ shift count is outside the range [0, BITS_PER_WORD). This routine must
+ avoid generating such shifts for OP1s in the range [0, BITS_PER_WORD * 2).
+
+ If SHIFT_MASK is nonzero, all word-mode shift counts are effectively
+ masked by it and shifts in the range [BITS_PER_WORD, SHIFT_MASK) will
+ fill with zeros or sign bits as appropriate.
+
+ If SHIFT_MASK is BITS_PER_WORD - 1, this routine will synthesise
+ a doubleword shift whose equivalent mask is BITS_PER_WORD * 2 - 1.
+ Doing this preserves semantics required by SHIFT_COUNT_TRUNCATED.
+ In all other cases, shifts by values outside [0, BITS_PER_UNIT * 2)
+ are undefined.
+
+ BINOPTAB, UNSIGNEDP and METHODS are as for expand_binop. This function
+ may not use INTO_INPUT after modifying INTO_TARGET, and similarly for
+ OUTOF_INPUT and OUTOF_TARGET. OUTOF_TARGET can be null if the parent
+ function wants to calculate it itself.
+
+ Return true if the shift could be successfully synthesized. */
+
+static bool
+expand_doubleword_shift (enum machine_mode op1_mode, optab binoptab,
+ rtx outof_input, rtx into_input, rtx op1,
+ rtx outof_target, rtx into_target,
+ int unsignedp, enum optab_methods methods,
+ unsigned HOST_WIDE_INT shift_mask)
+{
+ rtx superword_op1, tmp, cmp1, cmp2;
+ rtx subword_label, done_label;
+ enum rtx_code cmp_code;
+
+ /* See if word-mode shifts by BITS_PER_WORD...BITS_PER_WORD * 2 - 1 will
+ fill the result with sign or zero bits as appropriate. If so, the value
+ of OUTOF_TARGET will always be (SHIFT OUTOF_INPUT OP1). Recursively call
+ this routine to calculate INTO_TARGET (which depends on both OUTOF_INPUT
+ and INTO_INPUT), then emit code to set up OUTOF_TARGET.
+
+ This isn't worthwhile for constant shifts since the optimizers will
+ cope better with in-range shift counts. */
+ if (shift_mask >= BITS_PER_WORD
+ && outof_target != 0
+ && !CONSTANT_P (op1))
+ {
+ if (!expand_doubleword_shift (op1_mode, binoptab,
+ outof_input, into_input, op1,
+ 0, into_target,
+ unsignedp, methods, shift_mask))
+ return false;
+ if (!force_expand_binop (word_mode, binoptab, outof_input, op1,
+ outof_target, unsignedp, methods))
+ return false;
+ return true;
+ }
+
+ /* Set CMP_CODE, CMP1 and CMP2 so that the rtx (CMP_CODE CMP1 CMP2)
+ is true when the effective shift value is less than BITS_PER_WORD.
+ Set SUPERWORD_OP1 to the shift count that should be used to shift
+ OUTOF_INPUT into INTO_TARGET when the condition is false. */
+ tmp = immed_double_const (BITS_PER_WORD, 0, op1_mode);
+ if (!CONSTANT_P (op1) && shift_mask == BITS_PER_WORD - 1)
+ {
+ /* Set CMP1 to OP1 & BITS_PER_WORD. The result is zero iff OP1
+ is a subword shift count. */
+ cmp1 = simplify_expand_binop (op1_mode, and_optab, op1, tmp,
+ 0, true, methods);
+ cmp2 = CONST0_RTX (op1_mode);
+ cmp_code = EQ;
+ superword_op1 = op1;
+ }
+ else
+ {
+ /* Set CMP1 to OP1 - BITS_PER_WORD. */
+ cmp1 = simplify_expand_binop (op1_mode, sub_optab, op1, tmp,
+ 0, true, methods);
+ cmp2 = CONST0_RTX (op1_mode);
+ cmp_code = LT;
+ superword_op1 = cmp1;
+ }
+ if (cmp1 == 0)
+ return false;
+
+ /* If we can compute the condition at compile time, pick the
+ appropriate subroutine. */
+ tmp = simplify_relational_operation (cmp_code, SImode, op1_mode, cmp1, cmp2);
+ if (tmp != 0 && GET_CODE (tmp) == CONST_INT)
+ {
+ if (tmp == const0_rtx)
+ return expand_superword_shift (binoptab, outof_input, superword_op1,
+ outof_target, into_target,
+ unsignedp, methods);
+ else
+ return expand_subword_shift (op1_mode, binoptab,
+ outof_input, into_input, op1,
+ outof_target, into_target,
+ unsignedp, methods, shift_mask);
+ }
+
+#ifdef HAVE_conditional_move
+ /* Try using conditional moves to generate straight-line code. */
+ {
+ rtx start = get_last_insn ();
+ if (expand_doubleword_shift_condmove (op1_mode, binoptab,
+ cmp_code, cmp1, cmp2,
+ outof_input, into_input,
+ op1, superword_op1,
+ outof_target, into_target,
+ unsignedp, methods, shift_mask))
+ return true;
+ delete_insns_since (start);
+ }
+#endif
+
+ /* As a last resort, use branches to select the correct alternative. */
+ subword_label = gen_label_rtx ();
+ done_label = gen_label_rtx ();
+
+ do_compare_rtx_and_jump (cmp1, cmp2, cmp_code, false, op1_mode,
+ 0, 0, subword_label);
+
+ if (!expand_superword_shift (binoptab, outof_input, superword_op1,
+ outof_target, into_target,
+ unsignedp, methods))
+ return false;
+ emit_jump_insn (gen_jump (done_label));
+ emit_barrier ();
+ emit_label (subword_label);
+
+ if (!expand_subword_shift (op1_mode, binoptab,
+ outof_input, into_input, op1,
+ outof_target, into_target,
+ unsignedp, methods, shift_mask))
+ return false;
+
+ emit_label (done_label);
+ return true;
+}
/* Wrapper around expand_binop which takes an rtx code to specify
the operation to perform, not an optab pointer. All other
@@ -638,118 +987,71 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
if ((binoptab == lshr_optab || binoptab == ashl_optab
|| binoptab == ashr_optab)
&& class == MODE_INT
- && GET_CODE (op1) == CONST_INT
+ && (GET_CODE (op1) == CONST_INT || !optimize_size)
&& GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
&& binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
&& ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
&& lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
{
- rtx insns, inter, equiv_value;
- rtx into_target, outof_target;
- rtx into_input, outof_input;
- int shift_count, left_shift, outof_word;
+ unsigned HOST_WIDE_INT shift_mask, double_shift_mask;
+ enum machine_mode op1_mode;
- /* If TARGET is the same as one of the operands, the REG_EQUAL note
- won't be accurate, so use a new target. */
- if (target == 0 || target == op0 || target == op1)
- target = gen_reg_rtx (mode);
-
- start_sequence ();
+ double_shift_mask = targetm.shift_truncation_mask (mode);
+ shift_mask = targetm.shift_truncation_mask (word_mode);
+ op1_mode = GET_MODE (op1) != VOIDmode ? GET_MODE (op1) : word_mode;
- shift_count = INTVAL (op1);
+ /* Apply the truncation to constant shifts. */
+ if (double_shift_mask > 0 && GET_CODE (op1) == CONST_INT)
+ op1 = GEN_INT (INTVAL (op1) & double_shift_mask);
- /* OUTOF_* is the word we are shifting bits away from, and
- INTO_* is the word that we are shifting bits towards, thus
- they differ depending on the direction of the shift and
- WORDS_BIG_ENDIAN. */
-
- left_shift = binoptab == ashl_optab;
- outof_word = left_shift ^ ! WORDS_BIG_ENDIAN;
-
- outof_target = operand_subword (target, outof_word, 1, mode);
- into_target = operand_subword (target, 1 - outof_word, 1, mode);
-
- outof_input = operand_subword_force (op0, outof_word, mode);
- into_input = operand_subword_force (op0, 1 - outof_word, mode);
+ if (op1 == CONST0_RTX (op1_mode))
+ return op0;
- if (shift_count >= BITS_PER_WORD)
+ /* Make sure that this is a combination that expand_doubleword_shift
+ can handle. See the comments there for details. */
+ if (double_shift_mask == 0
+ || (shift_mask == BITS_PER_WORD - 1
+ && double_shift_mask == BITS_PER_WORD * 2 - 1))
{
- inter = expand_binop (word_mode, binoptab,
- outof_input,
- GEN_INT (shift_count - BITS_PER_WORD),
- into_target, unsignedp, next_methods);
+ rtx insns, equiv_value;
+ rtx into_target, outof_target;
+ rtx into_input, outof_input;
+ int left_shift, outof_word;
- if (inter != 0 && inter != into_target)
- emit_move_insn (into_target, inter);
-
- /* For a signed right shift, we must fill the word we are shifting
- out of with copies of the sign bit. Otherwise it is zeroed. */
- if (inter != 0 && binoptab != ashr_optab)
- inter = CONST0_RTX (word_mode);
- else if (inter != 0)
- inter = expand_binop (word_mode, binoptab,
- outof_input,
- GEN_INT (BITS_PER_WORD - 1),
- outof_target, unsignedp, next_methods);
-
- if (inter != 0 && inter != outof_target)
- emit_move_insn (outof_target, inter);
- }
- else
- {
- rtx carries;
- optab reverse_unsigned_shift, unsigned_shift;
-
- /* For a shift of less then BITS_PER_WORD, to compute the carry,
- we must do a logical shift in the opposite direction of the
- desired shift. */
-
- reverse_unsigned_shift = (left_shift ? lshr_optab : ashl_optab);
-
- /* For a shift of less than BITS_PER_WORD, to compute the word
- shifted towards, we need to unsigned shift the orig value of
- that word. */
-
- unsigned_shift = (left_shift ? ashl_optab : lshr_optab);
-
- carries = expand_binop (word_mode, reverse_unsigned_shift,
- outof_input,
- GEN_INT (BITS_PER_WORD - shift_count),
- 0, unsignedp, next_methods);
-
- if (carries == 0)
- inter = 0;
- else
- inter = expand_binop (word_mode, unsigned_shift, into_input,
- op1, 0, unsignedp, next_methods);
+ /* If TARGET is the same as one of the operands, the REG_EQUAL note
+ won't be accurate, so use a new target. */
+ if (target == 0 || target == op0 || target == op1)
+ target = gen_reg_rtx (mode);
- if (inter != 0)
- inter = expand_binop (word_mode, ior_optab, carries, inter,
- into_target, unsignedp, next_methods);
+ start_sequence ();
- if (inter != 0 && inter != into_target)
- emit_move_insn (into_target, inter);
+ /* OUTOF_* is the word we are shifting bits away from, and
+ INTO_* is the word that we are shifting bits towards, thus
+ they differ depending on the direction of the shift and
+ WORDS_BIG_ENDIAN. */
- if (inter != 0)
- inter = expand_binop (word_mode, binoptab, outof_input,
- op1, outof_target, unsignedp, next_methods);
+ left_shift = binoptab == ashl_optab;
+ outof_word = left_shift ^ ! WORDS_BIG_ENDIAN;
- if (inter != 0 && inter != outof_target)
- emit_move_insn (outof_target, inter);
- }
+ outof_target = operand_subword (target, outof_word, 1, mode);
+ into_target = operand_subword (target, 1 - outof_word, 1, mode);
- insns = get_insns ();
- end_sequence ();
+ outof_input = operand_subword_force (op0, outof_word, mode);
+ into_input = operand_subword_force (op0, 1 - outof_word, mode);
- if (inter != 0)
- {
- if (binoptab->code != UNKNOWN)
- equiv_value = gen_rtx_fmt_ee (binoptab->code, mode, op0, op1);
- else
- equiv_value = 0;
+ if (expand_doubleword_shift (op1_mode, binoptab,
+ outof_input, into_input, op1,
+ outof_target, into_target,
+ unsignedp, methods, shift_mask))
+ {
+ insns = get_insns ();
+ end_sequence ();
- emit_no_conflict_block (insns, target, op0, op1, equiv_value);
- return target;
+ equiv_value = gen_rtx_fmt_ee (binoptab->code, mode, op0, op1);
+ emit_no_conflict_block (insns, target, op0, op1, equiv_value);
+ return target;
+ }
+ end_sequence ();
}
}
diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c
index a0f17696cea..285f898de80 100644
--- a/gcc/simplify-rtx.c
+++ b/gcc/simplify-rtx.c
@@ -2343,41 +2343,26 @@ simplify_binary_operation (enum rtx_code code, enum machine_mode mode,
break;
case LSHIFTRT:
- /* If shift count is undefined, don't fold it; let the machine do
- what it wants. But truncate it if the machine will do that. */
- if (arg1 < 0)
- return 0;
-
- if (SHIFT_COUNT_TRUNCATED)
- arg1 %= width;
-
- val = ((unsigned HOST_WIDE_INT) arg0) >> arg1;
- break;
-
case ASHIFT:
- if (arg1 < 0)
- return 0;
-
- if (SHIFT_COUNT_TRUNCATED)
- arg1 %= width;
-
- val = ((unsigned HOST_WIDE_INT) arg0) << arg1;
- break;
-
case ASHIFTRT:
- if (arg1 < 0)
- return 0;
-
+ /* Truncate the shift if SHIFT_COUNT_TRUNCATED, otherwise make sure the
+ value is in range. We can't return any old value for out-of-range
+ arguments because either the middle-end (via shift_truncation_mask)
+ or the back-end might be relying on target-specific knowledge.
+ Nor can we rely on shift_truncation_mask, since the shift might
+ not be part of an ashlM3, lshrM3 or ashrM3 instruction. */
if (SHIFT_COUNT_TRUNCATED)
- arg1 %= width;
-
- val = arg0s >> arg1;
+ arg1 = (unsigned HOST_WIDE_INT) arg1 % width;
+ else if (arg1 < 0 || arg1 >= GET_MODE_BITSIZE (mode))
+ return 0;
- /* Bootstrap compiler may not have sign extended the right shift.
- Manually extend the sign to insure bootstrap cc matches gcc. */
- if (arg0s < 0 && arg1 > 0)
- val |= ((HOST_WIDE_INT) -1) << (HOST_BITS_PER_WIDE_INT - arg1);
+ val = (code == ASHIFT
+ ? ((unsigned HOST_WIDE_INT) arg0) << arg1
+ : ((unsigned HOST_WIDE_INT) arg0) >> arg1);
+ /* Sign-extend the result for arithmetic right shifts. */
+ if (code == ASHIFTRT && arg0s < 0 && arg1 > 0)
+ val |= ((HOST_WIDE_INT) -1) << (width - arg1);
break;
case ROTATERT:
diff --git a/gcc/target-def.h b/gcc/target-def.h
index 3b4c11b0694..01837efd604 100644
--- a/gcc/target-def.h
+++ b/gcc/target-def.h
@@ -301,6 +301,10 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define TARGET_BINDS_LOCAL_P default_binds_local_p
#endif
+#ifndef TARGET_SHIFT_TRUNCATION_MASK
+#define TARGET_SHIFT_TRUNCATION_MASK default_shift_truncation_mask
+#endif
+
#ifndef TARGET_VALID_POINTER_MODE
#define TARGET_VALID_POINTER_MODE default_valid_pointer_mode
#endif
@@ -488,6 +492,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
TARGET_BINDS_LOCAL_P, \
TARGET_ENCODE_SECTION_INFO, \
TARGET_STRIP_NAME_ENCODING, \
+ TARGET_SHIFT_TRUNCATION_MASK, \
TARGET_VALID_POINTER_MODE, \
TARGET_SCALAR_MODE_SUPPORTED_P, \
TARGET_VECTOR_MODE_SUPPORTED_P, \
diff --git a/gcc/target.h b/gcc/target.h
index 035aeed4426..735a85d11dd 100644
--- a/gcc/target.h
+++ b/gcc/target.h
@@ -378,6 +378,10 @@ struct gcc_target
/* Undo the effects of encode_section_info on the symbol string. */
const char * (* strip_name_encoding) (const char *);
+ /* If shift optabs for MODE are known to always truncate the shift count,
+ return the mask that they apply. Return 0 otherwise. */
+ unsigned HOST_WIDE_INT (* shift_truncation_mask) (enum machine_mode mode);
+
/* True if MODE is valid for a pointer in __attribute__((mode("MODE"))). */
bool (* valid_pointer_mode) (enum machine_mode mode);
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 6aa2e07117b..5ecb6d8370d 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -135,6 +135,14 @@ default_eh_return_filter_mode (void)
return word_mode;
}
+/* The default implementation of TARGET_SHIFT_TRUNCATION_MASK. */
+
+unsigned HOST_WIDE_INT
+default_shift_truncation_mask (enum machine_mode mode)
+{
+ return SHIFT_COUNT_TRUNCATED ? GET_MODE_BITSIZE (mode) - 1 : 0;
+}
+
/* Generic hook that takes a CUMULATIVE_ARGS pointer and returns true. */
bool
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index b91e51dec84..8745f7eb5b7 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -32,6 +32,8 @@ extern bool hook_bool_CUMULATIVE_ARGS_false (CUMULATIVE_ARGS *);
extern bool default_pretend_outgoing_varargs_named (CUMULATIVE_ARGS *);
extern enum machine_mode default_eh_return_filter_mode (void);
+extern unsigned HOST_WIDE_INT default_shift_truncation_mask
+ (enum machine_mode);
extern bool hook_bool_CUMULATIVE_ARGS_true (CUMULATIVE_ARGS *);
extern tree default_cxx_guard_type (void);