md.texi (shift patterns): New anchor.
authorRichard Sandiford <rsandifo@redhat.com>
Sat, 4 Sep 2004 08:50:36 +0000 (08:50 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Sat, 4 Sep 2004 08:50:36 +0000 (08:50 +0000)
* 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.

From-SVN: r87079

gcc/ChangeLog
gcc/config/arm/arm.c
gcc/doc/md.texi
gcc/doc/tm.texi
gcc/optabs.c
gcc/simplify-rtx.c
gcc/target-def.h
gcc/target.h
gcc/targhooks.c
gcc/targhooks.h

index 93ed7fde8d7cf4a47d9879fa0741ccaf83f83a76..19d3faef6818502e7eea5035cabd0e0fe1885674 100644 (file)
@@ -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.
index c2617052d7c88c054de13f41f40f5e4761fe5b6d..38ead5d3c3524cabc9ba4a2818e6397e41cc2943 100644 (file)
@@ -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);
 \f
 /* 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;
+}
index 8404707db27757a0148a8d54c652a78c2d37975e..0fe7c50eb8d26d15e076e0fa6536dd1743f7882e 100644 (file)
@@ -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
index 26724858e9911411aae50921f44465d2e9bb93eb..e21f72747ed34e248db30f92cd080d788e928115 100644 (file)
@@ -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}
index de7f4dc2caa117f66d0dd511c28c48809b06950c..22c0b3479468035764c7c0eaa72629eafe4a3747 100644 (file)
@@ -312,7 +312,356 @@ optab_for_tree_code (enum tree_code code, tree type)
       return NULL;
     }
 }
+\f
+/* 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;
+}
 \f
 /* 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 ();
        }
     }
 
index a0f17696cea5e005cf97f3a8b02679cb392daa2b..285f898de80b86043cf9626044b9c832fde78ad3 100644 (file)
@@ -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:
index 3b4c11b069458feb0e21356d113c2a3b33e4758c..01837efd60460c62b43887eda23f4ef371c4ac3f 100644 (file)
@@ -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,               \
index 035aeed4426863460d6fa638e35e0f5400ee1d4d..735a85d11dd00e0be28343ab054b45062e4ac20c 100644 (file)
@@ -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);
 
index 6aa2e07117bda7dd563d340a984b31f67c620fa6..5ecb6d8370d5010283840609c5b8c835e1941579 100644 (file)
@@ -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
index b91e51dec847e026531803a2428cf6b7cef04b16..8745f7eb5b7ec02b4eab886519d981c0ac4a842c 100644 (file)
@@ -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);