OSDN Git Service

2011-09-02 Richard Guenther <rguenther@suse.de>
[pf3gnuchains/gcc-fork.git] / gcc / optabs.c
index a82619e..5cdcd95 100644 (file)
@@ -1,7 +1,7 @@
 /* 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, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-   Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+   2011  Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -24,7 +24,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
 
 /* Include insn-config.h before expr.h so that HAVE_conditional_move
    is properly defined.  */
@@ -41,59 +41,22 @@ along with GCC; see the file COPYING3.  If not see
 #include "recog.h"
 #include "reload.h"
 #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
-   for all sizes and kinds of operands.
-
-   The operation to be performed is often specified
-   by passing one of these optabs as an argument.
-
-   See expr.h for documentation of these optabs.  */
-
-#if GCC_VERSION >= 4000 && HAVE_DESIGNATED_INITIALIZERS
-__extension__ struct optab_d optab_table[OTI_MAX]
-  = { [0 ... OTI_MAX - 1].handlers[0 ... NUM_MACHINE_MODES - 1].insn_code
-      = CODE_FOR_nothing };
-#else
-/* init_insn_codes will do runtime initialization otherwise.  */
-struct optab_d optab_table[OTI_MAX];
+struct target_optabs default_target_optabs;
+struct target_libfuncs default_target_libfuncs;
+#if SWITCHABLE_TARGET
+struct target_optabs *this_target_optabs = &default_target_optabs;
+struct target_libfuncs *this_target_libfuncs = &default_target_libfuncs;
 #endif
 
-rtx libfunc_table[LTI_MAX];
-
-/* Tables of patterns for converting one mode to another.  */
-#if GCC_VERSION >= 4000 && HAVE_DESIGNATED_INITIALIZERS
-__extension__ struct convert_optab_d convert_optab_table[COI_MAX]
-  = { [0 ... COI_MAX - 1].handlers[0 ... NUM_MACHINE_MODES - 1]
-       [0 ... NUM_MACHINE_MODES - 1].insn_code
-      = CODE_FOR_nothing };
-#else
-/* init_convert_optab will do runtime initialization otherwise.  */
-struct convert_optab_d convert_optab_table[COI_MAX];
-#endif
+#define libfunc_hash \
+  (this_target_libfuncs->x_libfunc_hash)
 
 /* Contains the optab used for each rtx code.  */
 optab code_to_optab[NUM_RTX_CODE + 1];
 
-#ifdef HAVE_conditional_move
-/* Indexed by the machine mode, gives the insn code to make a conditional
-   move insn.  This is not indexed by the rtx-code like bcc_gen_fctn and
-   setcc_gen_code to cut down on the number of named patterns.  Consider a day
-   when a lot more rtx codes are conditional (eg: for the ARM).  */
-
-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];
-
 static void prepare_float_lib_cmp (rtx, rtx, enum rtx_code, rtx *,
                                   enum machine_mode *);
 static rtx expand_unop_direct (enum machine_mode, optab, rtx, rtx, int);
@@ -108,19 +71,7 @@ void debug_optab_libfuncs (void);
 #define DECIMAL_PREFIX "dpd_"
 #endif
 \f
-
-/* Info about libfunc.  We use same hashtable for normal optabs and conversion
-   optab.  In the first case mode2 is unused.  */
-struct GTY(()) libfunc_entry {
-  size_t optab;
-  enum machine_mode mode1, mode2;
-  rtx libfunc;
-};
-
-/* Hash table used to convert declarations into nodes.  */
-static GTY((param_is (struct libfunc_entry))) htab_t libfunc_hash;
-
-/* Used for attribute_hash.  */
+/* Used for libfunc_hash.  */
 
 static hashval_t
 hash_libfunc (const void *p)
@@ -131,7 +82,7 @@ hash_libfunc (const void *p)
          ^ e->optab);
 }
 
-/* Used for optab_hash.  */
+/* Used for libfunc_hash.  */
 
 static int
 eq_libfunc (const void *p, const void *q)
@@ -265,7 +216,32 @@ add_equal_note (rtx insns, rtx target, enum rtx_code code, rtx op0, rtx op1)
     }
 
   if (GET_RTX_CLASS (code) == RTX_UNARY)
-    note = gen_rtx_fmt_e (code, GET_MODE (target), copy_rtx (op0));
+    switch (code)
+      {
+      case FFS:
+      case CLZ:
+      case CTZ:
+      case CLRSB:
+      case POPCOUNT:
+      case PARITY:
+      case BSWAP:
+       if (GET_MODE (op0) != VOIDmode && GET_MODE (target) != GET_MODE (op0))
+         {
+           note = gen_rtx_fmt_e (code, GET_MODE (op0), copy_rtx (op0));
+           if (GET_MODE_SIZE (GET_MODE (op0))
+               > GET_MODE_SIZE (GET_MODE (target)))
+             note = simplify_gen_unary (TRUNCATE, GET_MODE (target),
+                                        note, GET_MODE (op0));
+           else
+             note = simplify_gen_unary (ZERO_EXTEND, GET_MODE (target),
+                                        note, GET_MODE (op0));
+           break;
+         }
+       /* FALLTHRU */
+      default:
+       note = gen_rtx_fmt_e (code, GET_MODE (target), copy_rtx (op0));
+       break;
+      }
   else
     note = gen_rtx_fmt_ee (code, GET_MODE (target), copy_rtx (op0), copy_rtx (op1));
 
@@ -274,6 +250,61 @@ add_equal_note (rtx insns, rtx target, enum rtx_code code, rtx op0, rtx op1)
   return 1;
 }
 \f
+/* Given two input operands, OP0 and OP1, determine what the correct from_mode
+   for a widening operation would be.  In most cases this would be OP0, but if
+   that's a constant it'll be VOIDmode, which isn't useful.  */
+
+static enum machine_mode
+widened_mode (enum machine_mode to_mode, rtx op0, rtx op1)
+{
+  enum machine_mode m0 = GET_MODE (op0);
+  enum machine_mode m1 = GET_MODE (op1);
+  enum machine_mode result;
+
+  if (m0 == VOIDmode && m1 == VOIDmode)
+    return to_mode;
+  else if (m0 == VOIDmode || GET_MODE_SIZE (m0) < GET_MODE_SIZE (m1))
+    result = m1;
+  else
+    result = m0;
+
+  if (GET_MODE_SIZE (result) > GET_MODE_SIZE (to_mode))
+    return to_mode;
+
+  return result;
+}
+\f
+/* Find a widening optab even if it doesn't widen as much as we want.
+   E.g. if from_mode is HImode, and to_mode is DImode, and there is no
+   direct HI->SI insn, then return SI->DI, if that exists.
+   If PERMIT_NON_WIDENING is non-zero then this can be used with
+   non-widening optabs also.  */
+
+enum insn_code
+find_widening_optab_handler_and_mode (optab op, enum machine_mode to_mode,
+                                     enum machine_mode from_mode,
+                                     int permit_non_widening,
+                                     enum machine_mode *found_mode)
+{
+  for (; (permit_non_widening || from_mode != to_mode)
+        && GET_MODE_SIZE (from_mode) <= GET_MODE_SIZE (to_mode)
+        && from_mode != VOIDmode;
+       from_mode = GET_MODE_WIDER_MODE (from_mode))
+    {
+      enum insn_code handler = widening_optab_handler (op, to_mode,
+                                                      from_mode);
+
+      if (handler != CODE_FOR_nothing)
+       {
+         if (found_mode)
+           *found_mode = from_mode;
+         return handler;
+       }
+    }
+
+  return CODE_FOR_nothing;
+}
+\f
 /* Widen OP to MODE and return the rtx for the widened operand.  UNSIGNEDP
    says whether OP is signed or unsigned.  NO_EXTEND is nonzero if we need
    not actually do a sign-extend or zero-extend, but can leave the
@@ -352,7 +383,7 @@ optab_for_tree_code (enum tree_code code, const_tree type,
       return TYPE_UNSIGNED (type) ? udiv_optab : sdiv_optab;
 
     case LSHIFT_EXPR:
-      if (VECTOR_MODE_P (TYPE_MODE (type)))
+      if (TREE_CODE (type) == VECTOR_TYPE)
        {
          if (subtype == optab_vector)
            return TYPE_SATURATING (type) ? NULL : vashl_optab;
@@ -364,7 +395,7 @@ optab_for_tree_code (enum tree_code code, const_tree type,
       return ashl_optab;
 
     case RSHIFT_EXPR:
-      if (VECTOR_MODE_P (TYPE_MODE (type)))
+      if (TREE_CODE (type) == VECTOR_TYPE)
        {
          if (subtype == optab_vector)
            return TYPE_UNSIGNED (type) ? vlshr_optab : vashr_optab;
@@ -374,7 +405,7 @@ optab_for_tree_code (enum tree_code code, const_tree type,
       return TYPE_UNSIGNED (type) ? lshr_optab : ashr_optab;
 
     case LROTATE_EXPR:
-      if (VECTOR_MODE_P (TYPE_MODE (type)))
+      if (TREE_CODE (type) == VECTOR_TYPE)
        {
          if (subtype == optab_vector)
            return vrotl_optab;
@@ -384,7 +415,7 @@ optab_for_tree_code (enum tree_code code, const_tree type,
       return rotl_optab;
 
     case RROTATE_EXPR:
-      if (VECTOR_MODE_P (TYPE_MODE (type)))
+      if (TREE_CODE (type) == VECTOR_TYPE)
        {
          if (subtype == optab_vector)
            return vrotr_optab;
@@ -408,6 +439,23 @@ optab_for_tree_code (enum tree_code code, const_tree type,
     case DOT_PROD_EXPR:
       return TYPE_UNSIGNED (type) ? udot_prod_optab : sdot_prod_optab;
 
+    case WIDEN_MULT_PLUS_EXPR:
+      return (TYPE_UNSIGNED (type)
+             ? (TYPE_SATURATING (type)
+                ? usmadd_widen_optab : umadd_widen_optab)
+             : (TYPE_SATURATING (type)
+                ? ssmadd_widen_optab : smadd_widen_optab));
+
+    case WIDEN_MULT_MINUS_EXPR:
+      return (TYPE_UNSIGNED (type)
+             ? (TYPE_SATURATING (type)
+                ? usmsub_widen_optab : umsub_widen_optab)
+             : (TYPE_SATURATING (type)
+                ? ssmsub_widen_optab : smsub_widen_optab));
+
+    case FMA_EXPR:
+      return fma_optab;
+
     case REDUC_MAX_EXPR:
       return TYPE_UNSIGNED (type) ? reduc_umax_optab : reduc_smax_optab;
 
@@ -533,128 +581,53 @@ rtx
 expand_widen_pattern_expr (sepops ops, rtx op0, rtx op1, rtx wide_op,
                           rtx target, int unsignedp)
 {
+  struct expand_operand eops[4];
   tree oprnd0, oprnd1, oprnd2;
   enum machine_mode wmode = VOIDmode, tmode0, tmode1 = VOIDmode;
   optab widen_pattern_optab;
-  int icode;
-  enum machine_mode xmode0, xmode1 = VOIDmode, wxmode = VOIDmode;
-  rtx temp;
-  rtx pat;
-  rtx xop0, xop1, wxop;
+  enum insn_code icode;
   int nops = TREE_CODE_LENGTH (ops->code);
+  int op;
 
   oprnd0 = ops->op0;
   tmode0 = TYPE_MODE (TREE_TYPE (oprnd0));
   widen_pattern_optab =
     optab_for_tree_code (ops->code, TREE_TYPE (oprnd0), optab_default);
-  icode = (int) optab_handler (widen_pattern_optab, tmode0)->insn_code;
+  if (ops->code == WIDEN_MULT_PLUS_EXPR
+      || ops->code == WIDEN_MULT_MINUS_EXPR)
+    icode = find_widening_optab_handler (widen_pattern_optab,
+                                        TYPE_MODE (TREE_TYPE (ops->op2)),
+                                        tmode0, 0);
+  else
+    icode = optab_handler (widen_pattern_optab, tmode0);
   gcc_assert (icode != CODE_FOR_nothing);
-  xmode0 = insn_data[icode].operand[1].mode;
 
   if (nops >= 2)
     {
       oprnd1 = ops->op1;
       tmode1 = TYPE_MODE (TREE_TYPE (oprnd1));
-      xmode1 = insn_data[icode].operand[2].mode;
     }
 
   /* The last operand is of a wider mode than the rest of the operands.  */
   if (nops == 2)
-    {
-      wmode = tmode1;
-      wxmode = xmode1;
-    }
+    wmode = tmode1;
   else if (nops == 3)
     {
       gcc_assert (tmode1 == tmode0);
       gcc_assert (op1);
       oprnd2 = ops->op2;
       wmode = TYPE_MODE (TREE_TYPE (oprnd2));
-      wxmode = insn_data[icode].operand[3].mode;
     }
 
-  if (!wide_op)
-    wmode = wxmode = insn_data[icode].operand[0].mode;
-
-  if (!target
-      || ! (*insn_data[icode].operand[0].predicate) (target, wmode))
-    temp = gen_reg_rtx (wmode);
-  else
-    temp = target;
-
-  xop0 = op0;
-  xop1 = op1;
-  wxop = wide_op;
-
-  /* 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) != xmode0 && xmode0 != VOIDmode)
-    xop0 = convert_modes (xmode0,
-                          GET_MODE (op0) != VOIDmode
-                          ? GET_MODE (op0)
-                          : tmode0,
-                          xop0, unsignedp);
-
+  op = 0;
+  create_output_operand (&eops[op++], target, TYPE_MODE (ops->type));
+  create_convert_operand_from (&eops[op++], op0, tmode0, unsignedp);
   if (op1)
-    if (GET_MODE (op1) != xmode1 && xmode1 != VOIDmode)
-      xop1 = convert_modes (xmode1,
-                            GET_MODE (op1) != VOIDmode
-                            ? GET_MODE (op1)
-                            : tmode1,
-                            xop1, unsignedp);
-
+    create_convert_operand_from (&eops[op++], op1, tmode1, unsignedp);
   if (wide_op)
-    if (GET_MODE (wide_op) != wxmode && wxmode != VOIDmode)
-      wxop = convert_modes (wxmode,
-                            GET_MODE (wide_op) != VOIDmode
-                            ? GET_MODE (wide_op)
-                            : wmode,
-                            wxop, unsignedp);
-
-  /* Now, if insn's predicates don't allow our operands, put them into
-     pseudo regs.  */
-
-  if (! (*insn_data[icode].operand[1].predicate) (xop0, xmode0)
-      && xmode0 != VOIDmode)
-    xop0 = copy_to_mode_reg (xmode0, xop0);
-
-  if (op1)
-    {
-      if (! (*insn_data[icode].operand[2].predicate) (xop1, xmode1)
-          && xmode1 != VOIDmode)
-        xop1 = copy_to_mode_reg (xmode1, xop1);
-
-      if (wide_op)
-        {
-          if (! (*insn_data[icode].operand[3].predicate) (wxop, wxmode)
-              && wxmode != VOIDmode)
-            wxop = copy_to_mode_reg (wxmode, wxop);
-
-          pat = GEN_FCN (icode) (temp, xop0, xop1, wxop);
-        }
-      else
-        pat = GEN_FCN (icode) (temp, xop0, xop1);
-    }
-  else
-    {
-      if (wide_op)
-        {
-          if (! (*insn_data[icode].operand[2].predicate) (wxop, wxmode)
-              && wxmode != VOIDmode)
-            wxop = copy_to_mode_reg (wxmode, wxop);
-
-          pat = GEN_FCN (icode) (temp, xop0, wxop);
-        }
-      else
-        pat = GEN_FCN (icode) (temp, xop0);
-    }
-
-  emit_insn (pat);
-  return temp;
+    create_convert_operand_from (&eops[op++], wide_op, wmode, unsignedp);
+  expand_insn (icode, op, eops);
+  return eops[0].value;
 }
 
 /* Generate code to perform an operation specified by TERNARY_OPTAB
@@ -672,68 +645,17 @@ rtx
 expand_ternary_op (enum machine_mode mode, optab ternary_optab, rtx op0,
                   rtx op1, rtx op2, rtx target, int unsignedp)
 {
-  int icode = (int) optab_handler (ternary_optab, 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;
+  struct expand_operand ops[4];
+  enum insn_code icode = optab_handler (ternary_optab, mode);
 
-  gcc_assert (optab_handler (ternary_optab, mode)->insn_code
-             != CODE_FOR_nothing);
+  gcc_assert (optab_handler (ternary_optab, mode) != CODE_FOR_nothing);
 
-  if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-    temp = gen_reg_rtx (mode);
-  else
-    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;
+  create_output_operand (&ops[0], target, mode);
+  create_convert_operand_from (&ops[1], op0, mode, unsignedp);
+  create_convert_operand_from (&ops[2], op1, mode, unsignedp);
+  create_convert_operand_from (&ops[3], op2, mode, unsignedp);
+  expand_insn (icode, 4, ops);
+  return ops[0].value;
 }
 
 
@@ -779,15 +701,13 @@ force_expand_binop (enum machine_mode mode, optab binoptab,
 rtx
 expand_vec_shift_expr (sepops ops, rtx target)
 {
+  struct expand_operand eops[3];
   enum insn_code icode;
   rtx rtx_op1, rtx_op2;
-  enum machine_mode mode1;
-  enum machine_mode mode2;
   enum machine_mode mode = TYPE_MODE (ops->type);
   tree vec_oprnd = ops->op0;
   tree shift_oprnd = ops->op1;
   optab shift_optab;
-  rtx pat;
 
   switch (ops->code)
     {
@@ -801,32 +721,18 @@ expand_vec_shift_expr (sepops ops, rtx target)
        gcc_unreachable ();
     }
 
-  icode = optab_handler (shift_optab, mode)->insn_code;
+  icode = optab_handler (shift_optab, mode);
   gcc_assert (icode != CODE_FOR_nothing);
 
-  mode1 = insn_data[icode].operand[1].mode;
-  mode2 = insn_data[icode].operand[2].mode;
-
   rtx_op1 = expand_normal (vec_oprnd);
-  if (!(*insn_data[icode].operand[1].predicate) (rtx_op1, mode1)
-      && mode1 != VOIDmode)
-    rtx_op1 = force_reg (mode1, rtx_op1);
-
   rtx_op2 = expand_normal (shift_oprnd);
-  if (!(*insn_data[icode].operand[2].predicate) (rtx_op2, mode2)
-      && mode2 != VOIDmode)
-    rtx_op2 = force_reg (mode2, rtx_op2);
 
-  if (!target
-      || ! (*insn_data[icode].operand[0].predicate) (target, mode))
-    target = gen_reg_rtx (mode);
+  create_output_operand (&eops[0], target, mode);
+  create_input_operand (&eops[1], rtx_op1, GET_MODE (rtx_op1));
+  create_convert_operand_from_type (&eops[2], rtx_op2, TREE_TYPE (shift_oprnd));
+  expand_insn (icode, 3, eops);
 
-  /* Emit instruction */
-  pat = GEN_FCN (icode) (target, rtx_op1, rtx_op2);
-  gcc_assert (pat);
-  emit_insn (pat);
-
-  return target;
+  return eops[0].value;
 }
 
 /* This subroutine of expand_doubleword_shift handles the cases in which
@@ -1287,7 +1193,7 @@ expand_doubleword_mult (enum machine_mode mode, rtx op0, rtx op1, rtx target,
   /* OP1_HIGH should now be dead.  */
 
   adjust = expand_binop (word_mode, add_optab, adjust, temp,
-                        adjust, 0, OPTAB_DIRECT);
+                        NULL_RTX, 0, OPTAB_DIRECT);
 
   if (target && !REG_P (target))
     target = NULL_RTX;
@@ -1304,8 +1210,7 @@ expand_doubleword_mult (enum machine_mode mode, rtx op0, rtx op1, rtx target,
 
   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);
+                        NULL_RTX, 0, OPTAB_DIRECT);
   emit_move_insn (product_high, adjust);
   return product;
 }
@@ -1380,20 +1285,21 @@ commutative_optab_p (optab binoptab)
          || binoptab == umul_highpart_optab);
 }
 
-/* X is to be used in mode MODE as an operand to BINOPTAB.  If we're
+/* X is to be used in mode MODE as operand OPN to BINOPTAB.  If we're
    optimizing, and if the operand is a constant that costs more than
    1 instruction, force the constant into a register and return that
    register.  Return X otherwise.  UNSIGNEDP says whether X is unsigned.  */
 
 static rtx
 avoid_expensive_constant (enum machine_mode mode, optab binoptab,
-                         rtx x, bool unsignedp)
+                         int opn, rtx x, bool unsignedp)
 {
   bool speed = optimize_insn_for_speed_p ();
+
   if (mode != VOIDmode
       && optimize
       && CONSTANT_P (x)
-      && rtx_cost (x, binoptab->code, speed) > rtx_cost (x, SET, speed))
+      && rtx_cost (x, binoptab->code, opn, speed) > set_src_cost (x, speed))
     {
       if (CONST_INT_P (x))
        {
@@ -1417,27 +1323,24 @@ expand_binop_directly (enum machine_mode mode, optab binoptab,
                       rtx target, int unsignedp, enum optab_methods methods,
                       rtx last)
 {
-  int icode = (int) optab_handler (binoptab, 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 tmp_mode;
+  enum machine_mode from_mode = widened_mode (mode, op0, op1);
+  enum insn_code icode = find_widening_optab_handler (binoptab, mode,
+                                                     from_mode, 1);
+  enum machine_mode xmode0 = insn_data[(int) icode].operand[1].mode;
+  enum machine_mode xmode1 = insn_data[(int) icode].operand[2].mode;
+  enum machine_mode mode0, mode1, tmp_mode;
+  struct expand_operand ops[3];
   bool commutative_p;
   rtx pat;
   rtx xop0 = op0, xop1 = op1;
-  rtx temp;
   rtx swap;
 
-  if (target)
-    temp = target;
-  else
-    temp = gen_reg_rtx (mode);
-
   /* If it is a commutative operator and the modes would match
      if we would swap the operands, we can save the conversions.  */
   commutative_p = commutative_optab_p (binoptab);
   if (commutative_p
-      && GET_MODE (xop0) != mode0 && GET_MODE (xop1) != mode1
-      && GET_MODE (xop0) == mode1 && GET_MODE (xop1) == mode1)
+      && GET_MODE (xop0) != xmode0 && GET_MODE (xop1) != xmode1
+      && GET_MODE (xop0) == xmode1 && GET_MODE (xop1) == xmode1)
     {
       swap = xop0;
       xop0 = xop1;
@@ -1445,9 +1348,9 @@ expand_binop_directly (enum machine_mode mode, optab binoptab,
     }
 
   /* If we are optimizing, force expensive constants into a register.  */
-  xop0 = avoid_expensive_constant (mode0, binoptab, xop0, unsignedp);
+  xop0 = avoid_expensive_constant (xmode0, binoptab, 0, xop0, unsignedp);
   if (!shift_optab_p (binoptab))
-    xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp);
+    xop1 = avoid_expensive_constant (xmode1, binoptab, 1, xop1, unsignedp);
 
   /* In case the insn wants input operands in modes different from
      those of the actual operands, convert the operands.  It would
@@ -1455,19 +1358,19 @@ expand_binop_directly (enum machine_mode mode, optab binoptab,
      that they're properly zero-extended, sign-extended or truncated
      for their mode.  */
 
-  if (GET_MODE (xop0) != mode0 && mode0 != VOIDmode)
-    xop0 = convert_modes (mode0,
-                         GET_MODE (xop0) != VOIDmode
-                         ? GET_MODE (xop0)
-                         : mode,
-                         xop0, unsignedp);
+  mode0 = GET_MODE (xop0) != VOIDmode ? GET_MODE (xop0) : mode;
+  if (xmode0 != VOIDmode && xmode0 != mode0)
+    {
+      xop0 = convert_modes (xmode0, mode0, xop0, unsignedp);
+      mode0 = xmode0;
+    }
 
-  if (GET_MODE (xop1) != mode1 && mode1 != VOIDmode)
-    xop1 = convert_modes (mode1,
-                         GET_MODE (xop1) != VOIDmode
-                         ? GET_MODE (xop1)
-                         : mode,
-                         xop1, unsignedp);
+  mode1 = GET_MODE (xop1) != VOIDmode ? GET_MODE (xop1) : mode;
+  if (xmode1 != VOIDmode && xmode1 != mode1)
+    {
+      xop1 = convert_modes (xmode1, mode1, xop1, unsignedp);
+      mode1 = xmode1;
+    }
 
   /* If operation is commutative,
      try to make the first operand a register.
@@ -1484,14 +1387,6 @@ expand_binop_directly (enum machine_mode mode, optab binoptab,
   /* 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 (binoptab == vec_pack_trunc_optab
       || binoptab == vec_pack_usat_optab
       || binoptab == vec_pack_ssat_optab
@@ -1500,24 +1395,28 @@ expand_binop_directly (enum machine_mode mode, optab binoptab,
     {
       /* The mode of the result is different then the mode of the
         arguments.  */
-      tmp_mode = insn_data[icode].operand[0].mode;
+      tmp_mode = insn_data[(int) icode].operand[0].mode;
       if (GET_MODE_NUNITS (tmp_mode) != 2 * GET_MODE_NUNITS (mode))
-       return 0;
+       {
+         delete_insns_since (last);
+         return NULL_RTX;
+       }
     }
   else
     tmp_mode = mode;
 
-  if (!insn_data[icode].operand[0].predicate (temp, tmp_mode))
-    temp = gen_reg_rtx (tmp_mode);
-
-  pat = GEN_FCN (icode) (temp, xop0, xop1);
+  create_output_operand (&ops[0], target, tmp_mode);
+  create_input_operand (&ops[1], xop0, mode0);
+  create_input_operand (&ops[2], xop1, mode1);
+  pat = maybe_gen_insn (icode, 3, ops);
   if (pat)
     {
       /* If PAT is composed of more than one insn, try to add an appropriate
         REG_EQUAL note to it.  If we can't because TEMP conflicts with an
         operand, call expand_binop again, this time without a target.  */
       if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
-         && ! add_equal_note (pat, temp, binoptab->code, xop0, xop1))
+         && ! add_equal_note (pat, ops[0].value, binoptab->code,
+                              ops[1].value, ops[2].value))
        {
          delete_insns_since (last);
          return expand_binop (mode, binoptab, op0, op1, NULL_RTX,
@@ -1525,9 +1424,8 @@ expand_binop_directly (enum machine_mode mode, optab binoptab,
        }
 
       emit_insn (pat);
-      return temp;
+      return ops[0].value;
     }
-
   delete_insns_since (last);
   return NULL_RTX;
 }
@@ -1574,7 +1472,9 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
   /* If we can do it with a three-operand insn, do so.  */
 
   if (methods != OPTAB_MUST_WIDEN
-      && optab_handler (binoptab, mode)->insn_code != CODE_FOR_nothing)
+      && find_widening_optab_handler (binoptab, mode,
+                                     widened_mode (mode, op0, op1), 1)
+           != CODE_FOR_nothing)
     {
       temp = expand_binop_directly (mode, binoptab, op0, op1, target,
                                    unsignedp, methods, last);
@@ -1585,14 +1485,14 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
   /* If we were trying to rotate, and that didn't work, try rotating
      the other direction before falling back to shifts and bitwise-or.  */
   if (((binoptab == rotl_optab
-       && optab_handler (rotr_optab, mode)->insn_code != CODE_FOR_nothing)
+       && optab_handler (rotr_optab, mode) != CODE_FOR_nothing)
        || (binoptab == rotr_optab
-          && optab_handler (rotl_optab, mode)->insn_code != CODE_FOR_nothing))
+          && optab_handler (rotl_optab, mode) != CODE_FOR_nothing))
       && mclass == MODE_INT)
     {
       optab otheroptab = (binoptab == rotl_optab ? rotr_optab : rotl_optab);
       rtx newop1;
-      unsigned int bits = GET_MODE_BITSIZE (mode);
+      unsigned int bits = GET_MODE_PRECISION (mode);
 
       if (CONST_INT_P (op1))
         newop1 = GEN_INT (bits - INTVAL (op1));
@@ -1613,20 +1513,20 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
      takes operands of this mode and makes a wider mode.  */
 
   if (binoptab == smul_optab
-      && GET_MODE_WIDER_MODE (mode) != VOIDmode
-      && ((optab_handler ((unsignedp ? umul_widen_optab : smul_widen_optab),
-                         GET_MODE_WIDER_MODE (mode))->insn_code)
+      && GET_MODE_2XWIDER_MODE (mode) != VOIDmode
+      && (widening_optab_handler ((unsignedp ? umul_widen_optab
+                                            : smul_widen_optab),
+                                 GET_MODE_2XWIDER_MODE (mode), mode)
          != CODE_FOR_nothing))
     {
-      temp = expand_binop (GET_MODE_WIDER_MODE (mode),
+      temp = expand_binop (GET_MODE_2XWIDER_MODE (mode),
                           unsignedp ? umul_widen_optab : smul_widen_optab,
                           op0, op1, NULL_RTX, unsignedp, OPTAB_DIRECT);
 
       if (temp != 0)
        {
          if (GET_MODE_CLASS (mode) == MODE_INT
-             && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
-                                        GET_MODE_BITSIZE (GET_MODE (temp))))
+             && TRULY_NOOP_TRUNCATION_MODES_P (mode, GET_MODE (temp)))
            return gen_lowpart (mode, temp);
          else
            return convert_to_mode (mode, temp, unsignedp);
@@ -1643,12 +1543,14 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
         wider_mode != VOIDmode;
         wider_mode = GET_MODE_WIDER_MODE (wider_mode))
       {
-       if (optab_handler (binoptab, wider_mode)->insn_code != CODE_FOR_nothing
+       if (optab_handler (binoptab, wider_mode) != CODE_FOR_nothing
            || (binoptab == smul_optab
                && GET_MODE_WIDER_MODE (wider_mode) != VOIDmode
-               && ((optab_handler ((unsignedp ? umul_widen_optab
-                                    : smul_widen_optab),
-                                    GET_MODE_WIDER_MODE (wider_mode))->insn_code)
+               && (find_widening_optab_handler ((unsignedp
+                                                 ? umul_widen_optab
+                                                 : smul_widen_optab),
+                                                GET_MODE_WIDER_MODE (wider_mode),
+                                                mode, 0)
                    != CODE_FOR_nothing)))
          {
            rtx xop0 = op0, xop1 = op1;
@@ -1665,10 +1567,10 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
                && mclass == MODE_INT)
              {
                no_extend = 1;
-               xop0 = avoid_expensive_constant (mode, binoptab,
+               xop0 = avoid_expensive_constant (mode, binoptab, 0,
                                                 xop0, unsignedp);
                if (binoptab != ashl_optab)
-                 xop1 = avoid_expensive_constant (mode, binoptab,
+                 xop1 = avoid_expensive_constant (mode, binoptab, 1,
                                                   xop1, unsignedp);
              }
 
@@ -1683,8 +1585,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
            if (temp)
              {
                if (mclass != MODE_INT
-                    || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
-                                               GET_MODE_BITSIZE (wider_mode)))
+                    || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode))
                  {
                    if (target == 0)
                      target = gen_reg_rtx (mode);
@@ -1715,14 +1616,17 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
   if ((binoptab == and_optab || binoptab == ior_optab || binoptab == xor_optab)
       && mclass == MODE_INT
       && GET_MODE_SIZE (mode) > UNITS_PER_WORD
-      && optab_handler (binoptab, word_mode)->insn_code != CODE_FOR_nothing)
+      && optab_handler (binoptab, word_mode) != CODE_FOR_nothing)
     {
       int i;
       rtx insns;
 
       /* 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)
+      if (target == 0
+         || target == op0
+         || target == op1
+         || !valid_multiword_target_p (target))
        target = gen_reg_rtx (mode);
 
       start_sequence ();
@@ -1759,9 +1663,10 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
       && mclass == MODE_INT
       && (CONST_INT_P (op1) || optimize_insn_for_speed_p ())
       && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
-      && optab_handler (binoptab, word_mode)->insn_code != CODE_FOR_nothing
-      && optab_handler (ashl_optab, word_mode)->insn_code != CODE_FOR_nothing
-      && optab_handler (lshr_optab, word_mode)->insn_code != CODE_FOR_nothing)
+      && GET_MODE_PRECISION (mode) == GET_MODE_BITSIZE (mode)
+      && optab_handler (binoptab, word_mode) != CODE_FOR_nothing
+      && optab_handler (ashl_optab, word_mode) != CODE_FOR_nothing
+      && optab_handler (lshr_optab, word_mode) != CODE_FOR_nothing)
     {
       unsigned HOST_WIDE_INT shift_mask, double_shift_mask;
       enum machine_mode op1_mode;
@@ -1790,7 +1695,10 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
 
          /* 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)
+         if (target == 0
+             || target == op0
+             || target == op1
+             || !valid_multiword_target_p (target))
            target = gen_reg_rtx (mode);
 
          start_sequence ();
@@ -1828,9 +1736,9 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
   if ((binoptab == rotl_optab || binoptab == rotr_optab)
       && mclass == MODE_INT
       && CONST_INT_P (op1)
-      && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
-      && optab_handler (ashl_optab, word_mode)->insn_code != CODE_FOR_nothing
-      && optab_handler (lshr_optab, word_mode)->insn_code != CODE_FOR_nothing)
+      && GET_MODE_PRECISION (mode) == 2 * BITS_PER_WORD
+      && optab_handler (ashl_optab, word_mode) != CODE_FOR_nothing
+      && optab_handler (lshr_optab, word_mode) != CODE_FOR_nothing)
     {
       rtx insns;
       rtx into_target, outof_target;
@@ -1844,7 +1752,11 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
         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))
+      if (target == 0
+         || target == op0
+         || target == op1
+         || !REG_P (target)
+         || !valid_multiword_target_p (target))
        target = gen_reg_rtx (mode);
 
       start_sequence ();
@@ -1941,7 +1853,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
   if ((binoptab == add_optab || binoptab == sub_optab)
       && mclass == MODE_INT
       && GET_MODE_SIZE (mode) >= 2 * UNITS_PER_WORD
-      && optab_handler (binoptab, word_mode)->insn_code != CODE_FOR_nothing)
+      && optab_handler (binoptab, word_mode) != CODE_FOR_nothing)
     {
       unsigned int i;
       optab otheroptab = binoptab == add_optab ? sub_optab : add_optab;
@@ -1964,7 +1876,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
 
       xtarget = gen_reg_rtx (mode);
 
-      if (target == 0 || !REG_P (target))
+      if (target == 0 || !REG_P (target) || !valid_multiword_target_p (target))
        target = xtarget;
 
       /* Indicate for flow that the entire target reg is being set.  */
@@ -2038,7 +1950,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
 
       if (i == GET_MODE_BITSIZE (mode) / (unsigned) BITS_PER_WORD)
        {
-         if (optab_handler (mov_optab, mode)->insn_code != CODE_FOR_nothing
+         if (optab_handler (mov_optab, mode) != CODE_FOR_nothing
              || ! rtx_equal_p (target, xtarget))
            {
              rtx temp = emit_move_insn (target, xtarget);
@@ -2067,13 +1979,12 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
   if (binoptab == smul_optab
       && mclass == MODE_INT
       && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
-      && optab_handler (smul_optab, word_mode)->insn_code != CODE_FOR_nothing
-      && optab_handler (add_optab, word_mode)->insn_code != CODE_FOR_nothing)
+      && optab_handler (smul_optab, word_mode) != CODE_FOR_nothing
+      && optab_handler (add_optab, word_mode) != CODE_FOR_nothing)
     {
       rtx product = NULL_RTX;
-
-      if (optab_handler (umul_widen_optab, mode)->insn_code
-         != CODE_FOR_nothing)
+      if (widening_optab_handler (umul_widen_optab, mode, word_mode)
+           != CODE_FOR_nothing)
        {
          product = expand_doubleword_mult (mode, op0, op1, target,
                                            true, methods);
@@ -2082,8 +1993,8 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
        }
 
       if (product == NULL_RTX
-         && optab_handler (smul_widen_optab, mode)->insn_code
-            != CODE_FOR_nothing)
+         && widening_optab_handler (smul_widen_optab, mode, word_mode)
+               != CODE_FOR_nothing)
        {
          product = expand_doubleword_mult (mode, op0, op1, target,
                                            false, methods);
@@ -2093,7 +2004,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
 
       if (product != NULL_RTX)
        {
-         if (optab_handler (mov_optab, mode)->insn_code != CODE_FOR_nothing)
+         if (optab_handler (mov_optab, mode) != CODE_FOR_nothing)
            {
              temp = emit_move_insn (target ? target : product, product);
              set_unique_reg_note (temp,
@@ -2174,8 +2085,8 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
           wider_mode != VOIDmode;
           wider_mode = GET_MODE_WIDER_MODE (wider_mode))
        {
-         if ((optab_handler (binoptab, wider_mode)->insn_code
-              != CODE_FOR_nothing)
+         if (find_widening_optab_handler (binoptab, wider_mode, mode, 1)
+                 != CODE_FOR_nothing
              || (methods == OPTAB_LIB
                  && optab_libfunc (binoptab, wider_mode)))
            {
@@ -2205,8 +2116,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
              if (temp)
                {
                  if (mclass != MODE_INT
-                     || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
-                                                GET_MODE_BITSIZE (wider_mode)))
+                     || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode))
                    {
                      if (target == 0)
                        target = gen_reg_rtx (mode);
@@ -2251,7 +2161,7 @@ sign_expand_binop (enum machine_mode mode, optab uoptab, optab soptab,
   /* Try widening to a signed int.  Make a fake signed optab that
      hides any signed insn for direct use.  */
   wide_soptab = *soptab;
-  optab_handler (&wide_soptab, mode)->insn_code = CODE_FOR_nothing;
+  set_optab_handler (&wide_soptab, mode, CODE_FOR_nothing);
   /* We don't want to generate new hash table entries from this fake
      optab.  */
   wide_soptab.libcall_gen = NULL;
@@ -2313,34 +2223,16 @@ expand_twoval_unop (optab unoptab, rtx op0, rtx targ0, rtx targ1,
   /* Record where to go back to if we fail.  */
   last = get_last_insn ();
 
-  if (optab_handler (unoptab, mode)->insn_code != CODE_FOR_nothing)
+  if (optab_handler (unoptab, mode) != CODE_FOR_nothing)
     {
-      int icode = (int) optab_handler (unoptab, 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);
+      struct expand_operand ops[3];
+      enum insn_code icode = optab_handler (unoptab, mode);
 
-      /* We could handle this, but we should always be called with a pseudo
-        for our targets and all insns should take them as outputs.  */
-      gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode));
-      gcc_assert (insn_data[icode].operand[1].predicate (targ1, mode));
-
-      pat = GEN_FCN (icode) (targ0, targ1, xop0);
-      if (pat)
-       {
-         emit_insn (pat);
-         return 1;
-       }
-      else
-       delete_insns_since (last);
+      create_fixed_operand (&ops[0], targ0);
+      create_fixed_operand (&ops[1], targ1);
+      create_convert_operand_from (&ops[2], op0, mode, unsignedp);
+      if (maybe_expand_insn (icode, 3, ops))
+       return 1;
     }
 
   /* It can't be done in this mode.  Can we do it in a wider mode?  */
@@ -2351,8 +2243,7 @@ expand_twoval_unop (optab unoptab, rtx op0, rtx targ0, rtx targ1,
           wider_mode != VOIDmode;
           wider_mode = GET_MODE_WIDER_MODE (wider_mode))
        {
-         if (optab_handler (unoptab, wider_mode)->insn_code
-             != CODE_FOR_nothing)
+         if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing)
            {
              rtx t0 = gen_reg_rtx (wider_mode);
              rtx t1 = gen_reg_rtx (wider_mode);
@@ -2406,58 +2297,25 @@ expand_twoval_binop (optab binoptab, rtx op0, rtx op1, rtx targ0, rtx targ1,
   /* Record where to go back to if we fail.  */
   last = get_last_insn ();
 
-  if (optab_handler (binoptab, mode)->insn_code != CODE_FOR_nothing)
+  if (optab_handler (binoptab, mode) != CODE_FOR_nothing)
     {
-      int icode = (int) optab_handler (binoptab, mode)->insn_code;
+      struct expand_operand ops[4];
+      enum insn_code icode = optab_handler (binoptab, mode);
       enum machine_mode mode0 = insn_data[icode].operand[1].mode;
       enum machine_mode mode1 = insn_data[icode].operand[2].mode;
-      rtx pat;
       rtx xop0 = op0, xop1 = op1;
 
       /* If we are optimizing, force expensive constants into a register.  */
-      xop0 = avoid_expensive_constant (mode0, binoptab, xop0, unsignedp);
-      xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp);
-
-      /* 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);
-
-      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
-      if (!insn_data[icode].operand[1].predicate (xop0, mode0))
-       xop0 = copy_to_mode_reg (mode0, xop0);
-
-      if (!insn_data[icode].operand[2].predicate (xop1, mode1))
-       xop1 = copy_to_mode_reg (mode1, xop1);
-
-      /* We could handle this, but we should always be called with a pseudo
-        for our targets and all insns should take them as outputs.  */
-      gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode));
-      gcc_assert (insn_data[icode].operand[3].predicate (targ1, mode));
-
-      pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1);
-      if (pat)
-       {
-         emit_insn (pat);
-         return 1;
-       }
-      else
-       delete_insns_since (last);
+      xop0 = avoid_expensive_constant (mode0, binoptab, 0, xop0, unsignedp);
+      xop1 = avoid_expensive_constant (mode1, binoptab, 1, xop1, unsignedp);
+
+      create_fixed_operand (&ops[0], targ0);
+      create_convert_operand_from (&ops[1], op0, mode, unsignedp);
+      create_convert_operand_from (&ops[2], op1, mode, unsignedp);
+      create_fixed_operand (&ops[3], targ1);
+      if (maybe_expand_insn (icode, 4, ops))
+       return 1;
+      delete_insns_since (last);
     }
 
   /* It can't be done in this mode.  Can we do it in a wider mode?  */
@@ -2468,8 +2326,7 @@ expand_twoval_binop (optab binoptab, rtx op0, rtx op1, rtx targ0, rtx targ1,
           wider_mode != VOIDmode;
           wider_mode = GET_MODE_WIDER_MODE (wider_mode))
        {
-         if (optab_handler (binoptab, wider_mode)->insn_code
-             != CODE_FOR_nothing)
+         if (optab_handler (binoptab, wider_mode) != CODE_FOR_nothing)
            {
              rtx t0 = gen_reg_rtx (wider_mode);
              rtx t1 = gen_reg_rtx (wider_mode);
@@ -2558,9 +2415,12 @@ expand_simple_unop (enum machine_mode mode, enum rtx_code code, rtx op0,
 /* Try calculating
        (clz:narrow x)
    as
-       (clz:wide (zero_extend:wide x)) - ((width wide) - (width narrow)).  */
+       (clz:wide (zero_extend:wide x)) - ((width wide) - (width narrow)).
+
+   A similar operation can be used for clrsb.  UNOPTAB says which operation
+   we are trying to expand.  */
 static rtx
-widen_clz (enum machine_mode mode, rtx op0, rtx target)
+widen_leading (enum machine_mode mode, rtx op0, rtx target, optab unoptab)
 {
   enum mode_class mclass = GET_MODE_CLASS (mode);
   if (CLASS_HAS_WIDER_MODES_P (mclass))
@@ -2570,8 +2430,7 @@ widen_clz (enum machine_mode mode, rtx op0, rtx target)
           wider_mode != VOIDmode;
           wider_mode = GET_MODE_WIDER_MODE (wider_mode))
        {
-         if (optab_handler (clz_optab, wider_mode)->insn_code
-             != CODE_FOR_nothing)
+         if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing)
            {
              rtx xop0, temp, last;
 
@@ -2579,12 +2438,14 @@ widen_clz (enum machine_mode mode, rtx op0, rtx target)
 
              if (target == 0)
                target = gen_reg_rtx (mode);
-             xop0 = widen_operand (op0, wider_mode, mode, true, false);
-             temp = expand_unop (wider_mode, clz_optab, xop0, NULL_RTX, true);
+             xop0 = widen_operand (op0, wider_mode, mode,
+                                   unoptab != clrsb_optab, false);
+             temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX,
+                                 unoptab != clrsb_optab);
              if (temp != 0)
                temp = expand_binop (wider_mode, sub_optab, temp,
-                                    GEN_INT (GET_MODE_BITSIZE (wider_mode)
-                                             - GET_MODE_BITSIZE (mode)),
+                                    GEN_INT (GET_MODE_PRECISION (wider_mode)
+                                             - GET_MODE_PRECISION (mode)),
                                     target, true, OPTAB_DIRECT);
              if (temp == 0)
                delete_insns_since (last);
@@ -2683,7 +2544,7 @@ widen_bswap (enum machine_mode mode, rtx op0, rtx target)
   for (wider_mode = GET_MODE_WIDER_MODE (mode);
        wider_mode != VOIDmode;
        wider_mode = GET_MODE_WIDER_MODE (wider_mode))
-    if (optab_handler (bswap_optab, wider_mode)->insn_code != CODE_FOR_nothing)
+    if (optab_handler (bswap_optab, wider_mode) != CODE_FOR_nothing)
       goto found;
   return NULL_RTX;
 
@@ -2693,10 +2554,12 @@ widen_bswap (enum machine_mode mode, rtx op0, rtx target)
   x = widen_operand (op0, wider_mode, mode, true, true);
   x = expand_unop (wider_mode, bswap_optab, x, NULL_RTX, true);
 
+  gcc_assert (GET_MODE_PRECISION (wider_mode) == GET_MODE_BITSIZE (wider_mode)
+             && GET_MODE_PRECISION (mode) == GET_MODE_BITSIZE (mode));
   if (x != 0)
     x = expand_shift (RSHIFT_EXPR, wider_mode, x,
-                     size_int (GET_MODE_BITSIZE (wider_mode)
-                               - GET_MODE_BITSIZE (mode)),
+                     GET_MODE_BITSIZE (wider_mode)
+                     - GET_MODE_BITSIZE (mode),
                      NULL_RTX, true);
 
   if (x != 0)
@@ -2723,7 +2586,7 @@ expand_doubleword_bswap (enum machine_mode mode, rtx op, rtx target)
   t0 = expand_unop (word_mode, bswap_optab,
                    operand_subword_force (op, 1, mode), NULL_RTX, true);
 
-  if (target == 0)
+  if (target == 0 || !valid_multiword_target_p (target))
     target = gen_reg_rtx (mode);
   if (REG_P (target))
     emit_clobber (target);
@@ -2745,8 +2608,7 @@ expand_parity (enum machine_mode mode, rtx op0, rtx target)
       for (wider_mode = mode; wider_mode != VOIDmode;
           wider_mode = GET_MODE_WIDER_MODE (wider_mode))
        {
-         if (optab_handler (popcount_optab, wider_mode)->insn_code
-             != CODE_FOR_nothing)
+         if (optab_handler (popcount_optab, wider_mode) != CODE_FOR_nothing)
            {
              rtx xop0, temp, last;
 
@@ -2771,7 +2633,7 @@ expand_parity (enum machine_mode mode, rtx op0, rtx target)
 }
 
 /* Try calculating ctz(x) as K - clz(x & -x) ,
-   where K is GET_MODE_BITSIZE(mode) - 1.
+   where K is GET_MODE_PRECISION(mode) - 1.
 
    Both __builtin_ctz and __builtin_clz are undefined at zero, so we
    don't have to worry about what the hardware does in that case.  (If
@@ -2787,7 +2649,7 @@ expand_ctz (enum machine_mode mode, rtx op0, rtx target)
 {
   rtx seq, temp;
 
-  if (optab_handler (clz_optab, mode)->insn_code == CODE_FOR_nothing)
+  if (optab_handler (clz_optab, mode) == CODE_FOR_nothing)
     return 0;
 
   start_sequence ();
@@ -2799,7 +2661,7 @@ expand_ctz (enum machine_mode mode, rtx op0, rtx target)
   if (temp)
     temp = expand_unop_direct (mode, clz_optab, temp, NULL_RTX, true);
   if (temp)
-    temp = expand_binop (mode, sub_optab, GEN_INT (GET_MODE_BITSIZE (mode) - 1),
+    temp = expand_binop (mode, sub_optab, GEN_INT (GET_MODE_PRECISION (mode) - 1),
                         temp, target,
                         true, OPTAB_DIRECT);
   if (temp == 0)
@@ -2830,7 +2692,7 @@ expand_ffs (enum machine_mode mode, rtx op0, rtx target)
   bool defined_at_zero = false;
   rtx temp, seq;
 
-  if (optab_handler (ctz_optab, mode)->insn_code != CODE_FOR_nothing)
+  if (optab_handler (ctz_optab, mode) != CODE_FOR_nothing)
     {
       start_sequence ();
 
@@ -2840,7 +2702,7 @@ expand_ffs (enum machine_mode mode, rtx op0, rtx target)
 
       defined_at_zero = (CTZ_DEFINED_VALUE_AT_ZERO (mode, val) == 2);
     }
-  else if (optab_handler (clz_optab, mode)->insn_code != CODE_FOR_nothing)
+  else if (optab_handler (clz_optab, mode) != CODE_FOR_nothing)
     {
       start_sequence ();
       temp = expand_ctz (mode, op0, 0);
@@ -2850,7 +2712,7 @@ expand_ffs (enum machine_mode mode, rtx op0, rtx target)
       if (CLZ_DEFINED_VALUE_AT_ZERO (mode, val) == 2)
        {
          defined_at_zero = true;
-         val = (GET_MODE_BITSIZE (mode) - 1) - val;
+         val = (GET_MODE_PRECISION (mode) - 1) - val;
        }
     }
   else
@@ -2927,7 +2789,7 @@ expand_absneg_bit (enum rtx_code code, enum machine_mode mode,
   const struct real_format *fmt;
   int bitpos, word, nwords, i;
   enum machine_mode imode;
-  HOST_WIDE_INT hi, lo;
+  double_int mask;
   rtx temp, insns;
 
   /* The format has to have a simple sign bit.  */
@@ -2963,20 +2825,13 @@ expand_absneg_bit (enum rtx_code code, enum machine_mode mode,
       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;
-    }
+  mask = double_int_setbit (double_int_zero, bitpos);
   if (code == ABS)
-    lo = ~lo, hi = ~hi;
+    mask = double_int_not (mask);
 
-  if (target == 0 || target == op0)
+  if (target == 0
+      || target == op0
+      || (nwords > 1 && !valid_multiword_target_p (target)))
     target = gen_reg_rtx (mode);
 
   if (nwords > 1)
@@ -2992,7 +2847,7 @@ expand_absneg_bit (enum rtx_code code, enum machine_mode mode,
            {
              temp = expand_binop (imode, code == ABS ? and_optab : xor_optab,
                                   op0_piece,
-                                  immed_double_const (lo, hi, imode),
+                                  immed_double_int_const (mask, imode),
                                   targ_piece, 1, OPTAB_LIB_WIDEN);
              if (temp != targ_piece)
                emit_move_insn (targ_piece, temp);
@@ -3010,7 +2865,7 @@ expand_absneg_bit (enum rtx_code code, enum machine_mode mode,
     {
       temp = expand_binop (imode, code == ABS ? and_optab : xor_optab,
                           gen_lowpart (imode, op0),
-                          immed_double_const (lo, hi, imode),
+                          immed_double_int_const (mask, imode),
                           gen_lowpart (imode, target), 1, OPTAB_LIB_WIDEN);
       target = lowpart_subreg_maybe_copy (mode, temp, imode);
 
@@ -3027,36 +2882,21 @@ static rtx
 expand_unop_direct (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
             int unsignedp)
 {
-  if (optab_handler (unoptab, mode)->insn_code != CODE_FOR_nothing)
+  if (optab_handler (unoptab, mode) != CODE_FOR_nothing)
     {
-      int icode = (int) optab_handler (unoptab, mode)->insn_code;
-      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
-      rtx xop0 = op0;
+      struct expand_operand ops[2];
+      enum insn_code icode = optab_handler (unoptab, mode);
       rtx last = get_last_insn ();
-      rtx pat, temp;
-
-      if (target)
-       temp = target;
-      else
-       temp = gen_reg_rtx (mode);
-
-      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, mode))
-       temp = gen_reg_rtx (mode);
+      rtx pat;
 
-      pat = GEN_FCN (icode) (temp, xop0);
+      create_output_operand (&ops[0], target, mode);
+      create_convert_operand_from (&ops[1], op0, mode, unsignedp);
+      pat = maybe_gen_insn (icode, 2, ops);
       if (pat)
        {
          if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
-             && ! add_equal_note (pat, temp, unoptab->code, xop0, NULL_RTX))
+             && ! add_equal_note (pat, ops[0].value, unoptab->code,
+                                  ops[1].value, NULL_RTX))
            {
              delete_insns_since (last);
              return expand_unop (mode, unoptab, op0, NULL_RTX, unsignedp);
@@ -3064,10 +2904,8 @@ expand_unop_direct (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
 
          emit_insn (pat);
 
-         return temp;
+         return ops[0].value;
        }
-      else
-       delete_insns_since (last);
     }
   return 0;
 }
@@ -3101,19 +2939,27 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
   /* Widening (or narrowing) clz needs special treatment.  */
   if (unoptab == clz_optab)
     {
-      temp = widen_clz (mode, op0, target);
+      temp = widen_leading (mode, op0, target, unoptab);
       if (temp)
        return temp;
 
       if (GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
-         && optab_handler (unoptab, word_mode)->insn_code != CODE_FOR_nothing)
+         && optab_handler (unoptab, word_mode) != CODE_FOR_nothing)
        {
          temp = expand_doubleword_clz (mode, op0, target);
          if (temp)
            return temp;
        }
 
-       goto try_libcall;
+      goto try_libcall;
+    }
+
+  if (unoptab == clrsb_optab)
+    {
+      temp = widen_leading (mode, op0, target, unoptab);
+      if (temp)
+       return temp;
+      goto try_libcall;
     }
 
   /* Widening (or narrowing) bswap needs special treatment.  */
@@ -3124,7 +2970,7 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
        return temp;
 
       if (GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
-         && optab_handler (unoptab, word_mode)->insn_code != CODE_FOR_nothing)
+         && optab_handler (unoptab, word_mode) != CODE_FOR_nothing)
        {
          temp = expand_doubleword_bswap (mode, op0, target);
          if (temp)
@@ -3139,7 +2985,7 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
         wider_mode != VOIDmode;
         wider_mode = GET_MODE_WIDER_MODE (wider_mode))
       {
-       if (optab_handler (unoptab, wider_mode)->insn_code != CODE_FOR_nothing)
+       if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing)
          {
            rtx xop0 = op0;
            rtx last = get_last_insn ();
@@ -3159,8 +3005,7 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
            if (temp)
              {
                if (mclass != MODE_INT
-                   || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
-                                              GET_MODE_BITSIZE (wider_mode)))
+                   || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode))
                  {
                    if (target == 0)
                      target = gen_reg_rtx (mode);
@@ -3179,12 +3024,12 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
   if (unoptab == one_cmpl_optab
       && mclass == MODE_INT
       && GET_MODE_SIZE (mode) > UNITS_PER_WORD
-      && optab_handler (unoptab, word_mode)->insn_code != CODE_FOR_nothing)
+      && optab_handler (unoptab, word_mode) != CODE_FOR_nothing)
     {
       int i;
       rtx insns;
 
-      if (target == 0 || target == op0)
+      if (target == 0 || target == op0 || !valid_multiword_target_p (target))
        target = gen_reg_rtx (mode);
 
       start_sequence ();
@@ -3268,7 +3113,8 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
       /* All of these functions return small values.  Thus we choose to
         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)
+         || unoptab == clrsb_optab || unoptab == popcount_optab
+         || unoptab == parity_optab)
        outmode
          = GET_MODE (hard_libcall_value (TYPE_MODE (integer_type_node),
                                          optab_libfunc (unoptab, mode)));
@@ -3301,8 +3147,7 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
           wider_mode != VOIDmode;
           wider_mode = GET_MODE_WIDER_MODE (wider_mode))
        {
-         if ((optab_handler (unoptab, wider_mode)->insn_code
-              != CODE_FOR_nothing)
+         if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing
              || optab_libfunc (unoptab, wider_mode))
            {
              rtx xop0 = op0;
@@ -3321,11 +3166,12 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
                                  unsignedp);
 
              /* If we are generating clz using wider mode, adjust the
-                result.  */
-             if (unoptab == clz_optab && temp != 0)
+                result.  Similarly for clrsb.  */
+             if ((unoptab == clz_optab || unoptab == clrsb_optab)
+                 && temp != 0)
                temp = expand_binop (wider_mode, sub_optab, temp,
-                                    GEN_INT (GET_MODE_BITSIZE (wider_mode)
-                                             - GET_MODE_BITSIZE (mode)),
+                                    GEN_INT (GET_MODE_PRECISION (wider_mode)
+                                             - GET_MODE_PRECISION (mode)),
                                     target, true, OPTAB_DIRECT);
 
              if (temp)
@@ -3395,7 +3241,7 @@ expand_abs_nojump (enum machine_mode mode, rtx op0, rtx target,
     }
 
   /* If we have a MAX insn, we can do this as MAX (x, -x).  */
-  if (optab_handler (smax_optab, mode)->insn_code != CODE_FOR_nothing
+  if (optab_handler (smax_optab, mode) != CODE_FOR_nothing
       && !HONOR_SIGNED_ZEROS (mode))
     {
       rtx last = get_last_insn ();
@@ -3420,7 +3266,7 @@ expand_abs_nojump (enum machine_mode mode, rtx op0, rtx target,
                      false) >= 2)
     {
       rtx extended = expand_shift (RSHIFT_EXPR, mode, op0,
-                                  size_int (GET_MODE_BITSIZE (mode) - 1),
+                                  GET_MODE_PRECISION (mode) - 1,
                                   NULL_RTX, 0);
 
       temp = expand_binop (mode, xor_optab, extended, op0, target, 0,
@@ -3498,7 +3344,7 @@ expand_one_cmpl_abs_nojump (enum machine_mode mode, rtx op0, rtx target)
     return NULL_RTX;
 
   /* If we have a MAX insn, we can do this as MAX (x, ~x).  */
-  if (optab_handler (smax_optab, mode)->insn_code != CODE_FOR_nothing)
+  if (optab_handler (smax_optab, mode) != CODE_FOR_nothing)
     {
       rtx last = get_last_insn ();
 
@@ -3521,7 +3367,7 @@ expand_one_cmpl_abs_nojump (enum machine_mode mode, rtx op0, rtx target)
                     false) >= 2)
     {
       rtx extended = expand_shift (RSHIFT_EXPR, mode, op0,
-                                  size_int (GET_MODE_BITSIZE (mode) - 1),
+                                  GET_MODE_PRECISION (mode) - 1,
                                   NULL_RTX, 0);
 
       temp = expand_binop (mode, xor_optab, extended, op0, target, 0,
@@ -3544,7 +3390,7 @@ expand_copysign_absneg (enum machine_mode mode, rtx op0, rtx op1, rtx target,
                        int bitpos, bool op0_is_abs)
 {
   enum machine_mode imode;
-  int icode;
+  enum insn_code icode;
   rtx sign, label;
 
   if (target == op1)
@@ -3552,16 +3398,16 @@ expand_copysign_absneg (enum machine_mode mode, rtx op0, rtx op1, rtx target,
 
   /* Check if the back end provides an insn that handles signbit for the
      argument's mode. */
-  icode = (int) signbit_optab->handlers [(int) mode].insn_code;
+  icode = optab_handler (signbit_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      imode = insn_data[icode].operand[0].mode;
+      imode = insn_data[(int) icode].operand[0].mode;
       sign = gen_reg_rtx (imode);
       emit_unop_insn (icode, sign, op1, UNKNOWN);
     }
   else
     {
-      HOST_WIDE_INT hi, lo;
+      double_int mask;
 
       if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
        {
@@ -3583,20 +3429,10 @@ expand_copysign_absneg (enum machine_mode mode, rtx op0, rtx op1, rtx target,
          op1 = operand_subword_force (op1, word, mode);
        }
 
-      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;
-       }
+      mask = double_int_setbit (double_int_zero, bitpos);
 
-      sign = gen_reg_rtx (imode);
       sign = expand_binop (imode, and_optab, op1,
-                          immed_double_const (lo, hi, imode),
+                          immed_double_int_const (mask, imode),
                           NULL_RTX, 1, OPTAB_LIB_WIDEN);
     }
 
@@ -3640,7 +3476,7 @@ 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;
+  double_int mask;
   int word, nwords, i;
   rtx temp, insns;
 
@@ -3664,18 +3500,12 @@ expand_copysign_bit (enum machine_mode mode, rtx op0, rtx op1, rtx target,
       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;
-    }
+  mask = double_int_setbit (double_int_zero, bitpos);
 
-  if (target == 0 || target == op0 || target == op1)
+  if (target == 0
+      || target == op0
+      || target == op1
+      || (nwords > 1 && !valid_multiword_target_p (target)))
     target = gen_reg_rtx (mode);
 
   if (nwords > 1)
@@ -3690,13 +3520,15 @@ expand_copysign_bit (enum machine_mode mode, rtx op0, rtx op1, rtx target,
          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);
+               op0_piece
+                 = expand_binop (imode, and_optab, op0_piece,
+                                 immed_double_int_const (double_int_not (mask),
+                                                         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),
+                                 immed_double_int_const (mask, imode),
                                  NULL_RTX, 1, OPTAB_LIB_WIDEN);
 
              temp = expand_binop (imode, ior_optab, op0_piece, op1,
@@ -3716,13 +3548,14 @@ expand_copysign_bit (enum machine_mode mode, rtx op0, rtx op1, rtx target,
   else
     {
       op1 = expand_binop (imode, and_optab, gen_lowpart (imode, op1),
-                         immed_double_const (lo, hi, imode),
+                         immed_double_int_const (mask, 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),
+                           immed_double_int_const (double_int_not (mask),
+                                                   imode),
                            NULL_RTX, 1, OPTAB_LIB_WIDEN);
 
       temp = expand_binop (imode, ior_optab, op0, op1,
@@ -3768,8 +3601,8 @@ expand_copysign (rtx op0, rtx op1, rtx target)
 
   if (fmt->signbit_ro >= 0
       && (GET_CODE (op0) == CONST_DOUBLE
-         || (optab_handler (neg_optab, mode)->insn_code != CODE_FOR_nothing
-             && optab_handler (abs_optab, mode)->insn_code != CODE_FOR_nothing)))
+         || (optab_handler (neg_optab, mode) != CODE_FOR_nothing
+             && optab_handler (abs_optab, mode) != CODE_FOR_nothing)))
     {
       temp = expand_copysign_absneg (mode, op0, op1, target,
                                     fmt->signbit_ro, op0_is_abs);
@@ -3792,37 +3625,25 @@ expand_copysign (rtx op0, rtx op1, rtx target)
    Return false if expansion failed.  */
 
 bool
-maybe_emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
+maybe_emit_unop_insn (enum insn_code icode, rtx target, rtx op0,
+                     enum rtx_code code)
 {
-  rtx temp;
-  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+  struct expand_operand ops[2];
   rtx pat;
-  rtx last = get_last_insn ();
-
-  temp = target;
-
-  /* Now, if insn does not accept our operands, put them into pseudos.  */
-
-  if (!insn_data[icode].operand[1].predicate (op0, mode0))
-    op0 = copy_to_mode_reg (mode0, op0);
 
-  if (!insn_data[icode].operand[0].predicate (temp, GET_MODE (temp)))
-    temp = gen_reg_rtx (GET_MODE (temp));
-
-  pat = GEN_FCN (icode) (temp, op0);
+  create_output_operand (&ops[0], target, GET_MODE (target));
+  create_input_operand (&ops[1], op0, GET_MODE (op0));
+  pat = maybe_gen_insn (icode, 2, ops);
   if (!pat)
-    {
-      delete_insns_since (last);
-      return false;
-    }
+    return false;
 
   if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX && code != UNKNOWN)
-    add_equal_note (pat, temp, code, op0, NULL_RTX);
+    add_equal_note (pat, ops[0].value, code, ops[1].value, NULL_RTX);
 
   emit_insn (pat);
 
-  if (temp != target)
-    emit_move_insn (target, temp);
+  if (ops[0].value != target)
+    emit_move_insn (target, ops[0].value);
   return true;
 }
 /* Generate an instruction whose insn-code is INSN_CODE,
@@ -3832,7 +3653,7 @@ maybe_emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
    the value that is stored into TARGET.  */
 
 void
-emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
+emit_unop_insn (enum insn_code icode, rtx target, rtx op0, enum rtx_code code)
 {
   bool ok = maybe_emit_unop_insn (icode, target, op0, code);
   gcc_assert (ok);
@@ -3905,7 +3726,7 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv)
   /* If we're using non-call exceptions, a libcall corresponding to an
      operation that may trap may also trap.  */
   /* ??? See the comment in front of make_reg_eh_region_note.  */
-  if (flag_non_call_exceptions && may_trap_p (equiv))
+  if (cfun->can_throw_non_call_exceptions && may_trap_p (equiv))
     {
       for (insn = insns; insn; insn = NEXT_INSN (insn))
        if (CALL_P (insn))
@@ -3981,8 +3802,7 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv)
     }
 
   last = emit_move_insn (target, result);
-  if (optab_handler (mov_optab, GET_MODE (target))->insn_code
-      != CODE_FOR_nothing)
+  if (optab_handler (mov_optab, GET_MODE (target)) != CODE_FOR_nothing)
     set_unique_reg_note (last, REG_EQUAL, copy_rtx (equiv));
 
   if (final_dest != target)
@@ -4005,18 +3825,18 @@ can_compare_p (enum rtx_code code, enum machine_mode mode,
   test = gen_rtx_fmt_ee (code, mode, const0_rtx, const0_rtx);
   do
     {
-      int icode;
+      enum insn_code icode;
 
       if (purpose == ccp_jump
-          && (icode = optab_handler (cbranch_optab, mode)->insn_code) != CODE_FOR_nothing
-          && insn_data[icode].operand[0].predicate (test, mode))
+          && (icode = optab_handler (cbranch_optab, mode)) != CODE_FOR_nothing
+          && insn_operand_matches (icode, 0, test))
         return 1;
       if (purpose == ccp_store_flag
-          && (icode = optab_handler (cstore_optab, mode)->insn_code) != CODE_FOR_nothing
-          && insn_data[icode].operand[1].predicate (test, mode))
+          && (icode = optab_handler (cstore_optab, mode)) != CODE_FOR_nothing
+          && insn_operand_matches (icode, 1, test))
         return 1;
       if (purpose == ccp_cmov
-         && optab_handler (cmov_optab, mode)->insn_code != CODE_FOR_nothing)
+         && optab_handler (cmov_optab, mode) != CODE_FOR_nothing)
        return 1;
 
       mode = GET_MODE_WIDER_MODE (mode);
@@ -4060,12 +3880,12 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size,
 
   /* If we are optimizing, force expensive constants into a register.  */
   if (CONSTANT_P (x) && optimize
-      && (rtx_cost (x, COMPARE, optimize_insn_for_speed_p ())
+      && (rtx_cost (x, COMPARE, 0, optimize_insn_for_speed_p ())
           > COSTS_N_INSNS (1)))
     x = force_reg (mode, x);
 
   if (CONSTANT_P (y) && optimize
-      && (rtx_cost (y, COMPARE, optimize_insn_for_speed_p ())
+      && (rtx_cost (y, COMPARE, 1, optimize_insn_for_speed_p ())
           > COSTS_N_INSNS (1)))
     y = force_reg (mode, y);
 
@@ -4102,11 +3922,11 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size,
           cmp_mode != VOIDmode;
           cmp_mode = GET_MODE_WIDER_MODE (cmp_mode))
        {
-         cmp_code = cmpmem_optab[cmp_mode];
+         cmp_code = direct_optab_handler (cmpmem_optab, cmp_mode);
          if (cmp_code == CODE_FOR_nothing)
-           cmp_code = cmpstr_optab[cmp_mode];
+           cmp_code = direct_optab_handler (cmpstr_optab, cmp_mode);
          if (cmp_code == CODE_FOR_nothing)
-           cmp_code = cmpstrn_optab[cmp_mode];
+           cmp_code = direct_optab_handler (cmpstrn_optab, cmp_mode);
          if (cmp_code == CODE_FOR_nothing)
            continue;
 
@@ -4151,7 +3971,7 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size,
 
   /* 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 (cfun->can_throw_non_call_exceptions)
     {
       if (may_trap_p (x))
        x = force_reg (mode, x);
@@ -4172,18 +3992,16 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size,
   do
    {
       enum insn_code icode;
-      icode = optab_handler (cbranch_optab, cmp_mode)->insn_code;
+      icode = optab_handler (cbranch_optab, cmp_mode);
       if (icode != CODE_FOR_nothing
-         && insn_data[icode].operand[0].predicate (test, VOIDmode))
+         && insn_operand_matches (icode, 0, test))
        {
          rtx last = get_last_insn ();
          rtx op0 = prepare_operand (icode, x, 1, mode, cmp_mode, unsignedp);
          rtx op1 = prepare_operand (icode, y, 2, mode, cmp_mode, unsignedp);
          if (op0 && op1
-             && insn_data[icode].operand[1].predicate
-                (op0, insn_data[icode].operand[1].mode)
-             && insn_data[icode].operand[2].predicate
-                (op1, insn_data[icode].operand[2].mode))
+             && insn_operand_matches (icode, 1, op0)
+             && insn_operand_matches (icode, 2, op1))
            {
              XEXP (test, 0) = op0;
              XEXP (test, 1) = op1;
@@ -4231,11 +4049,13 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size,
         result against 1 in the biased case, and zero in the unbiased
         case. For unsigned comparisons always compare against 1 after
         biasing the unbiased result by adding 1. This gives us a way to
-        represent LTU. */
+        represent LTU.
+        The comparisons in the fixed-point helper library are always
+        biased.  */
       x = result;
       y = const1_rtx;
 
-      if (!TARGET_LIB_INT_CMP_BIASED)
+      if (!TARGET_LIB_INT_CMP_BIASED && !ALL_FIXED_POINT_MODE_P (mode))
        {
          if (unsignedp)
            x = plus_constant (result, 1);
@@ -4262,18 +4082,17 @@ prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size,
    that it is accepted by the operand predicate.  Return the new value.  */
 
 rtx
-prepare_operand (int icode, rtx x, int opnum, enum machine_mode mode,
+prepare_operand (enum insn_code icode, rtx x, int opnum, enum machine_mode mode,
                 enum machine_mode wider_mode, int unsignedp)
 {
   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))
+  if (!insn_operand_matches (icode, opnum, x))
     {
       if (reload_completed)
        return NULL_RTX;
-      x = copy_to_mode_reg (insn_data[icode].operand[opnum].mode, x);
+      x = copy_to_mode_reg (insn_data[(int) icode].operand[opnum].mode, x);
     }
 
   return x;
@@ -4291,10 +4110,10 @@ emit_cmp_and_jump_insn_1 (rtx test, enum machine_mode mode, rtx label)
 
   mclass = GET_MODE_CLASS (mode);
   optab_mode = (mclass == MODE_CC) ? CCmode : mode;
-  icode = optab_handler (cbranch_optab, optab_mode)->insn_code;
+  icode = optab_handler (cbranch_optab, optab_mode);
 
   gcc_assert (icode != CODE_FOR_nothing);
-  gcc_assert (insn_data[icode].operand[0].predicate (test, VOIDmode));
+  gcc_assert (insn_operand_matches (icode, 0, test));
   emit_jump_insn (GEN_FCN (icode) (test, XEXP (test, 0), XEXP (test, 1), label));
 }
 
@@ -4356,6 +4175,7 @@ prepare_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison,
   enum rtx_code reversed = reverse_condition_maybe_unordered (comparison);
   enum machine_mode orig_mode = GET_MODE (x);
   enum machine_mode mode, cmp_mode;
+  rtx true_rtx, false_rtx;
   rtx value, target, insns, equiv;
   rtx libfunc = 0;
   bool reversed_p = false;
@@ -4379,8 +4199,7 @@ prepare_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison,
        }
 
       if (code_to_optab[reversed]
-         && (libfunc = optab_libfunc (code_to_optab[reversed], mode))
-         && FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, reversed))
+         && (libfunc = optab_libfunc (code_to_optab[reversed], mode)))
        {
          comparison = reversed;
          reversed_p = true;
@@ -4399,6 +4218,51 @@ prepare_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison,
   /* 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
+      || FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison))
+    {
+      true_rtx = const_true_rtx;
+      false_rtx = const0_rtx;
+    }
+  else
+    {
+      switch (comparison)
+        {
+        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:
+          gcc_unreachable ();
+        }
+    }
+
   if (comparison == UNORDERED)
     {
       rtx temp = simplify_gen_relational (NE, cmp_mode, mode, x, x);
@@ -4410,47 +4274,8 @@ prepare_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison,
     {
       equiv = simplify_gen_relational (comparison, cmp_mode, mode, x, y);
       if (! FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison))
-       {
-         rtx true_rtx, false_rtx;
-
-         switch (comparison)
-           {
-           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:
-             gcc_unreachable ();
-           }
-         equiv = simplify_gen_ternary (IF_THEN_ELSE, cmp_mode, cmp_mode,
-                                       equiv, true_rtx, false_rtx);
-       }
+        equiv = simplify_gen_ternary (IF_THEN_ELSE, cmp_mode, cmp_mode,
+                                      equiv, true_rtx, false_rtx);
     }
 
   start_sequence ();
@@ -4463,10 +4288,12 @@ prepare_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison,
   emit_libcall_block (insns, target, value, equiv);
 
   if (comparison == UNORDERED
-      || FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison))
-    comparison = reversed_p ? EQ : NE;
+      || FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison)
+      || reversed_p)
+    *ptest = gen_rtx_fmt_ee (reversed_p ? EQ : NE, VOIDmode, target, false_rtx);
+  else
+    *ptest = gen_rtx_fmt_ee (comparison, VOIDmode, target, const0_rtx);
 
-  *ptest = gen_rtx_fmt_ee (comparison, VOIDmode, target, const0_rtx);
   *pmode = cmp_mode;
 }
 \f
@@ -4475,11 +4302,10 @@ prepare_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison,
 void
 emit_indirect_jump (rtx loc)
 {
-  if (!insn_data[(int) CODE_FOR_indirect_jump].operand[0].predicate
-      (loc, Pmode))
-    loc = copy_to_mode_reg (Pmode, loc);
+  struct expand_operand ops[1];
 
-  emit_jump_insn (gen_indirect_jump (loc));
+  create_address_operand (&ops[0], loc);
+  expand_jump_insn (CODE_FOR_indirect_jump, 1, ops);
   emit_barrier ();
 }
 \f
@@ -4504,7 +4330,7 @@ emit_conditional_move (rtx target, enum rtx_code code, rtx op0, rtx op1,
                       enum machine_mode cmode, rtx op2, rtx op3,
                       enum machine_mode mode, int unsignedp)
 {
-  rtx tem, subtarget, comparison, insn;
+  rtx tem, comparison, last;
   enum insn_code icode;
   enum rtx_code reversed;
 
@@ -4543,7 +4369,7 @@ emit_conditional_move (rtx target, enum rtx_code code, rtx op0, rtx op1,
   if (mode == VOIDmode)
     mode = GET_MODE (op2);
 
-  icode = movcc_gen_code[mode];
+  icode = direct_optab_handler (movcc_optab, mode);
 
   if (icode == CODE_FOR_nothing)
     return 0;
@@ -4551,24 +4377,6 @@ emit_conditional_move (rtx target, enum rtx_code code, rtx op0, rtx op1,
   if (!target)
     target = gen_reg_rtx (mode);
 
-  subtarget = target;
-
-  /* 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))
-    subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode);
-
-  if (!insn_data[icode].operand[2].predicate
-      (op2, insn_data[icode].operand[2].mode))
-    op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2);
-
-  if (!insn_data[icode].operand[3].predicate
-      (op3, insn_data[icode].operand[3].mode))
-    op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3);
-
-  /* Everything should now be in the suitable form.  */
-
   code = unsignedp ? unsigned_condition (code) : code;
   comparison = simplify_gen_relational (code, VOIDmode, cmode, op0, op1);
 
@@ -4579,30 +4387,27 @@ emit_conditional_move (rtx target, enum rtx_code code, rtx op0, rtx op1,
     return NULL_RTX;
 
   do_pending_stack_adjust ();
-  start_sequence ();
+  last = get_last_insn ();
   prepare_cmp_insn (XEXP (comparison, 0), XEXP (comparison, 1),
                    GET_CODE (comparison), NULL_RTX, unsignedp, OPTAB_WIDEN,
                    &comparison, &cmode);
-  if (!comparison)
-    insn = NULL_RTX;
-  else
-    insn = GEN_FCN (icode) (subtarget, comparison, op2, op3);
-
-  /* If that failed, then give up.  */
-  if (insn == 0)
+  if (comparison)
     {
-      end_sequence ();
-      return 0;
-    }
+      struct expand_operand ops[4];
 
-  emit_insn (insn);
-  insn = get_insns ();
-  end_sequence ();
-  emit_insn (insn);
-  if (subtarget != target)
-    convert_move (target, subtarget, 0);
-
-  return target;
+      create_output_operand (&ops[0], target, mode);
+      create_fixed_operand (&ops[1], comparison);
+      create_input_operand (&ops[2], op2, mode);
+      create_input_operand (&ops[3], op3, mode);
+      if (maybe_expand_insn (icode, 4, ops))
+       {
+         if (ops[0].value != target)
+           convert_move (target, ops[0].value, false);
+         return target;
+       }
+    }
+  delete_insns_since (last);
+  return NULL_RTX;
 }
 
 /* Return nonzero if a conditional move of mode MODE is supported.
@@ -4616,7 +4421,7 @@ emit_conditional_move (rtx target, enum rtx_code code, rtx op0, rtx op1,
 int
 can_conditionally_move_p (enum machine_mode mode)
 {
-  if (movcc_gen_code[mode] != CODE_FOR_nothing)
+  if (direct_optab_handler (movcc_optab, mode) != CODE_FOR_nothing)
     return 1;
 
   return 0;
@@ -4643,7 +4448,7 @@ emit_conditional_add (rtx target, enum rtx_code code, rtx op0, rtx op1,
                      enum machine_mode cmode, rtx op2, rtx op3,
                      enum machine_mode mode, int unsignedp)
 {
-  rtx tem, subtarget, comparison, insn;
+  rtx tem, comparison, last;
   enum insn_code icode;
   enum rtx_code reversed;
 
@@ -4682,7 +4487,7 @@ emit_conditional_add (rtx target, enum rtx_code code, rtx op0, rtx op1,
   if (mode == VOIDmode)
     mode = GET_MODE (op2);
 
-  icode = optab_handler (addcc_optab, mode)->insn_code;
+  icode = optab_handler (addcc_optab, mode);
 
   if (icode == CODE_FOR_nothing)
     return 0;
@@ -4690,24 +4495,6 @@ emit_conditional_add (rtx target, enum rtx_code code, rtx op0, rtx op1,
   if (!target)
     target = gen_reg_rtx (mode);
 
-  /* If the insn doesn't accept these operands, put them in pseudos.  */
-
-  if (!insn_data[icode].operand[0].predicate
-      (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))
-    op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2);
-
-  if (!insn_data[icode].operand[3].predicate
-      (op3, insn_data[icode].operand[3].mode))
-    op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3);
-
-  /* Everything should now be in the suitable form.  */
-
   code = unsignedp ? unsigned_condition (code) : code;
   comparison = simplify_gen_relational (code, VOIDmode, cmode, op0, op1);
 
@@ -4718,30 +4505,27 @@ emit_conditional_add (rtx target, enum rtx_code code, rtx op0, rtx op1,
     return NULL_RTX;
 
   do_pending_stack_adjust ();
-  start_sequence ();
+  last = get_last_insn ();
   prepare_cmp_insn (XEXP (comparison, 0), XEXP (comparison, 1),
                     GET_CODE (comparison), NULL_RTX, unsignedp, OPTAB_WIDEN,
                     &comparison, &cmode);
-  if (!comparison)
-    insn = NULL_RTX;
-  else
-    insn = GEN_FCN (icode) (subtarget, comparison, op2, op3);
-
-  /* If that failed, then give up.  */
-  if (insn == 0)
+  if (comparison)
     {
-      end_sequence ();
-      return 0;
-    }
-
-  emit_insn (insn);
-  insn = get_insns ();
-  end_sequence ();
-  emit_insn (insn);
-  if (subtarget != target)
-    convert_move (target, subtarget, 0);
+      struct expand_operand ops[4];
 
-  return target;
+      create_output_operand (&ops[0], target, mode);
+      create_fixed_operand (&ops[1], comparison);
+      create_input_operand (&ops[2], op2, mode);
+      create_input_operand (&ops[3], op3, mode);
+      if (maybe_expand_insn (icode, 4, ops))
+       {
+         if (ops[0].value != target)
+           convert_move (target, ops[0].value, false);
+         return target;
+       }
+    }
+  delete_insns_since (last);
+  return NULL_RTX;
 }
 \f
 /* These functions attempt to generate an insn body, rather than
@@ -4753,14 +4537,11 @@ emit_conditional_add (rtx target, enum rtx_code code, rtx op0, rtx op1,
 rtx
 gen_add2_insn (rtx x, rtx y)
 {
-  int icode = (int) optab_handler (add_optab, GET_MODE (x))->insn_code;
+  enum insn_code icode = optab_handler (add_optab, GET_MODE (x));
 
-  gcc_assert (insn_data[icode].operand[0].predicate
-             (x, insn_data[icode].operand[0].mode));
-  gcc_assert (insn_data[icode].operand[1].predicate
-             (x, insn_data[icode].operand[1].mode));
-  gcc_assert (insn_data[icode].operand[2].predicate
-             (y, insn_data[icode].operand[2].mode));
+  gcc_assert (insn_operand_matches (icode, 0, x));
+  gcc_assert (insn_operand_matches (icode, 1, x));
+  gcc_assert (insn_operand_matches (icode, 2, y));
 
   return GEN_FCN (icode) (x, x, y);
 }
@@ -4771,15 +4552,12 @@ gen_add2_insn (rtx x, rtx y)
 rtx
 gen_add3_insn (rtx r0, rtx r1, rtx c)
 {
-  int icode = (int) optab_handler (add_optab, GET_MODE (r0))->insn_code;
+  enum insn_code icode = optab_handler (add_optab, GET_MODE (r0));
 
   if (icode == CODE_FOR_nothing
-      || !(insn_data[icode].operand[0].predicate
-          (r0, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-          (r1, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-          (c, insn_data[icode].operand[2].mode)))
+      || !insn_operand_matches (icode, 0, r0)
+      || !insn_operand_matches (icode, 1, r1)
+      || !insn_operand_matches (icode, 2, c))
     return NULL_RTX;
 
   return GEN_FCN (icode) (r0, r1, c);
@@ -4788,21 +4566,18 @@ gen_add3_insn (rtx r0, rtx r1, rtx c)
 int
 have_add2_insn (rtx x, rtx y)
 {
-  int icode;
+  enum insn_code icode;
 
   gcc_assert (GET_MODE (x) != VOIDmode);
 
-  icode = (int) optab_handler (add_optab, GET_MODE (x))->insn_code;
+  icode = optab_handler (add_optab, GET_MODE (x));
 
   if (icode == CODE_FOR_nothing)
     return 0;
 
-  if (!(insn_data[icode].operand[0].predicate
-       (x, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-          (x, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-          (y, insn_data[icode].operand[2].mode)))
+  if (!insn_operand_matches (icode, 0, x)
+      || !insn_operand_matches (icode, 1, x)
+      || !insn_operand_matches (icode, 2, y))
     return 0;
 
   return 1;
@@ -4813,14 +4588,11 @@ have_add2_insn (rtx x, rtx y)
 rtx
 gen_sub2_insn (rtx x, rtx y)
 {
-  int icode = (int) optab_handler (sub_optab, GET_MODE (x))->insn_code;
+  enum insn_code icode = optab_handler (sub_optab, GET_MODE (x));
 
-  gcc_assert (insn_data[icode].operand[0].predicate
-             (x, insn_data[icode].operand[0].mode));
-  gcc_assert (insn_data[icode].operand[1].predicate
-             (x, insn_data[icode].operand[1].mode));
-  gcc_assert  (insn_data[icode].operand[2].predicate
-              (y, insn_data[icode].operand[2].mode));
+  gcc_assert (insn_operand_matches (icode, 0, x));
+  gcc_assert (insn_operand_matches (icode, 1, x));
+  gcc_assert (insn_operand_matches (icode, 2, y));
 
   return GEN_FCN (icode) (x, x, y);
 }
@@ -4831,15 +4603,12 @@ gen_sub2_insn (rtx x, rtx y)
 rtx
 gen_sub3_insn (rtx r0, rtx r1, rtx c)
 {
-  int icode = (int) optab_handler (sub_optab, GET_MODE (r0))->insn_code;
+  enum insn_code icode = optab_handler (sub_optab, GET_MODE (r0));
 
   if (icode == CODE_FOR_nothing
-      || !(insn_data[icode].operand[0].predicate
-          (r0, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-          (r1, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-          (c, insn_data[icode].operand[2].mode)))
+      || !insn_operand_matches (icode, 0, r0)
+      || !insn_operand_matches (icode, 1, r1)
+      || !insn_operand_matches (icode, 2, c))
     return NULL_RTX;
 
   return GEN_FCN (icode) (r0, r1, c);
@@ -4848,21 +4617,18 @@ gen_sub3_insn (rtx r0, rtx r1, rtx c)
 int
 have_sub2_insn (rtx x, rtx y)
 {
-  int icode;
+  enum insn_code icode;
 
   gcc_assert (GET_MODE (x) != VOIDmode);
 
-  icode = (int) optab_handler (sub_optab, GET_MODE (x))->insn_code;
+  icode = optab_handler (sub_optab, GET_MODE (x));
 
   if (icode == CODE_FOR_nothing)
     return 0;
 
-  if (!(insn_data[icode].operand[0].predicate
-       (x, insn_data[icode].operand[0].mode))
-      || !(insn_data[icode].operand[1].predicate
-          (x, insn_data[icode].operand[1].mode))
-      || !(insn_data[icode].operand[2].predicate
-          (y, insn_data[icode].operand[2].mode)))
+  if (!insn_operand_matches (icode, 0, x)
+      || !insn_operand_matches (icode, 1, x)
+      || !insn_operand_matches (icode, 2, y))
     return 0;
 
   return 1;
@@ -4898,7 +4664,7 @@ can_extend_p (enum machine_mode to_mode, enum machine_mode from_mode,
 #endif
 
   tab = unsignedp ? zext_optab : sext_optab;
-  return convert_optab_handler (tab, to_mode, from_mode)->insn_code;
+  return convert_optab_handler (tab, to_mode, from_mode);
 }
 
 /* Generate the body of an insn to extend Y (with mode MFROM)
@@ -4929,7 +4695,7 @@ can_fix_p (enum machine_mode fixmode, enum machine_mode fltmode,
   enum insn_code icode;
 
   tab = unsignedp ? ufixtrunc_optab : sfixtrunc_optab;
-  icode = convert_optab_handler (tab, fixmode, fltmode)->insn_code;
+  icode = convert_optab_handler (tab, fixmode, fltmode);
   if (icode != CODE_FOR_nothing)
     {
       *truncp_ptr = 0;
@@ -4940,9 +4706,9 @@ can_fix_p (enum machine_mode fixmode, enum machine_mode fltmode,
      for this to work. We need to rework the fix* and ftrunc* patterns
      and documentation.  */
   tab = unsignedp ? ufix_optab : sfix_optab;
-  icode = convert_optab_handler (tab, fixmode, fltmode)->insn_code;
+  icode = convert_optab_handler (tab, fixmode, fltmode);
   if (icode != CODE_FOR_nothing
-      && optab_handler (ftrunc_optab, fltmode)->insn_code != CODE_FOR_nothing)
+      && optab_handler (ftrunc_optab, fltmode) != CODE_FOR_nothing)
     {
       *truncp_ptr = 1;
       return icode;
@@ -4952,14 +4718,14 @@ can_fix_p (enum machine_mode fixmode, enum machine_mode fltmode,
   return CODE_FOR_nothing;
 }
 
-static enum insn_code
+enum insn_code
 can_float_p (enum machine_mode fltmode, enum machine_mode fixmode,
             int unsignedp)
 {
   convert_optab tab;
 
   tab = unsignedp ? ufloat_optab : sfloat_optab;
-  return convert_optab_handler (tab, fltmode, fixmode)->insn_code;
+  return convert_optab_handler (tab, fltmode, fixmode);
 }
 \f
 /* Generate code to convert FROM to floating point
@@ -4992,7 +4758,7 @@ expand_float (rtx to, rtx from, int unsignedp)
        int doing_unsigned = unsignedp;
 
        if (fmode != GET_MODE (to)
-           && significand_size (fmode) < GET_MODE_BITSIZE (GET_MODE (from)))
+           && significand_size (fmode) < GET_MODE_PRECISION (GET_MODE (from)))
          continue;
 
        icode = can_float_p (fmode, imode, unsignedp);
@@ -5036,7 +4802,7 @@ expand_float (rtx to, rtx from, int unsignedp)
 
       for (fmode = GET_MODE (to);  fmode != VOIDmode;
           fmode = GET_MODE_WIDER_MODE (fmode))
-       if (GET_MODE_BITSIZE (GET_MODE (from)) < GET_MODE_BITSIZE (fmode)
+       if (GET_MODE_PRECISION (GET_MODE (from)) < GET_MODE_BITSIZE (fmode)
            && can_float_p (fmode, GET_MODE (from), 0) != CODE_FOR_nothing)
          break;
 
@@ -5047,7 +4813,7 @@ expand_float (rtx to, rtx from, int unsignedp)
 
          /* Avoid double-rounding when TO is narrower than FROM.  */
          if ((significand_size (fmode) + 1)
-             < GET_MODE_BITSIZE (GET_MODE (from)))
+             < GET_MODE_PRECISION (GET_MODE (from)))
            {
              rtx temp1;
              rtx neglabel = gen_label_rtx ();
@@ -5079,8 +4845,7 @@ expand_float (rtx to, rtx from, int unsignedp)
              emit_label (neglabel);
              temp = expand_binop (imode, and_optab, from, const1_rtx,
                                   NULL_RTX, 1, OPTAB_LIB_WIDEN);
-             temp1 = expand_shift (RSHIFT_EXPR, imode, from, integer_one_node,
-                                   NULL_RTX, 1);
+             temp1 = expand_shift (RSHIFT_EXPR, imode, from, 1, NULL_RTX, 1);
              temp = expand_binop (imode, ior_optab, temp, temp1, temp, 1,
                                   OPTAB_LIB_WIDEN);
              expand_float (target, temp, 0);
@@ -5115,7 +4880,7 @@ expand_float (rtx to, rtx from, int unsignedp)
                               0, label);
 
 
-      real_2expN (&offset, GET_MODE_BITSIZE (GET_MODE (from)), fmode);
+      real_2expN (&offset, GET_MODE_PRECISION (GET_MODE (from)), fmode);
       temp = expand_binop (fmode, add_optab, target,
                           CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode),
                           target, 0, OPTAB_LIB_WIDEN);
@@ -5245,18 +5010,18 @@ expand_fix (rtx to, rtx from, int unsignedp)
      2^63.  The subtraction of 2^63 should not generate any rounding as it
      simply clears out that bit.  The rest is trivial.  */
 
-  if (unsignedp && GET_MODE_BITSIZE (GET_MODE (to)) <= HOST_BITS_PER_WIDE_INT)
+  if (unsignedp && GET_MODE_PRECISION (GET_MODE (to)) <= HOST_BITS_PER_WIDE_INT)
     for (fmode = GET_MODE (from); fmode != VOIDmode;
         fmode = GET_MODE_WIDER_MODE (fmode))
       if (CODE_FOR_nothing != can_fix_p (GET_MODE (to), fmode, 0, &must_trunc)
          && (!DECIMAL_FLOAT_MODE_P (fmode)
-             || GET_MODE_BITSIZE (fmode) > GET_MODE_BITSIZE (GET_MODE (to))))
+             || GET_MODE_BITSIZE (fmode) > GET_MODE_PRECISION (GET_MODE (to))))
        {
          int bitsize;
          REAL_VALUE_TYPE offset;
          rtx limit, lab1, lab2, insn;
 
-         bitsize = GET_MODE_BITSIZE (GET_MODE (to));
+         bitsize = GET_MODE_PRECISION (GET_MODE (to));
          real_2expN (&offset, bitsize - 1, fmode);
          limit = CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode);
          lab1 = gen_label_rtx ();
@@ -5293,8 +5058,7 @@ expand_fix (rtx to, rtx from, int unsignedp)
 
          emit_label (lab2);
 
-         if (optab_handler (mov_optab, GET_MODE (to))->insn_code
-             != CODE_FOR_nothing)
+         if (optab_handler (mov_optab, GET_MODE (to)) != CODE_FOR_nothing)
            {
              /* Make a place for a REG_NOTE and add it.  */
              insn = emit_move_insn (to, to);
@@ -5381,7 +5145,7 @@ expand_fixed_convert (rtx to, rtx from, int uintp, int satp)
       tab = satp ? satfract_optab : fract_optab;
       this_code = satp ? SAT_FRACT : FRACT_CONVERT;
     }
-  code = tab->handlers[to_mode][from_mode].insn_code;
+  code = convert_optab_handler (tab, to_mode, from_mode);
   if (code != CODE_FOR_nothing)
     {
       emit_unop_insn (code, to, from, this_code);
@@ -5422,7 +5186,7 @@ expand_sfix_optab (rtx to, rtx from, convert_optab tab)
     for (imode = GET_MODE (to); imode != VOIDmode;
         imode = GET_MODE_WIDER_MODE (imode))
       {
-       icode = convert_optab_handler (tab, imode, fmode)->insn_code;
+       icode = convert_optab_handler (tab, imode, fmode);
        if (icode != CODE_FOR_nothing)
          {
            rtx last = get_last_insn ();
@@ -5452,7 +5216,7 @@ int
 have_insn_for (enum rtx_code code, enum machine_mode mode)
 {
   return (code_to_optab[(int) code] != 0
-         && (optab_handler (code_to_optab[(int) code], mode)->insn_code
+         && (optab_handler (code_to_optab[(int) code], mode)
              != CODE_FOR_nothing));
 }
 
@@ -5461,27 +5225,9 @@ have_insn_for (enum rtx_code code, enum machine_mode mode)
 static void
 init_insn_codes (void)
 {
-  unsigned int i;
-
-  for (i = 0; i < (unsigned int) OTI_MAX; i++)
-    {
-      unsigned int j;
-      optab op;
-
-      op = &optab_table[i];
-      for (j = 0; j < NUM_MACHINE_MODES; j++)
-       optab_handler (op, j)->insn_code = CODE_FOR_nothing;
-    }
-  for (i = 0; i < (unsigned int) COI_MAX; i++)
-    {
-      unsigned int j, k;
-      convert_optab op;
-
-      op = &convert_optab_table[i];
-      for (j = 0; j < NUM_MACHINE_MODES; j++)
-       for (k = 0; k < NUM_MACHINE_MODES; k++)
-         convert_optab_handler (op, j, k)->insn_code = CODE_FOR_nothing;
-    }
+  memset (optab_table, 0, sizeof (optab_table));
+  memset (convert_optab_table, 0, sizeof (convert_optab_table));
+  memset (direct_optab_table, 0, sizeof (direct_optab_table));
 }
 
 /* Initialize OP's code to CODE, and write it into the code_to_optab table.  */
@@ -5527,13 +5273,22 @@ gen_libfunc (optab optable, const char *opname, int suffix, enum machine_mode mo
   unsigned opname_len = strlen (opname);
   const char *mname = GET_MODE_NAME (mode);
   unsigned mname_len = strlen (mname);
-  char *libfunc_name = XALLOCAVEC (char, 2 + opname_len + mname_len + 1 + 1);
+  int prefix_len = targetm.libfunc_gnu_prefix ? 6 : 2;
+  int len = prefix_len + opname_len + mname_len + 1 + 1;
+  char *libfunc_name = XALLOCAVEC (char, len);
   char *p;
   const char *q;
 
   p = libfunc_name;
   *p++ = '_';
   *p++ = '_';
+  if (targetm.libfunc_gnu_prefix)
+    {
+      *p++ = 'g';
+      *p++ = 'n';
+      *p++ = 'u';
+      *p++ = '_';
+    }
   for (q = opname; *q; )
     *p++ = *q++;
   for (q = mname; *q; q++)
@@ -5737,6 +5492,7 @@ gen_interclass_conv_libfunc (convert_optab tab,
 
   const char *fname, *tname;
   const char *q;
+  int prefix_len = targetm.libfunc_gnu_prefix ? 6 : 2;
   char *libfunc_name, *suffix;
   char *nondec_name, *dec_name, *nondec_suffix, *dec_suffix;
   char *p;
@@ -5747,11 +5503,19 @@ gen_interclass_conv_libfunc (convert_optab tab,
 
   mname_len = strlen (GET_MODE_NAME (tmode)) + strlen (GET_MODE_NAME (fmode));
 
-  nondec_name = XALLOCAVEC (char, 2 + opname_len + mname_len + 1 + 1);
+  nondec_name = XALLOCAVEC (char, prefix_len + opname_len + mname_len + 1 + 1);
   nondec_name[0] = '_';
   nondec_name[1] = '_';
-  memcpy (&nondec_name[2], opname, opname_len);
-  nondec_suffix = nondec_name + opname_len + 2;
+  if (targetm.libfunc_gnu_prefix)
+    {
+      nondec_name[2] = 'g';
+      nondec_name[3] = 'n';
+      nondec_name[4] = 'u';
+      nondec_name[5] = '_';
+    }
+
+  memcpy (&nondec_name[prefix_len], opname, opname_len);
+  nondec_suffix = nondec_name + opname_len + prefix_len;
 
   dec_name = XALLOCAVEC (char, 2 + dec_len + opname_len + mname_len + 1 + 1);
   dec_name[0] = '_';
@@ -5862,6 +5626,7 @@ gen_intraclass_conv_libfunc (convert_optab tab, const char *opname,
 
   const char *fname, *tname;
   const char *q;
+  int prefix_len = targetm.libfunc_gnu_prefix ? 6 : 2;
   char *nondec_name, *dec_name, *nondec_suffix, *dec_suffix;
   char *libfunc_name, *suffix;
   char *p;
@@ -5875,8 +5640,15 @@ gen_intraclass_conv_libfunc (convert_optab tab, const char *opname,
   nondec_name = XALLOCAVEC (char, 2 + opname_len + mname_len + 1 + 1);
   nondec_name[0] = '_';
   nondec_name[1] = '_';
-  memcpy (&nondec_name[2], opname, opname_len);
-  nondec_suffix = nondec_name + opname_len + 2;
+  if (targetm.libfunc_gnu_prefix)
+    {
+      nondec_name[2] = 'g';
+      nondec_name[3] = 'n';
+      nondec_name[4] = 'u';
+      nondec_name[5] = '_';
+    }
+  memcpy (&nondec_name[prefix_len], opname, opname_len);
+  nondec_suffix = nondec_name + opname_len + prefix_len;
 
   dec_name = XALLOCAVEC (char, 2 + dec_len + opname_len + mname_len + 1 + 1);
   dec_name[0] = '_';
@@ -6056,7 +5828,7 @@ static GTY ((param_is (union tree_node))) htab_t libfunc_decls;
 static hashval_t
 libfunc_decl_hash (const void *entry)
 {
-  return htab_hash_string (IDENTIFIER_POINTER (DECL_NAME ((const_tree) entry)));
+  return IDENTIFIER_HASH_VALUE (DECL_NAME ((const_tree) entry));
 }
 
 static int
@@ -6100,7 +5872,7 @@ init_one_libfunc (const char *name)
 
   /* See if we have already created a libfunc decl for this function.  */
   id = get_identifier (name);
-  hash = htab_hash_string (name);
+  hash = IDENTIFIER_HASH_VALUE (id);
   slot = htab_find_slot_with_hash (libfunc_decls, id, hash, INSERT);
   decl = (tree) *slot;
   if (decl == NULL)
@@ -6123,7 +5895,7 @@ set_user_assembler_libfunc (const char *name, const char *asmspec)
   hashval_t hash;
 
   id = get_identifier (name);
-  hash = htab_hash_string (name);
+  hash = IDENTIFIER_HASH_VALUE (id);
   slot = htab_find_slot_with_hash (libfunc_decls, id, hash, NO_INSERT);
   gcc_assert (slot);
   decl = (tree) *slot;
@@ -6149,7 +5921,7 @@ set_optab_libfunc (optab optable, enum machine_mode mode, const char *name)
     val = 0;
   slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash, &e, INSERT);
   if (*slot == NULL)
-    *slot = GGC_NEW (struct libfunc_entry);
+    *slot = ggc_alloc_libfunc_entry ();
   (*slot)->optab = (size_t) (optable - &optab_table[0]);
   (*slot)->mode1 = mode;
   (*slot)->mode2 = VOIDmode;
@@ -6176,7 +5948,7 @@ set_conv_libfunc (convert_optab optable, enum machine_mode tmode,
     val = 0;
   slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash, &e, INSERT);
   if (*slot == NULL)
-    *slot = GGC_NEW (struct libfunc_entry);
+    *slot = ggc_alloc_libfunc_entry ();
   (*slot)->optab = (size_t) (optable - &convert_optab_table[0]);
   (*slot)->mode1 = tmode;
   (*slot)->mode2 = fmode;
@@ -6189,30 +5961,15 @@ set_conv_libfunc (convert_optab optable, enum machine_mode tmode,
 void
 init_optabs (void)
 {
-  unsigned int i;
-  static bool reinit;
-
-  libfunc_hash = htab_create_ggc (10, hash_libfunc, eq_libfunc, NULL);
-  /* Start by initializing all tables to contain CODE_FOR_nothing.  */
-
-#ifdef HAVE_conditional_move
-  for (i = 0; i < NUM_MACHINE_MODES; i++)
-    movcc_gen_code[i] = CODE_FOR_nothing;
-#endif
-
-  for (i = 0; i < NUM_MACHINE_MODES; i++)
+  if (libfunc_hash)
     {
-      vcond_gen_code[i] = CODE_FOR_nothing;
-      vcondu_gen_code[i] = CODE_FOR_nothing;
+      htab_empty (libfunc_hash);
+      /* We statically initialize the insn_codes with the equivalent of
+        CODE_FOR_nothing.  Repeat the process if reinitialising.  */
+      init_insn_codes ();
     }
-
-#if GCC_VERSION >= 4000 && HAVE_DESIGNATED_INITIALIZERS
-  /* We statically initialize the insn_codes with CODE_FOR_nothing.  */
-  if (reinit)
-    init_insn_codes ();
-#else
-  init_insn_codes ();
-#endif
+  else
+    libfunc_hash = htab_create_ggc (10, hash_libfunc, eq_libfunc, NULL);
 
   init_optab (add_optab, PLUS);
   init_optabv (addv_optab, PLUS);
@@ -6259,6 +6016,9 @@ init_optabs (void)
   init_optab (usashl_optab, US_ASHIFT);
   init_optab (ashr_optab, ASHIFTRT);
   init_optab (lshr_optab, LSHIFTRT);
+  init_optabv (vashl_optab, ASHIFT);
+  init_optabv (vashr_optab, ASHIFTRT);
+  init_optabv (vlshr_optab, LSHIFTRT);
   init_optab (rotl_optab, ROTATE);
   init_optab (rotr_optab, ROTATERT);
   init_optab (smin_optab, SMIN);
@@ -6267,6 +6027,10 @@ init_optabs (void)
   init_optab (umax_optab, UMAX);
   init_optab (pow_optab, UNKNOWN);
   init_optab (atan2_optab, UNKNOWN);
+  init_optab (fma_optab, FMA);
+  init_optab (fms_optab, UNKNOWN);
+  init_optab (fnma_optab, UNKNOWN);
+  init_optab (fnms_optab, UNKNOWN);
 
   /* These three have codes assigned exclusively for the sake of
      have_insn_for.  */
@@ -6303,6 +6067,7 @@ init_optabs (void)
   init_optab (ffs_optab, FFS);
   init_optab (clz_optab, CLZ);
   init_optab (ctz_optab, CTZ);
+  init_optab (clrsb_optab, CLRSB);
   init_optab (popcount_optab, POPCOUNT);
   init_optab (parity_optab, PARITY);
   init_optab (sqrt_optab, SQRT);
@@ -6403,39 +6168,6 @@ init_optabs (void)
   init_convert_optab (satfract_optab, SAT_FRACT);
   init_convert_optab (satfractuns_optab, UNSIGNED_SAT_FRACT);
 
-  for (i = 0; i < NUM_MACHINE_MODES; i++)
-    {
-      movmem_optab[i] = CODE_FOR_nothing;
-      cmpstr_optab[i] = CODE_FOR_nothing;
-      cmpstrn_optab[i] = CODE_FOR_nothing;
-      cmpmem_optab[i] = CODE_FOR_nothing;
-      setmem_optab[i] = CODE_FOR_nothing;
-
-      sync_add_optab[i] = CODE_FOR_nothing;
-      sync_sub_optab[i] = CODE_FOR_nothing;
-      sync_ior_optab[i] = CODE_FOR_nothing;
-      sync_and_optab[i] = CODE_FOR_nothing;
-      sync_xor_optab[i] = CODE_FOR_nothing;
-      sync_nand_optab[i] = CODE_FOR_nothing;
-      sync_old_add_optab[i] = CODE_FOR_nothing;
-      sync_old_sub_optab[i] = CODE_FOR_nothing;
-      sync_old_ior_optab[i] = CODE_FOR_nothing;
-      sync_old_and_optab[i] = CODE_FOR_nothing;
-      sync_old_xor_optab[i] = CODE_FOR_nothing;
-      sync_old_nand_optab[i] = CODE_FOR_nothing;
-      sync_new_add_optab[i] = CODE_FOR_nothing;
-      sync_new_sub_optab[i] = CODE_FOR_nothing;
-      sync_new_ior_optab[i] = CODE_FOR_nothing;
-      sync_new_and_optab[i] = CODE_FOR_nothing;
-      sync_new_xor_optab[i] = CODE_FOR_nothing;
-      sync_new_nand_optab[i] = CODE_FOR_nothing;
-      sync_compare_and_swap[i] = CODE_FOR_nothing;
-      sync_lock_test_and_set[i] = CODE_FOR_nothing;
-      sync_lock_release[i] = CODE_FOR_nothing;
-
-      reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing;
-    }
-
   /* Fill in the optabs with the insns we support.  */
   init_all_optabs ();
 
@@ -6566,6 +6298,9 @@ init_optabs (void)
   ctz_optab->libcall_basename = "ctz";
   ctz_optab->libcall_suffix = '2';
   ctz_optab->libcall_gen = gen_int_libfunc;
+  clrsb_optab->libcall_basename = "clrsb";
+  clrsb_optab->libcall_suffix = '2';
+  clrsb_optab->libcall_gen = gen_int_libfunc;
   popcount_optab->libcall_basename = "popcount";
   popcount_optab->libcall_suffix = '2';
   popcount_optab->libcall_gen = gen_int_libfunc;
@@ -6650,8 +6385,16 @@ init_optabs (void)
 
   /* Explicitly initialize the bswap libfuncs since we need them to be
      valid for things other than word_mode.  */
-  set_optab_libfunc (bswap_optab, SImode, "__bswapsi2");
-  set_optab_libfunc (bswap_optab, DImode, "__bswapdi2");
+  if (targetm.libfunc_gnu_prefix)
+    {
+      set_optab_libfunc (bswap_optab, SImode, "__gnu_bswapsi2");
+      set_optab_libfunc (bswap_optab, DImode, "__gnu_bswapdi2");
+    }
+  else
+    {
+      set_optab_libfunc (bswap_optab, SImode, "__bswapsi2");
+      set_optab_libfunc (bswap_optab, DImode, "__bswapdi2");
+    }
 
   /* 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.  */
@@ -6686,14 +6429,12 @@ init_optabs (void)
 
   /* Allow the target to add more libcalls or rename some, etc.  */
   targetm.init_libfuncs ();
-
-  reinit = true;
 }
 
 /* Print information about the current contents of the optabs on
    STDERR.  */
 
-void
+DEBUG_FUNCTION void
 debug_optab_libfuncs (void)
 {
   int i;
@@ -6757,13 +6498,12 @@ gen_cond_trap (enum rtx_code code, rtx op1, rtx op2, rtx tcode)
   if (mode == VOIDmode)
     return 0;
 
-  icode = optab_handler (ctrap_optab, mode)->insn_code;
+  icode = optab_handler (ctrap_optab, mode);
   if (icode == CODE_FOR_nothing)
     return 0;
 
   /* Some targets only accept a zero trap code.  */
-  if (insn_data[icode].operand[3].predicate
-      && !insn_data[icode].operand[3].predicate (tcode, VOIDmode))
+  if (!insn_operand_matches (icode, 3, tcode))
     return 0;
 
   do_pending_stack_adjust ();
@@ -6854,6 +6594,7 @@ get_rtx_code (enum tree_code tcode, bool unsignedp)
 static rtx
 vector_compare_rtx (tree cond, bool unsignedp, enum insn_code icode)
 {
+  struct expand_operand ops[2];
   enum rtx_code rcode;
   tree t_op0, t_op1;
   rtx rtx_op0, rtx_op1;
@@ -6872,38 +6613,40 @@ vector_compare_rtx (tree cond, bool unsignedp, enum insn_code icode)
   rtx_op1 = expand_expr (t_op1, NULL_RTX, TYPE_MODE (TREE_TYPE (t_op1)),
                         EXPAND_STACK_PARM);
 
-  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);
+  create_input_operand (&ops[0], rtx_op0, GET_MODE (rtx_op0));
+  create_input_operand (&ops[1], rtx_op1, GET_MODE (rtx_op1));
+  if (!maybe_legitimize_operands (icode, 4, 2, ops))
+    gcc_unreachable ();
+  return gen_rtx_fmt_ee (rcode, VOIDmode, ops[0].value, ops[1].value);
 }
 
-/* Return insn code for TYPE, the type of a VEC_COND_EXPR.  */
+/* Return insn code for a conditional operator with a comparison in
+   mode CMODE, unsigned if UNS is true, resulting in a value of mode VMODE.  */
 
 static inline enum insn_code
-get_vcond_icode (tree type, enum machine_mode mode)
+get_vcond_icode (enum machine_mode vmode, enum machine_mode cmode, bool uns)
 {
   enum insn_code icode = CODE_FOR_nothing;
-
-  if (TYPE_UNSIGNED (type))
-    icode = vcondu_gen_code[mode];
+  if (uns)
+    icode = convert_optab_handler (vcondu_optab, vmode, cmode);
   else
-    icode = vcond_gen_code[mode];
+    icode = convert_optab_handler (vcond_optab, vmode, cmode);
   return icode;
 }
 
 /* Return TRUE iff, appropriate vector insns are available
-   for vector cond expr with type TYPE in VMODE mode.  */
+   for vector cond expr with vector type VALUE_TYPE and a comparison
+   with operand vector types in CMP_OP_TYPE.  */
 
 bool
-expand_vec_cond_expr_p (tree type, enum machine_mode vmode)
-{
-  if (get_vcond_icode (type, vmode) == CODE_FOR_nothing)
+expand_vec_cond_expr_p (tree value_type, tree cmp_op_type)
+{
+  enum machine_mode value_mode = TYPE_MODE (value_type);
+  enum machine_mode cmp_op_mode = TYPE_MODE (cmp_op_type);
+  if (GET_MODE_SIZE (value_mode) != GET_MODE_SIZE (cmp_op_mode)
+      || GET_MODE_NUNITS (value_mode) != GET_MODE_NUNITS (cmp_op_mode)
+      || get_vcond_icode (TYPE_MODE (value_type), TYPE_MODE (cmp_op_type),
+                         TYPE_UNSIGNED (cmp_op_type)) == CODE_FOR_nothing)
     return false;
   return true;
 }
@@ -6915,39 +6658,37 @@ rtx
 expand_vec_cond_expr (tree vec_cond_type, tree op0, tree op1, tree op2,
                      rtx target)
 {
+  struct expand_operand ops[6];
   enum insn_code icode;
-  rtx comparison, rtx_op1, rtx_op2, cc_op0, cc_op1;
+  rtx comparison, rtx_op1, rtx_op2;
   enum machine_mode mode = TYPE_MODE (vec_cond_type);
-  bool unsignedp = TYPE_UNSIGNED (vec_cond_type);
+  enum machine_mode cmp_op_mode;
+  bool unsignedp;
+
+  gcc_assert (COMPARISON_CLASS_P (op0));
 
-  icode = get_vcond_icode (vec_cond_type, mode);
+  unsignedp = TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op0, 0)));
+  cmp_op_mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (op0, 0)));
+
+  gcc_assert (GET_MODE_SIZE (mode) == GET_MODE_SIZE (cmp_op_mode)
+             && GET_MODE_NUNITS (mode) == GET_MODE_NUNITS (cmp_op_mode));
+
+  icode = get_vcond_icode (mode, cmp_op_mode, unsignedp);
   if (icode == CODE_FOR_nothing)
     return 0;
 
-  if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-    target = gen_reg_rtx (mode);
-
-  /* Get comparison rtx.  First expand both cond expr operands.  */
-  comparison = vector_compare_rtx (op0,
-                                  unsignedp, icode);
-  cc_op0 = XEXP (comparison, 0);
-  cc_op1 = XEXP (comparison, 1);
-  /* Expand both operands and force them in reg, if required.  */
+  comparison = vector_compare_rtx (op0, unsignedp, icode);
   rtx_op1 = expand_normal (op1);
-  if (!insn_data[icode].operand[1].predicate (rtx_op1, mode)
-      && mode != VOIDmode)
-    rtx_op1 = force_reg (mode, rtx_op1);
-
   rtx_op2 = expand_normal (op2);
-  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;
+  create_output_operand (&ops[0], target, mode);
+  create_input_operand (&ops[1], rtx_op1, mode);
+  create_input_operand (&ops[2], rtx_op2, mode);
+  create_fixed_operand (&ops[3], comparison);
+  create_fixed_operand (&ops[4], XEXP (comparison, 0));
+  create_fixed_operand (&ops[5], XEXP (comparison, 1));
+  expand_insn (icode, 6, ops);
+  return ops[0].value;
 }
 
 \f
@@ -6961,28 +6702,18 @@ static rtx
 expand_val_compare_and_swap_1 (rtx mem, rtx old_val, rtx new_val,
                               rtx target, enum insn_code icode)
 {
+  struct expand_operand ops[4];
   enum machine_mode mode = GET_MODE (mem);
-  rtx insn;
-
-  if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-    target = gen_reg_rtx (mode);
-
-  if (GET_MODE (old_val) != VOIDmode && GET_MODE (old_val) != mode)
-    old_val = convert_modes (mode, GET_MODE (old_val), old_val, 1);
-  if (!insn_data[icode].operand[2].predicate (old_val, mode))
-    old_val = force_reg (mode, old_val);
-
-  if (GET_MODE (new_val) != VOIDmode && GET_MODE (new_val) != mode)
-    new_val = convert_modes (mode, GET_MODE (new_val), new_val, 1);
-  if (!insn_data[icode].operand[3].predicate (new_val, mode))
-    new_val = force_reg (mode, new_val);
-
-  insn = GEN_FCN (icode) (target, mem, old_val, new_val);
-  if (insn == NULL_RTX)
-    return NULL_RTX;
-  emit_insn (insn);
 
-  return target;
+  create_output_operand (&ops[0], target, mode);
+  create_fixed_operand (&ops[1], mem);
+  /* OLD_VAL and NEW_VAL may have been promoted to a wider mode.
+     Shrink them if so.  */
+  create_convert_operand_to (&ops[2], old_val, mode, true);
+  create_convert_operand_to (&ops[3], new_val, mode, true);
+  if (maybe_expand_insn (icode, 4, ops))
+    return ops[0].value;
+  return NULL_RTX;
 }
 
 /* Expand a compare-and-swap operation and return its value.  */
@@ -6991,7 +6722,8 @@ rtx
 expand_val_compare_and_swap (rtx mem, rtx old_val, rtx new_val, rtx target)
 {
   enum machine_mode mode = GET_MODE (mem);
-  enum insn_code icode = sync_compare_and_swap[mode];
+  enum insn_code icode
+    = direct_optab_handler (sync_compare_and_swap_optab, mode);
 
   if (icode == CODE_FOR_nothing)
     return NULL_RTX;
@@ -7028,10 +6760,11 @@ expand_bool_compare_and_swap (rtx mem, rtx old_val, rtx new_val, rtx target)
   /* If the target supports a compare-and-swap pattern that simultaneously
      sets some flag for success, then use it.  Otherwise use the regular
      compare-and-swap and follow that immediately with a compare insn.  */
-  icode = sync_compare_and_swap[mode];
+  icode = direct_optab_handler (sync_compare_and_swap_optab, mode);
   if (icode == CODE_FOR_nothing)
     return NULL_RTX;
 
+  do_pending_stack_adjust ();
   do
     {
       start_sequence ();
@@ -7106,7 +6839,7 @@ expand_compare_and_swap_loop (rtx mem, rtx old_reg, rtx new_reg, rtx seq)
   /* If the target supports a compare-and-swap pattern that simultaneously
      sets some flag for success, then use it.  Otherwise use the regular
      compare-and-swap and follow that immediately with a compare insn.  */
-  icode = sync_compare_and_swap[mode];
+  icode = direct_optab_handler (sync_compare_and_swap_optab, mode);
   if (icode == CODE_FOR_nothing)
     return false;
 
@@ -7150,26 +6883,26 @@ expand_sync_operation (rtx mem, rtx val, enum rtx_code code)
   switch (code)
     {
     case PLUS:
-      icode = sync_add_optab[mode];
+      icode = direct_optab_handler (sync_add_optab, mode);
       break;
     case IOR:
-      icode = sync_ior_optab[mode];
+      icode = direct_optab_handler (sync_ior_optab, mode);
       break;
     case XOR:
-      icode = sync_xor_optab[mode];
+      icode = direct_optab_handler (sync_xor_optab, mode);
       break;
     case AND:
-      icode = sync_and_optab[mode];
+      icode = direct_optab_handler (sync_and_optab, mode);
       break;
     case NOT:
-      icode = sync_nand_optab[mode];
+      icode = direct_optab_handler (sync_nand_optab, mode);
       break;
 
     case MINUS:
-      icode = sync_sub_optab[mode];
+      icode = direct_optab_handler (sync_sub_optab, mode);
       if (icode == CODE_FOR_nothing || CONST_INT_P (val))
        {
-         icode = sync_add_optab[mode];
+         icode = direct_optab_handler (sync_add_optab, mode);
          if (icode != CODE_FOR_nothing)
            {
              val = expand_simple_unop (mode, NEG, val, NULL_RTX, 1);
@@ -7185,22 +6918,19 @@ expand_sync_operation (rtx mem, rtx val, enum rtx_code code)
   /* Generate the direct operation, if present.  */
   if (icode != CODE_FOR_nothing)
     {
-      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
-       val = convert_modes (mode, GET_MODE (val), val, 1);
-      if (!insn_data[icode].operand[1].predicate (val, mode))
-       val = force_reg (mode, val);
+      struct expand_operand ops[2];
 
-      insn = GEN_FCN (icode) (mem, val);
-      if (insn)
-       {
-         emit_insn (insn);
-         return const0_rtx;
-       }
+      create_fixed_operand (&ops[0], mem);
+      /* VAL may have been promoted to a wider mode.  Shrink it if so.  */
+      create_convert_operand_to (&ops[1], val, mode, true);
+      if (maybe_expand_insn (icode, 2, ops))
+       return const0_rtx;
     }
 
   /* Failing that, generate a compare-and-swap loop in which we perform the
      operation with normal arithmetic instructions.  */
-  if (sync_compare_and_swap[mode] != CODE_FOR_nothing)
+  if (direct_optab_handler (sync_compare_and_swap_optab, mode)
+      != CODE_FOR_nothing)
     {
       rtx t0 = gen_reg_rtx (mode), t1;
 
@@ -7245,34 +6975,34 @@ expand_sync_fetch_operation (rtx mem, rtx val, enum rtx_code code,
   switch (code)
     {
     case PLUS:
-      old_code = sync_old_add_optab[mode];
-      new_code = sync_new_add_optab[mode];
+      old_code = direct_optab_handler (sync_old_add_optab, mode);
+      new_code = direct_optab_handler (sync_new_add_optab, mode);
       break;
     case IOR:
-      old_code = sync_old_ior_optab[mode];
-      new_code = sync_new_ior_optab[mode];
+      old_code = direct_optab_handler (sync_old_ior_optab, mode);
+      new_code = direct_optab_handler (sync_new_ior_optab, mode);
       break;
     case XOR:
-      old_code = sync_old_xor_optab[mode];
-      new_code = sync_new_xor_optab[mode];
+      old_code = direct_optab_handler (sync_old_xor_optab, mode);
+      new_code = direct_optab_handler (sync_new_xor_optab, mode);
       break;
     case AND:
-      old_code = sync_old_and_optab[mode];
-      new_code = sync_new_and_optab[mode];
+      old_code = direct_optab_handler (sync_old_and_optab, mode);
+      new_code = direct_optab_handler (sync_new_and_optab, mode);
       break;
     case NOT:
-      old_code = sync_old_nand_optab[mode];
-      new_code = sync_new_nand_optab[mode];
+      old_code = direct_optab_handler (sync_old_nand_optab, mode);
+      new_code = direct_optab_handler (sync_new_nand_optab, mode);
       break;
 
     case MINUS:
-      old_code = sync_old_sub_optab[mode];
-      new_code = sync_new_sub_optab[mode];
+      old_code = direct_optab_handler (sync_old_sub_optab, mode);
+      new_code = direct_optab_handler (sync_new_sub_optab, mode);
       if ((old_code == CODE_FOR_nothing && new_code == CODE_FOR_nothing)
           || CONST_INT_P (val))
        {
-         old_code = sync_old_add_optab[mode];
-         new_code = sync_new_add_optab[mode];
+         old_code = direct_optab_handler (sync_old_add_optab, mode);
+         new_code = direct_optab_handler (sync_new_add_optab, mode);
          if (old_code != CODE_FOR_nothing || new_code != CODE_FOR_nothing)
            {
              val = expand_simple_unop (mode, NEG, val, NULL_RTX, 1);
@@ -7317,19 +7047,16 @@ expand_sync_fetch_operation (rtx mem, rtx val, enum rtx_code code,
   /* If we found something supported, great.  */
   if (icode != CODE_FOR_nothing)
     {
-      if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-       target = gen_reg_rtx (mode);
+      struct expand_operand ops[3];
 
-      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
-       val = convert_modes (mode, GET_MODE (val), val, 1);
-      if (!insn_data[icode].operand[2].predicate (val, mode))
-       val = force_reg (mode, val);
-
-      insn = GEN_FCN (icode) (target, mem, val);
-      if (insn)
+      create_output_operand (&ops[0], target, mode);
+      create_fixed_operand (&ops[1], mem);
+      /* VAL may have been promoted to a wider mode.  Shrink it if so.  */
+      create_convert_operand_to (&ops[2], val, mode, true);
+      if (maybe_expand_insn (icode, 3, ops))
        {
-         emit_insn (insn);
-
+         target = ops[0].value;
+         val = ops[2].value;
          /* If we need to compensate for using an operation with the
             wrong return value, do so now.  */
          if (compensate)
@@ -7362,7 +7089,8 @@ expand_sync_fetch_operation (rtx mem, rtx val, enum rtx_code code,
 
   /* Failing that, generate a compare-and-swap loop in which we perform the
      operation with normal arithmetic instructions.  */
-  if (sync_compare_and_swap[mode] != CODE_FOR_nothing)
+  if (direct_optab_handler (sync_compare_and_swap_optab, mode)
+      != CODE_FOR_nothing)
     {
       rtx t0 = gen_reg_rtx (mode), t1;
 
@@ -7408,30 +7136,24 @@ expand_sync_lock_test_and_set (rtx mem, rtx val, rtx target)
 {
   enum machine_mode mode = GET_MODE (mem);
   enum insn_code icode;
-  rtx insn;
 
   /* If the target supports the test-and-set directly, great.  */
-  icode = sync_lock_test_and_set[mode];
+  icode = direct_optab_handler (sync_lock_test_and_set_optab, mode);
   if (icode != CODE_FOR_nothing)
     {
-      if (!target || !insn_data[icode].operand[0].predicate (target, mode))
-       target = gen_reg_rtx (mode);
-
-      if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
-       val = convert_modes (mode, GET_MODE (val), val, 1);
-      if (!insn_data[icode].operand[2].predicate (val, mode))
-       val = force_reg (mode, val);
+      struct expand_operand ops[3];
 
-      insn = GEN_FCN (icode) (target, mem, val);
-      if (insn)
-       {
-         emit_insn (insn);
-         return target;
-       }
+      create_output_operand (&ops[0], target, mode);
+      create_fixed_operand (&ops[1], mem);
+      /* VAL may have been promoted to a wider mode.  Shrink it if so.  */
+      create_convert_operand_to (&ops[2], val, mode, true);
+      if (maybe_expand_insn (icode, 3, ops))
+       return ops[0].value;
     }
 
   /* Otherwise, use a compare-and-swap loop for the exchange.  */
-  if (sync_compare_and_swap[mode] != CODE_FOR_nothing)
+  if (direct_optab_handler (sync_compare_and_swap_optab, mode)
+      != CODE_FOR_nothing)
     {
       if (!target || !register_operand (target, mode))
        target = gen_reg_rtx (mode);
@@ -7443,5 +7165,263 @@ expand_sync_lock_test_and_set (rtx mem, rtx val, rtx target)
 
   return NULL_RTX;
 }
+\f
+/* Return true if OPERAND is suitable for operand number OPNO of
+   instruction ICODE.  */
+
+bool
+insn_operand_matches (enum insn_code icode, unsigned int opno, rtx operand)
+{
+  return (!insn_data[(int) icode].operand[opno].predicate
+         || (insn_data[(int) icode].operand[opno].predicate
+             (operand, insn_data[(int) icode].operand[opno].mode)));
+}
+\f
+/* TARGET is a target of a multiword operation that we are going to
+   implement as a series of word-mode operations.  Return true if
+   TARGET is suitable for this purpose.  */
+
+bool
+valid_multiword_target_p (rtx target)
+{
+  enum machine_mode mode;
+  int i;
+
+  mode = GET_MODE (target);
+  for (i = 0; i < GET_MODE_SIZE (mode); i += UNITS_PER_WORD)
+    if (!validate_subreg (word_mode, mode, target, i))
+      return false;
+  return true;
+}
+
+/* Like maybe_legitimize_operand, but do not change the code of the
+   current rtx value.  */
+
+static bool
+maybe_legitimize_operand_same_code (enum insn_code icode, unsigned int opno,
+                                   struct expand_operand *op)
+{
+  /* See if the operand matches in its current form.  */
+  if (insn_operand_matches (icode, opno, op->value))
+    return true;
+
+  /* If the operand is a memory whose address has no side effects,
+     try forcing the address into a register.  The check for side
+     effects is important because force_reg cannot handle things
+     like auto-modified addresses.  */
+  if (insn_data[(int) icode].operand[opno].allows_mem
+      && MEM_P (op->value)
+      && !side_effects_p (XEXP (op->value, 0)))
+    {
+      rtx addr, mem, last;
+
+      last = get_last_insn ();
+      addr = force_reg (Pmode, XEXP (op->value, 0));
+      mem = replace_equiv_address (op->value, addr);
+      if (insn_operand_matches (icode, opno, mem))
+       {
+         op->value = mem;
+         return true;
+       }
+      delete_insns_since (last);
+    }
+
+  return false;
+}
+
+/* Try to make OP match operand OPNO of instruction ICODE.  Return true
+   on success, storing the new operand value back in OP.  */
+
+static bool
+maybe_legitimize_operand (enum insn_code icode, unsigned int opno,
+                         struct expand_operand *op)
+{
+  enum machine_mode mode, imode;
+  bool old_volatile_ok, result;
+
+  mode = op->mode;
+  switch (op->type)
+    {
+    case EXPAND_FIXED:
+      old_volatile_ok = volatile_ok;
+      volatile_ok = true;
+      result = maybe_legitimize_operand_same_code (icode, opno, op);
+      volatile_ok = old_volatile_ok;
+      return result;
+
+    case EXPAND_OUTPUT:
+      gcc_assert (mode != VOIDmode);
+      if (op->value
+         && op->value != const0_rtx
+         && GET_MODE (op->value) == mode
+         && maybe_legitimize_operand_same_code (icode, opno, op))
+       return true;
+
+      op->value = gen_reg_rtx (mode);
+      break;
+
+    case EXPAND_INPUT:
+    input:
+      gcc_assert (mode != VOIDmode);
+      gcc_assert (GET_MODE (op->value) == VOIDmode
+                 || GET_MODE (op->value) == mode);
+      if (maybe_legitimize_operand_same_code (icode, opno, op))
+       return true;
+
+      op->value = copy_to_mode_reg (mode, op->value);
+      break;
+
+    case EXPAND_CONVERT_TO:
+      gcc_assert (mode != VOIDmode);
+      op->value = convert_to_mode (mode, op->value, op->unsigned_p);
+      goto input;
+
+    case EXPAND_CONVERT_FROM:
+      if (GET_MODE (op->value) != VOIDmode)
+       mode = GET_MODE (op->value);
+      else
+       /* The caller must tell us what mode this value has.  */
+       gcc_assert (mode != VOIDmode);
+
+      imode = insn_data[(int) icode].operand[opno].mode;
+      if (imode != VOIDmode && imode != mode)
+       {
+         op->value = convert_modes (imode, mode, op->value, op->unsigned_p);
+         mode = imode;
+       }
+      goto input;
+
+    case EXPAND_ADDRESS:
+      gcc_assert (mode != VOIDmode);
+      op->value = convert_memory_address (mode, op->value);
+      goto input;
+
+    case EXPAND_INTEGER:
+      mode = insn_data[(int) icode].operand[opno].mode;
+      if (mode != VOIDmode && const_int_operand (op->value, mode))
+       goto input;
+      break;
+    }
+  return insn_operand_matches (icode, opno, op->value);
+}
+
+/* Make OP describe an input operand that should have the same value
+   as VALUE, after any mode conversion that the target might request.
+   TYPE is the type of VALUE.  */
+
+void
+create_convert_operand_from_type (struct expand_operand *op,
+                                 rtx value, tree type)
+{
+  create_convert_operand_from (op, value, TYPE_MODE (type),
+                              TYPE_UNSIGNED (type));
+}
+
+/* Try to make operands [OPS, OPS + NOPS) match operands [OPNO, OPNO + NOPS)
+   of instruction ICODE.  Return true on success, leaving the new operand
+   values in the OPS themselves.  Emit no code on failure.  */
+
+bool
+maybe_legitimize_operands (enum insn_code icode, unsigned int opno,
+                          unsigned int nops, struct expand_operand *ops)
+{
+  rtx last;
+  unsigned int i;
+
+  last = get_last_insn ();
+  for (i = 0; i < nops; i++)
+    if (!maybe_legitimize_operand (icode, opno + i, &ops[i]))
+      {
+       delete_insns_since (last);
+       return false;
+      }
+  return true;
+}
+
+/* Try to generate instruction ICODE, using operands [OPS, OPS + NOPS)
+   as its operands.  Return the instruction pattern on success,
+   and emit any necessary set-up code.  Return null and emit no
+   code on failure.  */
+
+rtx
+maybe_gen_insn (enum insn_code icode, unsigned int nops,
+               struct expand_operand *ops)
+{
+  gcc_assert (nops == (unsigned int) insn_data[(int) icode].n_generator_args);
+  if (!maybe_legitimize_operands (icode, 0, nops, ops))
+    return NULL_RTX;
+
+  switch (nops)
+    {
+    case 1:
+      return GEN_FCN (icode) (ops[0].value);
+    case 2:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value);
+    case 3:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value);
+    case 4:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value,
+                             ops[3].value);
+    case 5:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value,
+                             ops[3].value, ops[4].value);
+    case 6:
+      return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value,
+                             ops[3].value, ops[4].value, ops[5].value);
+    }
+  gcc_unreachable ();
+}
+
+/* Try to emit instruction ICODE, using operands [OPS, OPS + NOPS)
+   as its operands.  Return true on success and emit no code on failure.  */
+
+bool
+maybe_expand_insn (enum insn_code icode, unsigned int nops,
+                  struct expand_operand *ops)
+{
+  rtx pat = maybe_gen_insn (icode, nops, ops);
+  if (pat)
+    {
+      emit_insn (pat);
+      return true;
+    }
+  return false;
+}
+
+/* Like maybe_expand_insn, but for jumps.  */
+
+bool
+maybe_expand_jump_insn (enum insn_code icode, unsigned int nops,
+                       struct expand_operand *ops)
+{
+  rtx pat = maybe_gen_insn (icode, nops, ops);
+  if (pat)
+    {
+      emit_jump_insn (pat);
+      return true;
+    }
+  return false;
+}
+
+/* Emit instruction ICODE, using operands [OPS, OPS + NOPS)
+   as its operands.  */
+
+void
+expand_insn (enum insn_code icode, unsigned int nops,
+            struct expand_operand *ops)
+{
+  if (!maybe_expand_insn (icode, nops, ops))
+    gcc_unreachable ();
+}
+
+/* Like expand_insn, but for jumps.  */
+
+void
+expand_jump_insn (enum insn_code icode, unsigned int nops,
+                 struct expand_operand *ops)
+{
+  if (!maybe_expand_jump_insn (icode, nops, ops))
+    gcc_unreachable ();
+}
 
 #include "gt-optabs.h"