Daily bump.
[gcc.git] / gcc / optabs.c
index 1dfb545d251e5bbb26cc2a753de26d7477c8b441..f8dabbfc50df7f42703af17bc6e0d14ec3ba7c2b 100644 (file)
@@ -1,5 +1,5 @@
 /* Expand the basic unary and binary arithmetic operations, for GNU compiler.
-   Copyright (C) 1987-2017 Free Software Foundation, Inc.
+   Copyright (C) 1987-2019 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "emit-rtl.h"
 #include "recog.h"
 #include "diagnostic-core.h"
+#include "rtx-vector-builder.h"
 
 /* Include insn-config.h before expr.h so that HAVE_conditional_move
    is properly defined.  */
@@ -138,8 +139,8 @@ add_equal_note (rtx_insn *insns, rtx target, enum rtx_code code, rtx op0, rtx op
        if (GET_MODE (op0) != VOIDmode && GET_MODE (target) != GET_MODE (op0))
          {
            note = gen_rtx_fmt_e (code, GET_MODE (op0), copy_rtx (op0));
-           if (GET_MODE_SIZE (GET_MODE (op0))
-               > GET_MODE_SIZE (GET_MODE (target)))
+           if (GET_MODE_UNIT_SIZE (GET_MODE (op0))
+               > GET_MODE_UNIT_SIZE (GET_MODE (target)))
              note = simplify_gen_unary (TRUNCATE, GET_MODE (target),
                                         note, GET_MODE (op0));
            else
@@ -173,12 +174,12 @@ widened_mode (machine_mode to_mode, rtx op0, rtx op1)
 
   if (m0 == VOIDmode && m1 == VOIDmode)
     return to_mode;
-  else if (m0 == VOIDmode || GET_MODE_SIZE (m0) < GET_MODE_SIZE (m1))
+  else if (m0 == VOIDmode || GET_MODE_UNIT_SIZE (m0) < GET_MODE_UNIT_SIZE (m1))
     result = m1;
   else
     result = m0;
 
-  if (GET_MODE_SIZE (result) > GET_MODE_SIZE (to_mode))
+  if (GET_MODE_UNIT_SIZE (result) > GET_MODE_UNIT_SIZE (to_mode))
     return to_mode;
 
   return result;
@@ -255,16 +256,40 @@ expand_widen_pattern_expr (sepops ops, rtx op0, rtx op1, rtx wide_op,
   enum insn_code icode;
   int nops = TREE_CODE_LENGTH (ops->code);
   int op;
+  bool sbool = false;
 
   oprnd0 = ops->op0;
   tmode0 = TYPE_MODE (TREE_TYPE (oprnd0));
-  widen_pattern_optab =
-    optab_for_tree_code (ops->code, TREE_TYPE (oprnd0), optab_default);
+  if (ops->code == VEC_UNPACK_FIX_TRUNC_HI_EXPR
+      || ops->code == VEC_UNPACK_FIX_TRUNC_LO_EXPR)
+    /* The sign is from the result type rather than operand's type
+       for these ops.  */
+    widen_pattern_optab
+      = optab_for_tree_code (ops->code, ops->type, optab_default);
+  else if ((ops->code == VEC_UNPACK_HI_EXPR
+           || ops->code == VEC_UNPACK_LO_EXPR)
+          && VECTOR_BOOLEAN_TYPE_P (ops->type)
+          && VECTOR_BOOLEAN_TYPE_P (TREE_TYPE (oprnd0))
+          && TYPE_MODE (ops->type) == TYPE_MODE (TREE_TYPE (oprnd0))
+          && SCALAR_INT_MODE_P (TYPE_MODE (ops->type)))
+    {
+      /* For VEC_UNPACK_{LO,HI}_EXPR if the mode of op0 and result is
+        the same scalar mode for VECTOR_BOOLEAN_TYPE_P vectors, use
+        vec_unpacks_sbool_{lo,hi}_optab, so that we can pass in
+        the pattern number of elements in the wider vector.  */
+      widen_pattern_optab
+       = (ops->code == VEC_UNPACK_HI_EXPR
+          ? vec_unpacks_sbool_hi_optab : vec_unpacks_sbool_lo_optab);
+      sbool = true;
+    }
+  else
+    widen_pattern_optab
+      = optab_for_tree_code (ops->code, TREE_TYPE (oprnd0), optab_default);
   if (ops->code == WIDEN_MULT_PLUS_EXPR
       || ops->code == WIDEN_MULT_MINUS_EXPR)
     icode = find_widening_optab_handler (widen_pattern_optab,
                                         TYPE_MODE (TREE_TYPE (ops->op2)),
-                                        tmode0, 0);
+                                        tmode0);
   else
     icode = optab_handler (widen_pattern_optab, tmode0);
   gcc_assert (icode != CODE_FOR_nothing);
@@ -274,6 +299,12 @@ expand_widen_pattern_expr (sepops ops, rtx op0, rtx op1, rtx wide_op,
       oprnd1 = ops->op1;
       tmode1 = TYPE_MODE (TREE_TYPE (oprnd1));
     }
+  else if (sbool)
+    {
+      nops = 2;
+      op1 = GEN_INT (TYPE_VECTOR_SUBPARTS (TREE_TYPE (oprnd0)).to_constant ());
+      tmode1 = tmode0;
+    }
 
   /* The last operand is of a wider mode than the rest of the operands.  */
   if (nops == 2)
@@ -367,23 +398,29 @@ force_expand_binop (machine_mode mode, optab binoptab,
    mode of OP must be the element mode of VMODE.  If OP is a constant,
    then the return value will be a constant.  */
 
-static rtx
+rtx
 expand_vector_broadcast (machine_mode vmode, rtx op)
 {
-  enum insn_code icode;
+  int n;
   rtvec vec;
-  rtx ret;
-  int i, n;
 
   gcc_checking_assert (VECTOR_MODE_P (vmode));
 
-  n = GET_MODE_NUNITS (vmode);
-  vec = rtvec_alloc (n);
-  for (i = 0; i < n; ++i)
-    RTVEC_ELT (vec, i) = op;
+  if (valid_for_const_vector_p (vmode, op))
+    return gen_const_vec_duplicate (vmode, op);
 
-  if (CONSTANT_P (op))
-    return gen_rtx_CONST_VECTOR (vmode, vec);
+  insn_code icode = optab_handler (vec_duplicate_optab, vmode);
+  if (icode != CODE_FOR_nothing)
+    {
+      struct expand_operand ops[2];
+      create_output_operand (&ops[0], NULL_RTX, vmode);
+      create_input_operand (&ops[1], op, GET_MODE (op));
+      expand_insn (icode, 2, ops);
+      return ops[0].value;
+    }
+
+  if (!GET_MODE_NUNITS (vmode).is_constant (&n))
+    return NULL;
 
   /* ??? If the target doesn't have a vec_init, then we have no easy way
      of performing this operation.  Most of this sort of generic support
@@ -393,7 +430,10 @@ expand_vector_broadcast (machine_mode vmode, rtx op)
   if (icode == CODE_FOR_nothing)
     return NULL;
 
-  ret = gen_reg_rtx (vmode);
+  vec = rtvec_alloc (n);
+  for (int i = 0; i < n; ++i)
+    RTVEC_ELT (vec, i) = op;
+  rtx ret = gen_reg_rtx (vmode);
   emit_insn (GEN_FCN (icode) (ret, gen_rtx_PARALLEL (vmode, vec)));
 
   return ret;
@@ -422,8 +462,9 @@ expand_superword_shift (optab binoptab, rtx outof_input, rtx superword_op1,
       if (binoptab != ashr_optab)
        emit_move_insn (outof_target, CONST0_RTX (word_mode));
       else
-       if (!force_expand_binop (word_mode, binoptab,
-                                outof_input, GEN_INT (BITS_PER_WORD - 1),
+       if (!force_expand_binop (word_mode, binoptab, outof_input,
+                                gen_int_shift_amount (word_mode,
+                                                      BITS_PER_WORD - 1),
                                 outof_target, unsignedp, methods))
          return false;
     }
@@ -435,7 +476,7 @@ expand_superword_shift (optab binoptab, rtx outof_input, rtx superword_op1,
    value are the same as for the parent routine.  */
 
 static bool
-expand_subword_shift (machine_mode op1_mode, optab binoptab,
+expand_subword_shift (scalar_int_mode op1_mode, optab binoptab,
                      rtx outof_input, rtx into_input, rtx op1,
                      rtx outof_target, rtx into_target,
                      int unsignedp, enum optab_methods methods,
@@ -518,7 +559,7 @@ expand_subword_shift (machine_mode op1_mode, optab binoptab,
    arguments are the same as the parent routine.  */
 
 static bool
-expand_doubleword_shift_condmove (machine_mode op1_mode, optab binoptab,
+expand_doubleword_shift_condmove (scalar_int_mode op1_mode, optab binoptab,
                                  enum rtx_code cmp_code, rtx cmp1, rtx cmp2,
                                  rtx outof_input, rtx into_input,
                                  rtx subword_op1, rtx superword_op1,
@@ -601,7 +642,7 @@ expand_doubleword_shift_condmove (machine_mode op1_mode, optab binoptab,
    Return true if the shift could be successfully synthesized.  */
 
 static bool
-expand_doubleword_shift (machine_mode op1_mode, optab binoptab,
+expand_doubleword_shift (scalar_int_mode op1_mode, optab binoptab,
                         rtx outof_input, rtx into_input, rtx op1,
                         rtx outof_target, rtx into_target,
                         int unsignedp, enum optab_methods methods,
@@ -780,7 +821,8 @@ expand_doubleword_mult (machine_mode mode, rtx op0, rtx op1, rtx target,
 {
   int low = (WORDS_BIG_ENDIAN ? 1 : 0);
   int high = (WORDS_BIG_ENDIAN ? 0 : 1);
-  rtx wordm1 = umulp ? NULL_RTX : GEN_INT (BITS_PER_WORD - 1);
+  rtx wordm1 = (umulp ? NULL_RTX
+               : gen_int_shift_amount (word_mode, BITS_PER_WORD - 1));
   rtx product, adjust, product_high, temp;
 
   rtx op0_high = operand_subword_force (op0, high, mode);
@@ -862,6 +904,11 @@ expand_doubleword_mult (machine_mode mode, rtx op0, rtx op1, rtx target,
   if (target && !REG_P (target))
     target = NULL_RTX;
 
+  /* *_widen_optab needs to determine operand mode, make sure at least
+     one operand has non-VOID mode.  */
+  if (GET_MODE (op0_low) == VOIDmode && GET_MODE (op1_low) == VOIDmode)
+    op0_low = force_reg (word_mode, op0_low);
+
   if (umulp)
     product = expand_binop (mode, umul_widen_optab, op0_low, op1_low,
                            target, 1, OPTAB_DIRECT);
@@ -980,17 +1027,14 @@ avoid_expensive_constant (machine_mode mode, optab binoptab,
 }
 
 /* Helper function for expand_binop: handle the case where there
-   is an insn that directly implements the indicated operation.
+   is an insn ICODE that directly implements the indicated operation.
    Returns null if this is not possible.  */
 static rtx
-expand_binop_directly (machine_mode mode, optab binoptab,
+expand_binop_directly (enum insn_code icode, machine_mode mode, optab binoptab,
                       rtx op0, rtx op1,
                       rtx target, int unsignedp, enum optab_methods methods,
                       rtx_insn *last)
 {
-  machine_mode from_mode = widened_mode (mode, op0, op1);
-  enum insn_code icode = find_widening_optab_handler (binoptab, mode,
-                                                     from_mode, 1);
   machine_mode xmode0 = insn_data[(int) icode].operand[1].mode;
   machine_mode xmode1 = insn_data[(int) icode].operand[2].mode;
   machine_mode mode0, mode1, tmp_mode;
@@ -1054,13 +1098,15 @@ expand_binop_directly (machine_mode mode, optab binoptab,
       || binoptab == vec_pack_usat_optab
       || binoptab == vec_pack_ssat_optab
       || binoptab == vec_pack_ufix_trunc_optab
-      || binoptab == vec_pack_sfix_trunc_optab)
+      || binoptab == vec_pack_sfix_trunc_optab
+      || binoptab == vec_packu_float_optab
+      || binoptab == vec_packs_float_optab)
     {
       /* The mode of the result is different then the mode of the
         arguments.  */
       tmp_mode = insn_data[(int) icode].operand[0].mode;
       if (VECTOR_MODE_P (mode)
-         && GET_MODE_NUNITS (tmp_mode) != 2 * GET_MODE_NUNITS (mode))
+         && maybe_ne (GET_MODE_NUNITS (tmp_mode), 2 * GET_MODE_NUNITS (mode)))
        {
          delete_insns_since (last);
          return NULL_RTX;
@@ -1114,6 +1160,7 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
     = (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN
        ? OPTAB_WIDEN : methods);
   enum mode_class mclass;
+  enum insn_code icode;
   machine_mode wider_mode;
   scalar_int_mode int_mode;
   rtx libfunc;
@@ -1147,23 +1194,30 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
 
   /* If we can do it with a three-operand insn, do so.  */
 
-  if (methods != OPTAB_MUST_WIDEN
-      && find_widening_optab_handler (binoptab, mode,
-                                     widened_mode (mode, op0, op1), 1)
-           != CODE_FOR_nothing)
+  if (methods != OPTAB_MUST_WIDEN)
     {
-      temp = expand_binop_directly (mode, binoptab, op0, op1, target,
-                                   unsignedp, methods, last);
-      if (temp)
-       return temp;
+      if (convert_optab_p (binoptab))
+       {
+         machine_mode from_mode = widened_mode (mode, op0, op1);
+         icode = find_widening_optab_handler (binoptab, mode, from_mode);
+       }
+      else
+       icode = optab_handler (binoptab, mode);
+      if (icode != CODE_FOR_nothing)
+       {
+         temp = expand_binop_directly (icode, mode, binoptab, op0, op1,
+                                       target, unsignedp, methods, last);
+         if (temp)
+           return temp;
+       }
     }
 
   /* If we were trying to rotate, and that didn't work, try rotating
      the other direction before falling back to shifts and bitwise-or.  */
   if (((binoptab == rotl_optab
-       && optab_handler (rotr_optab, mode) != CODE_FOR_nothing)
+       && (icode = optab_handler (rotr_optab, mode)) != CODE_FOR_nothing)
        || (binoptab == rotr_optab
-          && optab_handler (rotl_optab, mode) != CODE_FOR_nothing))
+          && (icode = optab_handler (rotl_optab, mode)) != CODE_FOR_nothing))
       && is_int_mode (mode, &int_mode))
     {
       optab otheroptab = (binoptab == rotl_optab ? rotr_optab : rotl_optab);
@@ -1171,7 +1225,7 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
       unsigned int bits = GET_MODE_PRECISION (int_mode);
 
       if (CONST_INT_P (op1))
-        newop1 = GEN_INT (bits - INTVAL (op1));
+       newop1 = gen_int_shift_amount (int_mode, bits - INTVAL (op1));
       else if (targetm.shift_truncation_mask (int_mode) == bits - 1)
         newop1 = negate_rtx (GET_MODE (op1), op1);
       else
@@ -1179,7 +1233,7 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
                               gen_int_mode (bits, GET_MODE (op1)), op1,
                               NULL_RTX, unsignedp, OPTAB_DIRECT);
 
-      temp = expand_binop_directly (int_mode, otheroptab, op0, newop1,
+      temp = expand_binop_directly (icode, int_mode, otheroptab, op0, newop1,
                                    target, unsignedp, methods, last);
       if (temp)
        return temp;
@@ -1195,6 +1249,10 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
                                  : smul_widen_optab),
                                 wider_mode, mode) != CODE_FOR_nothing))
     {
+      /* *_widen_optab needs to determine operand mode, make sure at least
+        one operand has non-VOID mode.  */
+      if (GET_MODE (op0) == VOIDmode && GET_MODE (op1) == VOIDmode)
+       op0 = force_reg (mode, op0);
       temp = expand_binop (wider_mode,
                           unsignedp ? umul_widen_optab : smul_widen_optab,
                           op0, op1, NULL_RTX, unsignedp, OPTAB_DIRECT);
@@ -1226,21 +1284,22 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
       else if (binoptab == rotr_optab)
        otheroptab = vrotr_optab;
 
-      if (otheroptab && optab_handler (otheroptab, mode) != CODE_FOR_nothing)
+      if (otheroptab
+         && (icode = optab_handler (otheroptab, mode)) != CODE_FOR_nothing)
        {
          /* The scalar may have been extended to be too wide.  Truncate
             it back to the proper size to fit in the broadcast vector.  */
-         machine_mode inner_mode = GET_MODE_INNER (mode);
+         scalar_mode inner_mode = GET_MODE_INNER (mode);
          if (!CONST_INT_P (op1)
-             && (GET_MODE_BITSIZE (inner_mode)
-                 < GET_MODE_BITSIZE (GET_MODE (op1))))
+             && (GET_MODE_BITSIZE (as_a <scalar_int_mode> (GET_MODE (op1)))
+                 > GET_MODE_BITSIZE (inner_mode)))
            op1 = force_reg (inner_mode,
                             simplify_gen_unary (TRUNCATE, inner_mode, op1,
                                                 GET_MODE (op1)));
          rtx vop1 = expand_vector_broadcast (mode, op1);
          if (vop1)
            {
-             temp = expand_binop_directly (mode, otheroptab, op0, vop1,
+             temp = expand_binop_directly (icode, mode, otheroptab, op0, vop1,
                                            target, unsignedp, methods, last);
              if (temp)
                return temp;
@@ -1263,7 +1322,7 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
                && (find_widening_optab_handler ((unsignedp
                                                  ? umul_widen_optab
                                                  : smul_widen_optab),
-                                                next_mode, mode, 0)
+                                                next_mode, mode)
                    != CODE_FOR_nothing)))
          {
            rtx xop0 = op0, xop1 = op1;
@@ -1341,12 +1400,18 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
       start_sequence ();
 
       /* Do the actual arithmetic.  */
+      machine_mode op0_mode = GET_MODE (op0);
+      machine_mode op1_mode = GET_MODE (op1);
+      if (op0_mode == VOIDmode)
+       op0_mode = int_mode;
+      if (op1_mode == VOIDmode)
+       op1_mode = int_mode;
       for (i = 0; i < GET_MODE_BITSIZE (int_mode) / BITS_PER_WORD; i++)
        {
          rtx target_piece = operand_subword (target, i, 1, int_mode);
          rtx x = expand_binop (word_mode, binoptab,
-                               operand_subword_force (op0, i, int_mode),
-                               operand_subword_force (op1, i, int_mode),
+                               operand_subword_force (op0, i, op0_mode),
+                               operand_subword_force (op1, i, op1_mode),
                                target_piece, unsignedp, next_methods);
 
          if (x == 0)
@@ -1378,15 +1443,17 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
       && optab_handler (lshr_optab, word_mode) != CODE_FOR_nothing)
     {
       unsigned HOST_WIDE_INT shift_mask, double_shift_mask;
-      machine_mode op1_mode;
+      scalar_int_mode op1_mode;
 
       double_shift_mask = targetm.shift_truncation_mask (int_mode);
       shift_mask = targetm.shift_truncation_mask (word_mode);
-      op1_mode = GET_MODE (op1) != VOIDmode ? GET_MODE (op1) : word_mode;
+      op1_mode = (GET_MODE (op1) != VOIDmode
+                 ? as_a <scalar_int_mode> (GET_MODE (op1))
+                 : word_mode);
 
       /* Apply the truncation to constant shifts.  */
       if (double_shift_mask > 0 && CONST_INT_P (op1))
-       op1 = GEN_INT (INTVAL (op1) & double_shift_mask);
+       op1 = gen_int_mode (INTVAL (op1) & double_shift_mask, op1_mode);
 
       if (op1 == CONST0_RTX (op1_mode))
        return op0;
@@ -1496,7 +1563,7 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
       else
        {
          rtx into_temp1, into_temp2, outof_temp1, outof_temp2;
-         rtx first_shift_count, second_shift_count;
+         HOST_WIDE_INT first_shift_count, second_shift_count;
          optab reverse_unsigned_shift, unsigned_shift;
 
          reverse_unsigned_shift = (left_shift ^ (shift_count < BITS_PER_WORD)
@@ -1507,20 +1574,24 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
 
          if (shift_count > BITS_PER_WORD)
            {
-             first_shift_count = GEN_INT (shift_count - BITS_PER_WORD);
-             second_shift_count = GEN_INT (2 * BITS_PER_WORD - shift_count);
+             first_shift_count = shift_count - BITS_PER_WORD;
+             second_shift_count = 2 * BITS_PER_WORD - shift_count;
            }
          else
            {
-             first_shift_count = GEN_INT (BITS_PER_WORD - shift_count);
-             second_shift_count = GEN_INT (shift_count);
+             first_shift_count = BITS_PER_WORD - shift_count;
+             second_shift_count = shift_count;
            }
+         rtx first_shift_count_rtx
+           = gen_int_shift_amount (word_mode, first_shift_count);
+         rtx second_shift_count_rtx
+           = gen_int_shift_amount (word_mode, second_shift_count);
 
          into_temp1 = expand_binop (word_mode, unsigned_shift,
-                                    outof_input, first_shift_count,
+                                    outof_input, first_shift_count_rtx,
                                     NULL_RTX, unsignedp, next_methods);
          into_temp2 = expand_binop (word_mode, reverse_unsigned_shift,
-                                    into_input, second_shift_count,
+                                    into_input, second_shift_count_rtx,
                                     NULL_RTX, unsignedp, next_methods);
 
          if (into_temp1 != 0 && into_temp2 != 0)
@@ -1533,10 +1604,10 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
            emit_move_insn (into_target, inter);
 
          outof_temp1 = expand_binop (word_mode, unsigned_shift,
-                                     into_input, first_shift_count,
+                                     into_input, first_shift_count_rtx,
                                      NULL_RTX, unsignedp, next_methods);
          outof_temp2 = expand_binop (word_mode, reverse_unsigned_shift,
-                                     outof_input, second_shift_count,
+                                     outof_input, second_shift_count_rtx,
                                      NULL_RTX, unsignedp, next_methods);
 
          if (inter != 0 && outof_temp1 != 0 && outof_temp2 != 0)
@@ -1692,7 +1763,7 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
       && optab_handler (add_optab, word_mode) != CODE_FOR_nothing)
     {
       rtx product = NULL_RTX;
-      if (widening_optab_handler (umul_widen_optab, int_mode, word_mode)
+      if (convert_optab_handler (umul_widen_optab, int_mode, word_mode)
          != CODE_FOR_nothing)
        {
          product = expand_doubleword_mult (int_mode, op0, op1, target,
@@ -1702,7 +1773,7 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
        }
 
       if (product == NULL_RTX
-         && (widening_optab_handler (smul_widen_optab, int_mode, word_mode)
+         && (convert_optab_handler (smul_widen_optab, int_mode, word_mode)
              != CODE_FOR_nothing))
        {
          product = expand_doubleword_mult (int_mode, op0, op1, target,
@@ -1757,7 +1828,7 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
       /* Pass 1 for NO_QUEUE so we don't lose any increments
         if the libcall is cse'd or moved.  */
       value = emit_library_call_value (libfunc,
-                                      NULL_RTX, LCT_CONST, mode, 2,
+                                      NULL_RTX, LCT_CONST, mode,
                                       op0, mode, op1x, op1_mode);
 
       insns = get_insns ();
@@ -1795,10 +1866,13 @@ expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
 
   if (CLASS_HAS_WIDER_MODES_P (mclass))
     {
+      /* This code doesn't make sense for conversion optabs, since we
+        wouldn't then want to extend the operands to be the same size
+        as the result.  */
+      gcc_assert (!convert_optab_p (binoptab));
       FOR_EACH_WIDER_MODE (wider_mode, mode)
        {
-         if (find_widening_optab_handler (binoptab, wider_mode, mode, 1)
-                 != CODE_FOR_nothing
+         if (optab_handler (binoptab, wider_mode)
              || (methods == OPTAB_LIB
                  && optab_libfunc (binoptab, wider_mode)))
            {
@@ -2090,7 +2164,7 @@ expand_twoval_binop_libfunc (optab binoptab, rtx op0, rtx op1,
   libval_mode = smallest_int_mode_for_size (2 * GET_MODE_BITSIZE (mode));
   start_sequence ();
   libval = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
-                                   libval_mode, 2,
+                                   libval_mode,
                                    op0, mode,
                                    op1, mode);
   /* Get the part of VAL containing the value that we want.  */
@@ -2127,39 +2201,36 @@ expand_simple_unop (machine_mode mode, enum rtx_code code, rtx op0,
    A similar operation can be used for clrsb.  UNOPTAB says which operation
    we are trying to expand.  */
 static rtx
-widen_leading (machine_mode mode, rtx op0, rtx target, optab unoptab)
+widen_leading (scalar_int_mode mode, rtx op0, rtx target, optab unoptab)
 {
-  enum mode_class mclass = GET_MODE_CLASS (mode);
-  if (CLASS_HAS_WIDER_MODES_P (mclass))
+  opt_scalar_int_mode wider_mode_iter;
+  FOR_EACH_WIDER_MODE (wider_mode_iter, mode)
     {
-      machine_mode wider_mode;
-      FOR_EACH_WIDER_MODE (wider_mode, mode)
+      scalar_int_mode wider_mode = wider_mode_iter.require ();
+      if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing)
        {
-         if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing)
-           {
-             rtx xop0, temp;
-             rtx_insn *last;
+         rtx xop0, temp;
+         rtx_insn *last;
 
-             last = get_last_insn ();
+         last = get_last_insn ();
 
-             if (target == 0)
-               target = gen_reg_rtx (mode);
-             xop0 = widen_operand (op0, wider_mode, mode,
-                                   unoptab != clrsb_optab, false);
-             temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX,
-                                 unoptab != clrsb_optab);
-             if (temp != 0)
-               temp = expand_binop
-                 (wider_mode, sub_optab, temp,
-                  gen_int_mode (GET_MODE_PRECISION (wider_mode)
-                                - GET_MODE_PRECISION (mode),
-                                wider_mode),
-                  target, true, OPTAB_DIRECT);
-             if (temp == 0)
-               delete_insns_since (last);
+         if (target == 0)
+           target = gen_reg_rtx (mode);
+         xop0 = widen_operand (op0, wider_mode, mode,
+                               unoptab != clrsb_optab, false);
+         temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX,
+                             unoptab != clrsb_optab);
+         if (temp != 0)
+           temp = expand_binop
+             (wider_mode, sub_optab, temp,
+              gen_int_mode (GET_MODE_PRECISION (wider_mode)
+                            - GET_MODE_PRECISION (mode),
+                            wider_mode),
+              target, true, OPTAB_DIRECT);
+         if (temp == 0)
+           delete_insns_since (last);
 
-             return temp;
-           }
+         return temp;
        }
     }
   return 0;
@@ -2168,7 +2239,7 @@ widen_leading (machine_mode mode, rtx op0, rtx target, optab unoptab)
 /* Try calculating clz of a double-word quantity as two clz's of word-sized
    quantities, choosing which based on whether the high word is nonzero.  */
 static rtx
-expand_doubleword_clz (machine_mode mode, rtx op0, rtx target)
+expand_doubleword_clz (scalar_int_mode mode, rtx op0, rtx target)
 {
   rtx xop0 = force_reg (mode, op0);
   rtx subhi = gen_highpart (word_mode, xop0);
@@ -2239,7 +2310,7 @@ expand_doubleword_clz (machine_mode mode, rtx op0, rtx target)
 /* Try calculating popcount of a double-word quantity as two popcount's of
    word-sized quantities and summing up the results.  */
 static rtx
-expand_doubleword_popcount (machine_mode mode, rtx op0, rtx target)
+expand_doubleword_popcount (scalar_int_mode mode, rtx op0, rtx target)
 {
   rtx t0, t1, t;
   rtx_insn *seq;
@@ -2279,7 +2350,7 @@ expand_doubleword_popcount (machine_mode mode, rtx op0, rtx target)
    as
        (parity:narrow (low (x) ^ high (x))) */
 static rtx
-expand_doubleword_parity (machine_mode mode, rtx op0, rtx target)
+expand_doubleword_parity (scalar_int_mode mode, rtx op0, rtx target)
 {
   rtx t = expand_binop (word_mode, xor_optab,
                        operand_subword_force (op0, 0, mode),
@@ -2293,22 +2364,21 @@ expand_doubleword_parity (machine_mode mode, rtx op0, rtx target)
    as
        (lshiftrt:wide (bswap:wide x) ((width wide) - (width narrow))).  */
 static rtx
-widen_bswap (machine_mode mode, rtx op0, rtx target)
+widen_bswap (scalar_int_mode mode, rtx op0, rtx target)
 {
-  enum mode_class mclass = GET_MODE_CLASS (mode);
-  machine_mode wider_mode;
   rtx x;
   rtx_insn *last;
+  opt_scalar_int_mode wider_mode_iter;
 
-  if (!CLASS_HAS_WIDER_MODES_P (mclass))
-    return NULL_RTX;
+  FOR_EACH_WIDER_MODE (wider_mode_iter, mode)
+    if (optab_handler (bswap_optab, wider_mode_iter.require ())
+       != CODE_FOR_nothing)
+      break;
 
-  FOR_EACH_WIDER_MODE (wider_mode, mode)
-    if (optab_handler (bswap_optab, wider_mode) != CODE_FOR_nothing)
-      goto found;
-  return NULL_RTX;
+  if (!wider_mode_iter.exists ())
+    return NULL_RTX;
 
- found:
+  scalar_int_mode wider_mode = wider_mode_iter.require ();
   last = get_last_insn ();
 
   x = widen_operand (op0, wider_mode, mode, true, true);
@@ -2359,42 +2429,40 @@ expand_doubleword_bswap (machine_mode mode, rtx op, rtx target)
 /* Try calculating (parity x) as (and (popcount x) 1), where
    popcount can also be done in a wider mode.  */
 static rtx
-expand_parity (machine_mode mode, rtx op0, rtx target)
+expand_parity (scalar_int_mode mode, rtx op0, rtx target)
 {
   enum mode_class mclass = GET_MODE_CLASS (mode);
-  if (CLASS_HAS_WIDER_MODES_P (mclass))
+  opt_scalar_int_mode wider_mode_iter;
+  FOR_EACH_MODE_FROM (wider_mode_iter, mode)
     {
-      machine_mode wider_mode;
-      FOR_EACH_MODE_FROM (wider_mode, mode)
+      scalar_int_mode wider_mode = wider_mode_iter.require ();
+      if (optab_handler (popcount_optab, wider_mode) != CODE_FOR_nothing)
        {
-         if (optab_handler (popcount_optab, wider_mode) != CODE_FOR_nothing)
-           {
-             rtx xop0, temp;
-             rtx_insn *last;
+         rtx xop0, temp;
+         rtx_insn *last;
 
-             last = get_last_insn ();
+         last = get_last_insn ();
 
-             if (target == 0 || GET_MODE (target) != wider_mode)
-               target = gen_reg_rtx (wider_mode);
+         if (target == 0 || GET_MODE (target) != wider_mode)
+           target = gen_reg_rtx (wider_mode);
 
-             xop0 = widen_operand (op0, wider_mode, mode, true, false);
-             temp = expand_unop (wider_mode, popcount_optab, xop0, NULL_RTX,
-                                 true);
-             if (temp != 0)
-               temp = expand_binop (wider_mode, and_optab, temp, const1_rtx,
-                                    target, true, OPTAB_DIRECT);
+         xop0 = widen_operand (op0, wider_mode, mode, true, false);
+         temp = expand_unop (wider_mode, popcount_optab, xop0, NULL_RTX,
+                             true);
+         if (temp != 0)
+           temp = expand_binop (wider_mode, and_optab, temp, const1_rtx,
+                                target, true, OPTAB_DIRECT);
 
-             if (temp)
-               {
-                 if (mclass != MODE_INT
-                     || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode))
-                   return convert_to_mode (mode, temp, 0);
-                 else
-                   return gen_lowpart (mode, temp);
-               }
+         if (temp)
+           {
+             if (mclass != MODE_INT
+                 || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode))
+               return convert_to_mode (mode, temp, 0);
              else
-               delete_insns_since (last);
+               return gen_lowpart (mode, temp);
            }
+         else
+           delete_insns_since (last);
        }
     }
   return 0;
@@ -2413,7 +2481,7 @@ expand_parity (machine_mode mode, rtx op0, rtx target)
    less convenient for expand_ffs anyway.  */
 
 static rtx
-expand_ctz (machine_mode mode, rtx op0, rtx target)
+expand_ctz (scalar_int_mode mode, rtx op0, rtx target)
 {
   rtx_insn *seq;
   rtx temp;
@@ -2456,7 +2524,7 @@ expand_ctz (machine_mode mode, rtx op0, rtx target)
    may have an undefined value in that case.  If they do not give us a
    convenient value, we have to generate a test and branch.  */
 static rtx
-expand_ffs (machine_mode mode, rtx op0, rtx target)
+expand_ffs (scalar_int_mode mode, rtx op0, rtx target)
 {
   HOST_WIDE_INT val = 0;
   bool defined_at_zero = false;
@@ -2559,7 +2627,7 @@ expand_absneg_bit (enum rtx_code code, scalar_float_mode mode,
 {
   const struct real_format *fmt;
   int bitpos, word, nwords, i;
-  machine_mode imode;
+  scalar_int_mode imode;
   rtx temp;
   rtx_insn *insns;
 
@@ -2713,16 +2781,19 @@ expand_unop (machine_mode mode, optab unoptab, rtx op0, rtx target,
   /* Widening (or narrowing) clz needs special treatment.  */
   if (unoptab == clz_optab)
     {
-      temp = widen_leading (mode, op0, target, unoptab);
-      if (temp)
-       return temp;
-
-      if (GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
-         && optab_handler (unoptab, word_mode) != CODE_FOR_nothing)
+      if (is_a <scalar_int_mode> (mode, &int_mode))
        {
-         temp = expand_doubleword_clz (mode, op0, target);
+         temp = widen_leading (int_mode, op0, target, unoptab);
          if (temp)
            return temp;
+
+         if (GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD
+             && optab_handler (unoptab, word_mode) != CODE_FOR_nothing)
+           {
+             temp = expand_doubleword_clz (int_mode, op0, target);
+             if (temp)
+               return temp;
+           }
        }
 
       goto try_libcall;
@@ -2730,29 +2801,34 @@ expand_unop (machine_mode mode, optab unoptab, rtx op0, rtx target,
 
   if (unoptab == clrsb_optab)
     {
-      temp = widen_leading (mode, op0, target, unoptab);
-      if (temp)
-       return temp;
+      if (is_a <scalar_int_mode> (mode, &int_mode))
+       {
+         temp = widen_leading (int_mode, op0, target, unoptab);
+         if (temp)
+           return temp;
+       }
       goto try_libcall;
     }
 
   if (unoptab == popcount_optab
-      && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+      && is_a <scalar_int_mode> (mode, &int_mode)
+      && GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD
       && optab_handler (unoptab, word_mode) != CODE_FOR_nothing
       && optimize_insn_for_speed_p ())
     {
-      temp = expand_doubleword_popcount (mode, op0, target);
+      temp = expand_doubleword_popcount (int_mode, op0, target);
       if (temp)
        return temp;
     }
 
   if (unoptab == parity_optab
-      && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+      && is_a <scalar_int_mode> (mode, &int_mode)
+      && GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD
       && (optab_handler (unoptab, word_mode) != CODE_FOR_nothing
          || optab_handler (popcount_optab, word_mode) != CODE_FOR_nothing)
       && optimize_insn_for_speed_p ())
     {
-      temp = expand_doubleword_parity (mode, op0, target);
+      temp = expand_doubleword_parity (int_mode, op0, target);
       if (temp)
        return temp;
     }
@@ -2771,25 +2847,29 @@ expand_unop (machine_mode mode, optab unoptab, rtx op0, rtx target,
 
          if (optab_handler (rotl_optab, mode) != CODE_FOR_nothing)
            {
-             temp = expand_binop (mode, rotl_optab, op0, GEN_INT (8), target,
-                                  unsignedp, OPTAB_DIRECT);
+             temp = expand_binop (mode, rotl_optab, op0,
+                                  gen_int_shift_amount (mode, 8),
+                                  target, unsignedp, OPTAB_DIRECT);
              if (temp)
                return temp;
             }
 
          if (optab_handler (rotr_optab, mode) != CODE_FOR_nothing)
            {
-             temp = expand_binop (mode, rotr_optab, op0, GEN_INT (8), target,
-                                  unsignedp, OPTAB_DIRECT);
+             temp = expand_binop (mode, rotr_optab, op0,
+                                  gen_int_shift_amount (mode, 8),
+                                  target, unsignedp, OPTAB_DIRECT);
              if (temp)
                return temp;
            }
 
          last = get_last_insn ();
 
-         temp1 = expand_binop (mode, ashl_optab, op0, GEN_INT (8), NULL_RTX,
+         temp1 = expand_binop (mode, ashl_optab, op0,
+                               gen_int_shift_amount (mode, 8), NULL_RTX,
                                unsignedp, OPTAB_WIDEN);
-         temp2 = expand_binop (mode, lshr_optab, op0, GEN_INT (8), NULL_RTX,
+         temp2 = expand_binop (mode, lshr_optab, op0,
+                               gen_int_shift_amount (mode, 8), NULL_RTX,
                                unsignedp, OPTAB_WIDEN);
          if (temp1 && temp2)
            {
@@ -2802,16 +2882,19 @@ expand_unop (machine_mode mode, optab unoptab, rtx op0, rtx target,
          delete_insns_since (last);
        }
 
-      temp = widen_bswap (mode, op0, target);
-      if (temp)
-       return temp;
-
-      if (GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
-         && optab_handler (unoptab, word_mode) != CODE_FOR_nothing)
+      if (is_a <scalar_int_mode> (mode, &int_mode))
        {
-         temp = expand_doubleword_bswap (mode, op0, target);
+         temp = widen_bswap (int_mode, op0, target);
          if (temp)
            return temp;
+
+         if (GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD
+             && optab_handler (unoptab, word_mode) != CODE_FOR_nothing)
+           {
+             temp = expand_doubleword_bswap (mode, op0, target);
+             if (temp)
+               return temp;
+           }
        }
 
       goto try_libcall;
@@ -2912,25 +2995,25 @@ expand_unop (machine_mode mode, optab unoptab, rtx op0, rtx target,
     }
 
   /* Try calculating parity (x) as popcount (x) % 2.  */
-  if (unoptab == parity_optab)
+  if (unoptab == parity_optab && is_a <scalar_int_mode> (mode, &int_mode))
     {
-      temp = expand_parity (mode, op0, target);
+      temp = expand_parity (int_mode, op0, target);
       if (temp)
        return temp;
     }
 
   /* Try implementing ffs (x) in terms of clz (x).  */
-  if (unoptab == ffs_optab)
+  if (unoptab == ffs_optab && is_a <scalar_int_mode> (mode, &int_mode))
     {
-      temp = expand_ffs (mode, op0, target);
+      temp = expand_ffs (int_mode, op0, target);
       if (temp)
        return temp;
     }
 
   /* Try implementing ctz (x) in terms of clz (x).  */
-  if (unoptab == ctz_optab)
+  if (unoptab == ctz_optab && is_a <scalar_int_mode> (mode, &int_mode))
     {
-      temp = expand_ctz (mode, op0, target);
+      temp = expand_ctz (int_mode, op0, target);
       if (temp)
        return temp;
     }
@@ -2959,7 +3042,7 @@ expand_unop (machine_mode mode, optab unoptab, rtx op0, rtx target,
       /* Pass 1 for NO_QUEUE so we don't lose any increments
         if the libcall is cse'd or moved.  */
       value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST, outmode,
-                                      1, op0, mode);
+                                      op0, mode);
       insns = get_insns ();
       end_sequence ();
 
@@ -2970,9 +3053,9 @@ expand_unop (machine_mode mode, optab unoptab, rtx op0, rtx target,
       else
        {
          eq_value = gen_rtx_fmt_e (optab_to_code (unoptab), mode, op0);
-         if (GET_MODE_SIZE (outmode) < GET_MODE_SIZE (mode))
+         if (GET_MODE_UNIT_SIZE (outmode) < GET_MODE_UNIT_SIZE (mode))
            eq_value = simplify_gen_unary (TRUNCATE, outmode, eq_value, mode);
-         else if (GET_MODE_SIZE (outmode) > GET_MODE_SIZE (mode))
+         else if (GET_MODE_UNIT_SIZE (outmode) > GET_MODE_UNIT_SIZE (mode))
            eq_value = simplify_gen_unary (ZERO_EXTEND,
                                           outmode, eq_value, mode);
        }
@@ -3009,24 +3092,32 @@ expand_unop (machine_mode mode, optab unoptab, rtx op0, rtx target,
                 result.  Similarly for clrsb.  */
              if ((unoptab == clz_optab || unoptab == clrsb_optab)
                  && temp != 0)
-               temp = expand_binop
-                 (wider_mode, sub_optab, temp,
-                  gen_int_mode (GET_MODE_PRECISION (wider_mode)
-                                - GET_MODE_PRECISION (mode),
-                                wider_mode),
-                  target, true, OPTAB_DIRECT);
+               {
+                 scalar_int_mode wider_int_mode
+                   = as_a <scalar_int_mode> (wider_mode);
+                 int_mode = as_a <scalar_int_mode> (mode);
+                 temp = expand_binop
+                   (wider_mode, sub_optab, temp,
+                    gen_int_mode (GET_MODE_PRECISION (wider_int_mode)
+                                  - GET_MODE_PRECISION (int_mode),
+                                  wider_int_mode),
+                    target, true, OPTAB_DIRECT);
+               }
 
              /* Likewise for bswap.  */
              if (unoptab == bswap_optab && temp != 0)
                {
-                 gcc_assert (GET_MODE_PRECISION (wider_mode)
-                             == GET_MODE_BITSIZE (wider_mode)
-                             && GET_MODE_PRECISION (mode)
-                                == GET_MODE_BITSIZE (mode));
-
-                 temp = expand_shift (RSHIFT_EXPR, wider_mode, temp,
-                                      GET_MODE_BITSIZE (wider_mode)
-                                      - GET_MODE_BITSIZE (mode),
+                 scalar_int_mode wider_int_mode
+                   = as_a <scalar_int_mode> (wider_mode);
+                 int_mode = as_a <scalar_int_mode> (mode);
+                 gcc_assert (GET_MODE_PRECISION (wider_int_mode)
+                             == GET_MODE_BITSIZE (wider_int_mode)
+                             && GET_MODE_PRECISION (int_mode)
+                                == GET_MODE_BITSIZE (int_mode));
+
+                 temp = expand_shift (RSHIFT_EXPR, wider_int_mode, temp,
+                                      GET_MODE_BITSIZE (wider_int_mode)
+                                      - GET_MODE_BITSIZE (int_mode),
                                       NULL_RTX, true);
                }
 
@@ -3254,7 +3345,7 @@ static rtx
 expand_copysign_absneg (scalar_float_mode mode, rtx op0, rtx op1, rtx target,
                        int bitpos, bool op0_is_abs)
 {
-  machine_mode imode;
+  scalar_int_mode imode;
   enum insn_code icode;
   rtx sign;
   rtx_code_label *label;
@@ -3267,7 +3358,7 @@ expand_copysign_absneg (scalar_float_mode mode, rtx op0, rtx op1, rtx target,
   icode = optab_handler (signbit_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      imode = insn_data[(int) icode].operand[0].mode;
+      imode = as_a <scalar_int_mode> (insn_data[(int) icode].operand[0].mode);
       sign = gen_reg_rtx (imode);
       emit_unop_insn (icode, sign, op1, UNKNOWN);
     }
@@ -3750,6 +3841,9 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size,
   gcc_assert (methods == OPTAB_DIRECT || methods == OPTAB_WIDEN
              || methods == OPTAB_LIB_WIDEN);
 
+  if (CONST_SCALAR_INT_P (y))
+    canonicalize_comparison (mode, &comparison, &y);
+
   /* If we are optimizing, force expensive constants into a register.  */
   if (CONSTANT_P (x) && optimize
       && (rtx_cost (x, mode, COMPARE, 0, optimize_insn_for_speed_p ())
@@ -3788,8 +3882,10 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size,
 
       /* Try to use a memory block compare insn - either cmpstr
         or cmpmem will do.  */
-      FOR_EACH_MODE_IN_CLASS (cmp_mode, MODE_INT)
+      opt_scalar_int_mode cmp_mode_iter;
+      FOR_EACH_MODE_IN_CLASS (cmp_mode_iter, MODE_INT)
        {
+         scalar_int_mode cmp_mode = cmp_mode_iter.require ();
          cmp_code = direct_optab_handler (cmpmem_optab, cmp_mode);
          if (cmp_code == CODE_FOR_nothing)
            cmp_code = direct_optab_handler (cmpstr_optab, cmp_mode);
@@ -3799,10 +3895,10 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size,
            continue;
 
          /* Must make sure the size fits the insn's mode.  */
-         if ((CONST_INT_P (size)
-              && INTVAL (size) >= (1 << GET_MODE_BITSIZE (cmp_mode)))
-             || (GET_MODE_BITSIZE (GET_MODE (size))
-                 > GET_MODE_BITSIZE (cmp_mode)))
+         if (CONST_INT_P (size)
+             ? INTVAL (size) >= (1 << GET_MODE_BITSIZE (cmp_mode))
+             : (GET_MODE_BITSIZE (as_a <scalar_int_mode> (GET_MODE (size)))
+                > GET_MODE_BITSIZE (cmp_mode)))
            continue;
 
          result_mode = insn_data[cmp_code].operand[0].mode;
@@ -3880,7 +3976,20 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size,
   if (methods != OPTAB_LIB_WIDEN)
     goto fail;
 
-  if (!SCALAR_FLOAT_MODE_P (mode))
+  if (SCALAR_FLOAT_MODE_P (mode))
+    {
+      /* Small trick if UNORDERED isn't implemented by the hardware.  */
+      if (comparison == UNORDERED && rtx_equal_p (x, y))
+       {
+         prepare_cmp_insn (x, y, UNLT, NULL_RTX, unsignedp, OPTAB_WIDEN,
+                           ptest, pmode);
+         if (*ptest)
+           return;
+       }
+
+      prepare_float_lib_cmp (x, y, comparison, ptest, pmode);
+    }
+  else
     {
       rtx result;
       machine_mode ret_mode;
@@ -3900,7 +4009,7 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size,
 
       ret_mode = targetm.libgcc_cmp_return_mode ();
       result = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
-                                       ret_mode, 2, x, mode, y, mode);
+                                       ret_mode, x, mode, y, mode);
 
       /* There are two kinds of comparison routines. Biased routines
         return 0/1/2, and unbiased routines return -1/0/1. Other parts
@@ -3927,8 +4036,6 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size,
       prepare_cmp_insn (x, y, comparison, NULL_RTX, unsignedp, methods,
                        ptest, pmode);
     }
-  else
-    prepare_float_lib_cmp (x, y, comparison, ptest, pmode);
 
   return;
 
@@ -4050,13 +4157,13 @@ prepare_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison,
   enum rtx_code swapped = swap_condition (comparison);
   enum rtx_code reversed = reverse_condition_maybe_unordered (comparison);
   machine_mode orig_mode = GET_MODE (x);
-  machine_mode mode, cmp_mode;
+  machine_mode mode;
   rtx true_rtx, false_rtx;
   rtx value, target, equiv;
   rtx_insn *insns;
   rtx libfunc = 0;
   bool reversed_p = false;
-  cmp_mode = targetm.libgcc_cmp_return_mode ();
+  scalar_int_mode cmp_mode = targetm.libgcc_cmp_return_mode ();
 
   FOR_EACH_MODE_FROM (mode, orig_mode)
     {
@@ -4154,7 +4261,7 @@ prepare_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison,
 
   start_sequence ();
   value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
-                                  cmp_mode, 2, x, mode, y, mode);
+                                  cmp_mode, x, mode, y, mode);
   insns = get_insns ();
   end_sequence ();
 
@@ -4279,9 +4386,10 @@ emit_conditional_move (rtx target, enum rtx_code code, rtx op0, rtx op1,
          save_pending_stack_adjust (&save);
          last = get_last_insn ();
          do_pending_stack_adjust ();
+         machine_mode cmpmode = cmode;
          prepare_cmp_insn (XEXP (comparison, 0), XEXP (comparison, 1),
                            GET_CODE (comparison), NULL_RTX, unsignedp,
-                           OPTAB_WIDEN, &comparison, &cmode);
+                           OPTAB_WIDEN, &comparison, &cmpmode);
          if (comparison)
            {
              struct expand_operand ops[4];
@@ -4620,6 +4728,7 @@ expand_float (rtx to, rtx from, int unsignedp)
 {
   enum insn_code icode;
   rtx target = to;
+  scalar_mode from_mode, to_mode;
   machine_mode fmode, imode;
   bool can_do_signed = false;
 
@@ -4637,7 +4746,8 @@ expand_float (rtx to, rtx from, int unsignedp)
        int doing_unsigned = unsignedp;
 
        if (fmode != GET_MODE (to)
-           && significand_size (fmode) < GET_MODE_PRECISION (GET_MODE (from)))
+           && (significand_size (fmode)
+               < GET_MODE_UNIT_PRECISION (GET_MODE (from))))
          continue;
 
        icode = can_float_p (fmode, imode, unsignedp);
@@ -4669,8 +4779,12 @@ expand_float (rtx to, rtx from, int unsignedp)
 
   /* Unsigned integer, and no way to convert directly.  Convert as signed,
      then unconditionally adjust the result.  */
-  if (unsignedp && can_do_signed)
+  if (unsignedp
+      && can_do_signed
+      && is_a <scalar_mode> (GET_MODE (to), &to_mode)
+      && is_a <scalar_mode> (GET_MODE (from), &from_mode))
     {
+      opt_scalar_mode fmode_iter;
       rtx_code_label *label = gen_label_rtx ();
       rtx temp;
       REAL_VALUE_TYPE offset;
@@ -4679,19 +4793,22 @@ expand_float (rtx to, rtx from, int unsignedp)
         least as wide as the target.  Using FMODE will avoid rounding woes
         with unsigned values greater than the signed maximum value.  */
 
-      FOR_EACH_MODE_FROM (fmode, GET_MODE (to))
-       if (GET_MODE_PRECISION (GET_MODE (from)) < GET_MODE_BITSIZE (fmode)
-           && can_float_p (fmode, GET_MODE (from), 0) != CODE_FOR_nothing)
-         break;
+      FOR_EACH_MODE_FROM (fmode_iter, to_mode)
+       {
+         scalar_mode fmode = fmode_iter.require ();
+         if (GET_MODE_PRECISION (from_mode) < GET_MODE_BITSIZE (fmode)
+             && can_float_p (fmode, from_mode, 0) != CODE_FOR_nothing)
+           break;
+       }
 
-      if (fmode == VOIDmode)
+      if (!fmode_iter.exists (&fmode))
        {
          /* There is no such mode.  Pretend the target is wide enough.  */
-         fmode = GET_MODE (to);
+         fmode = to_mode;
 
          /* Avoid double-rounding when TO is narrower than FROM.  */
          if ((significand_size (fmode) + 1)
-             < GET_MODE_PRECISION (GET_MODE (from)))
+             < GET_MODE_PRECISION (from_mode))
            {
              rtx temp1;
              rtx_code_label *neglabel = gen_label_rtx ();
@@ -4703,7 +4820,7 @@ expand_float (rtx to, rtx from, int unsignedp)
                  || GET_MODE (target) != fmode)
                target = gen_reg_rtx (fmode);
 
-             imode = GET_MODE (from);
+             imode = from_mode;
              do_pending_stack_adjust ();
 
              /* Test whether the sign bit is set.  */
@@ -4743,7 +4860,7 @@ expand_float (rtx to, rtx from, int unsignedp)
       /* If we are about to do some arithmetic to correct for an
         unsigned operand, do it in a pseudo-register.  */
 
-      if (GET_MODE (to) != fmode
+      if (to_mode != fmode
          || !REG_P (to) || REGNO (to) < FIRST_PSEUDO_REGISTER)
        target = gen_reg_rtx (fmode);
 
@@ -4754,11 +4871,11 @@ expand_float (rtx to, rtx from, int unsignedp)
         correct its value by 2**bitwidth.  */
 
       do_pending_stack_adjust ();
-      emit_cmp_and_jump_insns (from, const0_rtx, GE, NULL_RTX, GET_MODE (from),
+      emit_cmp_and_jump_insns (from, const0_rtx, GE, NULL_RTX, from_mode,
                               0, label);
 
 
-      real_2expN (&offset, GET_MODE_PRECISION (GET_MODE (from)), fmode);
+      real_2expN (&offset, GET_MODE_PRECISION (from_mode), fmode);
       temp = expand_binop (fmode, add_optab, target,
                           const_double_from_real_value (offset, fmode),
                           target, 0, OPTAB_LIB_WIDEN);
@@ -4777,7 +4894,7 @@ expand_float (rtx to, rtx from, int unsignedp)
       rtx value;
       convert_optab tab = unsignedp ? ufloat_optab : sfloat_optab;
 
-      if (GET_MODE_PRECISION (GET_MODE (from)) < GET_MODE_PRECISION (SImode))
+      if (is_narrower_int_mode (GET_MODE (from), SImode))
        from = convert_to_mode (SImode, from, unsignedp);
 
       libfunc = convert_optab_libfunc (tab, GET_MODE (to), GET_MODE (from));
@@ -4786,8 +4903,7 @@ expand_float (rtx to, rtx from, int unsignedp)
       start_sequence ();
 
       value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
-                                      GET_MODE (to), 1, from,
-                                      GET_MODE (from));
+                                      GET_MODE (to), from, GET_MODE (from));
       insns = get_insns ();
       end_sequence ();
 
@@ -4819,6 +4935,7 @@ expand_fix (rtx to, rtx from, int unsignedp)
   enum insn_code icode;
   rtx target = to;
   machine_mode fmode, imode;
+  opt_scalar_mode fmode_iter;
   bool must_trunc = false;
 
   /* We first try to find a pair of modes, one real and one integer, at
@@ -4886,73 +5003,80 @@ expand_fix (rtx to, rtx from, int unsignedp)
      2^63.  The subtraction of 2^63 should not generate any rounding as it
      simply clears out that bit.  The rest is trivial.  */
 
-  if (unsignedp && GET_MODE_PRECISION (GET_MODE (to)) <= HOST_BITS_PER_WIDE_INT)
-    FOR_EACH_MODE_FROM (fmode, GET_MODE (from))
-      if (CODE_FOR_nothing != can_fix_p (GET_MODE (to), fmode, 0, &must_trunc)
-         && (!DECIMAL_FLOAT_MODE_P (fmode)
-             || GET_MODE_BITSIZE (fmode) > GET_MODE_PRECISION (GET_MODE (to))))
-       {
-         int bitsize;
-         REAL_VALUE_TYPE offset;
-         rtx limit;
-         rtx_code_label *lab1, *lab2;
-         rtx_insn *insn;
-
-         bitsize = GET_MODE_PRECISION (GET_MODE (to));
-         real_2expN (&offset, bitsize - 1, fmode);
-         limit = const_double_from_real_value (offset, fmode);
-         lab1 = gen_label_rtx ();
-         lab2 = gen_label_rtx ();
-
-         if (fmode != GET_MODE (from))
-           from = convert_to_mode (fmode, from, 0);
-
-         /* See if we need to do the subtraction.  */
-         do_pending_stack_adjust ();
-         emit_cmp_and_jump_insns (from, limit, GE, NULL_RTX, GET_MODE (from),
-                                  0, lab1);
-
-         /* If not, do the signed "fix" and branch around fixup code.  */
-         expand_fix (to, from, 0);
-         emit_jump_insn (targetm.gen_jump (lab2));
-         emit_barrier ();
-
-         /* Otherwise, subtract 2**(N-1), convert to signed number,
-            then add 2**(N-1).  Do the addition using XOR since this
-            will often generate better code.  */
-         emit_label (lab1);
-         target = expand_binop (GET_MODE (from), sub_optab, from, limit,
-                                NULL_RTX, 0, OPTAB_LIB_WIDEN);
-         expand_fix (to, target, 0);
-         target = expand_binop (GET_MODE (to), xor_optab, to,
-                                gen_int_mode
-                                (HOST_WIDE_INT_1 << (bitsize - 1),
-                                 GET_MODE (to)),
-                                to, 1, OPTAB_LIB_WIDEN);
-
-         if (target != to)
-           emit_move_insn (to, target);
-
-         emit_label (lab2);
-
-         if (optab_handler (mov_optab, GET_MODE (to)) != CODE_FOR_nothing)
-           {
-             /* Make a place for a REG_NOTE and add it.  */
-             insn = emit_move_insn (to, to);
-             set_dst_reg_note (insn, REG_EQUAL,
-                               gen_rtx_fmt_e (UNSIGNED_FIX, GET_MODE (to),
-                                              copy_rtx (from)),
-                               to);
-           }
+  scalar_int_mode to_mode;
+  if (unsignedp
+      && is_a <scalar_int_mode> (GET_MODE (to), &to_mode)
+      && HWI_COMPUTABLE_MODE_P (to_mode))
+    FOR_EACH_MODE_FROM (fmode_iter, as_a <scalar_mode> (GET_MODE (from)))
+      {
+       scalar_mode fmode = fmode_iter.require ();
+       if (CODE_FOR_nothing != can_fix_p (to_mode, fmode,
+                                          0, &must_trunc)
+           && (!DECIMAL_FLOAT_MODE_P (fmode)
+               || (GET_MODE_BITSIZE (fmode) > GET_MODE_PRECISION (to_mode))))
+         {
+           int bitsize;
+           REAL_VALUE_TYPE offset;
+           rtx limit;
+           rtx_code_label *lab1, *lab2;
+           rtx_insn *insn;
+
+           bitsize = GET_MODE_PRECISION (to_mode);
+           real_2expN (&offset, bitsize - 1, fmode);
+           limit = const_double_from_real_value (offset, fmode);
+           lab1 = gen_label_rtx ();
+           lab2 = gen_label_rtx ();
 
-         return;
-       }
+           if (fmode != GET_MODE (from))
+             from = convert_to_mode (fmode, from, 0);
+
+           /* See if we need to do the subtraction.  */
+           do_pending_stack_adjust ();
+           emit_cmp_and_jump_insns (from, limit, GE, NULL_RTX,
+                                    GET_MODE (from), 0, lab1);
+
+           /* If not, do the signed "fix" and branch around fixup code.  */
+           expand_fix (to, from, 0);
+           emit_jump_insn (targetm.gen_jump (lab2));
+           emit_barrier ();
+
+           /* Otherwise, subtract 2**(N-1), convert to signed number,
+              then add 2**(N-1).  Do the addition using XOR since this
+              will often generate better code.  */
+           emit_label (lab1);
+           target = expand_binop (GET_MODE (from), sub_optab, from, limit,
+                                  NULL_RTX, 0, OPTAB_LIB_WIDEN);
+           expand_fix (to, target, 0);
+           target = expand_binop (to_mode, xor_optab, to,
+                                  gen_int_mode
+                                  (HOST_WIDE_INT_1 << (bitsize - 1),
+                                   to_mode),
+                                  to, 1, OPTAB_LIB_WIDEN);
+
+           if (target != to)
+             emit_move_insn (to, target);
+
+           emit_label (lab2);
+
+           if (optab_handler (mov_optab, to_mode) != CODE_FOR_nothing)
+             {
+               /* Make a place for a REG_NOTE and add it.  */
+               insn = emit_move_insn (to, to);
+               set_dst_reg_note (insn, REG_EQUAL,
+                                 gen_rtx_fmt_e (UNSIGNED_FIX, to_mode,
+                                                copy_rtx (from)),
+                                 to);
+             }
+
+           return;
+         }
+      }
 
   /* We can't do it with an insn, so use a library call.  But first ensure
      that the mode of TO is at least as wide as SImode, since those are the
      only library calls we know about.  */
 
-  if (GET_MODE_PRECISION (GET_MODE (to)) < GET_MODE_PRECISION (SImode))
+  if (is_narrower_int_mode (GET_MODE (to), SImode))
     {
       target = gen_reg_rtx (SImode);
 
@@ -4971,8 +5095,7 @@ expand_fix (rtx to, rtx from, int unsignedp)
       start_sequence ();
 
       value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
-                                      GET_MODE (to), 1, from,
-                                      GET_MODE (from));
+                                      GET_MODE (to), from, GET_MODE (from));
       insns = get_insns ();
       end_sequence ();
 
@@ -5064,7 +5187,7 @@ expand_fixed_convert (rtx to, rtx from, int uintp, int satp)
 
   start_sequence ();
   value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST, to_mode,
-                                  1, from, from_mode);
+                                  from, from_mode);
   insns = get_insns ();
   end_sequence ();
 
@@ -5318,35 +5441,39 @@ vector_compare_rtx (machine_mode cmp_mode, enum tree_code tcode,
   return gen_rtx_fmt_ee (rcode, cmp_mode, ops[0].value, ops[1].value);
 }
 
-/* Checks if vec_perm mask SEL is a constant equivalent to a shift of the first
-   vec_perm operand, assuming the second operand is a constant vector of zeroes.
-   Return the shift distance in bits if so, or NULL_RTX if the vec_perm is not a
-   shift.  */
+/* Check if vec_perm mask SEL is a constant equivalent to a shift of
+   the first vec_perm operand, assuming the second operand is a constant
+   vector of zeros.  Return the shift distance in bits if so, or NULL_RTX
+   if the vec_perm is not a shift.  MODE is the mode of the value being
+   shifted.  */
 static rtx
-shift_amt_for_vec_perm_mask (rtx sel)
+shift_amt_for_vec_perm_mask (machine_mode mode, const vec_perm_indices &sel)
 {
-  unsigned int i, first, nelt = GET_MODE_NUNITS (GET_MODE (sel));
-  unsigned int bitsize = GET_MODE_UNIT_BITSIZE (GET_MODE (sel));
-
-  if (GET_CODE (sel) != CONST_VECTOR)
+  unsigned int bitsize = GET_MODE_UNIT_BITSIZE (mode);
+  poly_int64 first = sel[0];
+  if (maybe_ge (sel[0], GET_MODE_NUNITS (mode)))
     return NULL_RTX;
 
-  first = INTVAL (CONST_VECTOR_ELT (sel, 0));
-  if (first >= nelt)
-    return NULL_RTX;
-  for (i = 1; i < nelt; i++)
+  if (!sel.series_p (0, 1, first, 1))
     {
-      int idx = INTVAL (CONST_VECTOR_ELT (sel, i));
-      unsigned int expected = i + first;
-      /* Indices into the second vector are all equivalent.  */
-      if (idx < 0 || (MIN (nelt, (unsigned) idx) != MIN (nelt, expected)))
+      unsigned int nelt;
+      if (!GET_MODE_NUNITS (mode).is_constant (&nelt))
        return NULL_RTX;
+      for (unsigned int i = 1; i < nelt; i++)
+       {
+         poly_int64 expected = i + first;
+         /* Indices into the second vector are all equivalent.  */
+         if (maybe_lt (sel[i], nelt)
+             ? maybe_ne (sel[i], expected)
+             : maybe_lt (expected, nelt))
+           return NULL_RTX;
+       }
     }
 
-  return GEN_INT (first * bitsize);
+  return gen_int_shift_amount (mode, first * bitsize);
 }
 
-/* A subroutine of expand_vec_perm for expanding one vec_perm insn.  */
+/* A subroutine of expand_vec_perm_var for expanding one vec_perm insn.  */
 
 static rtx
 expand_vec_perm_1 (enum insn_code icode, rtx target,
@@ -5356,6 +5483,8 @@ expand_vec_perm_1 (enum insn_code icode, rtx target,
   machine_mode smode = GET_MODE (sel);
   struct expand_operand ops[4];
 
+  gcc_assert (GET_MODE_CLASS (smode) == MODE_VECTOR_INT
+             || mode_for_int_vector (tmode).require () == smode);
   create_output_operand (&ops[0], target, tmode);
   create_input_operand (&ops[3], sel, smode);
 
@@ -5382,115 +5511,170 @@ expand_vec_perm_1 (enum insn_code icode, rtx target,
   return NULL_RTX;
 }
 
-/* Generate instructions for vec_perm optab given its mode
-   and three operands.  */
+/* Implement a permutation of vectors v0 and v1 using the permutation
+   vector in SEL and return the result.  Use TARGET to hold the result
+   if nonnull and convenient.
+
+   MODE is the mode of the vectors being permuted (V0 and V1).  SEL_MODE
+   is the TYPE_MODE associated with SEL, or BLKmode if SEL isn't known
+   to have a particular mode.  */
 
 rtx
-expand_vec_perm (machine_mode mode, rtx v0, rtx v1, rtx sel, rtx target)
+expand_vec_perm_const (machine_mode mode, rtx v0, rtx v1,
+                      const vec_perm_builder &sel, machine_mode sel_mode,
+                      rtx target)
 {
-  enum insn_code icode;
-  machine_mode qimode;
-  unsigned int i, w, e, u;
-  rtx tmp, sel_qi = NULL;
-  rtvec vec;
-
-  if (!target || GET_MODE (target) != mode)
+  if (!target || !register_operand (target, mode))
     target = gen_reg_rtx (mode);
 
-  w = GET_MODE_SIZE (mode);
-  e = GET_MODE_NUNITS (mode);
-  u = GET_MODE_UNIT_SIZE (mode);
-
   /* Set QIMODE to a different vector mode with byte elements.
      If no such mode, or if MODE already has byte elements, use VOIDmode.  */
-  qimode = VOIDmode;
-  if (GET_MODE_INNER (mode) != QImode)
-    {
-      qimode = mode_for_vector (QImode, w);
-      if (!VECTOR_MODE_P (qimode))
-       qimode = VOIDmode;
-    }
-
-  /* If the input is a constant, expand it specially.  */
-  gcc_assert (GET_MODE_CLASS (GET_MODE (sel)) == MODE_VECTOR_INT);
-  if (GET_CODE (sel) == CONST_VECTOR)
-    {
-      /* See if this can be handled with a vec_shr.  We only do this if the
-        second vector is all zeroes.  */
-      enum insn_code shift_code = optab_handler (vec_shr_optab, mode);
-      enum insn_code shift_code_qi = ((qimode != VOIDmode && qimode != mode)
-                                     ? optab_handler (vec_shr_optab, qimode)
-                                     : CODE_FOR_nothing);
-      rtx shift_amt = NULL_RTX;
-      if (v1 == CONST0_RTX (GET_MODE (v1))
-         && (shift_code != CODE_FOR_nothing
-             || shift_code_qi != CODE_FOR_nothing))
+  machine_mode qimode;
+  if (!qimode_for_vec_perm (mode).exists (&qimode))
+    qimode = VOIDmode;
+
+  rtx_insn *last = get_last_insn ();
+
+  bool single_arg_p = rtx_equal_p (v0, v1);
+  /* Always specify two input vectors here and leave the target to handle
+     cases in which the inputs are equal.  Not all backends can cope with
+     the single-input representation when testing for a double-input
+     target instruction.  */
+  vec_perm_indices indices (sel, 2, GET_MODE_NUNITS (mode));
+
+  /* See if this can be handled with a vec_shr.  We only do this if the
+     second vector is all zeroes.  */
+  insn_code shift_code = optab_handler (vec_shr_optab, mode);
+  insn_code shift_code_qi = ((qimode != VOIDmode && qimode != mode)
+                            ? optab_handler (vec_shr_optab, qimode)
+                            : CODE_FOR_nothing);
+
+  if (v1 == CONST0_RTX (GET_MODE (v1))
+      && (shift_code != CODE_FOR_nothing
+         || shift_code_qi != CODE_FOR_nothing))
+    {
+      rtx shift_amt = shift_amt_for_vec_perm_mask (mode, indices);
+      if (shift_amt)
        {
-         shift_amt = shift_amt_for_vec_perm_mask (sel);
-         if (shift_amt)
+         struct expand_operand ops[3];
+         if (shift_code != CODE_FOR_nothing)
            {
-             struct expand_operand ops[3];
-             if (shift_code != CODE_FOR_nothing)
-               {
-                 create_output_operand (&ops[0], target, mode);
-                 create_input_operand (&ops[1], v0, mode);
-                 create_convert_operand_from_type (&ops[2], shift_amt,
-                                                   sizetype);
-                 if (maybe_expand_insn (shift_code, 3, ops))
-                   return ops[0].value;
-               }
-             if (shift_code_qi != CODE_FOR_nothing)
-               {
-                 tmp = gen_reg_rtx (qimode);
-                 create_output_operand (&ops[0], tmp, qimode);
-                 create_input_operand (&ops[1], gen_lowpart (qimode, v0),
-                                       qimode);
-                 create_convert_operand_from_type (&ops[2], shift_amt,
-                                                   sizetype);
-                 if (maybe_expand_insn (shift_code_qi, 3, ops))
-                   return gen_lowpart (mode, ops[0].value);
-               }
+             create_output_operand (&ops[0], target, mode);
+             create_input_operand (&ops[1], v0, mode);
+             create_convert_operand_from_type (&ops[2], shift_amt, sizetype);
+             if (maybe_expand_insn (shift_code, 3, ops))
+               return ops[0].value;
+           }
+         if (shift_code_qi != CODE_FOR_nothing)
+           {
+             rtx tmp = gen_reg_rtx (qimode);
+             create_output_operand (&ops[0], tmp, qimode);
+             create_input_operand (&ops[1], gen_lowpart (qimode, v0), qimode);
+             create_convert_operand_from_type (&ops[2], shift_amt, sizetype);
+             if (maybe_expand_insn (shift_code_qi, 3, ops))
+               return gen_lowpart (mode, ops[0].value);
            }
        }
+    }
+
+  if (targetm.vectorize.vec_perm_const != NULL)
+    {
+      v0 = force_reg (mode, v0);
+      if (single_arg_p)
+       v1 = v0;
+      else
+       v1 = force_reg (mode, v1);
+
+      if (targetm.vectorize.vec_perm_const (mode, target, v0, v1, indices))
+       return target;
+    }
+
+  /* Fall back to a constant byte-based permutation.  */
+  vec_perm_indices qimode_indices;
+  rtx target_qi = NULL_RTX, v0_qi = NULL_RTX, v1_qi = NULL_RTX;
+  if (qimode != VOIDmode)
+    {
+      qimode_indices.new_expanded_vector (indices, GET_MODE_UNIT_SIZE (mode));
+      target_qi = gen_reg_rtx (qimode);
+      v0_qi = gen_lowpart (qimode, v0);
+      v1_qi = gen_lowpart (qimode, v1);
+      if (targetm.vectorize.vec_perm_const != NULL
+         && targetm.vectorize.vec_perm_const (qimode, target_qi, v0_qi,
+                                              v1_qi, qimode_indices))
+       return gen_lowpart (mode, target_qi);
+    }
+
+  /* Otherwise expand as a fully variable permuation.  */
+
+  /* The optabs are only defined for selectors with the same width
+     as the values being permuted.  */
+  machine_mode required_sel_mode;
+  if (!mode_for_int_vector (mode).exists (&required_sel_mode)
+      || !VECTOR_MODE_P (required_sel_mode))
+    {
+      delete_insns_since (last);
+      return NULL_RTX;
+    }
+
+  /* We know that it is semantically valid to treat SEL as having SEL_MODE.
+     If that isn't the mode we want then we need to prove that using
+     REQUIRED_SEL_MODE is OK.  */
+  if (sel_mode != required_sel_mode)
+    {
+      if (!selector_fits_mode_p (required_sel_mode, indices))
+       {
+         delete_insns_since (last);
+         return NULL_RTX;
+       }
+      sel_mode = required_sel_mode;
+    }
 
-      icode = direct_optab_handler (vec_perm_const_optab, mode);
+  insn_code icode = direct_optab_handler (vec_perm_optab, mode);
+  if (icode != CODE_FOR_nothing)
+    {
+      rtx sel_rtx = vec_perm_indices_to_rtx (sel_mode, indices);
+      rtx tmp = expand_vec_perm_1 (icode, target, v0, v1, sel_rtx);
+      if (tmp)
+       return tmp;
+    }
+
+  if (qimode != VOIDmode
+      && selector_fits_mode_p (qimode, qimode_indices))
+    {
+      icode = direct_optab_handler (vec_perm_optab, qimode);
       if (icode != CODE_FOR_nothing)
        {
-         tmp = expand_vec_perm_1 (icode, target, v0, v1, sel);
+         rtx sel_qi = vec_perm_indices_to_rtx (qimode, qimode_indices);
+         rtx tmp = expand_vec_perm_1 (icode, target_qi, v0_qi, v1_qi, sel_qi);
          if (tmp)
-           return tmp;
+           return gen_lowpart (mode, tmp);
        }
+    }
 
-      /* Fall back to a constant byte-based permutation.  */
-      if (qimode != VOIDmode)
-       {
-         vec = rtvec_alloc (w);
-         for (i = 0; i < e; ++i)
-           {
-             unsigned int j, this_e;
+  delete_insns_since (last);
+  return NULL_RTX;
+}
 
-             this_e = INTVAL (CONST_VECTOR_ELT (sel, i));
-             this_e &= 2 * e - 1;
-             this_e *= u;
+/* Implement a permutation of vectors v0 and v1 using the permutation
+   vector in SEL and return the result.  Use TARGET to hold the result
+   if nonnull and convenient.
 
-             for (j = 0; j < u; ++j)
-               RTVEC_ELT (vec, i * u + j) = GEN_INT (this_e + j);
-           }
-         sel_qi = gen_rtx_CONST_VECTOR (qimode, vec);
+   MODE is the mode of the vectors being permuted (V0 and V1).
+   SEL must have the integer equivalent of MODE and is known to be
+   unsuitable for permutes with a constant permutation vector.  */
 
-         icode = direct_optab_handler (vec_perm_const_optab, qimode);
-         if (icode != CODE_FOR_nothing)
-           {
-             tmp = mode != qimode ? gen_reg_rtx (qimode) : target;
-             tmp = expand_vec_perm_1 (icode, tmp, gen_lowpart (qimode, v0),
-                                      gen_lowpart (qimode, v1), sel_qi);
-             if (tmp)
-               return gen_lowpart (mode, tmp);
-           }
-       }
-    }
+rtx
+expand_vec_perm_var (machine_mode mode, rtx v0, rtx v1, rtx sel, rtx target)
+{
+  enum insn_code icode;
+  unsigned int i, u;
+  rtx tmp, sel_qi;
+
+  u = GET_MODE_UNIT_SIZE (mode);
+
+  if (!target || GET_MODE (target) != mode)
+    target = gen_reg_rtx (mode);
 
-  /* Otherwise expand as a fully variable permuation.  */
   icode = direct_optab_handler (vec_perm_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
@@ -5501,50 +5685,47 @@ expand_vec_perm (machine_mode mode, rtx v0, rtx v1, rtx sel, rtx target)
 
   /* As a special case to aid several targets, lower the element-based
      permutation to a byte-based permutation and try again.  */
-  if (qimode == VOIDmode)
+  machine_mode qimode;
+  if (!qimode_for_vec_perm (mode).exists (&qimode)
+      || maybe_gt (GET_MODE_NUNITS (qimode), GET_MODE_MASK (QImode) + 1))
     return NULL_RTX;
   icode = direct_optab_handler (vec_perm_optab, qimode);
   if (icode == CODE_FOR_nothing)
     return NULL_RTX;
 
-  if (sel_qi == NULL)
-    {
-      /* Multiply each element by its byte size.  */
-      machine_mode selmode = GET_MODE (sel);
-      if (u == 2)
-       sel = expand_simple_binop (selmode, PLUS, sel, sel,
-                                  NULL, 0, OPTAB_DIRECT);
-      else
-       sel = expand_simple_binop (selmode, ASHIFT, sel,
-                                  GEN_INT (exact_log2 (u)),
-                                  NULL, 0, OPTAB_DIRECT);
-      gcc_assert (sel != NULL);
-
-      /* Broadcast the low byte each element into each of its bytes.  */
-      vec = rtvec_alloc (w);
-      for (i = 0; i < w; ++i)
-       {
-         int this_e = i / u * u;
-         if (BYTES_BIG_ENDIAN)
-           this_e += u - 1;
-         RTVEC_ELT (vec, i) = GEN_INT (this_e);
-       }
-      tmp = gen_rtx_CONST_VECTOR (qimode, vec);
-      sel = gen_lowpart (qimode, sel);
-      sel = expand_vec_perm (qimode, sel, sel, tmp, NULL);
-      gcc_assert (sel != NULL);
-
-      /* Add the byte offset to each byte element.  */
-      /* Note that the definition of the indicies here is memory ordering,
-        so there should be no difference between big and little endian.  */
-      vec = rtvec_alloc (w);
-      for (i = 0; i < w; ++i)
-       RTVEC_ELT (vec, i) = GEN_INT (i % u);
-      tmp = gen_rtx_CONST_VECTOR (qimode, vec);
-      sel_qi = expand_simple_binop (qimode, PLUS, sel, tmp,
-                                   sel, 0, OPTAB_DIRECT);
-      gcc_assert (sel_qi != NULL);
-    }
+  /* Multiply each element by its byte size.  */
+  machine_mode selmode = GET_MODE (sel);
+  if (u == 2)
+    sel = expand_simple_binop (selmode, PLUS, sel, sel,
+                              NULL, 0, OPTAB_DIRECT);
+  else
+    sel = expand_simple_binop (selmode, ASHIFT, sel,
+                              gen_int_shift_amount (selmode, exact_log2 (u)),
+                              NULL, 0, OPTAB_DIRECT);
+  gcc_assert (sel != NULL);
+
+  /* Broadcast the low byte each element into each of its bytes.
+     The encoding has U interleaved stepped patterns, one for each
+     byte of an element.  */
+  vec_perm_builder const_sel (GET_MODE_SIZE (mode), u, 3);
+  unsigned int low_byte_in_u = BYTES_BIG_ENDIAN ? u - 1 : 0;
+  for (i = 0; i < 3; ++i)
+    for (unsigned int j = 0; j < u; ++j)
+      const_sel.quick_push (i * u + low_byte_in_u);
+  sel = gen_lowpart (qimode, sel);
+  sel = expand_vec_perm_const (qimode, sel, sel, const_sel, qimode, NULL);
+  gcc_assert (sel != NULL);
+
+  /* Add the byte offset to each byte element.  */
+  /* Note that the definition of the indicies here is memory ordering,
+     so there should be no difference between big and little endian.  */
+  rtx_vector_builder byte_indices (qimode, u, 1);
+  for (i = 0; i < u; ++i)
+    byte_indices.quick_push (GEN_INT (i));
+  tmp = byte_indices.build ();
+  sel_qi = expand_simple_binop (qimode, PLUS, sel, tmp,
+                               sel, 0, OPTAB_DIRECT);
+  gcc_assert (sel_qi != NULL);
 
   tmp = mode != qimode ? gen_reg_rtx (qimode) : target;
   tmp = expand_vec_perm_1 (icode, tmp, gen_lowpart (qimode, v0),
@@ -5629,8 +5810,9 @@ expand_vec_cond_expr (tree vec_cond_type, tree op0, tree op1, tree op2,
   unsignedp = TYPE_UNSIGNED (TREE_TYPE (op0a));
 
 
-  gcc_assert (GET_MODE_SIZE (mode) == GET_MODE_SIZE (cmp_op_mode)
-             && GET_MODE_NUNITS (mode) == GET_MODE_NUNITS (cmp_op_mode));
+  gcc_assert (known_eq (GET_MODE_SIZE (mode), GET_MODE_SIZE (cmp_op_mode))
+             && known_eq (GET_MODE_NUNITS (mode),
+                          GET_MODE_NUNITS (cmp_op_mode)));
 
   icode = get_vcond_icode (mode, cmp_op_mode, unsignedp);
   if (icode == CODE_FOR_nothing)
@@ -5656,6 +5838,27 @@ expand_vec_cond_expr (tree vec_cond_type, tree op0, tree op1, tree op2,
   return ops[0].value;
 }
 
+/* Generate VEC_SERIES_EXPR <OP0, OP1>, returning a value of mode VMODE.
+   Use TARGET for the result if nonnull and convenient.  */
+
+rtx
+expand_vec_series_expr (machine_mode vmode, rtx op0, rtx op1, rtx target)
+{
+  struct expand_operand ops[3];
+  enum insn_code icode;
+  machine_mode emode = GET_MODE_INNER (vmode);
+
+  icode = direct_optab_handler (vec_series_optab, vmode);
+  gcc_assert (icode != CODE_FOR_nothing);
+
+  create_output_operand (&ops[0], target, vmode);
+  create_input_operand (&ops[1], op0, emode);
+  create_input_operand (&ops[2], op1, emode);
+
+  expand_insn (icode, 3, ops);
+  return ops[0].value;
+}
+
 /* Generate insns for a vector comparison into a mask.  */
 
 rtx
@@ -5704,11 +5907,10 @@ expand_mult_highpart (machine_mode mode, rtx op0, rtx op1,
 {
   struct expand_operand eops[3];
   enum insn_code icode;
-  int method, i, nunits;
+  int method, i;
   machine_mode wmode;
-  rtx m1, m2, perm;
+  rtx m1, m2;
   optab tab1, tab2;
-  rtvec v;
 
   method = can_mult_highpart_p (mode, uns_p);
   switch (method)
@@ -5734,10 +5936,10 @@ expand_mult_highpart (machine_mode mode, rtx op0, rtx op1,
     }
 
   icode = optab_handler (tab1, mode);
-  nunits = GET_MODE_NUNITS (mode);
   wmode = insn_data[icode].operand[0].mode;
-  gcc_checking_assert (2 * GET_MODE_NUNITS (wmode) == nunits);
-  gcc_checking_assert (GET_MODE_SIZE (wmode) == GET_MODE_SIZE (mode));
+  gcc_checking_assert (known_eq (2 * GET_MODE_NUNITS (wmode),
+                                GET_MODE_NUNITS (mode)));
+  gcc_checking_assert (known_eq (GET_MODE_SIZE (wmode), GET_MODE_SIZE (mode)));
 
   create_output_operand (&eops[0], gen_reg_rtx (wmode), wmode);
   create_input_operand (&eops[1], op0, mode);
@@ -5751,21 +5953,24 @@ expand_mult_highpart (machine_mode mode, rtx op0, rtx op1,
   expand_insn (optab_handler (tab2, mode), 3, eops);
   m2 = gen_lowpart (mode, eops[0].value);
 
-  v = rtvec_alloc (nunits);
+  vec_perm_builder sel;
   if (method == 2)
     {
-      for (i = 0; i < nunits; ++i)
-       RTVEC_ELT (v, i) = GEN_INT (!BYTES_BIG_ENDIAN + (i & ~1)
-                                   + ((i & 1) ? nunits : 0));
+      /* The encoding has 2 interleaved stepped patterns.  */
+      sel.new_vector (GET_MODE_NUNITS (mode), 2, 3);
+      for (i = 0; i < 6; ++i)
+       sel.quick_push (!BYTES_BIG_ENDIAN + (i & ~1)
+                       + ((i & 1) ? GET_MODE_NUNITS (mode) : 0));
     }
   else
     {
-      for (i = 0; i < nunits; ++i)
-       RTVEC_ELT (v, i) = GEN_INT (2 * i + (BYTES_BIG_ENDIAN ? 0 : 1));
+      /* The encoding has a single interleaved stepped pattern.  */
+      sel.new_vector (GET_MODE_NUNITS (mode), 1, 3);
+      for (i = 0; i < 3; ++i)
+       sel.quick_push (2 * i + (BYTES_BIG_ENDIAN ? 0 : 1));
     }
-  perm = gen_rtx_CONST_VECTOR (mode, v);
 
-  return expand_vec_perm (mode, m1, m2, perm, target);
+  return expand_vec_perm_const (mode, m1, m2, sel, BLKmode, target);
 }
 \f
 /* Helper function to find the MODE_CC set in a sync_compare_and_swap
@@ -5911,7 +6116,7 @@ maybe_emit_sync_lock_test_and_set (rtx target, rtx mem, rtx val,
 
          addr = convert_memory_address (ptr_mode, XEXP (mem, 0));
          return emit_library_call_value (libfunc, NULL_RTX, LCT_NORMAL,
-                                         mode, 2, addr, ptr_mode,
+                                         mode, addr, ptr_mode,
                                          val, mode);
        }
     }
@@ -6219,7 +6424,7 @@ expand_atomic_compare_and_swap (rtx *ptarget_bool, rtx *ptarget_oval,
     {
       rtx addr = convert_memory_address (ptr_mode, XEXP (mem, 0));
       rtx target = emit_library_call_value (libfunc, NULL_RTX, LCT_NORMAL,
-                                           mode, 3, addr, ptr_mode,
+                                           mode, addr, ptr_mode,
                                            expected, mode, desired, mode);
       emit_move_insn (target_oval, target);
 
@@ -6245,10 +6450,10 @@ expand_atomic_compare_and_swap (rtx *ptarget_bool, rtx *ptarget_oval,
   return true;
 }
 
-/* Generate asm volatile("" : : : "memory") as the memory barrier.  */
+/* Generate asm volatile("" : : : "memory") as the memory blockage.  */
 
 static void
-expand_asm_memory_barrier (void)
+expand_asm_memory_blockage (void)
 {
   rtx asm_op, clob;
 
@@ -6264,6 +6469,17 @@ expand_asm_memory_barrier (void)
   emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, asm_op, clob)));
 }
 
+/* Do not propagate memory accesses across this point.  */
+
+static void
+expand_memory_blockage (void)
+{
+  if (targetm.have_memory_blockage ())
+    emit_insn (targetm.gen_memory_blockage ());
+  else
+    expand_asm_memory_blockage ();
+}
+
 /* This routine will either emit the mem_thread_fence pattern or issue a 
    sync_synchronize to generate a fence for memory model MEMMODEL.  */
 
@@ -6275,32 +6491,25 @@ expand_mem_thread_fence (enum memmodel model)
   if (targetm.have_mem_thread_fence ())
     {
       emit_insn (targetm.gen_mem_thread_fence (GEN_INT (model)));
-      expand_asm_memory_barrier ();
+      expand_memory_blockage ();
     }
   else if (targetm.have_memory_barrier ())
     emit_insn (targetm.gen_memory_barrier ());
   else if (synchronize_libfunc != NULL_RTX)
-    emit_library_call (synchronize_libfunc, LCT_NORMAL, VOIDmode, 0);
+    emit_library_call (synchronize_libfunc, LCT_NORMAL, VOIDmode);
   else
-    expand_asm_memory_barrier ();
+    expand_memory_blockage ();
 }
 
-/* This routine will either emit the mem_signal_fence pattern or issue a 
-   sync_synchronize to generate a fence for memory model MEMMODEL.  */
+/* Emit a signal fence with given memory model.  */
 
 void
 expand_mem_signal_fence (enum memmodel model)
 {
-  if (targetm.have_mem_signal_fence ())
-    emit_insn (targetm.gen_mem_signal_fence (GEN_INT (model)));
-  else if (!is_mm_relaxed (model))
-    {
-      /* By default targets are coherent between a thread and the signal
-        handler running on the same thread.  Thus this really becomes a
-        compiler barrier, in that stores must not be sunk past
-        (or raised above) a given point.  */
-      expand_asm_memory_barrier ();
-    }
+  /* No machine barrier is required to implement a signal fence, but
+     a compiler memory barrier must be issued, except for relaxed MM.  */
+  if (!is_mm_relaxed (model))
+    expand_memory_blockage ();
 }
 
 /* This function expands the atomic load operation:
@@ -6320,12 +6529,20 @@ expand_atomic_load (rtx target, rtx mem, enum memmodel model)
   if (icode != CODE_FOR_nothing)
     {
       struct expand_operand ops[3];
+      rtx_insn *last = get_last_insn ();
+      if (is_mm_seq_cst (model))
+       expand_memory_blockage ();
 
       create_output_operand (&ops[0], target, mode);
       create_fixed_operand (&ops[1], mem);
       create_integer_operand (&ops[2], model);
       if (maybe_expand_insn (icode, 3, ops))
-       return ops[0].value;
+       {
+         if (!is_mm_relaxed (model))
+           expand_memory_blockage ();
+         return ops[0].value;
+       }
+      delete_insns_since (last);
     }
 
   /* If the size of the object is greater than word size on this target,
@@ -6333,7 +6550,7 @@ expand_atomic_load (rtx target, rtx mem, enum memmodel model)
      emulate a load with a compare-and-swap operation, but the store that
      doing this could result in would be incorrect if this is a volatile
      atomic load or targetting read-only-mapped memory.  */
-  if (GET_MODE_PRECISION (mode) > BITS_PER_WORD)
+  if (maybe_gt (GET_MODE_PRECISION (mode), BITS_PER_WORD))
     /* If there is no atomic load, leave the library call.  */
     return NULL_RTX;
 
@@ -6370,11 +6587,19 @@ expand_atomic_store (rtx mem, rtx val, enum memmodel model, bool use_release)
   icode = direct_optab_handler (atomic_store_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
+      rtx_insn *last = get_last_insn ();
+      if (!is_mm_relaxed (model))
+       expand_memory_blockage ();
       create_fixed_operand (&ops[0], mem);
       create_input_operand (&ops[1], val, mode);
       create_integer_operand (&ops[2], model);
       if (maybe_expand_insn (icode, 3, ops))
-       return const0_rtx;
+       {
+         if (is_mm_seq_cst (model))
+           expand_memory_blockage ();
+         return const0_rtx;
+       }
+      delete_insns_since (last);
     }
 
   /* If using __sync_lock_release is a viable alternative, try it.
@@ -6399,7 +6624,7 @@ expand_atomic_store (rtx mem, rtx val, enum memmodel model, bool use_release)
 
   /* If the size of the object is greater than word size on this target,
      a default store will not be atomic.  */
-  if (GET_MODE_PRECISION (mode) > BITS_PER_WORD)
+  if (maybe_gt (GET_MODE_PRECISION (mode), BITS_PER_WORD))
     {
       /* If loads are atomic or we are called to provide a __sync builtin,
         we can try a atomic_exchange and throw away the result.  Otherwise,
@@ -6787,7 +7012,7 @@ expand_atomic_fetch_op (rtx target, rtx mem, rtx val, enum rtx_code code,
        {
          rtx addr = convert_memory_address (ptr_mode, XEXP (mem, 0));
          result = emit_library_call_value (libfunc, NULL, LCT_NORMAL, mode,
-                                           2, addr, ptr_mode, val, mode);
+                                           addr, ptr_mode, val, mode);
 
          if (!unused_result && fixup)
            result = expand_simple_binop (mode, code, result, val, target,
@@ -6862,15 +7087,31 @@ bool
 valid_multiword_target_p (rtx target)
 {
   machine_mode mode;
-  int i;
+  int i, size;
 
   mode = GET_MODE (target);
-  for (i = 0; i < GET_MODE_SIZE (mode); i += UNITS_PER_WORD)
+  if (!GET_MODE_SIZE (mode).is_constant (&size))
+    return false;
+  for (i = 0; i < size; i += UNITS_PER_WORD)
     if (!validate_subreg (word_mode, mode, target, i))
       return false;
   return true;
 }
 
+/* Make OP describe an input operand that has value INTVAL and that has
+   no inherent mode.  This function should only be used for operands that
+   are always expand-time constants.  The backend may request that INTVAL
+   be copied into a different kind of rtx, but it must specify the mode
+   of that rtx if so.  */
+
+void
+create_integer_operand (struct expand_operand *op, poly_int64 intval)
+{
+  create_expand_operand (op, EXPAND_INTEGER,
+                        gen_int_mode (intval, MAX_MODE_INT),
+                        VOIDmode, false, intval);
+}
+
 /* Like maybe_legitimize_operand, but do not change the code of the
    current rtx value.  */
 
@@ -6977,14 +7218,19 @@ maybe_legitimize_operand (enum insn_code icode, unsigned int opno,
       goto input;
 
     case EXPAND_ADDRESS:
-      gcc_assert (mode != VOIDmode);
-      op->value = convert_memory_address (mode, op->value);
+      op->value = convert_memory_address (as_a <scalar_int_mode> (mode),
+                                         op->value);
       goto input;
 
     case EXPAND_INTEGER:
       mode = insn_data[(int) icode].operand[opno].mode;
-      if (mode != VOIDmode && const_int_operand (op->value, mode))
-       goto input;
+      if (mode != VOIDmode
+         && known_eq (trunc_int_for_mode (op->int_value, mode),
+                      op->int_value))
+       {
+         op->value = gen_int_mode (op->int_value, mode);
+         goto input;
+       }
       break;
     }
   return insn_operand_matches (icode, opno, op->value);
@@ -7002,6 +7248,44 @@ create_convert_operand_from_type (struct expand_operand *op,
                               TYPE_UNSIGNED (type));
 }
 
+/* Return true if the requirements on operands OP1 and OP2 of instruction
+   ICODE are similar enough for the result of legitimizing OP1 to be
+   reusable for OP2.  OPNO1 and OPNO2 are the operand numbers associated
+   with OP1 and OP2 respectively.  */
+
+static inline bool
+can_reuse_operands_p (enum insn_code icode,
+                     unsigned int opno1, unsigned int opno2,
+                     const struct expand_operand *op1,
+                     const struct expand_operand *op2)
+{
+  /* Check requirements that are common to all types.  */
+  if (op1->type != op2->type
+      || op1->mode != op2->mode
+      || (insn_data[(int) icode].operand[opno1].mode
+         != insn_data[(int) icode].operand[opno2].mode))
+    return false;
+
+  /* Check the requirements for specific types.  */
+  switch (op1->type)
+    {
+    case EXPAND_OUTPUT:
+      /* Outputs must remain distinct.  */
+      return false;
+
+    case EXPAND_FIXED:
+    case EXPAND_INPUT:
+    case EXPAND_ADDRESS:
+    case EXPAND_INTEGER:
+      return true;
+
+    case EXPAND_CONVERT_TO:
+    case EXPAND_CONVERT_FROM:
+      return op1->unsigned_p == op2->unsigned_p;
+    }
+  gcc_unreachable ();
+}
+
 /* Try to make operands [OPS, OPS + NOPS) match operands [OPNO, OPNO + NOPS)
    of instruction ICODE.  Return true on success, leaving the new operand
    values in the OPS themselves.  Emit no code on failure.  */
@@ -7010,16 +7294,36 @@ bool
 maybe_legitimize_operands (enum insn_code icode, unsigned int opno,
                           unsigned int nops, struct expand_operand *ops)
 {
-  rtx_insn *last;
-  unsigned int i;
+  rtx_insn *last = get_last_insn ();
+  rtx *orig_values = XALLOCAVEC (rtx, nops);
+  for (unsigned int i = 0; i < nops; i++)
+    {
+      orig_values[i] = ops[i].value;
+
+      /* First try reusing the result of an earlier legitimization.
+        This avoids duplicate rtl and ensures that tied operands
+        remain tied.
+
+        This search is linear, but NOPS is bounded at compile time
+        to a small number (current a single digit).  */
+      unsigned int j = 0;
+      for (; j < i; ++j)
+       if (can_reuse_operands_p (icode, opno + j, opno + i, &ops[j], &ops[i])
+           && rtx_equal_p (orig_values[j], orig_values[i])
+           && ops[j].value
+           && insn_operand_matches (icode, opno + i, ops[j].value))
+         {
+           ops[i].value = copy_rtx (ops[j].value);
+           break;
+         }
 
-  last = get_last_insn ();
-  for (i = 0; i < nops; i++)
-    if (!maybe_legitimize_operand (icode, opno + i, &ops[i]))
-      {
-       delete_insns_since (last);
-       return false;
-      }
+      /* Otherwise try legitimizing the operand on its own.  */
+      if (j == i && !maybe_legitimize_operand (icode, opno + i, &ops[i]))
+       {
+         delete_insns_since (last);
+         return false;
+       }
+    }
   return true;
 }