OSDN Git Service

* g++.dg/other/static11.C: Use cleanup-rtl-dump.
[pf3gnuchains/gcc-fork.git] / gcc / optabs.c
index 3085b85..ecaff98 100644 (file)
@@ -1,6 +1,6 @@
 /* Expand the basic unary and binary arithmetic operations, for GNU compiler.
    Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -43,6 +43,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "ggc.h"
 #include "real.h"
 #include "basic-block.h"
+#include "target.h"
 
 /* Each optab contains info on how this target machine
    can perform a particular operation
@@ -57,13 +58,8 @@ optab optab_table[OTI_MAX];
 
 rtx libfunc_table[LTI_MAX];
 
-/* Tables of patterns for extending one integer mode to another.  */
-enum insn_code extendtab[MAX_MACHINE_MODE][MAX_MACHINE_MODE][2];
-
-/* Tables of patterns for converting between fixed and floating point.  */
-enum insn_code fixtab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
-enum insn_code fixtrunctab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
-enum insn_code floattab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+/* Tables of patterns for converting one mode to another.  */
+convert_optab convert_optab_table[CTI_MAX];
 
 /* Contains the optab used for each rtx code.  */
 optab code_to_optab[NUM_RTX_CODE + 1];
@@ -88,6 +84,12 @@ enum insn_code setcc_gen_code[NUM_RTX_CODE];
 enum insn_code movcc_gen_code[NUM_MACHINE_MODES];
 #endif
 
+/* Indexed by the machine mode, gives the insn code for vector conditional
+   operation.  */
+
+enum insn_code vcond_gen_code[NUM_MACHINE_MODES];
+enum insn_code vcondu_gen_code[NUM_MACHINE_MODES];
+
 /* The insn generating function can not take an rtx_code argument.
    TRAP_RTX is used as an rtx argument.  Its code is replaced with
    the code to be used in the trap insn and all other fields are ignored.  */
@@ -96,35 +98,32 @@ static GTY(()) rtx trap_rtx;
 static int add_equal_note (rtx, rtx, enum rtx_code, rtx, rtx);
 static rtx widen_operand (rtx, enum machine_mode, enum machine_mode, int,
                          int);
-static int expand_cmplxdiv_straight (rtx, rtx, rtx, rtx, rtx, rtx,
-                                    enum machine_mode, int,
-                                    enum optab_methods, enum mode_class,
-                                    optab);
-static int expand_cmplxdiv_wide (rtx, rtx, rtx, rtx, rtx, rtx,
-                                enum machine_mode, int, enum optab_methods,
-                                enum mode_class, optab);
 static void prepare_cmp_insn (rtx *, rtx *, enum rtx_code *, rtx,
                              enum machine_mode *, int *,
                              enum can_compare_purpose);
 static enum insn_code can_fix_p (enum machine_mode, enum machine_mode, int,
                                 int *);
 static enum insn_code can_float_p (enum machine_mode, enum machine_mode, int);
-static rtx ftruncify (rtx);
 static optab new_optab (void);
+static convert_optab new_convert_optab (void);
 static inline optab init_optab (enum rtx_code);
 static inline optab init_optabv (enum rtx_code);
+static inline convert_optab init_convert_optab (enum rtx_code);
 static void init_libfuncs (optab, int, int, const char *, int);
 static void init_integral_libfuncs (optab, const char *, int);
 static void init_floating_libfuncs (optab, const char *, int);
+static void init_interclass_conv_libfuncs (convert_optab, const char *,
+                                          enum mode_class, enum mode_class);
+static void init_intraclass_conv_libfuncs (convert_optab, const char *,
+                                          enum mode_class, bool);
 static void emit_cmp_and_jump_insn_1 (rtx, rtx, enum machine_mode,
                                      enum rtx_code, int, rtx);
 static void prepare_float_lib_cmp (rtx *, rtx *, enum rtx_code *,
                                   enum machine_mode *, int *);
-static rtx expand_vector_binop (enum machine_mode, optab, rtx, rtx, rtx, int,
-                               enum optab_methods);
-static rtx expand_vector_unop (enum machine_mode, optab, rtx, rtx, int);
 static rtx widen_clz (enum machine_mode, rtx, rtx);
 static rtx expand_parity (enum machine_mode, rtx, rtx);
+static enum rtx_code get_rtx_code (enum tree_code, bool);
+static rtx vector_compare_rtx (tree, bool, enum insn_code);
 
 #ifndef HAVE_conditional_trap
 #define HAVE_conditional_trap 0
@@ -152,8 +151,11 @@ add_equal_note (rtx insns, rtx target, enum rtx_code code, rtx op0, rtx op1)
       || NEXT_INSN (insns) == NULL_RTX)
     abort ();
 
-  if (GET_RTX_CLASS (code) != '1' && GET_RTX_CLASS (code) != '2'
-      && GET_RTX_CLASS (code) != 'c' && GET_RTX_CLASS (code) != '<')
+  if (GET_RTX_CLASS (code) != RTX_COMM_ARITH
+      && GET_RTX_CLASS (code) != RTX_BIN_ARITH
+      && GET_RTX_CLASS (code) != RTX_COMM_COMPARE
+      && GET_RTX_CLASS (code) != RTX_COMPARE
+      && GET_RTX_CLASS (code) != RTX_UNARY)
     return 1;
 
   if (GET_CODE (target) == ZERO_EXTRACT)
@@ -189,7 +191,7 @@ add_equal_note (rtx insns, rtx target, enum rtx_code code, rtx op0, rtx op1)
        }
     }
 
-  if (GET_RTX_CLASS (code) == '1')
+  if (GET_RTX_CLASS (code) == RTX_UNARY)
     note = gen_rtx_fmt_e (code, GET_MODE (target), copy_rtx (op0));
   else
     note = gen_rtx_fmt_ee (code, GET_MODE (target), copy_rtx (op0), copy_rtx (op1));
@@ -237,391 +239,683 @@ widen_operand (rtx op, enum machine_mode mode, enum machine_mode oldmode,
   return result;
 }
 \f
-/* Generate code to perform a straightforward complex divide.  */
-
-static int
-expand_cmplxdiv_straight (rtx real0, rtx real1, rtx imag0, rtx imag1,
-                         rtx realr, rtx imagr, enum machine_mode submode,
-                         int unsignedp, enum optab_methods methods,
-                         enum mode_class class, optab binoptab)
+/* Return the optab used for computing the operation given by
+   the tree code, CODE.  This function is not always usable (for
+   example, it cannot give complete results for multiplication
+   or division) but probably ought to be relied on more widely
+   throughout the expander.  */
+optab
+optab_for_tree_code (enum tree_code code, tree type)
 {
-  rtx divisor;
-  rtx real_t, imag_t;
-  rtx temp1, temp2;
-  rtx res;
-  optab this_add_optab = add_optab;
-  optab this_sub_optab = sub_optab;
-  optab this_neg_optab = neg_optab;
-  optab this_mul_optab = smul_optab;
-
-  if (binoptab == sdivv_optab)
+  bool trapv;
+  switch (code)
     {
-      this_add_optab = addv_optab;
-      this_sub_optab = subv_optab;
-      this_neg_optab = negv_optab;
-      this_mul_optab = smulv_optab;
-    }
+    case BIT_AND_EXPR:
+      return and_optab;
 
-  /* Don't fetch these from memory more than once.  */
-  real0 = force_reg (submode, real0);
-  real1 = force_reg (submode, real1);
+    case BIT_IOR_EXPR:
+      return ior_optab;
 
-  if (imag0 != 0)
-    imag0 = force_reg (submode, imag0);
+    case BIT_NOT_EXPR:
+      return one_cmpl_optab;
 
-  imag1 = force_reg (submode, imag1);
+    case BIT_XOR_EXPR:
+      return xor_optab;
 
-  /* Divisor: c*c + d*d.  */
-  temp1 = expand_binop (submode, this_mul_optab, real1, real1,
-                       NULL_RTX, unsignedp, methods);
+    case TRUNC_MOD_EXPR:
+    case CEIL_MOD_EXPR:
+    case FLOOR_MOD_EXPR:
+    case ROUND_MOD_EXPR:
+      return TYPE_UNSIGNED (type) ? umod_optab : smod_optab;
 
-  temp2 = expand_binop (submode, this_mul_optab, imag1, imag1,
-                       NULL_RTX, unsignedp, methods);
+    case RDIV_EXPR:
+    case TRUNC_DIV_EXPR:
+    case CEIL_DIV_EXPR:
+    case FLOOR_DIV_EXPR:
+    case ROUND_DIV_EXPR:
+    case EXACT_DIV_EXPR:
+      return TYPE_UNSIGNED (type) ? udiv_optab : sdiv_optab;
 
-  if (temp1 == 0 || temp2 == 0)
-    return 0;
+    case LSHIFT_EXPR:
+      return ashl_optab;
 
-  divisor = expand_binop (submode, this_add_optab, temp1, temp2,
-                         NULL_RTX, unsignedp, methods);
-  if (divisor == 0)
-    return 0;
+    case RSHIFT_EXPR:
+      return TYPE_UNSIGNED (type) ? lshr_optab : ashr_optab;
 
-  if (imag0 == 0)
-    {
-      /* Mathematically, ((a)(c-id))/divisor.  */
-      /* Computationally, (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)).  */
+    case LROTATE_EXPR:
+      return rotl_optab;
+
+    case RROTATE_EXPR:
+      return rotr_optab;
 
-      /* Calculate the dividend.  */
-      real_t = expand_binop (submode, this_mul_optab, real0, real1,
-                            NULL_RTX, unsignedp, methods);
+    case MAX_EXPR:
+      return TYPE_UNSIGNED (type) ? umax_optab : smax_optab;
 
-      imag_t = expand_binop (submode, this_mul_optab, real0, imag1,
-                            NULL_RTX, unsignedp, methods);
+    case MIN_EXPR:
+      return TYPE_UNSIGNED (type) ? umin_optab : smin_optab;
 
-      if (real_t == 0 || imag_t == 0)
-       return 0;
+    case REALIGN_LOAD_EXPR:
+      return vec_realign_load_optab;
 
-      imag_t = expand_unop (submode, this_neg_optab, imag_t,
-                           NULL_RTX, unsignedp);
+    default:
+      break;
     }
-  else
+
+  trapv = flag_trapv && INTEGRAL_TYPE_P (type) && !TYPE_UNSIGNED (type);
+  switch (code)
     {
-      /* Mathematically, ((a+ib)(c-id))/divider.  */
-      /* Calculate the dividend.  */
-      temp1 = expand_binop (submode, this_mul_optab, real0, real1,
-                           NULL_RTX, unsignedp, methods);
+    case PLUS_EXPR:
+      return trapv ? addv_optab : add_optab;
+
+    case MINUS_EXPR:
+      return trapv ? subv_optab : sub_optab;
+
+    case MULT_EXPR:
+      return trapv ? smulv_optab : smul_optab;
 
-      temp2 = expand_binop (submode, this_mul_optab, imag0, imag1,
-                           NULL_RTX, unsignedp, methods);
+    case NEGATE_EXPR:
+      return trapv ? negv_optab : neg_optab;
 
-      if (temp1 == 0 || temp2 == 0)
-       return 0;
+    case ABS_EXPR:
+      return trapv ? absv_optab : abs_optab;
 
-      real_t = expand_binop (submode, this_add_optab, temp1, temp2,
-                            NULL_RTX, unsignedp, methods);
+    default:
+      return NULL;
+    }
+}
+\f
 
-      temp1 = expand_binop (submode, this_mul_optab, imag0, real1,
-                           NULL_RTX, unsignedp, methods);
+/* Generate code to perform an operation specified by TERNARY_OPTAB
+   on operands OP0, OP1 and OP2, with result having machine-mode MODE.
 
-      temp2 = expand_binop (submode, this_mul_optab, real0, imag1,
-                           NULL_RTX, unsignedp, methods);
+   UNSIGNEDP is for the case where we have to widen the operands
+   to perform the operation.  It says to use zero-extension.
 
-      if (temp1 == 0 || temp2 == 0)
-       return 0;
+   If TARGET is nonzero, the value
+   is generated there, if it is convenient to do so.
+   In all cases an rtx is returned for the locus of the value;
+   this may or may not be TARGET.  */
 
-      imag_t = expand_binop (submode, this_sub_optab, temp1, temp2,
-                            NULL_RTX, unsignedp, methods);
+rtx
+expand_ternary_op (enum machine_mode mode, optab ternary_optab, rtx op0, 
+                  rtx op1, rtx op2, rtx target, int unsignedp) 
+{
+  int icode = (int) ternary_optab->handlers[(int) mode].insn_code;
+  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+  enum machine_mode mode1 = insn_data[icode].operand[2].mode;
+  enum machine_mode mode2 = insn_data[icode].operand[3].mode;
+  rtx temp;
+  rtx pat;
+  rtx xop0 = op0, xop1 = op1, xop2 = op2;
 
-      if (real_t == 0 || imag_t == 0)
-       return 0;
-    }
+  if (ternary_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+    abort ();
 
-  if (class == MODE_COMPLEX_FLOAT)
-    res = expand_binop (submode, binoptab, real_t, divisor,
-                       realr, unsignedp, methods);
+  if (!target
+      || ! (*insn_data[icode].operand[0].predicate) (target, mode))
+    temp = gen_reg_rtx (mode);
   else
-    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
-                        real_t, divisor, realr, unsignedp);
+    temp = target;
+
+  /* In case the insn wants input operands in modes different from
+     those of the actual operands, convert the operands.  It would
+     seem that we don't need to convert CONST_INTs, but we do, so
+     that they're properly zero-extended, sign-extended or truncated
+     for their mode.  */
+
+  if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
+    xop0 = convert_modes (mode0,
+                          GET_MODE (op0) != VOIDmode
+                          ? GET_MODE (op0) 
+                          : mode,
+                          xop0, unsignedp);
+
+  if (GET_MODE (op1) != mode1 && mode1 != VOIDmode)
+    xop1 = convert_modes (mode1,
+                          GET_MODE (op1) != VOIDmode
+                          ? GET_MODE (op1)
+                          : mode,
+                          xop1, unsignedp);
+
+  if (GET_MODE (op2) != mode2 && mode2 != VOIDmode)
+    xop2 = convert_modes (mode2,
+                          GET_MODE (op2) != VOIDmode
+                          ? GET_MODE (op2)
+                          : mode,
+                          xop2, unsignedp);
+
+  /* Now, if insn's predicates don't allow our operands, put them into
+     pseudo regs.  */
+  
+  if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0)
+      && mode0 != VOIDmode) 
+    xop0 = copy_to_mode_reg (mode0, xop0);
+  
+  if (! (*insn_data[icode].operand[2].predicate) (xop1, mode1)
+      && mode1 != VOIDmode)
+    xop1 = copy_to_mode_reg (mode1, xop1);
+    
+  if (! (*insn_data[icode].operand[3].predicate) (xop2, mode2)
+      && mode2 != VOIDmode)
+    xop2 = copy_to_mode_reg (mode2, xop2);
+    
+  pat = GEN_FCN (icode) (temp, xop0, xop1, xop2);
+    
+  emit_insn (pat);
+  return temp; 
+}
 
-  if (res == 0)
-    return 0;
 
-  if (res != realr)
-    emit_move_insn (realr, res);
+/* Like expand_binop, but return a constant rtx if the result can be
+   calculated at compile time.  The arguments and return value are
+   otherwise the same as for expand_binop.  */
 
-  if (class == MODE_COMPLEX_FLOAT)
-    res = expand_binop (submode, binoptab, imag_t, divisor,
-                       imagr, unsignedp, methods);
+static rtx
+simplify_expand_binop (enum machine_mode mode, optab binoptab,
+                      rtx op0, rtx op1, rtx target, int unsignedp,
+                      enum optab_methods methods)
+{
+  if (CONSTANT_P (op0) && CONSTANT_P (op1))
+    return simplify_gen_binary (binoptab->code, mode, op0, op1);
   else
-    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
-                        imag_t, divisor, imagr, unsignedp);
-
-  if (res == 0)
-    return 0;
+    return expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods);
+}
 
-  if (res != imagr)
-    emit_move_insn (imagr, res);
+/* Like simplify_expand_binop, but always put the result in TARGET.
+   Return true if the expansion succeeded.  */
 
-  return 1;
+bool
+force_expand_binop (enum machine_mode mode, optab binoptab,
+                   rtx op0, rtx op1, rtx target, int unsignedp,
+                   enum optab_methods methods)
+{
+  rtx x = simplify_expand_binop (mode, binoptab, op0, op1,
+                                target, unsignedp, methods);
+  if (x == 0)
+    return false;
+  if (x != target)
+    emit_move_insn (target, x);
+  return true;
 }
-\f
-/* Generate code to perform a wide-input-range-acceptable complex divide.  */
 
-static int
-expand_cmplxdiv_wide (rtx real0, rtx real1, rtx imag0, rtx imag1, rtx realr,
-                     rtx imagr, enum machine_mode submode, int unsignedp,
-                     enum optab_methods methods, enum mode_class class,
-                     optab binoptab)
+/* This subroutine of expand_doubleword_shift handles the cases in which
+   the effective shift value is >= BITS_PER_WORD.  The arguments and return
+   value are the same as for the parent routine, except that SUPERWORD_OP1
+   is the shift count to use when shifting OUTOF_INPUT into INTO_TARGET.
+   INTO_TARGET may be null if the caller has decided to calculate it.  */
+
+static bool
+expand_superword_shift (optab binoptab, rtx outof_input, rtx superword_op1,
+                       rtx outof_target, rtx into_target,
+                       int unsignedp, enum optab_methods methods)
 {
-  rtx ratio, divisor;
-  rtx real_t, imag_t;
-  rtx temp1, temp2, lab1, lab2;
-  enum machine_mode mode;
-  rtx res;
-  optab this_add_optab = add_optab;
-  optab this_sub_optab = sub_optab;
-  optab this_neg_optab = neg_optab;
-  optab this_mul_optab = smul_optab;
+  if (into_target != 0)
+    if (!force_expand_binop (word_mode, binoptab, outof_input, superword_op1,
+                            into_target, unsignedp, methods))
+      return false;
 
-  if (binoptab == sdivv_optab)
+  if (outof_target != 0)
     {
-      this_add_optab = addv_optab;
-      this_sub_optab = subv_optab;
-      this_neg_optab = negv_optab;
-      this_mul_optab = smulv_optab;
+      /* For a signed right shift, we must fill OUTOF_TARGET with copies
+        of the sign bit, otherwise we must fill it with zeros.  */
+      if (binoptab != ashr_optab)
+       emit_move_insn (outof_target, CONST0_RTX (word_mode));
+      else
+       if (!force_expand_binop (word_mode, binoptab,
+                                outof_input, GEN_INT (BITS_PER_WORD - 1),
+                                outof_target, unsignedp, methods))
+         return false;
     }
+  return true;
+}
 
-  /* Don't fetch these from memory more than once.  */
-  real0 = force_reg (submode, real0);
-  real1 = force_reg (submode, real1);
+/* This subroutine of expand_doubleword_shift handles the cases in which
+   the effective shift value is < BITS_PER_WORD.  The arguments and return
+   value are the same as for the parent routine.  */
 
-  if (imag0 != 0)
-    imag0 = force_reg (submode, imag0);
+static bool
+expand_subword_shift (enum machine_mode op1_mode, optab binoptab,
+                     rtx outof_input, rtx into_input, rtx op1,
+                     rtx outof_target, rtx into_target,
+                     int unsignedp, enum optab_methods methods,
+                     unsigned HOST_WIDE_INT shift_mask)
+{
+  optab reverse_unsigned_shift, unsigned_shift;
+  rtx tmp, carries;
 
-  imag1 = force_reg (submode, imag1);
+  reverse_unsigned_shift = (binoptab == ashl_optab ? lshr_optab : ashl_optab);
+  unsigned_shift = (binoptab == ashl_optab ? ashl_optab : lshr_optab);
 
-  /* XXX What's an "unsigned" complex number?  */
-  if (unsignedp)
+  /* The low OP1 bits of INTO_TARGET come from the high bits of OUTOF_INPUT.
+     We therefore need to shift OUTOF_INPUT by (BITS_PER_WORD - OP1) bits in
+     the opposite direction to BINOPTAB.  */
+  if (CONSTANT_P (op1) || shift_mask >= BITS_PER_WORD)
     {
-      temp1 = real1;
-      temp2 = imag1;
+      carries = outof_input;
+      tmp = immed_double_const (BITS_PER_WORD, 0, op1_mode);
+      tmp = simplify_expand_binop (op1_mode, sub_optab, tmp, op1,
+                                  0, true, methods);
     }
   else
     {
-      temp1 = expand_abs (submode, real1, NULL_RTX, unsignedp, 1);
-      temp2 = expand_abs (submode, imag1, NULL_RTX, unsignedp, 1);
+      /* We must avoid shifting by BITS_PER_WORD bits since that is either
+        the same as a zero shift (if shift_mask == BITS_PER_WORD - 1) or
+        has unknown behavior.  Do a single shift first, then shift by the
+        remainder.  It's OK to use ~OP1 as the remainder if shift counts
+        are truncated to the mode size.  */
+      carries = expand_binop (word_mode, reverse_unsigned_shift,
+                             outof_input, const1_rtx, 0, unsignedp, methods);
+      if (shift_mask == BITS_PER_WORD - 1)
+       {
+         tmp = immed_double_const (-1, -1, op1_mode);
+         tmp = simplify_expand_binop (op1_mode, xor_optab, op1, tmp,
+                                      0, true, methods);
+       }
+      else
+       {
+         tmp = immed_double_const (BITS_PER_WORD - 1, 0, op1_mode);
+         tmp = simplify_expand_binop (op1_mode, sub_optab, tmp, op1,
+                                      0, true, methods);
+       }
     }
+  if (tmp == 0 || carries == 0)
+    return false;
+  carries = expand_binop (word_mode, reverse_unsigned_shift,
+                         carries, tmp, 0, unsignedp, methods);
+  if (carries == 0)
+    return false;
+
+  /* Shift INTO_INPUT logically by OP1.  This is the last use of INTO_INPUT
+     so the result can go directly into INTO_TARGET if convenient.  */
+  tmp = expand_binop (word_mode, unsigned_shift, into_input, op1,
+                     into_target, unsignedp, methods);
+  if (tmp == 0)
+    return false;
+
+  /* Now OR in the bits carried over from OUTOF_INPUT.  */
+  if (!force_expand_binop (word_mode, ior_optab, tmp, carries,
+                          into_target, unsignedp, methods))
+    return false;
+
+  /* Use a standard word_mode shift for the out-of half.  */
+  if (outof_target != 0)
+    if (!force_expand_binop (word_mode, binoptab, outof_input, op1,
+                            outof_target, unsignedp, methods))
+      return false;
+
+  return true;
+}
 
-  if (temp1 == 0 || temp2 == 0)
-    return 0;
-
-  mode = GET_MODE (temp1);
-  lab1 = gen_label_rtx ();
-  emit_cmp_and_jump_insns (temp1, temp2, LT, NULL_RTX,
-                          mode, unsignedp, lab1);
-
-  /* |c| >= |d|; use ratio d/c to scale dividend and divisor.  */
-
-  if (class == MODE_COMPLEX_FLOAT)
-    ratio = expand_binop (submode, binoptab, imag1, real1,
-                         NULL_RTX, unsignedp, methods);
-  else
-    ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode,
-                          imag1, real1, NULL_RTX, unsignedp);
-
-  if (ratio == 0)
-    return 0;
-
-  /* Calculate divisor.  */
-
-  temp1 = expand_binop (submode, this_mul_optab, imag1, ratio,
-                       NULL_RTX, unsignedp, methods);
-
-  if (temp1 == 0)
-    return 0;
-
-  divisor = expand_binop (submode, this_add_optab, temp1, real1,
-                         NULL_RTX, unsignedp, methods);
-
-  if (divisor == 0)
-    return 0;
 
-  /* Calculate dividend.  */
+#ifdef HAVE_conditional_move
+/* Try implementing expand_doubleword_shift using conditional moves.
+   The shift is by < BITS_PER_WORD if (CMP_CODE CMP1 CMP2) is true,
+   otherwise it is by >= BITS_PER_WORD.  SUBWORD_OP1 and SUPERWORD_OP1
+   are the shift counts to use in the former and latter case.  All other
+   arguments are the same as the parent routine.  */
+
+static bool
+expand_doubleword_shift_condmove (enum machine_mode op1_mode, optab binoptab,
+                                 enum rtx_code cmp_code, rtx cmp1, rtx cmp2,
+                                 rtx outof_input, rtx into_input,
+                                 rtx subword_op1, rtx superword_op1,
+                                 rtx outof_target, rtx into_target,
+                                 int unsignedp, enum optab_methods methods,
+                                 unsigned HOST_WIDE_INT shift_mask)
+{
+  rtx outof_superword, into_superword;
 
-  if (imag0 == 0)
+  /* Put the superword version of the output into OUTOF_SUPERWORD and
+     INTO_SUPERWORD.  */
+  outof_superword = outof_target != 0 ? gen_reg_rtx (word_mode) : 0;
+  if (outof_target != 0 && subword_op1 == superword_op1)
     {
-      real_t = real0;
-
-      /* Compute a / (c+id) as a / (c+d(d/c)) + i (-a(d/c)) / (c+d(d/c)).  */
-
-      imag_t = expand_binop (submode, this_mul_optab, real0, ratio,
-                            NULL_RTX, unsignedp, methods);
-
-      if (imag_t == 0)
-       return 0;
-
-      imag_t = expand_unop (submode, this_neg_optab, imag_t,
-                           NULL_RTX, unsignedp);
-
-      if (real_t == 0 || imag_t == 0)
-       return 0;
+      /* The value INTO_TARGET >> SUBWORD_OP1, which we later store in
+        OUTOF_TARGET, is the same as the value of INTO_SUPERWORD.  */
+      into_superword = outof_target;
+      if (!expand_superword_shift (binoptab, outof_input, superword_op1,
+                                  outof_superword, 0, unsignedp, methods))
+       return false;
     }
   else
     {
-      /* Compute (a+ib)/(c+id) as
-        (a+b(d/c))/(c+d(d/c) + i(b-a(d/c))/(c+d(d/c)).  */
-
-      temp1 = expand_binop (submode, this_mul_optab, imag0, ratio,
-                           NULL_RTX, unsignedp, methods);
-
-      if (temp1 == 0)
-       return 0;
-
-      real_t = expand_binop (submode, this_add_optab, temp1, real0,
-                            NULL_RTX, unsignedp, methods);
-
-      temp1 = expand_binop (submode, this_mul_optab, real0, ratio,
-                           NULL_RTX, unsignedp, methods);
-
-      if (temp1 == 0)
-       return 0;
+      into_superword = gen_reg_rtx (word_mode);
+      if (!expand_superword_shift (binoptab, outof_input, superword_op1,
+                                  outof_superword, into_superword,
+                                  unsignedp, methods))
+       return false;
+    }
 
-      imag_t = expand_binop (submode, this_sub_optab, imag0, temp1,
-                            NULL_RTX, unsignedp, methods);
+  /* Put the subword version directly in OUTOF_TARGET and INTO_TARGET.  */
+  if (!expand_subword_shift (op1_mode, binoptab,
+                            outof_input, into_input, subword_op1,
+                            outof_target, into_target,
+                            unsignedp, methods, shift_mask))
+    return false;
+
+  /* Select between them.  Do the INTO half first because INTO_SUPERWORD
+     might be the current value of OUTOF_TARGET.  */
+  if (!emit_conditional_move (into_target, cmp_code, cmp1, cmp2, op1_mode,
+                             into_target, into_superword, word_mode, false))
+    return false;
+
+  if (outof_target != 0)
+    if (!emit_conditional_move (outof_target, cmp_code, cmp1, cmp2, op1_mode,
+                               outof_target, outof_superword,
+                               word_mode, false))
+      return false;
+
+  return true;
+}
+#endif
 
-      if (real_t == 0 || imag_t == 0)
-       return 0;
+/* Expand a doubleword shift (ashl, ashr or lshr) using word-mode shifts.
+   OUTOF_INPUT and INTO_INPUT are the two word-sized halves of the first
+   input operand; the shift moves bits in the direction OUTOF_INPUT->
+   INTO_TARGET.  OUTOF_TARGET and INTO_TARGET are the equivalent words
+   of the target.  OP1 is the shift count and OP1_MODE is its mode.
+   If OP1 is constant, it will have been truncated as appropriate
+   and is known to be nonzero.
+
+   If SHIFT_MASK is zero, the result of word shifts is undefined when the
+   shift count is outside the range [0, BITS_PER_WORD).  This routine must
+   avoid generating such shifts for OP1s in the range [0, BITS_PER_WORD * 2).
+
+   If SHIFT_MASK is nonzero, all word-mode shift counts are effectively
+   masked by it and shifts in the range [BITS_PER_WORD, SHIFT_MASK) will
+   fill with zeros or sign bits as appropriate.
+
+   If SHIFT_MASK is BITS_PER_WORD - 1, this routine will synthesize
+   a doubleword shift whose equivalent mask is BITS_PER_WORD * 2 - 1.
+   Doing this preserves semantics required by SHIFT_COUNT_TRUNCATED.
+   In all other cases, shifts by values outside [0, BITS_PER_UNIT * 2)
+   are undefined.
+
+   BINOPTAB, UNSIGNEDP and METHODS are as for expand_binop.  This function
+   may not use INTO_INPUT after modifying INTO_TARGET, and similarly for
+   OUTOF_INPUT and OUTOF_TARGET.  OUTOF_TARGET can be null if the parent
+   function wants to calculate it itself.
+
+   Return true if the shift could be successfully synthesized.  */
+
+static bool
+expand_doubleword_shift (enum machine_mode op1_mode, optab binoptab,
+                        rtx outof_input, rtx into_input, rtx op1,
+                        rtx outof_target, rtx into_target,
+                        int unsignedp, enum optab_methods methods,
+                        unsigned HOST_WIDE_INT shift_mask)
+{
+  rtx superword_op1, tmp, cmp1, cmp2;
+  rtx subword_label, done_label;
+  enum rtx_code cmp_code;
+
+  /* See if word-mode shifts by BITS_PER_WORD...BITS_PER_WORD * 2 - 1 will
+     fill the result with sign or zero bits as appropriate.  If so, the value
+     of OUTOF_TARGET will always be (SHIFT OUTOF_INPUT OP1).   Recursively call
+     this routine to calculate INTO_TARGET (which depends on both OUTOF_INPUT
+     and INTO_INPUT), then emit code to set up OUTOF_TARGET.
+
+     This isn't worthwhile for constant shifts since the optimizers will
+     cope better with in-range shift counts.  */
+  if (shift_mask >= BITS_PER_WORD
+      && outof_target != 0
+      && !CONSTANT_P (op1))
+    {
+      if (!expand_doubleword_shift (op1_mode, binoptab,
+                                   outof_input, into_input, op1,
+                                   0, into_target,
+                                   unsignedp, methods, shift_mask))
+       return false;
+      if (!force_expand_binop (word_mode, binoptab, outof_input, op1,
+                              outof_target, unsignedp, methods))
+       return false;
+      return true;
     }
 
-  if (class == MODE_COMPLEX_FLOAT)
-    res = expand_binop (submode, binoptab, real_t, divisor,
-                       realr, unsignedp, methods);
+  /* Set CMP_CODE, CMP1 and CMP2 so that the rtx (CMP_CODE CMP1 CMP2)
+     is true when the effective shift value is less than BITS_PER_WORD.
+     Set SUPERWORD_OP1 to the shift count that should be used to shift
+     OUTOF_INPUT into INTO_TARGET when the condition is false.  */
+  tmp = immed_double_const (BITS_PER_WORD, 0, op1_mode);
+  if (!CONSTANT_P (op1) && shift_mask == BITS_PER_WORD - 1)
+    {
+      /* Set CMP1 to OP1 & BITS_PER_WORD.  The result is zero iff OP1
+        is a subword shift count.  */
+      cmp1 = simplify_expand_binop (op1_mode, and_optab, op1, tmp,
+                                   0, true, methods);
+      cmp2 = CONST0_RTX (op1_mode);
+      cmp_code = EQ;
+      superword_op1 = op1;
+    }
   else
-    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
-                        real_t, divisor, realr, unsignedp);
+    {
+      /* Set CMP1 to OP1 - BITS_PER_WORD.  */
+      cmp1 = simplify_expand_binop (op1_mode, sub_optab, op1, tmp,
+                                   0, true, methods);
+      cmp2 = CONST0_RTX (op1_mode);
+      cmp_code = LT;
+      superword_op1 = cmp1;
+    }
+  if (cmp1 == 0)
+    return false;
 
-  if (res == 0)
-    return 0;
+  /* If we can compute the condition at compile time, pick the
+     appropriate subroutine.  */
+  tmp = simplify_relational_operation (cmp_code, SImode, op1_mode, cmp1, cmp2);
+  if (tmp != 0 && GET_CODE (tmp) == CONST_INT)
+    {
+      if (tmp == const0_rtx)
+       return expand_superword_shift (binoptab, outof_input, superword_op1,
+                                      outof_target, into_target,
+                                      unsignedp, methods);
+      else
+       return expand_subword_shift (op1_mode, binoptab,
+                                    outof_input, into_input, op1,
+                                    outof_target, into_target,
+                                    unsignedp, methods, shift_mask);
+    }
 
-  if (res != realr)
-    emit_move_insn (realr, res);
+#ifdef HAVE_conditional_move
+  /* Try using conditional moves to generate straight-line code.  */
+  {
+    rtx start = get_last_insn ();
+    if (expand_doubleword_shift_condmove (op1_mode, binoptab,
+                                         cmp_code, cmp1, cmp2,
+                                         outof_input, into_input,
+                                         op1, superword_op1,
+                                         outof_target, into_target,
+                                         unsignedp, methods, shift_mask))
+      return true;
+    delete_insns_since (start);
+  }
+#endif
 
-  if (class == MODE_COMPLEX_FLOAT)
-    res = expand_binop (submode, binoptab, imag_t, divisor,
-                       imagr, unsignedp, methods);
-  else
-    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
-                        imag_t, divisor, imagr, unsignedp);
+  /* As a last resort, use branches to select the correct alternative.  */
+  subword_label = gen_label_rtx ();
+  done_label = gen_label_rtx ();
 
-  if (res == 0)
-    return 0;
+  do_compare_rtx_and_jump (cmp1, cmp2, cmp_code, false, op1_mode,
+                          0, 0, subword_label);
 
-  if (res != imagr)
-    emit_move_insn (imagr, res);
+  if (!expand_superword_shift (binoptab, outof_input, superword_op1,
+                              outof_target, into_target,
+                              unsignedp, methods))
+    return false;
 
-  lab2 = gen_label_rtx ();
-  emit_jump_insn (gen_jump (lab2));
+  emit_jump_insn (gen_jump (done_label));
   emit_barrier ();
+  emit_label (subword_label);
 
-  emit_label (lab1);
+  if (!expand_subword_shift (op1_mode, binoptab,
+                            outof_input, into_input, op1,
+                            outof_target, into_target,
+                            unsignedp, methods, shift_mask))
+    return false;
 
-  /* |d| > |c|; use ratio c/d to scale dividend and divisor.  */
-
-  if (class == MODE_COMPLEX_FLOAT)
-    ratio = expand_binop (submode, binoptab, real1, imag1,
-                         NULL_RTX, unsignedp, methods);
-  else
-    ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode,
-                          real1, imag1, NULL_RTX, unsignedp);
+  emit_label (done_label);
+  return true;
+}
+\f
+/* Subroutine of expand_binop.  Perform a double word multiplication of
+   operands OP0 and OP1 both of mode MODE, which is exactly twice as wide
+   as the target's word_mode.  This function return NULL_RTX if anything
+   goes wrong, in which case it may have already emitted instructions
+   which need to be deleted.
+
+   If we want to multiply two two-word values and have normal and widening
+   multiplies of single-word values, we can do this with three smaller
+   multiplications.  Note that we do not make a REG_NO_CONFLICT block here
+   because we are not operating on one word at a time.
+
+   The multiplication proceeds as follows:
+                                _______________________
+                               [__op0_high_|__op0_low__]
+                                _______________________
+        *                      [__op1_high_|__op1_low__]
+        _______________________________________________
+                                _______________________
+    (1)                                [__op0_low__*__op1_low__]
+                    _______________________
+    (2a)           [__op0_low__*__op1_high_]
+                    _______________________
+    (2b)           [__op0_high_*__op1_low__]
+         _______________________
+    (3) [__op0_high_*__op1_high_]
 
-  if (ratio == 0)
-    return 0;
 
-  /* Calculate divisor.  */
+  This gives a 4-word result.  Since we are only interested in the
+  lower 2 words, partial result (3) and the upper words of (2a) and
+  (2b) don't need to be calculated.  Hence (2a) and (2b) can be
+  calculated using non-widening multiplication.
 
-  temp1 = expand_binop (submode, this_mul_optab, real1, ratio,
-                       NULL_RTX, unsignedp, methods);
+  (1), however, needs to be calculated with an unsigned widening
+  multiplication.  If this operation is not directly supported we
+  try using a signed widening multiplication and adjust the result.
+  This adjustment works as follows:
 
-  if (temp1 == 0)
-    return 0;
+      If both operands are positive then no adjustment is needed.
 
-  divisor = expand_binop (submode, this_add_optab, temp1, imag1,
-                         NULL_RTX, unsignedp, methods);
+      If the operands have different signs, for example op0_low < 0 and
+      op1_low >= 0, the instruction treats the most significant bit of
+      op0_low as a sign bit instead of a bit with significance
+      2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low
+      with 2**BITS_PER_WORD - op0_low, and two's complements the
+      result.  Conclusion: We need to add op1_low * 2**BITS_PER_WORD to
+      the result.
 
-  if (divisor == 0)
-    return 0;
+      Similarly, if both operands are negative, we need to add
+      (op0_low + op1_low) * 2**BITS_PER_WORD.
 
-  /* Calculate dividend.  */
+      We use a trick to adjust quickly.  We logically shift op0_low right
+      (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to
+      op0_high (op1_high) before it is used to calculate 2b (2a).  If no
+      logical shift exists, we do an arithmetic right shift and subtract
+      the 0 or -1.  */
 
-  if (imag0 == 0)
+static rtx
+expand_doubleword_mult (enum machine_mode mode, rtx op0, rtx op1, rtx target,
+                      bool umulp, enum optab_methods methods)
+{
+  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 product, adjust, product_high, temp;
+
+  rtx op0_high = operand_subword_force (op0, high, mode);
+  rtx op0_low = operand_subword_force (op0, low, mode);
+  rtx op1_high = operand_subword_force (op1, high, mode);
+  rtx op1_low = operand_subword_force (op1, low, mode);
+
+  /* If we're using an unsigned multiply to directly compute the product
+     of the low-order words of the operands and perform any required
+     adjustments of the operands, we begin by trying two more multiplications
+     and then computing the appropriate sum.
+
+     We have checked above that the required addition is provided.
+     Full-word addition will normally always succeed, especially if
+     it is provided at all, so we don't worry about its failure.  The
+     multiplication may well fail, however, so we do handle that.  */
+
+  if (!umulp)
     {
-      /* Compute a / (c+id) as a(c/d) / (c(c/d)+d) + i (-a) / (c(c/d)+d).  */
-
-      real_t = expand_binop (submode, this_mul_optab, real0, ratio,
-                            NULL_RTX, unsignedp, methods);
-
-      imag_t = expand_unop (submode, this_neg_optab, real0,
-                           NULL_RTX, unsignedp);
+      /* ??? This could be done with emit_store_flag where available.  */
+      temp = expand_binop (word_mode, lshr_optab, op0_low, wordm1,
+                          NULL_RTX, 1, methods);
+      if (temp)
+       op0_high = expand_binop (word_mode, add_optab, op0_high, temp,
+                                NULL_RTX, 0, OPTAB_DIRECT);
+      else
+       {
+         temp = expand_binop (word_mode, ashr_optab, op0_low, wordm1,
+                              NULL_RTX, 0, methods);
+         if (!temp)
+           return NULL_RTX;
+         op0_high = expand_binop (word_mode, sub_optab, op0_high, temp,
+                                  NULL_RTX, 0, OPTAB_DIRECT);
+       }
 
-      if (real_t == 0 || imag_t == 0)
-       return 0;
+      if (!op0_high)
+       return NULL_RTX;
     }
-  else
-    {
-      /* Compute (a+ib)/(c+id) as
-        (a(c/d)+b)/(c(c/d)+d) + i (b(c/d)-a)/(c(c/d)+d).  */
-
-      temp1 = expand_binop (submode, this_mul_optab, real0, ratio,
-                           NULL_RTX, unsignedp, methods);
 
-      if (temp1 == 0)
-       return 0;
-
-      real_t = expand_binop (submode, this_add_optab, temp1, imag0,
-                            NULL_RTX, unsignedp, methods);
-
-      temp1 = expand_binop (submode, this_mul_optab, imag0, ratio,
-                           NULL_RTX, unsignedp, methods);
+  adjust = expand_binop (word_mode, smul_optab, op0_high, op1_low,
+                        NULL_RTX, 0, OPTAB_DIRECT);
+  if (!adjust)
+    return NULL_RTX;
 
-      if (temp1 == 0)
-       return 0;
+  /* OP0_HIGH should now be dead.  */
 
-      imag_t = expand_binop (submode, this_sub_optab, temp1, real0,
-                            NULL_RTX, unsignedp, methods);
+  if (!umulp)
+    {
+      /* ??? This could be done with emit_store_flag where available.  */
+      temp = expand_binop (word_mode, lshr_optab, op1_low, wordm1,
+                          NULL_RTX, 1, methods);
+      if (temp)
+       op1_high = expand_binop (word_mode, add_optab, op1_high, temp,
+                                NULL_RTX, 0, OPTAB_DIRECT);
+      else
+       {
+         temp = expand_binop (word_mode, ashr_optab, op1_low, wordm1,
+                              NULL_RTX, 0, methods);
+         if (!temp)
+           return NULL_RTX;
+         op1_high = expand_binop (word_mode, sub_optab, op1_high, temp,
+                                  NULL_RTX, 0, OPTAB_DIRECT);
+       }
 
-      if (real_t == 0 || imag_t == 0)
-       return 0;
+      if (!op1_high)
+       return NULL_RTX;
     }
 
-  if (class == MODE_COMPLEX_FLOAT)
-    res = expand_binop (submode, binoptab, real_t, divisor,
-                       realr, unsignedp, methods);
-  else
-    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
-                        real_t, divisor, realr, unsignedp);
-
-  if (res == 0)
-    return 0;
+  temp = expand_binop (word_mode, smul_optab, op1_high, op0_low,
+                      NULL_RTX, 0, OPTAB_DIRECT);
+  if (!temp)
+    return NULL_RTX;
 
-  if (res != realr)
-    emit_move_insn (realr, res);
+  /* OP1_HIGH should now be dead.  */
 
-  if (class == MODE_COMPLEX_FLOAT)
-    res = expand_binop (submode, binoptab, imag_t, divisor,
-                       imagr, unsignedp, methods);
-  else
-    res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
-                        imag_t, divisor, imagr, unsignedp);
+  adjust = expand_binop (word_mode, add_optab, adjust, temp,
+                        adjust, 0, OPTAB_DIRECT);
 
-  if (res == 0)
-    return 0;
+  if (target && !REG_P (target))
+    target = NULL_RTX;
 
-  if (res != imagr)
-    emit_move_insn (imagr, res);
+  if (umulp)
+    product = expand_binop (mode, umul_widen_optab, op0_low, op1_low,
+                           target, 1, OPTAB_DIRECT);
+  else
+    product = expand_binop (mode, smul_widen_optab, op0_low, op1_low,
+                           target, 1, OPTAB_DIRECT);
 
-  emit_label (lab2);
+  if (!product)
+    return NULL_RTX;
 
-  return 1;
+  product_high = operand_subword (product, high, 1, mode);
+  adjust = expand_binop (word_mode, add_optab, product_high, adjust,
+                        REG_P (product_high) ? product_high : adjust,
+                        0, OPTAB_DIRECT);
+  emit_move_insn (product_high, adjust);
+  return product;
 }
 \f
 /* Wrapper around expand_binop which takes an rtx code to specify
@@ -671,11 +965,6 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
 
   class = GET_MODE_CLASS (mode);
 
-  op0 = protect_from_queue (op0, 0);
-  op1 = protect_from_queue (op1, 0);
-  if (target)
-    target = protect_from_queue (target, 1);
-
   if (flag_force_mem)
     {
       /* Load duplicate non-volatile operands once.  */
@@ -700,15 +989,23 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
       binoptab = add_optab;
     }
 
-  /* If we are inside an appropriately-short loop and one operand is an
-     expensive constant, force it into a register.  */
-  if (CONSTANT_P (op0) && preserve_subexpressions_p ()
+  /* If we are inside an appropriately-short loop and we are optimizing,
+     force expensive constants into a register.  */
+  if (CONSTANT_P (op0) && optimize
       && rtx_cost (op0, binoptab->code) > COSTS_N_INSNS (1))
-    op0 = force_reg (mode, op0);
+    {
+      if (GET_MODE (op0) != VOIDmode)
+       op0 = convert_modes (mode, VOIDmode, op0, unsignedp);
+      op0 = force_reg (mode, op0);
+    }
 
-  if (CONSTANT_P (op1) && preserve_subexpressions_p ()
+  if (CONSTANT_P (op1) && optimize
       && ! shift_op && rtx_cost (op1, binoptab->code) > COSTS_N_INSNS (1))
-    op1 = force_reg (mode, op1);
+    {
+      if (GET_MODE (op1) != VOIDmode)
+       op1 = convert_modes (mode, VOIDmode, op1, unsignedp);
+      op1 = force_reg (mode, op1);
+    }
 
   /* Record where to delete back to if we backtrack.  */
   last = get_last_insn ();
@@ -717,7 +1014,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
      try to make the first operand a register.
      Even better, try to make it the same as the target.
      Also try to make the last operand a constant.  */
-  if (GET_RTX_CLASS (binoptab->code) == 'c'
+  if (GET_RTX_CLASS (binoptab->code) == RTX_COMM_ARITH
       || binoptab == smul_widen_optab
       || binoptab == umul_widen_optab
       || binoptab == smul_highpart_optab
@@ -725,9 +1022,9 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
     {
       commutative_op = 1;
 
-      if (((target == 0 || GET_CODE (target) == REG)
-          ? ((GET_CODE (op1) == REG
-              && GET_CODE (op0) != REG)
+      if (((target == 0 || REG_P (target))
+          ? ((REG_P (op1)
+              && !REG_P (op0))
              || target == op1)
           : rtx_equal_p (op1, target))
          || GET_CODE (op0) == CONST_INT)
@@ -953,118 +1250,71 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
   if ((binoptab == lshr_optab || binoptab == ashl_optab
        || binoptab == ashr_optab)
       && class == MODE_INT
-      && GET_CODE (op1) == CONST_INT
+      && (GET_CODE (op1) == CONST_INT || !optimize_size)
       && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
       && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
       && ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
       && lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
     {
-      rtx insns, inter, equiv_value;
-      rtx into_target, outof_target;
-      rtx into_input, outof_input;
-      int shift_count, left_shift, outof_word;
-
-      /* If TARGET is the same as one of the operands, the REG_EQUAL note
-        won't be accurate, so use a new target.  */
-      if (target == 0 || target == op0 || target == op1)
-       target = gen_reg_rtx (mode);
-
-      start_sequence ();
-
-      shift_count = INTVAL (op1);
-
-      /* OUTOF_* is the word we are shifting bits away from, and
-        INTO_* is the word that we are shifting bits towards, thus
-        they differ depending on the direction of the shift and
-        WORDS_BIG_ENDIAN.  */
+      unsigned HOST_WIDE_INT shift_mask, double_shift_mask;
+      enum machine_mode op1_mode;
 
-      left_shift = binoptab == ashl_optab;
-      outof_word = left_shift ^ ! WORDS_BIG_ENDIAN;
-
-      outof_target = operand_subword (target, outof_word, 1, mode);
-      into_target = operand_subword (target, 1 - outof_word, 1, mode);
-
-      outof_input = operand_subword_force (op0, outof_word, mode);
-      into_input = operand_subword_force (op0, 1 - outof_word, mode);
-
-      if (shift_count >= BITS_PER_WORD)
-       {
-         inter = expand_binop (word_mode, binoptab,
-                              outof_input,
-                              GEN_INT (shift_count - BITS_PER_WORD),
-                              into_target, unsignedp, next_methods);
+      double_shift_mask = targetm.shift_truncation_mask (mode);
+      shift_mask = targetm.shift_truncation_mask (word_mode);
+      op1_mode = GET_MODE (op1) != VOIDmode ? GET_MODE (op1) : word_mode;
 
-         if (inter != 0 && inter != into_target)
-           emit_move_insn (into_target, inter);
+      /* Apply the truncation to constant shifts.  */
+      if (double_shift_mask > 0 && GET_CODE (op1) == CONST_INT)
+       op1 = GEN_INT (INTVAL (op1) & double_shift_mask);
 
-         /* For a signed right shift, we must fill the word we are shifting
-            out of with copies of the sign bit.  Otherwise it is zeroed.  */
-         if (inter != 0 && binoptab != ashr_optab)
-           inter = CONST0_RTX (word_mode);
-         else if (inter != 0)
-           inter = expand_binop (word_mode, binoptab,
-                                 outof_input,
-                                 GEN_INT (BITS_PER_WORD - 1),
-                                 outof_target, unsignedp, next_methods);
+      if (op1 == CONST0_RTX (op1_mode))
+       return op0;
 
-         if (inter != 0 && inter != outof_target)
-           emit_move_insn (outof_target, inter);
-       }
-      else
+      /* Make sure that this is a combination that expand_doubleword_shift
+        can handle.  See the comments there for details.  */
+      if (double_shift_mask == 0
+         || (shift_mask == BITS_PER_WORD - 1
+             && double_shift_mask == BITS_PER_WORD * 2 - 1))
        {
-         rtx carries;
-         optab reverse_unsigned_shift, unsigned_shift;
-
-         /* For a shift of less then BITS_PER_WORD, to compute the carry,
-            we must do a logical shift in the opposite direction of the
-            desired shift.  */
-
-         reverse_unsigned_shift = (left_shift ? lshr_optab : ashl_optab);
-
-         /* For a shift of less than BITS_PER_WORD, to compute the word
-            shifted towards, we need to unsigned shift the orig value of
-            that word.  */
+         rtx insns, equiv_value;
+         rtx into_target, outof_target;
+         rtx into_input, outof_input;
+         int left_shift, outof_word;
 
-         unsigned_shift = (left_shift ? ashl_optab : lshr_optab);
-
-         carries = expand_binop (word_mode, reverse_unsigned_shift,
-                                 outof_input,
-                                 GEN_INT (BITS_PER_WORD - shift_count),
-                                 0, unsignedp, next_methods);
-
-         if (carries == 0)
-           inter = 0;
-         else
-           inter = expand_binop (word_mode, unsigned_shift, into_input,
-                                 op1, 0, unsignedp, next_methods);
+         /* If TARGET is the same as one of the operands, the REG_EQUAL note
+            won't be accurate, so use a new target.  */
+         if (target == 0 || target == op0 || target == op1)
+           target = gen_reg_rtx (mode);
 
-         if (inter != 0)
-           inter = expand_binop (word_mode, ior_optab, carries, inter,
-                                 into_target, unsignedp, next_methods);
+         start_sequence ();
 
-         if (inter != 0 && inter != into_target)
-           emit_move_insn (into_target, inter);
+         /* OUTOF_* is the word we are shifting bits away from, and
+            INTO_* is the word that we are shifting bits towards, thus
+            they differ depending on the direction of the shift and
+            WORDS_BIG_ENDIAN.  */
 
-         if (inter != 0)
-           inter = expand_binop (word_mode, binoptab, outof_input,
-                                 op1, outof_target, unsignedp, next_methods);
+         left_shift = binoptab == ashl_optab;
+         outof_word = left_shift ^ ! WORDS_BIG_ENDIAN;
 
-         if (inter != 0 && inter != outof_target)
-           emit_move_insn (outof_target, inter);
-       }
+         outof_target = operand_subword (target, outof_word, 1, mode);
+         into_target = operand_subword (target, 1 - outof_word, 1, mode);
 
-      insns = get_insns ();
-      end_sequence ();
+         outof_input = operand_subword_force (op0, outof_word, mode);
+         into_input = operand_subword_force (op0, 1 - outof_word, mode);
 
-      if (inter != 0)
-       {
-         if (binoptab->code != UNKNOWN)
-           equiv_value = gen_rtx_fmt_ee (binoptab->code, mode, op0, op1);
-         else
-           equiv_value = 0;
+         if (expand_doubleword_shift (op1_mode, binoptab,
+                                      outof_input, into_input, op1,
+                                      outof_target, into_target,
+                                      unsignedp, methods, shift_mask))
+           {
+             insns = get_insns ();
+             end_sequence ();
 
-         emit_no_conflict_block (insns, target, op0, op1, equiv_value);
-         return target;
+             equiv_value = gen_rtx_fmt_ee (binoptab->code, mode, op0, op1);
+             emit_no_conflict_block (insns, target, op0, op1, equiv_value);
+             return target;
+           }
+         end_sequence ();
        }
     }
 
@@ -1083,8 +1333,12 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
       int shift_count, left_shift, outof_word;
 
       /* If TARGET is the same as one of the operands, the REG_EQUAL note
-        won't be accurate, so use a new target.  */
-      if (target == 0 || target == op0 || target == op1)
+        won't be accurate, so use a new target. Do this also if target is not
+        a REG, first because having a register instead may open optimization
+        opportunities, and second because if target and op0 happen to be MEMs
+        designating the same location, we would risk clobbering it too early
+        in the code sequence we generate below.  */
+      if (target == 0 || target == op0 || target == op1 || ! REG_P (target))
        target = gen_reg_rtx (mode);
 
       start_sequence ();
@@ -1217,11 +1471,11 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
 
       xtarget = gen_reg_rtx (mode);
 
-      if (target == 0 || GET_CODE (target) != REG)
+      if (target == 0 || !REG_P (target))
        target = xtarget;
 
       /* Indicate for flow that the entire target reg is being set.  */
-      if (GET_CODE (target) == REG)
+      if (REG_P (target))
        emit_insn (gen_rtx_CLOBBER (VOIDmode, xtarget));
 
       /* Do the actual arithmetic.  */
@@ -1280,6 +1534,11 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
                }
              emit_move_insn (target_piece, newx);
            }
+         else
+           {
+             if (x != target_piece)
+               emit_move_insn (target_piece, x);
+           }
 
          carry_in = carry_out;
        }
@@ -1307,456 +1566,50 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
        delete_insns_since (last);
     }
 
-  /* If we want to multiply two two-word values and have normal and widening
-     multiplies of single-word values, we can do this with three smaller
-     multiplications.  Note that we do not make a REG_NO_CONFLICT block here
-     because we are not operating on one word at a time.
-
-     The multiplication proceeds as follows:
-                                _______________________
-                               [__op0_high_|__op0_low__]
-                                _______________________
-        *                      [__op1_high_|__op1_low__]
-        _______________________________________________
-                                _______________________
-    (1)                                [__op0_low__*__op1_low__]
-                    _______________________
-    (2a)           [__op0_low__*__op1_high_]
-                    _______________________
-    (2b)           [__op0_high_*__op1_low__]
-         _______________________
-    (3) [__op0_high_*__op1_high_]
-
-
-    This gives a 4-word result.  Since we are only interested in the
-    lower 2 words, partial result (3) and the upper words of (2a) and
-    (2b) don't need to be calculated.  Hence (2a) and (2b) can be
-    calculated using non-widening multiplication.
-
-    (1), however, needs to be calculated with an unsigned widening
-    multiplication.  If this operation is not directly supported we
-    try using a signed widening multiplication and adjust the result.
-    This adjustment works as follows:
-
-      If both operands are positive then no adjustment is needed.
-
-      If the operands have different signs, for example op0_low < 0 and
-      op1_low >= 0, the instruction treats the most significant bit of
-      op0_low as a sign bit instead of a bit with significance
-      2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low
-      with 2**BITS_PER_WORD - op0_low, and two's complements the
-      result.  Conclusion: We need to add op1_low * 2**BITS_PER_WORD to
-      the result.
-
-      Similarly, if both operands are negative, we need to add
-      (op0_low + op1_low) * 2**BITS_PER_WORD.
-
-      We use a trick to adjust quickly.  We logically shift op0_low right
-      (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to
-      op0_high (op1_high) before it is used to calculate 2b (2a).  If no
-      logical shift exists, we do an arithmetic right shift and subtract
-      the 0 or -1.  */
+  /* Attempt to synthesize double word multiplies using a sequence of word
+     mode multiplications.  We first attempt to generate a sequence using a
+     more efficient unsigned widening multiply, and if that fails we then
+     try using a signed widening multiply.  */
 
   if (binoptab == smul_optab
       && class == MODE_INT
       && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
       && smul_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
-      && add_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
-      && ((umul_widen_optab->handlers[(int) mode].insn_code
-          != CODE_FOR_nothing)
-         || (smul_widen_optab->handlers[(int) mode].insn_code
-             != CODE_FOR_nothing)))
+      && add_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
     {
-      int low = (WORDS_BIG_ENDIAN ? 1 : 0);
-      int high = (WORDS_BIG_ENDIAN ? 0 : 1);
-      rtx op0_high = operand_subword_force (op0, high, mode);
-      rtx op0_low = operand_subword_force (op0, low, mode);
-      rtx op1_high = operand_subword_force (op1, high, mode);
-      rtx op1_low = operand_subword_force (op1, low, mode);
-      rtx product = 0;
-      rtx op0_xhigh = NULL_RTX;
-      rtx op1_xhigh = NULL_RTX;
-
-      /* If the target is the same as one of the inputs, don't use it.  This
-        prevents problems with the REG_EQUAL note.  */
-      if (target == op0 || target == op1
-         || (target != 0 && GET_CODE (target) != REG))
-       target = 0;
-
-      /* Multiply the two lower words to get a double-word product.
-        If unsigned widening multiplication is available, use that;
-        otherwise use the signed form and compensate.  */
-
-      if (umul_widen_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
-       {
-         product = expand_binop (mode, umul_widen_optab, op0_low, op1_low,
-                                 target, 1, OPTAB_DIRECT);
+      rtx product = NULL_RTX;
 
-         /* If we didn't succeed, delete everything we did so far.  */
-         if (product == 0)
+      if (umul_widen_optab->handlers[(int) mode].insn_code
+         != CODE_FOR_nothing)
+       {
+         product = expand_doubleword_mult (mode, op0, op1, target,
+                                           true, methods);
+         if (!product)
            delete_insns_since (last);
-         else
-           op0_xhigh = op0_high, op1_xhigh = op1_high;
        }
 
-      if (product == 0
+      if (product == NULL_RTX
          && smul_widen_optab->handlers[(int) mode].insn_code
-              != CODE_FOR_nothing)
-       {
-         rtx wordm1 = GEN_INT (BITS_PER_WORD - 1);
-         product = expand_binop (mode, smul_widen_optab, op0_low, op1_low,
-                                 target, 1, OPTAB_DIRECT);
-         op0_xhigh = expand_binop (word_mode, lshr_optab, op0_low, wordm1,
-                                   NULL_RTX, 1, next_methods);
-         if (op0_xhigh)
-           op0_xhigh = expand_binop (word_mode, add_optab, op0_high,
-                                     op0_xhigh, op0_xhigh, 0, next_methods);
-         else
-           {
-             op0_xhigh = expand_binop (word_mode, ashr_optab, op0_low, wordm1,
-                                       NULL_RTX, 0, next_methods);
-             if (op0_xhigh)
-               op0_xhigh = expand_binop (word_mode, sub_optab, op0_high,
-                                         op0_xhigh, op0_xhigh, 0,
-                                         next_methods);
-           }
-
-         op1_xhigh = expand_binop (word_mode, lshr_optab, op1_low, wordm1,
-                                   NULL_RTX, 1, next_methods);
-         if (op1_xhigh)
-           op1_xhigh = expand_binop (word_mode, add_optab, op1_high,
-                                     op1_xhigh, op1_xhigh, 0, next_methods);
-         else
-           {
-             op1_xhigh = expand_binop (word_mode, ashr_optab, op1_low, wordm1,
-                                       NULL_RTX, 0, next_methods);
-             if (op1_xhigh)
-               op1_xhigh = expand_binop (word_mode, sub_optab, op1_high,
-                                         op1_xhigh, op1_xhigh, 0,
-                                         next_methods);
-           }
-       }
-
-      /* If we have been able to directly compute the product of the
-        low-order words of the operands and perform any required adjustments
-        of the operands, we proceed by trying two more multiplications
-        and then computing the appropriate sum.
-
-        We have checked above that the required addition is provided.
-        Full-word addition will normally always succeed, especially if
-        it is provided at all, so we don't worry about its failure.  The
-        multiplication may well fail, however, so we do handle that.  */
-
-      if (product && op0_xhigh && op1_xhigh)
-       {
-         rtx product_high = operand_subword (product, high, 1, mode);
-         rtx temp = expand_binop (word_mode, binoptab, op0_low, op1_xhigh,
-                                  NULL_RTX, 0, OPTAB_DIRECT);
-
-         if (!REG_P (product_high))
-           product_high = force_reg (word_mode, product_high);
-
-         if (temp != 0)
-           temp = expand_binop (word_mode, add_optab, temp, product_high,
-                                product_high, 0, next_methods);
-
-         if (temp != 0 && temp != product_high)
-           emit_move_insn (product_high, temp);
-
-         if (temp != 0)
-           temp = expand_binop (word_mode, binoptab, op1_low, op0_xhigh,
-                                NULL_RTX, 0, OPTAB_DIRECT);
-
-         if (temp != 0)
-           temp = expand_binop (word_mode, add_optab, temp,
-                                product_high, product_high,
-                                0, next_methods);
-
-         if (temp != 0 && temp != product_high)
-           emit_move_insn (product_high, temp);
-
-         emit_move_insn (operand_subword (product, high, 1, mode), product_high);
-
-         if (temp != 0)
-           {
-             if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
-               {
-                 temp = emit_move_insn (product, product);
-                 set_unique_reg_note (temp,
-                                      REG_EQUAL,
-                                      gen_rtx_fmt_ee (MULT, mode,
-                                                      copy_rtx (op0),
-                                                      copy_rtx (op1)));
-               }
-
-             return product;
-           }
-       }
-
-      /* If we get here, we couldn't do it for some reason even though we
-        originally thought we could.  Delete anything we've emitted in
-        trying to do it.  */
-
-      delete_insns_since (last);
-    }
-
-  /* Open-code the vector operations if we have no hardware support
-     for them.  */
-  if (class == MODE_VECTOR_INT || class == MODE_VECTOR_FLOAT)
-    return expand_vector_binop (mode, binoptab, op0, op1, target,
-                               unsignedp, methods);
-
-  /* We need to open-code the complex type operations: '+, -, * and /' */
-
-  /* At this point we allow operations between two similar complex
-     numbers, and also if one of the operands is not a complex number
-     but rather of MODE_FLOAT or MODE_INT. However, the caller
-     must make sure that the MODE of the non-complex operand matches
-     the SUBMODE of the complex operand.  */
-
-  if (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)
-    {
-      rtx real0 = 0, imag0 = 0;
-      rtx real1 = 0, imag1 = 0;
-      rtx realr, imagr, res;
-      rtx seq;
-      rtx equiv_value;
-      int ok = 0;
-
-      /* Find the correct mode for the real and imaginary parts.  */
-      enum machine_mode submode = GET_MODE_INNER(mode);
-
-      if (submode == BLKmode)
-       abort ();
-
-      if (! target)
-       target = gen_reg_rtx (mode);
-
-      start_sequence ();
-
-      realr = gen_realpart (submode, target);
-      imagr = gen_imagpart (submode, target);
-
-      if (GET_MODE (op0) == mode)
+            != CODE_FOR_nothing)
        {
-         real0 = gen_realpart (submode, op0);
-         imag0 = gen_imagpart (submode, op0);
-       }
-      else
-       real0 = op0;
-
-      if (GET_MODE (op1) == mode)
-       {
-         real1 = gen_realpart (submode, op1);
-         imag1 = gen_imagpart (submode, op1);
+         product = expand_doubleword_mult (mode, op0, op1, target,
+                                           false, methods);
+         if (!product)
+           delete_insns_since (last);
        }
-      else
-       real1 = op1;
 
-      if (real0 == 0 || real1 == 0 || ! (imag0 != 0 || imag1 != 0))
-       abort ();
-
-      switch (binoptab->code)
+      if (product != NULL_RTX)
        {
-       case PLUS:
-         /* (a+ib) + (c+id) = (a+c) + i(b+d) */
-       case MINUS:
-         /* (a+ib) - (c+id) = (a-c) + i(b-d) */
-         res = expand_binop (submode, binoptab, real0, real1,
-                             realr, unsignedp, methods);
-
-         if (res == 0)
-           break;
-         else if (res != realr)
-           emit_move_insn (realr, res);
-
-         if (imag0 != 0 && imag1 != 0)
-           res = expand_binop (submode, binoptab, imag0, imag1,
-                               imagr, unsignedp, methods);
-         else if (imag0 != 0)
-           res = imag0;
-         else if (binoptab->code == MINUS)
-            res = expand_unop (submode,
-                                binoptab == subv_optab ? negv_optab : neg_optab,
-                                imag1, imagr, unsignedp);
-         else
-           res = imag1;
-
-         if (res == 0)
-           break;
-         else if (res != imagr)
-           emit_move_insn (imagr, res);
-
-         ok = 1;
-         break;
-
-       case MULT:
-         /* (a+ib) * (c+id) = (ac-bd) + i(ad+cb) */
-
-         if (imag0 != 0 && imag1 != 0)
-           {
-             rtx temp1, temp2;
-
-             /* Don't fetch these from memory more than once.  */
-             real0 = force_reg (submode, real0);
-             real1 = force_reg (submode, real1);
-             imag0 = force_reg (submode, imag0);
-             imag1 = force_reg (submode, imag1);
-
-             temp1 = expand_binop (submode, binoptab, real0, real1, NULL_RTX,
-                                   unsignedp, methods);
-
-             temp2 = expand_binop (submode, binoptab, imag0, imag1, NULL_RTX,
-                                   unsignedp, methods);
-
-             if (temp1 == 0 || temp2 == 0)
-               break;
-
-             res = (expand_binop
-                     (submode,
-                      binoptab == smulv_optab ? subv_optab : sub_optab,
-                      temp1, temp2, realr, unsignedp, methods));
-
-             if (res == 0)
-               break;
-             else if (res != realr)
-               emit_move_insn (realr, res);
-
-             temp1 = expand_binop (submode, binoptab, real0, imag1,
-                                   NULL_RTX, unsignedp, methods);
-
-             /* Avoid expanding redundant multiplication for the common
-                case of squaring a complex number.  */
-             if (rtx_equal_p (real0, real1) && rtx_equal_p (imag0, imag1))
-               temp2 = temp1;
-             else
-               temp2 = expand_binop (submode, binoptab, real1, imag0,
-                                     NULL_RTX, unsignedp, methods);
-
-             if (temp1 == 0 || temp2 == 0)
-               break;
-
-             res = (expand_binop
-                     (submode,
-                      binoptab == smulv_optab ? addv_optab : add_optab,
-                      temp1, temp2, imagr, unsignedp, methods));
-
-             if (res == 0)
-               break;
-             else if (res != imagr)
-               emit_move_insn (imagr, res);
-
-             ok = 1;
-           }
-         else
-           {
-             /* Don't fetch these from memory more than once.  */
-             real0 = force_reg (submode, real0);
-             real1 = force_reg (submode, real1);
-
-             res = expand_binop (submode, binoptab, real0, real1,
-                                 realr, unsignedp, methods);
-             if (res == 0)
-               break;
-             else if (res != realr)
-               emit_move_insn (realr, res);
-
-             if (imag0 != 0)
-               res = expand_binop (submode, binoptab,
-                                   real1, imag0, imagr, unsignedp, methods);
-             else
-               res = expand_binop (submode, binoptab,
-                                   real0, imag1, imagr, unsignedp, methods);
-
-             if (res == 0)
-               break;
-             else if (res != imagr)
-               emit_move_insn (imagr, res);
-
-             ok = 1;
-           }
-         break;
-
-       case DIV:
-         /* (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd)) */
-
-         if (imag1 == 0)
-           {
-             /* (a+ib) / (c+i0) = (a/c) + i(b/c) */
-
-             /* Don't fetch these from memory more than once.  */
-             real1 = force_reg (submode, real1);
-
-             /* Simply divide the real and imaginary parts by `c' */
-             if (class == MODE_COMPLEX_FLOAT)
-               res = expand_binop (submode, binoptab, real0, real1,
-                                   realr, unsignedp, methods);
-             else
-               res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
-                                    real0, real1, realr, unsignedp);
-
-             if (res == 0)
-               break;
-             else if (res != realr)
-               emit_move_insn (realr, res);
-
-             if (class == MODE_COMPLEX_FLOAT)
-               res = expand_binop (submode, binoptab, imag0, real1,
-                                   imagr, unsignedp, methods);
-             else
-               res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
-                                    imag0, real1, imagr, unsignedp);
-
-             if (res == 0)
-               break;
-             else if (res != imagr)
-               emit_move_insn (imagr, res);
-
-             ok = 1;
-           }
-         else
+         if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
            {
-             switch (flag_complex_divide_method)
-               {
-               case 0:
-                 ok = expand_cmplxdiv_straight (real0, real1, imag0, imag1,
-                                                realr, imagr, submode,
-                                                unsignedp, methods,
-                                                class, binoptab);
-                 break;
-
-               case 1:
-                 ok = expand_cmplxdiv_wide (real0, real1, imag0, imag1,
-                                            realr, imagr, submode,
-                                            unsignedp, methods,
-                                            class, binoptab);
-                 break;
-
-               default:
-                 abort ();
-               }
+             temp = emit_move_insn (target ? target : product, product);
+             set_unique_reg_note (temp,
+                                  REG_EQUAL,
+                                  gen_rtx_fmt_ee (MULT, mode,
+                                                  copy_rtx (op0),
+                                                  copy_rtx (op1)));
            }
-         break;
-
-       default:
-         abort ();
-       }
-
-      seq = get_insns ();
-      end_sequence ();
-
-      if (ok)
-       {
-         if (binoptab->code != UNKNOWN)
-           equiv_value
-             = gen_rtx_fmt_ee (binoptab->code, mode,
-                               copy_rtx (op0), copy_rtx (op1));
-         else
-           equiv_value = 0;
-
-         emit_no_conflict_block (seq, target, op0, op1, equiv_value);
-
-         return target;
+         return product;
        }
     }
 
@@ -1875,224 +1728,6 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
   delete_insns_since (entry_last);
   return 0;
 }
-
-/* Like expand_binop, but for open-coding vectors binops.  */
-
-static rtx
-expand_vector_binop (enum machine_mode mode, optab binoptab, rtx op0,
-                    rtx op1, rtx target, int unsignedp,
-                    enum optab_methods methods)
-{
-  enum machine_mode submode, tmode;
-  int size, elts, subsize, subbitsize, i;
-  rtx t, a, b, res, seq;
-  enum mode_class class;
-
-  class = GET_MODE_CLASS (mode);
-
-  size = GET_MODE_SIZE (mode);
-  submode = GET_MODE_INNER (mode);
-
-  /* Search for the widest vector mode with the same inner mode that is
-     still narrower than MODE and that allows to open-code this operator.
-     Note, if we find such a mode and the handler later decides it can't
-     do the expansion, we'll be called recursively with the narrower mode.  */
-  for (tmode = GET_CLASS_NARROWEST_MODE (class);
-       GET_MODE_SIZE (tmode) < GET_MODE_SIZE (mode);
-       tmode = GET_MODE_WIDER_MODE (tmode))
-    {
-      if (GET_MODE_INNER (tmode) == GET_MODE_INNER (mode)
-         && binoptab->handlers[(int) tmode].insn_code != CODE_FOR_nothing)
-       submode = tmode;
-    }
-
-  switch (binoptab->code)
-    {
-    case AND:
-    case IOR:
-    case XOR:
-      tmode = int_mode_for_mode (mode);
-      if (tmode != BLKmode)
-       submode = tmode;
-    case PLUS:
-    case MINUS:
-    case MULT:
-    case DIV:
-      subsize = GET_MODE_SIZE (submode);
-      subbitsize = GET_MODE_BITSIZE (submode);
-      elts = size / subsize;
-
-      /* If METHODS is OPTAB_DIRECT, we don't insist on the exact mode,
-        but that we operate on more than one element at a time.  */
-      if (subsize == GET_MODE_UNIT_SIZE (mode) && methods == OPTAB_DIRECT)
-       return 0;
-
-      start_sequence ();
-
-      /* Errors can leave us with a const0_rtx as operand.  */
-      if (GET_MODE (op0) != mode)
-       op0 = copy_to_mode_reg (mode, op0);
-      if (GET_MODE (op1) != mode)
-       op1 = copy_to_mode_reg (mode, op1);
-
-      if (!target)
-       target = gen_reg_rtx (mode);
-
-      for (i = 0; i < elts; ++i)
-       {
-         /* If this is part of a register, and not the first item in the
-            word, we can't store using a SUBREG - that would clobber
-            previous results.
-            And storing with a SUBREG is only possible for the least
-            significant part, hence we can't do it for big endian
-            (unless we want to permute the evaluation order.  */
-         if (GET_CODE (target) == REG
-             && (BYTES_BIG_ENDIAN
-                 ? subsize < UNITS_PER_WORD
-                 : ((i * subsize) % UNITS_PER_WORD) != 0))
-           t = NULL_RTX;
-         else
-           t = simplify_gen_subreg (submode, target, mode, i * subsize);
-         if (CONSTANT_P (op0))
-           a = simplify_gen_subreg (submode, op0, mode, i * subsize);
-         else
-           a = extract_bit_field (op0, subbitsize, i * subbitsize, unsignedp,
-                                  NULL_RTX, submode, submode, size);
-         if (CONSTANT_P (op1))
-           b = simplify_gen_subreg (submode, op1, mode, i * subsize);
-         else
-           b = extract_bit_field (op1, subbitsize, i * subbitsize, unsignedp,
-                                  NULL_RTX, submode, submode, size);
-
-         if (binoptab->code == DIV)
-           {
-             if (class == MODE_VECTOR_FLOAT)
-               res = expand_binop (submode, binoptab, a, b, t,
-                                   unsignedp, methods);
-             else
-               res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
-                                    a, b, t, unsignedp);
-           }
-         else
-           res = expand_binop (submode, binoptab, a, b, t,
-                               unsignedp, methods);
-
-         if (res == 0)
-           break;
-
-         if (t)
-           emit_move_insn (t, res);
-         else
-           store_bit_field (target, subbitsize, i * subbitsize, submode, res,
-                            size);
-       }
-      break;
-
-    default:
-      abort ();
-    }
-
-  seq = get_insns ();
-  end_sequence ();
-  emit_insn (seq);
-
-  return target;
-}
-
-/* Like expand_unop but for open-coding vector unops.  */
-
-static rtx
-expand_vector_unop (enum machine_mode mode, optab unoptab, rtx op0,
-                   rtx target, int unsignedp)
-{
-  enum machine_mode submode, tmode;
-  int size, elts, subsize, subbitsize, i;
-  rtx t, a, res, seq;
-
-  size = GET_MODE_SIZE (mode);
-  submode = GET_MODE_INNER (mode);
-
-  /* Search for the widest vector mode with the same inner mode that is
-     still narrower than MODE and that allows to open-code this operator.
-     Note, if we find such a mode and the handler later decides it can't
-     do the expansion, we'll be called recursively with the narrower mode.  */
-  for (tmode = GET_CLASS_NARROWEST_MODE (GET_MODE_CLASS (mode));
-       GET_MODE_SIZE (tmode) < GET_MODE_SIZE (mode);
-       tmode = GET_MODE_WIDER_MODE (tmode))
-    {
-      if (GET_MODE_INNER (tmode) == GET_MODE_INNER (mode)
-         && unoptab->handlers[(int) tmode].insn_code != CODE_FOR_nothing)
-       submode = tmode;
-    }
-  /* If there is no negate operation, try doing a subtract from zero.  */
-  if (unoptab == neg_optab && GET_MODE_CLASS (submode) == MODE_INT
-      /* Avoid infinite recursion when an
-        error has left us with the wrong mode.  */
-      && GET_MODE (op0) == mode)
-    {
-      rtx temp;
-      temp = expand_binop (mode, sub_optab, CONST0_RTX (mode), op0,
-                           target, unsignedp, OPTAB_DIRECT);
-      if (temp)
-       return temp;
-    }
-
-  if (unoptab == one_cmpl_optab)
-    {
-      tmode = int_mode_for_mode (mode);
-      if (tmode != BLKmode)
-       submode = tmode;
-    }
-
-  subsize = GET_MODE_SIZE (submode);
-  subbitsize = GET_MODE_BITSIZE (submode);
-  elts = size / subsize;
-
-  /* Errors can leave us with a const0_rtx as operand.  */
-  if (GET_MODE (op0) != mode)
-    op0 = copy_to_mode_reg (mode, op0);
-
-  if (!target)
-    target = gen_reg_rtx (mode);
-
-  start_sequence ();
-
-  for (i = 0; i < elts; ++i)
-    {
-      /* If this is part of a register, and not the first item in the
-        word, we can't store using a SUBREG - that would clobber
-        previous results.
-        And storing with a SUBREG is only possible for the least
-        significant part, hence we can't do it for big endian
-        (unless we want to permute the evaluation order.  */
-      if (GET_CODE (target) == REG
-         && (BYTES_BIG_ENDIAN
-             ?  subsize < UNITS_PER_WORD
-             : ((i * subsize) % UNITS_PER_WORD) != 0))
-       t = NULL_RTX;
-      else
-       t = simplify_gen_subreg (submode, target, mode, i * subsize);
-      if (CONSTANT_P (op0))
-       a = simplify_gen_subreg (submode, op0, mode, i * subsize);
-      else
-       a = extract_bit_field (op0, subbitsize, i * subbitsize, unsignedp,
-                              t, submode, submode, size);
-
-      res = expand_unop (submode, unoptab, a, t, unsignedp);
-
-      if (t)
-       emit_move_insn (t, res);
-      else
-       store_bit_field (target, subbitsize, i * subbitsize, submode, res,
-                        size);
-    }
-
-  seq = get_insns ();
-  end_sequence ();
-  emit_insn (seq);
-
-  return target;
-}
 \f
 /* Expand a binary operator which has both signed and unsigned forms.
    UOPTAB is the optab for unsigned operations, and SOPTAB is for
@@ -2122,29 +1757,124 @@ sign_expand_binop (enum machine_mode mode, optab uoptab, optab soptab,
   wide_soptab.handlers[(int) mode].insn_code = CODE_FOR_nothing;
   wide_soptab.handlers[(int) mode].libfunc = 0;
 
-  temp = expand_binop (mode, &wide_soptab, op0, op1, target,
-                      unsignedp, OPTAB_WIDEN);
+  temp = expand_binop (mode, &wide_soptab, op0, op1, target,
+                      unsignedp, OPTAB_WIDEN);
+
+  /* For unsigned operands, try widening to an unsigned int.  */
+  if (temp == 0 && unsignedp)
+    temp = expand_binop (mode, uoptab, op0, op1, target,
+                        unsignedp, OPTAB_WIDEN);
+  if (temp || methods == OPTAB_WIDEN)
+    return temp;
+
+  /* Use the right width lib call if that exists.  */
+  temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_LIB);
+  if (temp || methods == OPTAB_LIB)
+    return temp;
+
+  /* Must widen and use a lib call, use either signed or unsigned.  */
+  temp = expand_binop (mode, &wide_soptab, op0, op1, target,
+                      unsignedp, methods);
+  if (temp != 0)
+    return temp;
+  if (unsignedp)
+    return expand_binop (mode, uoptab, op0, op1, target,
+                        unsignedp, methods);
+  return 0;
+}
+\f
+/* Generate code to perform an operation specified by UNOPPTAB
+   on operand OP0, with two results to TARG0 and TARG1.
+   We assume that the order of the operands for the instruction
+   is TARG0, TARG1, OP0.
+
+   Either TARG0 or TARG1 may be zero, but what that means is that
+   the result is not actually wanted.  We will generate it into
+   a dummy pseudo-reg and discard it.  They may not both be zero.
+
+   Returns 1 if this operation can be performed; 0 if not.  */
+
+int
+expand_twoval_unop (optab unoptab, rtx op0, rtx targ0, rtx targ1,
+                   int unsignedp)
+{
+  enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1);
+  enum mode_class class;
+  enum machine_mode wider_mode;
+  rtx entry_last = get_last_insn ();
+  rtx last;
+
+  class = GET_MODE_CLASS (mode);
+
+  if (flag_force_mem)
+    op0 = force_not_mem (op0);
+
+  if (!targ0)
+    targ0 = gen_reg_rtx (mode);
+  if (!targ1)
+    targ1 = gen_reg_rtx (mode);
+
+  /* Record where to go back to if we fail.  */
+  last = get_last_insn ();
+
+  if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+    {
+      int icode = (int) unoptab->handlers[(int) mode].insn_code;
+      enum machine_mode mode0 = insn_data[icode].operand[2].mode;
+      rtx pat;
+      rtx xop0 = op0;
+
+      if (GET_MODE (xop0) != VOIDmode
+         && GET_MODE (xop0) != mode0)
+       xop0 = convert_to_mode (mode0, xop0, unsignedp);
+
+      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
+      if (! (*insn_data[icode].operand[2].predicate) (xop0, mode0))
+       xop0 = copy_to_mode_reg (mode0, xop0);
+
+      /* We could handle this, but we should always be called with a pseudo
+        for our targets and all insns should take them as outputs.  */
+      if (! (*insn_data[icode].operand[0].predicate) (targ0, mode)
+         || ! (*insn_data[icode].operand[1].predicate) (targ1, mode))
+       abort ();
+
+      pat = GEN_FCN (icode) (targ0, targ1, xop0);
+      if (pat)
+       {
+         emit_insn (pat);
+         return 1;
+       }
+      else
+       delete_insns_since (last);
+    }
+
+  /* It can't be done in this mode.  Can we do it in a wider mode?  */
 
-  /* For unsigned operands, try widening to an unsigned int.  */
-  if (temp == 0 && unsignedp)
-    temp = expand_binop (mode, uoptab, op0, op1, target,
-                        unsignedp, OPTAB_WIDEN);
-  if (temp || methods == OPTAB_WIDEN)
-    return temp;
+  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+    {
+      for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+          wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+       {
+         if (unoptab->handlers[(int) wider_mode].insn_code
+             != CODE_FOR_nothing)
+           {
+             rtx t0 = gen_reg_rtx (wider_mode);
+             rtx t1 = gen_reg_rtx (wider_mode);
+             rtx cop0 = convert_modes (wider_mode, mode, op0, unsignedp);
 
-  /* Use the right width lib call if that exists.  */
-  temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_LIB);
-  if (temp || methods == OPTAB_LIB)
-    return temp;
+             if (expand_twoval_unop (unoptab, cop0, t0, t1, unsignedp))
+               {
+                 convert_move (targ0, t0, unsignedp);
+                 convert_move (targ1, t1, unsignedp);
+                 return 1;
+               }
+             else
+               delete_insns_since (last);
+           }
+       }
+    }
 
-  /* Must widen and use a lib call, use either signed or unsigned.  */
-  temp = expand_binop (mode, &wide_soptab, op0, op1, target,
-                      unsignedp, methods);
-  if (temp != 0)
-    return temp;
-  if (unsignedp)
-    return expand_binop (mode, uoptab, op0, op1, target,
-                        unsignedp, methods);
+  delete_insns_since (entry_last);
   return 0;
 }
 \f
@@ -2172,32 +1902,25 @@ expand_twoval_binop (optab binoptab, rtx op0, rtx op1, rtx targ0, rtx targ1,
 
   class = GET_MODE_CLASS (mode);
 
-  op0 = protect_from_queue (op0, 0);
-  op1 = protect_from_queue (op1, 0);
-
   if (flag_force_mem)
     {
       op0 = force_not_mem (op0);
       op1 = force_not_mem (op1);
     }
 
-  /* If we are inside an appropriately-short loop and one operand is an
-     expensive constant, force it into a register.  */
-  if (CONSTANT_P (op0) && preserve_subexpressions_p ()
+  /* If we are inside an appropriately-short loop and we are optimizing,
+     force expensive constants into a register.  */
+  if (CONSTANT_P (op0) && optimize
       && rtx_cost (op0, binoptab->code) > COSTS_N_INSNS (1))
     op0 = force_reg (mode, op0);
 
-  if (CONSTANT_P (op1) && preserve_subexpressions_p ()
+  if (CONSTANT_P (op1) && optimize
       && rtx_cost (op1, binoptab->code) > COSTS_N_INSNS (1))
     op1 = force_reg (mode, op1);
 
-  if (targ0)
-    targ0 = protect_from_queue (targ0, 1);
-  else
+  if (!targ0)
     targ0 = gen_reg_rtx (mode);
-  if (targ1)
-    targ1 = protect_from_queue (targ1, 1);
-  else
+  if (!targ1)
     targ1 = gen_reg_rtx (mode);
 
   /* Record where to go back to if we fail.  */
@@ -2285,6 +2008,55 @@ expand_twoval_binop (optab binoptab, rtx op0, rtx op1, rtx targ0, rtx targ1,
   delete_insns_since (entry_last);
   return 0;
 }
+
+/* Expand the two-valued library call indicated by BINOPTAB, but
+   preserve only one of the values.  If TARG0 is non-NULL, the first
+   value is placed into TARG0; otherwise the second value is placed
+   into TARG1.  Exactly one of TARG0 and TARG1 must be non-NULL.  The
+   value stored into TARG0 or TARG1 is equivalent to (CODE OP0 OP1).
+   This routine assumes that the value returned by the library call is
+   as if the return value was of an integral mode twice as wide as the
+   mode of OP0.  Returns 1 if the call was successful.  */
+
+bool
+expand_twoval_binop_libfunc (optab binoptab, rtx op0, rtx op1,
+                            rtx targ0, rtx targ1, enum rtx_code code)
+{
+  enum machine_mode mode;
+  enum machine_mode libval_mode;
+  rtx libval;
+  rtx insns;
+
+  /* Exactly one of TARG0 or TARG1 should be non-NULL.  */
+  if (!((targ0 != NULL_RTX) ^ (targ1 != NULL_RTX)))
+    abort ();
+
+  mode = GET_MODE (op0);
+  if (!binoptab->handlers[(int) mode].libfunc)
+    return false;
+
+  /* The value returned by the library function will have twice as
+     many bits as the nominal MODE.  */
+  libval_mode = smallest_mode_for_size (2 * GET_MODE_BITSIZE (mode),
+                                       MODE_INT);
+  start_sequence ();
+  libval = emit_library_call_value (binoptab->handlers[(int) mode].libfunc,
+                                   NULL_RTX, LCT_CONST,
+                                   libval_mode, 2,
+                                   op0, mode,
+                                   op1, mode);
+  /* Get the part of VAL containing the value that we want.  */
+  libval = simplify_gen_subreg (mode, libval, libval_mode,
+                               targ0 ? 0 : GET_MODE_SIZE (mode));
+  insns = get_insns ();
+  end_sequence ();
+  /* Move the into the desired location.  */
+  emit_libcall_block (insns, targ0 ? targ0 : targ1, libval,
+                     gen_rtx_fmt_ee (code, mode, op0, op1));
+
+  return true;
+}
+
 \f
 /* Wrapper around expand_unop which takes an rtx code to specify
    the operation to perform, not an optab pointer.  All other
@@ -2365,7 +2137,7 @@ expand_parity (enum machine_mode mode, rtx op0, rtx target)
              temp = expand_unop (wider_mode, popcount_optab, xop0, NULL_RTX,
                                  true);
              if (temp != 0)
-               temp = expand_binop (wider_mode, and_optab, temp, GEN_INT (1),
+               temp = expand_binop (wider_mode, and_optab, temp, const1_rtx,
                                     target, true, OPTAB_DIRECT);
              if (temp == 0)
                delete_insns_since (last);
@@ -2377,6 +2149,131 @@ expand_parity (enum machine_mode mode, rtx op0, rtx target)
   return 0;
 }
 
+/* Extract the OMODE lowpart from VAL, which has IMODE.  Under certain 
+   conditions, VAL may already be a SUBREG against which we cannot generate
+   a further SUBREG.  In this case, we expect forcing the value into a
+   register will work around the situation.  */
+
+static rtx
+lowpart_subreg_maybe_copy (enum machine_mode omode, rtx val,
+                          enum machine_mode imode)
+{
+  rtx ret;
+  ret = lowpart_subreg (omode, val, imode);
+  if (ret == NULL)
+    {
+      val = force_reg (imode, val);
+      ret = lowpart_subreg (omode, val, imode);
+      gcc_assert (ret != NULL);
+    }
+  return ret;
+}
+
+/* Expand a floating point absolute value or negation operation via a
+   logical operation on the sign bit.  */
+
+static rtx
+expand_absneg_bit (enum rtx_code code, enum machine_mode mode,
+                  rtx op0, rtx target)
+{
+  const struct real_format *fmt;
+  int bitpos, word, nwords, i;
+  enum machine_mode imode;
+  HOST_WIDE_INT hi, lo;
+  rtx temp, insns;
+
+  /* The format has to have a simple sign bit.  */
+  fmt = REAL_MODE_FORMAT (mode);
+  if (fmt == NULL)
+    return NULL_RTX;
+
+  bitpos = fmt->signbit_rw;
+  if (bitpos < 0)
+    return NULL_RTX;
+
+  /* Don't create negative zeros if the format doesn't support them.  */
+  if (code == NEG && !fmt->has_signed_zero)
+    return NULL_RTX;
+
+  if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
+    {
+      imode = int_mode_for_mode (mode);
+      if (imode == BLKmode)
+       return NULL_RTX;
+      word = 0;
+      nwords = 1;
+    }
+  else
+    {
+      imode = word_mode;
+
+      if (FLOAT_WORDS_BIG_ENDIAN)
+       word = (GET_MODE_BITSIZE (mode) - bitpos) / BITS_PER_WORD;
+      else
+       word = bitpos / BITS_PER_WORD;
+      bitpos = bitpos % BITS_PER_WORD;
+      nwords = (GET_MODE_BITSIZE (mode) + BITS_PER_WORD - 1) / BITS_PER_WORD;
+    }
+
+  if (bitpos < HOST_BITS_PER_WIDE_INT)
+    {
+      hi = 0;
+      lo = (HOST_WIDE_INT) 1 << bitpos;
+    }
+  else
+    {
+      hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
+      lo = 0;
+    }
+  if (code == ABS)
+    lo = ~lo, hi = ~hi;
+
+  if (target == 0 || target == op0)
+    target = gen_reg_rtx (mode);
+
+  if (nwords > 1)
+    {
+      start_sequence ();
+
+      for (i = 0; i < nwords; ++i)
+       {
+         rtx targ_piece = operand_subword (target, i, 1, mode);
+         rtx op0_piece = operand_subword_force (op0, i, mode);
+       
+         if (i == word)
+           {
+             temp = expand_binop (imode, code == ABS ? and_optab : xor_optab,
+                                  op0_piece,
+                                  immed_double_const (lo, hi, imode),
+                                  targ_piece, 1, OPTAB_LIB_WIDEN);
+             if (temp != targ_piece)
+               emit_move_insn (targ_piece, temp);
+           }
+         else
+           emit_move_insn (targ_piece, op0_piece);
+       }
+
+      insns = get_insns ();
+      end_sequence ();
+
+      temp = gen_rtx_fmt_e (code, mode, copy_rtx (op0));
+      emit_no_conflict_block (insns, target, op0, NULL_RTX, temp);
+    }
+  else
+    {
+      temp = expand_binop (imode, code == ABS ? and_optab : xor_optab,
+                          gen_lowpart (imode, op0),
+                          immed_double_const (lo, hi, imode),
+                          gen_lowpart (imode, target), 1, OPTAB_LIB_WIDEN);
+      target = lowpart_subreg_maybe_copy (mode, temp, imode);
+
+      set_unique_reg_note (get_last_insn (), REG_EQUAL,
+                          gen_rtx_fmt_e (code, mode, copy_rtx (op0)));
+    }
+
+  return target;
+}
+
 /* Generate code to perform an operation specified by UNOPTAB
    on operand OP0, with result having machine-mode MODE.
 
@@ -2400,15 +2297,8 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
 
   class = GET_MODE_CLASS (mode);
 
-  op0 = protect_from_queue (op0, 0);
-
   if (flag_force_mem)
-    {
-      op0 = force_not_mem (op0);
-    }
-
-  if (target)
-    target = protect_from_queue (target, 1);
+    op0 = force_not_mem (op0);
 
   if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
     {
@@ -2535,87 +2425,27 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
       return target;
     }
 
-  /* Open-code the complex negation operation.  */
-  else if (unoptab->code == NEG
-          && (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT))
-    {
-      rtx target_piece;
-      rtx x;
-      rtx seq;
-
-      /* Find the correct mode for the real and imaginary parts.  */
-      enum machine_mode submode = GET_MODE_INNER (mode);
-
-      if (submode == BLKmode)
-       abort ();
-
-      if (target == 0)
-       target = gen_reg_rtx (mode);
-
-      start_sequence ();
-
-      target_piece = gen_imagpart (submode, target);
-      x = expand_unop (submode, unoptab,
-                      gen_imagpart (submode, op0),
-                      target_piece, unsignedp);
-      if (target_piece != x)
-       emit_move_insn (target_piece, x);
-
-      target_piece = gen_realpart (submode, target);
-      x = expand_unop (submode, unoptab,
-                      gen_realpart (submode, op0),
-                      target_piece, unsignedp);
-      if (target_piece != x)
-       emit_move_insn (target_piece, x);
-
-      seq = get_insns ();
-      end_sequence ();
-
-      emit_no_conflict_block (seq, target, op0, 0,
-                             gen_rtx_fmt_e (unoptab->code, mode,
-                                            copy_rtx (op0)));
-      return target;
-    }
-
-  /* Try negating floating point values by flipping the sign bit.  */
-  if (unoptab->code == NEG && class == MODE_FLOAT
-      && GET_MODE_BITSIZE (mode) <= 2 * HOST_BITS_PER_WIDE_INT)
+  if (unoptab->code == NEG)
     {
-      const struct real_format *fmt = real_format_for_mode[mode - QFmode];
-      enum machine_mode imode = int_mode_for_mode (mode);
-      int bitpos = (fmt != 0) ? fmt->signbit : -1;
-
-      if (imode != BLKmode && bitpos >= 0 && fmt->has_signed_zero)
+      /* Try negating floating point values by flipping the sign bit.  */
+      if (class == MODE_FLOAT)
        {
-         HOST_WIDE_INT hi, lo;
-         rtx last = get_last_insn ();
-
-         /* Handle targets with different FP word orders.  */
-         if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN)
-           {
-             int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
-             int word = nwords - (bitpos / BITS_PER_WORD) - 1;
-             bitpos = word * BITS_PER_WORD + bitpos % BITS_PER_WORD;
-           }
+         temp = expand_absneg_bit (NEG, mode, op0, target);
+         if (temp)
+           return temp;
+       }
 
-         if (bitpos < HOST_BITS_PER_WIDE_INT)
-           {
-             hi = 0;
-             lo = (HOST_WIDE_INT) 1 << bitpos;
-           }
-         else
-           {
-             hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
-             lo = 0;
-           }
-         temp = expand_binop (imode, xor_optab,
-                              gen_lowpart (imode, op0),
-                              immed_double_const (lo, hi, imode),
-                              NULL_RTX, 1, OPTAB_LIB_WIDEN);
-         if (temp != 0)
-           return gen_lowpart (mode, temp);
-         delete_insns_since (last);
-        }
+      /* If there is no negation pattern, and we have no negative zero,
+        try subtracting from zero.  */
+      if (!HONOR_SIGNED_ZEROS (mode))
+       {
+         temp = expand_binop (mode, (unoptab == negv_optab
+                                     ? subv_optab : sub_optab),
+                              CONST0_RTX (mode), op0, target,
+                              unsignedp, OPTAB_DIRECT);
+         if (temp)
+           return temp;
+       }
     }
 
   /* Try calculating parity (x) as popcount (x) % 2.  */
@@ -2638,7 +2468,8 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
         have them return something that isn't a double-word.  */
       if (unoptab == ffs_optab || unoptab == clz_optab || unoptab == ctz_optab
          || unoptab == popcount_optab || unoptab == parity_optab)
-       outmode = TYPE_MODE (integer_type_node);
+       outmode
+           = GET_MODE (hard_libcall_value (TYPE_MODE (integer_type_node)));
 
       start_sequence ();
 
@@ -2657,9 +2488,6 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
       return target;
     }
 
-  if (class == MODE_VECTOR_FLOAT || class == MODE_VECTOR_INT)
-    return expand_vector_unop (mode, unoptab, op0, target, unsignedp);
-
   /* It can't be done in this mode.  Can we do it in a wider mode?  */
 
   if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
@@ -2711,9 +2539,9 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
        }
     }
 
-  /* If there is no negate operation, try doing a subtract from zero.
-     The US Software GOFAST library needs this.  */
-  if (unoptab->code == NEG)
+  /* One final attempt at implementing negation via subtraction,
+     this time allowing widening of the operand.  */
+  if (unoptab->code == NEG && !HONOR_SIGNED_ZEROS (mode))
     {
       rtx temp;
       temp = expand_binop (mode,
@@ -2721,7 +2549,7 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
                            CONST0_RTX (mode), op0,
                            target, unsignedp, OPTAB_LIB_WIDEN);
       if (temp)
-       return temp;
+        return temp;
     }
 
   return 0;
@@ -2752,48 +2580,16 @@ expand_abs_nojump (enum machine_mode mode, rtx op0, rtx target,
     return temp;
 
   /* For floating point modes, try clearing the sign bit.  */
-  if (GET_MODE_CLASS (mode) == MODE_FLOAT
-      && GET_MODE_BITSIZE (mode) <= 2 * HOST_BITS_PER_WIDE_INT)
+  if (GET_MODE_CLASS (mode) == MODE_FLOAT)
     {
-      const struct real_format *fmt = real_format_for_mode[mode - QFmode];
-      enum machine_mode imode = int_mode_for_mode (mode);
-      int bitpos = (fmt != 0) ? fmt->signbit : -1;
-
-      if (imode != BLKmode && bitpos >= 0)
-       {
-         HOST_WIDE_INT hi, lo;
-         rtx last = get_last_insn ();
-
-         /* Handle targets with different FP word orders.  */
-         if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN)
-           {
-             int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
-             int word = nwords - (bitpos / BITS_PER_WORD) - 1;
-             bitpos = word * BITS_PER_WORD + bitpos % BITS_PER_WORD;
-           }
-
-         if (bitpos < HOST_BITS_PER_WIDE_INT)
-           {
-             hi = 0;
-             lo = (HOST_WIDE_INT) 1 << bitpos;
-           }
-         else
-           {
-             hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
-             lo = 0;
-           }
-         temp = expand_binop (imode, and_optab,
-                              gen_lowpart (imode, op0),
-                              immed_double_const (~lo, ~hi, imode),
-                              NULL_RTX, 1, OPTAB_LIB_WIDEN);
-         if (temp != 0)
-           return gen_lowpart (mode, temp);
-         delete_insns_since (last);
-       }
+      temp = expand_absneg_bit (ABS, mode, op0, target);
+      if (temp)
+       return temp;
     }
 
   /* If we have a MAX insn, we can do this as MAX (x, -x).  */
-  if (smax_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+  if (smax_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing
+      && !HONOR_SIGNED_ZEROS (mode))
     {
       rtx last = get_last_insn ();
 
@@ -2848,15 +2644,15 @@ expand_abs (enum machine_mode mode, rtx op0, rtx target,
 
   /* It is safe to use the target if it is the same
      as the source if this is also a pseudo register */
-  if (op0 == target && GET_CODE (op0) == REG
+  if (op0 == target && REG_P (op0)
       && REGNO (op0) >= FIRST_PSEUDO_REGISTER)
     safe = 1;
 
   op1 = gen_label_rtx ();
   if (target == 0 || ! safe
       || GET_MODE (target) != mode
-      || (GET_CODE (target) == MEM && MEM_VOLATILE_P (target))
-      || (GET_CODE (target) == REG
+      || (MEM_P (target) && MEM_VOLATILE_P (target))
+      || (REG_P (target)
          && REGNO (target) < FIRST_PSEUDO_REGISTER))
     target = gen_reg_rtx (mode);
 
@@ -2881,205 +2677,238 @@ expand_abs (enum machine_mode mode, rtx op0, rtx target,
   OK_DEFER_POP;
   return target;
 }
-\f
-/* Emit code to compute the absolute value of OP0, with result to
-   TARGET if convenient.  (TARGET may be 0.)  The return value says
-   where the result actually is to be found.
-
-   MODE is the mode of the operand; the mode of the result is
-   different but can be deduced from MODE.
 
-   UNSIGNEDP is relevant for complex integer modes.  */
+/* A subroutine of expand_copysign, perform the copysign operation using the
+   abs and neg primitives advertised to exist on the target.  The assumption
+   is that we have a split register file, and leaving op0 in fp registers,
+   and not playing with subregs so much, will help the register allocator.  */
 
-rtx
-expand_complex_abs (enum machine_mode mode, rtx op0, rtx target,
-                   int unsignedp)
+static rtx
+expand_copysign_absneg (enum machine_mode mode, rtx op0, rtx op1, rtx target,
+                       int bitpos, bool op0_is_abs)
 {
-  enum mode_class class = GET_MODE_CLASS (mode);
-  enum machine_mode wider_mode;
-  rtx temp;
-  rtx entry_last = get_last_insn ();
-  rtx last;
-  rtx pat;
-  optab this_abs_optab;
-
-  /* Find the correct mode for the real and imaginary parts.  */
-  enum machine_mode submode = GET_MODE_INNER (mode);
-
-  if (submode == BLKmode)
-    abort ();
+  enum machine_mode imode;
+  HOST_WIDE_INT hi, lo;
+  int word;
+  rtx label;
 
-  op0 = protect_from_queue (op0, 0);
+  if (target == op1)
+    target = NULL_RTX;
 
-  if (flag_force_mem)
+  if (!op0_is_abs)
     {
-      op0 = force_not_mem (op0);
+      op0 = expand_unop (mode, abs_optab, op0, target, 0);
+      if (op0 == NULL)
+       return NULL_RTX;
+      target = op0;
     }
-
-  last = get_last_insn ();
-
-  if (target)
-    target = protect_from_queue (target, 1);
-
-  this_abs_optab = ! unsignedp && flag_trapv
-                   && (GET_MODE_CLASS(mode) == MODE_INT)
-                   ? absv_optab : abs_optab;
-
-  if (this_abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+  else
     {
-      int icode = (int) this_abs_optab->handlers[(int) mode].insn_code;
-      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
-      rtx xop0 = op0;
-
-      if (target)
-       temp = target;
+      if (target == NULL_RTX)
+        target = copy_to_reg (op0);
       else
-       temp = gen_reg_rtx (submode);
-
-      if (GET_MODE (xop0) != VOIDmode
-         && GET_MODE (xop0) != mode0)
-       xop0 = convert_to_mode (mode0, xop0, unsignedp);
-
-      /* Now, if insn doesn't accept our operand, put it into a pseudo.  */
-
-      if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0))
-       xop0 = copy_to_mode_reg (mode0, xop0);
-
-      if (! (*insn_data[icode].operand[0].predicate) (temp, submode))
-       temp = gen_reg_rtx (submode);
-
-      pat = GEN_FCN (icode) (temp, xop0);
-      if (pat)
-       {
-         if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
-             && ! add_equal_note (pat, temp, this_abs_optab->code, xop0,
-                                  NULL_RTX))
-           {
-             delete_insns_since (last);
-             return expand_unop (mode, this_abs_optab, op0, NULL_RTX,
-                                 unsignedp);
-           }
-
-         emit_insn (pat);
+       emit_move_insn (target, op0);
+    }
 
-         return temp;
-       }
+  if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
+    {
+      imode = int_mode_for_mode (mode);
+      if (imode == BLKmode)
+       return NULL_RTX;
+      op1 = gen_lowpart (imode, op1);
+    }
+  else
+    {
+      imode = word_mode;
+      if (FLOAT_WORDS_BIG_ENDIAN)
+       word = (GET_MODE_BITSIZE (mode) - bitpos) / BITS_PER_WORD;
       else
-       delete_insns_since (last);
+       word = bitpos / BITS_PER_WORD;
+      bitpos = bitpos % BITS_PER_WORD;
+      op1 = operand_subword_force (op1, word, mode);
     }
 
-  /* It can't be done in this mode.  Can we open-code it in a wider mode?  */
-
-  for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
-       wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+  if (bitpos < HOST_BITS_PER_WIDE_INT)
     {
-      if (this_abs_optab->handlers[(int) wider_mode].insn_code
-         != CODE_FOR_nothing)
-       {
-         rtx xop0 = op0;
+      hi = 0;
+      lo = (HOST_WIDE_INT) 1 << bitpos;
+    }
+  else
+    {
+      hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
+      lo = 0;
+    }
 
-         xop0 = convert_modes (wider_mode, mode, xop0, unsignedp);
-         temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp);
+  op1 = expand_binop (imode, and_optab, op1,
+                     immed_double_const (lo, hi, imode),
+                     NULL_RTX, 1, OPTAB_LIB_WIDEN);
 
-         if (temp)
-           {
-             if (class != MODE_COMPLEX_INT)
-               {
-                 if (target == 0)
-                   target = gen_reg_rtx (submode);
-                 convert_move (target, temp, 0);
-                 return target;
-               }
-             else
-               return gen_lowpart (submode, temp);
-           }
-         else
-           delete_insns_since (last);
-       }
-    }
+  label = gen_label_rtx ();
+  emit_cmp_and_jump_insns (op1, const0_rtx, EQ, NULL_RTX, imode, 1, label);
+
+  if (GET_CODE (op0) == CONST_DOUBLE)
+    op0 = simplify_unary_operation (NEG, mode, op0, mode);
+  else
+    op0 = expand_unop (mode, neg_optab, op0, target, 0);
+  if (op0 != target)
+    emit_move_insn (target, op0);
 
-  /* Open-code the complex absolute-value operation
-     if we can open-code sqrt.  Otherwise it's not worth while.  */
-  if (sqrt_optab->handlers[(int) submode].insn_code != CODE_FOR_nothing
-      && ! flag_trapv)
-    {
-      rtx real, imag, total;
+  emit_label (label);
+
+  return target;
+}
 
-      real = gen_realpart (submode, op0);
-      imag = gen_imagpart (submode, op0);
 
-      /* Square both parts.  */
-      real = expand_mult (submode, real, real, NULL_RTX, 0);
-      imag = expand_mult (submode, imag, imag, NULL_RTX, 0);
+/* A subroutine of expand_copysign, perform the entire copysign operation
+   with integer bitmasks.  BITPOS is the position of the sign bit; OP0_IS_ABS
+   is true if op0 is known to have its sign bit clear.  */
 
-      /* Sum the parts.  */
-      total = expand_binop (submode, add_optab, real, imag, NULL_RTX,
-                           0, OPTAB_LIB_WIDEN);
+static rtx
+expand_copysign_bit (enum machine_mode mode, rtx op0, rtx op1, rtx target,
+                    int bitpos, bool op0_is_abs)
+{
+  enum machine_mode imode;
+  HOST_WIDE_INT hi, lo;
+  int word, nwords, i;
+  rtx temp, insns;
 
-      /* Get sqrt in TARGET.  Set TARGET to where the result is.  */
-      target = expand_unop (submode, sqrt_optab, total, target, 0);
-      if (target == 0)
-       delete_insns_since (last);
+  if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
+    {
+      imode = int_mode_for_mode (mode);
+      if (imode == BLKmode)
+       return NULL_RTX;
+      word = 0;
+      nwords = 1;
+    }
+  else
+    {
+      imode = word_mode;
+
+      if (FLOAT_WORDS_BIG_ENDIAN)
+       word = (GET_MODE_BITSIZE (mode) - bitpos) / BITS_PER_WORD;
       else
-       return target;
+       word = bitpos / BITS_PER_WORD;
+      bitpos = bitpos % BITS_PER_WORD;
+      nwords = (GET_MODE_BITSIZE (mode) + BITS_PER_WORD - 1) / BITS_PER_WORD;
     }
 
-  /* Now try a library call in this mode.  */
-  if (this_abs_optab->handlers[(int) mode].libfunc)
+  if (bitpos < HOST_BITS_PER_WIDE_INT)
     {
-      rtx insns;
-      rtx value;
+      hi = 0;
+      lo = (HOST_WIDE_INT) 1 << bitpos;
+    }
+  else
+    {
+      hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
+      lo = 0;
+    }
+
+  if (target == 0 || target == op0 || target == op1)
+    target = gen_reg_rtx (mode);
 
+  if (nwords > 1)
+    {
       start_sequence ();
 
-      /* 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 (abs_optab->handlers[(int) mode].libfunc,
-                                      NULL_RTX, LCT_CONST, submode, 1, op0, mode);
+      for (i = 0; i < nwords; ++i)
+       {
+         rtx targ_piece = operand_subword (target, i, 1, mode);
+         rtx op0_piece = operand_subword_force (op0, i, mode);
+       
+         if (i == word)
+           {
+             if (!op0_is_abs)
+               op0_piece = expand_binop (imode, and_optab, op0_piece,
+                                         immed_double_const (~lo, ~hi, imode),
+                                         NULL_RTX, 1, OPTAB_LIB_WIDEN);
+
+             op1 = expand_binop (imode, and_optab,
+                                 operand_subword_force (op1, i, mode),
+                                 immed_double_const (lo, hi, imode),
+                                 NULL_RTX, 1, OPTAB_LIB_WIDEN);
+
+             temp = expand_binop (imode, ior_optab, op0_piece, op1,
+                                  targ_piece, 1, OPTAB_LIB_WIDEN);
+             if (temp != targ_piece)
+               emit_move_insn (targ_piece, temp);
+           }
+         else
+           emit_move_insn (targ_piece, op0_piece);
+       }
+
       insns = get_insns ();
       end_sequence ();
 
-      target = gen_reg_rtx (submode);
-      emit_libcall_block (insns, target, value,
-                         gen_rtx_fmt_e (this_abs_optab->code, mode, op0));
-
-      return target;
+      emit_no_conflict_block (insns, target, op0, op1, NULL_RTX);
+    }
+  else
+    {
+      op1 = expand_binop (imode, and_optab, gen_lowpart (imode, op1),
+                         immed_double_const (lo, hi, imode),
+                         NULL_RTX, 1, OPTAB_LIB_WIDEN);
+
+      op0 = gen_lowpart (imode, op0);
+      if (!op0_is_abs)
+       op0 = expand_binop (imode, and_optab, op0,
+                           immed_double_const (~lo, ~hi, imode),
+                           NULL_RTX, 1, OPTAB_LIB_WIDEN);
+
+      temp = expand_binop (imode, ior_optab, op0, op1,
+                          gen_lowpart (imode, target), 1, OPTAB_LIB_WIDEN);
+      target = lowpart_subreg_maybe_copy (mode, temp, imode);
     }
 
-  /* It can't be done in this mode.  Can we do it in a wider mode?  */
+  return target;
+}
 
-  for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
-       wider_mode = GET_MODE_WIDER_MODE (wider_mode))
-    {
-      if ((this_abs_optab->handlers[(int) wider_mode].insn_code
-          != CODE_FOR_nothing)
-         || this_abs_optab->handlers[(int) wider_mode].libfunc)
-       {
-         rtx xop0 = op0;
+/* Expand the C99 copysign operation.  OP0 and OP1 must be the same 
+   scalar floating point mode.  Return NULL if we do not know how to
+   expand the operation inline.  */
 
-         xop0 = convert_modes (wider_mode, mode, xop0, unsignedp);
+rtx
+expand_copysign (rtx op0, rtx op1, rtx target)
+{
+  enum machine_mode mode = GET_MODE (op0);
+  const struct real_format *fmt;
+  bool op0_is_abs;
+  rtx temp;
 
-         temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp);
+  gcc_assert (SCALAR_FLOAT_MODE_P (mode));
+  gcc_assert (GET_MODE (op1) == mode);
 
-         if (temp)
-           {
-             if (class != MODE_COMPLEX_INT)
-               {
-                 if (target == 0)
-                   target = gen_reg_rtx (submode);
-                 convert_move (target, temp, 0);
-                 return target;
-               }
-             else
-               return gen_lowpart (submode, temp);
-           }
-         else
-           delete_insns_since (last);
-       }
+  /* First try to do it with a special instruction.  */
+  temp = expand_binop (mode, copysign_optab, op0, op1,
+                      target, 0, OPTAB_DIRECT);
+  if (temp)
+    return temp;
+
+  fmt = REAL_MODE_FORMAT (mode);
+  if (fmt == NULL || !fmt->has_signed_zero)
+    return NULL_RTX;
+
+  op0_is_abs = false;
+  if (GET_CODE (op0) == CONST_DOUBLE)
+    {
+      if (real_isneg (CONST_DOUBLE_REAL_VALUE (op0)))
+       op0 = simplify_unary_operation (ABS, mode, op0, mode);
+      op0_is_abs = true;
     }
 
-  delete_insns_since (entry_last);
-  return 0;
+  if (fmt->signbit_ro >= 0
+      && (GET_CODE (op0) == CONST_DOUBLE
+         || (neg_optab->handlers[mode].insn_code != CODE_FOR_nothing
+             && abs_optab->handlers[mode].insn_code != CODE_FOR_nothing)))
+    {
+      temp = expand_copysign_absneg (mode, op0, op1, target,
+                                    fmt->signbit_ro, op0_is_abs);
+      if (temp)
+       return temp;
+    }
+
+  if (fmt->signbit_rw < 0)
+    return NULL_RTX;
+  return expand_copysign_bit (mode, op0, op1, target,
+                             fmt->signbit_rw, op0_is_abs);
 }
 \f
 /* Generate an instruction whose insn-code is INSN_CODE,
@@ -3095,9 +2924,7 @@ emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
   enum machine_mode mode0 = insn_data[icode].operand[1].mode;
   rtx pat;
 
-  temp = target = protect_from_queue (target, 1);
-
-  op0 = protect_from_queue (op0, 0);
+  temp = target;
 
   /* Sign and zero extension from memory is often done specially on
      RISC machines, so forcing into a register here can pessimize
@@ -3111,7 +2938,7 @@ emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
     op0 = copy_to_mode_reg (mode0, op0);
 
   if (! (*insn_data[icode].operand[0].predicate) (temp, GET_MODE (temp))
-      || (flag_force_mem && GET_CODE (temp) == MEM))
+      || (flag_force_mem && MEM_P (temp)))
     temp = gen_reg_rtx (GET_MODE (temp));
 
   pat = GEN_FCN (icode) (temp, op0);
@@ -3158,11 +2985,11 @@ emit_no_conflict_block (rtx insns, rtx target, rtx op0, rtx op1, rtx equiv)
 {
   rtx prev, next, first, last, insn;
 
-  if (GET_CODE (target) != REG || reload_in_progress)
+  if (!REG_P (target) || reload_in_progress)
     return emit_insn (insns);
   else
     for (insn = insns; insn; insn = NEXT_INSN (insn))
-      if (GET_CODE (insn) != INSN
+      if (!NONJUMP_INSN_P (insn)
          || find_reg_note (insn, REG_LIBCALL, NULL_RTX))
        return emit_insn (insns);
 
@@ -3175,7 +3002,7 @@ emit_no_conflict_block (rtx insns, rtx target, rtx op0, rtx op1, rtx equiv)
 
       next = NEXT_INSN (insn);
 
-      /* Some ports (cris) create an libcall regions at their own.  We must
+      /* Some ports (cris) create a libcall regions at their own.  We must
         avoid any potential nesting of LIBCALLs.  */
       if ((note = find_reg_note (insn, REG_LIBCALL, NULL)) != NULL)
        remove_note (insn, note);
@@ -3224,11 +3051,11 @@ emit_no_conflict_block (rtx insns, rtx target, rtx op0, rtx op1, rtx equiv)
       next = NEXT_INSN (insn);
       add_insn (insn);
 
-      if (op1 && GET_CODE (op1) == REG)
+      if (op1 && REG_P (op1))
        REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_NO_CONFLICT, op1,
                                              REG_NOTES (insn));
 
-      if (op0 && GET_CODE (op0) == REG)
+      if (op0 && REG_P (op0))
        REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_NO_CONFLICT, op0,
                                              REG_NOTES (insn));
     }
@@ -3306,7 +3133,7 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv)
   if (flag_non_call_exceptions && may_trap_p (equiv))
     {
       for (insn = insns; insn; insn = NEXT_INSN (insn))
-       if (GET_CODE (insn) == CALL_INSN)
+       if (CALL_P (insn))
          {
            rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
 
@@ -3320,14 +3147,14 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv)
      goto (unless there is already a REG_EH_REGION note, in which case
      we update it).  */
     for (insn = insns; insn; insn = NEXT_INSN (insn))
-      if (GET_CODE (insn) == CALL_INSN)
+      if (CALL_P (insn))
        {
          rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
 
          if (note != 0)
-           XEXP (note, 0) = GEN_INT (-1);
+           XEXP (note, 0) = constm1_rtx;
          else
-           REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, GEN_INT (-1),
+           REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, constm1_rtx,
                                                  REG_NOTES (insn));
        }
 
@@ -3342,7 +3169,7 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv)
       rtx set = single_set (insn);
       rtx note;
 
-      /* Some ports (cris) create an libcall regions at their own.  We must
+      /* Some ports (cris) create a libcall regions at their own.  We must
         avoid any potential nesting of LIBCALLs.  */
       if ((note = find_reg_note (insn, REG_LIBCALL, NULL)) != NULL)
        remove_note (insn, note);
@@ -3351,7 +3178,7 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv)
 
       next = NEXT_INSN (insn);
 
-      if (set != 0 && GET_CODE (SET_DEST (set)) == REG
+      if (set != 0 && REG_P (SET_DEST (set))
          && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER
          && (insn == insns
              || ((! INSN_P(insns)
@@ -3373,7 +3200,7 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv)
 
       /* Some ports use a loop to copy large arguments onto the stack.
         Don't move anything outside such a loop.  */
-      if (GET_CODE (insn) == CODE_LABEL)
+      if (LABEL_P (insn))
        break;
     }
 
@@ -3436,23 +3263,6 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv)
     }
 }
 \f
-/* Generate code to store zero in X.  */
-
-void
-emit_clr_insn (rtx x)
-{
-  emit_move_insn (x, const0_rtx);
-}
-
-/* Generate code to store 1 in X
-   assuming it contains zero beforehand.  */
-
-void
-emit_0_to_1_insn (rtx x)
-{
-  emit_move_insn (x, const1_rtx);
-}
-
 /* Nonzero if we can perform a comparison of mode MODE straightforwardly.
    PURPOSE describes how this comparison will be used.  CODE is the rtx
    comparison code we will be using.
@@ -3486,7 +3296,6 @@ can_compare_p (enum rtx_code code, enum machine_mode mode,
       if (purpose == ccp_store_flag
          && cstore_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
        return 1;
-
       mode = GET_MODE_WIDER_MODE (mode);
     }
   while (mode != VOIDmode);
@@ -3542,13 +3351,13 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size,
        }
     }
 
-  /* If we are inside an appropriately-short loop and one operand is an
-     expensive constant, force it into a register.  */
-  if (CONSTANT_P (x) && preserve_subexpressions_p ()
+  /* If we are inside an appropriately-short loop and we are optimizing,
+     force expensive constants into a register.  */
+  if (CONSTANT_P (x) && optimize
       && rtx_cost (x, COMPARE) > COSTS_N_INSNS (1))
     x = force_reg (mode, x);
 
-  if (CONSTANT_P (y) && preserve_subexpressions_p ()
+  if (CONSTANT_P (y) && optimize
       && rtx_cost (y, COMPARE) > COSTS_N_INSNS (1))
     y = force_reg (mode, y);
 
@@ -3568,111 +3377,76 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size,
 
   if (mode == BLKmode)
     {
+      enum machine_mode cmp_mode, result_mode;
+      enum insn_code cmp_code;
+      tree length_type;
+      rtx libfunc;
       rtx result;
-      enum machine_mode result_mode;
-      rtx opalign ATTRIBUTE_UNUSED
+      rtx opalign
        = GEN_INT (MIN (MEM_ALIGN (x), MEM_ALIGN (y)) / BITS_PER_UNIT);
 
-      emit_queue ();
-      x = protect_from_queue (x, 0);
-      y = protect_from_queue (y, 0);
-
       if (size == 0)
        abort ();
-#ifdef HAVE_cmpmemqi
-      if (HAVE_cmpmemqi
-         && GET_CODE (size) == CONST_INT
-         && INTVAL (size) < (1 << GET_MODE_BITSIZE (QImode)))
-       {
-         result_mode = insn_data[(int) CODE_FOR_cmpmemqi].operand[0].mode;
-         result = gen_reg_rtx (result_mode);
-         emit_insn (gen_cmpmemqi (result, x, y, size, opalign));
-       }
-      else
-#endif
-#ifdef HAVE_cmpmemhi
-      if (HAVE_cmpmemhi
-         && GET_CODE (size) == CONST_INT
-         && INTVAL (size) < (1 << GET_MODE_BITSIZE (HImode)))
-       {
-         result_mode = insn_data[(int) CODE_FOR_cmpmemhi].operand[0].mode;
-         result = gen_reg_rtx (result_mode);
-         emit_insn (gen_cmpmemhi (result, x, y, size, opalign));
-       }
-      else
-#endif
-#ifdef HAVE_cmpmemsi
-      if (HAVE_cmpmemsi)
-       {
-         result_mode = insn_data[(int) CODE_FOR_cmpmemsi].operand[0].mode;
-         result = gen_reg_rtx (result_mode);
-         size = protect_from_queue (size, 0);
-         emit_insn (gen_cmpmemsi (result, x, y,
-                                  convert_to_mode (SImode, size, 1),
-                                  opalign));
-       }
-      else
-#endif
-#ifdef HAVE_cmpstrqi
-      if (HAVE_cmpstrqi
-         && GET_CODE (size) == CONST_INT
-         && INTVAL (size) < (1 << GET_MODE_BITSIZE (QImode)))
-       {
-         result_mode = insn_data[(int) CODE_FOR_cmpstrqi].operand[0].mode;
-         result = gen_reg_rtx (result_mode);
-         emit_insn (gen_cmpstrqi (result, x, y, size, opalign));
-       }
-      else
-#endif
-#ifdef HAVE_cmpstrhi
-      if (HAVE_cmpstrhi
-         && GET_CODE (size) == CONST_INT
-         && INTVAL (size) < (1 << GET_MODE_BITSIZE (HImode)))
-       {
-         result_mode = insn_data[(int) CODE_FOR_cmpstrhi].operand[0].mode;
-         result = gen_reg_rtx (result_mode);
-         emit_insn (gen_cmpstrhi (result, x, y, size, opalign));
-       }
-      else
-#endif
-#ifdef HAVE_cmpstrsi
-      if (HAVE_cmpstrsi)
+
+      /* Try to use a memory block compare insn - either cmpstr
+        or cmpmem will do.  */
+      for (cmp_mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+          cmp_mode != VOIDmode;
+          cmp_mode = GET_MODE_WIDER_MODE (cmp_mode))
        {
-         result_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
+         cmp_code = cmpmem_optab[cmp_mode];
+         if (cmp_code == CODE_FOR_nothing)
+           cmp_code = cmpstr_optab[cmp_mode];
+         if (cmp_code == CODE_FOR_nothing)
+           continue;
+
+         /* Must make sure the size fits the insn's mode.  */
+         if ((GET_CODE (size) == CONST_INT
+              && INTVAL (size) >= (1 << GET_MODE_BITSIZE (cmp_mode)))
+             || (GET_MODE_BITSIZE (GET_MODE (size))
+                 > GET_MODE_BITSIZE (cmp_mode)))
+           continue;
+
+         result_mode = insn_data[cmp_code].operand[0].mode;
          result = gen_reg_rtx (result_mode);
-         size = protect_from_queue (size, 0);
-         emit_insn (gen_cmpstrsi (result, x, y,
-                                  convert_to_mode (SImode, size, 1),
-                                  opalign));
-       }
-      else
-#endif
-       {
-#ifdef TARGET_MEM_FUNCTIONS
-         result = emit_library_call_value (memcmp_libfunc, NULL_RTX, LCT_PURE_MAKE_BLOCK,
-                                           TYPE_MODE (integer_type_node), 3,
-                                           XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
-                                           convert_to_mode (TYPE_MODE (sizetype), size,
-                                                            TREE_UNSIGNED (sizetype)),
-                                           TYPE_MODE (sizetype));
-#else
-         result = emit_library_call_value (bcmp_libfunc, NULL_RTX, LCT_PURE_MAKE_BLOCK,
-                                           TYPE_MODE (integer_type_node), 3,
-                                           XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
-                                           convert_to_mode (TYPE_MODE (integer_type_node),
-                                                            size,
-                                                            TREE_UNSIGNED (integer_type_node)),
-                                           TYPE_MODE (integer_type_node));
-#endif
+         size = convert_to_mode (cmp_mode, size, 1);
+         emit_insn (GEN_FCN (cmp_code) (result, x, y, size, opalign));
 
-         result_mode = TYPE_MODE (integer_type_node);
+         *px = result;
+         *py = const0_rtx;
+         *pmode = result_mode;
+         return;
        }
+
+      /* Otherwise call a library function, memcmp.  */
+      libfunc = memcmp_libfunc;
+      length_type = sizetype;
+      result_mode = TYPE_MODE (integer_type_node);
+      cmp_mode = TYPE_MODE (length_type);
+      size = convert_to_mode (TYPE_MODE (length_type), size,
+                             TYPE_UNSIGNED (length_type));
+
+      result = emit_library_call_value (libfunc, 0, LCT_PURE_MAKE_BLOCK,
+                                       result_mode, 3,
+                                       XEXP (x, 0), Pmode,
+                                       XEXP (y, 0), Pmode,
+                                       size, cmp_mode);
       *px = result;
       *py = const0_rtx;
       *pmode = result_mode;
       return;
     }
 
+  /* Don't allow operands to the compare to trap, as that can put the
+     compare and branch in different basic blocks.  */
+  if (flag_non_call_exceptions)
+    {
+      if (may_trap_p (x))
+       x = force_reg (mode, x);
+      if (may_trap_p (y))
+       y = force_reg (mode, y);
+    }
+
   *px = x;
   *py = y;
   if (can_compare_p (*pcomparison, mode, purpose))
@@ -3693,12 +3467,19 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size,
       result = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST_MAKE_BLOCK,
                                        word_mode, 2, x, mode, y, mode);
 
-      /* Integer comparison returns a result that must be compared against 1,
-        so that even if we do an unsigned compare afterward,
-        there is still a value that can represent the result "less than".  */
       *px = result;
-      *py = const1_rtx;
       *pmode = word_mode;
+      if (TARGET_LIB_INT_CMP_BIASED)
+       /* Integer comparison returns a result that must be compared
+          against 1, so that even if we do an unsigned compare
+          afterward, there is still a value that can represent the
+          result "less than".  */
+       *py = const1_rtx;
+      else
+       {
+         *py = const0_rtx;
+         *punsignedp = 1;
+       }
       return;
     }
 
@@ -3714,18 +3495,21 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size,
    WIDER_MODE (UNSIGNEDP determines whether it is an unsigned conversion), and
    that it is accepted by the operand predicate.  Return the new value.  */
 
-rtx
+static rtx
 prepare_operand (int icode, rtx x, int opnum, enum machine_mode mode,
                 enum machine_mode wider_mode, int unsignedp)
 {
-  x = protect_from_queue (x, 0);
-
   if (mode != wider_mode)
     x = convert_modes (wider_mode, mode, x, unsignedp);
 
   if (! (*insn_data[icode].operand[opnum].predicate)
       (x, insn_data[icode].operand[opnum].mode))
-    x = copy_to_mode_reg (insn_data[icode].operand[opnum].mode, x);
+    {
+      if (no_new_pseudos)
+       return NULL_RTX;
+      x = copy_to_mode_reg (insn_data[icode].operand[opnum].mode, x);
+    }
+
   return x;
 }
 
@@ -3840,7 +3624,6 @@ emit_cmp_and_jump_insns (rtx x, rtx y, enum rtx_code comparison, rtx size,
     op0 = force_reg (mode, op0);
 #endif
 
-  emit_queue ();
   if (unsignedp)
     comparison = unsigned_condition (comparison);
 
@@ -3866,339 +3649,121 @@ prepare_float_lib_cmp (rtx *px, rtx *py, enum rtx_code *pcomparison,
                       enum machine_mode *pmode, int *punsignedp)
 {
   enum rtx_code comparison = *pcomparison;
-  rtx tmp;
-  rtx x = *px = protect_from_queue (*px, 0);
-  rtx y = *py = protect_from_queue (*py, 0);
-  enum machine_mode mode = GET_MODE (x);
+  enum rtx_code swapped = swap_condition (comparison);
+  enum rtx_code reversed = reverse_condition_maybe_unordered (comparison);
+  rtx x = *px;
+  rtx y = *py;
+  enum machine_mode orig_mode = GET_MODE (x);
+  enum machine_mode mode;
+  rtx value, target, insns, equiv;
   rtx libfunc = 0;
-  rtx result;
-
-  if (mode == HFmode)
-    switch (comparison)
-      {
-      case EQ:
-       libfunc = eqhf2_libfunc;
-       break;
-
-      case NE:
-       libfunc = nehf2_libfunc;
-       break;
-
-      case GT:
-       libfunc = gthf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = LT;
-           libfunc = lthf2_libfunc;
-         }
-       break;
-
-      case GE:
-       libfunc = gehf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = LE;
-           libfunc = lehf2_libfunc;
-         }
-       break;
-
-      case LT:
-       libfunc = lthf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = GT;
-           libfunc = gthf2_libfunc;
-         }
-       break;
-
-      case LE:
-       libfunc = lehf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = GE;
-           libfunc = gehf2_libfunc;
-         }
-       break;
-
-      case UNORDERED:
-       libfunc = unordhf2_libfunc;
-       break;
-
-      default:
-       break;
-      }
-  else if (mode == SFmode)
-    switch (comparison)
-      {
-      case EQ:
-       libfunc = eqsf2_libfunc;
-       break;
-
-      case NE:
-       libfunc = nesf2_libfunc;
-       break;
-
-      case GT:
-       libfunc = gtsf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = LT;
-           libfunc = ltsf2_libfunc;
-         }
-       break;
-
-      case GE:
-       libfunc = gesf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = LE;
-           libfunc = lesf2_libfunc;
-         }
-       break;
-
-      case LT:
-       libfunc = ltsf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = GT;
-           libfunc = gtsf2_libfunc;
-         }
-       break;
-
-      case LE:
-       libfunc = lesf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = GE;
-           libfunc = gesf2_libfunc;
-         }
-       break;
-
-      case UNORDERED:
-       libfunc = unordsf2_libfunc;
-       break;
-
-      default:
-       break;
-      }
-  else if (mode == DFmode)
-    switch (comparison)
-      {
-      case EQ:
-       libfunc = eqdf2_libfunc;
-       break;
-
-      case NE:
-       libfunc = nedf2_libfunc;
-       break;
-
-      case GT:
-       libfunc = gtdf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = LT;
-           libfunc = ltdf2_libfunc;
-         }
-       break;
-
-      case GE:
-       libfunc = gedf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = LE;
-           libfunc = ledf2_libfunc;
-         }
-       break;
+  bool reversed_p = false;
 
-      case LT:
-       libfunc = ltdf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = GT;
-           libfunc = gtdf2_libfunc;
-         }
-       break;
-
-      case LE:
-       libfunc = ledf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = GE;
-           libfunc = gedf2_libfunc;
-         }
-       break;
-
-      case UNORDERED:
-       libfunc = unorddf2_libfunc;
-       break;
-
-      default:
-       break;
-      }
-  else if (mode == XFmode)
-    switch (comparison)
-      {
-      case EQ:
-       libfunc = eqxf2_libfunc;
-       break;
-
-      case NE:
-       libfunc = nexf2_libfunc;
-       break;
-
-      case GT:
-       libfunc = gtxf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = LT;
-           libfunc = ltxf2_libfunc;
-         }
-       break;
-
-      case GE:
-       libfunc = gexf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = LE;
-           libfunc = lexf2_libfunc;
-         }
-       break;
-
-      case LT:
-       libfunc = ltxf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = GT;
-           libfunc = gtxf2_libfunc;
-         }
-       break;
-
-      case LE:
-       libfunc = lexf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = GE;
-           libfunc = gexf2_libfunc;
-         }
-       break;
-
-      case UNORDERED:
-       libfunc = unordxf2_libfunc;
-       break;
-
-      default:
-       break;
-      }
-  else if (mode == TFmode)
-    switch (comparison)
-      {
-      case EQ:
-       libfunc = eqtf2_libfunc;
-       break;
-
-      case NE:
-       libfunc = netf2_libfunc;
-       break;
-
-      case GT:
-       libfunc = gttf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = LT;
-           libfunc = lttf2_libfunc;
-         }
+  for (mode = orig_mode; mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode))
+    {
+      if ((libfunc = code_to_optab[comparison]->handlers[mode].libfunc))
        break;
 
-      case GE:
-       libfunc = getf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = LE;
-           libfunc = letf2_libfunc;
-         }
-       break;
+      if ((libfunc = code_to_optab[swapped]->handlers[mode].libfunc))
+       {
+         rtx tmp;
+         tmp = x; x = y; y = tmp;
+         comparison = swapped;
+         break;
+       }
 
-      case LT:
-       libfunc = lttf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = GT;
-           libfunc = gttf2_libfunc;
-         }
-       break;
+      if ((libfunc = code_to_optab[reversed]->handlers[mode].libfunc)
+         && FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, reversed))
+       {
+         comparison = reversed;
+         reversed_p = true;
+         break;
+       }
+    }
 
-      case LE:
-       libfunc = letf2_libfunc;
-       if (libfunc == NULL_RTX)
-         {
-           tmp = x; x = y; y = tmp;
-           *pcomparison = GE;
-           libfunc = getf2_libfunc;
-         }
-       break;
+  if (mode == VOIDmode)
+    abort ();
 
-      case UNORDERED:
-       libfunc = unordtf2_libfunc;
-       break;
+  if (mode != orig_mode)
+    {
+      x = convert_to_mode (mode, x, 0);
+      y = convert_to_mode (mode, y, 0);
+    }
 
-      default:
-       break;
-      }
+  /* Attach a REG_EQUAL note describing the semantics of the libcall to
+     the RTL.  The allows the RTL optimizers to delete the libcall if the
+     condition can be determined at compile-time.  */
+  if (comparison == UNORDERED)
+    {
+      rtx temp = simplify_gen_relational (NE, word_mode, mode, x, x);
+      equiv = simplify_gen_relational (NE, word_mode, mode, y, y);
+      equiv = simplify_gen_ternary (IF_THEN_ELSE, word_mode, word_mode,
+                                   temp, const_true_rtx, equiv);
+    }
   else
     {
-      enum machine_mode wider_mode;
-
-      for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
-          wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+      equiv = simplify_gen_relational (comparison, word_mode, mode, x, y);
+      if (! FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison))
        {
-         if ((cmp_optab->handlers[(int) wider_mode].insn_code
-              != CODE_FOR_nothing)
-             || (cmp_optab->handlers[(int) wider_mode].libfunc != 0))
+         rtx true_rtx, false_rtx;
+
+         switch (comparison)
            {
-             x = protect_from_queue (x, 0);
-             y = protect_from_queue (y, 0);
-             *px = convert_to_mode (wider_mode, x, 0);
-             *py = convert_to_mode (wider_mode, y, 0);
-             prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp);
-             return;
+           case EQ:
+             true_rtx = const0_rtx;
+             false_rtx = const_true_rtx;
+             break;
+
+           case NE:
+             true_rtx = const_true_rtx;
+             false_rtx = const0_rtx;
+             break;
+
+           case GT:
+             true_rtx = const1_rtx;
+             false_rtx = const0_rtx;
+             break;
+
+           case GE:
+             true_rtx = const0_rtx;
+             false_rtx = constm1_rtx;
+             break;
+
+           case LT:
+             true_rtx = constm1_rtx;
+             false_rtx = const0_rtx;
+             break;
+
+           case LE:
+             true_rtx = const0_rtx;
+             false_rtx = const1_rtx;
+             break;
+
+           default:
+             abort ();
            }
+         equiv = simplify_gen_ternary (IF_THEN_ELSE, word_mode, word_mode,
+                                       equiv, true_rtx, false_rtx);
        }
-      abort ();
     }
 
-  if (libfunc == 0)
-    abort ();
+  start_sequence ();
+  value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
+                                  word_mode, 2, x, mode, y, mode);
+  insns = get_insns ();
+  end_sequence ();
+
+  target = gen_reg_rtx (word_mode);
+  emit_libcall_block (insns, target, value, equiv);
+
+  if (comparison == UNORDERED
+      || FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison))
+    comparison = reversed_p ? EQ : NE;
 
-  result = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST_MAKE_BLOCK,
-                                   word_mode, 2, x, mode, y, mode);
-  *px = result;
+  *px = target;
   *py = const0_rtx;
   *pmode = word_mode;
-  if (comparison == UNORDERED)
-    *pcomparison = NE;
-#ifdef FLOAT_LIB_COMPARE_RETURNS_BOOL
-  else if (FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison))
-    *pcomparison = NE;
-#endif
+  *pcomparison = comparison;
   *punsignedp = 0;
 }
 \f
@@ -4254,9 +3819,9 @@ emit_conditional_move (rtx target, enum rtx_code code, rtx op0, rtx op1,
   /* get_condition will prefer to generate LT and GT even if the old
      comparison was against zero, so undo that canonicalization here since
      comparisons against zero are cheaper.  */
-  if (code == LT && GET_CODE (op1) == CONST_INT && INTVAL (op1) == 1)
+  if (code == LT && op1 == const1_rtx)
     code = LE, op1 = const0_rtx;
-  else if (code == GT && GET_CODE (op1) == CONST_INT && INTVAL (op1) == -1)
+  else if (code == GT && op1 == constm1_rtx)
     code = GE, op1 = const0_rtx;
 
   if (cmode == VOIDmode)
@@ -4286,18 +3851,11 @@ emit_conditional_move (rtx target, enum rtx_code code, rtx op0, rtx op1,
       op3 = force_not_mem (op3);
     }
 
-  if (target)
-    target = protect_from_queue (target, 1);
-  else
+  if (!target)
     target = gen_reg_rtx (mode);
 
   subtarget = target;
 
-  emit_queue ();
-
-  op2 = protect_from_queue (op2, 0);
-  op3 = protect_from_queue (op3, 0);
-
   /* If the insn doesn't accept these operands, put them in pseudos.  */
 
   if (! (*insn_data[icode].operand[0].predicate)
@@ -4395,9 +3953,9 @@ emit_conditional_add (rtx target, enum rtx_code code, rtx op0, rtx op1,
   /* get_condition will prefer to generate LT and GT even if the old
      comparison was against zero, so undo that canonicalization here since
      comparisons against zero are cheaper.  */
-  if (code == LT && GET_CODE (op1) == CONST_INT && INTVAL (op1) == 1)
+  if (code == LT && op1 == const1_rtx)
     code = LE, op1 = const0_rtx;
-  else if (code == GT && GET_CODE (op1) == CONST_INT && INTVAL (op1) == -1)
+  else if (code == GT && op1 == constm1_rtx)
     code = GE, op1 = const0_rtx;
 
   if (cmode == VOIDmode)
@@ -4427,23 +3985,16 @@ emit_conditional_add (rtx target, enum rtx_code code, rtx op0, rtx op1,
       op3 = force_not_mem (op3);
     }
 
-  if (target)
-    target = protect_from_queue (target, 1);
-  else
+  if (!target)
     target = gen_reg_rtx (mode);
 
-  subtarget = target;
-
-  emit_queue ();
-
-  op2 = protect_from_queue (op2, 0);
-  op3 = protect_from_queue (op3, 0);
-
   /* If the insn doesn't accept these operands, put them in pseudos.  */
 
   if (! (*insn_data[icode].operand[0].predicate)
-      (subtarget, insn_data[icode].operand[0].mode))
+      (target, insn_data[icode].operand[0].mode))
     subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode);
+  else
+    subtarget = target;
 
   if (! (*insn_data[icode].operand[2].predicate)
       (op2, insn_data[icode].operand[2].mode))
@@ -4482,11 +4033,7 @@ emit_conditional_add (rtx target, enum rtx_code code, rtx op0, rtx op1,
 \f
 /* These functions attempt to generate an insn body, rather than
    emitting the insn, but if the gen function already emits them, we
-   make no attempt to turn them back into naked patterns.
-
-   They do not protect from queued increments,
-   because they may be used 1) in protect_from_queue itself
-   and 2) in other passes where there is no queue.  */
+   make no attempt to turn them back into naked patterns.  */
 
 /* Generate and return an insn body to add Y to X.  */
 
@@ -4633,12 +4180,14 @@ enum insn_code
 can_extend_p (enum machine_mode to_mode, enum machine_mode from_mode,
              int unsignedp)
 {
+  convert_optab tab;
 #ifdef HAVE_ptr_extend
   if (unsignedp < 0)
     return CODE_FOR_ptr_extend;
-  else
 #endif
-    return extendtab[(int) to_mode][(int) from_mode][unsignedp != 0];
+
+  tab = unsignedp ? zext_optab : sext_optab;
+  return tab->handlers[to_mode][from_mode].insn_code;
 }
 
 /* Generate the body of an insn to extend Y (with mode MFROM)
@@ -4648,7 +4197,8 @@ rtx
 gen_extend_insn (rtx x, rtx y, enum machine_mode mto,
                 enum machine_mode mfrom, int unsignedp)
 {
-  return (GEN_FCN (extendtab[(int) mto][(int) mfrom][unsignedp != 0]) (x, y));
+  enum insn_code icode = can_extend_p (mto, mfrom, unsignedp);
+  return GEN_FCN (icode) (x, y);
 }
 \f
 /* can_fix_p and can_float_p say whether the target machine
@@ -4664,16 +4214,30 @@ static enum insn_code
 can_fix_p (enum machine_mode fixmode, enum machine_mode fltmode,
           int unsignedp, int *truncp_ptr)
 {
-  *truncp_ptr = 0;
-  if (fixtrunctab[(int) fltmode][(int) fixmode][unsignedp != 0]
-      != CODE_FOR_nothing)
-    return fixtrunctab[(int) fltmode][(int) fixmode][unsignedp != 0];
+  convert_optab tab;
+  enum insn_code icode;
 
-  if (ftrunc_optab->handlers[(int) fltmode].insn_code != CODE_FOR_nothing)
+  tab = unsignedp ? ufixtrunc_optab : sfixtrunc_optab;
+  icode = tab->handlers[fixmode][fltmode].insn_code;
+  if (icode != CODE_FOR_nothing)
+    {
+      *truncp_ptr = 0;
+      return icode;
+    }
+
+  /* FIXME: This requires a port to define both FIX and FTRUNC pattern
+     for this to work. We need to rework the fix* and ftrunc* patterns
+     and documentation.  */
+  tab = unsignedp ? ufix_optab : sfix_optab;
+  icode = tab->handlers[fixmode][fltmode].insn_code;
+  if (icode != CODE_FOR_nothing
+      && ftrunc_optab->handlers[fltmode].insn_code != CODE_FOR_nothing)
     {
       *truncp_ptr = 1;
-      return fixtab[(int) fltmode][(int) fixmode][unsignedp != 0];
+      return icode;
     }
+
+  *truncp_ptr = 0;
   return CODE_FOR_nothing;
 }
 
@@ -4681,7 +4245,10 @@ static enum insn_code
 can_float_p (enum machine_mode fltmode, enum machine_mode fixmode,
             int unsignedp)
 {
-  return floattab[(int) fltmode][(int) fixmode][unsignedp != 0];
+  convert_optab tab;
+
+  tab = unsignedp ? ufloat_optab : sfloat_optab;
+  return tab->handlers[fltmode][fixmode].insn_code;
 }
 \f
 /* Generate code to convert FROM to floating point
@@ -4723,9 +4290,6 @@ expand_float (rtx to, rtx from, int unsignedp)
 
        if (icode != CODE_FOR_nothing)
          {
-           to = protect_from_queue (to, 1);
-           from = protect_from_queue (from, 0);
-
            if (imode != GET_MODE (from))
              from = convert_to_mode (imode, from, unsignedp);
 
@@ -4749,11 +4313,6 @@ expand_float (rtx to, rtx from, int unsignedp)
       rtx temp;
       REAL_VALUE_TYPE offset;
 
-      emit_queue ();
-
-      to = protect_from_queue (to, 1);
-      from = protect_from_queue (from, 0);
-
       if (flag_force_mem)
        from = force_not_mem (from);
 
@@ -4781,7 +4340,7 @@ expand_float (rtx to, rtx from, int unsignedp)
 
              /* Don't use TARGET if it isn't a register, is a hard register,
                 or is the wrong mode.  */
-             if (GET_CODE (target) != REG
+             if (!REG_P (target)
                  || REGNO (target) < FIRST_PSEUDO_REGISTER
                  || GET_MODE (target) != fmode)
                target = gen_reg_rtx (fmode);
@@ -4828,7 +4387,7 @@ expand_float (rtx to, rtx from, int unsignedp)
         unsigned operand, do it in a pseudo-register.  */
 
       if (GET_MODE (to) != fmode
-         || GET_CODE (to) != REG || REGNO (to) < FIRST_PSEUDO_REGISTER)
+         || !REG_P (to) || REGNO (to) < FIRST_PSEUDO_REGISTER)
        target = gen_reg_rtx (fmode);
 
       /* Convert as signed integer to floating.  */
@@ -4854,15 +4413,12 @@ expand_float (rtx to, rtx from, int unsignedp)
       goto done;
     }
 
-  /* No hardware instruction available; call a library routine to convert from
-     SImode, DImode, or TImode into SFmode, DFmode, XFmode, or TFmode.  */
+  /* No hardware instruction available; call a library routine.  */
     {
-      rtx libfcn;
+      rtx libfunc;
       rtx insns;
       rtx value;
-
-      to = protect_from_queue (to, 1);
-      from = protect_from_queue (from, 0);
+      convert_optab tab = unsignedp ? ufloat_optab : sfloat_optab;
 
       if (GET_MODE_SIZE (GET_MODE (from)) < GET_MODE_SIZE (SImode))
        from = convert_to_mode (SImode, from, unsignedp);
@@ -4870,56 +4426,13 @@ expand_float (rtx to, rtx from, int unsignedp)
       if (flag_force_mem)
        from = force_not_mem (from);
 
-      if (GET_MODE (to) == SFmode)
-       {
-         if (GET_MODE (from) == SImode)
-           libfcn = floatsisf_libfunc;
-         else if (GET_MODE (from) == DImode)
-           libfcn = floatdisf_libfunc;
-         else if (GET_MODE (from) == TImode)
-           libfcn = floattisf_libfunc;
-         else
-           abort ();
-       }
-      else if (GET_MODE (to) == DFmode)
-       {
-         if (GET_MODE (from) == SImode)
-           libfcn = floatsidf_libfunc;
-         else if (GET_MODE (from) == DImode)
-           libfcn = floatdidf_libfunc;
-         else if (GET_MODE (from) == TImode)
-           libfcn = floattidf_libfunc;
-         else
-           abort ();
-       }
-      else if (GET_MODE (to) == XFmode)
-       {
-         if (GET_MODE (from) == SImode)
-           libfcn = floatsixf_libfunc;
-         else if (GET_MODE (from) == DImode)
-           libfcn = floatdixf_libfunc;
-         else if (GET_MODE (from) == TImode)
-           libfcn = floattixf_libfunc;
-         else
-           abort ();
-       }
-      else if (GET_MODE (to) == TFmode)
-       {
-         if (GET_MODE (from) == SImode)
-           libfcn = floatsitf_libfunc;
-         else if (GET_MODE (from) == DImode)
-           libfcn = floatditf_libfunc;
-         else if (GET_MODE (from) == TImode)
-           libfcn = floattitf_libfunc;
-         else
-           abort ();
-       }
-      else
+      libfunc = tab->handlers[GET_MODE (to)][GET_MODE (from)].libfunc;
+      if (!libfunc)
        abort ();
 
       start_sequence ();
 
-      value = emit_library_call_value (libfcn, NULL_RTX, LCT_CONST,
+      value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
                                       GET_MODE (to), 1, from,
                                       GET_MODE (from));
       insns = get_insns ();
@@ -4943,15 +4456,8 @@ expand_float (rtx to, rtx from, int unsignedp)
     }
 }
 \f
-/* expand_fix: generate code to convert FROM to fixed point
-   and store in TO.  FROM must be floating point.  */
-
-static rtx
-ftruncify (rtx x)
-{
-  rtx temp = gen_reg_rtx (GET_MODE (x));
-  return expand_unop (GET_MODE (x), ftrunc_optab, x, temp, 0);
-}
+/* Generate code to convert FROM to fixed point and store in TO.  FROM
+   must be floating point.  */
 
 void
 expand_fix (rtx to, rtx from, int unsignedp)
@@ -4960,7 +4466,6 @@ expand_fix (rtx to, rtx from, int unsignedp)
   rtx target = to;
   enum machine_mode fmode, imode;
   int must_trunc = 0;
-  rtx libfcn = 0;
 
   /* We first try to find a pair of modes, one real and one integer, at
      least as wide as FROM and TO, respectively, in which we can open-code
@@ -4980,14 +4485,15 @@ expand_fix (rtx to, rtx from, int unsignedp)
 
        if (icode != CODE_FOR_nothing)
          {
-           to = protect_from_queue (to, 1);
-           from = protect_from_queue (from, 0);
-
            if (fmode != GET_MODE (from))
              from = convert_to_mode (fmode, from, 0);
 
            if (must_trunc)
-             from = ftruncify (from);
+             {
+               rtx temp = gen_reg_rtx (GET_MODE (from));
+               from = expand_unop (GET_MODE (from), ftrunc_optab, from,
+                                   temp, 0);
+             }
 
            if (imode != GET_MODE (to))
              target = gen_reg_rtx (imode);
@@ -5038,10 +4544,6 @@ expand_fix (rtx to, rtx from, int unsignedp)
          lab1 = gen_label_rtx ();
          lab2 = gen_label_rtx ();
 
-         emit_queue ();
-         to = protect_from_queue (to, 1);
-         from = protect_from_queue (from, 0);
-
          if (flag_force_mem)
            from = force_not_mem (from);
 
@@ -5101,67 +4603,23 @@ expand_fix (rtx to, rtx from, int unsignedp)
 
       expand_fix (target, from, unsignedp);
     }
-  else if (GET_MODE (from) == SFmode)
-    {
-      if (GET_MODE (to) == SImode)
-       libfcn = unsignedp ? fixunssfsi_libfunc : fixsfsi_libfunc;
-      else if (GET_MODE (to) == DImode)
-       libfcn = unsignedp ? fixunssfdi_libfunc : fixsfdi_libfunc;
-      else if (GET_MODE (to) == TImode)
-       libfcn = unsignedp ? fixunssfti_libfunc : fixsfti_libfunc;
-      else
-       abort ();
-    }
-  else if (GET_MODE (from) == DFmode)
-    {
-      if (GET_MODE (to) == SImode)
-       libfcn = unsignedp ? fixunsdfsi_libfunc : fixdfsi_libfunc;
-      else if (GET_MODE (to) == DImode)
-       libfcn = unsignedp ? fixunsdfdi_libfunc : fixdfdi_libfunc;
-      else if (GET_MODE (to) == TImode)
-       libfcn = unsignedp ? fixunsdfti_libfunc : fixdfti_libfunc;
-      else
-       abort ();
-    }
-  else if (GET_MODE (from) == XFmode)
-    {
-      if (GET_MODE (to) == SImode)
-       libfcn = unsignedp ? fixunsxfsi_libfunc : fixxfsi_libfunc;
-      else if (GET_MODE (to) == DImode)
-       libfcn = unsignedp ? fixunsxfdi_libfunc : fixxfdi_libfunc;
-      else if (GET_MODE (to) == TImode)
-       libfcn = unsignedp ? fixunsxfti_libfunc : fixxfti_libfunc;
-      else
-       abort ();
-    }
-  else if (GET_MODE (from) == TFmode)
-    {
-      if (GET_MODE (to) == SImode)
-       libfcn = unsignedp ? fixunstfsi_libfunc : fixtfsi_libfunc;
-      else if (GET_MODE (to) == DImode)
-       libfcn = unsignedp ? fixunstfdi_libfunc : fixtfdi_libfunc;
-      else if (GET_MODE (to) == TImode)
-       libfcn = unsignedp ? fixunstfti_libfunc : fixtfti_libfunc;
-      else
-       abort ();
-    }
   else
-    abort ();
-
-  if (libfcn)
     {
       rtx insns;
       rtx value;
+      rtx libfunc;
 
-      to = protect_from_queue (to, 1);
-      from = protect_from_queue (from, 0);
+      convert_optab tab = unsignedp ? ufix_optab : sfix_optab;
+      libfunc = tab->handlers[GET_MODE (to)][GET_MODE (from)].libfunc;
+      if (!libfunc)
+       abort ();
 
       if (flag_force_mem)
        from = force_not_mem (from);
 
       start_sequence ();
 
-      value = emit_library_call_value (libfcn, NULL_RTX, LCT_CONST,
+      value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
                                       GET_MODE (to), 1, from,
                                       GET_MODE (from));
       insns = get_insns ();
@@ -5196,7 +4654,7 @@ static optab
 new_optab (void)
 {
   int i;
-  optab op = (optab) ggc_alloc (sizeof (struct optab));
+  optab op = ggc_alloc (sizeof (struct optab));
   for (i = 0; i < NUM_MACHINE_MODES; i++)
     {
       op->handlers[i].insn_code = CODE_FOR_nothing;
@@ -5206,6 +4664,20 @@ new_optab (void)
   return op;
 }
 
+static convert_optab
+new_convert_optab (void)
+{
+  int i, j;
+  convert_optab op = ggc_alloc (sizeof (struct convert_optab));
+  for (i = 0; i < NUM_MACHINE_MODES; i++)
+    for (j = 0; j < NUM_MACHINE_MODES; j++)
+      {
+       op->handlers[i][j].insn_code = CODE_FOR_nothing;
+       op->handlers[i][j].libfunc = 0;
+      }
+  return op;
+}
+
 /* Same, but fill in its code as CODE, and write it into the
    code_to_optab table.  */
 static inline optab
@@ -5227,10 +4699,19 @@ init_optabv (enum rtx_code code)
   return op;
 }
 
+/* Conversion optabs never go in the code_to_optab table.  */
+static inline convert_optab
+init_convert_optab (enum rtx_code code)
+{
+  convert_optab op = new_convert_optab ();
+  op->code = code;
+  return op;
+}
+
 /* Initialize the libfunc fields of an entire group of entries in some
    optab.  Each entry is set equal to a string consisting of a leading
    pair of underscores followed by a generic operation name followed by
-   a mode name (downshifted to lower case) followed by a single character
+   a mode name (downshifted to lowercase) followed by a single character
    representing the number of operands for the given operation (which is
    usually one of the characters '2', '3', or '4').
 
@@ -5299,20 +4780,122 @@ init_integral_libfuncs (optab optable, const char *opname, int suffix)
 static void
 init_floating_libfuncs (optab optable, const char *opname, int suffix)
 {
-  enum machine_mode fmode, dmode, lmode;
-
-  fmode = float_type_node ? TYPE_MODE (float_type_node) : VOIDmode;
-  dmode = double_type_node ? TYPE_MODE (double_type_node) : VOIDmode;
-  lmode = long_double_type_node ? TYPE_MODE (long_double_type_node) : VOIDmode;
-
-  if (fmode != VOIDmode)
-    init_libfuncs (optable, fmode, fmode, opname, suffix);
-  if (dmode != fmode && dmode != VOIDmode)
-    init_libfuncs (optable, dmode, dmode, opname, suffix);
-  if (lmode != dmode && lmode != VOIDmode)
-    init_libfuncs (optable, lmode, lmode, opname, suffix);
+  init_libfuncs (optable, MIN_MODE_FLOAT, MAX_MODE_FLOAT, opname, suffix);
+}
+
+/* Initialize the libfunc fields of an entire group of entries of an
+   inter-mode-class conversion optab.  The string formation rules are
+   similar to the ones for init_libfuncs, above, but instead of having
+   a mode name and an operand count these functions have two mode names
+   and no operand count.  */
+static void
+init_interclass_conv_libfuncs (convert_optab tab, const char *opname,
+                              enum mode_class from_class,
+                              enum mode_class to_class)
+{
+  enum machine_mode first_from_mode = GET_CLASS_NARROWEST_MODE (from_class);
+  enum machine_mode first_to_mode = GET_CLASS_NARROWEST_MODE (to_class);
+  size_t opname_len = strlen (opname);
+  size_t max_mname_len = 0;
+
+  enum machine_mode fmode, tmode;
+  const char *fname, *tname;
+  const char *q;
+  char *libfunc_name, *suffix;
+  char *p;
+
+  for (fmode = first_from_mode;
+       fmode != VOIDmode;
+       fmode = GET_MODE_WIDER_MODE (fmode))
+    max_mname_len = MAX (max_mname_len, strlen (GET_MODE_NAME (fmode)));
+
+  for (tmode = first_to_mode;
+       tmode != VOIDmode;
+       tmode = GET_MODE_WIDER_MODE (tmode))
+    max_mname_len = MAX (max_mname_len, strlen (GET_MODE_NAME (tmode)));
+
+  libfunc_name = alloca (2 + opname_len + 2*max_mname_len + 1 + 1);
+  libfunc_name[0] = '_';
+  libfunc_name[1] = '_';
+  memcpy (&libfunc_name[2], opname, opname_len);
+  suffix = libfunc_name + opname_len + 2;
+
+  for (fmode = first_from_mode; fmode != VOIDmode;
+       fmode = GET_MODE_WIDER_MODE (fmode))
+    for (tmode = first_to_mode; tmode != VOIDmode;
+        tmode = GET_MODE_WIDER_MODE (tmode))
+      {
+       fname = GET_MODE_NAME (fmode);
+       tname = GET_MODE_NAME (tmode);
+
+       p = suffix;
+       for (q = fname; *q; p++, q++)
+         *p = TOLOWER (*q);
+       for (q = tname; *q; p++, q++)
+         *p = TOLOWER (*q);
+
+       *p = '\0';
+
+       tab->handlers[tmode][fmode].libfunc
+         = init_one_libfunc (ggc_alloc_string (libfunc_name,
+                                               p - libfunc_name));
+      }
+}
+
+/* Initialize the libfunc fields of an entire group of entries of an
+   intra-mode-class conversion optab.  The string formation rules are
+   similar to the ones for init_libfunc, above.  WIDENING says whether
+   the optab goes from narrow to wide modes or vice versa.  These functions
+   have two mode names _and_ an operand count.  */
+static void
+init_intraclass_conv_libfuncs (convert_optab tab, const char *opname,
+                              enum mode_class class, bool widening)
+{
+  enum machine_mode first_mode = GET_CLASS_NARROWEST_MODE (class);
+  size_t opname_len = strlen (opname);
+  size_t max_mname_len = 0;
+
+  enum machine_mode nmode, wmode;
+  const char *nname, *wname;
+  const char *q;
+  char *libfunc_name, *suffix;
+  char *p;
+
+  for (nmode = first_mode; nmode != VOIDmode;
+       nmode = GET_MODE_WIDER_MODE (nmode))
+    max_mname_len = MAX (max_mname_len, strlen (GET_MODE_NAME (nmode)));
+
+  libfunc_name = alloca (2 + opname_len + 2*max_mname_len + 1 + 1);
+  libfunc_name[0] = '_';
+  libfunc_name[1] = '_';
+  memcpy (&libfunc_name[2], opname, opname_len);
+  suffix = libfunc_name + opname_len + 2;
+
+  for (nmode = first_mode; nmode != VOIDmode;
+       nmode = GET_MODE_WIDER_MODE (nmode))
+    for (wmode = GET_MODE_WIDER_MODE (nmode); wmode != VOIDmode;
+        wmode = GET_MODE_WIDER_MODE (wmode))
+      {
+       nname = GET_MODE_NAME (nmode);
+       wname = GET_MODE_NAME (wmode);
+
+       p = suffix;
+       for (q = widening ? nname : wname; *q; p++, q++)
+         *p = TOLOWER (*q);
+       for (q = widening ? wname : nname; *q; p++, q++)
+         *p = TOLOWER (*q);
+
+       *p++ = '2';
+       *p = '\0';
+
+       tab->handlers[widening ? wmode : nmode]
+                    [widening ? nmode : wmode].libfunc
+         = init_one_libfunc (ggc_alloc_string (libfunc_name,
+                                               p - libfunc_name));
+      }
 }
 
+
 rtx
 init_one_libfunc (const char *name)
 {
@@ -5337,36 +4920,40 @@ init_one_libfunc (const char *name)
   return symbol;
 }
 
+/* Call this to reset the function entry for one optab (OPTABLE) in mode
+   MODE to NAME, which should be either 0 or a string constant.  */
+void
+set_optab_libfunc (optab optable, enum machine_mode mode, const char *name)
+{
+  if (name)
+    optable->handlers[mode].libfunc = init_one_libfunc (name);
+  else
+    optable->handlers[mode].libfunc = 0;
+}
+
+/* Call this to reset the function entry for one conversion optab
+   (OPTABLE) from mode FMODE to mode TMODE to NAME, which should be
+   either 0 or a string constant.  */
+void
+set_conv_libfunc (convert_optab optable, enum machine_mode tmode,
+                 enum machine_mode fmode, const char *name)
+{
+  if (name)
+    optable->handlers[tmode][fmode].libfunc = init_one_libfunc (name);
+  else
+    optable->handlers[tmode][fmode].libfunc = 0;
+}
+
 /* Call this once to initialize the contents of the optabs
    appropriately for the current target machine.  */
 
 void
 init_optabs (void)
 {
-  unsigned int i, j, k;
+  unsigned int i;
 
   /* Start by initializing all tables to contain CODE_FOR_nothing.  */
 
-  for (i = 0; i < ARRAY_SIZE (fixtab); i++)
-    for (j = 0; j < ARRAY_SIZE (fixtab[0]); j++)
-      for (k = 0; k < ARRAY_SIZE (fixtab[0][0]); k++)
-       fixtab[i][j][k] = CODE_FOR_nothing;
-
-  for (i = 0; i < ARRAY_SIZE (fixtrunctab); i++)
-    for (j = 0; j < ARRAY_SIZE (fixtrunctab[0]); j++)
-      for (k = 0; k < ARRAY_SIZE (fixtrunctab[0][0]); k++)
-       fixtrunctab[i][j][k] = CODE_FOR_nothing;
-
-  for (i = 0; i < ARRAY_SIZE (floattab); i++)
-    for (j = 0; j < ARRAY_SIZE (floattab[0]); j++)
-      for (k = 0; k < ARRAY_SIZE (floattab[0][0]); k++)
-       floattab[i][j][k] = CODE_FOR_nothing;
-
-  for (i = 0; i < ARRAY_SIZE (extendtab); i++)
-    for (j = 0; j < ARRAY_SIZE (extendtab[0]); j++)
-      for (k = 0; k < ARRAY_SIZE (extendtab[0][0]); k++)
-       extendtab[i][j][k] = CODE_FOR_nothing;
-
   for (i = 0; i < NUM_RTX_CODE; i++)
     setcc_gen_code[i] = CODE_FOR_nothing;
 
@@ -5375,6 +4962,12 @@ init_optabs (void)
     movcc_gen_code[i] = CODE_FOR_nothing;
 #endif
 
+  for (i = 0; i < NUM_MACHINE_MODES; i++)
+    {
+      vcond_gen_code[i] = CODE_FOR_nothing;
+      vcondu_gen_code[i] = CODE_FOR_nothing;
+    }
+
   add_optab = init_optab (PLUS);
   addv_optab = init_optabv (PLUS);
   sub_optab = init_optab (MINUS);
@@ -5392,6 +4985,8 @@ init_optabs (void)
   udivmod_optab = init_optab (UNKNOWN);
   smod_optab = init_optab (MOD);
   umod_optab = init_optab (UMOD);
+  fmod_optab = init_optab (UNKNOWN);
+  drem_optab = init_optab (UNKNOWN);
   ftrunc_optab = init_optab (UNKNOWN);
   and_optab = init_optab (AND);
   ior_optab = init_optab (IOR);
@@ -5416,6 +5011,15 @@ init_optabs (void)
 
   ucmp_optab = init_optab (UNKNOWN);
   tst_optab = init_optab (UNKNOWN);
+
+  eq_optab = init_optab (EQ);
+  ne_optab = init_optab (NE);
+  gt_optab = init_optab (GT);
+  ge_optab = init_optab (GE);
+  lt_optab = init_optab (LT);
+  le_optab = init_optab (LE);
+  unord_optab = init_optab (UNORDERED);
+
   neg_optab = init_optab (NEG);
   negv_optab = init_optabv (NEG);
   abs_optab = init_optab (ABS);
@@ -5431,24 +5035,61 @@ init_optabs (void)
   floor_optab = init_optab (UNKNOWN);
   ceil_optab = init_optab (UNKNOWN);
   round_optab = init_optab (UNKNOWN);
-  trunc_optab = init_optab (UNKNOWN);
+  btrunc_optab = init_optab (UNKNOWN);
   nearbyint_optab = init_optab (UNKNOWN);
+  rint_optab = init_optab (UNKNOWN);
+  lrint_optab = init_optab (UNKNOWN);
+  sincos_optab = init_optab (UNKNOWN);
   sin_optab = init_optab (UNKNOWN);
+  asin_optab = init_optab (UNKNOWN);
   cos_optab = init_optab (UNKNOWN);
+  acos_optab = init_optab (UNKNOWN);
   exp_optab = init_optab (UNKNOWN);
+  exp10_optab = init_optab (UNKNOWN);
+  exp2_optab = init_optab (UNKNOWN);
+  expm1_optab = init_optab (UNKNOWN);
+  ldexp_optab = init_optab (UNKNOWN);
+  logb_optab = init_optab (UNKNOWN);
+  ilogb_optab = init_optab (UNKNOWN);
   log_optab = init_optab (UNKNOWN);
+  log10_optab = init_optab (UNKNOWN);
+  log2_optab = init_optab (UNKNOWN);
+  log1p_optab = init_optab (UNKNOWN);
   tan_optab = init_optab (UNKNOWN);
   atan_optab = init_optab (UNKNOWN);
+  copysign_optab = init_optab (UNKNOWN);
+
   strlen_optab = init_optab (UNKNOWN);
   cbranch_optab = init_optab (UNKNOWN);
   cmov_optab = init_optab (UNKNOWN);
   cstore_optab = init_optab (UNKNOWN);
   push_optab = init_optab (UNKNOWN);
 
+  vec_extract_optab = init_optab (UNKNOWN);
+  vec_set_optab = init_optab (UNKNOWN);
+  vec_init_optab = init_optab (UNKNOWN);
+  vec_realign_load_optab = init_optab (UNKNOWN);
+  movmisalign_optab = init_optab (UNKNOWN);
+
+  powi_optab = init_optab (UNKNOWN);
+
+  /* Conversions.  */
+  sext_optab = init_convert_optab (SIGN_EXTEND);
+  zext_optab = init_convert_optab (ZERO_EXTEND);
+  trunc_optab = init_convert_optab (TRUNCATE);
+  sfix_optab = init_convert_optab (FIX);
+  ufix_optab = init_convert_optab (UNSIGNED_FIX);
+  sfixtrunc_optab = init_convert_optab (UNKNOWN);
+  ufixtrunc_optab = init_convert_optab (UNKNOWN);
+  sfloat_optab = init_convert_optab (FLOAT);
+  ufloat_optab = init_convert_optab (UNSIGNED_FLOAT);
+
   for (i = 0; i < NUM_MACHINE_MODES; i++)
     {
-      movstr_optab[i] = CODE_FOR_nothing;
-      clrstr_optab[i] = CODE_FOR_nothing;
+      movmem_optab[i] = CODE_FOR_nothing;
+      clrmem_optab[i] = CODE_FOR_nothing;
+      cmpstr_optab[i] = CODE_FOR_nothing;
+      cmpmem_optab[i] = CODE_FOR_nothing;
 
 #ifdef HAVE_SECONDARY_RELOADS
       reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing;
@@ -5458,14 +5099,6 @@ init_optabs (void)
   /* Fill in the optabs with the insns we support.  */
   init_all_optabs ();
 
-#ifdef FIXUNS_TRUNC_LIKE_FIX_TRUNC
-  /* This flag says the same insns that convert to a signed fixnum
-     also convert validly to an unsigned one.  */
-  for (i = 0; i < NUM_MACHINE_MODES; i++)
-    for (j = 0; j < NUM_MACHINE_MODES; j++)
-      fixtrunctab[i][j][1] = fixtrunctab[i][j][0];
-#endif
-
   /* Initialize the optabs with the names of the library functions.  */
   init_integral_libfuncs (add_optab, "add", '3');
   init_floating_libfuncs (add_optab, "add", '3');
@@ -5511,85 +5144,50 @@ init_optabs (void)
   init_integral_libfuncs (popcount_optab, "popcount", '2');
   init_integral_libfuncs (parity_optab, "parity", '2');
 
-  /* Comparison libcalls for integers MUST come in pairs, signed/unsigned.  */
+  /* Comparison libcalls for integers MUST come in pairs,
+     signed/unsigned.  */
   init_integral_libfuncs (cmp_optab, "cmp", '2');
   init_integral_libfuncs (ucmp_optab, "ucmp", '2');
   init_floating_libfuncs (cmp_optab, "cmp", '2');
 
-#ifdef MULSI3_LIBCALL
-  smul_optab->handlers[(int) SImode].libfunc
-    = init_one_libfunc (MULSI3_LIBCALL);
-#endif
-#ifdef MULDI3_LIBCALL
-  smul_optab->handlers[(int) DImode].libfunc
-    = init_one_libfunc (MULDI3_LIBCALL);
-#endif
-
-#ifdef DIVSI3_LIBCALL
-  sdiv_optab->handlers[(int) SImode].libfunc
-    = init_one_libfunc (DIVSI3_LIBCALL);
-#endif
-#ifdef DIVDI3_LIBCALL
-  sdiv_optab->handlers[(int) DImode].libfunc
-    = init_one_libfunc (DIVDI3_LIBCALL);
-#endif
-
-#ifdef UDIVSI3_LIBCALL
-  udiv_optab->handlers[(int) SImode].libfunc
-    = init_one_libfunc (UDIVSI3_LIBCALL);
-#endif
-#ifdef UDIVDI3_LIBCALL
-  udiv_optab->handlers[(int) DImode].libfunc
-    = init_one_libfunc (UDIVDI3_LIBCALL);
-#endif
-
-#ifdef MODSI3_LIBCALL
-  smod_optab->handlers[(int) SImode].libfunc
-    = init_one_libfunc (MODSI3_LIBCALL);
-#endif
-#ifdef MODDI3_LIBCALL
-  smod_optab->handlers[(int) DImode].libfunc
-    = init_one_libfunc (MODDI3_LIBCALL);
-#endif
-
-#ifdef UMODSI3_LIBCALL
-  umod_optab->handlers[(int) SImode].libfunc
-    = init_one_libfunc (UMODSI3_LIBCALL);
-#endif
-#ifdef UMODDI3_LIBCALL
-  umod_optab->handlers[(int) DImode].libfunc
-    = init_one_libfunc (UMODDI3_LIBCALL);
-#endif
-
-  /* Use cabs for DC complex abs, since systems generally have cabs.
-     Don't define any libcall for SCmode, so that cabs will be used.  */
-  abs_optab->handlers[(int) DCmode].libfunc
-    = init_one_libfunc ("cabs");
+  /* EQ etc are floating point only.  */
+  init_floating_libfuncs (eq_optab, "eq", '2');
+  init_floating_libfuncs (ne_optab, "ne", '2');
+  init_floating_libfuncs (gt_optab, "gt", '2');
+  init_floating_libfuncs (ge_optab, "ge", '2');
+  init_floating_libfuncs (lt_optab, "lt", '2');
+  init_floating_libfuncs (le_optab, "le", '2');
+  init_floating_libfuncs (unord_optab, "unord", '2');
+
+  init_floating_libfuncs (powi_optab, "powi", '2');
+
+  /* Conversions.  */
+  init_interclass_conv_libfuncs (sfloat_optab, "float",
+                                MODE_INT, MODE_FLOAT);
+  init_interclass_conv_libfuncs (sfix_optab, "fix",
+                                MODE_FLOAT, MODE_INT);
+  init_interclass_conv_libfuncs (ufix_optab, "fixuns",
+                                MODE_FLOAT, MODE_INT);
+
+  /* sext_optab is also used for FLOAT_EXTEND.  */
+  init_intraclass_conv_libfuncs (sext_optab, "extend", MODE_FLOAT, true);
+  init_intraclass_conv_libfuncs (trunc_optab, "trunc", MODE_FLOAT, false);
+
+  /* Use cabs for double complex abs, since systems generally have cabs.
+     Don't define any libcall for float complex, so that cabs will be used.  */
+  if (complex_double_type_node)
+    abs_optab->handlers[TYPE_MODE (complex_double_type_node)].libfunc
+      = init_one_libfunc ("cabs");
 
   /* The ffs function operates on `int'.  */
   ffs_optab->handlers[(int) mode_for_size (INT_TYPE_SIZE, MODE_INT, 0)].libfunc
     = init_one_libfunc ("ffs");
 
-  extendsfdf2_libfunc = init_one_libfunc ("__extendsfdf2");
-  extendsfxf2_libfunc = init_one_libfunc ("__extendsfxf2");
-  extendsftf2_libfunc = init_one_libfunc ("__extendsftf2");
-  extenddfxf2_libfunc = init_one_libfunc ("__extenddfxf2");
-  extenddftf2_libfunc = init_one_libfunc ("__extenddftf2");
-
-  truncdfsf2_libfunc = init_one_libfunc ("__truncdfsf2");
-  truncxfsf2_libfunc = init_one_libfunc ("__truncxfsf2");
-  trunctfsf2_libfunc = init_one_libfunc ("__trunctfsf2");
-  truncxfdf2_libfunc = init_one_libfunc ("__truncxfdf2");
-  trunctfdf2_libfunc = init_one_libfunc ("__trunctfdf2");
-
   abort_libfunc = init_one_libfunc ("abort");
   memcpy_libfunc = init_one_libfunc ("memcpy");
   memmove_libfunc = init_one_libfunc ("memmove");
-  bcopy_libfunc = init_one_libfunc ("bcopy");
   memcmp_libfunc = init_one_libfunc ("memcmp");
-  bcmp_libfunc = init_one_libfunc ("__gcc_bcmp");
   memset_libfunc = init_one_libfunc ("memset");
-  bzero_libfunc = init_one_libfunc ("bzero");
   setbits_libfunc = init_one_libfunc ("__setbits");
 
   unwind_resume_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS
@@ -5606,94 +5204,6 @@ init_optabs (void)
   unwind_sjlj_unregister_libfunc
     = init_one_libfunc ("_Unwind_SjLj_Unregister");
 
-  eqhf2_libfunc = init_one_libfunc ("__eqhf2");
-  nehf2_libfunc = init_one_libfunc ("__nehf2");
-  gthf2_libfunc = init_one_libfunc ("__gthf2");
-  gehf2_libfunc = init_one_libfunc ("__gehf2");
-  lthf2_libfunc = init_one_libfunc ("__lthf2");
-  lehf2_libfunc = init_one_libfunc ("__lehf2");
-  unordhf2_libfunc = init_one_libfunc ("__unordhf2");
-
-  eqsf2_libfunc = init_one_libfunc ("__eqsf2");
-  nesf2_libfunc = init_one_libfunc ("__nesf2");
-  gtsf2_libfunc = init_one_libfunc ("__gtsf2");
-  gesf2_libfunc = init_one_libfunc ("__gesf2");
-  ltsf2_libfunc = init_one_libfunc ("__ltsf2");
-  lesf2_libfunc = init_one_libfunc ("__lesf2");
-  unordsf2_libfunc = init_one_libfunc ("__unordsf2");
-
-  eqdf2_libfunc = init_one_libfunc ("__eqdf2");
-  nedf2_libfunc = init_one_libfunc ("__nedf2");
-  gtdf2_libfunc = init_one_libfunc ("__gtdf2");
-  gedf2_libfunc = init_one_libfunc ("__gedf2");
-  ltdf2_libfunc = init_one_libfunc ("__ltdf2");
-  ledf2_libfunc = init_one_libfunc ("__ledf2");
-  unorddf2_libfunc = init_one_libfunc ("__unorddf2");
-
-  eqxf2_libfunc = init_one_libfunc ("__eqxf2");
-  nexf2_libfunc = init_one_libfunc ("__nexf2");
-  gtxf2_libfunc = init_one_libfunc ("__gtxf2");
-  gexf2_libfunc = init_one_libfunc ("__gexf2");
-  ltxf2_libfunc = init_one_libfunc ("__ltxf2");
-  lexf2_libfunc = init_one_libfunc ("__lexf2");
-  unordxf2_libfunc = init_one_libfunc ("__unordxf2");
-
-  eqtf2_libfunc = init_one_libfunc ("__eqtf2");
-  netf2_libfunc = init_one_libfunc ("__netf2");
-  gttf2_libfunc = init_one_libfunc ("__gttf2");
-  getf2_libfunc = init_one_libfunc ("__getf2");
-  lttf2_libfunc = init_one_libfunc ("__lttf2");
-  letf2_libfunc = init_one_libfunc ("__letf2");
-  unordtf2_libfunc = init_one_libfunc ("__unordtf2");
-
-  floatsisf_libfunc = init_one_libfunc ("__floatsisf");
-  floatdisf_libfunc = init_one_libfunc ("__floatdisf");
-  floattisf_libfunc = init_one_libfunc ("__floattisf");
-
-  floatsidf_libfunc = init_one_libfunc ("__floatsidf");
-  floatdidf_libfunc = init_one_libfunc ("__floatdidf");
-  floattidf_libfunc = init_one_libfunc ("__floattidf");
-
-  floatsixf_libfunc = init_one_libfunc ("__floatsixf");
-  floatdixf_libfunc = init_one_libfunc ("__floatdixf");
-  floattixf_libfunc = init_one_libfunc ("__floattixf");
-
-  floatsitf_libfunc = init_one_libfunc ("__floatsitf");
-  floatditf_libfunc = init_one_libfunc ("__floatditf");
-  floattitf_libfunc = init_one_libfunc ("__floattitf");
-
-  fixsfsi_libfunc = init_one_libfunc ("__fixsfsi");
-  fixsfdi_libfunc = init_one_libfunc ("__fixsfdi");
-  fixsfti_libfunc = init_one_libfunc ("__fixsfti");
-
-  fixdfsi_libfunc = init_one_libfunc ("__fixdfsi");
-  fixdfdi_libfunc = init_one_libfunc ("__fixdfdi");
-  fixdfti_libfunc = init_one_libfunc ("__fixdfti");
-
-  fixxfsi_libfunc = init_one_libfunc ("__fixxfsi");
-  fixxfdi_libfunc = init_one_libfunc ("__fixxfdi");
-  fixxfti_libfunc = init_one_libfunc ("__fixxfti");
-
-  fixtfsi_libfunc = init_one_libfunc ("__fixtfsi");
-  fixtfdi_libfunc = init_one_libfunc ("__fixtfdi");
-  fixtfti_libfunc = init_one_libfunc ("__fixtfti");
-
-  fixunssfsi_libfunc = init_one_libfunc ("__fixunssfsi");
-  fixunssfdi_libfunc = init_one_libfunc ("__fixunssfdi");
-  fixunssfti_libfunc = init_one_libfunc ("__fixunssfti");
-
-  fixunsdfsi_libfunc = init_one_libfunc ("__fixunsdfsi");
-  fixunsdfdi_libfunc = init_one_libfunc ("__fixunsdfdi");
-  fixunsdfti_libfunc = init_one_libfunc ("__fixunsdfti");
-
-  fixunsxfsi_libfunc = init_one_libfunc ("__fixunsxfsi");
-  fixunsxfdi_libfunc = init_one_libfunc ("__fixunsxfdi");
-  fixunsxfti_libfunc = init_one_libfunc ("__fixunsxfti");
-
-  fixunstfsi_libfunc = init_one_libfunc ("__fixunstfsi");
-  fixunstfdi_libfunc = init_one_libfunc ("__fixunstfdi");
-  fixunstfti_libfunc = init_one_libfunc ("__fixunstfti");
-
   /* For function entry/exit instrumentation.  */
   profile_function_entry_libfunc
     = init_one_libfunc ("__cyg_profile_func_enter");
@@ -5701,16 +5211,71 @@ init_optabs (void)
     = init_one_libfunc ("__cyg_profile_func_exit");
 
   gcov_flush_libfunc = init_one_libfunc ("__gcov_flush");
-  gcov_init_libfunc = init_one_libfunc ("__gcov_init");
 
   if (HAVE_conditional_trap)
     trap_rtx = gen_rtx_fmt_ee (EQ, VOIDmode, NULL_RTX, NULL_RTX);
 
-#ifdef INIT_TARGET_OPTABS
   /* Allow the target to add more libcalls or rename some, etc.  */
-  INIT_TARGET_OPTABS;
-#endif
+  targetm.init_libfuncs ();
+}
+
+#ifdef DEBUG
+
+/* Print information about the current contents of the optabs on
+   STDERR.  */
+
+static void
+debug_optab_libfuncs (void)
+{
+  int i;
+  int j;
+  int k;
+
+  /* Dump the arithmetic optabs.  */
+  for (i = 0; i != (int) OTI_MAX; i++)
+    for (j = 0; j < NUM_MACHINE_MODES; ++j)
+      {
+       optab o;
+       struct optab_handlers *h;
+
+       o = optab_table[i];
+       h = &o->handlers[j];
+       if (h->libfunc)
+         {
+           if (GET_CODE (h->libfunc) != SYMBOL_REF)
+             abort ();
+           fprintf (stderr, "%s\t%s:\t%s\n",
+                    GET_RTX_NAME (o->code),
+                    GET_MODE_NAME (j),
+                    XSTR (h->libfunc, 0));
+         }
+      }
+
+  /* Dump the conversion optabs.  */
+  for (i = 0; i < (int) CTI_MAX; ++i)
+    for (j = 0; j < NUM_MACHINE_MODES; ++j)
+      for (k = 0; k < NUM_MACHINE_MODES; ++k)
+       {
+         convert_optab o;
+         struct optab_handlers *h;
+
+         o = &convert_optab_table[i];
+         h = &o->handlers[j][k];
+         if (h->libfunc)
+           {
+             if (GET_CODE (h->libfunc) != SYMBOL_REF)
+               abort ();
+             fprintf (stderr, "%s\t%s\t%s:\t%s\n",
+                      GET_RTX_NAME (o->code),
+                      GET_MODE_NAME (j),
+                      GET_MODE_NAME (k),
+                      XSTR (h->libfunc, 0));
+           }
+       }
 }
+
+#endif /* DEBUG */
+
 \f
 /* Generate insns to trap with code TCODE if OP1 and OP2 satisfy condition
    CODE.  Return 0 on failure.  */
@@ -5736,6 +5301,11 @@ gen_cond_trap (enum rtx_code code ATTRIBUTE_UNUSED, rtx op1,
   start_sequence ();
   op1 = prepare_operand (icode, op1, 0, mode, mode, 0);
   op2 = prepare_operand (icode, op2, 1, mode, mode, 0);
+  if (!op1 || !op2)
+    {
+      end_sequence ();
+      return 0;
+    }
   emit_insn (GEN_FCN (icode) (op1, op2));
 
   PUT_CODE (trap_rtx, code);
@@ -5750,4 +5320,168 @@ gen_cond_trap (enum rtx_code code ATTRIBUTE_UNUSED, rtx op1,
   return insn;
 }
 
+/* Return rtx code for TCODE. Use UNSIGNEDP to select signed
+   or unsigned operation code.  */
+
+static enum rtx_code
+get_rtx_code (enum tree_code tcode, bool unsignedp)
+{
+  enum rtx_code code;
+  switch (tcode)
+    {
+    case EQ_EXPR:
+      code = EQ;
+      break;
+    case NE_EXPR:
+      code = NE;
+      break;
+    case LT_EXPR:
+      code = unsignedp ? LTU : LT;
+      break;
+    case LE_EXPR:
+      code = unsignedp ? LEU : LE;
+      break;
+    case GT_EXPR:
+      code = unsignedp ? GTU : GT;
+      break;
+    case GE_EXPR:
+      code = unsignedp ? GEU : GE;
+      break;
+      
+    case UNORDERED_EXPR:
+      code = UNORDERED;
+      break;
+    case ORDERED_EXPR:
+      code = ORDERED;
+      break;
+    case UNLT_EXPR:
+      code = UNLT;
+      break;
+    case UNLE_EXPR:
+      code = UNLE;
+      break;
+    case UNGT_EXPR:
+      code = UNGT;
+      break;
+    case UNGE_EXPR:
+      code = UNGE;
+      break;
+    case UNEQ_EXPR:
+      code = UNEQ;
+      break;
+    case LTGT_EXPR:
+      code = LTGT;
+      break;
+
+    default:
+      abort ();
+    }
+  return code;
+}
+
+/* Return comparison rtx for COND. Use UNSIGNEDP to select signed or
+   unsigned operators. Do not generate compare instruction.  */
+
+static rtx
+vector_compare_rtx (tree cond, bool unsignedp, enum insn_code icode)
+{
+  enum rtx_code rcode;
+  tree t_op0, t_op1;
+  rtx rtx_op0, rtx_op1;
+
+  if (!COMPARISON_CLASS_P (cond))
+    {
+      /* This is unlikely. While generating VEC_COND_EXPR,
+        auto vectorizer ensures that condition is a relational
+        operation.  */
+      abort ();
+    }
+  else
+    {
+      rcode = get_rtx_code (TREE_CODE (cond), unsignedp); 
+      t_op0 = TREE_OPERAND (cond, 0);
+      t_op1 = TREE_OPERAND (cond, 1);
+    }
+
+  /* Expand operands.  */
+  rtx_op0 = expand_expr (t_op0, NULL_RTX, TYPE_MODE (TREE_TYPE (t_op0)), 1);
+  rtx_op1 = expand_expr (t_op1, NULL_RTX, TYPE_MODE (TREE_TYPE (t_op1)), 1);
+
+  if (!(*insn_data[icode].operand[4].predicate) (rtx_op0, GET_MODE (rtx_op0))
+      && GET_MODE (rtx_op0) != VOIDmode)
+    rtx_op0 = force_reg (GET_MODE (rtx_op0), rtx_op0);
+  
+  if (!(*insn_data[icode].operand[5].predicate) (rtx_op1, GET_MODE (rtx_op1))
+      && GET_MODE (rtx_op1) != VOIDmode)
+    rtx_op1 = force_reg (GET_MODE (rtx_op1), rtx_op1);
+
+  return gen_rtx_fmt_ee (rcode, VOIDmode, rtx_op0, rtx_op1);
+}
+
+/* Return insn code for VEC_COND_EXPR EXPR.  */
+  
+static inline enum insn_code 
+get_vcond_icode (tree expr, enum machine_mode mode)
+{
+  enum insn_code icode = CODE_FOR_nothing;
+
+  if (TYPE_UNSIGNED (TREE_TYPE (expr)))
+    icode = vcondu_gen_code[mode];
+  else
+    icode = vcond_gen_code[mode];
+  return icode;
+}
+
+/* Return TRUE iff, appropriate vector insns are available
+   for vector cond expr expr in VMODE mode.  */
+
+bool
+expand_vec_cond_expr_p (tree expr, enum machine_mode vmode)
+{
+  if (get_vcond_icode (expr, vmode) == CODE_FOR_nothing)
+    return false;
+  return true;
+}
+
+/* Generate insns for VEC_COND_EXPR.  */
+
+rtx
+expand_vec_cond_expr (tree vec_cond_expr, rtx target)
+{
+  enum insn_code icode;
+  rtx comparison, rtx_op1, rtx_op2, cc_op0, cc_op1;
+  enum machine_mode mode = TYPE_MODE (TREE_TYPE (vec_cond_expr));
+  bool unsignedp = TYPE_UNSIGNED (TREE_TYPE (vec_cond_expr));
+
+  icode = get_vcond_icode (vec_cond_expr, mode);
+  if (icode == CODE_FOR_nothing)
+    return 0;
+
+  if (!target)
+    target = gen_reg_rtx (mode);
+
+  /* Get comparison rtx.  First expand both cond expr operands.  */
+  comparison = vector_compare_rtx (TREE_OPERAND (vec_cond_expr, 0), 
+                                  unsignedp, icode);
+  cc_op0 = XEXP (comparison, 0);
+  cc_op1 = XEXP (comparison, 1);
+  /* Expand both operands and force them in reg, if required.  */
+  rtx_op1 = expand_expr (TREE_OPERAND (vec_cond_expr, 1),
+                        NULL_RTX, VOIDmode, 1);
+  if (!(*insn_data[icode].operand[1].predicate) (rtx_op1, mode)
+      && mode != VOIDmode)
+    rtx_op1 = force_reg (mode, rtx_op1);
+
+  rtx_op2 = expand_expr (TREE_OPERAND (vec_cond_expr, 2),
+                        NULL_RTX, VOIDmode, 1);
+  if (!(*insn_data[icode].operand[2].predicate) (rtx_op2, mode)
+      && mode != VOIDmode)
+    rtx_op2 = force_reg (mode, rtx_op2);
+
+  /* Emit instruction! */
+  emit_insn (GEN_FCN (icode) (target, rtx_op1, rtx_op2, 
+                             comparison, cc_op0,  cc_op1));
+
+  return target;
+}
 #include "gt-optabs.h"