OSDN Git Service

compiler: Prohibit comparisons of funcs, maps, and slices to non-nil.
[pf3gnuchains/gcc-fork.git] / gcc / simplify-rtx.c
index a7a91e5..6733b84 100644 (file)
@@ -1,7 +1,7 @@
 /* RTL simplification functions for GNU compiler.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-   Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+   2011  Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -34,7 +34,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "recog.h"
 #include "function.h"
 #include "expr.h"
-#include "toplev.h"
 #include "diagnostic-core.h"
 #include "output.h"
 #include "ggc.h"
@@ -83,7 +82,7 @@ mode_signbit_p (enum machine_mode mode, const_rtx x)
   if (GET_MODE_CLASS (mode) != MODE_INT)
     return false;
 
-  width = GET_MODE_BITSIZE (mode);
+  width = GET_MODE_PRECISION (mode);
   if (width == 0)
     return false;
 
@@ -104,6 +103,62 @@ mode_signbit_p (enum machine_mode mode, const_rtx x)
     val &= ((unsigned HOST_WIDE_INT) 1 << width) - 1;
   return val == ((unsigned HOST_WIDE_INT) 1 << (width - 1));
 }
+
+/* Test whether VAL is equal to the most significant bit of mode MODE
+   (after masking with the mode mask of MODE).  Returns false if the
+   precision of MODE is too large to handle.  */
+
+bool
+val_signbit_p (enum machine_mode mode, unsigned HOST_WIDE_INT val)
+{
+  unsigned int width;
+
+  if (GET_MODE_CLASS (mode) != MODE_INT)
+    return false;
+
+  width = GET_MODE_PRECISION (mode);
+  if (width == 0 || width > HOST_BITS_PER_WIDE_INT)
+    return false;
+
+  val &= GET_MODE_MASK (mode);
+  return val == ((unsigned HOST_WIDE_INT) 1 << (width - 1));
+}
+
+/* Test whether the most significant bit of mode MODE is set in VAL.
+   Returns false if the precision of MODE is too large to handle.  */
+bool
+val_signbit_known_set_p (enum machine_mode mode, unsigned HOST_WIDE_INT val)
+{
+  unsigned int width;
+
+  if (GET_MODE_CLASS (mode) != MODE_INT)
+    return false;
+
+  width = GET_MODE_PRECISION (mode);
+  if (width == 0 || width > HOST_BITS_PER_WIDE_INT)
+    return false;
+
+  val &= (unsigned HOST_WIDE_INT) 1 << (width - 1);
+  return val != 0;
+}
+
+/* Test whether the most significant bit of mode MODE is clear in VAL.
+   Returns false if the precision of MODE is too large to handle.  */
+bool
+val_signbit_known_clear_p (enum machine_mode mode, unsigned HOST_WIDE_INT val)
+{
+  unsigned int width;
+
+  if (GET_MODE_CLASS (mode) != MODE_INT)
+    return false;
+
+  width = GET_MODE_PRECISION (mode);
+  if (width == 0 || width > HOST_BITS_PER_WIDE_INT)
+    return false;
+
+  val &= (unsigned HOST_WIDE_INT) 1 << (width - 1);
+  return val == 0;
+}
 \f
 /* Make a binary operation by properly ordering the operands and
    seeing if the expression folds.  */
@@ -213,7 +268,7 @@ delegitimize_mem_from_attrs (rtx x)
      use their base addresses as equivalent.  */
   if (MEM_P (x)
       && MEM_EXPR (x)
-      && MEM_OFFSET (x))
+      && MEM_OFFSET_KNOWN_P (x))
     {
       tree decl = MEM_EXPR (x);
       enum machine_mode mode = GET_MODE (x);
@@ -266,7 +321,7 @@ delegitimize_mem_from_attrs (rtx x)
        {
          rtx newx;
 
-         offset += INTVAL (MEM_OFFSET (x));
+         offset += MEM_OFFSET (x);
 
          newx = DECL_RTL (decl);
 
@@ -594,7 +649,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
       if (STORE_FLAG_VALUE == -1
          && GET_CODE (op) == ASHIFTRT
          && GET_CODE (XEXP (op, 1))
-         && INTVAL (XEXP (op, 1)) == GET_MODE_BITSIZE (mode) - 1)
+         && INTVAL (XEXP (op, 1)) == GET_MODE_PRECISION (mode) - 1)
        return simplify_gen_relational (GE, mode, VOIDmode,
                                        XEXP (op, 0), const0_rtx);
 
@@ -687,13 +742,13 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          return simplify_gen_binary (MINUS, mode, temp, XEXP (op, 1));
        }
 
-      /* (neg (mult A B)) becomes (mult (neg A) B).
+      /* (neg (mult A B)) becomes (mult A (neg B)).
         This works even for floating-point values.  */
       if (GET_CODE (op) == MULT
          && !HONOR_SIGN_DEPENDENT_ROUNDING (mode))
        {
-         temp = simplify_gen_unary (NEG, mode, XEXP (op, 0), mode);
-         return simplify_gen_binary (MULT, mode, temp, XEXP (op, 1));
+         temp = simplify_gen_unary (NEG, mode, XEXP (op, 1), mode);
+         return simplify_gen_binary (MULT, mode, XEXP (op, 0), temp);
        }
 
       /* NEG commutes with ASHIFT since it is multiplication.  Only do
@@ -710,7 +765,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
         C is equal to the width of MODE minus 1.  */
       if (GET_CODE (op) == ASHIFTRT
          && CONST_INT_P (XEXP (op, 1))
-         && INTVAL (XEXP (op, 1)) == GET_MODE_BITSIZE (mode) - 1)
+         && INTVAL (XEXP (op, 1)) == GET_MODE_PRECISION (mode) - 1)
        return simplify_gen_binary (LSHIFTRT, mode,
                                    XEXP (op, 0), XEXP (op, 1));
 
@@ -718,7 +773,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
         C is equal to the width of MODE minus 1.  */
       if (GET_CODE (op) == LSHIFTRT
          && CONST_INT_P (XEXP (op, 1))
-         && INTVAL (XEXP (op, 1)) == GET_MODE_BITSIZE (mode) - 1)
+         && INTVAL (XEXP (op, 1)) == GET_MODE_PRECISION (mode) - 1)
        return simplify_gen_binary (ASHIFTRT, mode,
                                    XEXP (op, 0), XEXP (op, 1));
 
@@ -735,14 +790,14 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          && SCALAR_INT_MODE_P (GET_MODE (XEXP (op, 0))))
        {
          enum machine_mode inner = GET_MODE (XEXP (op, 0));
-         int isize = GET_MODE_BITSIZE (inner);
+         int isize = GET_MODE_PRECISION (inner);
          if (STORE_FLAG_VALUE == 1)
            {
              temp = simplify_gen_binary (ASHIFTRT, inner, XEXP (op, 0),
                                          GEN_INT (isize - 1));
              if (mode == inner)
                return temp;
-             if (GET_MODE_BITSIZE (mode) > isize)
+             if (GET_MODE_PRECISION (mode) > isize)
                return simplify_gen_unary (SIGN_EXTEND, mode, temp, inner);
              return simplify_gen_unary (TRUNCATE, mode, temp, inner);
            }
@@ -752,7 +807,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
                                          GEN_INT (isize - 1));
              if (mode == inner)
                return temp;
-             if (GET_MODE_BITSIZE (mode) > isize)
+             if (GET_MODE_PRECISION (mode) > isize)
                return simplify_gen_unary (ZERO_EXTEND, mode, temp, inner);
              return simplify_gen_unary (TRUNCATE, mode, temp, inner);
            }
@@ -797,11 +852,10 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          truncation.  But don't do this for an (LSHIFTRT (MULT ...))
          since this will cause problems with the umulXi3_highpart
          patterns.  */
-      if ((TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
-                                GET_MODE_BITSIZE (GET_MODE (op)))
+      if ((TRULY_NOOP_TRUNCATION_MODES_P (mode, GET_MODE (op))
           ? (num_sign_bit_copies (op, GET_MODE (op))
-             > (unsigned int) (GET_MODE_BITSIZE (GET_MODE (op))
-                               - GET_MODE_BITSIZE (mode)))
+             > (unsigned int) (GET_MODE_PRECISION (GET_MODE (op))
+                               - GET_MODE_PRECISION (mode)))
           : truncated_to_mode (mode, op))
          && ! (GET_CODE (op) == LSHIFTRT
                && GET_CODE (XEXP (op, 0)) == MULT))
@@ -811,9 +865,9 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          STORE_FLAG_VALUE permits.  This is like the previous test,
          but it works even if the comparison is done in a mode larger
          than HOST_BITS_PER_WIDE_INT.  */
-      if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+      if (HWI_COMPUTABLE_MODE_P (mode)
          && COMPARISON_P (op)
-         && ((HOST_WIDE_INT) STORE_FLAG_VALUE & ~GET_MODE_MASK (mode)) == 0)
+         && (STORE_FLAG_VALUE & ~GET_MODE_MASK (mode)) == 0)
        return rtl_hooks.gen_lowpart_no_emit (mode, op);
       break;
 
@@ -850,7 +904,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          && (flag_unsafe_math_optimizations
              || (SCALAR_FLOAT_MODE_P (GET_MODE (op))
                  && ((unsigned)significand_size (GET_MODE (op))
-                     >= (GET_MODE_BITSIZE (GET_MODE (XEXP (op, 0)))
+                     >= (GET_MODE_PRECISION (GET_MODE (XEXP (op, 0)))
                          - num_sign_bit_copies (XEXP (op, 0),
                                                 GET_MODE (XEXP (op, 0))))))))
        return simplify_gen_unary (FLOAT, mode,
@@ -887,7 +941,7 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          || (GET_CODE (op) == FLOAT
              && SCALAR_FLOAT_MODE_P (GET_MODE (op))
              && ((unsigned)significand_size (GET_MODE (op))
-                 >= (GET_MODE_BITSIZE (GET_MODE (XEXP (op, 0)))
+                 >= (GET_MODE_PRECISION (GET_MODE (XEXP (op, 0)))
                      - num_sign_bit_copies (XEXP (op, 0),
                                             GET_MODE (XEXP (op, 0)))))))
        return simplify_gen_unary (GET_CODE (op), mode,
@@ -909,16 +963,12 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
 
       /* If operand is something known to be positive, ignore the ABS.  */
       if (GET_CODE (op) == FFS || GET_CODE (op) == ABS
-         || ((GET_MODE_BITSIZE (GET_MODE (op))
-              <= HOST_BITS_PER_WIDE_INT)
-             && ((nonzero_bits (op, GET_MODE (op))
-                  & ((HOST_WIDE_INT) 1
-                     << (GET_MODE_BITSIZE (GET_MODE (op)) - 1)))
-                 == 0)))
+         || val_signbit_known_clear_p (GET_MODE (op),
+                                       nonzero_bits (op, GET_MODE (op))))
        return op;
 
       /* If operand is known to be only -1 or 0, convert ABS to NEG.  */
-      if (num_sign_bit_copies (op, mode) == GET_MODE_BITSIZE (mode))
+      if (num_sign_bit_copies (op, mode) == GET_MODE_PRECISION (mode))
        return gen_rtx_NEG (mode, op);
 
       break;
@@ -1001,6 +1051,48 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          && GET_CODE (XEXP (XEXP (op, 0), 1)) == LABEL_REF)
        return XEXP (op, 0);
 
+      /* Extending a widening multiplication should be canonicalized to
+        a wider widening multiplication.  */
+      if (GET_CODE (op) == MULT)
+       {
+         rtx lhs = XEXP (op, 0);
+         rtx rhs = XEXP (op, 1);
+         enum rtx_code lcode = GET_CODE (lhs);
+         enum rtx_code rcode = GET_CODE (rhs);
+
+         /* Widening multiplies usually extend both operands, but sometimes
+            they use a shift to extract a portion of a register.  */
+         if ((lcode == SIGN_EXTEND
+              || (lcode == ASHIFTRT && CONST_INT_P (XEXP (lhs, 1))))
+             && (rcode == SIGN_EXTEND
+                 || (rcode == ASHIFTRT && CONST_INT_P (XEXP (rhs, 1)))))
+           {
+             enum machine_mode lmode = GET_MODE (lhs);
+             enum machine_mode rmode = GET_MODE (rhs);
+             int bits;
+
+             if (lcode == ASHIFTRT)
+               /* Number of bits not shifted off the end.  */
+               bits = GET_MODE_PRECISION (lmode) - INTVAL (XEXP (lhs, 1));
+             else /* lcode == SIGN_EXTEND */
+               /* Size of inner mode.  */
+               bits = GET_MODE_PRECISION (GET_MODE (XEXP (lhs, 0)));
+
+             if (rcode == ASHIFTRT)
+               bits += GET_MODE_PRECISION (rmode) - INTVAL (XEXP (rhs, 1));
+             else /* rcode == SIGN_EXTEND */
+               bits += GET_MODE_PRECISION (GET_MODE (XEXP (rhs, 0)));
+
+             /* We can only widen multiplies if the result is mathematiclly
+                equivalent.  I.e. if overflow was impossible.  */
+             if (bits <= GET_MODE_PRECISION (GET_MODE (op)))
+               return simplify_gen_binary
+                        (MULT, mode,
+                         simplify_gen_unary (SIGN_EXTEND, mode, lhs, lmode),
+                         simplify_gen_unary (SIGN_EXTEND, mode, rhs, rmode));
+           }
+       }
+
       /* Check for a sign extension of a subreg of a promoted
         variable, where the promotion is sign-extended, and the
         target mode is the same as the variable's promotion.  */
@@ -1010,6 +1102,42 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (XEXP (op, 0))))
        return rtl_hooks.gen_lowpart_no_emit (mode, op);
 
+      /* (sign_extend:M (sign_extend:N <X>)) is (sign_extend:M <X>).
+        (sign_extend:M (zero_extend:N <X>)) is (zero_extend:M <X>).  */
+      if (GET_CODE (op) == SIGN_EXTEND || GET_CODE (op) == ZERO_EXTEND)
+       {
+         gcc_assert (GET_MODE_BITSIZE (mode)
+                     > GET_MODE_BITSIZE (GET_MODE (op)));
+         return simplify_gen_unary (GET_CODE (op), mode, XEXP (op, 0),
+                                    GET_MODE (XEXP (op, 0)));
+       }
+
+      /* (sign_extend:M (ashiftrt:N (ashift <X> (const_int I)) (const_int I)))
+        is (sign_extend:M (subreg:O <X>)) if there is mode with
+        GET_MODE_BITSIZE (N) - I bits.
+        (sign_extend:M (lshiftrt:N (ashift <X> (const_int I)) (const_int I)))
+        is similarly (zero_extend:M (subreg:O <X>)).  */
+      if ((GET_CODE (op) == ASHIFTRT || GET_CODE (op) == LSHIFTRT)
+         && GET_CODE (XEXP (op, 0)) == ASHIFT
+         && CONST_INT_P (XEXP (op, 1))
+         && XEXP (XEXP (op, 0), 1) == XEXP (op, 1)
+         && GET_MODE_BITSIZE (GET_MODE (op)) > INTVAL (XEXP (op, 1)))
+       {
+         enum machine_mode tmode
+           = mode_for_size (GET_MODE_BITSIZE (GET_MODE (op))
+                            - INTVAL (XEXP (op, 1)), MODE_INT, 1);
+         gcc_assert (GET_MODE_BITSIZE (mode)
+                     > GET_MODE_BITSIZE (GET_MODE (op)));
+         if (tmode != BLKmode)
+           {
+             rtx inner =
+               rtl_hooks.gen_lowpart_no_emit (tmode, XEXP (XEXP (op, 0), 0));
+             return simplify_gen_unary (GET_CODE (op) == ASHIFTRT
+                                        ? SIGN_EXTEND : ZERO_EXTEND,
+                                        mode, inner, tmode);
+           }
+       }
+
 #if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend)
       /* As we do not know which address space the pointer is refering to,
         we can do this only if the target does not support different pointer
@@ -1036,6 +1164,73 @@ simplify_unary_operation_1 (enum rtx_code code, enum machine_mode mode, rtx op)
          && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (XEXP (op, 0))))
        return rtl_hooks.gen_lowpart_no_emit (mode, op);
 
+      /* Extending a widening multiplication should be canonicalized to
+        a wider widening multiplication.  */
+      if (GET_CODE (op) == MULT)
+       {
+         rtx lhs = XEXP (op, 0);
+         rtx rhs = XEXP (op, 1);
+         enum rtx_code lcode = GET_CODE (lhs);
+         enum rtx_code rcode = GET_CODE (rhs);
+
+         /* Widening multiplies usually extend both operands, but sometimes
+            they use a shift to extract a portion of a register.  */
+         if ((lcode == ZERO_EXTEND
+              || (lcode == LSHIFTRT && CONST_INT_P (XEXP (lhs, 1))))
+             && (rcode == ZERO_EXTEND
+                 || (rcode == LSHIFTRT && CONST_INT_P (XEXP (rhs, 1)))))
+           {
+             enum machine_mode lmode = GET_MODE (lhs);
+             enum machine_mode rmode = GET_MODE (rhs);
+             int bits;
+
+             if (lcode == LSHIFTRT)
+               /* Number of bits not shifted off the end.  */
+               bits = GET_MODE_PRECISION (lmode) - INTVAL (XEXP (lhs, 1));
+             else /* lcode == ZERO_EXTEND */
+               /* Size of inner mode.  */
+               bits = GET_MODE_PRECISION (GET_MODE (XEXP (lhs, 0)));
+
+             if (rcode == LSHIFTRT)
+               bits += GET_MODE_PRECISION (rmode) - INTVAL (XEXP (rhs, 1));
+             else /* rcode == ZERO_EXTEND */
+               bits += GET_MODE_PRECISION (GET_MODE (XEXP (rhs, 0)));
+
+             /* We can only widen multiplies if the result is mathematiclly
+                equivalent.  I.e. if overflow was impossible.  */
+             if (bits <= GET_MODE_PRECISION (GET_MODE (op)))
+               return simplify_gen_binary
+                        (MULT, mode,
+                         simplify_gen_unary (ZERO_EXTEND, mode, lhs, lmode),
+                         simplify_gen_unary (ZERO_EXTEND, mode, rhs, rmode));
+           }
+       }
+
+      /* (zero_extend:M (zero_extend:N <X>)) is (zero_extend:M <X>).  */
+      if (GET_CODE (op) == ZERO_EXTEND)
+       return simplify_gen_unary (ZERO_EXTEND, mode, XEXP (op, 0),
+                                  GET_MODE (XEXP (op, 0)));
+
+      /* (zero_extend:M (lshiftrt:N (ashift <X> (const_int I)) (const_int I)))
+        is (zero_extend:M (subreg:O <X>)) if there is mode with
+        GET_MODE_BITSIZE (N) - I bits.  */
+      if (GET_CODE (op) == LSHIFTRT
+         && GET_CODE (XEXP (op, 0)) == ASHIFT
+         && CONST_INT_P (XEXP (op, 1))
+         && XEXP (XEXP (op, 0), 1) == XEXP (op, 1)
+         && GET_MODE_BITSIZE (GET_MODE (op)) > INTVAL (XEXP (op, 1)))
+       {
+         enum machine_mode tmode
+           = mode_for_size (GET_MODE_BITSIZE (GET_MODE (op))
+                            - INTVAL (XEXP (op, 1)), MODE_INT, 1);
+         if (tmode != BLKmode)
+           {
+             rtx inner =
+               rtl_hooks.gen_lowpart_no_emit (tmode, XEXP (XEXP (op, 0), 0));
+             return simplify_gen_unary (ZERO_EXTEND, mode, inner, tmode);
+           }
+       }
+
 #if defined(POINTERS_EXTEND_UNSIGNED) && !defined(HAVE_ptr_extend)
       /* As we do not know which address space the pointer is refering to,
         we can do this only if the target does not support different pointer
@@ -1066,7 +1261,8 @@ rtx
 simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
                                rtx op, enum machine_mode op_mode)
 {
-  unsigned int width = GET_MODE_BITSIZE (mode);
+  unsigned int width = GET_MODE_PRECISION (mode);
+  unsigned int op_width = GET_MODE_PRECISION (op_mode);
 
   if (code == VEC_DUPLICATE)
     {
@@ -1166,7 +1362,7 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
          if (hv < 0)
            return 0;
        }
-      else if (GET_MODE_BITSIZE (op_mode) >= HOST_BITS_PER_WIDE_INT * 2)
+      else if (GET_MODE_PRECISION (op_mode) >= HOST_BITS_PER_WIDE_INT * 2)
        ;
       else
        hv = 0, lv &= GET_MODE_MASK (op_mode);
@@ -1206,7 +1402,17 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
          if (arg0 == 0 && CLZ_DEFINED_VALUE_AT_ZERO (mode, val))
            ;
          else
-           val = GET_MODE_BITSIZE (mode) - floor_log2 (arg0) - 1;
+           val = GET_MODE_PRECISION (mode) - floor_log2 (arg0) - 1;
+         break;
+
+       case CLRSB:
+         arg0 &= GET_MODE_MASK (mode);
+         if (arg0 == 0)
+           val = GET_MODE_PRECISION (mode) - 1;
+         else if (arg0 >= 0)
+           val = GET_MODE_PRECISION (mode) - floor_log2 (arg0) - 2;
+         else if (arg0 < 0)
+           val = GET_MODE_PRECISION (mode) - floor_log2 (~arg0) - 2;
          break;
 
        case CTZ:
@@ -1216,7 +1422,7 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
              /* Even if the value at zero is undefined, we have to come
                 up with some replacement.  Seems good enough.  */
              if (! CTZ_DEFINED_VALUE_AT_ZERO (mode, val))
-               val = GET_MODE_BITSIZE (mode);
+               val = GET_MODE_PRECISION (mode);
            }
          else
            val = ctz_hwi (arg0);
@@ -1260,16 +1466,16 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
          /* When zero-extending a CONST_INT, we need to know its
              original mode.  */
          gcc_assert (op_mode != VOIDmode);
-         if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT)
+         if (op_width == HOST_BITS_PER_WIDE_INT)
            {
              /* If we were really extending the mode,
                 we would have to distinguish between zero-extension
                 and sign-extension.  */
-             gcc_assert (width == GET_MODE_BITSIZE (op_mode));
+             gcc_assert (width == op_width);
              val = arg0;
            }
          else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT)
-           val = arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode));
+           val = arg0 & GET_MODE_MASK (op_mode);
          else
            return 0;
          break;
@@ -1277,21 +1483,20 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
        case SIGN_EXTEND:
          if (op_mode == VOIDmode)
            op_mode = mode;
-         if (GET_MODE_BITSIZE (op_mode) == HOST_BITS_PER_WIDE_INT)
+         op_width = GET_MODE_PRECISION (op_mode);
+         if (op_width == HOST_BITS_PER_WIDE_INT)
            {
              /* If we were really extending the mode,
                 we would have to distinguish between zero-extension
                 and sign-extension.  */
-             gcc_assert (width == GET_MODE_BITSIZE (op_mode));
+             gcc_assert (width == op_width);
              val = arg0;
            }
-         else if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT)
+         else if (op_width < HOST_BITS_PER_WIDE_INT)
            {
-             val
-               = arg0 & ~((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (op_mode));
-             if (val
-                 & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (op_mode) - 1)))
-               val -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode);
+             val = arg0 & GET_MODE_MASK (op_mode);
+             if (val_signbit_known_set_p (op_mode, val))
+               val |= ~GET_MODE_MASK (op_mode);
            }
          else
            return 0;
@@ -1360,12 +1565,12 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
        case CLZ:
          hv = 0;
          if (h1 != 0)
-           lv = GET_MODE_BITSIZE (mode) - floor_log2 (h1) - 1
+           lv = GET_MODE_PRECISION (mode) - floor_log2 (h1) - 1
              - HOST_BITS_PER_WIDE_INT;
          else if (l1 != 0)
-           lv = GET_MODE_BITSIZE (mode) - floor_log2 (l1) - 1;
+           lv = GET_MODE_PRECISION (mode) - floor_log2 (l1) - 1;
          else if (! CLZ_DEFINED_VALUE_AT_ZERO (mode, lv))
-           lv = GET_MODE_BITSIZE (mode);
+           lv = GET_MODE_PRECISION (mode);
          break;
 
        case CTZ:
@@ -1375,7 +1580,7 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
          else if (h1 != 0)
            lv = HOST_BITS_PER_WIDE_INT + ctz_hwi (h1);
          else if (! CTZ_DEFINED_VALUE_AT_ZERO (mode, lv))
-           lv = GET_MODE_BITSIZE (mode);
+           lv = GET_MODE_PRECISION (mode);
          break;
 
        case POPCOUNT:
@@ -1429,7 +1634,7 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
        case ZERO_EXTEND:
          gcc_assert (op_mode != VOIDmode);
 
-         if (GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT)
+         if (op_width > HOST_BITS_PER_WIDE_INT)
            return 0;
 
          hv = 0;
@@ -1438,15 +1643,13 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
 
        case SIGN_EXTEND:
          if (op_mode == VOIDmode
-             || GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT)
+             || op_width > HOST_BITS_PER_WIDE_INT)
            return 0;
          else
            {
              lv = l1 & GET_MODE_MASK (op_mode);
-             if (GET_MODE_BITSIZE (op_mode) < HOST_BITS_PER_WIDE_INT
-                 && (lv & ((HOST_WIDE_INT) 1
-                           << (GET_MODE_BITSIZE (op_mode) - 1))) != 0)
-               lv -= (HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (op_mode);
+             if (val_signbit_known_set_p (op_mode, lv))
+               lv |= ~GET_MODE_MASK (op_mode);
 
              hv = HWI_SIGN_EXTEND (lv);
            }
@@ -1463,7 +1666,8 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
     }
 
   else if (GET_CODE (op) == CONST_DOUBLE
-          && SCALAR_FLOAT_MODE_P (mode))
+          && SCALAR_FLOAT_MODE_P (mode)
+          && SCALAR_FLOAT_MODE_P (GET_MODE (op)))
     {
       REAL_VALUE_TYPE d, t;
       REAL_VALUE_FROM_CONST_DOUBLE (d, op);
@@ -1486,7 +1690,10 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
          d = real_value_truncate (mode, d);
          break;
        case FLOAT_EXTEND:
-         /* All this does is change the mode.  */
+         /* All this does is change the mode, unless changing
+            mode class.  */
+         if (GET_MODE_CLASS (mode) != GET_MODE_CLASS (GET_MODE (op)))
+           real_convert (&d, mode, &d);
          break;
        case FIX:
          real_arithmetic (&d, FIX_TRUNC_EXPR, &d, NULL);
@@ -1552,13 +1759,14 @@ simplify_const_unary_operation (enum rtx_code code, enum machine_mode mode,
          /* Test against the signed lower bound.  */
          if (width > HOST_BITS_PER_WIDE_INT)
            {
-             th = (HOST_WIDE_INT) -1 << (width - HOST_BITS_PER_WIDE_INT - 1);
+             th = (unsigned HOST_WIDE_INT) (-1)
+                  << (width - HOST_BITS_PER_WIDE_INT - 1);
              tl = 0;
            }
          else
            {
              th = -1;
-             tl = (HOST_WIDE_INT) -1 << (width - 1);
+             tl = (unsigned HOST_WIDE_INT) (-1) << (width - 1);
            }
          real_from_integer (&t, VOIDmode, tl, th, 0);
          if (REAL_VALUES_LESS (x, t))
@@ -1712,7 +1920,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
 {
   rtx tem, reversed, opleft, opright;
   HOST_WIDE_INT val;
-  unsigned int width = GET_MODE_BITSIZE (mode);
+  unsigned int width = GET_MODE_PRECISION (mode);
 
   /* Even if we can't compute a constant result,
      there are some cases worth simplifying.  */
@@ -1824,7 +2032,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
              coeff = immed_double_int_const (val, mode);
 
              tem = simplify_gen_binary (MULT, mode, lhs, coeff);
-             return rtx_cost (tem, SET, speed) <= rtx_cost (orig, SET, speed)
+             return set_src_cost (tem, speed) <= set_src_cost (orig, speed)
                ? tem : 0;
            }
        }
@@ -2005,7 +2213,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
              coeff = immed_double_int_const (val, mode);
 
              tem = simplify_gen_binary (MULT, mode, lhs, coeff);
-             return rtx_cost (tem, SET, speed) <= rtx_cost (orig, SET, speed)
+             return set_src_cost (tem, speed) <= set_src_cost (orig, speed)
                ? tem : 0;
            }
        }
@@ -2107,12 +2315,34 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
       if (GET_CODE (op0) == NEG)
        {
          rtx temp = simplify_unary_operation (NEG, mode, op1, mode);
+         /* If op1 is a MULT as well and simplify_unary_operation
+            just moved the NEG to the second operand, simplify_gen_binary
+            below could through simplify_associative_operation move
+            the NEG around again and recurse endlessly.  */
+         if (temp
+             && GET_CODE (op1) == MULT
+             && GET_CODE (temp) == MULT
+             && XEXP (op1, 0) == XEXP (temp, 0)
+             && GET_CODE (XEXP (temp, 1)) == NEG
+             && XEXP (op1, 1) == XEXP (XEXP (temp, 1), 0))
+           temp = NULL_RTX;
          if (temp)
            return simplify_gen_binary (MULT, mode, XEXP (op0, 0), temp);
        }
       if (GET_CODE (op1) == NEG)
        {
          rtx temp = simplify_unary_operation (NEG, mode, op0, mode);
+         /* If op0 is a MULT as well and simplify_unary_operation
+            just moved the NEG to the second operand, simplify_gen_binary
+            below could through simplify_associative_operation move
+            the NEG around again and recurse endlessly.  */
+         if (temp
+             && GET_CODE (op0) == MULT
+             && GET_CODE (temp) == MULT
+             && XEXP (op0, 0) == XEXP (temp, 0)
+             && GET_CODE (XEXP (temp, 1)) == NEG
+             && XEXP (op0, 1) == XEXP (XEXP (temp, 1), 0))
+           temp = NULL_RTX;
          if (temp)
            return simplify_gen_binary (MULT, mode, temp, XEXP (op1, 0));
        }
@@ -2136,7 +2366,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
       /* Convert multiply by constant power of two into shift unless
         we are still generating RTL.  This test is a kludge.  */
       if (CONST_INT_P (trueop1)
-         && (val = exact_log2 (INTVAL (trueop1))) >= 0
+         && (val = exact_log2 (UINTVAL (trueop1))) >= 0
          /* If the mode is larger than the host word size, and the
             uppermost bit is set, then this isn't a power of two due
             to implicit sign extension.  */
@@ -2199,11 +2429,9 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
       break;
 
     case IOR:
-      if (trueop1 == const0_rtx)
+      if (trueop1 == CONST0_RTX (mode))
        return op0;
-      if (CONST_INT_P (trueop1)
-         && ((INTVAL (trueop1) & GET_MODE_MASK (mode))
-             == GET_MODE_MASK (mode)))
+      if (INTEGRAL_MODE_P (mode) && trueop1 == CONSTM1_RTX (mode))
        return op1;
       if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
        return op0;
@@ -2216,8 +2444,8 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
 
       /* (ior A C) is C if all bits of A that might be nonzero are on in C.  */
       if (CONST_INT_P (op1)
-         && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
-         && (nonzero_bits (op0, mode) & ~INTVAL (op1)) == 0)
+         && HWI_COMPUTABLE_MODE_P (mode)
+         && (nonzero_bits (op0, mode) & ~UINTVAL (op1)) == 0)
        return op1;
 
       /* Canonicalize (X & C1) | C2.  */
@@ -2275,7 +2503,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
           && CONST_INT_P (XEXP (opleft, 1))
           && CONST_INT_P (XEXP (opright, 1))
           && (INTVAL (XEXP (opleft, 1)) + INTVAL (XEXP (opright, 1))
-              == GET_MODE_BITSIZE (mode)))
+              == GET_MODE_PRECISION (mode)))
         return gen_rtx_ROTATE (mode, XEXP (opright, 0), XEXP (opleft, 1));
 
       /* Same, but for ashift that has been "simplified" to a wider mode
@@ -2294,24 +2522,24 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
           && CONST_INT_P (XEXP (SUBREG_REG (opleft), 1))
           && CONST_INT_P (XEXP (opright, 1))
           && (INTVAL (XEXP (SUBREG_REG (opleft), 1)) + INTVAL (XEXP (opright, 1))
-              == GET_MODE_BITSIZE (mode)))
+              == GET_MODE_PRECISION (mode)))
         return gen_rtx_ROTATE (mode, XEXP (opright, 0),
                                XEXP (SUBREG_REG (opleft), 1));
 
       /* If we have (ior (and (X C1) C2)), simplify this by making
         C1 as small as possible if C1 actually changes.  */
       if (CONST_INT_P (op1)
-         && (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+         && (HWI_COMPUTABLE_MODE_P (mode)
              || INTVAL (op1) > 0)
          && GET_CODE (op0) == AND
          && CONST_INT_P (XEXP (op0, 1))
          && CONST_INT_P (op1)
-         && (INTVAL (XEXP (op0, 1)) & INTVAL (op1)) != 0)
+         && (UINTVAL (XEXP (op0, 1)) & UINTVAL (op1)) != 0)
        return simplify_gen_binary (IOR, mode,
                                    simplify_gen_binary
                                          (AND, mode, XEXP (op0, 0),
-                                          GEN_INT (INTVAL (XEXP (op0, 1))
-                                                   & ~INTVAL (op1))),
+                                          GEN_INT (UINTVAL (XEXP (op0, 1))
+                                                   & ~UINTVAL (op1))),
                                    op1);
 
       /* If OP0 is (ashiftrt (plus ...) C), it might actually be
@@ -2341,11 +2569,9 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
       break;
 
     case XOR:
-      if (trueop1 == const0_rtx)
+      if (trueop1 == CONST0_RTX (mode))
        return op0;
-      if (CONST_INT_P (trueop1)
-         && ((INTVAL (trueop1) & GET_MODE_MASK (mode))
-             == GET_MODE_MASK (mode)))
+      if (INTEGRAL_MODE_P (mode) && trueop1 == CONSTM1_RTX (mode))
        return simplify_gen_unary (NOT, mode, op0, mode);
       if (rtx_equal_p (trueop0, trueop1)
          && ! side_effects_p (op0)
@@ -2372,7 +2598,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
         convert them into an IOR.  This helps to detect rotation encoded
         using those methods and possibly other simplifications.  */
 
-      if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+      if (HWI_COMPUTABLE_MODE_P (mode)
          && (nonzero_bits (op0, mode)
              & nonzero_bits (op1, mode)) == 0)
        return (simplify_gen_binary (IOR, mode, op0, op1));
@@ -2416,6 +2642,46 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
                                                        XEXP (op0, 1), mode),
                                    op1);
 
+      /* Given (xor (and A B) C), using P^Q == (~P&Q) | (~Q&P),
+        we can transform like this:
+            (A&B)^C == ~(A&B)&C | ~C&(A&B)
+                    == (~A|~B)&C | ~C&(A&B)    * DeMorgan's Law
+                    == ~A&C | ~B&C | A&(~C&B)  * Distribute and re-order
+        Attempt a few simplifications when B and C are both constants.  */
+      if (GET_CODE (op0) == AND
+         && CONST_INT_P (op1)
+         && CONST_INT_P (XEXP (op0, 1)))
+       {
+         rtx a = XEXP (op0, 0);
+         rtx b = XEXP (op0, 1);
+         rtx c = op1;
+         HOST_WIDE_INT bval = INTVAL (b);
+         HOST_WIDE_INT cval = INTVAL (c);
+
+         rtx na_c
+           = simplify_binary_operation (AND, mode,
+                                        simplify_gen_unary (NOT, mode, a, mode),
+                                        c);
+         if ((~cval & bval) == 0)
+           {
+             /* Try to simplify ~A&C | ~B&C.  */
+             if (na_c != NULL_RTX)
+               return simplify_gen_binary (IOR, mode, na_c,
+                                           GEN_INT (~bval & cval));
+           }
+         else
+           {
+             /* If ~A&C is zero, simplify A&(~C&B) | ~B&C.  */
+             if (na_c == const0_rtx)
+               {
+                 rtx a_nc_b = simplify_gen_binary (AND, mode, a,
+                                                   GEN_INT (~cval & bval));
+                 return simplify_gen_binary (IOR, mode, a_nc_b,
+                                             GEN_INT (~bval & cval));
+               }
+           }
+       }
+
       /* (xor (comparison foo bar) (const_int 1)) can become the reversed
         comparison if STORE_FLAG_VALUE is 1.  */
       if (STORE_FLAG_VALUE == 1
@@ -2432,14 +2698,12 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          && trueop1 == const1_rtx
          && GET_CODE (op0) == LSHIFTRT
          && CONST_INT_P (XEXP (op0, 1))
-         && INTVAL (XEXP (op0, 1)) == GET_MODE_BITSIZE (mode) - 1)
+         && INTVAL (XEXP (op0, 1)) == GET_MODE_PRECISION (mode) - 1)
        return gen_rtx_GE (mode, XEXP (op0, 0), const0_rtx);
 
       /* (xor (comparison foo bar) (const_int sign-bit))
         when STORE_FLAG_VALUE is the sign bit.  */
-      if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
-         && ((STORE_FLAG_VALUE & GET_MODE_MASK (mode))
-             == (unsigned HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))
+      if (val_signbit_p (mode, STORE_FLAG_VALUE)
          && trueop1 == const_true_rtx
          && COMPARISON_P (op0)
          && (reversed = reversed_comparison (op0, mode)))
@@ -2453,7 +2717,9 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
     case AND:
       if (trueop1 == CONST0_RTX (mode) && ! side_effects_p (op0))
        return trueop1;
-      if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+      if (INTEGRAL_MODE_P (mode) && trueop1 == CONSTM1_RTX (mode))
+       return op0;
+      if (HWI_COMPUTABLE_MODE_P (mode))
        {
          HOST_WIDE_INT nzop0 = nonzero_bits (trueop0, mode);
          HOST_WIDE_INT nzop1;
@@ -2486,9 +2752,9 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
       if ((GET_CODE (op0) == SIGN_EXTEND
           || GET_CODE (op0) == ZERO_EXTEND)
          && CONST_INT_P (trueop1)
-         && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+         && HWI_COMPUTABLE_MODE_P (mode)
          && (~GET_MODE_MASK (GET_MODE (XEXP (op0, 0)))
-             & INTVAL (trueop1)) == 0)
+             & UINTVAL (trueop1)) == 0)
        {
          enum machine_mode imode = GET_MODE (XEXP (op0, 0));
          tem = simplify_gen_binary (AND, imode, XEXP (op0, 0),
@@ -2568,9 +2834,9 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          Also, if (N & M) == 0, then
         (A +- N) & M -> A & M.  */
       if (CONST_INT_P (trueop1)
-         && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
-         && ~INTVAL (trueop1)
-         && (INTVAL (trueop1) & (INTVAL (trueop1) + 1)) == 0
+         && HWI_COMPUTABLE_MODE_P (mode)
+         && ~UINTVAL (trueop1)
+         && (UINTVAL (trueop1) & (UINTVAL (trueop1) + 1)) == 0
          && (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS))
        {
          rtx pmop[2];
@@ -2580,7 +2846,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          pmop[1] = XEXP (op0, 1);
 
          if (CONST_INT_P (pmop[1])
-             && (INTVAL (pmop[1]) & INTVAL (trueop1)) == 0)
+             && (UINTVAL (pmop[1]) & UINTVAL (trueop1)) == 0)
            return simplify_gen_binary (AND, mode, pmop[0], op1);
 
          for (which = 0; which < 2; which++)
@@ -2590,14 +2856,14 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
                {
                case AND:
                  if (CONST_INT_P (XEXP (tem, 1))
-                     && (INTVAL (XEXP (tem, 1)) & INTVAL (trueop1))
-                     == INTVAL (trueop1))
+                     && (UINTVAL (XEXP (tem, 1)) & UINTVAL (trueop1))
+                     == UINTVAL (trueop1))
                    pmop[which] = XEXP (tem, 0);
                  break;
                case IOR:
                case XOR:
                  if (CONST_INT_P (XEXP (tem, 1))
-                     && (INTVAL (XEXP (tem, 1)) & INTVAL (trueop1)) == 0)
+                     && (UINTVAL (XEXP (tem, 1)) & UINTVAL (trueop1)) == 0)
                    pmop[which] = XEXP (tem, 0);
                  break;
                default:
@@ -2643,7 +2909,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
        return rtl_hooks.gen_lowpart_no_emit (mode, op0);
       /* Convert divide by power of two into shift.  */
       if (CONST_INT_P (trueop1)
-         && (val = exact_log2 (INTVAL (trueop1))) > 0)
+         && (val = exact_log2 (UINTVAL (trueop1))) > 0)
        return simplify_gen_binary (LSHIFTRT, mode, op0, GEN_INT (val));
       break;
 
@@ -2687,10 +2953,11 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
                }
            }
        }
-      else
+      else if (SCALAR_INT_MODE_P (mode))
        {
          /* 0/x is 0 (or x&0 if x has side-effects).  */
-         if (trueop0 == CONST0_RTX (mode))
+         if (trueop0 == CONST0_RTX (mode)
+             && !cfun->can_throw_non_call_exceptions)
            {
              if (side_effects_p (op1))
                return simplify_gen_binary (AND, mode, op1, trueop0);
@@ -2725,7 +2992,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
        }
       /* Implement modulus by power of two as AND.  */
       if (CONST_INT_P (trueop1)
-         && exact_log2 (INTVAL (trueop1)) > 0)
+         && exact_log2 (UINTVAL (trueop1)) > 0)
        return simplify_gen_binary (AND, mode, op0,
                                    GEN_INT (INTVAL (op1) - 1));
       break;
@@ -2756,7 +3023,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
        return op0;
       /* Rotating ~0 always results in ~0.  */
       if (CONST_INT_P (trueop0) && width <= HOST_BITS_PER_WIDE_INT
-         && (unsigned HOST_WIDE_INT) INTVAL (trueop0) == GET_MODE_MASK (mode)
+         && UINTVAL (trueop0) == GET_MODE_MASK (mode)
          && ! side_effects_p (op1))
        return op0;
     canonicalize_shift:
@@ -2792,7 +3059,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
          unsigned HOST_WIDE_INT zero_val = 0;
 
          if (CLZ_DEFINED_VALUE_AT_ZERO (imode, zero_val)
-             && zero_val == GET_MODE_BITSIZE (imode)
+             && zero_val == GET_MODE_PRECISION (imode)
              && INTVAL (trueop1) == exact_log2 (zero_val))
            return simplify_gen_relational (EQ, mode, imode,
                                            XEXP (op0, 0), const0_rtx);
@@ -2801,8 +3068,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
 
     case SMIN:
       if (width <= HOST_BITS_PER_WIDE_INT
-         && CONST_INT_P (trueop1)
-         && INTVAL (trueop1) == (HOST_WIDE_INT) 1 << (width -1)
+         && mode_signbit_p (mode, trueop1)
          && ! side_effects_p (op0))
        return op1;
       if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
@@ -2815,8 +3081,7 @@ simplify_binary_operation_1 (enum rtx_code code, enum machine_mode mode,
     case SMAX:
       if (width <= HOST_BITS_PER_WIDE_INT
          && CONST_INT_P (trueop1)
-         && ((unsigned HOST_WIDE_INT) INTVAL (trueop1)
-             == (unsigned HOST_WIDE_INT) GET_MODE_MASK (mode) >> 1)
+         && (UINTVAL (trueop1) == GET_MODE_MASK (mode) >> 1)
          && ! side_effects_p (op0))
        return op1;
       if (rtx_equal_p (trueop0, trueop1) && ! side_effects_p (op0))
@@ -3084,7 +3349,7 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
 {
   HOST_WIDE_INT arg0, arg1, arg0s, arg1s;
   HOST_WIDE_INT val;
-  unsigned int width = GET_MODE_BITSIZE (mode);
+  unsigned int width = GET_MODE_PRECISION (mode);
 
   if (VECTOR_MODE_P (mode)
       && code != VEC_CONCAT
@@ -3369,24 +3634,24 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
            unsigned HOST_WIDE_INT cnt;
 
            if (SHIFT_COUNT_TRUNCATED)
-             o1 = double_int_zext (o1, GET_MODE_BITSIZE (mode));
+             o1 = double_int_zext (o1, GET_MODE_PRECISION (mode));
 
            if (!double_int_fits_in_uhwi_p (o1)
-               || double_int_to_uhwi (o1) >= GET_MODE_BITSIZE (mode))
+               || double_int_to_uhwi (o1) >= GET_MODE_PRECISION (mode))
              return 0;
 
            cnt = double_int_to_uhwi (o1);
 
            if (code == LSHIFTRT || code == ASHIFTRT)
-             res = double_int_rshift (o0, cnt, GET_MODE_BITSIZE (mode),
+             res = double_int_rshift (o0, cnt, GET_MODE_PRECISION (mode),
                                       code == ASHIFTRT);
            else if (code == ASHIFT)
-             res = double_int_lshift (o0, cnt, GET_MODE_BITSIZE (mode),
+             res = double_int_lshift (o0, cnt, GET_MODE_PRECISION (mode),
                                       true);
            else if (code == ROTATE)
-             res = double_int_lrotate (o0, cnt, GET_MODE_BITSIZE (mode));
+             res = double_int_lrotate (o0, cnt, GET_MODE_PRECISION (mode));
            else /* code == ROTATERT */
-             res = double_int_rrotate (o0, cnt, GET_MODE_BITSIZE (mode));
+             res = double_int_rrotate (o0, cnt, GET_MODE_PRECISION (mode));
          }
          break;
 
@@ -3408,16 +3673,16 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
 
       if (width < HOST_BITS_PER_WIDE_INT)
         {
-          arg0 &= ((HOST_WIDE_INT) 1 << width) - 1;
-          arg1 &= ((HOST_WIDE_INT) 1 << width) - 1;
+          arg0 &= GET_MODE_MASK (mode);
+          arg1 &= GET_MODE_MASK (mode);
 
           arg0s = arg0;
-          if (arg0s & ((HOST_WIDE_INT) 1 << (width - 1)))
-           arg0s |= ((HOST_WIDE_INT) (-1) << width);
+         if (val_signbit_known_set_p (mode, arg0s))
+           arg0s |= ~GET_MODE_MASK (mode);
 
-         arg1s = arg1;
-         if (arg1s & ((HOST_WIDE_INT) 1 << (width - 1)))
-           arg1s |= ((HOST_WIDE_INT) (-1) << width);
+          arg1s = arg1;
+         if (val_signbit_known_set_p (mode, arg1s))
+           arg1s |= ~GET_MODE_MASK (mode);
        }
       else
        {
@@ -3443,7 +3708,8 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
 
        case DIV:
          if (arg1s == 0
-             || (arg0s == (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)
+             || ((unsigned HOST_WIDE_INT) arg0s
+                 == (unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)
                  && arg1s == -1))
            return 0;
          val = arg0s / arg1s;
@@ -3451,7 +3717,8 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
 
        case MOD:
          if (arg1s == 0
-             || (arg0s == (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)
+             || ((unsigned HOST_WIDE_INT) arg0s
+                 == (unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)
                  && arg1s == -1))
            return 0;
          val = arg0s % arg1s;
@@ -3459,7 +3726,8 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
 
        case UDIV:
          if (arg1 == 0
-             || (arg0s == (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)
+             || ((unsigned HOST_WIDE_INT) arg0s
+                 == (unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)
                  && arg1s == -1))
            return 0;
          val = (unsigned HOST_WIDE_INT) arg0 / arg1;
@@ -3467,7 +3735,8 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
 
        case UMOD:
          if (arg1 == 0
-             || (arg0s == (HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)
+             || ((unsigned HOST_WIDE_INT) arg0s
+                 == (unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1)
                  && arg1s == -1))
            return 0;
          val = (unsigned HOST_WIDE_INT) arg0 % arg1;
@@ -3506,7 +3775,7 @@ simplify_const_binary_operation (enum rtx_code code, enum machine_mode mode,
 
          /* Sign-extend the result for arithmetic right shifts.  */
          if (code == ASHIFTRT && arg0s < 0 && arg1 > 0)
-           val |= ((HOST_WIDE_INT) -1) << (width - arg1);
+           val |= ((unsigned HOST_WIDE_INT) (-1)) << (width - arg1);
          break;
 
        case ROTATERT:
@@ -4083,10 +4352,20 @@ simplify_relational_operation_1 (enum rtx_code code, enum machine_mode mode,
     {
       rtx x = XEXP (op0, 0);
       rtx c = XEXP (op0, 1);
+      enum rtx_code invcode = op0code == PLUS ? MINUS : PLUS;
+      rtx tem = simplify_gen_binary (invcode, cmp_mode, op1, c);
+
+      /* Detect an infinite recursive condition, where we oscillate at this
+        simplification case between:
+           A + B == C  <--->  C - B == A,
+        where A, B, and C are all constants with non-simplifiable expressions,
+        usually SYMBOL_REFs.  */
+      if (GET_CODE (tem) == invcode
+         && CONSTANT_P (x)
+         && rtx_equal_p (c, XEXP (tem, 1)))
+       return NULL_RTX;
 
-      c = simplify_gen_binary (op0code == PLUS ? MINUS : PLUS,
-                              cmp_mode, op1, c);
-      return simplify_gen_relational (code, mode, cmp_mode, x, c);
+      return simplify_gen_relational (code, mode, cmp_mode, x, tem);
     }
 
   /* (ne:SI (zero_extract:SI FOO (const_int 1) BAR) (const_int 0))) is
@@ -4355,7 +4634,7 @@ simplify_const_relational_operation (enum rtx_code code,
        && (GET_CODE (trueop1) == CONST_DOUBLE
           || CONST_INT_P (trueop1)))
     {
-      int width = GET_MODE_BITSIZE (mode);
+      int width = GET_MODE_PRECISION (mode);
       HOST_WIDE_INT l0s, h0s, l1s, h1s;
       unsigned HOST_WIDE_INT l0u, h0u, l1u, h1u;
 
@@ -4386,14 +4665,14 @@ simplify_const_relational_operation (enum rtx_code code,
         we have to sign or zero-extend the values.  */
       if (width != 0 && width < HOST_BITS_PER_WIDE_INT)
        {
-         l0u &= ((HOST_WIDE_INT) 1 << width) - 1;
-         l1u &= ((HOST_WIDE_INT) 1 << width) - 1;
+         l0u &= GET_MODE_MASK (mode);
+         l1u &= GET_MODE_MASK (mode);
 
-         if (l0s & ((HOST_WIDE_INT) 1 << (width - 1)))
-           l0s |= ((HOST_WIDE_INT) (-1) << width);
+         if (val_signbit_known_set_p (mode, l0s))
+           l0s |= ~GET_MODE_MASK (mode);
 
-         if (l1s & ((HOST_WIDE_INT) 1 << (width - 1)))
-           l1s |= ((HOST_WIDE_INT) (-1) << width);
+         if (val_signbit_known_set_p (mode, l1s))
+           l1s |= ~GET_MODE_MASK (mode);
        }
       if (width != 0 && width <= HOST_BITS_PER_WIDE_INT)
        h0u = h1u = 0, h0s = HWI_SIGN_EXTEND (l0s), h1s = HWI_SIGN_EXTEND (l1s);
@@ -4410,8 +4689,7 @@ simplify_const_relational_operation (enum rtx_code code,
     }
 
   /* Optimize comparisons with upper and lower bounds.  */
-  if (SCALAR_INT_MODE_P (mode)
-      && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+  if (HWI_COMPUTABLE_MODE_P (mode)
       && CONST_INT_P (trueop1))
     {
       int sign;
@@ -4544,10 +4822,11 @@ simplify_const_relational_operation (enum rtx_code code,
          rtx inner_const = avoid_constant_pool_reference (XEXP (op0, 1));
          if (CONST_INT_P (inner_const) && inner_const != const0_rtx)
            {
-             int sign_bitnum = GET_MODE_BITSIZE (mode) - 1;
+             int sign_bitnum = GET_MODE_PRECISION (mode) - 1;
              int has_sign = (HOST_BITS_PER_WIDE_INT >= sign_bitnum
-                             && (INTVAL (inner_const)
-                                 & ((HOST_WIDE_INT) 1 << sign_bitnum)));
+                             && (UINTVAL (inner_const)
+                                 & ((unsigned HOST_WIDE_INT) 1
+                                    << sign_bitnum)));
 
              switch (code)
                {
@@ -4635,7 +4914,9 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
                            enum machine_mode op0_mode, rtx op0, rtx op1,
                            rtx op2)
 {
-  unsigned int width = GET_MODE_BITSIZE (mode);
+  unsigned int width = GET_MODE_PRECISION (mode);
+  bool any_change = false;
+  rtx tem;
 
   /* VOIDmode means "infinite" precision.  */
   if (width == 0)
@@ -4643,6 +4924,31 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
 
   switch (code)
     {
+    case FMA:
+      /* Simplify negations around the multiplication.  */
+      /* -a * -b + c  =>  a * b + c.  */
+      if (GET_CODE (op0) == NEG)
+       {
+         tem = simplify_unary_operation (NEG, mode, op1, mode);
+         if (tem)
+           op1 = tem, op0 = XEXP (op0, 0), any_change = true;
+       }
+      else if (GET_CODE (op1) == NEG)
+       {
+         tem = simplify_unary_operation (NEG, mode, op0, mode);
+         if (tem)
+           op0 = tem, op1 = XEXP (op1, 0), any_change = true;
+       }
+
+      /* Canonicalize the two multiplication operands.  */
+      /* a * -b + c  =>  -b * a + c.  */
+      if (swap_commutative_operands_p (op0, op1))
+       tem = op0, op0 = op1, op1 = tem, any_change = true;
+
+      if (any_change)
+       return gen_rtx_FMA (mode, op0, op1, op2);
+      return NULL_RTX;
+
     case SIGN_EXTRACT:
     case ZERO_EXTRACT:
       if (CONST_INT_P (op0)
@@ -4652,33 +4958,25 @@ simplify_ternary_operation (enum rtx_code code, enum machine_mode mode,
          && width <= (unsigned) HOST_BITS_PER_WIDE_INT)
        {
          /* Extracting a bit-field from a constant */
-         HOST_WIDE_INT val = INTVAL (op0);
-
+         unsigned HOST_WIDE_INT val = UINTVAL (op0);
+         HOST_WIDE_INT op1val = INTVAL (op1);
+         HOST_WIDE_INT op2val = INTVAL (op2);
          if (BITS_BIG_ENDIAN)
-           val >>= (GET_MODE_BITSIZE (op0_mode)
-                    - INTVAL (op2) - INTVAL (op1));
+           val >>= GET_MODE_PRECISION (op0_mode) - op2val - op1val;
          else
-           val >>= INTVAL (op2);
+           val >>= op2val;
 
-         if (HOST_BITS_PER_WIDE_INT != INTVAL (op1))
+         if (HOST_BITS_PER_WIDE_INT != op1val)
            {
              /* First zero-extend.  */
-             val &= ((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1;
+             val &= ((unsigned HOST_WIDE_INT) 1 << op1val) - 1;
              /* If desired, propagate sign bit.  */
              if (code == SIGN_EXTRACT
-                 && (val & ((HOST_WIDE_INT) 1 << (INTVAL (op1) - 1))))
-               val |= ~ (((HOST_WIDE_INT) 1 << INTVAL (op1)) - 1);
+                 && (val & ((unsigned HOST_WIDE_INT) 1 << (op1val - 1)))
+                    != 0)
+               val |= ~ (((unsigned HOST_WIDE_INT) 1 << op1val) - 1);
            }
 
-         /* Clear the bits that don't belong in our mode,
-            unless they and our sign bit are all one.
-            So we get either a reasonable negative value or a reasonable
-            unsigned value for this mode.  */
-         if (width < HOST_BITS_PER_WIDE_INT
-             && ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
-                 != ((HOST_WIDE_INT) (-1) << (width - 1))))
-           val &= ((HOST_WIDE_INT) 1 << width) - 1;
-
          return gen_int_mode (val, mode);
        }
       break;
@@ -5035,10 +5333,10 @@ simplify_immed_subreg (enum machine_mode outermode, rtx op,
            for (i = 0;
                 i < HOST_BITS_PER_WIDE_INT && i < elem_bitsize;
                 i += value_bit)
-             lo |= (HOST_WIDE_INT)(*vp++ & value_mask) << i;
+             lo |= (unsigned HOST_WIDE_INT)(*vp++ & value_mask) << i;
            for (; i < elem_bitsize; i += value_bit)
-             hi |= ((HOST_WIDE_INT)(*vp++ & value_mask)
-                    << (i - HOST_BITS_PER_WIDE_INT));
+             hi |= (unsigned HOST_WIDE_INT)(*vp++ & value_mask)
+                    << (i - HOST_BITS_PER_WIDE_INT);
 
            /* immed_double_const doesn't call trunc_int_for_mode.  I don't
               know why.  */
@@ -5091,9 +5389,9 @@ simplify_immed_subreg (enum machine_mode outermode, rtx op,
            for (i = 0;
                 i < HOST_BITS_PER_WIDE_INT && i < elem_bitsize;
                 i += value_bit)
-             f.data.low |= (HOST_WIDE_INT)(*vp++ & value_mask) << i;
+             f.data.low |= (unsigned HOST_WIDE_INT)(*vp++ & value_mask) << i;
            for (; i < elem_bitsize; i += value_bit)
-             f.data.high |= ((HOST_WIDE_INT)(*vp++ & value_mask)
+             f.data.high |= ((unsigned HOST_WIDE_INT)(*vp++ & value_mask)
                             << (i - HOST_BITS_PER_WIDE_INT));
 
            elems[elem] = CONST_FIXED_FROM_FIXED_VALUE (f, outer_submode);
@@ -5321,7 +5619,8 @@ simplify_subreg (enum machine_mode outermode, rtx op,
   /* Optimize SUBREG truncations of zero and sign extended values.  */
   if ((GET_CODE (op) == ZERO_EXTEND
        || GET_CODE (op) == SIGN_EXTEND)
-      && GET_MODE_BITSIZE (outermode) < GET_MODE_BITSIZE (innermode))
+      && SCALAR_INT_MODE_P (innermode)
+      && GET_MODE_PRECISION (outermode) < GET_MODE_PRECISION (innermode))
     {
       unsigned int bitpos = subreg_lsb_1 (outermode, innermode, byte);
 
@@ -5337,7 +5636,7 @@ simplify_subreg (enum machine_mode outermode, rtx op,
          enum machine_mode origmode = GET_MODE (XEXP (op, 0));
          if (outermode == origmode)
            return XEXP (op, 0);
-         if (GET_MODE_BITSIZE (outermode) <= GET_MODE_BITSIZE (origmode))
+         if (GET_MODE_PRECISION (outermode) <= GET_MODE_PRECISION (origmode))
            return simplify_gen_subreg (outermode, XEXP (op, 0), origmode,
                                        subreg_lowpart_offset (outermode,
                                                               origmode));
@@ -5349,7 +5648,7 @@ simplify_subreg (enum machine_mode outermode, rtx op,
       /* A SUBREG resulting from a zero extension may fold to zero if
         it extracts higher bits that the ZERO_EXTEND's source bits.  */
       if (GET_CODE (op) == ZERO_EXTEND
-         && bitpos >= GET_MODE_BITSIZE (GET_MODE (XEXP (op, 0))))
+         && bitpos >= GET_MODE_PRECISION (GET_MODE (XEXP (op, 0))))
        return CONST0_RTX (outermode);
     }
 
@@ -5359,15 +5658,16 @@ simplify_subreg (enum machine_mode outermode, rtx op,
   if ((GET_CODE (op) == LSHIFTRT
        || GET_CODE (op) == ASHIFTRT)
       && SCALAR_INT_MODE_P (outermode)
+      && SCALAR_INT_MODE_P (innermode)
       /* Ensure that OUTERMODE is at least twice as wide as the INNERMODE
         to avoid the possibility that an outer LSHIFTRT shifts by more
         than the sign extension's sign_bit_copies and introduces zeros
         into the high bits of the result.  */
-      && (2 * GET_MODE_BITSIZE (outermode)) <= GET_MODE_BITSIZE (innermode)
+      && (2 * GET_MODE_PRECISION (outermode)) <= GET_MODE_PRECISION (innermode)
       && CONST_INT_P (XEXP (op, 1))
       && GET_CODE (XEXP (op, 0)) == SIGN_EXTEND
       && GET_MODE (XEXP (XEXP (op, 0), 0)) == outermode
-      && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (outermode)
+      && INTVAL (XEXP (op, 1)) < GET_MODE_PRECISION (outermode)
       && subreg_lsb_1 (outermode, innermode, byte) == 0)
     return simplify_gen_binary (ASHIFTRT, outermode,
                                XEXP (XEXP (op, 0), 0), XEXP (op, 1));
@@ -5378,11 +5678,12 @@ simplify_subreg (enum machine_mode outermode, rtx op,
   if ((GET_CODE (op) == LSHIFTRT
        || GET_CODE (op) == ASHIFTRT)
       && SCALAR_INT_MODE_P (outermode)
-      && GET_MODE_BITSIZE (outermode) < GET_MODE_BITSIZE (innermode)
+      && SCALAR_INT_MODE_P (innermode)
+      && GET_MODE_PRECISION (outermode) < GET_MODE_PRECISION (innermode)
       && CONST_INT_P (XEXP (op, 1))
       && GET_CODE (XEXP (op, 0)) == ZERO_EXTEND
       && GET_MODE (XEXP (XEXP (op, 0), 0)) == outermode
-      && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (outermode)
+      && INTVAL (XEXP (op, 1)) < GET_MODE_PRECISION (outermode)
       && subreg_lsb_1 (outermode, innermode, byte) == 0)
     return simplify_gen_binary (LSHIFTRT, outermode,
                                XEXP (XEXP (op, 0), 0), XEXP (op, 1));
@@ -5392,12 +5693,13 @@ simplify_subreg (enum machine_mode outermode, rtx op,
      the outer subreg is effectively a truncation to the original mode.  */
   if (GET_CODE (op) == ASHIFT
       && SCALAR_INT_MODE_P (outermode)
-      && GET_MODE_BITSIZE (outermode) < GET_MODE_BITSIZE (innermode)
+      && SCALAR_INT_MODE_P (innermode)
+      && GET_MODE_PRECISION (outermode) < GET_MODE_PRECISION (innermode)
       && CONST_INT_P (XEXP (op, 1))
       && (GET_CODE (XEXP (op, 0)) == ZERO_EXTEND
          || GET_CODE (XEXP (op, 0)) == SIGN_EXTEND)
       && GET_MODE (XEXP (XEXP (op, 0), 0)) == outermode
-      && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (outermode)
+      && INTVAL (XEXP (op, 1)) < GET_MODE_PRECISION (outermode)
       && subreg_lsb_1 (outermode, innermode, byte) == 0)
     return simplify_gen_binary (ASHIFT, outermode,
                                XEXP (XEXP (op, 0), 0), XEXP (op, 1));
@@ -5405,13 +5707,13 @@ simplify_subreg (enum machine_mode outermode, rtx op,
   /* Recognize a word extraction from a multi-word subreg.  */
   if ((GET_CODE (op) == LSHIFTRT
        || GET_CODE (op) == ASHIFTRT)
-      && SCALAR_INT_MODE_P (outermode)
-      && GET_MODE_BITSIZE (outermode) >= BITS_PER_WORD
-      && GET_MODE_BITSIZE (innermode) >= (2 * GET_MODE_BITSIZE (outermode))
+      && SCALAR_INT_MODE_P (innermode)
+      && GET_MODE_PRECISION (outermode) >= BITS_PER_WORD
+      && GET_MODE_PRECISION (innermode) >= (2 * GET_MODE_PRECISION (outermode))
       && CONST_INT_P (XEXP (op, 1))
-      && (INTVAL (XEXP (op, 1)) & (GET_MODE_BITSIZE (outermode) - 1)) == 0
+      && (INTVAL (XEXP (op, 1)) & (GET_MODE_PRECISION (outermode) - 1)) == 0
       && INTVAL (XEXP (op, 1)) >= 0
-      && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (innermode)
+      && INTVAL (XEXP (op, 1)) < GET_MODE_PRECISION (innermode)
       && byte == subreg_lowpart_offset (outermode, innermode))
     {
       int shifted_bytes = INTVAL (XEXP (op, 1)) / BITS_PER_UNIT;
@@ -5421,6 +5723,32 @@ simplify_subreg (enum machine_mode outermode, rtx op,
                                   : byte + shifted_bytes));
     }
 
+  /* If we have a lowpart SUBREG of a right shift of MEM, make a new MEM
+     and try replacing the SUBREG and shift with it.  Don't do this if
+     the MEM has a mode-dependent address or if we would be widening it.  */
+
+  if ((GET_CODE (op) == LSHIFTRT
+       || GET_CODE (op) == ASHIFTRT)
+      && SCALAR_INT_MODE_P (innermode)
+      && MEM_P (XEXP (op, 0))
+      && CONST_INT_P (XEXP (op, 1))
+      && GET_MODE_SIZE (outermode) < GET_MODE_SIZE (GET_MODE (op))
+      && (INTVAL (XEXP (op, 1)) % GET_MODE_BITSIZE (outermode)) == 0
+      && INTVAL (XEXP (op, 1)) > 0
+      && INTVAL (XEXP (op, 1)) < GET_MODE_BITSIZE (innermode)
+      && ! mode_dependent_address_p (XEXP (XEXP (op, 0), 0))
+      && ! MEM_VOLATILE_P (XEXP (op, 0))
+      && byte == subreg_lowpart_offset (outermode, innermode)
+      && (GET_MODE_SIZE (outermode) >= UNITS_PER_WORD
+         || WORDS_BIG_ENDIAN == BYTES_BIG_ENDIAN))
+    {
+      int shifted_bytes = INTVAL (XEXP (op, 1)) / BITS_PER_UNIT;
+      return adjust_address_nv (XEXP (op, 0), outermode,
+                               (WORDS_BIG_ENDIAN
+                                ? byte - shifted_bytes
+                                : byte + shifted_bytes));
+    }
+
   return NULL_RTX;
 }