Use vec<> in build_vector
[gcc.git] / gcc / combine.c
index f2a5388de0e8cce136903d0319d53bce53bde908..1832c3cb2bca0c1caa115a9761263090efccf538 100644 (file)
@@ -370,7 +370,7 @@ static int label_tick_ebb_start;
 /* Mode used to compute significance in reg_stat[].nonzero_bits.  It is the
    largest integer mode that can fit in HOST_BITS_PER_WIDE_INT.  */
 
-static machine_mode nonzero_bits_mode;
+static scalar_int_mode nonzero_bits_mode;
 
 /* Nonzero when reg_stat[].nonzero_bits and reg_stat[].sign_bit_copies can
    be safely used.  It is zero while computing them and after combine has
@@ -414,13 +414,12 @@ static struct undobuf undobuf;
 
 static int n_occurrences;
 
-static rtx reg_nonzero_bits_for_combine (const_rtx, machine_mode, const_rtx,
-                                        machine_mode,
-                                        unsigned HOST_WIDE_INT,
+static rtx reg_nonzero_bits_for_combine (const_rtx, scalar_int_mode,
+                                        scalar_int_mode,
                                         unsigned HOST_WIDE_INT *);
-static rtx reg_num_sign_bit_copies_for_combine (const_rtx, machine_mode, const_rtx,
-                                               machine_mode,
-                                               unsigned int, unsigned int *);
+static rtx reg_num_sign_bit_copies_for_combine (const_rtx, scalar_int_mode,
+                                               scalar_int_mode,
+                                               unsigned int *);
 static void do_SUBST (rtx *, rtx);
 static void do_SUBST_INT (int *, int);
 static void init_reg_last (void);
@@ -445,10 +444,11 @@ static rtx expand_compound_operation (rtx);
 static const_rtx expand_field_assignment (const_rtx);
 static rtx make_extraction (machine_mode, rtx, HOST_WIDE_INT,
                            rtx, unsigned HOST_WIDE_INT, int, int, int);
-static rtx extract_left_shift (rtx, int);
 static int get_pos_from_mask (unsigned HOST_WIDE_INT,
                              unsigned HOST_WIDE_INT *);
 static rtx canon_reg_for_combine (rtx, rtx);
+static rtx force_int_to_mode (rtx, scalar_int_mode, scalar_int_mode,
+                             scalar_int_mode, unsigned HOST_WIDE_INT, int);
 static rtx force_to_mode (rtx, machine_mode,
                          unsigned HOST_WIDE_INT, int);
 static rtx if_then_else_cond (rtx, rtx *, rtx *);
@@ -457,9 +457,9 @@ static int rtx_equal_for_field_assignment_p (rtx, rtx, bool = false);
 static rtx make_field_assignment (rtx);
 static rtx apply_distributive_law (rtx);
 static rtx distribute_and_simplify_rtx (rtx, int);
-static rtx simplify_and_const_int_1 (machine_mode, rtx,
+static rtx simplify_and_const_int_1 (scalar_int_mode, rtx,
                                     unsigned HOST_WIDE_INT);
-static rtx simplify_and_const_int (rtx, machine_mode, rtx,
+static rtx simplify_and_const_int (rtx, scalar_int_mode, rtx,
                                   unsigned HOST_WIDE_INT);
 static int merge_outer_ops (enum rtx_code *, HOST_WIDE_INT *, enum rtx_code,
                            HOST_WIDE_INT, machine_mode, int *);
@@ -1157,7 +1157,7 @@ combine_instructions (rtx_insn *f, unsigned int nregs)
   uid_insn_cost = XCNEWVEC (int, max_uid_known + 1);
   gcc_obstack_init (&insn_link_obstack);
 
-  nonzero_bits_mode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0);
+  nonzero_bits_mode = int_mode_for_size (HOST_BITS_PER_WIDE_INT, 0).require ();
 
   /* Don't use reg_stat[].nonzero_bits when computing it.  This can cause
      problems when, for example, we have j <<= 1 in a loop.  */
@@ -2011,7 +2011,7 @@ can_combine_p (rtx_insn *insn, rtx_insn *i3, rtx_insn *pred ATTRIBUTE_UNUSED,
 
       if (REG_P (src)
          && ((REGNO (dest) < FIRST_PSEUDO_REGISTER
-              && ! HARD_REGNO_MODE_OK (REGNO (dest), GET_MODE (dest)))
+              && !targetm.hard_regno_mode_ok (REGNO (dest), GET_MODE (dest)))
              /* Don't extend the life of a hard register unless it is
                 user variable (if we have few registers) or it can't
                 fit into the desired register (meaning something special
@@ -2020,7 +2020,8 @@ can_combine_p (rtx_insn *insn, rtx_insn *i3, rtx_insn *pred ATTRIBUTE_UNUSED,
                 reload can't handle a conflict with constraints of other
                 inputs.  */
              || (REGNO (src) < FIRST_PSEUDO_REGISTER
-                 && ! HARD_REGNO_MODE_OK (REGNO (src), GET_MODE (src)))))
+                 && !targetm.hard_regno_mode_ok (REGNO (src),
+                                                 GET_MODE (src)))))
        return 0;
     }
   else if (GET_CODE (dest) != CC0)
@@ -2210,8 +2211,8 @@ combinable_i3pat (rtx_insn *i3, rtx *loc, rtx i2dest, rtx i1dest, rtx i0dest,
 
          || (REG_P (inner_dest)
              && REGNO (inner_dest) < FIRST_PSEUDO_REGISTER
-             && (! HARD_REGNO_MODE_OK (REGNO (inner_dest),
-                                       GET_MODE (inner_dest))))
+             && !targetm.hard_regno_mode_ok (REGNO (inner_dest),
+                                             GET_MODE (inner_dest)))
          || (i1_not_in_src && reg_overlap_mentioned_p (i1dest, src))
          || (i0_not_in_src && reg_overlap_mentioned_p (i0dest, src)))
        return 0;
@@ -2224,9 +2225,7 @@ combinable_i3pat (rtx_insn *i3, rtx *loc, rtx i2dest, rtx i1dest, rtx i0dest,
         STACK_POINTER_REGNUM, since these are always considered to be
         live.  Similarly for ARG_POINTER_REGNUM if it is fixed.  */
       subdest = dest;
-      if (GET_CODE (subdest) == SUBREG
-         && (GET_MODE_SIZE (GET_MODE (subdest))
-             >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (subdest)))))
+      if (GET_CODE (subdest) == SUBREG && !partial_subreg_p (subdest))
        subdest = SUBREG_REG (subdest);
       if (pi3dest_killed
          && REG_P (subdest)
@@ -2456,8 +2455,8 @@ can_change_dest_mode (rtx x, int added_sets, machine_mode mode)
   /* Allow hard registers if the new mode is legal, and occupies no more
      registers than the old mode.  */
   if (regno < FIRST_PSEUDO_REGISTER)
-    return (HARD_REGNO_MODE_OK (regno, mode)
-           && REG_NREGS (x) >= hard_regno_nregs[regno][mode]);
+    return (targetm.hard_regno_mode_ok (regno, mode)
+           && REG_NREGS (x) >= hard_regno_nregs (regno, mode));
 
   /* Or a pseudo that is only used once.  */
   return (regno < reg_n_sets_max
@@ -2645,6 +2644,7 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0,
   rtx other_pat = 0;
   rtx new_other_notes;
   int i;
+  scalar_int_mode dest_mode, temp_mode;
 
   /* Immediately return if any of I0,I1,I2 are the same insn (I3 can
      never be).  */
@@ -2847,33 +2847,40 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0,
      constant.  */
   if (i1 == 0
       && (temp_expr = single_set (i2)) != 0
+      && is_a <scalar_int_mode> (GET_MODE (SET_DEST (temp_expr)), &temp_mode)
       && CONST_SCALAR_INT_P (SET_SRC (temp_expr))
       && GET_CODE (PATTERN (i3)) == SET
       && CONST_SCALAR_INT_P (SET_SRC (PATTERN (i3)))
       && reg_subword_p (SET_DEST (PATTERN (i3)), SET_DEST (temp_expr)))
     {
       rtx dest = SET_DEST (PATTERN (i3));
+      rtx temp_dest = SET_DEST (temp_expr);
       int offset = -1;
       int width = 0;
-      
+
       if (GET_CODE (dest) == ZERO_EXTRACT)
        {
          if (CONST_INT_P (XEXP (dest, 1))
-             && CONST_INT_P (XEXP (dest, 2)))
+             && CONST_INT_P (XEXP (dest, 2))
+             && is_a <scalar_int_mode> (GET_MODE (XEXP (dest, 0)),
+                                        &dest_mode))
            {
              width = INTVAL (XEXP (dest, 1));
              offset = INTVAL (XEXP (dest, 2));
              dest = XEXP (dest, 0);
              if (BITS_BIG_ENDIAN)
-               offset = GET_MODE_PRECISION (GET_MODE (dest)) - width - offset;
+               offset = GET_MODE_PRECISION (dest_mode) - width - offset;
            }
        }
       else
        {
          if (GET_CODE (dest) == STRICT_LOW_PART)
            dest = XEXP (dest, 0);
-         width = GET_MODE_PRECISION (GET_MODE (dest));
-         offset = 0;
+         if (is_a <scalar_int_mode> (GET_MODE (dest), &dest_mode))
+           {
+             width = GET_MODE_PRECISION (dest_mode);
+             offset = 0;
+           }
        }
 
       if (offset >= 0)
@@ -2882,9 +2889,9 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0,
          if (subreg_lowpart_p (dest))
            ;
          /* Handle the case where inner is twice the size of outer.  */
-         else if (GET_MODE_PRECISION (GET_MODE (SET_DEST (temp_expr)))
-                  == 2 * GET_MODE_PRECISION (GET_MODE (dest)))
-           offset += GET_MODE_PRECISION (GET_MODE (dest));
+         else if (GET_MODE_PRECISION (temp_mode)
+                  == 2 * GET_MODE_PRECISION (dest_mode))
+           offset += GET_MODE_PRECISION (dest_mode);
          /* Otherwise give up for now.  */
          else
            offset = -1;
@@ -2895,23 +2902,22 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0,
          rtx inner = SET_SRC (PATTERN (i3));
          rtx outer = SET_SRC (temp_expr);
 
-         wide_int o
-           = wi::insert (rtx_mode_t (outer, GET_MODE (SET_DEST (temp_expr))),
-                         rtx_mode_t (inner, GET_MODE (dest)),
-                         offset, width);
+         wide_int o = wi::insert (rtx_mode_t (outer, temp_mode),
+                                  rtx_mode_t (inner, dest_mode),
+                                  offset, width);
 
          combine_merges++;
          subst_insn = i3;
          subst_low_luid = DF_INSN_LUID (i2);
          added_sets_2 = added_sets_1 = added_sets_0 = 0;
-         i2dest = SET_DEST (temp_expr);
+         i2dest = temp_dest;
          i2dest_killed = dead_or_set_p (i2, i2dest);
 
          /* Replace the source in I2 with the new constant and make the
             resulting insn the new pattern for I3.  Then skip to where we
             validate the pattern.  Everything was set up above.  */
          SUBST (SET_SRC (temp_expr),
-                immed_wide_int_const (o, GET_MODE (SET_DEST (temp_expr))));
+                immed_wide_int_const (o, temp_mode));
 
          newpat = PATTERN (i2);
 
@@ -3483,7 +3489,10 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0,
      i3, and one from i2.  Combining then splitting the parallel results
      in the original i2 again plus an invalid insn (which we delete).
      The net effect is only to move instructions around, which makes
-     debug info less accurate.  */
+     debug info less accurate.
+
+     If the remaining SET came from I2 its destination should not be used
+     between I2 and I3.  See PR82024.  */
 
   if (!(added_sets_2 && i1 == 0)
       && is_parallel_of_n_reg_sets (newpat, 2)
@@ -3512,11 +3521,17 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0,
               && insn_nothrow_p (i3)
               && !side_effects_p (SET_SRC (set0)))
        {
-         newpat = set1;
-         insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
+         rtx dest = SET_DEST (set1);
+         if (GET_CODE (dest) == SUBREG)
+           dest = SUBREG_REG (dest);
+         if (!reg_used_between_p (dest, i2, i3))
+           {
+             newpat = set1;
+             insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
 
-         if (insn_code_number >= 0)
-           changed_i3_dest = 1;
+             if (insn_code_number >= 0)
+               changed_i3_dest = 1;
+           }
        }
 
       if (insn_code_number < 0)
@@ -5432,11 +5447,11 @@ subst (rtx x, rtx from, rtx to, int in_dest, int in_cond, int unique_copy)
                     FROM to CC0.  */
 
                  if (GET_CODE (to) == SUBREG
-                     && ! MODES_TIEABLE_P (GET_MODE (to),
-                                           GET_MODE (SUBREG_REG (to)))
+                     && !targetm.modes_tieable_p (GET_MODE (to),
+                                                  GET_MODE (SUBREG_REG (to)))
                      && ! (code == SUBREG
-                           && MODES_TIEABLE_P (GET_MODE (x),
-                                               GET_MODE (SUBREG_REG (to))))
+                           && (targetm.modes_tieable_p
+                               (GET_MODE (x), GET_MODE (SUBREG_REG (to)))))
                      && (!HAVE_cc0
                          || (! (code == SET
                                 && i == 1
@@ -6487,7 +6502,7 @@ simplify_if_then_else (rtx x)
       rtx cond_op0 = XEXP (cond, 0);
       rtx cond_op1 = XEXP (cond, 1);
       enum rtx_code op = UNKNOWN, extend_op = UNKNOWN;
-      machine_mode m = int_mode;
+      scalar_int_mode m = int_mode;
       rtx z = 0, c1 = NULL_RTX;
 
       if ((GET_CODE (t) == PLUS || GET_CODE (t) == MINUS
@@ -6875,10 +6890,8 @@ simplify_set (rtx x)
   /* If we have (set (cc0) (subreg ...)), we try to remove the subreg
      in SRC.  */
   if (dest == cc0_rtx
-      && GET_CODE (src) == SUBREG
-      && subreg_lowpart_p (src)
-      && (GET_MODE_PRECISION (GET_MODE (src))
-         < GET_MODE_PRECISION (GET_MODE (SUBREG_REG (src)))))
+      && partial_subreg_p (src)
+      && subreg_lowpart_p (src))
     {
       rtx inner = SUBREG_REG (src);
       machine_mode inner_mode = GET_MODE (inner);
@@ -7143,16 +7156,19 @@ expand_compound_operation (rtx x)
     default:
       return x;
     }
+
+  /* We've rejected non-scalar operations by now.  */
+  scalar_int_mode mode = as_a <scalar_int_mode> (GET_MODE (x));
+
   /* Convert sign extension to zero extension, if we know that the high
      bit is not set, as this is easier to optimize.  It will be converted
      back to cheaper alternative in make_extraction.  */
   if (GET_CODE (x) == SIGN_EXTEND
-      && HWI_COMPUTABLE_MODE_P (GET_MODE (x))
+      && HWI_COMPUTABLE_MODE_P (mode)
       && ((nonzero_bits (XEXP (x, 0), inner_mode)
           & ~(((unsigned HOST_WIDE_INT) GET_MODE_MASK (inner_mode)) >> 1))
          == 0))
     {
-      machine_mode mode = GET_MODE (x);
       rtx temp = gen_rtx_ZERO_EXTEND (mode, XEXP (x, 0));
       rtx temp2 = expand_compound_operation (temp);
 
@@ -7174,27 +7190,27 @@ expand_compound_operation (rtx x)
         know that the last value didn't have any inappropriate bits
         set.  */
       if (GET_CODE (XEXP (x, 0)) == TRUNCATE
-         && GET_MODE (XEXP (XEXP (x, 0), 0)) == GET_MODE (x)
-         && HWI_COMPUTABLE_MODE_P (GET_MODE (x))
-         && (nonzero_bits (XEXP (XEXP (x, 0), 0), GET_MODE (x))
+         && GET_MODE (XEXP (XEXP (x, 0), 0)) == mode
+         && HWI_COMPUTABLE_MODE_P (mode)
+         && (nonzero_bits (XEXP (XEXP (x, 0), 0), mode)
              & ~GET_MODE_MASK (inner_mode)) == 0)
        return XEXP (XEXP (x, 0), 0);
 
       /* Likewise for (zero_extend:DI (subreg:SI foo:DI 0)).  */
       if (GET_CODE (XEXP (x, 0)) == SUBREG
-         && GET_MODE (SUBREG_REG (XEXP (x, 0))) == GET_MODE (x)
+         && GET_MODE (SUBREG_REG (XEXP (x, 0))) == mode
          && subreg_lowpart_p (XEXP (x, 0))
-         && HWI_COMPUTABLE_MODE_P (GET_MODE (x))
-         && (nonzero_bits (SUBREG_REG (XEXP (x, 0)), GET_MODE (x))
+         && HWI_COMPUTABLE_MODE_P (mode)
+         && (nonzero_bits (SUBREG_REG (XEXP (x, 0)), mode)
              & ~GET_MODE_MASK (inner_mode)) == 0)
        return SUBREG_REG (XEXP (x, 0));
 
       /* (zero_extend:DI (truncate:SI foo:DI)) is just foo:DI when foo
         is a comparison and STORE_FLAG_VALUE permits.  This is like
-        the first case, but it works even when GET_MODE (x) is larger
+        the first case, but it works even when MODE is larger
         than HOST_WIDE_INT.  */
       if (GET_CODE (XEXP (x, 0)) == TRUNCATE
-         && GET_MODE (XEXP (XEXP (x, 0), 0)) == GET_MODE (x)
+         && GET_MODE (XEXP (XEXP (x, 0), 0)) == mode
          && COMPARISON_P (XEXP (XEXP (x, 0), 0))
          && GET_MODE_PRECISION (inner_mode) <= HOST_BITS_PER_WIDE_INT
          && (STORE_FLAG_VALUE & ~GET_MODE_MASK (inner_mode)) == 0)
@@ -7202,7 +7218,7 @@ expand_compound_operation (rtx x)
 
       /* Likewise for (zero_extend:DI (subreg:SI foo:DI 0)).  */
       if (GET_CODE (XEXP (x, 0)) == SUBREG
-         && GET_MODE (SUBREG_REG (XEXP (x, 0))) == GET_MODE (x)
+         && GET_MODE (SUBREG_REG (XEXP (x, 0))) == mode
          && subreg_lowpart_p (XEXP (x, 0))
          && COMPARISON_P (SUBREG_REG (XEXP (x, 0)))
          && GET_MODE_PRECISION (inner_mode) <= HOST_BITS_PER_WIDE_INT
@@ -7226,10 +7242,9 @@ expand_compound_operation (rtx x)
      extraction.  Then the constant of 31 would be substituted in
      to produce such a position.  */
 
-  modewidth = GET_MODE_PRECISION (GET_MODE (x));
+  modewidth = GET_MODE_PRECISION (mode);
   if (modewidth >= pos + len)
     {
-      machine_mode mode = GET_MODE (x);
       tem = gen_lowpart (mode, XEXP (x, 0));
       if (!tem || GET_CODE (tem) == CLOBBER)
        return x;
@@ -7239,10 +7254,10 @@ expand_compound_operation (rtx x)
                                  mode, tem, modewidth - len);
     }
   else if (unsignedp && len < HOST_BITS_PER_WIDE_INT)
-    tem = simplify_and_const_int (NULL_RTX, GET_MODE (x),
+    tem = simplify_and_const_int (NULL_RTX, mode,
                                  simplify_shift_const (NULL_RTX, LSHIFTRT,
-                                                       GET_MODE (x),
-                                                       XEXP (x, 0), pos),
+                                                       mode, XEXP (x, 0),
+                                                       pos),
                                  (HOST_WIDE_INT_1U << len) - 1);
   else
     /* Any other cases we can't handle.  */
@@ -7418,9 +7433,9 @@ make_extraction (machine_mode mode, rtx inner, HOST_WIDE_INT pos,
      ignore the POS lowest bits, etc.  */
   machine_mode is_mode = GET_MODE (inner);
   machine_mode inner_mode;
-  machine_mode wanted_inner_mode;
-  machine_mode wanted_inner_reg_mode = word_mode;
-  machine_mode pos_mode = word_mode;
+  scalar_int_mode wanted_inner_mode;
+  scalar_int_mode wanted_inner_reg_mode = word_mode;
+  scalar_int_mode pos_mode = word_mode;
   machine_mode extraction_mode = word_mode;
   rtx new_rtx = 0;
   rtx orig_pos_rtx = pos_rtx;
@@ -7617,7 +7632,7 @@ make_extraction (machine_mode mode, rtx inner, HOST_WIDE_INT pos,
   if (get_best_reg_extraction_insn (&insn, pattern,
                                    GET_MODE_BITSIZE (inner_mode), mode))
     {
-      wanted_inner_reg_mode = insn.struct_mode;
+      wanted_inner_reg_mode = insn.struct_mode.require ();
       pos_mode = insn.pos_mode;
       extraction_mode = insn.field_mode;
     }
@@ -7625,7 +7640,7 @@ make_extraction (machine_mode mode, rtx inner, HOST_WIDE_INT pos,
   /* Never narrow an object, since that might not be safe.  */
 
   if (mode != VOIDmode
-      && GET_MODE_SIZE (extraction_mode) < GET_MODE_SIZE (mode))
+      && partial_subreg_p (extraction_mode, mode))
     extraction_mode = mode;
 
   if (!MEM_P (inner))
@@ -7672,7 +7687,7 @@ make_extraction (machine_mode mode, rtx inner, HOST_WIDE_INT pos,
   if (wanted_inner_mode != VOIDmode
       && inner_mode != wanted_inner_mode
       && ! pos_rtx
-      && GET_MODE_SIZE (wanted_inner_mode) < GET_MODE_SIZE (is_mode)
+      && partial_subreg_p (wanted_inner_mode, is_mode)
       && MEM_P (inner)
       && ! mode_dependent_address_p (XEXP (inner, 0), MEM_ADDR_SPACE (inner))
       && ! MEM_VOLATILE_P (inner))
@@ -7732,9 +7747,13 @@ make_extraction (machine_mode mode, rtx inner, HOST_WIDE_INT pos,
     }
 
   /* Adjust mode of POS_RTX, if needed.  If we want a wider mode, we
-     have to zero extend.  Otherwise, we can just use a SUBREG.  */
+     have to zero extend.  Otherwise, we can just use a SUBREG.
+
+     We dealt with constant rtxes earlier, so pos_rtx cannot
+     have VOIDmode at this point.  */
   if (pos_rtx != 0
-      && GET_MODE_SIZE (pos_mode) > GET_MODE_SIZE (GET_MODE (pos_rtx)))
+      && (GET_MODE_SIZE (pos_mode)
+         > GET_MODE_SIZE (as_a <scalar_int_mode> (GET_MODE (pos_rtx)))))
     {
       rtx temp = simplify_gen_unary (ZERO_EXTEND, pos_mode, pos_rtx,
                                     GET_MODE (pos_rtx));
@@ -7781,14 +7800,14 @@ make_extraction (machine_mode mode, rtx inner, HOST_WIDE_INT pos,
   return new_rtx;
 }
 \f
-/* See if X contains an ASHIFT of COUNT or more bits that can be commuted
-   with any other operations in X.  Return X without that shift if so.  */
+/* See if X (of mode MODE) contains an ASHIFT of COUNT or more bits that
+   can be commuted with any other operations in X.  Return X without
+   that shift if so.  */
 
 static rtx
-extract_left_shift (rtx x, int count)
+extract_left_shift (scalar_int_mode mode, rtx x, int count)
 {
   enum rtx_code code = GET_CODE (x);
-  machine_mode mode = GET_MODE (x);
   rtx tem;
 
   switch (code)
@@ -7804,7 +7823,7 @@ extract_left_shift (rtx x, int count)
       break;
 
     case NEG:  case NOT:
-      if ((tem = extract_left_shift (XEXP (x, 0), count)) != 0)
+      if ((tem = extract_left_shift (mode, XEXP (x, 0), count)) != 0)
        return simplify_gen_unary (code, mode, tem, mode);
 
       break;
@@ -7815,7 +7834,7 @@ extract_left_shift (rtx x, int count)
       if (CONST_INT_P (XEXP (x, 1))
          && (UINTVAL (XEXP (x, 1))
              & (((HOST_WIDE_INT_1U << count)) - 1)) == 0
-         && (tem = extract_left_shift (XEXP (x, 0), count)) != 0)
+         && (tem = extract_left_shift (mode, XEXP (x, 0), count)) != 0)
        {
          HOST_WIDE_INT val = INTVAL (XEXP (x, 1)) >> count;
          return simplify_gen_binary (code, mode, tem,
@@ -7843,7 +7862,7 @@ extract_left_shift (rtx x, int count)
    - Return a new rtx, which the caller returns directly.  */
 
 static rtx
-make_compound_operation_int (machine_mode mode, rtx *x_ptr,
+make_compound_operation_int (scalar_int_mode mode, rtx *x_ptr,
                             enum rtx_code in_code,
                             enum rtx_code *next_code_ptr)
 {
@@ -7957,8 +7976,8 @@ make_compound_operation_int (machine_mode mode, rtx *x_ptr,
          && (i = exact_log2 (UINTVAL (XEXP (x, 1)) + 1)) >= 0)
        {
          new_rtx = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code);
-         new_rtx = make_extraction (mode, new_rtx, 0, XEXP (XEXP (x, 0), 1), i, 1,
-                                0, in_code == COMPARE);
+         new_rtx = make_extraction (mode, new_rtx, 0, XEXP (XEXP (x, 0), 1),
+                                    i, 1, 0, in_code == COMPARE);
        }
 
       /* Same as previous, but for (subreg (lshiftrt ...)) in first op.  */
@@ -7997,10 +8016,10 @@ make_compound_operation_int (machine_mode mode, rtx *x_ptr,
        {
          /* Apply the distributive law, and then try to make extractions.  */
          new_rtx = gen_rtx_fmt_ee (GET_CODE (XEXP (x, 0)), mode,
-                               gen_rtx_AND (mode, XEXP (XEXP (x, 0), 0),
-                                            XEXP (x, 1)),
-                               gen_rtx_AND (mode, XEXP (XEXP (x, 0), 1),
-                                            XEXP (x, 1)));
+                                   gen_rtx_AND (mode, XEXP (XEXP (x, 0), 0),
+                                                XEXP (x, 1)),
+                                   gen_rtx_AND (mode, XEXP (XEXP (x, 0), 1),
+                                                XEXP (x, 1)));
          new_rtx = make_compound_operation (new_rtx, in_code);
        }
 
@@ -8014,9 +8033,9 @@ make_compound_operation_int (machine_mode mode, rtx *x_ptr,
        {
          new_rtx = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code);
          new_rtx = make_extraction (mode, new_rtx,
-                                (GET_MODE_PRECISION (mode)
-                                 - INTVAL (XEXP (XEXP (x, 0), 1))),
-                                NULL_RTX, i, 1, 0, in_code == COMPARE);
+                                    (GET_MODE_PRECISION (mode)
+                                     - INTVAL (XEXP (XEXP (x, 0), 1))),
+                                    NULL_RTX, i, 1, 0, in_code == COMPARE);
        }
 
       /* On machines without logical shifts, if the operand of the AND is
@@ -8036,8 +8055,10 @@ make_compound_operation_int (machine_mode mode, rtx *x_ptr,
          if ((INTVAL (XEXP (x, 1)) & ~mask) == 0)
            SUBST (XEXP (x, 0),
                   gen_rtx_ASHIFTRT (mode,
-                                    make_compound_operation
-                                    (XEXP (XEXP (x, 0), 0), next_code),
+                                    make_compound_operation (XEXP (XEXP (x,
+                                                                         0),
+                                                                   0),
+                                                             next_code),
                                     XEXP (XEXP (x, 0), 1)));
        }
 
@@ -8047,9 +8068,9 @@ make_compound_operation_int (machine_mode mode, rtx *x_ptr,
         we are in a COMPARE.  */
       else if ((i = exact_log2 (UINTVAL (XEXP (x, 1)) + 1)) >= 0)
        new_rtx = make_extraction (mode,
-                              make_compound_operation (XEXP (x, 0),
-                                                       next_code),
-                              0, NULL_RTX, i, 1, 0, in_code == COMPARE);
+                                  make_compound_operation (XEXP (x, 0),
+                                                           next_code),
+                                  0, NULL_RTX, i, 1, 0, in_code == COMPARE);
 
       /* If we are in a comparison and this is an AND with a power of two,
         convert this into the appropriate bit extract.  */
@@ -8100,9 +8121,9 @@ make_compound_operation_int (machine_mode mode, rtx *x_ptr,
          && (nonzero_bits (XEXP (x, 0), mode) & (1 << (mode_width - 1))) == 0)
        {
          new_rtx = gen_rtx_ASHIFTRT (mode,
-                                 make_compound_operation (XEXP (x, 0),
-                                                          next_code),
-                                 XEXP (x, 1));
+                                     make_compound_operation (XEXP (x, 0),
+                                                              next_code),
+                                     XEXP (x, 1));
          break;
        }
 
@@ -8123,9 +8144,9 @@ make_compound_operation_int (machine_mode mode, rtx *x_ptr,
        {
          new_rtx = make_compound_operation (XEXP (lhs, 0), next_code);
          new_rtx = make_extraction (mode, new_rtx,
-                                INTVAL (rhs) - INTVAL (XEXP (lhs, 1)),
-                                NULL_RTX, mode_width - INTVAL (rhs),
-                                code == LSHIFTRT, 0, in_code == COMPARE);
+                                    INTVAL (rhs) - INTVAL (XEXP (lhs, 1)),
+                                    NULL_RTX, mode_width - INTVAL (rhs),
+                                    code == LSHIFTRT, 0, in_code == COMPARE);
          break;
        }
 
@@ -8141,10 +8162,11 @@ make_compound_operation_int (machine_mode mode, rtx *x_ptr,
          && INTVAL (rhs) >= 0
          && INTVAL (rhs) < HOST_BITS_PER_WIDE_INT
          && INTVAL (rhs) < mode_width
-         && (new_rtx = extract_left_shift (lhs, INTVAL (rhs))) != 0)
-       new_rtx = make_extraction (mode, make_compound_operation (new_rtx, next_code),
-                              0, NULL_RTX, mode_width - INTVAL (rhs),
-                              code == LSHIFTRT, 0, in_code == COMPARE);
+         && (new_rtx = extract_left_shift (mode, lhs, INTVAL (rhs))) != 0)
+       new_rtx = make_extraction (mode, make_compound_operation (new_rtx,
+                                                                 next_code),
+                                  0, NULL_RTX, mode_width - INTVAL (rhs),
+                                  code == LSHIFTRT, 0, in_code == COMPARE);
 
       break;
 
@@ -8188,7 +8210,7 @@ make_compound_operation_int (machine_mode mode, rtx *x_ptr,
                   to (subreg:QI (lshiftrt:SI (reg:SI) (const_int 7)) 0).  */
                || (GET_CODE (inner) == AND
                    && CONST_INT_P (XEXP (inner, 1))
-                   && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (inner))
+                   && partial_subreg_p (x)
                    && exact_log2 (UINTVAL (XEXP (inner, 1)))
                       >= GET_MODE_BITSIZE (mode) - 1)))
          subreg_code = SET;
@@ -8201,7 +8223,7 @@ make_compound_operation_int (machine_mode mode, rtx *x_ptr,
          tem = simplified;
 
        if (GET_CODE (tem) != GET_CODE (inner)
-           && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (inner))
+           && partial_subreg_p (x)
            && subreg_lowpart_p (x))
          {
            rtx newer
@@ -8212,8 +8234,9 @@ make_compound_operation_int (machine_mode mode, rtx *x_ptr,
            if (GET_CODE (newer) != SUBREG)
              newer = make_compound_operation (newer, in_code);
 
-           /* force_to_mode can expand compounds.  If it just re-expanded the
-              compound, use gen_lowpart to convert to the desired mode.  */
+           /* force_to_mode can expand compounds.  If it just re-expanded
+              the compound, use gen_lowpart to convert to the desired
+              mode.  */
            if (rtx_equal_p (newer, x)
                /* Likewise if it re-expanded the compound only partially.
                   This happens for SUBREG of ZERO_EXTRACT if they extract
@@ -8455,7 +8478,7 @@ static rtx
 gen_lowpart_or_truncate (machine_mode mode, rtx x)
 {
   if (!CONST_INT_P (x)
-      && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (x))
+      && partial_subreg_p (mode, GET_MODE (x))
       && !TRULY_NOOP_TRUNCATION_MODES_P (mode, GET_MODE (x))
       && !(REG_P (x) && reg_truncated_to_mode (mode, x)))
     {
@@ -8489,8 +8512,7 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
   enum rtx_code code = GET_CODE (x);
   int next_select = just_select || code == XOR || code == NOT || code == NEG;
   machine_mode op_mode;
-  unsigned HOST_WIDE_INT fuller_mask, nonzero;
-  rtx op0, op1, temp;
+  unsigned HOST_WIDE_INT nonzero;
 
   /* If this is a CALL or ASM_OPERANDS, don't do anything.  Some of the
      code below will do the wrong thing since the mode of such an
@@ -8511,22 +8533,13 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
   /* It is not valid to do a right-shift in a narrower mode
      than the one it came in with.  */
   if ((code == LSHIFTRT || code == ASHIFTRT)
-      && GET_MODE_PRECISION (mode) < GET_MODE_PRECISION (GET_MODE (x)))
+      && partial_subreg_p (mode, GET_MODE (x)))
     op_mode = GET_MODE (x);
 
   /* Truncate MASK to fit OP_MODE.  */
   if (op_mode)
     mask &= GET_MODE_MASK (op_mode);
 
-  /* When we have an arithmetic operation, or a shift whose count we
-     do not know, we need to assume that all bits up to the highest-order
-     bit in MASK will be needed.  This is how we form such a mask.  */
-  if (mask & (HOST_WIDE_INT_1U << (HOST_BITS_PER_WIDE_INT - 1)))
-    fuller_mask = HOST_WIDE_INT_M1U;
-  else
-    fuller_mask = ((HOST_WIDE_INT_1U << (floor_log2 (mask) + 1))
-                  - 1);
-
   /* Determine what bits of X are guaranteed to be (non)zero.  */
   nonzero = nonzero_bits (x, mode);
 
@@ -8557,16 +8570,48 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
      if the constant masks to zero all the bits the mode doesn't have.  */
   if (GET_CODE (x) == SUBREG
       && subreg_lowpart_p (x)
-      && ((GET_MODE_SIZE (GET_MODE (x))
-          < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+      && (partial_subreg_p (x)
          || (0 == (mask
                    & GET_MODE_MASK (GET_MODE (x))
                    & ~GET_MODE_MASK (GET_MODE (SUBREG_REG (x)))))))
     return force_to_mode (SUBREG_REG (x), mode, mask, next_select);
 
-  /* The arithmetic simplifications here only work for scalar integer modes.  */
-  if (!SCALAR_INT_MODE_P (mode) || !SCALAR_INT_MODE_P (GET_MODE (x)))
-    return gen_lowpart_or_truncate (mode, x);
+  scalar_int_mode int_mode, xmode;
+  if (is_a <scalar_int_mode> (mode, &int_mode)
+      && is_a <scalar_int_mode> (GET_MODE (x), &xmode))
+    /* OP_MODE is either MODE or XMODE, so it must be a scalar
+       integer too.  */
+    return force_int_to_mode (x, int_mode, xmode,
+                             as_a <scalar_int_mode> (op_mode),
+                             mask, just_select);
+
+  return gen_lowpart_or_truncate (mode, x);
+}
+
+/* Subroutine of force_to_mode that handles cases in which both X and
+   the result are scalar integers.  MODE is the mode of the result,
+   XMODE is the mode of X, and OP_MODE says which of MODE or XMODE
+   is preferred for simplified versions of X.  The other arguments
+   are as for force_to_mode.  */
+
+static rtx
+force_int_to_mode (rtx x, scalar_int_mode mode, scalar_int_mode xmode,
+                  scalar_int_mode op_mode, unsigned HOST_WIDE_INT mask,
+                  int just_select)
+{
+  enum rtx_code code = GET_CODE (x);
+  int next_select = just_select || code == XOR || code == NOT || code == NEG;
+  unsigned HOST_WIDE_INT fuller_mask;
+  rtx op0, op1, temp;
+
+  /* When we have an arithmetic operation, or a shift whose count we
+     do not know, we need to assume that all bits up to the highest-order
+     bit in MASK will be needed.  This is how we form such a mask.  */
+  if (mask & (HOST_WIDE_INT_1U << (HOST_BITS_PER_WIDE_INT - 1)))
+    fuller_mask = HOST_WIDE_INT_M1U;
+  else
+    fuller_mask = ((HOST_WIDE_INT_1U << (floor_log2 (mask) + 1))
+                  - 1);
 
   switch (code)
     {
@@ -8597,14 +8642,14 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
        {
          x = simplify_and_const_int (x, op_mode, XEXP (x, 0),
                                      mask & INTVAL (XEXP (x, 1)));
+         xmode = op_mode;
 
          /* If X is still an AND, see if it is an AND with a mask that
             is just some low-order bits.  If so, and it is MASK, we don't
             need it.  */
 
          if (GET_CODE (x) == AND && CONST_INT_P (XEXP (x, 1))
-             && ((INTVAL (XEXP (x, 1)) & GET_MODE_MASK (GET_MODE (x)))
-                 == mask))
+             && (INTVAL (XEXP (x, 1)) & GET_MODE_MASK (xmode)) == mask)
            x = XEXP (x, 0);
 
          /* If it remains an AND, try making another AND with the bits
@@ -8613,18 +8658,17 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
             cheaper constant.  */
 
          if (GET_CODE (x) == AND && CONST_INT_P (XEXP (x, 1))
-             && GET_MODE_MASK (GET_MODE (x)) != mask
-             && HWI_COMPUTABLE_MODE_P (GET_MODE (x)))
+             && GET_MODE_MASK (xmode) != mask
+             && HWI_COMPUTABLE_MODE_P (xmode))
            {
              unsigned HOST_WIDE_INT cval
-               = UINTVAL (XEXP (x, 1))
-                 | (GET_MODE_MASK (GET_MODE (x)) & ~mask);
+               = UINTVAL (XEXP (x, 1)) | (GET_MODE_MASK (xmode) & ~mask);
              rtx y;
 
-             y = simplify_gen_binary (AND, GET_MODE (x), XEXP (x, 0),
-                                      gen_int_mode (cval, GET_MODE (x)));
-             if (set_src_cost (y, GET_MODE (x), optimize_this_for_speed_p)
-                 < set_src_cost (x, GET_MODE (x), optimize_this_for_speed_p))
+             y = simplify_gen_binary (AND, xmode, XEXP (x, 0),
+                                      gen_int_mode (cval, xmode));
+             if (set_src_cost (y, xmode, optimize_this_for_speed_p)
+                 < set_src_cost (x, xmode, optimize_this_for_speed_p))
                x = y;
            }
 
@@ -8654,7 +8698,7 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
            && pow2p_hwi (- smask)
            && (nonzero_bits (XEXP (x, 0), mode) & ~smask) == 0
            && (INTVAL (XEXP (x, 1)) & ~smask) != 0)
-         return force_to_mode (plus_constant (GET_MODE (x), XEXP (x, 0),
+         return force_to_mode (plus_constant (xmode, XEXP (x, 0),
                                               (INTVAL (XEXP (x, 1)) & smask)),
                                mode, smask, next_select);
       }
@@ -8685,8 +8729,7 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
       if (CONST_INT_P (XEXP (x, 0))
          && least_bit_hwi (UINTVAL (XEXP (x, 0))) > mask)
        {
-         x = simplify_gen_unary (NEG, GET_MODE (x), XEXP (x, 1),
-                                 GET_MODE (x));
+         x = simplify_gen_unary (NEG, xmode, XEXP (x, 1), xmode);
          return force_to_mode (x, mode, mask, next_select);
        }
 
@@ -8695,8 +8738,7 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
       if (CONST_INT_P (XEXP (x, 0))
          && ((UINTVAL (XEXP (x, 0)) | fuller_mask) == UINTVAL (XEXP (x, 0))))
        {
-         x = simplify_gen_unary (NOT, GET_MODE (x),
-                                 XEXP (x, 1), GET_MODE (x));
+         x = simplify_gen_unary (NOT, xmode, XEXP (x, 1), xmode);
          return force_to_mode (x, mode, mask, next_select);
        }
 
@@ -8717,16 +8759,16 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
          && CONST_INT_P (XEXP (x, 1))
          && ((INTVAL (XEXP (XEXP (x, 0), 1))
               + floor_log2 (INTVAL (XEXP (x, 1))))
-             < GET_MODE_PRECISION (GET_MODE (x)))
+             < GET_MODE_PRECISION (xmode))
          && (UINTVAL (XEXP (x, 1))
-             & ~nonzero_bits (XEXP (x, 0), GET_MODE (x))) == 0)
+             & ~nonzero_bits (XEXP (x, 0), xmode)) == 0)
        {
          temp = gen_int_mode ((INTVAL (XEXP (x, 1)) & mask)
                               << INTVAL (XEXP (XEXP (x, 0), 1)),
-                              GET_MODE (x));
-         temp = simplify_gen_binary (GET_CODE (x), GET_MODE (x),
+                              xmode);
+         temp = simplify_gen_binary (GET_CODE (x), xmode,
                                      XEXP (XEXP (x, 0), 0), temp);
-         x = simplify_gen_binary (LSHIFTRT, GET_MODE (x), temp,
+         x = simplify_gen_binary (LSHIFTRT, xmode, temp,
                                   XEXP (XEXP (x, 0), 1));
          return force_to_mode (x, mode, mask, next_select);
        }
@@ -8750,8 +8792,11 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
       op0 = gen_lowpart_or_truncate (op_mode, op0);
       op1 = gen_lowpart_or_truncate (op_mode, op1);
 
-      if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0) || op1 != XEXP (x, 1))
-       x = simplify_gen_binary (code, op_mode, op0, op1);
+      if (op_mode != xmode || op0 != XEXP (x, 0) || op1 != XEXP (x, 1))
+       {
+         x = simplify_gen_binary (code, op_mode, op0, op1);
+         xmode = op_mode;
+       }
       break;
 
     case ASHIFT:
@@ -8784,8 +8829,11 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
                                     force_to_mode (XEXP (x, 0), op_mode,
                                                    mask, next_select));
 
-      if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0))
-       x = simplify_gen_binary (code, op_mode, op0, XEXP (x, 1));
+      if (op_mode != xmode || op0 != XEXP (x, 0))
+       {
+         x = simplify_gen_binary (code, op_mode, op0, XEXP (x, 1));
+         xmode = op_mode;
+       }
       break;
 
     case LSHIFTRT:
@@ -8807,13 +8855,16 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
          /* We can only change the mode of the shift if we can do arithmetic
             in the mode of the shift and INNER_MASK is no wider than the
             width of X's mode.  */
-         if ((inner_mask & ~GET_MODE_MASK (GET_MODE (x))) != 0)
-           op_mode = GET_MODE (x);
+         if ((inner_mask & ~GET_MODE_MASK (xmode)) != 0)
+           op_mode = xmode;
 
          inner = force_to_mode (inner, op_mode, inner_mask, next_select);
 
-         if (GET_MODE (x) != op_mode || inner != XEXP (x, 0))
-           x = simplify_gen_binary (LSHIFTRT, op_mode, inner, XEXP (x, 1));
+         if (xmode != op_mode || inner != XEXP (x, 0))
+           {
+             x = simplify_gen_binary (LSHIFTRT, op_mode, inner, XEXP (x, 1));
+             xmode = op_mode;
+           }
        }
 
       /* If we have (and (lshiftrt FOO C1) C2) where the combination of the
@@ -8826,17 +8877,17 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
             bit.  */
          && ((INTVAL (XEXP (x, 1))
               + num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0))))
-             >= GET_MODE_PRECISION (GET_MODE (x)))
+             >= GET_MODE_PRECISION (xmode))
          && pow2p_hwi (mask + 1)
          /* Number of bits left after the shift must be more than the mask
             needs.  */
          && ((INTVAL (XEXP (x, 1)) + exact_log2 (mask + 1))
-             <= GET_MODE_PRECISION (GET_MODE (x)))
+             <= GET_MODE_PRECISION (xmode))
          /* Must be more sign bit copies than the mask needs.  */
          && ((int) num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
              >= exact_log2 (mask + 1)))
-       x = simplify_gen_binary (LSHIFTRT, GET_MODE (x), XEXP (x, 0),
-                                GEN_INT (GET_MODE_PRECISION (GET_MODE (x))
+       x = simplify_gen_binary (LSHIFTRT, xmode, XEXP (x, 0),
+                                GEN_INT (GET_MODE_PRECISION (xmode)
                                          - exact_log2 (mask + 1)));
 
       goto shiftrt;
@@ -8844,7 +8895,7 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
     case ASHIFTRT:
       /* If we are just looking for the sign bit, we don't need this shift at
         all, even if it has a variable count.  */
-      if (val_signbit_p (GET_MODE (x), mask))
+      if (val_signbit_p (xmode, mask))
        return force_to_mode (XEXP (x, 0), mode, mask, next_select);
 
       /* If this is a shift by a constant, get a mask that contains those bits
@@ -8857,13 +8908,14 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
       if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) >= 0
          && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT)
        {
+         unsigned HOST_WIDE_INT nonzero;
          int i;
 
          /* If the considered data is wider than HOST_WIDE_INT, we can't
             represent a mask for all its bits in a single scalar.
             But we only care about the lower bits, so calculate these.  */
 
-         if (GET_MODE_PRECISION (GET_MODE (x)) > HOST_BITS_PER_WIDE_INT)
+         if (GET_MODE_PRECISION (xmode) > HOST_BITS_PER_WIDE_INT)
            {
              nonzero = HOST_WIDE_INT_M1U;
 
@@ -8872,21 +8924,21 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
                 We need only shift if these are fewer than nonzero can
                 hold.  If not, we must keep all bits set in nonzero.  */
 
-             if (GET_MODE_PRECISION (GET_MODE (x)) - INTVAL (XEXP (x, 1))
+             if (GET_MODE_PRECISION (xmode) - INTVAL (XEXP (x, 1))
                  < HOST_BITS_PER_WIDE_INT)
                nonzero >>= INTVAL (XEXP (x, 1))
                            + HOST_BITS_PER_WIDE_INT
-                           - GET_MODE_PRECISION (GET_MODE (x)) ;
+                           - GET_MODE_PRECISION (xmode);
            }
          else
            {
-             nonzero = GET_MODE_MASK (GET_MODE (x));
+             nonzero = GET_MODE_MASK (xmode);
              nonzero >>= INTVAL (XEXP (x, 1));
            }
 
          if ((mask & ~nonzero) == 0)
            {
-             x = simplify_shift_const (NULL_RTX, LSHIFTRT, GET_MODE (x),
+             x = simplify_shift_const (NULL_RTX, LSHIFTRT, xmode,
                                        XEXP (x, 0), INTVAL (XEXP (x, 1)));
              if (GET_CODE (x) != ASHIFTRT)
                return force_to_mode (x, mode, mask, next_select);
@@ -8895,8 +8947,8 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
          else if ((i = exact_log2 (mask)) >= 0)
            {
              x = simplify_shift_const
-                 (NULL_RTX, LSHIFTRT, GET_MODE (x), XEXP (x, 0),
-                  GET_MODE_PRECISION (GET_MODE (x)) - 1 - i);
+                 (NULL_RTX, LSHIFTRT, xmode, XEXP (x, 0),
+                  GET_MODE_PRECISION (xmode) - 1 - i);
 
              if (GET_CODE (x) != ASHIFTRT)
                return force_to_mode (x, mode, mask, next_select);
@@ -8906,8 +8958,7 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
       /* If MASK is 1, convert this to an LSHIFTRT.  This can be done
         even if the shift count isn't a constant.  */
       if (mask == 1)
-       x = simplify_gen_binary (LSHIFTRT, GET_MODE (x),
-                                XEXP (x, 0), XEXP (x, 1));
+       x = simplify_gen_binary (LSHIFTRT, xmode, XEXP (x, 0), XEXP (x, 1));
 
     shiftrt:
 
@@ -8919,7 +8970,7 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
          && CONST_INT_P (XEXP (x, 1))
          && INTVAL (XEXP (x, 1)) >= 0
          && (INTVAL (XEXP (x, 1))
-             <= GET_MODE_PRECISION (GET_MODE (x)) - (floor_log2 (mask) + 1))
+             <= GET_MODE_PRECISION (xmode) - (floor_log2 (mask) + 1))
          && GET_CODE (XEXP (x, 0)) == ASHIFT
          && XEXP (XEXP (x, 0), 1) == XEXP (x, 1))
        return force_to_mode (XEXP (XEXP (x, 0), 0), mode, mask,
@@ -8937,12 +8988,11 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
          && INTVAL (XEXP (x, 1)) >= 0)
        {
          temp = simplify_binary_operation (code == ROTATE ? ROTATERT : ROTATE,
-                                           GET_MODE (x),
-                                           gen_int_mode (mask, GET_MODE (x)),
+                                           xmode, gen_int_mode (mask, xmode),
                                            XEXP (x, 1));
          if (temp && CONST_INT_P (temp))
-           x = simplify_gen_binary (code, GET_MODE (x),
-                                    force_to_mode (XEXP (x, 0), GET_MODE (x),
+           x = simplify_gen_binary (code, xmode,
+                                    force_to_mode (XEXP (x, 0), xmode,
                                                    INTVAL (temp), next_select),
                                     XEXP (x, 1));
        }
@@ -8969,14 +9019,12 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
          && CONST_INT_P (XEXP (XEXP (x, 0), 1))
          && INTVAL (XEXP (XEXP (x, 0), 1)) >= 0
          && (INTVAL (XEXP (XEXP (x, 0), 1)) + floor_log2 (mask)
-             < GET_MODE_PRECISION (GET_MODE (x)))
+             < GET_MODE_PRECISION (xmode))
          && INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT)
        {
-         temp = gen_int_mode (mask << INTVAL (XEXP (XEXP (x, 0), 1)),
-                              GET_MODE (x));
-         temp = simplify_gen_binary (XOR, GET_MODE (x),
-                                     XEXP (XEXP (x, 0), 0), temp);
-         x = simplify_gen_binary (LSHIFTRT, GET_MODE (x),
+         temp = gen_int_mode (mask << INTVAL (XEXP (XEXP (x, 0), 1)), xmode);
+         temp = simplify_gen_binary (XOR, xmode, XEXP (XEXP (x, 0), 0), temp);
+         x = simplify_gen_binary (LSHIFTRT, xmode,
                                   temp, XEXP (XEXP (x, 0), 1));
 
          return force_to_mode (x, mode, mask, next_select);
@@ -8990,8 +9038,11 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
       op0 = gen_lowpart_or_truncate (op_mode,
                                     force_to_mode (XEXP (x, 0), mode, mask,
                                                    next_select));
-      if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0))
-       x = simplify_gen_unary (code, op_mode, op0, op_mode);
+      if (op_mode != xmode || op0 != XEXP (x, 0))
+       {
+         x = simplify_gen_unary (code, op_mode, op0, op_mode);
+         xmode = op_mode;
+       }
       break;
 
     case NE:
@@ -9012,14 +9063,14 @@ force_to_mode (rtx x, machine_mode mode, unsigned HOST_WIDE_INT mask,
       /* We have no way of knowing if the IF_THEN_ELSE can itself be
         written in a narrower mode.  We play it safe and do not do so.  */
 
-      op0 = gen_lowpart_or_truncate (GET_MODE (x),
+      op0 = gen_lowpart_or_truncate (xmode,
                                     force_to_mode (XEXP (x, 1), mode,
                                                    mask, next_select));
-      op1 = gen_lowpart_or_truncate (GET_MODE (x),
+      op1 = gen_lowpart_or_truncate (xmode,
                                     force_to_mode (XEXP (x, 2), mode,
                                                    mask, next_select));
       if (op0 != XEXP (x, 1) || op1 != XEXP (x, 2))
-       x = simplify_gen_ternary (IF_THEN_ELSE, GET_MODE (x),
+       x = simplify_gen_ternary (IF_THEN_ELSE, xmode,
                                  GET_MODE (XEXP (x, 0)), XEXP (x, 0),
                                  op0, op1);
       break;
@@ -9513,8 +9564,7 @@ make_field_assignment (rtx x)
 
   if (GET_CODE (src) == AND && GET_CODE (XEXP (src, 0)) == SUBREG
       && subreg_lowpart_p (XEXP (src, 0))
-      && (GET_MODE_SIZE (GET_MODE (XEXP (src, 0)))
-         < GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (src, 0)))))
+      && partial_subreg_p (XEXP (src, 0))
       && GET_CODE (SUBREG_REG (XEXP (src, 0))) == ROTATE
       && CONST_INT_P (XEXP (SUBREG_REG (XEXP (src, 0)), 0))
       && INTVAL (XEXP (SUBREG_REG (XEXP (src, 0)), 0)) == -2
@@ -9868,7 +9918,7 @@ distribute_and_simplify_rtx (rtx x, int n)
    (const_int CONSTOP)).  Otherwise, return NULL_RTX.  */
 
 static rtx
-simplify_and_const_int_1 (machine_mode mode, rtx varop,
+simplify_and_const_int_1 (scalar_int_mode mode, rtx varop,
                          unsigned HOST_WIDE_INT constop)
 {
   unsigned HOST_WIDE_INT nonzero;
@@ -9928,19 +9978,20 @@ simplify_and_const_int_1 (machine_mode mode, rtx varop,
      won't match a pattern either with or without this.  */
 
   if (GET_CODE (varop) == IOR || GET_CODE (varop) == XOR)
-    return
-      gen_lowpart
-       (mode,
-        apply_distributive_law
-        (simplify_gen_binary (GET_CODE (varop), GET_MODE (varop),
-                              simplify_and_const_int (NULL_RTX,
-                                                      GET_MODE (varop),
-                                                      XEXP (varop, 0),
-                                                      constop),
-                              simplify_and_const_int (NULL_RTX,
-                                                      GET_MODE (varop),
-                                                      XEXP (varop, 1),
-                                                      constop))));
+    {
+      scalar_int_mode varop_mode = as_a <scalar_int_mode> (GET_MODE (varop));
+      return
+       gen_lowpart
+         (mode,
+          apply_distributive_law
+          (simplify_gen_binary (GET_CODE (varop), varop_mode,
+                                simplify_and_const_int (NULL_RTX, varop_mode,
+                                                        XEXP (varop, 0),
+                                                        constop),
+                                simplify_and_const_int (NULL_RTX, varop_mode,
+                                                        XEXP (varop, 1),
+                                                        constop))));
+    }
 
   /* If VAROP is PLUS, and the constant is a mask of low bits, distribute
      the AND and see if one of the operands simplifies to zero.  If so, we
@@ -9983,7 +10034,7 @@ simplify_and_const_int_1 (machine_mode mode, rtx varop,
    X is zero, we are to always construct the equivalent form.  */
 
 static rtx
-simplify_and_const_int (rtx x, machine_mode mode, rtx varop,
+simplify_and_const_int (rtx x, scalar_int_mode mode, rtx varop,
                        unsigned HOST_WIDE_INT constop)
 {
   rtx tem = simplify_and_const_int_1 (mode, varop, constop);
@@ -9998,17 +10049,15 @@ simplify_and_const_int (rtx x, machine_mode mode, rtx varop,
   return x;
 }
 \f
-/* Given a REG, X, compute which bits in X can be nonzero.
+/* Given a REG X of mode XMODE, compute which bits in X can be nonzero.
    We don't care about bits outside of those defined in MODE.
 
    For most X this is simply GET_MODE_MASK (GET_MODE (MODE)), but if X is
    a shift, AND, or zero_extract, we can do better.  */
 
 static rtx
-reg_nonzero_bits_for_combine (const_rtx x, machine_mode mode,
-                             const_rtx known_x ATTRIBUTE_UNUSED,
-                             machine_mode known_mode ATTRIBUTE_UNUSED,
-                             unsigned HOST_WIDE_INT known_ret ATTRIBUTE_UNUSED,
+reg_nonzero_bits_for_combine (const_rtx x, scalar_int_mode xmode,
+                             scalar_int_mode mode,
                              unsigned HOST_WIDE_INT *nonzero)
 {
   rtx tem;
@@ -10049,8 +10098,7 @@ reg_nonzero_bits_for_combine (const_rtx x, machine_mode mode,
   if (tem)
     {
       if (SHORT_IMMEDIATES_SIGN_EXTEND)
-       tem = sign_extend_short_imm (tem, GET_MODE (x),
-                                    GET_MODE_PRECISION (mode));
+       tem = sign_extend_short_imm (tem, xmode, GET_MODE_PRECISION (mode));
 
       return tem;
     }
@@ -10059,9 +10107,9 @@ reg_nonzero_bits_for_combine (const_rtx x, machine_mode mode,
     {
       unsigned HOST_WIDE_INT mask = rsp->nonzero_bits;
 
-      if (GET_MODE_PRECISION (GET_MODE (x)) < GET_MODE_PRECISION (mode))
+      if (GET_MODE_PRECISION (xmode) < GET_MODE_PRECISION (mode))
        /* We don't know anything about the upper bits.  */
-       mask |= GET_MODE_MASK (mode) ^ GET_MODE_MASK (GET_MODE (x));
+       mask |= GET_MODE_MASK (mode) ^ GET_MODE_MASK (xmode);
 
       *nonzero &= mask;
     }
@@ -10069,17 +10117,14 @@ reg_nonzero_bits_for_combine (const_rtx x, machine_mode mode,
   return NULL;
 }
 
-/* Return the number of bits at the high-order end of X that are known to
-   be equal to the sign bit.  X will be used in mode MODE; if MODE is
-   VOIDmode, X will be used in its own mode.  The returned value  will always
-   be between 1 and the number of bits in MODE.  */
+/* Given a reg X of mode XMODE, return the number of bits at the high-order
+   end of X that are known to be equal to the sign bit.  X will be used
+   in mode MODE; the returned value will always be between 1 and the
+   number of bits in MODE.  */
 
 static rtx
-reg_num_sign_bit_copies_for_combine (const_rtx x, machine_mode mode,
-                                    const_rtx known_x ATTRIBUTE_UNUSED,
-                                    machine_mode known_mode
-                                    ATTRIBUTE_UNUSED,
-                                    unsigned int known_ret ATTRIBUTE_UNUSED,
+reg_num_sign_bit_copies_for_combine (const_rtx x, scalar_int_mode xmode,
+                                    scalar_int_mode mode,
                                     unsigned int *result)
 {
   rtx tem;
@@ -10108,7 +10153,7 @@ reg_num_sign_bit_copies_for_combine (const_rtx x, machine_mode mode,
     return tem;
 
   if (nonzero_sign_valid && rsp->sign_bit_copies != 0
-      && GET_MODE_PRECISION (GET_MODE (x)) == GET_MODE_PRECISION (mode))
+      && GET_MODE_PRECISION (xmode) == GET_MODE_PRECISION (mode))
     *result = rsp->sign_bit_copies;
 
   return NULL;
@@ -10279,13 +10324,11 @@ merge_outer_ops (enum rtx_code *pop0, HOST_WIDE_INT *pconst0, enum rtx_code op1,
    result of the shift is subject to operation OUTER_CODE with operand
    OUTER_CONST.  */
 
-static machine_mode
+static scalar_int_mode
 try_widen_shift_mode (enum rtx_code code, rtx op, int count,
-                     machine_mode orig_mode, machine_mode mode,
+                     scalar_int_mode orig_mode, scalar_int_mode mode,
                      enum rtx_code outer_code, HOST_WIDE_INT outer_const)
 {
-  if (orig_mode == mode)
-    return mode;
   gcc_assert (GET_MODE_PRECISION (mode) > GET_MODE_PRECISION (orig_mode));
 
   /* In general we can't perform in wider mode for right shift and rotate.  */
@@ -10346,7 +10389,7 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
   int count;
   machine_mode mode = result_mode;
   machine_mode shift_mode;
-  scalar_int_mode tmode, inner_mode;
+  scalar_int_mode tmode, inner_mode, int_mode, int_varop_mode, int_result_mode;
   unsigned int mode_words
     = (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
   /* We form (outer_op (code varop count) (outer_const)).  */
@@ -10386,9 +10429,19 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
          count = bitsize - count;
        }
 
-      shift_mode = try_widen_shift_mode (code, varop, count, result_mode,
-                                        mode, outer_op, outer_const);
-      machine_mode shift_unit_mode = GET_MODE_INNER (shift_mode);
+      shift_mode = result_mode;
+      if (shift_mode != mode)
+       {
+         /* We only change the modes of scalar shifts.  */
+         int_mode = as_a <scalar_int_mode> (mode);
+         int_result_mode = as_a <scalar_int_mode> (result_mode);
+         shift_mode = try_widen_shift_mode (code, varop, count,
+                                            int_result_mode, int_mode,
+                                            outer_op, outer_const);
+       }
+
+      scalar_int_mode shift_unit_mode
+       = as_a <scalar_int_mode> (GET_MODE_INNER (shift_mode));
 
       /* Handle cases where the count is greater than the size of the mode
         minus 1.  For ASHIFT, use the size minus one as the count (this can
@@ -10483,6 +10536,7 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
          /* The following rules apply only to scalars.  */
          if (shift_mode != shift_unit_mode)
            break;
+         int_mode = as_a <scalar_int_mode> (mode);
 
          /* If we have (xshiftrt (mem ...) C) and C is MODE_WIDTH
             minus the width of a smaller mode, we can do this with a
@@ -10491,15 +10545,15 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
              && ! mode_dependent_address_p (XEXP (varop, 0),
                                             MEM_ADDR_SPACE (varop))
              && ! MEM_VOLATILE_P (varop)
-             && (int_mode_for_size (GET_MODE_BITSIZE (mode) - count, 1)
+             && (int_mode_for_size (GET_MODE_BITSIZE (int_mode) - count, 1)
                  .exists (&tmode)))
            {
              new_rtx = adjust_address_nv (varop, tmode,
-                                      BYTES_BIG_ENDIAN ? 0
-                                      : count / BITS_PER_UNIT);
+                                          BYTES_BIG_ENDIAN ? 0
+                                          : count / BITS_PER_UNIT);
 
              varop = gen_rtx_fmt_e (code == ASHIFTRT ? SIGN_EXTEND
-                                    : ZERO_EXTEND, mode, new_rtx);
+                                    : ZERO_EXTEND, int_mode, new_rtx);
              count = 0;
              continue;
            }
@@ -10509,20 +10563,22 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
          /* The following rules apply only to scalars.  */
          if (shift_mode != shift_unit_mode)
            break;
+         int_mode = as_a <scalar_int_mode> (mode);
+         int_varop_mode = as_a <scalar_int_mode> (GET_MODE (varop));
 
          /* If VAROP is a SUBREG, strip it as long as the inner operand has
             the same number of words as what we've seen so far.  Then store
             the widest mode in MODE.  */
          if (subreg_lowpart_p (varop)
              && is_int_mode (GET_MODE (SUBREG_REG (varop)), &inner_mode)
-             && GET_MODE_SIZE (inner_mode) > GET_MODE_SIZE (GET_MODE (varop))
+             && GET_MODE_SIZE (inner_mode) > GET_MODE_SIZE (int_varop_mode)
              && (unsigned int) ((GET_MODE_SIZE (inner_mode)
                                  + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
                 == mode_words
-             && GET_MODE_CLASS (GET_MODE (varop)) == MODE_INT)
+             && GET_MODE_CLASS (int_varop_mode) == MODE_INT)
            {
              varop = SUBREG_REG (varop);
-             if (GET_MODE_SIZE (inner_mode) > GET_MODE_SIZE (mode))
+             if (GET_MODE_SIZE (inner_mode) > GET_MODE_SIZE (int_mode))
                mode = inner_mode;
              continue;
            }
@@ -10581,14 +10637,17 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
          /* The following rules apply only to scalars.  */
          if (shift_mode != shift_unit_mode)
            break;
+         int_mode = as_a <scalar_int_mode> (mode);
+         int_varop_mode = as_a <scalar_int_mode> (GET_MODE (varop));
+         int_result_mode = as_a <scalar_int_mode> (result_mode);
 
          /* Here we have two nested shifts.  The result is usually the
             AND of a new shift with a mask.  We compute the result below.  */
          if (CONST_INT_P (XEXP (varop, 1))
              && INTVAL (XEXP (varop, 1)) >= 0
-             && INTVAL (XEXP (varop, 1)) < GET_MODE_PRECISION (GET_MODE (varop))
-             && HWI_COMPUTABLE_MODE_P (result_mode)
-             && HWI_COMPUTABLE_MODE_P (mode))
+             && INTVAL (XEXP (varop, 1)) < GET_MODE_PRECISION (int_varop_mode)
+             && HWI_COMPUTABLE_MODE_P (int_result_mode)
+             && HWI_COMPUTABLE_MODE_P (int_mode))
            {
              enum rtx_code first_code = GET_CODE (varop);
              unsigned int first_count = INTVAL (XEXP (varop, 1));
@@ -10603,18 +10662,18 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
                 (ashiftrt:M1 (ashift:M1 (and:M1 (subreg:M1 FOO 0) C3) C2) C1).
                 This simplifies certain SIGN_EXTEND operations.  */
              if (code == ASHIFT && first_code == ASHIFTRT
-                 && count == (GET_MODE_PRECISION (result_mode)
-                              - GET_MODE_PRECISION (GET_MODE (varop))))
+                 && count == (GET_MODE_PRECISION (int_result_mode)
+                              - GET_MODE_PRECISION (int_varop_mode)))
                {
                  /* C3 has the low-order C1 bits zero.  */
 
-                 mask = GET_MODE_MASK (mode)
+                 mask = GET_MODE_MASK (int_mode)
                         & ~((HOST_WIDE_INT_1U << first_count) - 1);
 
-                 varop = simplify_and_const_int (NULL_RTX, result_mode,
+                 varop = simplify_and_const_int (NULL_RTX, int_result_mode,
                                                  XEXP (varop, 0), mask);
-                 varop = simplify_shift_const (NULL_RTX, ASHIFT, result_mode,
-                                               varop, count);
+                 varop = simplify_shift_const (NULL_RTX, ASHIFT,
+                                               int_result_mode, varop, count);
                  count = first_count;
                  code = ASHIFTRT;
                  continue;
@@ -10625,11 +10684,11 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
                 this to either an ASHIFT or an ASHIFTRT depending on the
                 two counts.
 
-                We cannot do this if VAROP's mode is not SHIFT_MODE.  */
+                We cannot do this if VAROP's mode is not SHIFT_UNIT_MODE.  */
 
              if (code == ASHIFTRT && first_code == ASHIFT
-                 && GET_MODE (varop) == shift_mode
-                 && (num_sign_bit_copies (XEXP (varop, 0), shift_mode)
+                 && int_varop_mode == shift_unit_mode
+                 && (num_sign_bit_copies (XEXP (varop, 0), shift_unit_mode)
                      > first_count))
                {
                  varop = XEXP (varop, 0);
@@ -10660,7 +10719,7 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
 
              if (code == first_code)
                {
-                 if (GET_MODE (varop) != result_mode
+                 if (int_varop_mode != int_result_mode
                      && (code == ASHIFTRT || code == LSHIFTRT
                          || code == ROTATE))
                    break;
@@ -10672,8 +10731,8 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
 
              if (code == ASHIFTRT
                  || (code == ROTATE && first_code == ASHIFTRT)
-                 || GET_MODE_PRECISION (mode) > HOST_BITS_PER_WIDE_INT
-                 || (GET_MODE (varop) != result_mode
+                 || GET_MODE_PRECISION (int_mode) > HOST_BITS_PER_WIDE_INT
+                 || (int_varop_mode != int_result_mode
                      && (first_code == ASHIFTRT || first_code == LSHIFTRT
                          || first_code == ROTATE
                          || code == ROTATE)))
@@ -10683,19 +10742,19 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
                 nonzero bits of the inner shift the same way the
                 outer shift will.  */
 
-             mask_rtx = gen_int_mode (nonzero_bits (varop, GET_MODE (varop)),
-                                      result_mode);
+             mask_rtx = gen_int_mode (nonzero_bits (varop, int_varop_mode),
+                                      int_result_mode);
 
              mask_rtx
-               = simplify_const_binary_operation (code, result_mode, mask_rtx,
-                                                  GEN_INT (count));
+               = simplify_const_binary_operation (code, int_result_mode,
+                                                  mask_rtx, GEN_INT (count));
 
              /* Give up if we can't compute an outer operation to use.  */
              if (mask_rtx == 0
                  || !CONST_INT_P (mask_rtx)
                  || ! merge_outer_ops (&outer_op, &outer_const, AND,
                                        INTVAL (mask_rtx),
-                                       result_mode, &complement_p))
+                                       int_result_mode, &complement_p))
                break;
 
              /* If the shifts are in the same direction, we add the
@@ -10732,22 +10791,22 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
              /* For ((unsigned) (cstULL >> count)) >> cst2 we have to make
                 sure the result will be masked.  See PR70222.  */
              if (code == LSHIFTRT
-                 && mode != result_mode
+                 && int_mode != int_result_mode
                  && !merge_outer_ops (&outer_op, &outer_const, AND,
-                                      GET_MODE_MASK (result_mode)
-                                      >> orig_count, result_mode,
+                                      GET_MODE_MASK (int_result_mode)
+                                      >> orig_count, int_result_mode,
                                       &complement_p))
                break;
              /* For ((int) (cstLL >> count)) >> cst2 just give up.  Queuing
                 up outer sign extension (often left and right shift) is
                 hardly more efficient than the original.  See PR70429.  */
-             if (code == ASHIFTRT && mode != result_mode)
+             if (code == ASHIFTRT && int_mode != int_result_mode)
                break;
 
-             rtx new_rtx = simplify_const_binary_operation (code, mode,
+             rtx new_rtx = simplify_const_binary_operation (code, int_mode,
                                                             XEXP (varop, 0),
                                                             GEN_INT (count));
-             varop = gen_rtx_fmt_ee (code, mode, new_rtx, XEXP (varop, 1));
+             varop = gen_rtx_fmt_ee (code, int_mode, new_rtx, XEXP (varop, 1));
              count = 0;
              continue;
            }
@@ -10768,6 +10827,8 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
          /* The following rules apply only to scalars.  */
          if (shift_mode != shift_unit_mode)
            break;
+         int_varop_mode = as_a <scalar_int_mode> (GET_MODE (varop));
+         int_result_mode = as_a <scalar_int_mode> (result_mode);
 
          /* If we have (xshiftrt (ior (plus X (const_int -1)) X) C)
             with C the size of VAROP - 1 and the shift is logical if
@@ -10780,15 +10841,15 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
              && XEXP (XEXP (varop, 0), 1) == constm1_rtx
              && (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1)
              && (code == LSHIFTRT || code == ASHIFTRT)
-             && count == (GET_MODE_PRECISION (GET_MODE (varop)) - 1)
+             && count == (GET_MODE_PRECISION (int_varop_mode) - 1)
              && rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1)))
            {
              count = 0;
-             varop = gen_rtx_LE (GET_MODE (varop), XEXP (varop, 1),
+             varop = gen_rtx_LE (int_varop_mode, XEXP (varop, 1),
                                  const0_rtx);
 
              if (STORE_FLAG_VALUE == 1 ? code == ASHIFTRT : code == LSHIFTRT)
-               varop = gen_rtx_NEG (GET_MODE (varop), varop);
+               varop = gen_rtx_NEG (int_varop_mode, varop);
 
              continue;
            }
@@ -10801,19 +10862,20 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
 
          if (CONST_INT_P (XEXP (varop, 1))
              /* We can't do this if we have (ashiftrt (xor))  and the
-                constant has its sign bit set in shift_mode with shift_mode
-                wider than result_mode.  */
+                constant has its sign bit set in shift_unit_mode with
+                shift_unit_mode wider than result_mode.  */
              && !(code == ASHIFTRT && GET_CODE (varop) == XOR
-                  && result_mode != shift_mode
+                  && int_result_mode != shift_unit_mode
                   && 0 > trunc_int_for_mode (INTVAL (XEXP (varop, 1)),
-                                             shift_mode))
+                                             shift_unit_mode))
              && (new_rtx = simplify_const_binary_operation
-                 (code, result_mode,
-                  gen_int_mode (INTVAL (XEXP (varop, 1)), result_mode),
+                 (code, int_result_mode,
+                  gen_int_mode (INTVAL (XEXP (varop, 1)), int_result_mode),
                   GEN_INT (count))) != 0
              && CONST_INT_P (new_rtx)
              && merge_outer_ops (&outer_op, &outer_const, GET_CODE (varop),
-                                 INTVAL (new_rtx), result_mode, &complement_p))
+                                 INTVAL (new_rtx), int_result_mode,
+                                 &complement_p))
            {
              varop = XEXP (varop, 0);
              continue;
@@ -10826,16 +10888,16 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
             changes the sign bit.  */
          if (CONST_INT_P (XEXP (varop, 1))
             && !(code == ASHIFTRT && GET_CODE (varop) == XOR
-                 && result_mode != shift_mode
+                 && int_result_mode != shift_unit_mode
                  && 0 > trunc_int_for_mode (INTVAL (XEXP (varop, 1)),
-                                            shift_mode)))
+                                            shift_unit_mode)))
            {
-             rtx lhs = simplify_shift_const (NULL_RTX, code, shift_mode,
+             rtx lhs = simplify_shift_const (NULL_RTX, code, shift_unit_mode,
                                              XEXP (varop, 0), count);
-             rtx rhs = simplify_shift_const (NULL_RTX, code, shift_mode,
+             rtx rhs = simplify_shift_const (NULL_RTX, code, shift_unit_mode,
                                              XEXP (varop, 1), count);
 
-             varop = simplify_gen_binary (GET_CODE (varop), shift_mode,
+             varop = simplify_gen_binary (GET_CODE (varop), shift_unit_mode,
                                           lhs, rhs);
              varop = apply_distributive_law (varop);
 
@@ -10848,6 +10910,7 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
          /* The following rules apply only to scalars.  */
          if (shift_mode != shift_unit_mode)
            break;
+         int_result_mode = as_a <scalar_int_mode> (result_mode);
 
          /* Convert (lshiftrt (eq FOO 0) C) to (xor FOO 1) if STORE_FLAG_VALUE
             says that the sign bit can be tested, FOO has mode MODE, C is
@@ -10855,13 +10918,13 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
             that may be nonzero.  */
          if (code == LSHIFTRT
              && XEXP (varop, 1) == const0_rtx
-             && GET_MODE (XEXP (varop, 0)) == result_mode
-             && count == (GET_MODE_PRECISION (result_mode) - 1)
-             && HWI_COMPUTABLE_MODE_P (result_mode)
+             && GET_MODE (XEXP (varop, 0)) == int_result_mode
+             && count == (GET_MODE_PRECISION (int_result_mode) - 1)
+             && HWI_COMPUTABLE_MODE_P (int_result_mode)
              && STORE_FLAG_VALUE == -1
-             && nonzero_bits (XEXP (varop, 0), result_mode) == 1
-             && merge_outer_ops (&outer_op, &outer_const, XOR, 1, result_mode,
-                                 &complement_p))
+             && nonzero_bits (XEXP (varop, 0), int_result_mode) == 1
+             && merge_outer_ops (&outer_op, &outer_const, XOR, 1,
+                                 int_result_mode, &complement_p))
            {
              varop = XEXP (varop, 0);
              count = 0;
@@ -10873,12 +10936,13 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
          /* The following rules apply only to scalars.  */
          if (shift_mode != shift_unit_mode)
            break;
+         int_result_mode = as_a <scalar_int_mode> (result_mode);
 
          /* (lshiftrt (neg A) C) where A is either 0 or 1 and C is one less
             than the number of bits in the mode is equivalent to A.  */
          if (code == LSHIFTRT
-             && count == (GET_MODE_PRECISION (result_mode) - 1)
-             && nonzero_bits (XEXP (varop, 0), result_mode) == 1)
+             && count == (GET_MODE_PRECISION (int_result_mode) - 1)
+             && nonzero_bits (XEXP (varop, 0), int_result_mode) == 1)
            {
              varop = XEXP (varop, 0);
              count = 0;
@@ -10888,8 +10952,8 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
          /* NEG commutes with ASHIFT since it is multiplication.  Move the
             NEG outside to allow shifts to combine.  */
          if (code == ASHIFT
-             && merge_outer_ops (&outer_op, &outer_const, NEG, 0, result_mode,
-                                 &complement_p))
+             && merge_outer_ops (&outer_op, &outer_const, NEG, 0,
+                                 int_result_mode, &complement_p))
            {
              varop = XEXP (varop, 0);
              continue;
@@ -10900,16 +10964,17 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
          /* The following rules apply only to scalars.  */
          if (shift_mode != shift_unit_mode)
            break;
+         int_result_mode = as_a <scalar_int_mode> (result_mode);
 
          /* (lshiftrt (plus A -1) C) where A is either 0 or 1 and C
             is one less than the number of bits in the mode is
             equivalent to (xor A 1).  */
          if (code == LSHIFTRT
-             && count == (GET_MODE_PRECISION (result_mode) - 1)
+             && count == (GET_MODE_PRECISION (int_result_mode) - 1)
              && XEXP (varop, 1) == constm1_rtx
-             && nonzero_bits (XEXP (varop, 0), result_mode) == 1
-             && merge_outer_ops (&outer_op, &outer_const, XOR, 1, result_mode,
-                                 &complement_p))
+             && nonzero_bits (XEXP (varop, 0), int_result_mode) == 1
+             && merge_outer_ops (&outer_op, &outer_const, XOR, 1,
+                                 int_result_mode, &complement_p))
            {
              count = 0;
              varop = XEXP (varop, 0);
@@ -10924,21 +10989,20 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
 
          if ((code == ASHIFTRT || code == LSHIFTRT)
              && count < HOST_BITS_PER_WIDE_INT
-             && nonzero_bits (XEXP (varop, 1), result_mode) >> count == 0
-             && (nonzero_bits (XEXP (varop, 1), result_mode)
-                 & nonzero_bits (XEXP (varop, 0), result_mode)) == 0)
+             && nonzero_bits (XEXP (varop, 1), int_result_mode) >> count == 0
+             && (nonzero_bits (XEXP (varop, 1), int_result_mode)
+                 & nonzero_bits (XEXP (varop, 0), int_result_mode)) == 0)
            {
              varop = XEXP (varop, 0);
              continue;
            }
          else if ((code == ASHIFTRT || code == LSHIFTRT)
                   && count < HOST_BITS_PER_WIDE_INT
-                  && HWI_COMPUTABLE_MODE_P (result_mode)
-                  && 0 == (nonzero_bits (XEXP (varop, 0), result_mode)
+                  && HWI_COMPUTABLE_MODE_P (int_result_mode)
+                  && 0 == (nonzero_bits (XEXP (varop, 0), int_result_mode)
                            >> count)
-                  && 0 == (nonzero_bits (XEXP (varop, 0), result_mode)
-                           & nonzero_bits (XEXP (varop, 1),
-                                                result_mode)))
+                  && 0 == (nonzero_bits (XEXP (varop, 0), int_result_mode)
+                           & nonzero_bits (XEXP (varop, 1), int_result_mode)))
            {
              varop = XEXP (varop, 1);
              continue;
@@ -10948,12 +11012,13 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
          if (code == ASHIFT
              && CONST_INT_P (XEXP (varop, 1))
              && (new_rtx = simplify_const_binary_operation
-                 (ASHIFT, result_mode,
-                  gen_int_mode (INTVAL (XEXP (varop, 1)), result_mode),
+                 (ASHIFT, int_result_mode,
+                  gen_int_mode (INTVAL (XEXP (varop, 1)), int_result_mode),
                   GEN_INT (count))) != 0
              && CONST_INT_P (new_rtx)
              && merge_outer_ops (&outer_op, &outer_const, PLUS,
-                                 INTVAL (new_rtx), result_mode, &complement_p))
+                                 INTVAL (new_rtx), int_result_mode,
+                                 &complement_p))
            {
              varop = XEXP (varop, 0);
              continue;
@@ -10966,14 +11031,15 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
             for reasoning in doing so.  */
          if (code == LSHIFTRT
              && CONST_INT_P (XEXP (varop, 1))
-             && mode_signbit_p (result_mode, XEXP (varop, 1))
+             && mode_signbit_p (int_result_mode, XEXP (varop, 1))
              && (new_rtx = simplify_const_binary_operation
-                 (code, result_mode,
-                  gen_int_mode (INTVAL (XEXP (varop, 1)), result_mode),
+                 (code, int_result_mode,
+                  gen_int_mode (INTVAL (XEXP (varop, 1)), int_result_mode),
                   GEN_INT (count))) != 0
              && CONST_INT_P (new_rtx)
              && merge_outer_ops (&outer_op, &outer_const, XOR,
-                                 INTVAL (new_rtx), result_mode, &complement_p))
+                                 INTVAL (new_rtx), int_result_mode,
+                                 &complement_p))
            {
              varop = XEXP (varop, 0);
              continue;
@@ -10985,6 +11051,7 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
          /* The following rules apply only to scalars.  */
          if (shift_mode != shift_unit_mode)
            break;
+         int_varop_mode = as_a <scalar_int_mode> (GET_MODE (varop));
 
          /* If we have (xshiftrt (minus (ashiftrt X C)) X) C)
             with C the size of VAROP - 1 and the shift is logical if
@@ -10995,18 +11062,18 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
 
          if ((STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1)
              && GET_CODE (XEXP (varop, 0)) == ASHIFTRT
-             && count == (GET_MODE_PRECISION (GET_MODE (varop)) - 1)
+             && count == (GET_MODE_PRECISION (int_varop_mode) - 1)
              && (code == LSHIFTRT || code == ASHIFTRT)
              && CONST_INT_P (XEXP (XEXP (varop, 0), 1))
              && INTVAL (XEXP (XEXP (varop, 0), 1)) == count
              && rtx_equal_p (XEXP (XEXP (varop, 0), 0), XEXP (varop, 1)))
            {
              count = 0;
-             varop = gen_rtx_GT (GET_MODE (varop), XEXP (varop, 1),
+             varop = gen_rtx_GT (int_varop_mode, XEXP (varop, 1),
                                  const0_rtx);
 
              if (STORE_FLAG_VALUE == 1 ? code == ASHIFTRT : code == LSHIFTRT)
-               varop = gen_rtx_NEG (GET_MODE (varop), varop);
+               varop = gen_rtx_NEG (int_varop_mode, varop);
 
              continue;
            }
@@ -11042,8 +11109,15 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
       break;
     }
 
-  shift_mode = try_widen_shift_mode (code, varop, count, result_mode, mode,
-                                    outer_op, outer_const);
+  shift_mode = result_mode;
+  if (shift_mode != mode)
+    {
+      /* We only change the modes of scalar shifts.  */
+      int_mode = as_a <scalar_int_mode> (mode);
+      int_result_mode = as_a <scalar_int_mode> (result_mode);
+      shift_mode = try_widen_shift_mode (code, varop, count, int_result_mode,
+                                        int_mode, outer_op, outer_const);
+    }
 
   /* We have now finished analyzing the shift.  The result should be
      a shift of type CODE with SHIFT_MODE shifting VAROP COUNT places.  If
@@ -11078,8 +11152,9 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
   /* If we were doing an LSHIFTRT in a wider mode than it was originally,
      turn off all the bits that the shift would have turned off.  */
   if (orig_code == LSHIFTRT && result_mode != shift_mode)
-    x = simplify_and_const_int (NULL_RTX, shift_mode, x,
-                               GET_MODE_MASK (result_mode) >> orig_count);
+    /* We only change the modes of scalar shifts.  */
+    x = simplify_and_const_int (NULL_RTX, as_a <scalar_int_mode> (shift_mode),
+                               x, GET_MODE_MASK (result_mode) >> orig_count);
 
   /* Do the remainder of the processing in RESULT_MODE.  */
   x = gen_lowpart_or_truncate (result_mode, x);
@@ -11091,12 +11166,14 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
 
   if (outer_op != UNKNOWN)
     {
+      int_result_mode = as_a <scalar_int_mode> (result_mode);
+
       if (GET_RTX_CLASS (outer_op) != RTX_UNARY
-         && GET_MODE_PRECISION (result_mode) < HOST_BITS_PER_WIDE_INT)
-       outer_const = trunc_int_for_mode (outer_const, result_mode);
+         && GET_MODE_PRECISION (int_result_mode) < HOST_BITS_PER_WIDE_INT)
+       outer_const = trunc_int_for_mode (outer_const, int_result_mode);
 
       if (outer_op == AND)
-       x = simplify_and_const_int (NULL_RTX, result_mode, x, outer_const);
+       x = simplify_and_const_int (NULL_RTX, int_result_mode, x, outer_const);
       else if (outer_op == SET)
        {
          /* This means that we have determined that the result is
@@ -11105,9 +11182,9 @@ simplify_shift_const_1 (enum rtx_code code, machine_mode result_mode,
            x = GEN_INT (outer_const);
        }
       else if (GET_RTX_CLASS (outer_op) == RTX_UNARY)
-       x = simplify_gen_unary (outer_op, result_mode, x, result_mode);
+       x = simplify_gen_unary (outer_op, int_result_mode, x, int_result_mode);
       else
-       x = simplify_gen_binary (outer_op, result_mode, x,
+       x = simplify_gen_binary (outer_op, int_result_mode, x,
                                 GEN_INT (outer_const));
     }
 
@@ -11336,7 +11413,8 @@ change_zero_ext (rtx pat)
               && !paradoxical_subreg_p (XEXP (x, 0))
               && subreg_lowpart_p (XEXP (x, 0)))
        {
-         size = GET_MODE_PRECISION (GET_MODE (XEXP (x, 0)));
+         inner_mode = as_a <scalar_int_mode> (GET_MODE (XEXP (x, 0)));
+         size = GET_MODE_PRECISION (inner_mode);
          x = SUBREG_REG (XEXP (x, 0));
          if (GET_MODE (x) != mode)
            x = gen_lowpart_SUBREG (mode, x);
@@ -11346,7 +11424,8 @@ change_zero_ext (rtx pat)
               && HARD_REGISTER_P (XEXP (x, 0))
               && can_change_dest_mode (XEXP (x, 0), 0, mode))
        {
-         size = GET_MODE_PRECISION (GET_MODE (XEXP (x, 0)));
+         inner_mode = as_a <scalar_int_mode> (GET_MODE (XEXP (x, 0)));
+         size = GET_MODE_PRECISION (inner_mode);
          x = gen_rtx_REG (mode, REGNO (XEXP (x, 0)));
        }
       else
@@ -11569,7 +11648,7 @@ static enum rtx_code
 simplify_compare_const (enum rtx_code code, machine_mode mode,
                        rtx op0, rtx *pop1)
 {
-  unsigned int mode_width = GET_MODE_PRECISION (mode);
+  scalar_int_mode int_mode;
   HOST_WIDE_INT const_op = INTVAL (*pop1);
 
   /* Get the constant we are comparing against and turn off all bits
@@ -11584,10 +11663,11 @@ simplify_compare_const (enum rtx_code code, machine_mode mode,
   if (const_op
       && (code == EQ || code == NE || code == GE || code == GEU
          || code == LT || code == LTU)
-      && mode_width - 1 < HOST_BITS_PER_WIDE_INT
-      && pow2p_hwi (const_op & GET_MODE_MASK (mode))
-      && (nonzero_bits (op0, mode)
-         == (unsigned HOST_WIDE_INT) (const_op & GET_MODE_MASK (mode))))
+      && is_a <scalar_int_mode> (mode, &int_mode)
+      && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT
+      && pow2p_hwi (const_op & GET_MODE_MASK (int_mode))
+      && (nonzero_bits (op0, int_mode)
+         == (unsigned HOST_WIDE_INT) (const_op & GET_MODE_MASK (int_mode))))
     {
       code = (code == EQ || code == GE || code == GEU ? NE : EQ);
       const_op = 0;
@@ -11598,7 +11678,8 @@ simplify_compare_const (enum rtx_code code, machine_mode mode,
   if (const_op == -1
       && (code == EQ || code == NE || code == GT || code == LE
          || code == GEU || code == LTU)
-      && num_sign_bit_copies (op0, mode) == mode_width)
+      && is_a <scalar_int_mode> (mode, &int_mode)
+      && num_sign_bit_copies (op0, int_mode) == GET_MODE_PRECISION (int_mode))
     {
       code = (code == EQ || code == LE || code == GEU ? NE : EQ);
       const_op = 0;
@@ -11632,9 +11713,10 @@ simplify_compare_const (enum rtx_code code, machine_mode mode,
       /* If we are doing a <= 0 comparison on a value known to have
         a zero sign bit, we can replace this with == 0.  */
       else if (const_op == 0
-              && mode_width - 1 < HOST_BITS_PER_WIDE_INT
-              && (nonzero_bits (op0, mode)
-                  & (HOST_WIDE_INT_1U << (mode_width - 1)))
+              && is_a <scalar_int_mode> (mode, &int_mode)
+              && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT
+              && (nonzero_bits (op0, int_mode)
+                  & (HOST_WIDE_INT_1U << (GET_MODE_PRECISION (int_mode) - 1)))
               == 0)
        code = EQ;
       break;
@@ -11662,9 +11744,10 @@ simplify_compare_const (enum rtx_code code, machine_mode mode,
       /* If we are doing a > 0 comparison on a value known to have
         a zero sign bit, we can replace this with != 0.  */
       else if (const_op == 0
-              && mode_width - 1 < HOST_BITS_PER_WIDE_INT
-              && (nonzero_bits (op0, mode)
-                  & (HOST_WIDE_INT_1U << (mode_width - 1)))
+              && is_a <scalar_int_mode> (mode, &int_mode)
+              && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT
+              && (nonzero_bits (op0, int_mode)
+                  & (HOST_WIDE_INT_1U << (GET_MODE_PRECISION (int_mode) - 1)))
               == 0)
        code = NE;
       break;
@@ -11678,9 +11761,10 @@ simplify_compare_const (enum rtx_code code, machine_mode mode,
          /* ... fall through ...  */
        }
       /* (unsigned) < 0x80000000 is equivalent to >= 0.  */
-      else if (mode_width - 1 < HOST_BITS_PER_WIDE_INT
-              && (unsigned HOST_WIDE_INT) const_op
-              == HOST_WIDE_INT_1U << (mode_width - 1))
+      else if (is_a <scalar_int_mode> (mode, &int_mode)
+              && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT
+              && ((unsigned HOST_WIDE_INT) const_op
+                  == HOST_WIDE_INT_1U << (GET_MODE_PRECISION (int_mode) - 1)))
        {
          const_op = 0;
          code = GE;
@@ -11694,9 +11778,11 @@ simplify_compare_const (enum rtx_code code, machine_mode mode,
       if (const_op == 0)
        code = EQ;
       /* (unsigned) <= 0x7fffffff is equivalent to >= 0.  */
-      else if (mode_width - 1 < HOST_BITS_PER_WIDE_INT
-              && (unsigned HOST_WIDE_INT) const_op
-              == (HOST_WIDE_INT_1U << (mode_width - 1)) - 1)
+      else if (is_a <scalar_int_mode> (mode, &int_mode)
+              && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT
+              && ((unsigned HOST_WIDE_INT) const_op
+                  == ((HOST_WIDE_INT_1U
+                       << (GET_MODE_PRECISION (int_mode) - 1)) - 1)))
        {
          const_op = 0;
          code = GE;
@@ -11713,9 +11799,10 @@ simplify_compare_const (enum rtx_code code, machine_mode mode,
        }
 
       /* (unsigned) >= 0x80000000 is equivalent to < 0.  */
-      else if (mode_width - 1 < HOST_BITS_PER_WIDE_INT
-              && (unsigned HOST_WIDE_INT) const_op
-              == HOST_WIDE_INT_1U << (mode_width - 1))
+      else if (is_a <scalar_int_mode> (mode, &int_mode)
+              && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT
+              && ((unsigned HOST_WIDE_INT) const_op
+                  == HOST_WIDE_INT_1U << (GET_MODE_PRECISION (int_mode) - 1)))
        {
          const_op = 0;
          code = LT;
@@ -11729,9 +11816,11 @@ simplify_compare_const (enum rtx_code code, machine_mode mode,
       if (const_op == 0)
        code = NE;
       /* (unsigned) > 0x7fffffff is equivalent to < 0.  */
-      else if (mode_width - 1 < HOST_BITS_PER_WIDE_INT
-              && (unsigned HOST_WIDE_INT) const_op
-              == (HOST_WIDE_INT_1U << (mode_width - 1)) - 1)
+      else if (is_a <scalar_int_mode> (mode, &int_mode)
+              && GET_MODE_PRECISION (int_mode) - 1 < HOST_BITS_PER_WIDE_INT
+              && ((unsigned HOST_WIDE_INT) const_op
+                  == (HOST_WIDE_INT_1U
+                      << (GET_MODE_PRECISION (int_mode) - 1)) - 1))
        {
          const_op = 0;
          code = LT;
@@ -11764,8 +11853,8 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1)
   rtx op1 = *pop1;
   rtx tem, tem1;
   int i;
-  scalar_int_mode mode, inner_mode;
-  machine_mode tmode;
+  scalar_int_mode mode, inner_mode, tmode;
+  opt_scalar_int_mode tmode_iter;
 
   /* Try a few ways of applying the same transformation to both operands.  */
   while (1)
@@ -11873,7 +11962,8 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1)
            }
 
          else if (c0 == c1)
-           FOR_EACH_MODE_UNTIL (tmode, GET_MODE (op0))
+           FOR_EACH_MODE_UNTIL (tmode,
+                                as_a <scalar_int_mode> (GET_MODE (op0)))
              if ((unsigned HOST_WIDE_INT) c0 == GET_MODE_MASK (tmode))
                {
                  op0 = gen_lowpart_or_truncate (tmode, inner_op0);
@@ -11915,9 +12005,8 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1)
 
   while (CONST_INT_P (op1))
     {
-      machine_mode mode = GET_MODE (op0);
-      unsigned int mode_width = GET_MODE_PRECISION (mode);
-      unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode);
+      machine_mode raw_mode = GET_MODE (op0);
+      scalar_int_mode int_mode;
       int equality_comparison_p;
       int sign_bit_comparison_p;
       int unsigned_comparison_p;
@@ -11928,14 +12017,14 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1)
         can handle VOIDmode if OP0 is a COMPARE or a comparison
         operation.  */
 
-      if (GET_MODE_CLASS (mode) != MODE_INT
-         && ! (mode == VOIDmode
+      if (GET_MODE_CLASS (raw_mode) != MODE_INT
+         && ! (raw_mode == VOIDmode
                && (GET_CODE (op0) == COMPARE || COMPARISON_P (op0))))
        break;
 
       /* Try to simplify the compare to constant, possibly changing the
         comparison op, and/or changing op1 to zero.  */
-      code = simplify_compare_const (code, mode, op0, &op1);
+      code = simplify_compare_const (code, raw_mode, op0, &op1);
       const_op = INTVAL (op1);
 
       /* Compute some predicates to simplify code below.  */
@@ -11947,16 +12036,62 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1)
 
       /* If this is a sign bit comparison and we can do arithmetic in
         MODE, say that we will only be needing the sign bit of OP0.  */
-      if (sign_bit_comparison_p && HWI_COMPUTABLE_MODE_P (mode))
-       op0 = force_to_mode (op0, mode,
+      if (sign_bit_comparison_p
+         && is_a <scalar_int_mode> (raw_mode, &int_mode)
+         && HWI_COMPUTABLE_MODE_P (int_mode))
+       op0 = force_to_mode (op0, int_mode,
                             HOST_WIDE_INT_1U
-                            << (GET_MODE_PRECISION (mode) - 1),
+                            << (GET_MODE_PRECISION (int_mode) - 1),
                             0);
 
+      if (COMPARISON_P (op0))
+       {
+         /* We can't do anything if OP0 is a condition code value, rather
+            than an actual data value.  */
+         if (const_op != 0
+             || CC0_P (XEXP (op0, 0))
+             || GET_MODE_CLASS (GET_MODE (XEXP (op0, 0))) == MODE_CC)
+           break;
+
+         /* Get the two operands being compared.  */
+         if (GET_CODE (XEXP (op0, 0)) == COMPARE)
+           tem = XEXP (XEXP (op0, 0), 0), tem1 = XEXP (XEXP (op0, 0), 1);
+         else
+           tem = XEXP (op0, 0), tem1 = XEXP (op0, 1);
+
+         /* Check for the cases where we simply want the result of the
+            earlier test or the opposite of that result.  */
+         if (code == NE || code == EQ
+             || (val_signbit_known_set_p (raw_mode, STORE_FLAG_VALUE)
+                 && (code == LT || code == GE)))
+           {
+             enum rtx_code new_code;
+             if (code == LT || code == NE)
+               new_code = GET_CODE (op0);
+             else
+               new_code = reversed_comparison_code (op0, NULL);
+
+             if (new_code != UNKNOWN)
+               {
+                 code = new_code;
+                 op0 = tem;
+                 op1 = tem1;
+                 continue;
+               }
+           }
+         break;
+       }
+
+      if (raw_mode == VOIDmode)
+       break;
+      scalar_int_mode mode = as_a <scalar_int_mode> (raw_mode);
+
       /* Now try cases based on the opcode of OP0.  If none of the cases
         does a "continue", we exit this loop immediately after the
         switch.  */
 
+      unsigned int mode_width = GET_MODE_PRECISION (mode);
+      unsigned HOST_WIDE_INT mask = GET_MODE_MASK (mode);
       switch (GET_CODE (op0))
        {
        case ZERO_EXTRACT:
@@ -12101,8 +12236,7 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1)
             insn of the given mode, since we'd have to revert it
             later on, and then we wouldn't know whether to sign- or
             zero-extend.  */
-         mode = GET_MODE (XEXP (op0, 0));
-         if (GET_MODE_CLASS (mode) == MODE_INT
+         if (is_int_mode (GET_MODE (XEXP (op0, 0)), &mode)
              && ! unsigned_comparison_p
              && HWI_COMPUTABLE_MODE_P (mode)
              && trunc_int_for_mode (const_op, mode) == const_op
@@ -12136,11 +12270,12 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1)
 
          if (mode_width <= HOST_BITS_PER_WIDE_INT
              && subreg_lowpart_p (op0)
-             && GET_MODE_PRECISION (GET_MODE (SUBREG_REG (op0))) > mode_width
+             && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (op0)),
+                                        &inner_mode)
+             && GET_MODE_PRECISION (inner_mode) > mode_width
              && GET_CODE (SUBREG_REG (op0)) == PLUS
              && CONST_INT_P (XEXP (SUBREG_REG (op0), 1)))
            {
-             machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
              rtx a = XEXP (SUBREG_REG (op0), 0);
              HOST_WIDE_INT c1 = -INTVAL (XEXP (SUBREG_REG (op0), 1));
 
@@ -12176,19 +12311,19 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1)
          if (paradoxical_subreg_p (op0))
            ;
          else if (subreg_lowpart_p (op0)
-                  && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT
+                  && GET_MODE_CLASS (mode) == MODE_INT
                   && is_int_mode (GET_MODE (SUBREG_REG (op0)), &inner_mode)
                   && (code == NE || code == EQ)
                   && GET_MODE_PRECISION (inner_mode) <= HOST_BITS_PER_WIDE_INT
                   && !paradoxical_subreg_p (op0)
                   && (nonzero_bits (SUBREG_REG (op0), inner_mode)
-                      & ~GET_MODE_MASK (GET_MODE (op0))) == 0)
+                      & ~GET_MODE_MASK (mode)) == 0)
            {
              /* Remove outer subregs that don't do anything.  */
              tem = gen_lowpart (inner_mode, op1);
 
              if ((nonzero_bits (tem, inner_mode)
-                  & ~GET_MODE_MASK (GET_MODE (op0))) == 0)
+                  & ~GET_MODE_MASK (mode)) == 0)
                {
                  op0 = SUBREG_REG (op0);
                  op1 = tem;
@@ -12202,8 +12337,7 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1)
          /* FALLTHROUGH */
 
        case ZERO_EXTEND:
-         mode = GET_MODE (XEXP (op0, 0));
-         if (GET_MODE_CLASS (mode) == MODE_INT
+         if (is_int_mode (GET_MODE (XEXP (op0, 0)), &mode)
              && (unsigned_comparison_p || equality_comparison_p)
              && HWI_COMPUTABLE_MODE_P (mode)
              && (unsigned HOST_WIDE_INT) const_op <= GET_MODE_MASK (mode)
@@ -12292,45 +12426,6 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1)
            }
          break;
 
-       case EQ:  case NE:
-       case UNEQ:  case LTGT:
-       case LT:  case LTU:  case UNLT:  case LE:  case LEU:  case UNLE:
-       case GT:  case GTU:  case UNGT:  case GE:  case GEU:  case UNGE:
-       case UNORDERED: case ORDERED:
-         /* We can't do anything if OP0 is a condition code value, rather
-            than an actual data value.  */
-         if (const_op != 0
-             || CC0_P (XEXP (op0, 0))
-             || GET_MODE_CLASS (GET_MODE (XEXP (op0, 0))) == MODE_CC)
-           break;
-
-         /* Get the two operands being compared.  */
-         if (GET_CODE (XEXP (op0, 0)) == COMPARE)
-           tem = XEXP (XEXP (op0, 0), 0), tem1 = XEXP (XEXP (op0, 0), 1);
-         else
-           tem = XEXP (op0, 0), tem1 = XEXP (op0, 1);
-
-         /* Check for the cases where we simply want the result of the
-            earlier test or the opposite of that result.  */
-         if (code == NE || code == EQ
-             || (val_signbit_known_set_p (GET_MODE (op0), STORE_FLAG_VALUE)
-                 && (code == LT || code == GE)))
-           {
-             enum rtx_code new_code;
-             if (code == LT || code == NE)
-               new_code = GET_CODE (op0);
-             else
-               new_code = reversed_comparison_code (op0, NULL);
-
-             if (new_code != UNKNOWN)
-               {
-                 code = new_code;
-                 op0 = tem;
-                 op1 = tem1;
-                 continue;
-               }
-           }
-         break;
 
        case IOR:
          /* The sign bit of (ior (plus X (const_int -1)) X) is nonzero
@@ -12602,7 +12697,7 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1)
            {
              rtx inner = XEXP (XEXP (XEXP (op0, 0), 0), 0);
              rtx add_const = XEXP (XEXP (op0, 0), 1);
-             rtx new_const = simplify_gen_binary (ASHIFTRT, GET_MODE (op0),
+             rtx new_const = simplify_gen_binary (ASHIFTRT, mode,
                                                   add_const, XEXP (op0, 1));
 
              op0 = simplify_gen_binary (PLUS, tmode,
@@ -12739,8 +12834,9 @@ simplify_comparison (enum rtx_code code, rtx *pop0, rtx *pop1)
   if (is_int_mode (GET_MODE (op0), &mode)
       && GET_MODE_SIZE (mode) < UNITS_PER_WORD
       && ! have_insn_for (COMPARE, mode))
-    FOR_EACH_WIDER_MODE (tmode, mode)
+    FOR_EACH_WIDER_MODE (tmode_iter, mode)
       {
+       tmode = tmode_iter.require ();
        if (!HWI_COMPUTABLE_MODE_P (tmode))
          break;
        if (have_insn_for (COMPARE, tmode))
@@ -13237,7 +13333,7 @@ reg_truncated_to_mode (machine_mode mode, const_rtx x)
   if (truncated == 0
       || rsp->truncation_label < label_tick_ebb_start)
     return false;
-  if (GET_MODE_SIZE (truncated) <= GET_MODE_SIZE (mode))
+  if (!partial_subreg_p (mode, truncated))
     return true;
   if (TRULY_NOOP_TRUNCATION_MODES_P (mode, truncated))
     return true;
@@ -13260,9 +13356,10 @@ record_truncated_value (rtx x)
       machine_mode original_mode = GET_MODE (SUBREG_REG (x));
       truncated_mode = GET_MODE (x);
 
-      if (GET_MODE_SIZE (original_mode) <= GET_MODE_SIZE (truncated_mode))
+      if (!partial_subreg_p (truncated_mode, original_mode))
        return true;
 
+      truncated_mode = GET_MODE (x);
       if (TRULY_NOOP_TRUNCATION_MODES_P (truncated_mode, original_mode))
        return true;
 
@@ -13278,8 +13375,7 @@ record_truncated_value (rtx x)
   rsp = &reg_stat[REGNO (x)];
   if (rsp->truncated_to_mode == 0
       || rsp->truncation_label < label_tick_ebb_start
-      || (GET_MODE_SIZE (truncated_mode)
-         < GET_MODE_SIZE (rsp->truncated_to_mode)))
+      || partial_subreg_p (truncated_mode, rsp->truncated_to_mode))
     {
       rsp->truncated_to_mode = truncated_mode;
       rsp->truncation_label = label_tick;
@@ -13803,8 +13899,7 @@ move_deaths (rtx x, rtx maybe_kill_insn, int from_luid, rtx_insn *to_insn,
             the remaining registers in place of NOTE.  */
 
          if (note != 0 && regno < FIRST_PSEUDO_REGISTER
-             && (GET_MODE_SIZE (GET_MODE (XEXP (note, 0)))
-                 > GET_MODE_SIZE (GET_MODE (x))))
+             && partial_subreg_p (GET_MODE (x), GET_MODE (XEXP (note, 0))))
            {
              unsigned int deadregno = REGNO (XEXP (note, 0));
              unsigned int deadend = END_REGNO (XEXP (note, 0));
@@ -13823,8 +13918,8 @@ move_deaths (rtx x, rtx maybe_kill_insn, int from_luid, rtx_insn *to_insn,
             their own REG_DEAD notes lying around.  */
          else if ((note == 0
                    || (note != 0
-                       && (GET_MODE_SIZE (GET_MODE (XEXP (note, 0)))
-                           < GET_MODE_SIZE (GET_MODE (x)))))
+                       && partial_subreg_p (GET_MODE (XEXP (note, 0)),
+                                            GET_MODE (x))))
                   && regno < FIRST_PSEUDO_REGISTER
                   && REG_NREGS (x) > 1)
            {
@@ -13833,7 +13928,7 @@ move_deaths (rtx x, rtx maybe_kill_insn, int from_luid, rtx_insn *to_insn,
              rtx oldnotes = 0;
 
              if (note)
-               offset = hard_regno_nregs[regno][GET_MODE (XEXP (note, 0))];
+               offset = hard_regno_nregs (regno, GET_MODE (XEXP (note, 0)));
              else
                offset = 1;
 
@@ -14445,7 +14540,7 @@ distribute_notes (rtx notes, rtx_insn *from_insn, rtx_insn *i3, rtx_insn *i2,
                         not already dead or set.  */
 
                      for (i = regno; i < endregno;
-                          i += hard_regno_nregs[i][reg_raw_mode[i]])
+                          i += hard_regno_nregs (i, reg_raw_mode[i]))
                        {
                          rtx piece = regno_reg_rtx[i];
                          basic_block bb = this_basic_block;