OSDN Git Service

Initial revision
authorkenner <kenner@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 30 Dec 1991 03:19:53 +0000 (03:19 +0000)
committerkenner <kenner@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 30 Dec 1991 03:19:53 +0000 (03:19 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@148 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/optabs.c [new file with mode: 0644]

diff --git a/gcc/optabs.c b/gcc/optabs.c
new file mode 100644 (file)
index 0000000..d83d1eb
--- /dev/null
@@ -0,0 +1,3990 @@
+/* Expand the basic unary and binary arithmetic operations, for GNU compiler.
+   Copyright (C) 1987-1991 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+
+#include "config.h"
+#include "rtl.h"
+#include "tree.h"
+#include "flags.h"
+#include "insn-flags.h"
+#include "insn-codes.h"
+#include "expr.h"
+#include "insn-config.h"
+#include "recog.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.  */
+
+optab add_optab;
+optab sub_optab;
+optab smul_optab;
+optab smul_widen_optab;
+optab umul_widen_optab;
+optab sdiv_optab;
+optab sdivmod_optab;
+optab udiv_optab;
+optab udivmod_optab;
+optab smod_optab;
+optab umod_optab;
+optab flodiv_optab;
+optab ftrunc_optab;
+optab and_optab;
+optab ior_optab;
+optab xor_optab;
+optab ashl_optab;
+optab lshr_optab;
+optab lshl_optab;
+optab ashr_optab;
+optab rotl_optab;
+optab rotr_optab;
+optab smin_optab;
+optab smax_optab;
+optab umin_optab;
+optab umax_optab;
+
+optab mov_optab;
+optab movstrict_optab;
+
+optab neg_optab;
+optab abs_optab;
+optab one_cmpl_optab;
+optab ffs_optab;
+
+optab cmp_optab;
+optab ucmp_optab;  /* Used only for libcalls for unsigned comparisons.  */
+optab tst_optab;
+
+/* SYMBOL_REF rtx's for the library functions that are called
+   implicitly and not via optabs.  */
+
+rtx extendsfdf2_libfunc;
+rtx truncdfsf2_libfunc;
+rtx memcpy_libfunc;
+rtx bcopy_libfunc;
+rtx memcmp_libfunc;
+rtx bcmp_libfunc;
+rtx memset_libfunc;
+rtx bzero_libfunc;
+rtx eqsf2_libfunc;
+rtx nesf2_libfunc;
+rtx gtsf2_libfunc;
+rtx gesf2_libfunc;
+rtx ltsf2_libfunc;
+rtx lesf2_libfunc;
+rtx eqdf2_libfunc;
+rtx nedf2_libfunc;
+rtx gtdf2_libfunc;
+rtx gedf2_libfunc;
+rtx ltdf2_libfunc;
+rtx ledf2_libfunc;
+
+/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...)
+   gives the gen_function to make a branch to test that condition.  */
+
+rtxfun bcc_gen_fctn[NUM_RTX_CODE];
+
+/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...)
+   gives the insn code to make a store-condition insn
+   to test that condition.  */
+
+enum insn_code setcc_gen_code[NUM_RTX_CODE];
+
+static void emit_float_lib_cmp ();
+\f
+/* Add a REG_EQUAL note to the last insn in SEQ.  TARGET is being set to
+   the result of operation CODE applied to OP0 (and OP1 if it is a binary
+   operation).
+
+   If the last insn does not set TARGET, don't do anything, but return 1.
+
+   If a previous insn sets TARGET and TARGET is one of OP0 or OP1,
+   don't add the REG_EQUAL note but return 0.  Our caller can then try
+   again, ensuring that TARGET is not one of the operands.  */
+
+static int
+add_equal_note (seq, target, code, op0, op1)
+     rtx seq;
+     rtx target;
+     enum rtx_code code;
+     rtx op0, op1;
+{
+  rtx set;
+  int i;
+  rtx note;
+
+  if ((GET_RTX_CLASS (code) != '1' && GET_RTX_CLASS (code) != '2'
+       && GET_RTX_CLASS (code) != 'c' && GET_RTX_CLASS (code) != '<')
+      || GET_CODE (seq) != SEQUENCE
+      || (set = single_set (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1))) == 0
+      || GET_CODE (target) == ZERO_EXTRACT
+      || (! rtx_equal_p (SET_DEST (set), target)
+         /* For a STRICT_LOW_PART, the REG_NOTE applies to what is inside the
+            SUBREG.  */
+         && (GET_CODE (SET_DEST (set)) != STRICT_LOW_PART
+             || ! rtx_equal_p (SUBREG_REG (XEXP (SET_DEST (set), 0)),
+                               target))))
+    return 1;
+
+  /* If TARGET is in OP0 or OP1, check if anything in SEQ sets TARGET
+     besides the last insn.  */
+  if (reg_overlap_mentioned_p (target, op0)
+      || (op1 && reg_overlap_mentioned_p (target, op1)))
+    for (i = XVECLEN (seq, 0) - 2; i >= 0; i--)
+      if (reg_set_p (target, XVECEXP (seq, 0, i)))
+       return 0;
+
+  if (GET_RTX_CLASS (code) == '1')
+    note = gen_rtx (code, GET_MODE (target), op0);
+  else
+    note = gen_rtx (code, GET_MODE (target), op0, op1);
+
+  REG_NOTES (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1))
+    = gen_rtx (EXPR_LIST, REG_EQUAL, note,
+              REG_NOTES (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1)));
+
+  return 1;
+}
+\f
+/* Generate code to perform an operation specified by BINOPTAB
+   on operands OP0 and OP1, with result having machine-mode MODE.
+
+   UNSIGNEDP is for the case where we have to widen the operands
+   to perform the operation.  It says to use zero-extension.
+
+   If TARGET is nonzero, the value
+   is generated there, if it is convenient to do so.
+   In all cases an rtx is returned for the locus of the value;
+   this may or may not be TARGET.  */
+
+rtx
+expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
+     enum machine_mode mode;
+     optab binoptab;
+     rtx op0, op1;
+     rtx target;
+     int unsignedp;
+     enum optab_methods methods;
+{
+  enum mode_class class;
+  enum machine_mode wider_mode;
+  enum machine_mode submode = mode_for_size (BITS_PER_WORD, MODE_INT, 0);
+  register rtx temp;
+  int commutative_op = 0;
+  int shift_op = (binoptab->code ==  ASHIFT
+                 || binoptab->code == ASHIFTRT
+                 || binoptab->code == LSHIFT
+                 || binoptab->code == LSHIFTRT
+                 || binoptab->code == ROTATE
+                 || binoptab->code == ROTATERT);
+  rtx last;
+
+  class = GET_MODE_CLASS (mode);
+
+  op0 = protect_from_queue (op0, 0);
+  op1 = protect_from_queue (op1, 0);
+  if (target)
+    target = protect_from_queue (target, 1);
+
+  if (flag_force_mem)
+    {
+      op0 = force_not_mem (op0);
+      op1 = force_not_mem (op1);
+    }
+
+  /* If we are inside an appropriately-short loop and one operand is an
+     expensive constant, force it into a register.  */
+  if (CONSTANT_P (op0) && preserve_subexpressions_p () && rtx_cost (op0) > 2)
+    op0 = force_reg (mode, op0);
+
+  if (CONSTANT_P (op1) && preserve_subexpressions_p () && rtx_cost (op1) > 2)
+    op1 = force_reg ((shift_op
+                     ? mode_for_size (BITS_PER_WORD, MODE_INT, 0)
+                     : mode),
+                    op1);
+
+#if 0  /* Turned off because it seems to be a kludgy method.  */
+  /* If subtracting integer from pointer, and the pointer has a special mode,
+     then change it to an add.  We use the add insn of Pmode for combining
+     integers with pointers, and the sub insn to subtract two pointers.  */
+
+  if (binoptab == sub_optab
+      && GET_MODE (op0) == Pmode && GET_MODE (op1) != Pmode)
+    {
+      op1 = negate_rtx (GET_MODE(op1), op1);
+      binoptab = add_optab;
+    }
+#endif /* 0 */
+
+  /* Record where to delete back to if we backtrack.  */
+  last = get_last_insn ();
+
+  /* If operation is commutative,
+     try to make the first operand a register.
+     Even better, try to make it the same as the target.
+     Also try to make the last operand a constant.  */
+  if (GET_RTX_CLASS (binoptab->code) == 'c'
+      || binoptab == smul_widen_optab
+      || binoptab == umul_widen_optab)
+    {
+      commutative_op = 1;
+
+      if (((target == 0 || GET_CODE (target) == REG)
+          ? ((GET_CODE (op1) == REG
+              && GET_CODE (op0) != REG)
+             || target == op1)
+          : rtx_equal_p (op1, target))
+         || GET_CODE (op0) == CONST_INT)
+       {
+         temp = op1;
+         op1 = op0;
+         op0 = temp;
+       }
+    }
+
+  /* If we can do it with a three-operand insn, do so.  */
+
+  if (methods != OPTAB_MUST_WIDEN
+      && binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+    {
+      int icode = (int) binoptab->handlers[(int) mode].insn_code;
+      enum machine_mode mode0 = insn_operand_mode[icode][1];
+      enum machine_mode mode1 = insn_operand_mode[icode][2];
+      rtx pat;
+      rtx xop0 = op0, xop1 = op1;
+
+      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. */
+      if (commutative_op)
+       {
+         if (GET_MODE (op0) != mode0 && GET_MODE (op1) != mode1
+             && GET_MODE (op0) == mode1 && GET_MODE (op1) == mode0)
+           {
+             register rtx tmp;
+
+             tmp = op0; op0 = op1; op1 = tmp;
+             tmp = xop0; xop0 = xop1; xop1 = tmp;
+           }
+       }
+
+      /* In case the insn wants input operands in modes different from
+        the result, convert the operands.  */
+
+      if (GET_MODE (op0) != VOIDmode
+         && GET_MODE (op0) != mode0)
+       xop0 = convert_to_mode (mode0, xop0, unsignedp);
+
+      if (GET_MODE (xop1) != VOIDmode
+         && GET_MODE (xop1) != mode1)
+       xop1 = convert_to_mode (mode1, xop1, unsignedp);
+
+      /* Now, if insn's predicates don't allow our operands, put them into
+        pseudo regs.  */
+
+      if (! (*insn_operand_predicate[icode][1]) (xop0, mode0))
+       xop0 = copy_to_mode_reg (mode0, xop0);
+
+      if (! (*insn_operand_predicate[icode][2]) (xop1, mode1))
+       xop1 = copy_to_mode_reg (mode1, xop1);
+
+      if (! (*insn_operand_predicate[icode][0]) (temp, mode))
+       temp = gen_reg_rtx (mode);
+
+      pat = GEN_FCN (icode) (temp, xop0, xop1);
+      if (pat)
+       {
+         /* If PAT is a multi-insn sequence, try to add an appropriate
+            REG_EQUAL note to it.  If we can't because TEMP conflicts with an
+            operand, call ourselves again, this time without a target.  */
+         if (GET_CODE (pat) == SEQUENCE
+             && ! add_equal_note (pat, temp, binoptab->code, xop0, xop1))
+           {
+             delete_insns_since (last);
+             return expand_binop (mode, binoptab, op0, op1, 0, unsignedp,
+                                  methods);
+           }
+
+         emit_insn (pat);
+         return temp;
+       }
+      else
+       delete_insns_since (last);
+    }
+
+  /* These can be done a word at a time.  */
+  if ((binoptab == and_optab || binoptab == ior_optab || binoptab == xor_optab)
+      && class == MODE_INT
+      && GET_MODE_SIZE (mode) > UNITS_PER_WORD
+      && binoptab->handlers[(int) submode].insn_code != CODE_FOR_nothing)
+    {
+      int i;
+      rtx insns;
+      rtx equiv_value;
+
+      /* If TARGET is the same as one of the operands, the REG_EQUAL note
+        won't be accurate, so use a new target.  */
+      if (target == 0 || target == op0 || target == op1)
+       target = gen_reg_rtx (mode);
+
+      start_sequence ();
+
+      /* Do the actual arithmetic.  */
+      for (i = 0; i < GET_MODE_BITSIZE (mode) / BITS_PER_WORD; i++)
+       {
+         rtx target_piece = operand_subword (target, i, 1, mode);
+         rtx x = expand_binop (submode, binoptab,
+                               operand_subword_force (op0, i, mode),
+                               operand_subword_force (op1, i, mode),
+                               target_piece, unsignedp, methods);
+         if (target_piece != x)
+           emit_move_insn (target_piece, x);
+       }
+
+      insns = get_insns ();
+      end_sequence ();
+
+      if (binoptab->code != UNKNOWN)
+       equiv_value = gen_rtx (binoptab->code, mode, op0, op1);
+      else
+       equiv_value = 0;
+
+      emit_no_conflict_block (insns, target, op0, op1, equiv_value);
+      return target;
+    }
+
+  /* These can be done a word at a time by propagating carries.  */
+  if ((binoptab == add_optab || binoptab == sub_optab)
+      && class == MODE_INT
+      && GET_MODE_SIZE (mode) >= 2 * UNITS_PER_WORD
+      && binoptab->handlers[(int) submode].insn_code != CODE_FOR_nothing)
+    {
+      int i;
+      rtx carry_tmp = gen_reg_rtx (submode);
+      optab otheroptab = binoptab == add_optab ? sub_optab : add_optab;
+      int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
+      rtx carry_in, carry_out;
+
+      /* We can handle either a 1 or -1 value for the carry.  If STORE_FLAG
+        value is one of those, use it.  Otherwise, use 1 since it is the
+        one easiest to get.  */
+#if STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1
+      int normalizep = STORE_FLAG_VALUE;
+#else
+      int normalizep = 1;
+#endif
+
+      /* Prepare the operands.  */
+      op0 = force_reg (mode, op0);
+      op1 = force_reg (mode, op1);
+
+      if (target == 0 || GET_CODE (target) != REG
+         || target == op0 || target == op1)
+       target = gen_reg_rtx (mode);
+
+      /* Do the actual arithmetic.  */
+      for (i = 0; i < nwords; i++)
+       {
+         int index = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i);
+         rtx target_piece = operand_subword (target, index, 1, mode);
+         rtx op0_piece = operand_subword_force (op0, index, mode);
+         rtx op1_piece = operand_subword_force (op1, index, mode);
+         rtx x;
+
+         /* Main add/subtract of the input operands.  */
+         x = expand_binop (submode, binoptab,
+                           op0_piece, op1_piece,
+                           target_piece, unsignedp, methods);
+         if (x == 0)
+           break;
+
+         if (i + 1 < nwords)
+           {
+             /* Store carry from main add/subtract.  */
+             carry_out = gen_reg_rtx (submode);
+             carry_out = emit_store_flag (carry_out,
+                                          binoptab == add_optab ? LTU : GTU,
+                                          x, op0_piece,
+                                          submode, 1, normalizep);
+             if (!carry_out)
+               break;
+           }
+
+         if (i > 0)
+           {
+             /* Add/subtract previous carry to main result.  */
+             x = expand_binop (submode,
+                               normalizep == 1 ? binoptab : otheroptab,
+                               x, carry_in,
+                               target_piece, 1, methods);
+             if (target_piece != x)
+               emit_move_insn (target_piece, x);
+
+             if (i + 1 < nwords)
+               {
+                 /* THIS CODE HAS NOT BEEN TESTED.  */
+                 /* Get out carry from adding/subtracting carry in.  */
+                 carry_tmp = emit_store_flag (carry_tmp,
+                                              binoptab == add_optab
+                                                ? LTU : GTU,
+                                              x, carry_in,
+                                              submode, 1, normalizep);
+                 /* Logical-ior the two poss. carry together.  */
+                 carry_out = expand_binop (submode, ior_optab,
+                                           carry_out, carry_tmp,
+                                           carry_out, 0, methods);
+                 if (!carry_out)
+                   break;
+               }
+           }
+
+         carry_in = carry_out;
+       }       
+
+      if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD)
+       {
+         rtx temp;
+         
+         temp = emit_move_insn (target, target);
+         REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL,
+                                     gen_rtx (binoptab->code, mode, op0, op1),
+                                     REG_NOTES (temp));
+         return target;
+       }
+      else
+       delete_insns_since (last);
+    }
+
+  /* If we want to multiply two two-word values and have normal and widening
+     multiplies of single-word values, we can do this with three smaller
+     multiplications.  Note that we do not make a REG_NO_CONFLICT block here
+     because we are not operating on one word at a time. 
+
+     The multiplication proceeds as follows:
+                            _______________________
+                           [__op0_high_|__op0_low__]
+                            _______________________
+    *                      [__op1_high_|__op1_low__]
+    _______________________________________________
+                            _______________________
+(1)                        [__op0_low__*__op1_low__]
+                _______________________
+(2a)           [__op0_low__*__op1_high_]
+                _______________________
+(2b)           [__op0_high_*__op1_low__]
+     _______________________
+(3) [__op0_high_*__op1_high_]
+
+
+    This gives a 4-word result.  Since we are only interested in the
+    lower 2 words, partial result (3) and the upper words of (2a) and
+    (2b) don't need to be calculated.  Hence (2a) and (2b) can be
+    calculated using non-widening multiplication.
+
+    (1), however, needs to be calculated with an unsigned widening
+    multiplication.  If this operation is not directly supported we
+    try using a signed widening multiplication and adjust the result.
+    This adjustment works as follows:
+
+      If both operands are positive then no adjustment is needed.
+
+      If the operands have different signs, for example op0_low < 0 and
+      op1_low >= 0, the instruction treats the most significant bit of
+      op0_low as a sign bit instead of a bit with significance
+      2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low
+      with 2**BITS_PER_WORD - op0_low, and two's complements the
+      result.  Conclusion: We need to add op1_low * 2**BITS_PER_WORD to
+      the result.
+
+      Similarly, if both operands are negative, we need to add
+      (op0_low + op1_low) * 2**BITS_PER_WORD.
+
+      We use a trick to adjust quickly.  We logically shift op0_low right
+      (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to
+      op0_high (op1_high) before it is used to calculate 2b (2a).  If no
+      logical shift exists, we do an arithmetic right shift and subtract
+      the 0 or -1.  */
+
+  if (binoptab == smul_optab
+      && class == MODE_INT
+      && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+      && smul_optab->handlers[(int) submode].insn_code != CODE_FOR_nothing
+      && add_optab->handlers[(int) submode].insn_code != CODE_FOR_nothing
+      && ((umul_widen_optab->handlers[(int) mode].insn_code
+          != CODE_FOR_nothing)
+         || (smul_widen_optab->handlers[(int) mode].insn_code
+             != CODE_FOR_nothing)))
+    {
+      int low = (WORDS_BIG_ENDIAN ? 1 : 0);
+      int high = (WORDS_BIG_ENDIAN ? 0 : 1);
+      rtx op0_high = operand_subword_force (op0, high, mode);
+      rtx op0_low = operand_subword_force (op0, low, mode);
+      rtx op1_high = operand_subword_force (op1, high, mode);
+      rtx op1_low = operand_subword_force (op1, low, mode);
+      rtx product = 0;
+      rtx op0_xhigh;
+      rtx op1_xhigh;
+
+      /* If the target is the same as one of the inputs, don't use it.  This
+        prevents problems with the REG_EQUAL note.  */
+      if (target == op0 || target == op1)
+       target = 0;
+
+      /* Multiply the two lower words to get a double-word product.
+        If unsigned widening multiplication is available, use that;
+        otherwise use the signed form and compensate.  */
+
+      if (umul_widen_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+       {
+         product = expand_binop (mode, umul_widen_optab, op0_low, op1_low,
+                                 target, 1, OPTAB_DIRECT);
+
+         /* If we didn't succeed, delete everything we did so far.  */
+         if (product == 0)
+           delete_insns_since (last);
+         else
+           op0_xhigh = op0_high, op1_xhigh = op1_high;
+       }
+
+      if (product == 0
+         && smul_widen_optab->handlers[(int) mode].insn_code
+              != CODE_FOR_nothing)
+       {
+         rtx wordm1 = gen_rtx (CONST_INT, VOIDmode, BITS_PER_WORD - 1);
+         product = expand_binop (mode, smul_widen_optab, op0_low, op1_low,
+                                 target, 1, OPTAB_DIRECT);
+         op0_xhigh = expand_binop (submode, lshr_optab, op0_low, wordm1,
+                                   0, 1, OPTAB_DIRECT);
+         if (op0_xhigh)
+           op0_xhigh = expand_binop (submode, add_optab, op0_high, op0_xhigh,
+                                     op0_xhigh, 0, OPTAB_DIRECT);
+         else
+           {
+             op0_xhigh = expand_binop (submode, ashr_optab, op0_low, wordm1,
+                                       0, 0, OPTAB_DIRECT);
+             if (op0_xhigh)
+               op0_xhigh = expand_binop (submode, sub_optab, op0_high,
+                                         op0_xhigh, op0_xhigh, 0,
+                                         OPTAB_DIRECT);
+           }
+
+         op1_xhigh = expand_binop (submode, lshr_optab, op1_low, wordm1,
+                                   0, 1, OPTAB_DIRECT);
+         if (op1_xhigh)
+           op1_xhigh = expand_binop (SImode, add_optab, op1_high, op1_xhigh,
+                                     op1_xhigh, 0, OPTAB_DIRECT);
+         else
+           {
+             op1_xhigh = expand_binop (submode, ashr_optab, op1_low, wordm1,
+                                       0, 0, OPTAB_DIRECT);
+             if (op1_xhigh)
+               op1_xhigh = expand_binop (SImode, sub_optab, op1_high,
+                                         op1_xhigh, op1_xhigh, 0,
+                                         OPTAB_DIRECT);
+           }
+       }
+
+      /* If we have been able to directly compute the product of the
+        low-order words of the operands and perform any required adjustments
+        of the operands, we proceed by trying two more multiplications
+        and then computing the appropriate sum.
+
+        We have checked above that the required addition is provided.
+        Full-word addition will normally always succeed, especially if
+        it is provided at all, so we don't worry about its failure.  The
+        multiplication may well fail, however, so we do handle that.  */
+
+      if (product && op0_xhigh && op1_xhigh)
+       {
+         rtx product_piece;
+         rtx product_high = operand_subword (product, high, 1, mode);
+         rtx temp = expand_binop (submode, binoptab, op0_low, op1_xhigh, 0,
+                                  0, OPTAB_DIRECT);
+
+         if (temp)
+           {
+             product_piece = expand_binop (submode, add_optab, temp,
+                                           product_high, product_high,
+                                           0, OPTAB_LIB_WIDEN);
+             if (product_piece != product_high)
+               emit_move_insn (product_high, product_piece);
+
+             temp = expand_binop (submode, binoptab, op1_low, op0_xhigh, 0,
+                                  0, OPTAB_DIRECT);
+
+             product_piece = expand_binop (submode, add_optab, temp,
+                                           product_high, product_high,
+                                           0, OPTAB_LIB_WIDEN);
+             if (product_piece != product_high)
+               emit_move_insn (product_high, product_piece);
+
+             temp = emit_move_insn (product, product);
+             REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL,
+                                         gen_rtx (MULT, mode, op0, op1),
+                                         REG_NOTES (temp));
+
+             return product;
+           }
+       }
+
+      /* If we get here, we couldn't do it for some reason even though we
+        originally thought we could.  Delete anything we've emitted in
+        trying to do it.  */
+
+      delete_insns_since (last);
+    }
+
+  /* It can't be open-coded in this mode.
+     Use a library call if one is available and caller says that's ok.  */
+
+  if (binoptab->handlers[(int) mode].libfunc
+      && (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN))
+    {
+      rtx insns;
+      rtx funexp = binoptab->handlers[(int) mode].libfunc;
+
+      start_sequence ();
+
+      /* Pass 1 for NO_QUEUE so we don't lose any increments
+        if the libcall is cse'd or moved.  */
+      emit_library_call (binoptab->handlers[(int) mode].libfunc,
+                        1, mode, 2, op0, mode, op1,
+                        (shift_op
+                         ? mode_for_size (BITS_PER_WORD, MODE_INT, 0)
+                         : mode));
+
+      insns = get_insns ();
+      end_sequence ();
+
+      target = gen_reg_rtx (mode);
+      emit_libcall_block (insns, target, hard_libcall_value (mode),
+                         gen_rtx (binoptab->code, mode, op0, op1));
+
+      return target;
+    }
+
+  delete_insns_since (last);
+
+  /* It can't be done in this mode.  Can we do it in a wider mode?  */
+
+  if (! (methods == OPTAB_WIDEN || methods == OPTAB_LIB_WIDEN
+        || methods == OPTAB_MUST_WIDEN))
+    return 0;                  /* Caller says, don't even try.  */
+
+  /* Compute the value of METHODS to pass to recursive calls.
+     Don't allow widening to be tried recursively.  */
+
+  methods = (methods == OPTAB_LIB_WIDEN ? OPTAB_LIB : OPTAB_DIRECT);
+
+  /* Widening is now independent of specific machine modes.
+     It is assumed that widening may be performed to any
+     higher numbered mode in the same mode class.  */
+
+  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+    {
+      for (wider_mode = GET_MODE_WIDER_MODE (mode);
+          ((int) wider_mode < (int) MAX_MACHINE_MODE
+           && GET_MODE_CLASS (wider_mode) == class);
+          wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+       {
+         if ((binoptab->handlers[(int) wider_mode].insn_code
+              != CODE_FOR_nothing)
+             || (methods == OPTAB_LIB
+                 && binoptab->handlers[(int) wider_mode].libfunc))
+           {
+             rtx xop0 = op0, xop1 = op1;
+             int no_extend = 0;
+
+             /* For certain operations, we need not actually extend
+                the narrow operands, as long as we will truncate
+                the results to the same narrowness.  */
+
+             if (binoptab == ior_optab || binoptab == and_optab
+                 || binoptab == xor_optab
+                 || binoptab == add_optab || binoptab == sub_optab
+                 || binoptab == smul_optab
+                 || binoptab == ashl_optab || binoptab == lshl_optab)
+               no_extend = 1;
+
+             if (GET_MODE (xop0) != VOIDmode
+                 && GET_MODE_BITSIZE (wider_mode) <= HOST_BITS_PER_INT)
+               {
+                 if (no_extend)
+                   {
+                     temp = force_reg (GET_MODE (xop0), xop0);
+                     xop0 = gen_rtx (SUBREG, wider_mode, temp, 0);
+                   }
+                 else
+                   {
+                     temp = gen_reg_rtx (wider_mode);
+                     convert_move (temp, xop0, unsignedp);
+                     xop0 = temp;
+                   }
+               }
+             if (GET_MODE (xop1) != VOIDmode
+                 && GET_MODE_BITSIZE (wider_mode) <= HOST_BITS_PER_INT)
+               {
+                 if (no_extend)
+                   {
+                     temp = force_reg (GET_MODE (xop1), xop1);
+                     xop1 = gen_rtx (SUBREG, wider_mode, temp, 0);
+                   }
+                 else
+                   {
+                     temp = gen_reg_rtx (wider_mode);
+                     convert_move (temp, xop1, unsignedp);
+                     xop1 = temp;
+                   }
+               }
+
+             temp = expand_binop (wider_mode, binoptab, xop0, xop1, 0,
+                                  unsignedp, methods);
+             if (temp)
+               {
+                 if (class == MODE_FLOAT)
+                   {
+                     if (target == 0)
+                       target = gen_reg_rtx (mode);
+                     convert_move (target, temp, 0);
+                     return target;
+                   }
+                 else
+                   return gen_lowpart (mode, temp);
+               }
+             else
+               delete_insns_since (last);
+           }
+       }
+    }
+
+  return 0;
+}
+\f
+/* Expand a binary operator which has both signed and unsigned forms.
+   UOPTAB is the optab for unsigned operations, and SOPTAB is for
+   signed operations.
+
+   If we widen unsigned operands, we may use a signed wider operation instead
+   of an unsigned wider operation, since the result would be the same.  */
+
+rtx
+sign_expand_binop (mode, uoptab, soptab, op0, op1, target, unsignedp, methods)
+    enum machine_mode mode;
+    optab uoptab, soptab;
+    rtx op0, op1, target;
+    int unsignedp;
+    enum optab_methods methods;
+{
+  register rtx temp;
+  optab direct_optab = unsignedp ? uoptab : soptab;
+  struct optab wide_soptab;
+
+  /* Do it without widening, if possible.  */
+  temp = expand_binop (mode, direct_optab, op0, op1, target,
+                      unsignedp, OPTAB_DIRECT);
+  if (temp || methods == OPTAB_DIRECT)
+    return temp;
+
+  /* Try widening to a signed int.  Make a fake signed optab that
+     hides any signed insn for direct use.  */
+  wide_soptab = *soptab;
+  wide_soptab.handlers[(int) mode].insn_code = CODE_FOR_nothing;
+  wide_soptab.handlers[(int) mode].libfunc = 0;
+
+  temp = expand_binop (mode, &wide_soptab, op0, op1, target,
+                      unsignedp, OPTAB_WIDEN);
+
+  /* For unsigned operands, try widening to an unsigned int.  */
+  if (temp == 0 && unsignedp)
+    temp = expand_binop (mode, uoptab, op0, op1, target,
+                        unsignedp, OPTAB_WIDEN);
+  if (temp || methods == OPTAB_WIDEN)
+    return temp;
+
+  /* Use the right width lib call if that exists.  */
+  temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_LIB);
+  if (temp || methods == OPTAB_LIB)
+    return temp;
+
+  /* Must widen and use a lib call, use either signed or unsigned.  */
+  temp = expand_binop (mode, &wide_soptab, op0, op1, target,
+                      unsignedp, methods);
+  if (temp != 0)
+    return temp;
+  if (unsignedp)
+    return expand_binop (mode, uoptab, op0, op1, target,
+                        unsignedp, methods);
+  return 0;
+}
+\f
+/* Generate code to perform an operation specified by BINOPTAB
+   on operands OP0 and OP1, with two results to TARG1 and TARG2.
+   We assume that the order of the operands for the instruction
+   is TARG0, OP0, OP1, TARG1, which would fit a pattern like
+   [(set TARG0 (operate OP0 OP1)) (set TARG1 (operate ...))].
+
+   Either TARG0 or TARG1 may be zero, but what that means is that
+   that result is not actually wanted.  We will generate it into
+   a dummy pseudo-reg and discard it.  They may not both be zero.
+
+   Returns 1 if this operation can be performed; 0 if not.  */
+
+int
+expand_twoval_binop (binoptab, op0, op1, targ0, targ1, unsignedp)
+     optab binoptab;
+     rtx op0, op1;
+     rtx targ0, targ1;
+     int unsignedp;
+{
+  enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1);
+  enum mode_class class;
+  enum machine_mode wider_mode;
+  rtx last;
+
+  class = GET_MODE_CLASS (mode);
+
+  op0 = protect_from_queue (op0, 0);
+  op1 = protect_from_queue (op1, 0);
+
+  if (flag_force_mem)
+    {
+      op0 = force_not_mem (op0);
+      op1 = force_not_mem (op1);
+    }
+
+  /* If we are inside an appropriately-short loop and one operand is an
+     expensive constant, force it into a register.  */
+  if (CONSTANT_P (op0) && preserve_subexpressions_p () && rtx_cost (op0) > 2)
+    op0 = force_reg (mode, op0);
+
+  if (CONSTANT_P (op1) && preserve_subexpressions_p () && rtx_cost (op1) > 2)
+    op1 = force_reg (mode, op1);
+
+  if (targ0)
+    targ0 = protect_from_queue (targ0, 1);
+  else
+    targ0 = gen_reg_rtx (mode);
+  if (targ1)
+    targ1 = protect_from_queue (targ1, 1);
+  else
+    targ1 = gen_reg_rtx (mode);
+
+  /* Record where to go back to if we fail.  */
+  last = get_last_insn ();
+
+  if (binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+    {
+      int icode = (int) binoptab->handlers[(int) mode].insn_code;
+      enum machine_mode mode0 = insn_operand_mode[icode][1];
+      enum machine_mode mode1 = insn_operand_mode[icode][2];
+      rtx pat;
+      rtx xop0 = op0, xop1 = op1;
+
+      /* In case this insn wants input operands in modes different from the
+        result, convert the operands.  */
+      if (GET_MODE (op0) != VOIDmode && GET_MODE (op0) != mode0)
+       xop0 = convert_to_mode (mode0, xop0, unsignedp);
+
+      if (GET_MODE (op1) != VOIDmode && GET_MODE (op1) != mode1)
+       xop1 = convert_to_mode (mode1, xop1, unsignedp);
+
+      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
+      if (! (*insn_operand_predicate[icode][1]) (xop0, mode0))
+       xop0 = copy_to_mode_reg (mode0, xop0);
+
+      if (! (*insn_operand_predicate[icode][2]) (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.  */
+      if (! (*insn_operand_predicate[icode][0]) (targ0, mode)
+         || ! (*insn_operand_predicate[icode][3]) (targ1, mode))
+       abort ();
+       
+      pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1);
+      if (pat)
+       {
+         emit_insn (pat);
+         return 1;
+       }
+      else
+       delete_insns_since (last);
+    }
+
+  /* It can't be done in this mode.  Can we do it in a wider mode?  */
+
+  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+    {
+      for (wider_mode = GET_MODE_WIDER_MODE (mode);
+          GET_MODE_CLASS (wider_mode) == class;
+          wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+       {
+         if (binoptab->handlers[(int) wider_mode].insn_code
+             != CODE_FOR_nothing)
+           {
+             register rtx t0 = gen_reg_rtx (wider_mode);
+             register rtx t1 = gen_reg_rtx (wider_mode);
+
+             if (expand_twoval_binop (binoptab,
+                                      convert_to_mode (wider_mode, op0,
+                                                       unsignedp),
+                                      convert_to_mode (wider_mode, op1,
+                                                       unsignedp),
+                                      t0, t1, unsignedp))
+               {
+                 convert_move (targ0, t0, unsignedp);
+                 convert_move (targ1, t1, unsignedp);
+                 return 1;
+               }
+             else
+               delete_insns_since (last);
+           }
+       }
+    }
+
+  return 0;
+}
+\f
+/* Generate code to perform an operation specified by UNOPTAB
+   on operand OP0, with result having machine-mode MODE.
+
+   UNSIGNEDP is for the case where we have to widen the operands
+   to perform the operation.  It says to use zero-extension.
+
+   If TARGET is nonzero, the value
+   is generated there, if it is convenient to do so.
+   In all cases an rtx is returned for the locus of the value;
+   this may or may not be TARGET.  */
+
+rtx
+expand_unop (mode, unoptab, op0, target, unsignedp)
+     enum machine_mode mode;
+     optab unoptab;
+     rtx op0;
+     rtx target;
+     int unsignedp;
+{
+  enum mode_class class;
+  enum machine_mode wider_mode;
+  enum machine_mode submode = mode_for_size (BITS_PER_WORD, MODE_INT, 0);
+  register rtx temp;
+  rtx last = get_last_insn ();
+  rtx pat;
+
+  class = GET_MODE_CLASS (mode);
+
+  op0 = protect_from_queue (op0, 0);
+
+  if (flag_force_mem)
+    {
+      op0 = force_not_mem (op0);
+    }
+
+  if (target)
+    target = protect_from_queue (target, 1);
+
+  if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+    {
+      int icode = (int) unoptab->handlers[(int) mode].insn_code;
+      enum machine_mode mode0 = insn_operand_mode[icode][1];
+      rtx xop0 = op0;
+
+      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_operand_predicate[icode][1]) (xop0, mode0))
+       xop0 = copy_to_mode_reg (mode0, xop0);
+
+      if (! (*insn_operand_predicate[icode][0]) (temp, mode))
+       temp = gen_reg_rtx (mode);
+
+      pat = GEN_FCN (icode) (temp, xop0);
+      if (pat)
+       {
+         if (GET_CODE (pat) == SEQUENCE
+             && ! add_equal_note (pat, temp, unoptab->code, xop0, 0))
+           {
+             delete_insns_since (last);
+             return expand_unop (mode, unoptab, op0, 0, unsignedp);
+           }
+
+         emit_insn (pat);
+         
+         return temp;
+       }
+      else
+       delete_insns_since (last);
+    }
+
+  /* These can be done a word at a time.  */
+  if (unoptab == one_cmpl_optab
+      && class == MODE_INT
+      && GET_MODE_SIZE (mode) > UNITS_PER_WORD
+      && unoptab->handlers[(int) submode].insn_code != CODE_FOR_nothing)
+    {
+      int i;
+      rtx insns;
+
+      if (target == 0 || target == op0)
+       target = gen_reg_rtx (mode);
+
+      start_sequence ();
+
+      /* Do the actual arithmetic.  */
+      for (i = 0; i < GET_MODE_BITSIZE (mode) / BITS_PER_WORD; i++)
+       {
+         rtx target_piece = operand_subword (target, i, 1, mode);
+         rtx x = expand_unop (submode, unoptab,
+                              operand_subword_force (op0, i, mode),
+                              target_piece, unsignedp);
+         if (target_piece != x)
+           emit_move_insn (target_piece, x);
+       }
+
+      insns = get_insns ();
+      end_sequence ();
+
+      emit_no_conflict_block (insns, target, op0, 0,
+                             gen_rtx (unoptab->code, mode, op0));
+      return target;
+    }
+
+  if (unoptab->handlers[(int) mode].libfunc)
+    {
+      rtx insns;
+      rtx funexp = unoptab->handlers[(int) mode].libfunc;
+
+      start_sequence ();
+
+      /* Pass 1 for NO_QUEUE so we don't lose any increments
+        if the libcall is cse'd or moved.  */
+      emit_library_call (unoptab->handlers[(int) mode].libfunc,
+                        1, mode, 1, op0, mode);
+      insns = get_insns ();
+      end_sequence ();
+
+      target = gen_reg_rtx (mode);
+      emit_libcall_block (insns, target, hard_libcall_value (mode),
+                         gen_rtx (unoptab->code, mode, op0));
+
+      return target;
+    }
+
+  /* It can't be done in this mode.  Can we do it in a wider mode?  */
+
+  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+    {
+      for (wider_mode = GET_MODE_WIDER_MODE (mode);
+          GET_MODE_CLASS (wider_mode) == class;
+          wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+       {
+         if ((unoptab->handlers[(int) wider_mode].insn_code
+              != CODE_FOR_nothing)
+             || unoptab->handlers[(int) wider_mode].libfunc)
+           {
+             if (GET_MODE (op0) != VOIDmode
+                 && GET_MODE_BITSIZE (wider_mode) <= HOST_BITS_PER_INT)
+               {
+                 temp = gen_reg_rtx (wider_mode);
+                 convert_move (temp, op0, unsignedp);
+                 op0 = temp;
+               }
+             
+             target = expand_unop (wider_mode, unoptab, op0, 0, unsignedp);
+             if (class == MODE_FLOAT)
+               {
+                 if (target == 0)
+                   target = gen_reg_rtx (mode);
+                 convert_move (target, temp, 0);
+                 return target;
+               }
+             else
+               return gen_lowpart (mode, target);
+           }
+       }
+    }
+
+  return 0;
+}
+\f
+/* Generate an instruction whose insn-code is INSN_CODE,
+   with two operands: an output TARGET and an input OP0.
+   TARGET *must* be nonzero, and the output is always stored there.
+   CODE is an rtx code such that (CODE OP0) is an rtx that describes
+   the value that is stored into TARGET.  */
+
+void
+emit_unop_insn (icode, target, op0, code)
+     int icode;
+     rtx target;
+     rtx op0;
+     enum rtx_code code;
+{
+  register rtx temp;
+  enum machine_mode mode0 = insn_operand_mode[icode][1];
+  rtx pat;
+
+  temp = target = protect_from_queue (target, 1);
+
+  op0 = protect_from_queue (op0, 0);
+
+  if (flag_force_mem)
+    op0 = force_not_mem (op0);
+
+  /* Now, if insn does not accept our operands, put them into pseudos.  */
+
+  if (! (*insn_operand_predicate[icode][1]) (op0, mode0))
+    op0 = copy_to_mode_reg (mode0, op0);
+
+  if (! (*insn_operand_predicate[icode][0]) (temp, GET_MODE (temp))
+      || (flag_force_mem && GET_CODE (temp) == MEM))
+    temp = gen_reg_rtx (GET_MODE (temp));
+
+  pat = GEN_FCN (icode) (temp, op0);
+
+  if (GET_CODE (pat) == SEQUENCE && code != UNKNOWN)
+    add_equal_note (pat, temp, code, op0, 0);
+  
+  emit_insn (pat);
+
+  if (temp != target)
+    emit_move_insn (target, temp);
+}
+\f
+/* Emit code to perform a series of operations on a multi-word quantity, one
+   word at a time.
+
+   Such a block is preceeded by a CLOBBER of the output, consists of multiple
+   insns, each setting one word of the output, and followed by a SET copying
+   the output to itself.
+
+   Each of the insns setting words of the output receives a REG_NO_CONFLICT
+   note indicating that it doesn't conflict with the (also multi-word)
+   inputs.  The entire block is surrounded by REG_LIBCALL and REG_RETVAL
+   notes.
+
+   INSNS is a block of code generated to perform the operation, not including
+   the CLOBBER and final copy.  All insns that compute intermediate values
+   are first emitted, followed by the block as described above.  Only
+   INSNs are allowed in the block; no library calls or jumps may be
+   present.
+
+   TARGET, OP0, and OP1 are the output and inputs of the operations,
+   respectively.  OP1 may be zero for a unary operation.
+
+   EQUIV, if non-zero, is an expression to be placed into a REG_EQUAL note
+   on the last insn.
+
+   If TARGET is not a register, INSNS is simply emitted with no special
+   processing.
+
+   The final insn emitted is returned.  */
+
+rtx
+emit_no_conflict_block (insns, target, op0, op1, equiv)
+     rtx insns;
+     rtx target;
+     rtx op0, op1;
+     rtx equiv;
+{
+  rtx prev, next, first, last, insn;
+
+  if (GET_CODE (target) != REG || reload_in_progress)
+    return emit_insns (insns);
+
+  /* First emit all insns that do not store into words of the output and remove
+     these from the list.  */
+  for (insn = insns; insn; insn = next)
+    {
+      rtx set = 0;
+      int i;
+
+      next = NEXT_INSN (insn);
+
+      if (GET_CODE (insn) != INSN)
+       abort ();
+
+      if (GET_CODE (PATTERN (insn)) == SET)
+       set = PATTERN (insn);
+      else if (GET_CODE (PATTERN (insn)) == PARALLEL)
+       {
+         for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+           if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
+             {
+               set = XVECEXP (PATTERN (insn), 0, i);
+               break;
+             }
+       }
+
+      if (set == 0)
+       abort ();
+
+      if (! reg_overlap_mentioned_p (target, SET_DEST (set)))
+       {
+         if (PREV_INSN (insn))
+           NEXT_INSN (PREV_INSN (insn)) = next;
+         else
+           insns = next;
+
+         if (next)
+           PREV_INSN (next) = PREV_INSN (insn);
+
+         add_insn (insn);
+       }
+    }
+
+  prev = get_last_insn ();
+
+  /* Now write the CLOBBER of the output, followed by the setting of each
+     of the words, followed by the final copy.  */
+  if (target != op0 && target != op1)
+    emit_insn (gen_rtx (CLOBBER, VOIDmode, target));
+
+  for (insn = insns; insn; insn = next)
+    {
+      next = NEXT_INSN (insn);
+      add_insn (insn);
+
+      if (op1 && GET_CODE (op1) == REG)
+       REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_NO_CONFLICT, op1,
+                                   REG_NOTES (insn));
+
+      if (op0 && GET_CODE (op0) == REG)
+       REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_NO_CONFLICT, op0,
+                                   REG_NOTES (insn));
+    }
+
+  last = emit_move_insn (target, target);
+  if (equiv)
+    REG_NOTES (last) = gen_rtx (EXPR_LIST, REG_EQUAL, equiv, REG_NOTES (last));
+
+  if (prev == 0)
+    first = get_insns ();
+  else
+    first = NEXT_INSN (prev);
+
+  /* Encapsulate the block so it gets manipulated as a unit.  */
+  REG_NOTES (first) = gen_rtx (INSN_LIST, REG_LIBCALL, last,
+                              REG_NOTES (first));
+  REG_NOTES (last) = gen_rtx (INSN_LIST, REG_RETVAL, first, REG_NOTES (last));
+
+  return last;
+}
+\f
+/* Emit code to make a call to a constant function or a library call.
+
+   INSNS is a list containing all insns emitted in the call.
+   These insns leave the result in RESULT.  Our block is to copy RESULT
+   to TARGET, which is logically equivalent to EQUIV.
+
+   We first emit any insns that set a pseudo on the assumption that these are
+   loading constants into registers; doing so allows them to be safely cse'ed
+   between blocks.  Then we emit all the other insns in the block, followed by
+   an insn to move RESULT to TARGET.  This last insn will have a REQ_EQUAL
+   note with an operand of EQUIV.
+
+   Except for the first group of insns (the ones setting pseudos), the
+   block is delimited by REG_RETVAL and REG_LIBCALL notes.  */
+
+void
+emit_libcall_block (insns, target, result, equiv)
+     rtx insns;
+     rtx target;
+     rtx result;
+     rtx equiv;
+{
+  rtx prev, next, first, last, insn;
+
+  /* First emit all insns that set pseudos.  Remove them from the list as
+     we go.  */
+
+  for (insn = insns; insn; insn = next)
+    {
+      rtx set = single_set (insn);
+
+      next = NEXT_INSN (insn);
+
+      if (set != 0 && GET_CODE (SET_DEST (set)) == REG
+         && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER)
+       {
+         if (PREV_INSN (insn))
+           NEXT_INSN (PREV_INSN (insn)) = next;
+         else
+           insns = next;
+
+         if (next)
+           PREV_INSN (next) = PREV_INSN (insn);
+
+         add_insn (insn);
+       }
+    }
+
+  prev = get_last_insn ();
+
+  /* Write the remaining insns followed by the final copy.  */
+
+  for (insn = insns; insn; insn = next)
+    {
+      next = NEXT_INSN (insn);
+
+      add_insn (insn);
+    }
+
+  last = emit_move_insn (target, result);
+  REG_NOTES (last) = gen_rtx (EXPR_LIST, REG_EQUAL, equiv, REG_NOTES (last));
+
+  if (prev == 0)
+    first = get_insns ();
+  else
+    first = NEXT_INSN (prev);
+
+  /* Encapsulate the block so it gets manipulated as a unit.  */
+  REG_NOTES (first) = gen_rtx (INSN_LIST, REG_LIBCALL, last,
+                              REG_NOTES (first));
+  REG_NOTES (last) = gen_rtx (INSN_LIST, REG_RETVAL, first, REG_NOTES (last));
+}
+\f
+/* Generate code to store zero in X.  */
+
+void
+emit_clr_insn (x)
+     rtx x;
+{
+  emit_move_insn (x, const0_rtx);
+}
+
+/* Generate code to store 1 in X
+   assuming it contains zero beforehand.  */
+
+void
+emit_0_to_1_insn (x)
+     rtx x;
+{
+  emit_move_insn (x, const1_rtx);
+}
+
+/* Generate code to compare X with Y
+   so that the condition codes are set.
+
+   MODE is the mode of the inputs (in case they are const_int).
+   UNSIGNEDP nonzero says that X and Y are unsigned;
+   this matters if they need to be widened.
+
+   If they have mode BLKmode, then SIZE specifies the size of both X and Y,
+   and ALIGN specifies the known shared alignment of X and Y.
+
+   COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.).
+   It is ignored for fixed-point and block comparisons;
+   it is used only for floating-point comparisons.  */
+
+void
+emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
+     rtx x, y;
+     enum rtx_code comparison;
+     rtx size;
+     int unsignedp;
+     int align;
+{
+  enum mode_class class;
+  enum machine_mode wider_mode;
+
+  class = GET_MODE_CLASS (mode);
+
+  /* They could both be VOIDmode if both args are immediate constants,
+     but we should fold that at an earlier stage.
+     With no special code here, this will call abort,
+     reminding the programmer to implement such folding.  */
+
+  if (mode != BLKmode && flag_force_mem)
+    {
+      x = force_not_mem (x);
+      y = force_not_mem (y);
+    }
+
+  /* If we are inside an appropriately-short loop and one operand is an
+     expensive constant, force it into a register.  */
+  if (CONSTANT_P (x) && preserve_subexpressions_p () && rtx_cost (x) > 2)
+    x = force_reg (mode, x);
+
+  if (CONSTANT_P (y) && preserve_subexpressions_p () && rtx_cost (y) > 2)
+    y = force_reg (mode, y);
+
+  /* Don't let both operands fail to indicate the mode.  */
+  if (GET_MODE (x) == VOIDmode && GET_MODE (y) == VOIDmode)
+    x = force_reg (mode, x);
+
+  /* Handle all BLKmode compares.  */
+
+  if (mode == BLKmode)
+    {
+      emit_queue ();
+      x = protect_from_queue (x, 0);
+      y = protect_from_queue (y, 0);
+
+      if (size == 0)
+       abort ();
+#ifdef HAVE_cmpstrqi
+      if (HAVE_cmpstrqi
+         && GET_CODE (size) == CONST_INT
+         && INTVAL (size) < (1 << GET_MODE_BITSIZE (QImode)))
+       {
+         enum machine_mode result_mode
+           = insn_operand_mode[(int) CODE_FOR_cmpstrqi][0];
+         rtx result = gen_reg_rtx (result_mode);
+         emit_insn (gen_cmpstrqi (result, x, y, size,
+                                  gen_rtx (CONST_INT, VOIDmode, align)));
+         emit_cmp_insn (result, const0_rtx, comparison, 0, result_mode, 0, 0);
+       }
+      else
+#endif
+#ifdef HAVE_cmpstrhi
+      if (HAVE_cmpstrhi
+         && GET_CODE (size) == CONST_INT
+         && INTVAL (size) < (1 << GET_MODE_BITSIZE (HImode)))
+       {
+         enum machine_mode result_mode
+           = insn_operand_mode[(int) CODE_FOR_cmpstrhi][0];
+         rtx result = gen_reg_rtx (result_mode);
+         emit_insn (gen_cmpstrhi (result, x, y, size,
+                                  gen_rtx (CONST_INT, VOIDmode, align)));
+         emit_cmp_insn (result, const0_rtx, comparison, 0, result_mode, 0, 0);
+       }
+      else
+#endif
+#ifdef HAVE_cmpstrsi
+      if (HAVE_cmpstrsi)
+       {
+         enum machine_mode result_mode
+           = insn_operand_mode[(int) CODE_FOR_cmpstrsi][0];
+         rtx result = gen_reg_rtx (result_mode);
+         emit_insn (gen_cmpstrsi (result, x, y,
+                                  convert_to_mode (SImode, size, 1),
+                                  gen_rtx (CONST_INT, VOIDmode, align)));
+         emit_cmp_insn (result, const0_rtx, comparison, 0, result_mode, 0, 0);
+       }
+      else
+#endif
+       {
+#ifdef TARGET_MEM_FUNCTIONS
+         emit_library_call (memcmp_libfunc, 0,
+                            TYPE_MODE (integer_type_node), 3,
+                            XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
+                            size, Pmode);
+#else
+         emit_library_call (bcmp_libfunc, 0,
+                            TYPE_MODE (integer_type_node), 3,
+                            XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
+                            size, Pmode);
+#endif
+         emit_cmp_insn (hard_libcall_value (TYPE_MODE (integer_type_node)),
+                        const0_rtx, comparison, 0,
+                        TYPE_MODE (integer_type_node), 0, 0);
+       }
+      return;
+    }
+
+  /* Handle some compares against zero.  */
+
+  if (y == CONST0_RTX (mode)
+      && tst_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+    {
+      int icode = (int) tst_optab->handlers[(int) mode].insn_code;
+
+      emit_queue ();
+      x = protect_from_queue (x, 0);
+      y = protect_from_queue (y, 0);
+
+      /* Now, if insn does accept these operands, put them into pseudos.  */
+      if (! (*insn_operand_predicate[icode][0])
+         (x, insn_operand_mode[icode][0]))
+       x = copy_to_mode_reg (insn_operand_mode[icode][0], x);
+
+      emit_insn (GEN_FCN (icode) (x));
+      return;
+    }
+
+  /* Handle compares for which there is a directly suitable insn.  */
+
+  if (cmp_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+    {
+      int icode = (int) cmp_optab->handlers[(int) mode].insn_code;
+
+      emit_queue ();
+      x = protect_from_queue (x, 0);
+      y = protect_from_queue (y, 0);
+
+      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
+      if (! (*insn_operand_predicate[icode][0])
+         (x, insn_operand_mode[icode][0]))
+       x = copy_to_mode_reg (insn_operand_mode[icode][0], x);
+
+      if (! (*insn_operand_predicate[icode][1])
+         (y, insn_operand_mode[icode][1]))
+       y = copy_to_mode_reg (insn_operand_mode[icode][1], y);
+
+      emit_insn (GEN_FCN (icode) (x, y));
+      return;
+    }
+
+  /* Try widening if we can find a direct insn that way.  */
+
+  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+    {
+      for (wider_mode = GET_MODE_WIDER_MODE (mode);
+          GET_MODE_CLASS (wider_mode) == class;
+          wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+       {
+         if (cmp_optab->handlers[(int) wider_mode].insn_code
+             != CODE_FOR_nothing)
+           {
+             x = convert_to_mode (wider_mode, x, unsignedp);
+             y = convert_to_mode (wider_mode, y, unsignedp);
+             emit_cmp_insn (x, y, comparison, 0,
+                            wider_mode, unsignedp, align);
+             return;
+           }
+       }
+    }
+
+  /* Handle a lib call just for the mode we are using.  */
+
+  if (cmp_optab->handlers[(int) mode].libfunc
+      && class != MODE_FLOAT)
+    {
+      rtx libfunc = cmp_optab->handlers[(int) mode].libfunc;
+      /* If we want unsigned, and this mode has a distinct unsigned
+        comparison routine, use that.  */
+      if (unsignedp && ucmp_optab->handlers[(int) mode].libfunc)
+       libfunc = ucmp_optab->handlers[(int) mode].libfunc;
+
+      emit_library_call (libfunc, 0,
+                        SImode, 2, x, mode, y, mode);
+
+      /* Integer comparison returns a result that must be compared against 1,
+        so that even if we do an unsigned compare afterward,
+        there is still a value that can represent the result "less than".  */
+
+      emit_cmp_insn (hard_libcall_value (SImode), const1_rtx,
+                    comparison, 0, SImode, unsignedp, 0);
+      return;
+    }
+
+  if (class == MODE_FLOAT)
+    emit_float_lib_cmp (x, y, comparison);
+
+  else
+    abort ();
+}
+
+/* Nonzero if a compare of mode MODE can be done straightforwardly
+   (without splitting it into pieces).  */
+
+int
+can_compare_p (mode)
+     enum machine_mode mode;
+{
+  do
+    {
+      if (cmp_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing)
+       return 1;
+      mode = GET_MODE_WIDER_MODE (mode);
+    } while (mode != VOIDmode);
+
+  return 0;
+}
+\f
+/* Emit a library call comparison between floating point X and Y.
+   COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.).  */
+
+static void
+emit_float_lib_cmp (x, y, comparison)
+     rtx x, y;
+     enum rtx_code comparison;
+{
+  enum machine_mode mode = GET_MODE (x);
+  rtx libfunc;
+
+  if (mode == SFmode)
+    switch (comparison)
+      {
+      case EQ:
+       libfunc = eqsf2_libfunc;
+       break;
+
+      case NE:
+       libfunc = nesf2_libfunc;
+       break;
+
+      case GT:
+       libfunc = gtsf2_libfunc;
+       break;
+
+      case GE:
+       libfunc = gesf2_libfunc;
+       break;
+
+      case LT:
+       libfunc = ltsf2_libfunc;
+       break;
+
+      case LE:
+       libfunc = lesf2_libfunc;
+       break;
+      }
+  else if (mode == DFmode)
+    switch (comparison)
+      {
+      case EQ:
+       libfunc = eqdf2_libfunc;
+       break;
+
+      case NE:
+       libfunc = nedf2_libfunc;
+       break;
+
+      case GT:
+       libfunc = gtdf2_libfunc;
+       break;
+
+      case GE:
+       libfunc = gedf2_libfunc;
+       break;
+
+      case LT:
+       libfunc = ltdf2_libfunc;
+       break;
+
+      case LE:
+       libfunc = ledf2_libfunc;
+       break;
+      }
+  else
+    {
+      enum machine_mode wider_mode;
+
+      for (wider_mode = GET_MODE_WIDER_MODE (mode);
+          GET_MODE_CLASS (wider_mode) == MODE_FLOAT;
+          wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+       {
+         if ((cmp_optab->handlers[(int) wider_mode].insn_code
+              != CODE_FOR_nothing)
+             || (cmp_optab->handlers[(int) wider_mode].libfunc != 0))
+           {
+             x = convert_to_mode (wider_mode, x, 0);
+             y = convert_to_mode (wider_mode, y, 0);
+             emit_float_lib_cmp (x, y, comparison);
+             return;
+           }
+       }
+      abort ();
+    }
+
+  emit_library_call (libfunc, 0,
+                    SImode, 2, x, mode, y, mode);
+
+  emit_cmp_insn (hard_libcall_value (SImode), const0_rtx, comparison,
+                0, SImode, 0, 0);
+}
+\f
+/* Generate code to indirectly jump to a location given in the rtx LOC.  */
+
+void
+emit_indirect_jump (loc)
+     rtx loc;
+{
+  if (! ((*insn_operand_predicate[(int)CODE_FOR_indirect_jump][0])
+        (loc, VOIDmode)))
+    loc = copy_to_mode_reg (insn_operand_mode[(int)CODE_FOR_indirect_jump][0],
+                           loc);
+
+  emit_jump_insn (gen_indirect_jump (loc));
+}
+\f
+/* These three functions generate an insn body and return it
+   rather than emitting the insn.
+
+   They do not protect from queued increments,
+   because they may be used 1) in protect_from_queue itself
+   and 2) in other passes where there is no queue.  */
+
+/* Generate and return an insn body to add Y to X.  */
+
+rtx
+gen_add2_insn (x, y)
+     rtx x, y;
+{
+  int icode = (int) add_optab->handlers[(int) GET_MODE (x)].insn_code; 
+
+  if (! (*insn_operand_predicate[icode][0]) (x, insn_operand_mode[icode][0])
+      || ! (*insn_operand_predicate[icode][1]) (x, insn_operand_mode[icode][1])
+      || ! (*insn_operand_predicate[icode][2]) (y, insn_operand_mode[icode][2]))
+    abort ();
+
+  return (GEN_FCN (icode) (x, x, y));
+}
+
+int
+have_add2_insn (mode)
+     enum machine_mode mode;
+{
+  return add_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing;
+}
+
+/* Generate and return an insn body to subtract Y from X.  */
+
+rtx
+gen_sub2_insn (x, y)
+     rtx x, y;
+{
+  int icode = (int) sub_optab->handlers[(int) GET_MODE (x)].insn_code; 
+
+  if (! (*insn_operand_predicate[icode][0]) (x, insn_operand_mode[icode][0])
+      || ! (*insn_operand_predicate[icode][1]) (x, insn_operand_mode[icode][1])
+      || ! (*insn_operand_predicate[icode][2]) (y, insn_operand_mode[icode][2]))
+    abort ();
+
+  return (GEN_FCN (icode) (x, x, y));
+}
+
+int
+have_sub2_insn (mode)
+     enum machine_mode mode;
+{
+  return sub_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing;
+}
+
+/* Generate the body of an instruction to copy Y into X.  */
+
+rtx
+gen_move_insn (x, y)
+     rtx x, y;
+{
+  register enum machine_mode mode = GET_MODE (x);
+  enum insn_code insn_code;
+
+  if (mode == VOIDmode)
+    mode = GET_MODE (y); 
+
+  insn_code = mov_optab->handlers[(int) mode].insn_code;
+
+  /* Handle MODE_CC modes:  If we don't have a special move insn for this mode,
+     find a mode to do it in.  If we have a movcc, use it.  Otherwise,
+     find the MODE_INT mode of the same width.  */
+
+  if (insn_code == CODE_FOR_nothing)
+    {
+      enum machine_mode tmode = VOIDmode;
+      rtx x1 = x, y1 = y;
+
+      if (GET_MODE_CLASS (mode) == MODE_CC && mode != CCmode
+         && mov_optab->handlers[(int) CCmode].insn_code != CODE_FOR_nothing)
+       tmode = CCmode;
+      else if (GET_MODE_CLASS (mode) == MODE_CC)
+       for (tmode = QImode; tmode != VOIDmode;
+            tmode = GET_MODE_WIDER_MODE (tmode))
+         if (GET_MODE_SIZE (tmode) == GET_MODE_SIZE (mode))
+           break;
+
+      if (tmode == VOIDmode)
+       abort ();
+
+      /* Get X and Y in TMODE.  We can't use gen_lowpart here because it
+        may call change_address which is not appropriate if we were
+        called when a reload was in progress.  We don't have to worry
+        about changing the address since the size in bytes is supposed to
+        be the same.  Copy the MEM to change the mode and move any
+        substitutions from the old MEM to the new one.  */
+
+      if (reload_in_progress)
+       {
+         x = gen_lowpart_common (tmode, x1);
+         if (x == 0 && GET_CODE (x1) == MEM)
+           {
+             x = gen_rtx (MEM, tmode, XEXP (x1, 0));
+             RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (x1);
+             MEM_IN_STRUCT_P (x) = MEM_IN_STRUCT_P (x1);
+             MEM_VOLATILE_P (x) = MEM_VOLATILE_P (x1);
+             copy_replacements (x1, x);
+           }
+
+         y = gen_lowpart_common (tmode, y1);
+         if (y == 0 && GET_CODE (y1) == MEM)
+           {
+             y = gen_rtx (MEM, tmode, XEXP (y1, 0));
+             RTX_UNCHANGING_P (y) = RTX_UNCHANGING_P (y1);
+             MEM_IN_STRUCT_P (y) = MEM_IN_STRUCT_P (y1);
+             MEM_VOLATILE_P (y) = MEM_VOLATILE_P (y1);
+             copy_replacements (y1, y);
+           }
+       }
+      else
+       {
+         x = gen_lowpart (tmode, x);
+         y = gen_lowpart (tmode, y);
+       }
+         
+      insn_code = mov_optab->handlers[(int) tmode].insn_code;
+    }
+
+  return (GEN_FCN (insn_code) (x, y));
+}
+\f
+/* Tables of patterns for extending one integer mode to another.  */
+static enum insn_code zero_extend_codes[MAX_MACHINE_MODE][MAX_MACHINE_MODE];
+static enum insn_code sign_extend_codes[MAX_MACHINE_MODE][MAX_MACHINE_MODE];
+
+/* Return nonzero if it's possible to extend FROM_MODE to TO_MODE.
+   UNSIGNEDP specifies zero-extension instead of sign-extension.
+
+   Actually, the value is the instruction code for the extension pattern.  */
+
+int
+can_extend_p (to_mode, from_mode, unsignedp)
+     enum machine_mode to_mode, from_mode;
+     int unsignedp;
+{
+  return ((unsignedp ? zero_extend_codes : sign_extend_codes)
+         [(int) to_mode][(int) from_mode]);
+}
+
+/* Generate the body of an insn to extend Y (with mode MFROM)
+   into X (with mode MTO).  Do zero-extension if UNSIGNEDP is nonzero.  */
+
+rtx
+gen_extend_insn (x, y, mto, mfrom, unsignedp)
+     rtx x, y;
+     enum machine_mode mto, mfrom;
+     int unsignedp;
+{
+  return (GEN_FCN ((unsignedp ? zero_extend_codes : sign_extend_codes)
+                  [(int)mto][(int)mfrom])
+         (x, y));
+}
+
+static void
+init_extends ()
+{
+  bzero (sign_extend_codes, sizeof sign_extend_codes);
+  bzero (zero_extend_codes, sizeof zero_extend_codes);
+
+#ifdef HAVE_extendditi2
+  if (HAVE_extendditi2)
+    sign_extend_codes[(int) TImode][(int) DImode] = CODE_FOR_extendditi2;
+#endif
+#ifdef HAVE_extendsiti2
+  if (HAVE_extendsiti2)
+    sign_extend_codes[(int) TImode][(int) SImode] = CODE_FOR_extendsiti2;
+#endif
+#ifdef HAVE_extendhiti2
+  if (HAVE_extendhiti2)
+    sign_extend_codes[(int) TImode][(int) HImode] = CODE_FOR_extendhiti2;
+#endif
+#ifdef HAVE_extendqiti2
+  if (HAVE_extendqiti2)
+    sign_extend_codes[(int) TImode][(int) QImode] = CODE_FOR_extendqiti2;
+#endif
+#ifdef HAVE_extendsidi2
+  if (HAVE_extendsidi2)
+    sign_extend_codes[(int) DImode][(int) SImode] = CODE_FOR_extendsidi2;
+#endif
+#ifdef HAVE_extendhidi2
+  if (HAVE_extendhidi2)
+    sign_extend_codes[(int) DImode][(int) HImode] = CODE_FOR_extendhidi2;
+#endif
+#ifdef HAVE_extendqidi2
+  if (HAVE_extendqidi2)
+    sign_extend_codes[(int) DImode][(int) QImode] = CODE_FOR_extendqidi2;
+#endif
+#ifdef HAVE_extendhisi2
+  if (HAVE_extendhisi2)
+    sign_extend_codes[(int) SImode][(int) HImode] = CODE_FOR_extendhisi2;
+#endif
+#ifdef HAVE_extendqisi2
+  if (HAVE_extendqisi2)
+    sign_extend_codes[(int) SImode][(int) QImode] = CODE_FOR_extendqisi2;
+#endif
+#ifdef HAVE_extendqihi2
+  if (HAVE_extendqihi2)
+    sign_extend_codes[(int) HImode][(int) QImode] = CODE_FOR_extendqihi2;
+#endif
+
+#ifdef HAVE_zero_extendditi2
+  if (HAVE_zero_extendsiti2)
+    zero_extend_codes[(int) TImode][(int) DImode] = CODE_FOR_zero_extendditi2;
+#endif
+#ifdef HAVE_zero_extendsiti2
+  if (HAVE_zero_extendsiti2)
+    zero_extend_codes[(int) TImode][(int) SImode] = CODE_FOR_zero_extendsiti2;
+#endif
+#ifdef HAVE_zero_extendhiti2
+  if (HAVE_zero_extendhiti2)
+    zero_extend_codes[(int) TImode][(int) HImode] = CODE_FOR_zero_extendhiti2;
+#endif
+#ifdef HAVE_zero_extendqiti2
+  if (HAVE_zero_extendqiti2)
+    zero_extend_codes[(int) TImode][(int) QImode] = CODE_FOR_zero_extendqiti2;
+#endif
+#ifdef HAVE_zero_extendsidi2
+  if (HAVE_zero_extendsidi2)
+    zero_extend_codes[(int) DImode][(int) SImode] = CODE_FOR_zero_extendsidi2;
+#endif
+#ifdef HAVE_zero_extendhidi2
+  if (HAVE_zero_extendhidi2)
+    zero_extend_codes[(int) DImode][(int) HImode] = CODE_FOR_zero_extendhidi2;
+#endif
+#ifdef HAVE_zero_extendqidi2
+  if (HAVE_zero_extendqidi2)
+    zero_extend_codes[(int) DImode][(int) QImode] = CODE_FOR_zero_extendqidi2;
+#endif
+#ifdef HAVE_zero_extendhisi2
+  if (HAVE_zero_extendhisi2)
+    zero_extend_codes[(int) SImode][(int) HImode] = CODE_FOR_zero_extendhisi2;
+#endif
+#ifdef HAVE_zero_extendqisi2
+  if (HAVE_zero_extendqisi2)
+    zero_extend_codes[(int) SImode][(int) QImode] = CODE_FOR_zero_extendqisi2;
+#endif
+#ifdef HAVE_zero_extendqihi2
+  if (HAVE_zero_extendqihi2)
+    zero_extend_codes[(int) HImode][(int) QImode] = CODE_FOR_zero_extendqihi2;
+#endif
+}
+\f
+/* can_fix_p and can_float_p say whether the target machine
+   can directly convert a given fixed point type to
+   a given floating point type, or vice versa.
+   The returned value is the CODE_FOR_... value to use,
+   or CODE_FOR_nothing if these modes cannot be directly converted.  */
+
+static enum insn_code fixtab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+static enum insn_code fixtrunctab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+static enum insn_code floattab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+
+/* *TRUNCP_PTR is set to 1 if it is necessary to output
+   an explicit FTRUNC insn before the fix insn; otherwise 0.  */
+
+static enum insn_code
+can_fix_p (fixmode, fltmode, unsignedp, truncp_ptr)
+     enum machine_mode fltmode, fixmode;
+     int unsignedp;
+     int *truncp_ptr;
+{
+  *truncp_ptr = 0;
+  if (fixtrunctab[(int) fltmode][(int) fixmode][unsignedp] != CODE_FOR_nothing)
+    return fixtrunctab[(int) fltmode][(int) fixmode][unsignedp];
+
+  if (ftrunc_optab->handlers[(int) fltmode].insn_code != CODE_FOR_nothing)
+    {
+      *truncp_ptr = 1;
+      return fixtab[(int) fltmode][(int) fixmode][unsignedp];
+    }
+  return CODE_FOR_nothing;
+}
+
+static enum insn_code
+can_float_p (fltmode, fixmode, unsignedp)
+     enum machine_mode fixmode, fltmode;
+     int unsignedp;
+{
+  return floattab[(int) fltmode][(int) fixmode][unsignedp];
+}
+
+void
+init_fixtab ()
+{
+  enum insn_code *p;
+  for (p = fixtab[0][0];
+       p < fixtab[0][0] + sizeof fixtab / sizeof (fixtab[0][0][0]); 
+       p++)
+    *p = CODE_FOR_nothing;
+  for (p = fixtrunctab[0][0];
+       p < fixtrunctab[0][0] + sizeof fixtrunctab / sizeof (fixtrunctab[0][0][0]); 
+       p++)
+    *p = CODE_FOR_nothing;
+
+#ifdef HAVE_fixsfqi2
+  if (HAVE_fixsfqi2)
+    fixtab[(int) SFmode][(int) QImode][0] = CODE_FOR_fixsfqi2;
+#endif
+#ifdef HAVE_fixsfhi2
+  if (HAVE_fixsfhi2)
+    fixtab[(int) SFmode][(int) HImode][0] = CODE_FOR_fixsfhi2;
+#endif
+#ifdef HAVE_fixsfsi2
+  if (HAVE_fixsfsi2)
+    fixtab[(int) SFmode][(int) SImode][0] = CODE_FOR_fixsfsi2;
+#endif
+#ifdef HAVE_fixsfdi2
+  if (HAVE_fixsfdi2)
+    fixtab[(int) SFmode][(int) DImode][0] = CODE_FOR_fixsfdi2;
+#endif
+
+#ifdef HAVE_fixdfqi2
+  if (HAVE_fixdfqi2)
+    fixtab[(int) DFmode][(int) QImode][0] = CODE_FOR_fixdfqi2;
+#endif
+#ifdef HAVE_fixdfhi2
+  if (HAVE_fixdfhi2)
+    fixtab[(int) DFmode][(int) HImode][0] = CODE_FOR_fixdfhi2;
+#endif
+#ifdef HAVE_fixdfsi2
+  if (HAVE_fixdfsi2)
+    fixtab[(int) DFmode][(int) SImode][0] = CODE_FOR_fixdfsi2;
+#endif
+#ifdef HAVE_fixdfdi2
+  if (HAVE_fixdfdi2)
+    fixtab[(int) DFmode][(int) DImode][0] = CODE_FOR_fixdfdi2;
+#endif
+#ifdef HAVE_fixdfti2
+  if (HAVE_fixdfti2)
+    fixtab[(int) DFmode][(int) TImode][0] = CODE_FOR_fixdfti2;
+#endif
+
+#ifdef HAVE_fixtfqi2
+  if (HAVE_fixtfqi2)
+    fixtab[(int) TFmode][(int) QImode][0] = CODE_FOR_fixtfqi2;
+#endif
+#ifdef HAVE_fixtfhi2
+  if (HAVE_fixtfhi2)
+    fixtab[(int) TFmode][(int) HImode][0] = CODE_FOR_fixtfhi2;
+#endif
+#ifdef HAVE_fixtfsi2
+  if (HAVE_fixtfsi2)
+    fixtab[(int) TFmode][(int) SImode][0] = CODE_FOR_fixtfsi2;
+#endif
+#ifdef HAVE_fixtfdi2
+  if (HAVE_fixtfdi2)
+    fixtab[(int) TFmode][(int) DImode][0] = CODE_FOR_fixtfdi2;
+#endif
+#ifdef HAVE_fixtfti2
+  if (HAVE_fixtfti2)
+    fixtab[(int) TFmode][(int) TImode][0] = CODE_FOR_fixtfti2;
+#endif
+
+#ifdef HAVE_fixunssfqi2
+  if (HAVE_fixunssfqi2)
+    fixtab[(int) SFmode][(int) QImode][1] = CODE_FOR_fixunssfqi2;
+#endif
+#ifdef HAVE_fixunssfhi2
+  if (HAVE_fixunssfhi2)
+    fixtab[(int) SFmode][(int) HImode][1] = CODE_FOR_fixunssfhi2;
+#endif
+#ifdef HAVE_fixunssfsi2
+  if (HAVE_fixunssfsi2)
+    fixtab[(int) SFmode][(int) SImode][1] = CODE_FOR_fixunssfsi2;
+#endif
+#ifdef HAVE_fixunssfdi2
+  if (HAVE_fixunssfdi2)
+    fixtab[(int) SFmode][(int) DImode][1] = CODE_FOR_fixunssfdi2;
+#endif
+
+#ifdef HAVE_fixunsdfqi2
+  if (HAVE_fixunsdfqi2)
+    fixtab[(int) DFmode][(int) QImode][1] = CODE_FOR_fixunsdfqi2;
+#endif
+#ifdef HAVE_fixunsdfhi2
+  if (HAVE_fixunsdfhi2)
+    fixtab[(int) DFmode][(int) HImode][1] = CODE_FOR_fixunsdfhi2;
+#endif
+#ifdef HAVE_fixunsdfsi2
+  if (HAVE_fixunsdfsi2)
+    fixtab[(int) DFmode][(int) SImode][1] = CODE_FOR_fixunsdfsi2;
+#endif
+#ifdef HAVE_fixunsdfdi2
+  if (HAVE_fixunsdfdi2)
+    fixtab[(int) DFmode][(int) DImode][1] = CODE_FOR_fixunsdfdi2;
+#endif
+#ifdef HAVE_fixunsdfti2
+  if (HAVE_fixunsdfti2)
+    fixtab[(int) DFmode][(int) TImode][1] = CODE_FOR_fixunsdfti2;
+#endif
+
+#ifdef HAVE_fixunstfqi2
+  if (HAVE_fixunstfqi2)
+    fixtab[(int) TFmode][(int) QImode][1] = CODE_FOR_fixunstfqi2;
+#endif
+#ifdef HAVE_fixunstfhi2
+  if (HAVE_fixunstfhi2)
+    fixtab[(int) TFmode][(int) HImode][1] = CODE_FOR_fixunstfhi2;
+#endif
+#ifdef HAVE_fixunstfsi2
+  if (HAVE_fixunstfsi2)
+    fixtab[(int) TFmode][(int) SImode][1] = CODE_FOR_fixunstfsi2;
+#endif
+#ifdef HAVE_fixunstfdi2
+  if (HAVE_fixunstfdi2)
+    fixtab[(int) TFmode][(int) DImode][1] = CODE_FOR_fixunstfdi2;
+#endif
+#ifdef HAVE_fixunstfti2
+  if (HAVE_fixunstfti2)
+    fixtab[(int) TFmode][(int) TImode][1] = CODE_FOR_fixunstfti2;
+#endif
+
+#ifdef HAVE_fix_truncsfqi2
+  if (HAVE_fix_truncsfqi2)
+    fixtrunctab[(int) SFmode][(int) QImode][0] = CODE_FOR_fix_truncsfqi2;
+#endif
+#ifdef HAVE_fix_truncsfhi2
+  if (HAVE_fix_truncsfhi2)
+    fixtrunctab[(int) SFmode][(int) HImode][0] = CODE_FOR_fix_truncsfhi2;
+#endif
+#ifdef HAVE_fix_truncsfsi2
+  if (HAVE_fix_truncsfsi2)
+    fixtrunctab[(int) SFmode][(int) SImode][0] = CODE_FOR_fix_truncsfsi2;
+#endif
+#ifdef HAVE_fix_truncsfdi2
+  if (HAVE_fix_truncsfdi2)
+    fixtrunctab[(int) SFmode][(int) DImode][0] = CODE_FOR_fix_truncsfdi2;
+#endif
+
+#ifdef HAVE_fix_truncdfqi2
+  if (HAVE_fix_truncdfsi2)
+    fixtrunctab[(int) DFmode][(int) QImode][0] = CODE_FOR_fix_truncdfqi2;
+#endif
+#ifdef HAVE_fix_truncdfhi2
+  if (HAVE_fix_truncdfhi2)
+    fixtrunctab[(int) DFmode][(int) HImode][0] = CODE_FOR_fix_truncdfhi2;
+#endif
+#ifdef HAVE_fix_truncdfsi2
+  if (HAVE_fix_truncdfsi2)
+    fixtrunctab[(int) DFmode][(int) SImode][0] = CODE_FOR_fix_truncdfsi2;
+#endif
+#ifdef HAVE_fix_truncdfdi2
+  if (HAVE_fix_truncdfdi2)
+    fixtrunctab[(int) DFmode][(int) DImode][0] = CODE_FOR_fix_truncdfdi2;
+#endif
+#ifdef HAVE_fix_truncdfti2
+  if (HAVE_fix_truncdfti2)
+    fixtrunctab[(int) DFmode][(int) TImode][0] = CODE_FOR_fix_truncdfti2;
+#endif
+
+#ifdef HAVE_fix_trunctfqi2
+  if (HAVE_fix_trunctfqi2)
+    fixtrunctab[(int) TFmode][(int) QImode][0] = CODE_FOR_fix_trunctfqi2;
+#endif
+#ifdef HAVE_fix_trunctfhi2
+  if (HAVE_fix_trunctfhi2)
+    fixtrunctab[(int) TFmode][(int) HImode][0] = CODE_FOR_fix_trunctfhi2;
+#endif
+#ifdef HAVE_fix_trunctfsi2
+  if (HAVE_fix_trunctfsi2)
+    fixtrunctab[(int) TFmode][(int) SImode][0] = CODE_FOR_fix_trunctfsi2;
+#endif
+#ifdef HAVE_fix_trunctfdi2
+  if (HAVE_fix_trunctfdi2)
+    fixtrunctab[(int) TFmode][(int) DImode][0] = CODE_FOR_fix_trunctfdi2;
+#endif
+#ifdef HAVE_fix_trunctfti2
+  if (HAVE_fix_trunctfti2)
+    fixtrunctab[(int) TFmode][(int) TImode][0] = CODE_FOR_fix_trunctfti2;
+#endif
+
+#ifdef HAVE_fixuns_truncsfqi2
+  if (HAVE_fixuns_truncsfqi2)
+    fixtrunctab[(int) SFmode][(int) QImode][1] = CODE_FOR_fixuns_truncsfqi2;
+#endif
+#ifdef HAVE_fixuns_truncsfhi2
+  if (HAVE_fixuns_truncsfhi2)
+    fixtrunctab[(int) SFmode][(int) HImode][1] = CODE_FOR_fixuns_truncsfhi2;
+#endif
+#ifdef HAVE_fixuns_truncsfsi2
+  if (HAVE_fixuns_truncsfsi2)
+    fixtrunctab[(int) SFmode][(int) SImode][1] = CODE_FOR_fixuns_truncsfsi2;
+#endif
+#ifdef HAVE_fixuns_truncsfdi2
+  if (HAVE_fixuns_truncsfdi2)
+    fixtrunctab[(int) SFmode][(int) DImode][1] = CODE_FOR_fixuns_truncsfdi2;
+#endif
+
+#ifdef HAVE_fixuns_truncdfqi2
+  if (HAVE_fixuns_truncdfqi2)
+    fixtrunctab[(int) DFmode][(int) QImode][1] = CODE_FOR_fixuns_truncdfqi2;
+#endif
+#ifdef HAVE_fixuns_truncdfhi2
+  if (HAVE_fixuns_truncdfhi2)
+    fixtrunctab[(int) DFmode][(int) HImode][1] = CODE_FOR_fixuns_truncdfhi2;
+#endif
+#ifdef HAVE_fixuns_truncdfsi2
+  if (HAVE_fixuns_truncdfsi2)
+    fixtrunctab[(int) DFmode][(int) SImode][1] = CODE_FOR_fixuns_truncdfsi2;
+#endif
+#ifdef HAVE_fixuns_truncdfdi2
+  if (HAVE_fixuns_truncdfdi2)
+    fixtrunctab[(int) DFmode][(int) DImode][1] = CODE_FOR_fixuns_truncdfdi2;
+#endif
+#ifdef HAVE_fixuns_truncdfti2
+  if (HAVE_fixuns_truncdfti2)
+    fixtrunctab[(int) DFmode][(int) TImode][1] = CODE_FOR_fixuns_truncdfti2;
+#endif
+
+#ifdef HAVE_fixuns_trunctfqi2
+  if (HAVE_fixuns_trunctfqi2)
+    fixtrunctab[(int) TFmode][(int) QImode][1] = CODE_FOR_fixuns_trunctfqi2;
+#endif
+#ifdef HAVE_fixuns_trunctfhi2
+  if (HAVE_fixuns_trunctfhi2)
+    fixtrunctab[(int) TFmode][(int) HImode][1] = CODE_FOR_fixuns_trunctfhi2;
+#endif
+#ifdef HAVE_fixuns_trunctfsi2
+  if (HAVE_fixuns_trunctfsi2)
+    fixtrunctab[(int) TFmode][(int) SImode][1] = CODE_FOR_fixuns_trunctfsi2;
+#endif
+#ifdef HAVE_fixuns_trunctfdi2
+  if (HAVE_fixuns_trunctfdi2)
+    fixtrunctab[(int) TFmode][(int) DImode][1] = CODE_FOR_fixuns_trunctfdi2;
+#endif
+#ifdef HAVE_fixuns_trunctfti2
+  if (HAVE_fixuns_trunctfti2)
+    fixtrunctab[(int) TFmode][(int) TImode][1] = CODE_FOR_fixuns_trunctfti2;
+#endif
+
+#ifdef FIXUNS_TRUNC_LIKE_FIX_TRUNC
+  /* This flag says the same insns that convert to a signed fixnum
+     also convert validly to an unsigned one.  */
+  {
+    int i;
+    int j;
+    for (i = 0; i < NUM_MACHINE_MODES; i++)
+      for (j = 0; j < NUM_MACHINE_MODES; j++)
+       fixtrunctab[i][j][1] = fixtrunctab[i][j][0];
+  }
+#endif
+}
+
+void
+init_floattab ()
+{
+  enum insn_code *p;
+  for (p = floattab[0][0];
+       p < floattab[0][0] + sizeof floattab / sizeof (floattab[0][0][0]); 
+       p++)
+    *p = CODE_FOR_nothing;
+
+#ifdef HAVE_floatqisf2
+  if (HAVE_floatqisf2)
+    floattab[(int) SFmode][(int) QImode][0] = CODE_FOR_floatqisf2;
+#endif
+#ifdef HAVE_floathisf2
+  if (HAVE_floathisf2)
+    floattab[(int) SFmode][(int) HImode][0] = CODE_FOR_floathisf2;
+#endif
+#ifdef HAVE_floatsisf2
+  if (HAVE_floatsisf2)
+    floattab[(int) SFmode][(int) SImode][0] = CODE_FOR_floatsisf2;
+#endif
+#ifdef HAVE_floatdisf2
+  if (HAVE_floatdisf2)
+    floattab[(int) SFmode][(int) DImode][0] = CODE_FOR_floatdisf2;
+#endif
+#ifdef HAVE_floattisf2
+  if (HAVE_floattisf2)
+    floattab[(int) SFmode][(int) TImode][0] = CODE_FOR_floattisf2;
+#endif
+
+#ifdef HAVE_floatqidf2
+  if (HAVE_floatqidf2)
+    floattab[(int) DFmode][(int) QImode][0] = CODE_FOR_floatqidf2;
+#endif
+#ifdef HAVE_floathidf2
+  if (HAVE_floathidf2)
+    floattab[(int) DFmode][(int) HImode][0] = CODE_FOR_floathidf2;
+#endif
+#ifdef HAVE_floatsidf2
+  if (HAVE_floatsidf2)
+    floattab[(int) DFmode][(int) SImode][0] = CODE_FOR_floatsidf2;
+#endif
+#ifdef HAVE_floatdidf2
+  if (HAVE_floatdidf2)
+    floattab[(int) DFmode][(int) DImode][0] = CODE_FOR_floatdidf2;
+#endif
+#ifdef HAVE_floattidf2
+  if (HAVE_floattidf2)
+    floattab[(int) DFmode][(int) TImode][0] = CODE_FOR_floattidf2;
+#endif
+
+#ifdef HAVE_floatqitf2
+  if (HAVE_floatqitf2)
+    floattab[(int) TFmode][(int) QImode][0] = CODE_FOR_floatqitf2;
+#endif
+#ifdef HAVE_floathitf2
+  if (HAVE_floathitf2)
+    floattab[(int) TFmode][(int) HImode][0] = CODE_FOR_floathitf2;
+#endif
+#ifdef HAVE_floatsitf2
+  if (HAVE_floatsitf2)
+    floattab[(int) TFmode][(int) SImode][0] = CODE_FOR_floatsitf2;
+#endif
+#ifdef HAVE_floatditf2
+  if (HAVE_floatditf2)
+    floattab[(int) TFmode][(int) DImode][0] = CODE_FOR_floatditf2;
+#endif
+#ifdef HAVE_floattitf2
+  if (HAVE_floattitf2)
+    floattab[(int) TFmode][(int) TImode][0] = CODE_FOR_floattitf2;
+#endif
+
+#ifdef HAVE_floatunsqisf2
+  if (HAVE_floatunsqisf2)
+    floattab[(int) SFmode][(int) QImode][1] = CODE_FOR_floatunsqisf2;
+#endif
+#ifdef HAVE_floatunshisf2
+  if (HAVE_floatunshisf2)
+    floattab[(int) SFmode][(int) HImode][1] = CODE_FOR_floatunshisf2;
+#endif
+#ifdef HAVE_floatunssisf2
+  if (HAVE_floatunssisf2)
+    floattab[(int) SFmode][(int) SImode][1] = CODE_FOR_floatunssisf2;
+#endif
+#ifdef HAVE_floatunsdisf2
+  if (HAVE_floatunsdisf2)
+    floattab[(int) SFmode][(int) DImode][1] = CODE_FOR_floatunsdisf2;
+#endif
+#ifdef HAVE_floatunstisf2
+  if (HAVE_floatunstisf2)
+    floattab[(int) SFmode][(int) TImode][1] = CODE_FOR_floatunstisf2;
+#endif
+
+#ifdef HAVE_floatunsqidf2
+  if (HAVE_floatunsqidf2)
+    floattab[(int) DFmode][(int) QImode][1] = CODE_FOR_floatunsqidf2;
+#endif
+#ifdef HAVE_floatunshidf2
+  if (HAVE_floatunshidf2)
+    floattab[(int) DFmode][(int) HImode][1] = CODE_FOR_floatunshidf2;
+#endif
+#ifdef HAVE_floatunssidf2
+  if (HAVE_floatunssidf2)
+    floattab[(int) DFmode][(int) SImode][1] = CODE_FOR_floatunssidf2;
+#endif
+#ifdef HAVE_floatunsdidf2
+  if (HAVE_floatunsdidf2)
+    floattab[(int) DFmode][(int) DImode][1] = CODE_FOR_floatunsdidf2;
+#endif
+#ifdef HAVE_floatunstidf2
+  if (HAVE_floatunstidf2)
+    floattab[(int) DFmode][(int) TImode][1] = CODE_FOR_floatunstidf2;
+#endif
+
+#ifdef HAVE_floatunsqitf2
+  if (HAVE_floatunsqitf2)
+    floattab[(int) TFmode][(int) QImode][1] = CODE_FOR_floatunsqitf2;
+#endif
+#ifdef HAVE_floatunshitf2
+  if (HAVE_floatunshitf2)
+    floattab[(int) TFmode][(int) HImode][1] = CODE_FOR_floatunshitf2;
+#endif
+#ifdef HAVE_floatunssitf2
+  if (HAVE_floatunssitf2)
+    floattab[(int) TFmode][(int) SImode][1] = CODE_FOR_floatunssitf2;
+#endif
+#ifdef HAVE_floatunsditf2
+  if (HAVE_floatunsditf2)
+    floattab[(int) TFmode][(int) DImode][1] = CODE_FOR_floatunsditf2;
+#endif
+#ifdef HAVE_floatunstitf2
+  if (HAVE_floatunstitf2)
+    floattab[(int) TFmode][(int) TImode][1] = CODE_FOR_floatunstitf2;
+#endif
+}
+\f
+/* Generate code to convert FROM to floating point
+   and store in TO.  FROM must be fixed point.
+   UNSIGNEDP nonzero means regard FROM as unsigned.
+   Normally this is done by correcting the final value
+   if it is negative.  */
+
+void
+expand_float (to, from, unsignedp)
+     rtx to, from;
+     int unsignedp;
+{
+  enum insn_code icode;
+  register rtx target = to;
+  enum machine_mode fmode, imode;
+
+  /* Look for an insn to do the conversion.  Do it in the specified
+     modes if possible; otherwise convert either input, output or both to
+     wider mode.  If the integer mode is wider than the mode of FROM,
+     we can do the conversion signed even if the input is unsigned.  */
+
+  for (imode = GET_MODE (from); imode != VOIDmode;
+       imode = GET_MODE_WIDER_MODE (imode))
+    for (fmode = GET_MODE (to); fmode != VOIDmode;
+        fmode = GET_MODE_WIDER_MODE (fmode))
+      {
+       int doing_unsigned = unsignedp;
+
+       icode = can_float_p (fmode, imode, unsignedp);
+       if (icode == CODE_FOR_nothing && imode != GET_MODE (from) && unsignedp)
+         icode = can_float_p (fmode, imode, 0), doing_unsigned = 0;
+
+       if (icode != CODE_FOR_nothing)
+         {
+           to = protect_from_queue (to, 1);
+
+           if (imode != GET_MODE (from))
+             from = convert_to_mode (imode, from, unsignedp);
+           else
+             from = protect_from_queue (from, 0);
+
+           if (fmode != GET_MODE (to))
+             target = gen_reg_rtx (fmode);
+
+           emit_unop_insn (icode, target, from,
+                           doing_unsigned ? UNSIGNED_FLOAT : FLOAT);
+
+           if (target != to)
+             convert_move (to, target, 0);
+           return;
+         }
+    }
+
+#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+
+  /* Unsigned integer, and no way to convert directly.
+     Convert as signed, then conditionally adjust the result.  */
+  if (unsignedp)
+    {
+      rtx label = gen_label_rtx ();
+      rtx temp;
+      REAL_VALUE_TYPE offset;
+
+      emit_queue ();
+
+      to = protect_from_queue (to, 1);
+      from = protect_from_queue (from, 0);
+
+      if (flag_force_mem)
+       from = force_not_mem (from);
+
+      /* If we are about to do some arithmetic to correct for an
+        unsigned operand, do it in a pseudo-register.  */
+
+      if (GET_CODE (to) != REG || REGNO (to) <= LAST_VIRTUAL_REGISTER)
+       target = gen_reg_rtx (GET_MODE (to));
+
+      /* Convert as signed integer to floating.  */
+      expand_float (target, from, 0);
+
+      /* If FROM is negative (and therefore TO is negative),
+        correct its value by 2**bitwidth.  */
+
+      do_pending_stack_adjust ();
+      emit_cmp_insn (from, const0_rtx, GE, 0, GET_MODE (from), 0, 0);
+      emit_jump_insn (gen_bge (label));
+      /* On SCO 3.2.1, ldexp rejects values outside [0.5, 1).
+        Rather than setting up a dconst_dot_5, let's hope SCO
+        fixes the bug.  */
+      offset = REAL_VALUE_LDEXP (dconst1, GET_MODE_BITSIZE (GET_MODE (from)));
+      temp = expand_binop (GET_MODE (to), add_optab, target,
+                          immed_real_const_1 (offset, GET_MODE (to)),
+                          target, 0, OPTAB_LIB_WIDEN);
+      if (temp != target)
+       emit_move_insn (target, temp);
+      do_pending_stack_adjust ();
+      emit_label (label);
+    }
+  else
+#endif
+
+  /* No hardware instruction available; call a library
+     to convert from SImode or DImode into SFmode or DFmode.  */
+    {
+      char *fnname;
+      rtx insns;
+
+      to = protect_from_queue (to, 1);
+
+      if (GET_MODE_SIZE (GET_MODE (from)) < GET_MODE_SIZE (SImode))
+       from = convert_to_mode (SImode, from, unsignedp);
+      else
+       from = protect_from_queue (from, 0);
+
+      if (flag_force_mem)
+       from = force_not_mem (from);
+
+      if (GET_MODE (to) == SFmode)
+       {
+         if (GET_MODE (from) == SImode)
+           fnname = "__floatsisf";
+         else if (GET_MODE (from) == DImode)
+           fnname = "__floatdisf";
+         else
+           abort ();
+       }
+      else if (GET_MODE (to) == DFmode)
+       {
+         if (GET_MODE (from) == SImode)
+           fnname = "__floatsidf";
+         else if (GET_MODE (from) == DImode)
+           fnname = "__floatdidf";
+         else
+           abort ();
+       }
+      else
+       abort ();
+
+      start_sequence ();
+
+      emit_library_call (gen_rtx (SYMBOL_REF, Pmode, fnname),
+                              0, GET_MODE (to), 1, from, GET_MODE (from));
+      insns = get_insns ();
+      end_sequence ();
+
+      emit_libcall_block (insns, target, hard_libcall_value (GET_MODE (to)),
+                         gen_rtx (FLOAT, GET_MODE (to), from));
+    }
+
+  /* Copy result to requested destination
+     if we have been computing in a temp location.  */
+
+  if (target != to)
+    {
+      if (GET_MODE (target) == GET_MODE (to))
+       emit_move_insn (to, target);
+      else
+       convert_move (to, target, 0);
+    }
+}
+\f
+/* expand_fix: generate code to convert FROM to fixed point
+   and store in TO.  FROM must be floating point.  */
+
+static rtx
+ftruncify (x)
+     rtx x;
+{
+  rtx temp = gen_reg_rtx (GET_MODE (x));
+  return expand_unop (GET_MODE (x), ftrunc_optab, x, temp, 0);
+}
+
+void
+expand_fix (to, from, unsignedp)
+     register rtx to, from;
+     int unsignedp;
+{
+  enum insn_code icode;
+  register rtx target = to;
+  enum machine_mode fmode, imode;
+  int must_trunc = 0;
+  char *fnname = 0;
+
+  /* We first try to find a pair of modes, one real and one integer, at
+     least as wide as FROM and TO, respectively, in which we can open-code
+     this conversion.  If the integer mode is wider than the mode of TO,
+     we can do the conversion either signed or unsigned.  */
+
+  for (imode = GET_MODE (to); imode != VOIDmode;
+       imode = GET_MODE_WIDER_MODE (imode))
+    for (fmode = GET_MODE (from); fmode != VOIDmode;
+        fmode = GET_MODE_WIDER_MODE (fmode))
+      {
+       int doing_unsigned = unsignedp;
+
+       icode = can_fix_p (imode, fmode, unsignedp, &must_trunc);
+       if (icode == CODE_FOR_nothing && imode != GET_MODE (to) && unsignedp)
+         icode = can_fix_p (imode, fmode, 0, &must_trunc), doing_unsigned = 0;
+
+       if (icode != CODE_FOR_nothing)
+         {
+           to = protect_from_queue (to, 1);
+
+           if (fmode != GET_MODE (from))
+             from = convert_to_mode (fmode, from, 0);
+           else
+             from = protect_from_queue (from, 0);
+
+           if (must_trunc)
+             from = ftruncify (from);
+
+           if (imode != GET_MODE (to))
+             target = gen_reg_rtx (imode);
+
+           emit_unop_insn (icode, target, from,
+                           doing_unsigned ? UNSIGNED_FIX : FIX);
+           if (target != to)
+             convert_move (to, target, unsignedp);
+           return;
+         }
+      }
+
+#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
+  /* For an unsigned conversion, there is one more way to do it.
+     If we have a signed conversion, we generate code that compares
+     the real value to the largest representable positive number.  If if
+     is smaller, the conversion is done normally.  Otherwise, subtract
+     one plus the highest signed number, convert, and add it back.
+
+     We only need to check all real modes, since we know we didn't find
+     anything with a wider inetger mode.  */
+
+  if (unsignedp && GET_MODE_BITSIZE (GET_MODE (to)) <= HOST_BITS_PER_INT)
+    for (fmode = GET_MODE (from); fmode != VOIDmode;
+        fmode = GET_MODE_WIDER_MODE (fmode))
+      /* Make sure we won't lose significant bits doing this.  */
+      if (GET_MODE_BITSIZE (fmode) > GET_MODE_BITSIZE (GET_MODE (to))
+         && CODE_FOR_nothing != can_fix_p (GET_MODE (to), fmode, 0,
+                                           &must_trunc))
+       {
+         int bitsize = GET_MODE_BITSIZE (GET_MODE (to));
+         REAL_VALUE_TYPE offset = REAL_VALUE_LDEXP (dconst1, bitsize - 1);
+         rtx limit = immed_real_const_1 (offset, fmode);
+         rtx lab1 = gen_label_rtx ();
+         rtx lab2 = gen_label_rtx ();
+         rtx insn;
+
+         emit_queue ();
+         to = protect_from_queue (to, 1);
+         from = protect_from_queue (from, 0);
+
+         if (flag_force_mem)
+           from = force_not_mem (from);
+
+         if (fmode != GET_MODE (from))
+           from = convert_to_mode (fmode, from, 0);
+
+         /* See if we need to do the subtraction.  */
+         do_pending_stack_adjust ();
+         emit_cmp_insn (from, limit, GE, 0, GET_MODE (from), 0, 0);
+         emit_jump_insn (gen_bge (lab1));
+
+         /* If not, do the signed "fix" and branch around fixup code.  */
+         expand_fix (to, from, 0);
+         emit_jump_insn (gen_jump (lab2));
+         emit_barrier ();
+
+         /* Otherwise, subtract 2**(N-1), convert to signed number,
+            then add 2**(N-1).  Do the addition using XOR since this
+            will often generate better code.  */
+         emit_label (lab1);
+         target = expand_binop (GET_MODE (from), sub_optab, from, limit,
+                                0, 0, OPTAB_LIB_WIDEN);
+         expand_fix (to, target, 0);
+         target = expand_binop (GET_MODE (to), xor_optab, to,
+                                gen_rtx (CONST_INT, VOIDmode,
+                                       1 << (bitsize - 1)),
+                                to, 1, OPTAB_LIB_WIDEN);
+
+         if (target != to)
+           emit_move_insn (to, target);
+
+         emit_label (lab2);
+
+         /* Make a place for a REG_NOTE and add it.  */
+         insn = emit_move_insn (to, to);
+         REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL,
+                                     gen_rtx (UNSIGNED_FIX, GET_MODE (to),
+                                              from), REG_NOTES (insn));
+
+         return;
+       }
+#endif
+
+  /* We can't do it with an insn, so use a library call.  But first ensure
+     that the mode of TO is at least as wide as SImode, since those are the
+     only library calls we know about.  */
+
+  if (GET_MODE_SIZE (GET_MODE (to)) < GET_MODE_SIZE (SImode))
+    {
+      target = gen_reg_rtx (SImode);
+
+      expand_fix (target, from, unsignedp);
+    }
+  else if (GET_MODE (from) == SFmode)
+    {
+      if (GET_MODE (to) == SImode)
+       fnname = unsignedp ? "__fixunssfsi" : "__fixsfsi";
+      else if (GET_MODE (to) == DImode)
+       fnname = unsignedp ? "__fixunssfdi" : "__fixsfdi";
+      else
+       abort ();
+    }
+  else if (GET_MODE (from) == DFmode)
+    {
+      if (GET_MODE (to) == SImode)
+       fnname = unsignedp ? "__fixunsdfsi" : "__fixdfsi";
+      else if (GET_MODE (to) == DImode)
+       fnname = unsignedp ? "__fixunsdfdi" : "__fixdfdi";
+      else
+       abort ();
+    }
+  else
+    abort ();
+
+  if (fnname)
+    {
+      rtx insns;
+
+      to = protect_from_queue (to, 1);
+      from = protect_from_queue (from, 0);
+
+      if (flag_force_mem)
+       from = force_not_mem (from);
+
+      start_sequence ();
+
+      emit_library_call (gen_rtx (SYMBOL_REF, Pmode, fnname),
+                              0, GET_MODE (to), 1, from, GET_MODE (from));
+      insns = get_insns ();
+      end_sequence ();
+
+      emit_libcall_block (insns, target, hard_libcall_value (GET_MODE (to)),
+                         gen_rtx (unsignedp ? FIX : UNSIGNED_FIX,
+                                  GET_MODE (to), from));
+    }
+      
+  if (GET_MODE (to) == GET_MODE (target))
+    emit_move_insn (to, target);
+  else
+    convert_move (to, target, 0);
+}
+\f
+static optab
+init_optab (code)
+     enum rtx_code code;
+{
+  int i;
+  optab op = (optab) xmalloc (sizeof (struct optab));
+  op->code = code;
+  for (i = 0; i < NUM_MACHINE_MODES; i++)
+    {
+      op->handlers[i].insn_code = CODE_FOR_nothing;
+      op->handlers[i].libfunc = 0;
+    }
+  return op;
+}
+
+/* Call this once to initialize the contents of the optabs
+   appropriately for the current target machine.  */
+
+void
+init_optabs ()
+{
+  int i;
+
+  init_fixtab ();
+  init_floattab ();
+  init_extends ();
+
+  add_optab = init_optab (PLUS);
+  sub_optab = init_optab (MINUS);
+  smul_optab = init_optab (MULT);
+  smul_widen_optab = init_optab (UNKNOWN);
+  umul_widen_optab = init_optab (UNKNOWN);
+  sdiv_optab = init_optab (DIV);
+  sdivmod_optab = init_optab (UNKNOWN);
+  udiv_optab = init_optab (UDIV);
+  udivmod_optab = init_optab (UNKNOWN);
+  smod_optab = init_optab (MOD);
+  umod_optab = init_optab (UMOD);
+  flodiv_optab = init_optab (DIV);
+  ftrunc_optab = init_optab (UNKNOWN);
+  and_optab = init_optab (AND);
+  ior_optab = init_optab (IOR);
+  xor_optab = init_optab (XOR);
+  ashl_optab = init_optab (ASHIFT);
+  ashr_optab = init_optab (ASHIFTRT);
+  lshl_optab = init_optab (LSHIFT);
+  lshr_optab = init_optab (LSHIFTRT);
+  rotl_optab = init_optab (ROTATE);
+  rotr_optab = init_optab (ROTATERT);
+  smin_optab = init_optab (SMIN);
+  smax_optab = init_optab (SMAX);
+  umin_optab = init_optab (UMIN);
+  umax_optab = init_optab (UMAX);
+  mov_optab = init_optab (UNKNOWN);
+  movstrict_optab = init_optab (UNKNOWN);
+  cmp_optab = init_optab (UNKNOWN);
+  ucmp_optab = init_optab (UNKNOWN);
+  tst_optab = init_optab (UNKNOWN);
+  neg_optab = init_optab (NEG);
+  abs_optab = init_optab (ABS);
+  one_cmpl_optab = init_optab (NOT);
+  ffs_optab = init_optab (FFS);
+
+#ifdef HAVE_addqi3
+  if (HAVE_addqi3)
+    add_optab->handlers[(int) QImode].insn_code = CODE_FOR_addqi3;
+#endif
+#ifdef HAVE_addhi3
+  if (HAVE_addhi3)
+    add_optab->handlers[(int) HImode].insn_code = CODE_FOR_addhi3;
+#endif
+#ifdef HAVE_addpsi3
+  if (HAVE_addpsi3)
+    add_optab->handlers[(int) PSImode].insn_code = CODE_FOR_addpsi3;
+#endif
+#ifdef HAVE_addsi3
+  if (HAVE_addsi3)
+    add_optab->handlers[(int) SImode].insn_code = CODE_FOR_addsi3;
+#endif
+#ifdef HAVE_adddi3
+  if (HAVE_adddi3)
+    add_optab->handlers[(int) DImode].insn_code = CODE_FOR_adddi3;
+#endif
+#ifdef HAVE_addti3
+  if (HAVE_addti3)
+    add_optab->handlers[(int) TImode].insn_code = CODE_FOR_addti3;
+#endif
+#ifdef HAVE_addsf3
+  if (HAVE_addsf3)
+    add_optab->handlers[(int) SFmode].insn_code = CODE_FOR_addsf3;
+#endif
+#ifdef HAVE_adddf3
+  if (HAVE_adddf3)
+    add_optab->handlers[(int) DFmode].insn_code = CODE_FOR_adddf3;
+#endif
+#ifdef HAVE_addtf3
+  if (HAVE_addtf3)
+    add_optab->handlers[(int) TFmode].insn_code = CODE_FOR_addtf3;
+#endif
+  add_optab->handlers[(int) SFmode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__addsf3");
+  add_optab->handlers[(int) DFmode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__adddf3");
+
+#ifdef HAVE_subqi3
+  if (HAVE_subqi3)
+    sub_optab->handlers[(int) QImode].insn_code = CODE_FOR_subqi3;
+#endif
+#ifdef HAVE_subhi3
+  if (HAVE_subhi3)
+    sub_optab->handlers[(int) HImode].insn_code = CODE_FOR_subhi3;
+#endif
+#ifdef HAVE_subpsi3
+  if (HAVE_subpsi3)
+    sub_optab->handlers[(int) PSImode].insn_code = CODE_FOR_subpsi3;
+#endif
+#ifdef HAVE_subsi3
+  if (HAVE_subsi3)
+    sub_optab->handlers[(int) SImode].insn_code = CODE_FOR_subsi3;
+#endif
+#ifdef HAVE_subdi3
+  if (HAVE_subdi3)
+    sub_optab->handlers[(int) DImode].insn_code = CODE_FOR_subdi3;
+#endif
+#ifdef HAVE_subti3
+  if (HAVE_subti3)
+    sub_optab->handlers[(int) Imode].insn_code = CODE_FOR_subti3;
+#endif
+#ifdef HAVE_subsf3
+  if (HAVE_subsf3)
+    sub_optab->handlers[(int) SFmode].insn_code = CODE_FOR_subsf3;
+#endif
+#ifdef HAVE_subdf3
+  if (HAVE_subdf3)
+    sub_optab->handlers[(int) DFmode].insn_code = CODE_FOR_subdf3;
+#endif
+#ifdef HAVE_subtf3
+  if (HAVE_subtf3)
+    sub_optab->handlers[(int) TFmode].insn_code = CODE_FOR_subtf3;
+#endif
+  sub_optab->handlers[(int) SFmode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__subsf3");
+  sub_optab->handlers[(int) DFmode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__subdf3");
+
+#ifdef HAVE_mulqi3
+  if (HAVE_mulqi3)
+    smul_optab->handlers[(int) QImode].insn_code = CODE_FOR_mulqi3;
+#endif
+#ifdef HAVE_mulhi3
+  if (HAVE_mulhi3)
+    smul_optab->handlers[(int) HImode].insn_code = CODE_FOR_mulhi3;
+#endif
+#ifdef HAVE_mulpsi3
+  if (HAVE_mulpsi3)
+    smul_optab->handlers[(int) PSImode].insn_code = CODE_FOR_mulpsi3;
+#endif
+#ifdef HAVE_mulsi3
+  if (HAVE_mulsi3)
+    smul_optab->handlers[(int) SImode].insn_code = CODE_FOR_mulsi3;
+#endif
+#ifdef HAVE_muldi3
+  if (HAVE_muldi3)
+    smul_optab->handlers[(int) DImode].insn_code = CODE_FOR_muldi3;
+#endif
+#ifdef HAVE_multi3
+  if (HAVE_multi3)
+    smul_optab->handlers[(int) TImode].insn_code = CODE_FOR_multi3;
+#endif
+#ifdef HAVE_mulsf3
+  if (HAVE_mulsf3)
+    smul_optab->handlers[(int) SFmode].insn_code = CODE_FOR_mulsf3;
+#endif
+#ifdef HAVE_muldf3
+  if (HAVE_muldf3)
+    smul_optab->handlers[(int) DFmode].insn_code = CODE_FOR_muldf3;
+#endif
+#ifdef HAVE_multf3
+  if (HAVE_multf3)
+    smul_optab->handlers[(int) TFmode].insn_code = CODE_FOR_multf3;
+#endif
+
+#ifdef MULSI3_LIBCALL
+  smul_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, MULSI3_LIBCALL);
+#else
+  smul_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__mulsi3");
+#endif
+#ifdef MULDI3_LIBCALL
+  smul_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, MULDI3_LIBCALL);
+#else
+  smul_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__muldi3");
+#endif
+  smul_optab->handlers[(int) SFmode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__mulsf3");
+  smul_optab->handlers[(int) DFmode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__muldf3");
+
+#ifdef HAVE_mulqihi3
+  if (HAVE_mulqihi3)
+    smul_widen_optab->handlers[(int) HImode].insn_code = CODE_FOR_mulqihi3;
+#endif
+#ifdef HAVE_mulhisi3
+  if (HAVE_mulhisi3)
+    smul_widen_optab->handlers[(int) SImode].insn_code = CODE_FOR_mulhisi3;
+#endif
+#ifdef HAVE_mulsidi3
+  if (HAVE_mulsidi3)
+    smul_widen_optab->handlers[(int) DImode].insn_code = CODE_FOR_mulsidi3;
+#endif
+#ifdef HAVE_mulditi3
+  if (HAVE_mulditi3)
+    smul_widen_optab->handlers[(int) TImode].insn_code = CODE_FOR_mulditi3;
+#endif
+
+#ifdef HAVE_umulqihi3
+  if (HAVE_umulqihi3)
+    umul_widen_optab->handlers[(int) HImode].insn_code = CODE_FOR_umulqihi3;
+#endif
+#ifdef HAVE_umulhisi3
+  if (HAVE_umulhisi3)
+    umul_widen_optab->handlers[(int) SImode].insn_code = CODE_FOR_umulhisi3;
+#endif
+#ifdef HAVE_umulsidi3
+  if (HAVE_umulsidi3)
+    umul_widen_optab->handlers[(int) DImode].insn_code = CODE_FOR_umulsidi3;
+#endif
+#ifdef HAVE_umulditi3
+  if (HAVE_umulditi3)
+    umul_widen_optab->handlers[(int) TImode].insn_code = CODE_FOR_umulditi3;
+#endif
+
+#ifdef HAVE_divqi3
+  if (HAVE_divqi3)
+    sdiv_optab->handlers[(int) QImode].insn_code = CODE_FOR_divqi3;
+#endif
+#ifdef HAVE_divhi3
+  if (HAVE_divhi3)
+    sdiv_optab->handlers[(int) HImode].insn_code = CODE_FOR_divhi3;
+#endif
+#ifdef HAVE_divpsi3
+  if (HAVE_divpsi3)
+    sdiv_optab->handlers[(int) PSImode].insn_code = CODE_FOR_divpsi3;
+#endif
+#ifdef HAVE_divsi3
+  if (HAVE_divsi3)
+    sdiv_optab->handlers[(int) SImode].insn_code = CODE_FOR_divsi3;
+#endif
+#ifdef HAVE_divdi3
+  if (HAVE_divdi3)
+    sdiv_optab->handlers[(int) DImode].insn_code = CODE_FOR_divdi3;
+#endif
+#ifdef HAVE_divti3
+  if (HAVE_divti3)
+    sdiv_optab->handlers[(int) TImode].insn_code = CODE_FOR_divti3;
+#endif
+
+#ifdef DIVSI3_LIBCALL
+  sdiv_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, DIVSI3_LIBCALL);
+#else
+  sdiv_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__divsi3");
+#endif
+#ifdef DIVDI3_LIBCALL
+  sdiv_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, DIVDI3_LIBCALL);
+#else
+  sdiv_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__divdi3");
+#endif
+
+#ifdef HAVE_udivqi3
+  if (HAVE_udivqi3)
+    udiv_optab->handlers[(int) QImode].insn_code = CODE_FOR_udivqi3;
+#endif
+#ifdef HAVE_udivhi3
+  if (HAVE_udivhi3)
+    udiv_optab->handlers[(int) HImode].insn_code = CODE_FOR_udivhi3;
+#endif
+#ifdef HAVE_udivpsi3
+  if (HAVE_udivpsi3)
+    udiv_optab->handlers[(int) PSImode].insn_code = CODE_FOR_udivpsi3;
+#endif
+#ifdef HAVE_udivsi3
+  if (HAVE_udivsi3)
+    udiv_optab->handlers[(int) SImode].insn_code = CODE_FOR_udivsi3;
+#endif
+#ifdef HAVE_udivdi3
+  if (HAVE_udivdi3)
+    udiv_optab->handlers[(int) DImode].insn_code = CODE_FOR_udivdi3;
+#endif
+#ifdef HAVE_udivti3
+  if (HAVE_udivti3)
+    udiv_optab->handlers[(int) TImode].insn_code = CODE_FOR_udivti3;
+#endif
+
+#ifdef UDIVSI3_LIBCALL
+  udiv_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, UDIVSI3_LIBCALL);
+#else
+  udiv_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__udivsi3");
+#endif
+#ifdef UDIVDI3_LIBCALL
+  udiv_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, UDIVDI3_LIBCALL);
+#else
+  udiv_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__udivdi3");
+#endif
+
+#ifdef HAVE_divmodqi4
+  if (HAVE_divmodqi4)
+    sdivmod_optab->handlers[(int) QImode].insn_code = CODE_FOR_divmodqi4;
+#endif
+#ifdef HAVE_divmodhi4
+  if (HAVE_divmodhi4)
+    sdivmod_optab->handlers[(int) HImode].insn_code = CODE_FOR_divmodhi4;
+#endif
+#ifdef HAVE_divmodsi4
+  if (HAVE_divmodsi4)
+    sdivmod_optab->handlers[(int) SImode].insn_code = CODE_FOR_divmodsi4;
+#endif
+#ifdef HAVE_divmoddi4
+  if (HAVE_divmoddi4)
+    sdivmod_optab->handlers[(int) DImode].insn_code = CODE_FOR_divmoddi4;
+#endif
+#ifdef HAVE_divmodti4
+  if (HAVE_divmodti4)
+    sdivmod_optab->handlers[(int) TImode].insn_code = CODE_FOR_divmodti4;
+#endif
+
+#ifdef HAVE_udivmodqi4
+  if (HAVE_udivmodqi4)
+    udivmod_optab->handlers[(int) QImode].insn_code = CODE_FOR_udivmodqi4;
+#endif
+#ifdef HAVE_udivmodhi4
+  if (HAVE_udivmodhi4)
+    udivmod_optab->handlers[(int) HImode].insn_code = CODE_FOR_udivmodhi4;
+#endif
+#ifdef HAVE_udivmodsi4
+  if (HAVE_udivmodsi4)
+    udivmod_optab->handlers[(int) SImode].insn_code = CODE_FOR_udivmodsi4;
+#endif
+#ifdef HAVE_udivmoddi4
+  if (HAVE_udivmoddi4)
+    udivmod_optab->handlers[(int) DImode].insn_code = CODE_FOR_udivmoddi4;
+#endif
+#ifdef HAVE_udivmodti4
+  if (HAVE_udivmodti4)
+    udivmod_optab->handlers[(int) TImode].insn_code = CODE_FOR_udivmodti4;
+#endif
+
+#ifdef HAVE_modqi3
+  if (HAVE_modqi3)
+    smod_optab->handlers[(int) QImode].insn_code = CODE_FOR_modqi3;
+#endif
+#ifdef HAVE_modhi3
+  if (HAVE_modhi3)
+    smod_optab->handlers[(int) HImode].insn_code = CODE_FOR_modhi3;
+#endif
+#ifdef HAVE_modpsi3
+  if (HAVE_modpsi3)
+    smod_optab->handlers[(int) PSImode].insn_code = CODE_FOR_modpsi3;
+#endif
+#ifdef HAVE_modsi3
+  if (HAVE_modsi3)
+    smod_optab->handlers[(int) SImode].insn_code = CODE_FOR_modsi3;
+#endif
+#ifdef HAVE_moddi3
+  if (HAVE_moddi3)
+    smod_optab->handlers[(int) DImode].insn_code = CODE_FOR_moddi3;
+#endif
+#ifdef HAVE_modti3
+  if (HAVE_modti3)
+    smod_optab->handlers[(int) TImode].insn_code = CODE_FOR_modti3;
+#endif
+
+#ifdef MODSI3_LIBCALL
+  smod_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, MODSI3_LIBCALL);
+#else
+  smod_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__modsi3");
+#endif
+#ifdef MODDI3_LIBCALL
+  smod_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, MODDI3_LIBCALL);
+#else
+  smod_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__moddi3");
+#endif
+
+#ifdef HAVE_umodqi3
+  if (HAVE_umodqi3)
+    umod_optab->handlers[(int) QImode].insn_code = CODE_FOR_umodqi3;
+#endif
+#ifdef HAVE_umodhi3
+  if (HAVE_umodhi3)
+    umod_optab->handlers[(int) HImode].insn_code = CODE_FOR_umodhi3;
+#endif
+#ifdef HAVE_umodpsi3
+  if (HAVE_umodpsi3)
+    umod_optab->handlers[(int) PSImode].insn_code = CODE_FOR_umodpsi3;
+#endif
+#ifdef HAVE_umodsi3
+  if (HAVE_umodsi3)
+    umod_optab->handlers[(int) SImode].insn_code = CODE_FOR_umodsi3;
+#endif
+#ifdef HAVE_umoddi3
+  if (HAVE_umoddi3)
+    umod_optab->handlers[(int) DImode].insn_code = CODE_FOR_umoddi3;
+#endif
+#ifdef HAVE_umodti3
+  if (HAVE_umodti3)
+    umod_optab->handlers[(int) TImode].insn_code = CODE_FOR_umodti3;
+#endif
+
+#ifdef UMODSI3_LIBCALL
+  umod_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, UMODSI3_LIBCALL);
+#else
+  umod_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__umodsi3");
+#endif
+#ifdef UMODDI3_LIBCALL
+  umod_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, UMODDI3_LIBCALL);
+#else
+  umod_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__umoddi3");
+#endif
+
+#ifdef HAVE_divsf3
+  if (HAVE_divsf3)
+    flodiv_optab->handlers[(int) SFmode].insn_code = CODE_FOR_divsf3;
+#endif
+#ifdef HAVE_divdf3
+  if (HAVE_divdf3)
+    flodiv_optab->handlers[(int) DFmode].insn_code = CODE_FOR_divdf3;
+#endif
+#ifdef HAVE_divtf3
+  if (HAVE_divtf3)
+    flodiv_optab->handlers[(int) TFmode].insn_code = CODE_FOR_divtf3;
+#endif
+  flodiv_optab->handlers[(int) SFmode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__divsf3");
+  flodiv_optab->handlers[(int) DFmode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__divdf3");
+
+#ifdef HAVE_ftruncsf2
+  if (HAVE_ftruncsf2)
+    ftrunc_optab->handlers[(int) SFmode].insn_code = CODE_FOR_ftruncsf2;
+#endif
+#ifdef HAVE_ftruncdf2
+  if (HAVE_ftruncdf2)
+    ftrunc_optab->handlers[(int) DFmode].insn_code = CODE_FOR_ftruncdf2;
+#endif
+#ifdef HAVE_ftrunctf2
+  if (HAVE_ftrunctf2)
+    ftrunc_optab->handlers[(int) TFmode].insn_code = CODE_FOR_ftrunctf2;
+#endif
+
+#ifdef HAVE_andqi3
+  if (HAVE_andqi3)
+    and_optab->handlers[(int) QImode].insn_code = CODE_FOR_andqi3;
+#endif
+#ifdef HAVE_andhi3
+  if (HAVE_andhi3)
+    and_optab->handlers[(int) HImode].insn_code = CODE_FOR_andhi3;
+#endif
+#ifdef HAVE_andpsi3
+  if (HAVE_andpsi3)
+    and_optab->handlers[(int) PSImode].insn_code = CODE_FOR_andpsi3;
+#endif
+#ifdef HAVE_andsi3
+  if (HAVE_andsi3)
+    and_optab->handlers[(int) SImode].insn_code = CODE_FOR_andsi3;
+#endif
+#ifdef HAVE_anddi3
+  if (HAVE_anddi3)
+    and_optab->handlers[(int) DImode].insn_code = CODE_FOR_anddi3;
+#endif
+#ifdef HAVE_andti3
+  if (HAVE_andti3)
+    and_optab->handlers[(int) TImode].insn_code = CODE_FOR_andti3;
+#endif
+
+#ifdef HAVE_iorqi3
+  if (HAVE_iorqi3)
+    ior_optab->handlers[(int) QImode].insn_code = CODE_FOR_iorqi3;
+#endif
+#ifdef HAVE_iorhi3
+  if (HAVE_iorhi3)
+    ior_optab->handlers[(int) HImode].insn_code = CODE_FOR_iorhi3;
+#endif
+#ifdef HAVE_iorpsi3
+  if (HAVE_iorpsi3)
+    ior_optab->handlers[(int) PSImode].insn_code = CODE_FOR_iorpsi3;
+#endif
+#ifdef HAVE_iorsi3
+  if (HAVE_iorsi3)
+    ior_optab->handlers[(int) SImode].insn_code = CODE_FOR_iorsi3;
+#endif
+#ifdef HAVE_iordi3
+  if (HAVE_iordi3)
+    ior_optab->handlers[(int) DImode].insn_code = CODE_FOR_iordi3;
+#endif
+#ifdef HAVE_iorti3
+  if (HAVE_iorti3)
+    ior_optab->handlers[(int) TImode].insn_code = CODE_FOR_iorti3;
+#endif
+
+#ifdef HAVE_xorqi3
+  if (HAVE_xorqi3)
+    xor_optab->handlers[(int) QImode].insn_code = CODE_FOR_xorqi3;
+#endif
+#ifdef HAVE_xorhi3
+  if (HAVE_xorhi3)
+    xor_optab->handlers[(int) HImode].insn_code = CODE_FOR_xorhi3;
+#endif
+#ifdef HAVE_xorpsi3
+  if (HAVE_xorpsi3)
+    xor_optab->handlers[(int) PSImode].insn_code = CODE_FOR_xorpsi3;
+#endif
+#ifdef HAVE_xorsi3
+  if (HAVE_xorsi3)
+    xor_optab->handlers[(int) SImode].insn_code = CODE_FOR_xorsi3;
+#endif
+#ifdef HAVE_xordi3
+  if (HAVE_xordi3)
+    xor_optab->handlers[(int) DImode].insn_code = CODE_FOR_xordi3;
+#endif
+#ifdef HAVE_xorti3
+  if (HAVE_xorti3)
+    xor_optab->handlers[(int) TImode].insn_code = CODE_FOR_xorti3;
+#endif
+
+#ifdef HAVE_ashlqi3
+  if (HAVE_ashlqi3)
+    ashl_optab->handlers[(int) QImode].insn_code = CODE_FOR_ashlqi3;
+#endif
+#ifdef HAVE_ashlhi3
+  if (HAVE_ashlhi3)
+    ashl_optab->handlers[(int) HImode].insn_code = CODE_FOR_ashlhi3;
+#endif
+#ifdef HAVE_ashlpsi3
+  if (HAVE_ashlpsi3)
+    ashl_optab->handlers[(int) PSImode].insn_code = CODE_FOR_ashlpsi3;
+#endif
+#ifdef HAVE_ashlsi3
+  if (HAVE_ashlsi3)
+    ashl_optab->handlers[(int) SImode].insn_code = CODE_FOR_ashlsi3;
+#endif
+#ifdef HAVE_ashldi3
+  if (HAVE_ashldi3)
+    ashl_optab->handlers[(int) DImode].insn_code = CODE_FOR_ashldi3;
+#endif
+#ifdef HAVE_ashlti3
+  if (HAVE_ashlti3)
+    ashl_optab->handlers[(int) TImode].insn_code = CODE_FOR_ashlti3;
+#endif
+  ashl_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__ashlsi3");
+  ashl_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__ashldi3");
+
+#ifdef HAVE_ashrqi3
+  if (HAVE_ashrqi3)
+    ashr_optab->handlers[(int) QImode].insn_code = CODE_FOR_ashrqi3;
+#endif
+#ifdef HAVE_ashrhi3
+  if (HAVE_ashrhi3)
+    ashr_optab->handlers[(int) HImode].insn_code = CODE_FOR_ashrhi3;
+#endif
+#ifdef HAVE_ashrpsi3
+  if (HAVE_ashrpsi3)
+    ashr_optab->handlers[(int) PSImode].insn_code = CODE_FOR_ashrpsi3;
+#endif
+#ifdef HAVE_ashrsi3
+  if (HAVE_ashrsi3)
+    ashr_optab->handlers[(int) SImode].insn_code = CODE_FOR_ashrsi3;
+#endif
+#ifdef HAVE_ashrdi3
+  if (HAVE_ashrdi3)
+    ashr_optab->handlers[(int) DImode].insn_code = CODE_FOR_ashrdi3;
+#endif
+#ifdef HAVE_ashrti3
+  if (HAVE_ashrti3)
+    ashr_optab->handlers[(int) TImode].insn_code = CODE_FOR_ashrti3;
+#endif
+  ashr_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__ashrsi3");
+  ashr_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__ashrdi3");
+
+#ifdef HAVE_lshlqi3
+  if (HAVE_lshlqi3)
+    lshl_optab->handlers[(int) QImode].insn_code = CODE_FOR_lshlqi3;
+#endif
+#ifdef HAVE_lshlhi3
+  if (HAVE_lshlhi3)
+    lshl_optab->handlers[(int) HImode].insn_code = CODE_FOR_lshlhi3;
+#endif
+#ifdef HAVE_lshlpsi3
+  if (HAVE_lshlpsi3)
+    lshl_optab->handlers[(int) PSImode].insn_code = CODE_FOR_lshlpsi3;
+#endif
+#ifdef HAVE_lshlsi3
+  if (HAVE_lshlsi3)
+    lshl_optab->handlers[(int) SImode].insn_code = CODE_FOR_lshlsi3;
+#endif
+#ifdef HAVE_lshldi3
+  if (HAVE_lshldi3)
+    lshl_optab->handlers[(int) DImode].insn_code = CODE_FOR_lshldi3;
+#endif
+#ifdef HAVE_lshlti3
+  if (HAVE_lshlti3)
+    lshl_optab->handlers[(int) TImode].insn_code = CODE_FOR_lshlti3;
+#endif
+  lshl_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__lshlsi3");
+  lshl_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__lshldi3");
+
+#ifdef HAVE_lshrqi3
+  if (HAVE_lshrqi3)
+    lshr_optab->handlers[(int) QImode].insn_code = CODE_FOR_lshrqi3;
+#endif
+#ifdef HAVE_lshrhi3
+  if (HAVE_lshrhi3)
+    lshr_optab->handlers[(int) HImode].insn_code = CODE_FOR_lshrhi3;
+#endif
+#ifdef HAVE_lshrpsi3
+  if (HAVE_lshrpsi3)
+    lshr_optab->handlers[(int) PSImode].insn_code = CODE_FOR_lshrpsi3;
+#endif
+#ifdef HAVE_lshrsi3
+  if (HAVE_lshrsi3)
+    lshr_optab->handlers[(int) SImode].insn_code = CODE_FOR_lshrsi3;
+#endif
+#ifdef HAVE_lshrdi3
+  if (HAVE_lshrdi3)
+    lshr_optab->handlers[(int) DImode].insn_code = CODE_FOR_lshrdi3;
+#endif
+#ifdef HAVE_lshrti3
+  if (HAVE_lshrti3)
+    lshr_optab->handlers[(int) TImode].insn_code = CODE_FOR_lshrti3;
+#endif
+  lshr_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__lshrsi3");
+  lshr_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__lshrdi3");
+
+#ifdef HAVE_rotlqi3
+  if (HAVE_rotlqi3)
+    rotl_optab->handlers[(int) QImode].insn_code = CODE_FOR_rotlqi3;
+#endif
+#ifdef HAVE_rotlhi3
+  if (HAVE_rotlhi3)
+    rotl_optab->handlers[(int) HImode].insn_code = CODE_FOR_rotlhi3;
+#endif
+#ifdef HAVE_rotlpsi3
+  if (HAVE_rotlpsi3)
+    rotl_optab->handlers[(int) PSImode].insn_code = CODE_FOR_rotlpsi3;
+#endif
+#ifdef HAVE_rotlsi3
+  if (HAVE_rotlsi3)
+    rotl_optab->handlers[(int) SImode].insn_code = CODE_FOR_rotlsi3;
+#endif
+#ifdef HAVE_rotldi3
+  if (HAVE_rotldi3)
+    rotl_optab->handlers[(int) DImode].insn_code = CODE_FOR_rotldi3;
+#endif
+#ifdef HAVE_rotlti3
+  if (HAVE_rotlti3)
+    rotl_optab->handlers[(int) TImode].insn_code = CODE_FOR_rotlti3;
+#endif
+  rotl_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__rotlsi3");
+  rotl_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__rotldi3");
+
+#ifdef HAVE_rotrqi3
+  if (HAVE_rotrqi3)
+    rotr_optab->handlers[(int) QImode].insn_code = CODE_FOR_rotrqi3;
+#endif
+#ifdef HAVE_rotrhi3
+  if (HAVE_rotrhi3)
+    rotr_optab->handlers[(int) HImode].insn_code = CODE_FOR_rotrhi3;
+#endif
+#ifdef HAVE_rotrpsi3
+  if (HAVE_rotrpsi3)
+    rotr_optab->handlers[(int) PSImode].insn_code = CODE_FOR_rotrpsi3;
+#endif
+#ifdef HAVE_rotrsi3
+  if (HAVE_rotrsi3)
+    rotr_optab->handlers[(int) SImode].insn_code = CODE_FOR_rotrsi3;
+#endif
+#ifdef HAVE_rotrdi3
+  if (HAVE_rotrdi3)
+    rotr_optab->handlers[(int) DImode].insn_code = CODE_FOR_rotrdi3;
+#endif
+#ifdef HAVE_rotrti3
+  if (HAVE_rotrti3)
+    rotr_optab->handlers[(int) TImode].insn_code = CODE_FOR_rotrti3;
+#endif
+  rotr_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__rotrsi3");
+  rotr_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__rotrdi3");
+
+#ifdef HAVE_sminqi3
+  if (HAVE_sminqi3)
+    smin_optab->handlers[(int) QImode].insn_code = CODE_FOR_sminqi3;
+#endif
+#ifdef HAVE_sminhi3
+  if (HAVE_sminhi3)
+    smin_optab->handlers[(int) HImode].insn_code = CODE_FOR_sminhi3;
+#endif
+#ifdef HAVE_sminsi3
+  if (HAVE_sminsi3)
+    smin_optab->handlers[(int) SImode].insn_code = CODE_FOR_sminsi3;
+#endif
+#ifdef HAVE_smindi3
+  if (HAVE_smindi3)
+    smin_optab->handlers[(int) DImode].insn_code = CODE_FOR_smindi3;
+#endif
+#ifdef HAVE_sminti3
+  if (HAVE_sminti3)
+    smin_optab->handlers[(int) TImode].insn_code = CODE_FOR_sminti3;
+#endif
+#ifdef HAVE_sminsf3
+  if (HAVE_sminsf3)
+    smin_optab->handlers[(int) SFmode].insn_code = CODE_FOR_sminsf3;
+#endif
+#ifdef HAVE_smindf3
+  if (HAVE_smindf3)
+    smin_optab->handlers[(int) DFmode].insn_code = CODE_FOR_smindf3;
+#endif
+#ifdef HAVE_smintf3
+  if (HAVE_smintf3)
+    smin_optab->handlers[(int) TFmode].insn_code = CODE_FOR_smintf3;
+#endif
+
+#ifdef HAVE_smaxqi3
+  if (HAVE_smaxqi3)
+    smax_optab->handlers[(int) QImode].insn_code = CODE_FOR_smaxqi3;
+#endif
+#ifdef HAVE_smaxhi3
+  if (HAVE_smaxhi3)
+    smax_optab->handlers[(int) HImode].insn_code = CODE_FOR_smaxhi3;
+#endif
+#ifdef HAVE_smaxsi3
+  if (HAVE_smaxsi3)
+    smax_optab->handlers[(int) SImode].insn_code = CODE_FOR_smaxsi3;
+#endif
+#ifdef HAVE_smaxdi3
+  if (HAVE_smaxdi3)
+    smax_optab->handlers[(int) DImode].insn_code = CODE_FOR_smaxdi3;
+#endif
+#ifdef HAVE_smaxti3
+  if (HAVE_smaxti3)
+    smax_optab->handlers[(int) TImode].insn_code = CODE_FOR_smaxti3;
+#endif
+#ifdef HAVE_smaxsf3
+  if (HAVE_smaxsf3)
+    smax_optab->handlers[(int) SFmode].insn_code = CODE_FOR_smaxsf3;
+#endif
+#ifdef HAVE_smaxdf3
+  if (HAVE_smaxdf3)
+    smax_optab->handlers[(int) DFmode].insn_code = CODE_FOR_smaxdf3;
+#endif
+#ifdef HAVE_smaxtf3
+  if (HAVE_smaxtf3)
+    smax_optab->handlers[(int) TFmode].insn_code = CODE_FOR_smaxtf3;
+#endif
+
+#ifdef HAVE_uminqi3
+  if (HAVE_uminqi3)
+    umin_optab->handlers[(int) QImode].insn_code = CODE_FOR_uminqi3;
+#endif
+#ifdef HAVE_uminhi3
+  if (HAVE_uminhi3)
+    umin_optab->handlers[(int) HImode].insn_code = CODE_FOR_uminhi3;
+#endif
+#ifdef HAVE_uminsi3
+  if (HAVE_uminsi3)
+    umin_optab->handlers[(int) SImode].insn_code = CODE_FOR_uminsi3;
+#endif
+#ifdef HAVE_umindi3
+  if (HAVE_umindi3)
+    umin_optab->handlers[(int) DImode].insn_code = CODE_FOR_umindi3;
+#endif
+#ifdef HAVE_uminti3
+  if (HAVE_uminti3)
+    umin_optab->handlers[(int) TImode].insn_code = CODE_FOR_uminti3;
+#endif
+
+#ifdef HAVE_umaxqi3
+  if (HAVE_umaxqi3)
+    umax_optab->handlers[(int) QImode].insn_code = CODE_FOR_umaxqi3;
+#endif
+#ifdef HAVE_umaxhi3
+  if (HAVE_umaxhi3)
+    umax_optab->handlers[(int) HImode].insn_code = CODE_FOR_umaxhi3;
+#endif
+#ifdef HAVE_umaxsi3
+  if (HAVE_umaxsi3)
+    umax_optab->handlers[(int) SImode].insn_code = CODE_FOR_umaxsi3;
+#endif
+#ifdef HAVE_umaxdi3
+  if (HAVE_umaxdi3)
+    umax_optab->handlers[(int) DImode].insn_code = CODE_FOR_umaxdi3;
+#endif
+#ifdef HAVE_umaxti3
+  if (HAVE_umaxti3)
+    umax_optab->handlers[(int) TImode].insn_code = CODE_FOR_umaxti3;
+#endif
+
+#ifdef HAVE_negqi2
+  if (HAVE_negqi2)
+    neg_optab->handlers[(int) QImode].insn_code = CODE_FOR_negqi2;
+#endif
+#ifdef HAVE_neghi2
+  if (HAVE_neghi2)
+    neg_optab->handlers[(int) HImode].insn_code = CODE_FOR_neghi2;
+#endif
+#ifdef HAVE_negpsi2
+  if (HAVE_negpsi2)
+    neg_optab->handlers[(int) PSImode].insn_code = CODE_FOR_negpsi2;
+#endif
+#ifdef HAVE_negsi2
+  if (HAVE_negsi2)
+    neg_optab->handlers[(int) SImode].insn_code = CODE_FOR_negsi2;
+#endif
+#ifdef HAVE_negdi2
+  if (HAVE_negdi2)
+    neg_optab->handlers[(int) DImode].insn_code = CODE_FOR_negdi2;
+#endif
+#ifdef HAVE_negti2
+  if (HAVE_negti2)
+    neg_optab->handlers[(int) TImode].insn_code = CODE_FOR_negti2;
+#endif
+#ifdef HAVE_negsf2
+  if (HAVE_negsf2)
+    neg_optab->handlers[(int) SFmode].insn_code = CODE_FOR_negsf2;
+#endif
+#ifdef HAVE_negdf2
+  if (HAVE_negdf2)
+    neg_optab->handlers[(int) DFmode].insn_code = CODE_FOR_negdf2;
+#endif
+#ifdef HAVE_negtf2
+  if (HAVE_negtf2)
+    neg_optab->handlers[(int) TFmode].insn_code = CODE_FOR_negtf2;
+#endif
+  neg_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__negsi2"); 
+  neg_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__negdi2");
+  neg_optab->handlers[(int) SFmode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__negsf2");
+  neg_optab->handlers[(int) DFmode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__negdf2");
+
+#ifdef HAVE_absqi2
+  if (HAVE_absqi2)
+    abs_optab->handlers[(int) QImode].insn_code = CODE_FOR_absqi2;
+#endif
+#ifdef HAVE_abshi2
+  if (HAVE_abshi2)
+    abs_optab->handlers[(int) HImode].insn_code = CODE_FOR_abshi2;
+#endif
+#ifdef HAVE_abspsi2
+  if (HAVE_abspsi2)
+    abs_optab->handlers[(int) PSImode].insn_code = CODE_FOR_abspsi2;
+#endif
+#ifdef HAVE_abssi2
+  if (HAVE_abssi2)
+    abs_optab->handlers[(int) SImode].insn_code = CODE_FOR_abssi2;
+#endif
+#ifdef HAVE_absdi2
+  if (HAVE_absdi2)
+    abs_optab->handlers[(int) DImode].insn_code = CODE_FOR_absdi2;
+#endif
+#ifdef HAVE_absti2
+  if (HAVE_absti2)
+    abs_optab->handlers[(int) TImode].insn_code = CODE_FOR_absti2;
+#endif
+#ifdef HAVE_abssf2
+  if (HAVE_abssf2)
+    abs_optab->handlers[(int) SFmode].insn_code = CODE_FOR_abssf2;
+#endif
+#ifdef HAVE_absdf2
+  if (HAVE_absdf2)
+    abs_optab->handlers[(int) DFmode].insn_code = CODE_FOR_absdf2;
+#endif
+#ifdef HAVE_abstf2
+  if (HAVE_abstf2)
+    abs_optab->handlers[(int) TFmode].insn_code = CODE_FOR_abstf2;
+#endif
+  /* No library calls here!  If there is no abs instruction,
+     expand_expr will generate a conditional negation.  */
+
+#ifdef HAVE_one_cmplqi2
+  if (HAVE_one_cmplqi2)
+    one_cmpl_optab->handlers[(int) QImode].insn_code = CODE_FOR_one_cmplqi2;
+#endif
+#ifdef HAVE_one_cmplhi2
+  if (HAVE_one_cmplhi2)
+    one_cmpl_optab->handlers[(int) HImode].insn_code = CODE_FOR_one_cmplhi2;
+#endif
+#ifdef HAVE_one_cmplpsi2
+  if (HAVE_one_cmplpsi2)
+    one_cmpl_optab->handlers[(int) PSImode].insn_code = CODE_FOR_one_cmplpsi2;
+#endif
+#ifdef HAVE_one_cmplsi2
+  if (HAVE_one_cmplsi2)
+    one_cmpl_optab->handlers[(int) SImode].insn_code = CODE_FOR_one_cmplsi2;
+#endif
+#ifdef HAVE_one_cmpldi2
+  if (HAVE_one_cmpldi2)
+    one_cmpl_optab->handlers[(int) DImode].insn_code = CODE_FOR_one_cmpldi2;
+#endif
+#ifdef HAVE_one_cmplti2
+  if (HAVE_one_cmplti2)
+    one_cmpl_optab->handlers[(int) TImode].insn_code = CODE_FOR_one_cmplti2;
+#endif
+  one_cmpl_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__one_cmplsi2"); 
+
+#ifdef HAVE_ffsqi2
+  if (HAVE_ffsqi2)
+    ffs_optab->handlers[(int) QImode].insn_code = CODE_FOR_ffsqi2;
+#endif
+#ifdef HAVE_ffshi2
+  if (HAVE_ffshi2)
+    ffs_optab->handlers[(int) HImode].insn_code = CODE_FOR_ffshi2;
+#endif
+#ifdef HAVE_ffspsi2
+  if (HAVE_ffspsi2)
+    ffs_optab->handlers[(int) PSImode].insn_code = CODE_FOR_ffspsi2;
+#endif
+#ifdef HAVE_ffssi2
+  if (HAVE_ffssi2)
+    ffs_optab->handlers[(int) SImode].insn_code = CODE_FOR_ffssi2;
+#endif
+#ifdef HAVE_ffsdi2
+  if (HAVE_ffsdi2)
+    ffs_optab->handlers[(int) DImode].insn_code = CODE_FOR_ffsdi2;
+#endif
+#ifdef HAVE_ffsti2
+  if (HAVE_ffsti2)
+    ffs_optab->handlers[(int) TImode].insn_code = CODE_FOR_ffsti2;
+#endif
+  ffs_optab->handlers[(int) SImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "ffs"); 
+
+#ifdef HAVE_movqi
+  if (HAVE_movqi)
+    mov_optab->handlers[(int) QImode].insn_code = CODE_FOR_movqi;
+#endif
+#ifdef HAVE_movhi
+  if (HAVE_movhi)
+    mov_optab->handlers[(int) HImode].insn_code = CODE_FOR_movhi;
+#endif
+#ifdef HAVE_movpsi
+  if (HAVE_movpsi)
+    mov_optab->handlers[(int) PSImode].insn_code = CODE_FOR_movpsi;
+#endif
+#ifdef HAVE_movsi
+  if (HAVE_movsi)
+    mov_optab->handlers[(int) SImode].insn_code = CODE_FOR_movsi;
+#endif
+#ifdef HAVE_movdi
+  if (HAVE_movdi)
+    mov_optab->handlers[(int) DImode].insn_code = CODE_FOR_movdi;
+#endif
+#ifdef HAVE_movti
+  if (HAVE_movti)
+    mov_optab->handlers[(int) TImode].insn_code = CODE_FOR_movti;
+#endif
+#ifdef HAVE_movsf
+  if (HAVE_movsf)
+    mov_optab->handlers[(int) SFmode].insn_code = CODE_FOR_movsf;
+#endif
+#ifdef HAVE_movdf
+  if (HAVE_movdf)
+    mov_optab->handlers[(int) DFmode].insn_code = CODE_FOR_movdf;
+#endif
+#ifdef HAVE_movtf
+  if (HAVE_movtf)
+    mov_optab->handlers[(int) TFmode].insn_code = CODE_FOR_movtf;
+#endif
+#ifdef HAVE_movcc
+  if (HAVE_movcc)
+    mov_optab->handlers[(int) CCmode].insn_code = CODE_FOR_movcc;
+#endif
+
+#ifdef EXTRA_CC_MODES
+  init_mov_optab ();
+#endif
+
+#ifdef HAVE_movstrictqi
+  if (HAVE_movstrictqi)
+    movstrict_optab->handlers[(int) QImode].insn_code = CODE_FOR_movstrictqi;
+#endif
+#ifdef HAVE_movstricthi
+  if (HAVE_movstricthi)
+    movstrict_optab->handlers[(int) HImode].insn_code = CODE_FOR_movstricthi;
+#endif
+#ifdef HAVE_movstrictpsi
+  if (HAVE_movstrictpsi)
+    movstrict_optab->handlers[(int) PSImode].insn_code = CODE_FOR_movstrictpsi;
+#endif
+#ifdef HAVE_movstrictsi
+  if (HAVE_movstrictsi)
+    movstrict_optab->handlers[(int) SImode].insn_code = CODE_FOR_movstrictsi;
+#endif
+#ifdef HAVE_movstrictdi
+  if (HAVE_movstrictdi)
+    movstrict_optab->handlers[(int) DImode].insn_code = CODE_FOR_movstrictdi;
+#endif
+#ifdef HAVE_movstrictti
+  if (HAVE_movstrictti)
+    movstrict_optab->handlers[(int) TImode].insn_code = CODE_FOR_movstrictti;
+#endif
+
+#ifdef HAVE_cmpqi
+  if (HAVE_cmpqi)
+    cmp_optab->handlers[(int) QImode].insn_code = CODE_FOR_cmpqi;
+#endif
+#ifdef HAVE_cmphi
+  if (HAVE_cmphi)
+    cmp_optab->handlers[(int) HImode].insn_code = CODE_FOR_cmphi;
+#endif
+#ifdef HAVE_cmppsi
+  if (HAVE_cmppsi)
+    cmp_optab->handlers[(int) PSImode].insn_code = CODE_FOR_cmppsi;
+#endif
+#ifdef HAVE_cmpsi
+  if (HAVE_cmpsi)
+    cmp_optab->handlers[(int) SImode].insn_code = CODE_FOR_cmpsi;
+#endif
+#ifdef HAVE_cmpdi
+  if (HAVE_cmpdi)
+    cmp_optab->handlers[(int) DImode].insn_code = CODE_FOR_cmpdi;
+#endif
+#ifdef HAVE_cmpti
+  if (HAVE_cmpti)
+    cmp_optab->handlers[(int) TImode].insn_code = CODE_FOR_cmpti;
+#endif
+#ifdef HAVE_cmpsf
+  if (HAVE_cmpsf)
+    cmp_optab->handlers[(int) SFmode].insn_code = CODE_FOR_cmpsf;
+#endif
+#ifdef HAVE_cmpdf
+  if (HAVE_cmpdf)
+    cmp_optab->handlers[(int) DFmode].insn_code = CODE_FOR_cmpdf;
+#endif
+#ifdef HAVE_cmptf
+  if (HAVE_cmptf)
+    cmp_optab->handlers[(int) TFmode].insn_code = CODE_FOR_cmptf;
+#endif
+#ifdef HAVE_tstqi
+  if (HAVE_tstqi)
+    tst_optab->handlers[(int) QImode].insn_code = CODE_FOR_tstqi;
+#endif
+#ifdef HAVE_tsthi
+  if (HAVE_tsthi)
+    tst_optab->handlers[(int) HImode].insn_code = CODE_FOR_tsthi;
+#endif
+#ifdef HAVE_tstpsi
+  if (HAVE_tstpsi)
+    tst_optab->handlers[(int) PSImode].insn_code = CODE_FOR_tstpsi;
+#endif
+#ifdef HAVE_tstsi
+  if (HAVE_tstsi)
+    tst_optab->handlers[(int) SImode].insn_code = CODE_FOR_tstsi;
+#endif
+#ifdef HAVE_tstdi
+  if (HAVE_tstdi)
+    tst_optab->handlers[(int) DImode].insn_code = CODE_FOR_tstdi;
+#endif
+#ifdef HAVE_tstti
+  if (HAVE_tstti)
+    tst_optab->handlers[(int) TImode].insn_code = CODE_FOR_tstti;
+#endif
+#ifdef HAVE_tstsf
+  if (HAVE_tstsf)
+    tst_optab->handlers[(int) SFmode].insn_code = CODE_FOR_tstsf;
+#endif
+#ifdef HAVE_tstdf
+  if (HAVE_tstdf)
+    tst_optab->handlers[(int) DFmode].insn_code = CODE_FOR_tstdf;
+#endif
+#ifdef HAVE_tsttf
+  if (HAVE_tsttf)
+    tst_optab->handlers[(int) TFmode].insn_code = CODE_FOR_tsttf;
+#endif
+  /* Comparison libcalls for integers MUST come in pairs, signed/unsigned.  */
+  cmp_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__cmpdi2");
+  ucmp_optab->handlers[(int) DImode].libfunc
+    = gen_rtx (SYMBOL_REF, Pmode, "__ucmpdi2");
+
+#ifdef HAVE_beq
+  if (HAVE_beq)
+    bcc_gen_fctn[(int) EQ] = gen_beq;
+#endif
+#ifdef HAVE_bne
+  if (HAVE_bne)
+    bcc_gen_fctn[(int) NE] = gen_bne;
+#endif
+#ifdef HAVE_bgt
+  if (HAVE_bgt)
+    bcc_gen_fctn[(int) GT] = gen_bgt;
+#endif
+#ifdef HAVE_bge
+  if (HAVE_bge)
+    bcc_gen_fctn[(int) GE] = gen_bge;
+#endif
+#ifdef HAVE_bgtu
+  if (HAVE_bgtu)
+    bcc_gen_fctn[(int) GTU] = gen_bgtu;
+#endif
+#ifdef HAVE_bgeu
+  if (HAVE_bgeu)
+    bcc_gen_fctn[(int) GEU] = gen_bgeu;
+#endif
+#ifdef HAVE_blt
+  if (HAVE_blt)
+    bcc_gen_fctn[(int) LT] = gen_blt;
+#endif
+#ifdef HAVE_ble
+  if (HAVE_ble)
+    bcc_gen_fctn[(int) LE] = gen_ble;
+#endif
+#ifdef HAVE_bltu
+  if (HAVE_bltu)
+    bcc_gen_fctn[(int) LTU] = gen_bltu;
+#endif
+#ifdef HAVE_bleu
+  if (HAVE_bleu)
+    bcc_gen_fctn[(int) LEU] = gen_bleu;
+#endif
+
+  for (i = 0; i < NUM_RTX_CODE; i++)
+    setcc_gen_code[i] = CODE_FOR_nothing;
+
+#ifdef HAVE_seq
+  if (HAVE_seq)
+    setcc_gen_code[(int) EQ] = CODE_FOR_seq;
+#endif
+#ifdef HAVE_sne
+  if (HAVE_sne)
+    setcc_gen_code[(int) NE] = CODE_FOR_sne;
+#endif
+#ifdef HAVE_sgt
+  if (HAVE_sgt)
+    setcc_gen_code[(int) GT] = CODE_FOR_sgt;
+#endif
+#ifdef HAVE_sge
+  if (HAVE_sge)
+    setcc_gen_code[(int) GE] = CODE_FOR_sge;
+#endif
+#ifdef HAVE_sgtu
+  if (HAVE_sgtu)
+    setcc_gen_code[(int) GTU] = CODE_FOR_sgtu;
+#endif
+#ifdef HAVE_sgeu
+  if (HAVE_sgeu)
+    setcc_gen_code[(int) GEU] = CODE_FOR_sgeu;
+#endif
+#ifdef HAVE_slt
+  if (HAVE_slt)
+    setcc_gen_code[(int) LT] = CODE_FOR_slt;
+#endif
+#ifdef HAVE_sle
+  if (HAVE_sle)
+    setcc_gen_code[(int) LE] = CODE_FOR_sle;
+#endif
+#ifdef HAVE_sltu
+  if (HAVE_sltu)
+    setcc_gen_code[(int) LTU] = CODE_FOR_sltu;
+#endif
+#ifdef HAVE_sleu
+  if (HAVE_sleu)
+    setcc_gen_code[(int) LEU] = CODE_FOR_sleu;
+#endif
+
+  extendsfdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__extendsfdf2");
+  truncdfsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__truncdfsf2");
+  memcpy_libfunc = gen_rtx (SYMBOL_REF, Pmode, "memcpy");
+  bcopy_libfunc = gen_rtx (SYMBOL_REF, Pmode, "bcopy");
+  memcmp_libfunc = gen_rtx (SYMBOL_REF, Pmode, "memcmp");
+  bcmp_libfunc = gen_rtx (SYMBOL_REF, Pmode, "bcmp");
+  memset_libfunc = gen_rtx (SYMBOL_REF, Pmode, "memset");
+  bzero_libfunc = gen_rtx (SYMBOL_REF, Pmode, "bzero");
+  eqsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__eqsf2");
+  nesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__nesf2");
+  gtsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gtsf2");
+  gesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gesf2");
+  ltsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__ltsf2");
+  lesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__lesf2");
+  eqdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__eqdf2");
+  nedf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__nedf2");
+  gtdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gtdf2");
+  gedf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gedf2");
+  ltdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__ltdf2");
+  ledf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__ledf2");
+}