OSDN Git Service

* tree.c (unsave_expr_now): Avoid recursing into the parts of
[pf3gnuchains/gcc-fork.git] / gcc / optabs.c
index cc742a4..992660d 100644 (file)
@@ -1,5 +1,5 @@
 /* Expand the basic unary and binary arithmetic operations, for GNU compiler.
-   Copyright (C) 1987, 1988, 1992, 1993 Free Software Foundation, Inc.
+   Copyright (C) 1987, 88, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -15,7 +15,8 @@ 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.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 #include "config.h"
@@ -42,6 +43,8 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 optab add_optab;
 optab sub_optab;
 optab smul_optab;
+optab smul_highpart_optab;
+optab umul_highpart_optab;
 optab smul_widen_optab;
 optab umul_widen_optab;
 optab sdiv_optab;
@@ -57,7 +60,6 @@ optab ior_optab;
 optab xor_optab;
 optab ashl_optab;
 optab lshr_optab;
-optab lshl_optab;
 optab ashr_optab;
 optab rotl_optab;
 optab rotr_optab;
@@ -86,7 +88,7 @@ optab strlen_optab;
 /* Tables of patterns for extending one integer mode to another.  */
 enum insn_code extendtab[MAX_MACHINE_MODE][MAX_MACHINE_MODE][2];
 
-/* Tables of patterns for converting between fixed and floating point. */
+/* Tables of patterns for converting between fixed and floating point.  */
 enum insn_code fixtab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
 enum insn_code fixtrunctab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
 enum insn_code floattab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
@@ -116,6 +118,21 @@ rtx bcmp_libfunc;
 rtx memset_libfunc;
 rtx bzero_libfunc;
 
+rtx throw_libfunc;
+rtx sjthrow_libfunc;
+rtx sjpopnthrow_libfunc;
+rtx terminate_libfunc;
+rtx setjmp_libfunc;
+rtx longjmp_libfunc;
+rtx get_dynamic_handler_chain_libfunc;
+
+rtx eqhf2_libfunc;
+rtx nehf2_libfunc;
+rtx gthf2_libfunc;
+rtx gehf2_libfunc;
+rtx lthf2_libfunc;
+rtx lehf2_libfunc;
+
 rtx eqsf2_libfunc;
 rtx nesf2_libfunc;
 rtx gtsf2_libfunc;
@@ -192,9 +209,6 @@ rtx fixunstfsi_libfunc;
 rtx fixunstfdi_libfunc;
 rtx fixunstfti_libfunc;
 
-/* from emit-rtl.c */
-extern rtx gen_highpart ();
-
 /* Indexed by the rtx-code for a conditional (eg. EQ, LT,...)
    gives the gen_function to make a branch to test that condition.  */
 
@@ -206,9 +220,18 @@ rtxfun bcc_gen_fctn[NUM_RTX_CODE];
 
 enum insn_code setcc_gen_code[NUM_RTX_CODE];
 
+#ifdef HAVE_conditional_move
+/* Indexed by the machine mode, gives the insn code to make a conditional
+   move insn.  This is not indexed by the rtx-code like bcc_gen_fctn and
+   setcc_gen_code to cut down on the number of named patterns.  Consider a day
+   when a lot more rtx codes are conditional (eg: for the ARM).  */
+
+enum insn_code movcc_gen_code[NUM_MACHINE_MODES];
+#endif
+
 static int add_equal_note      PROTO((rtx, rtx, enum rtx_code, rtx, rtx));
-static rtx widen_operand       PROTO((rtx, enum machine_mode, int, int));
-static void emit_float_lib_cmp PROTO((rtx, rtx, enum rtx_code));
+static rtx widen_operand       PROTO((rtx, enum machine_mode,
+                                      enum machine_mode, int, int));
 static enum insn_code can_fix_p        PROTO((enum machine_mode, enum machine_mode,
                                       int, int *));
 static enum insn_code can_float_p PROTO((enum machine_mode, enum machine_mode,
@@ -281,9 +304,9 @@ add_equal_note (seq, target, code, op0, op1)
    of logical operations, but not right shifts.  */
 
 static rtx
-widen_operand (op, mode, unsignedp, no_extend)
+widen_operand (op, mode, oldmode, unsignedp, no_extend)
      rtx op;
-     enum machine_mode mode;
+     enum machine_mode mode, oldmode;
      int unsignedp;
      int no_extend;
 {
@@ -295,7 +318,7 @@ widen_operand (op, mode, unsignedp, no_extend)
   if (! no_extend
       || GET_MODE (op) == VOIDmode
       || (GET_CODE (op) == SUBREG && SUBREG_PROMOTED_VAR_P (op)))
-    return convert_to_mode (mode, op, unsignedp);
+    return convert_modes (mode, oldmode, op, unsignedp);
 
   /* If MODE is no wider than a single word, we return a paradoxical
      SUBREG.  */
@@ -331,13 +354,15 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
      int unsignedp;
      enum optab_methods methods;
 {
+  enum optab_methods next_methods
+    = (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN
+       ? OPTAB_WIDEN : methods);
   enum mode_class class;
   enum machine_mode wider_mode;
   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);
@@ -373,8 +398,8 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
     op0 = force_reg (mode, op0);
 
   if (CONSTANT_P (op1) && preserve_subexpressions_p ()
-      && rtx_cost (op1, binoptab->code) > 2)
-    op1 = force_reg (shift_op ? word_mode : mode, op1);
+      && ! shift_op && rtx_cost (op1, binoptab->code) > 2)
+    op1 = force_reg (mode, op1);
 
   /* Record where to delete back to if we backtrack.  */
   last = get_last_insn ();
@@ -385,7 +410,9 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
      Also try to make the last operand a constant.  */
   if (GET_RTX_CLASS (binoptab->code) == 'c'
       || binoptab == smul_widen_optab
-      || binoptab == umul_widen_optab)
+      || binoptab == umul_widen_optab
+      || binoptab == smul_highpart_optab
+      || binoptab == umul_highpart_optab)
     {
       commutative_op = 1;
 
@@ -419,7 +446,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
        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 we would swap the operands, we can save the conversions.  */
       if (commutative_op)
        {
          if (GET_MODE (op0) != mode0 && GET_MODE (op1) != mode1
@@ -436,20 +463,24 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
         the result, convert the operands.  */
 
       if (GET_MODE (op0) != VOIDmode
-         && GET_MODE (op0) != mode0)
+         && GET_MODE (op0) != mode0
+         && mode0 != VOIDmode)
        xop0 = convert_to_mode (mode0, xop0, unsignedp);
 
       if (GET_MODE (xop1) != VOIDmode
-         && GET_MODE (xop1) != mode1)
+         && GET_MODE (xop1) != mode1
+         && mode1 != VOIDmode)
        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))
+      if (! (*insn_operand_predicate[icode][1]) (xop0, mode0)
+         && mode0 != VOIDmode)
        xop0 = copy_to_mode_reg (mode0, xop0);
 
-      if (! (*insn_operand_predicate[icode][2]) (xop1, mode1))
+      if (! (*insn_operand_predicate[icode][2]) (xop1, mode1)
+         && mode1 != VOIDmode)
        xop1 = copy_to_mode_reg (mode1, xop1);
 
       if (! (*insn_operand_predicate[icode][0]) (temp, mode))
@@ -486,12 +517,15 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
     {
       temp = expand_binop (GET_MODE_WIDER_MODE (mode),
                           unsignedp ? umul_widen_optab : smul_widen_optab,
-                          op0, op1, 0, unsignedp, OPTAB_DIRECT);
+                          op0, op1, NULL_RTX, unsignedp, OPTAB_DIRECT);
 
-      if (GET_MODE_CLASS (mode) == MODE_INT)
-       return gen_lowpart (mode, temp);
-      else
-       return convert_to_mode (mode, temp, unsignedp);
+      if (temp != 0)
+       {
+         if (GET_MODE_CLASS (mode) == MODE_INT)
+           return gen_lowpart (mode, temp);
+         else
+           return convert_to_mode (mode, temp, unsignedp);
+       }
     }
 
   /* Look for a wider mode of the same class for which we think we
@@ -520,13 +554,16 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
            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)
+                || binoptab == smul_optab || binoptab == ashl_optab)
                && class == MODE_INT)
              no_extend = 1;
 
-           xop0 = widen_operand (xop0, wider_mode, unsignedp, no_extend);
-           xop1 = widen_operand (xop1, wider_mode, unsignedp, no_extend);
+           xop0 = widen_operand (xop0, wider_mode, mode, unsignedp, no_extend);
+
+           /* The second operand of a shift must always be extended.  */
+           xop1 = widen_operand (xop1, wider_mode, mode, unsignedp,
+                                 no_extend && binoptab != ashl_optab);
+
            temp = expand_binop (wider_mode, binoptab, xop0, xop1, NULL_RTX,
                                 unsignedp, OPTAB_DIRECT);
            if (temp)
@@ -570,7 +607,11 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
          rtx x = expand_binop (word_mode, binoptab,
                                operand_subword_force (op0, i, mode),
                                operand_subword_force (op1, i, mode),
-                               target_piece, unsignedp, methods);
+                               target_piece, unsignedp, next_methods);
+
+         if (x == 0)
+           break;
+
          if (target_piece != x)
            emit_move_insn (target_piece, x);
        }
@@ -578,19 +619,22 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
       insns = get_insns ();
       end_sequence ();
 
-      if (binoptab->code != UNKNOWN)
-       equiv_value
-         = gen_rtx (binoptab->code, mode, copy_rtx (op0), copy_rtx (op1));
-      else
-       equiv_value = 0;
+      if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD)
+       {
+         if (binoptab->code != UNKNOWN)
+           equiv_value
+             = gen_rtx (binoptab->code, mode, copy_rtx (op0), copy_rtx (op1));
+         else
+           equiv_value = 0;
 
-      emit_no_conflict_block (insns, target, op0, op1, equiv_value);
-      return target;
+         emit_no_conflict_block (insns, target, op0, op1, equiv_value);
+         return target;
+       }
     }
 
   /* Synthesize double word shifts from single word shifts.  */
-  if ((binoptab == lshl_optab || binoptab == lshr_optab
-       || binoptab == ashl_optab || binoptab == ashr_optab)
+  if ((binoptab == lshr_optab || binoptab == ashl_optab
+       || binoptab == ashr_optab)
       && class == MODE_INT
       && GET_CODE (op1) == CONST_INT
       && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
@@ -598,7 +642,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
       && ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
       && lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
     {
-      rtx insns, equiv_value;
+      rtx insns, inter, equiv_value;
       rtx into_target, outof_target;
       rtx into_input, outof_input;
       int shift_count, left_shift, outof_word;
@@ -617,7 +661,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
         they differ depending on the direction of the shift and
         WORDS_BIG_ENDIAN.  */
 
-      left_shift = (binoptab == ashl_optab || binoptab == lshl_optab);
+      left_shift = binoptab == ashl_optab;
       outof_word = left_shift ^ ! WORDS_BIG_ENDIAN;
 
       outof_target = operand_subword (target, outof_word, 1, mode);
@@ -628,35 +672,36 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
 
       if (shift_count >= BITS_PER_WORD)
        {
-         emit_move_insn (into_target,
-                         expand_binop (word_mode, binoptab,
-                                       outof_input,
-                                       GEN_INT (shift_count - BITS_PER_WORD),
-                                       into_target, unsignedp, methods));
+         inter = expand_binop (word_mode, binoptab,
+                              outof_input,
+                              GEN_INT (shift_count - BITS_PER_WORD),
+                              into_target, unsignedp, next_methods);
+
+         if (inter != 0 && inter != into_target)
+           emit_move_insn (into_target, inter);
 
          /* For a signed right shift, we must fill the word we are shifting
             out of with copies of the sign bit.  Otherwise it is zeroed.  */
-         if (binoptab != ashr_optab)
-           emit_move_insn (outof_target, CONST0_RTX (word_mode));
-         else
-           emit_move_insn (outof_target,
-                           expand_binop (word_mode, binoptab,
-                                         outof_input,
-                                         GEN_INT (BITS_PER_WORD - 1),
-                                         outof_target, unsignedp, methods));
+         if (inter != 0 && binoptab != ashr_optab)
+           inter = CONST0_RTX (word_mode);
+         else if (inter != 0)
+           inter = expand_binop (word_mode, binoptab,
+                                 outof_input,
+                                 GEN_INT (BITS_PER_WORD - 1),
+                                 outof_target, unsignedp, next_methods);
+
+         if (inter != 0 && inter != outof_target)
+           emit_move_insn (outof_target, inter);
        }
       else
        {
-         rtx carries, into_temp;
+         rtx carries;
          optab reverse_unsigned_shift, unsigned_shift;
 
          /* For a shift of less then BITS_PER_WORD, to compute the carry,
             we must do a logical shift in the opposite direction of the
             desired shift.  */
 
-         /* We use ashl_optab instead of lshl_optab, because ashl is
-            guaranteed to exist, but lshl may or may not exist.  */
-
          reverse_unsigned_shift = (left_shift ? lshr_optab : ashl_optab);
 
          /* For a shift of less than BITS_PER_WORD, to compute the word
@@ -668,32 +713,42 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
          carries = expand_binop (word_mode, reverse_unsigned_shift,
                                  outof_input,
                                  GEN_INT (BITS_PER_WORD - shift_count),
-                                 0, unsignedp, methods);
-
-         emit_move_insn (outof_target,
-                         expand_binop (word_mode, binoptab,
-                                       outof_input,
-                                       op1, outof_target, unsignedp, methods));
-         into_temp = expand_binop (word_mode, unsigned_shift,
-                                   into_input,
-                                   op1, 0, unsignedp, methods);
-
-         emit_move_insn (into_target,
-                         expand_binop (word_mode, ior_optab,
-                                       carries, into_temp,
-                                       into_target, unsignedp, methods));
+                                 0, unsignedp, next_methods);
+
+         if (carries == 0)
+           inter = 0;
+         else
+           inter = expand_binop (word_mode, unsigned_shift, into_input,
+                                 op1, 0, unsignedp, next_methods);
+
+         if (inter != 0)
+           inter = expand_binop (word_mode, ior_optab, carries, inter,
+                                 into_target, unsignedp, next_methods);
+
+         if (inter != 0 && inter != into_target)
+           emit_move_insn (into_target, inter);
+
+         if (inter != 0)
+           inter = expand_binop (word_mode, binoptab, outof_input,
+                                 op1, outof_target, unsignedp, next_methods);
+         
+         if (inter != 0 && inter != outof_target)
+           emit_move_insn (outof_target, inter);
        }
 
       insns = get_insns ();
       end_sequence ();
 
-      if (binoptab->code != UNKNOWN)
-       equiv_value = gen_rtx (binoptab->code, mode, op0, op1);
-      else
-       equiv_value = 0;
+      if (inter != 0)
+       {
+         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;
+         emit_no_conflict_block (insns, target, op0, op1, equiv_value);
+         return target;
+       }
     }
 
   /* Synthesize double word rotates from single word shifts.  */
@@ -707,6 +762,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
       rtx insns, equiv_value;
       rtx into_target, outof_target;
       rtx into_input, outof_input;
+      rtx inter;
       int shift_count, left_shift, outof_word;
 
       /* If TARGET is the same as one of the operands, the REG_EQUAL note
@@ -737,6 +793,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
          /* This is just a word swap.  */
          emit_move_insn (outof_target, into_input);
          emit_move_insn (into_target, outof_input);
+         inter = const0_rtx;
        }
       else
        {
@@ -763,43 +820,57 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
 
          into_temp1 = expand_binop (word_mode, unsigned_shift,
                                     outof_input, first_shift_count,
-                                    0, unsignedp, methods);
+                                    NULL_RTX, unsignedp, next_methods);
          into_temp2 = expand_binop (word_mode, reverse_unsigned_shift,
                                     into_input, second_shift_count,
-                                    into_target, unsignedp, methods);
-         emit_move_insn (into_target,
-                         expand_binop (word_mode, ior_optab,
-                                       into_temp1, into_temp2,
-                                       into_target, unsignedp, methods));
+                                    into_target, unsignedp, next_methods);
+
+         if (into_temp1 != 0 && into_temp2 != 0)
+           inter = expand_binop (word_mode, ior_optab, into_temp1, into_temp2,
+                                 into_target, unsignedp, next_methods);
+         else
+           inter = 0;
+
+         if (inter != 0 && inter != into_target)
+           emit_move_insn (into_target, inter);
 
          outof_temp1 = expand_binop (word_mode, unsigned_shift,
                                      into_input, first_shift_count,
-                                     0, unsignedp, methods);
+                                     NULL_RTX, unsignedp, next_methods);
          outof_temp2 = expand_binop (word_mode, reverse_unsigned_shift,
                                      outof_input, second_shift_count,
-                                     outof_target, unsignedp, methods);
-         emit_move_insn (outof_target,
-                         expand_binop (word_mode, ior_optab,
-                                       outof_temp1, outof_temp2,
-                                       outof_target, unsignedp, methods));
+                                     outof_target, unsignedp, next_methods);
+
+         if (inter != 0 && outof_temp1 != 0 && outof_temp2 != 0)
+           inter = expand_binop (word_mode, ior_optab,
+                                 outof_temp1, outof_temp2,
+                                 outof_target, unsignedp, next_methods);
+
+         if (inter != 0 && inter != outof_target)
+           emit_move_insn (outof_target, inter);
        }
 
       insns = get_insns ();
       end_sequence ();
 
-      if (binoptab->code != UNKNOWN)
-       equiv_value = gen_rtx (binoptab->code, mode, op0, op1);
-      else
-       equiv_value = 0;
+      if (inter != 0)
+       {
+         if (binoptab->code != UNKNOWN)
+           equiv_value = gen_rtx (binoptab->code, mode, op0, op1);
+         else
+           equiv_value = 0;
 
-      /* We can't make this a no conflict block if this is a word swap,
-        because the word swap case fails if the input and output values
-        are in the same register.  */
-      if (shift_count != BITS_PER_WORD)
-       emit_no_conflict_block (insns, target, op0, op1, equiv_value);
-      else
-       emit_insns (insns);
-      return target;
+         /* We can't make this a no conflict block if this is a word swap,
+            because the word swap case fails if the input and output values
+            are in the same register.  */
+         if (shift_count != BITS_PER_WORD)
+           emit_no_conflict_block (insns, target, op0, op1, equiv_value);
+         else
+           emit_insns (insns);
+
+
+         return target;
+       }
     }
 
   /* These can be done a word at a time by propagating carries.  */
@@ -848,7 +919,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
          /* Main add/subtract of the input operands.  */
          x = expand_binop (word_mode, binoptab,
                            op0_piece, op1_piece,
-                           target_piece, unsignedp, methods);
+                           target_piece, unsignedp, next_methods);
          if (x == 0)
            break;
 
@@ -856,12 +927,11 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
            {
              /* Store carry from main add/subtract.  */
              carry_out = gen_reg_rtx (word_mode);
-             carry_out = emit_store_flag (carry_out,
-                                          binoptab == add_optab ? LTU : GTU,
-                                          x, op0_piece,
-                                          word_mode, 1, normalizep);
-             if (!carry_out)
-               break;
+             carry_out = emit_store_flag_force (carry_out,
+                                                (binoptab == add_optab
+                                                 ? LTU : GTU),
+                                                x, op0_piece,
+                                                word_mode, 1, normalizep);
            }
 
          if (i > 0)
@@ -870,24 +940,27 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
              x = expand_binop (word_mode,
                                normalizep == 1 ? binoptab : otheroptab,
                                x, carry_in,
-                               target_piece, 1, methods);
-             if (target_piece != x)
+                               target_piece, 1, next_methods);
+             if (x == 0)
+               break;
+             else 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,
-                                              word_mode, 1, normalizep);
+                 carry_tmp = emit_store_flag_force (carry_tmp,
+                                                    binoptab == add_optab
+                                                    ? LTU : GTU,
+                                                    x, carry_in,
+                                                    word_mode, 1, normalizep);
+
                  /* Logical-ior the two poss. carry together.  */
                  carry_out = expand_binop (word_mode, ior_optab,
                                            carry_out, carry_tmp,
-                                           carry_out, 0, methods);
-                 if (!carry_out)
+                                           carry_out, 0, next_methods);
+                 if (carry_out == 0)
                    break;
                }
            }
@@ -897,14 +970,16 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
 
       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,
-                                              copy_rtx (xop0),
-                                              copy_rtx (xop1)),
-                                     REG_NOTES (temp));
+         if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+           {
+             rtx temp = emit_move_insn (target, target);
+
+             REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL,
+                                         gen_rtx (binoptab->code, mode,
+                                                  copy_rtx (xop0),
+                                                  copy_rtx (xop1)),
+                                         REG_NOTES (temp));
+           }
          return target;
        }
       else
@@ -983,7 +1058,8 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
 
       /* 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)
+      if (target == op0 || target == op1
+         || (target != 0 && GET_CODE (target) != REG))
        target = 0;
 
       /* Multiply the two lower words to get a double-word product.
@@ -1010,33 +1086,33 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
          product = expand_binop (mode, smul_widen_optab, op0_low, op1_low,
                                  target, 1, OPTAB_DIRECT);
          op0_xhigh = expand_binop (word_mode, lshr_optab, op0_low, wordm1,
-                                   NULL_RTX, 1, OPTAB_DIRECT);
+                                   NULL_RTX, 1, next_methods);
          if (op0_xhigh)
            op0_xhigh = expand_binop (word_mode, add_optab, op0_high,
-                                     op0_xhigh, op0_xhigh, 0, OPTAB_DIRECT);
+                                     op0_xhigh, op0_xhigh, 0, next_methods);
          else
            {
              op0_xhigh = expand_binop (word_mode, ashr_optab, op0_low, wordm1,
-                                       NULL_RTX, 0, OPTAB_DIRECT);
+                                       NULL_RTX, 0, next_methods);
              if (op0_xhigh)
                op0_xhigh = expand_binop (word_mode, sub_optab, op0_high,
                                          op0_xhigh, op0_xhigh, 0,
-                                         OPTAB_DIRECT);
+                                         next_methods);
            }
 
          op1_xhigh = expand_binop (word_mode, lshr_optab, op1_low, wordm1,
-                                   NULL_RTX, 1, OPTAB_DIRECT);
+                                   NULL_RTX, 1, next_methods);
          if (op1_xhigh)
            op1_xhigh = expand_binop (word_mode, add_optab, op1_high,
-                                     op1_xhigh, op1_xhigh, 0, OPTAB_DIRECT);
+                                     op1_xhigh, op1_xhigh, 0, next_methods);
          else
            {
              op1_xhigh = expand_binop (word_mode, ashr_optab, op1_low, wordm1,
-                                       NULL_RTX, 0, OPTAB_DIRECT);
+                                       NULL_RTX, 0, next_methods);
              if (op1_xhigh)
                op1_xhigh = expand_binop (word_mode, sub_optab, op1_high,
                                          op1_xhigh, op1_xhigh, 0,
-                                         OPTAB_DIRECT);
+                                         next_methods);
            }
        }
 
@@ -1052,34 +1128,40 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
 
       if (product && op0_xhigh && op1_xhigh)
        {
-         rtx product_piece;
          rtx product_high = operand_subword (product, high, 1, mode);
          rtx temp = expand_binop (word_mode, binoptab, op0_low, op1_xhigh,
                                   NULL_RTX, 0, OPTAB_DIRECT);
 
-         if (temp)
-           {
-             product_piece = expand_binop (word_mode, add_optab, temp,
-                                           product_high, product_high,
-                                           0, OPTAB_LIB_WIDEN);
-             if (product_piece != product_high)
-               emit_move_insn (product_high, product_piece);
+         if (temp != 0)
+           temp = expand_binop (word_mode, add_optab, temp, product_high,
+                                product_high, 0, next_methods);
 
-             temp = expand_binop (word_mode, binoptab, op1_low, op0_xhigh, 
-                                  NULL_RTX, 0, OPTAB_DIRECT);
+         if (temp != 0 && temp != product_high)
+           emit_move_insn (product_high, temp);
 
-             product_piece = expand_binop (word_mode, add_optab, temp,
-                                           product_high, product_high,
-                                           0, OPTAB_LIB_WIDEN);
-             if (product_piece != product_high)
-               emit_move_insn (product_high, product_piece);
+         if (temp != 0)
+           temp = expand_binop (word_mode, binoptab, op1_low, op0_xhigh, 
+                                NULL_RTX, 0, OPTAB_DIRECT);
 
-             temp = emit_move_insn (product, product);
-             REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL,
-                                         gen_rtx (MULT, mode, copy_rtx (op0),
-                                                  copy_rtx (op1)),
-                                         REG_NOTES (temp));
+         if (temp != 0)
+           temp = expand_binop (word_mode, add_optab, temp,
+                                product_high, product_high,
+                                0, next_methods);
+
+         if (temp != 0 && temp != product_high)
+           emit_move_insn (product_high, temp);
 
+         if (temp != 0)
+           {
+             if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+               {
+                 temp = emit_move_insn (product, product);
+                 REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL,
+                                             gen_rtx (MULT, mode,
+                                                      copy_rtx (op0),
+                                                      copy_rtx (op1)),
+                                             REG_NOTES (temp));
+               }
              return product;
            }
        }
@@ -1101,15 +1183,12 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
 
   if (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)
     {
-      rtx real0 = (rtx) 0;
-      rtx imag0 = (rtx) 0;
-      rtx real1 = (rtx) 0;
-      rtx imag1 = (rtx) 0;
-      rtx realr;
-      rtx imagr;
-      rtx res;
+      rtx real0 = 0, imag0 = 0;
+      rtx real1 = 0, imag1 = 0;
+      rtx realr, imagr, res;
       rtx seq;
       rtx equiv_value;
+      int ok = 0;
 
       /* Find the correct mode for the real and imaginary parts */
       enum machine_mode submode
@@ -1144,7 +1223,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
       else
        real1 = op1;
 
-      if (! real0 || ! real1 || ! (imag0 || imag1))
+      if (real0 == 0 || real1 == 0 || ! (imag0 != 0|| imag1 != 0))
        abort ();
 
       switch (binoptab->code)
@@ -1155,7 +1234,10 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
          /* (a+ib) - (c+id) = (a-c) + i(b-d) */
          res = expand_binop (submode, binoptab, real0, real1,
                              realr, unsignedp, methods);
-         if (res != realr)
+
+         if (res == 0)
+           break;
+         else if (res != realr)
            emit_move_insn (realr, res);
 
          if (imag0 && imag1)
@@ -1168,8 +1250,12 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
          else
            res = imag1;
 
-         if (res != imagr)
+         if (res == 0)
+           break;
+         else if (res != imagr)
            emit_move_insn (imagr, res);
+
+         ok = 1;
          break;
 
        case MULT:
@@ -1177,32 +1263,49 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
 
          if (imag0 && imag1)
            {
+             rtx temp1, temp2;
+
              /* Don't fetch these from memory more than once.  */
              real0 = force_reg (submode, real0);
              real1 = force_reg (submode, real1);
              imag0 = force_reg (submode, imag0);
              imag1 = force_reg (submode, imag1);
 
-             res = expand_binop (submode, sub_optab,
-                                 expand_binop (submode, binoptab, real0,
-                                               real1, 0, unsignedp, methods),
-                                 expand_binop (submode, binoptab, imag0,
-                                               imag1, 0, unsignedp, methods),
+             temp1 = expand_binop (submode, binoptab, real0, real1, NULL_RTX,
+                                   unsignedp, methods);
+
+             temp2 = expand_binop (submode, binoptab, imag0, imag1, NULL_RTX,
+                                   unsignedp, methods);
+
+             if (temp1 == 0 || temp2 == 0)
+               break;
+
+             res = expand_binop (submode, sub_optab, temp1, temp2,
                                  realr, unsignedp, methods);
 
-             if (res != realr)
+             if (res == 0)
+               break;
+             else if (res != realr)
                emit_move_insn (realr, res);
 
-             res = expand_binop (submode, add_optab,
-                                 expand_binop (submode, binoptab,
-                                               real0, imag1,
-                                               0, unsignedp, methods),
-                                 expand_binop (submode, binoptab,
-                                               real1, imag0,
-                                               0, unsignedp, methods),
+             temp1 = expand_binop (submode, binoptab, real0, imag1,
+                                   NULL_RTX, unsignedp, methods);
+
+             temp2 = expand_binop (submode, binoptab, real1, imag0,
+                                   NULL_RTX, unsignedp, methods);
+
+             if (temp1 == 0 || temp2 == 0)
+                 break;
+
+             res = expand_binop (submode, add_optab, temp1, temp2,
                                  imagr, unsignedp, methods);
-             if (res != imagr)
+
+             if (res == 0)
+               break;
+             else if (res != imagr)
                emit_move_insn (imagr, res);
+
+             ok = 1;
            }
          else
            {
@@ -1212,110 +1315,172 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
 
              res = expand_binop (submode, binoptab, real0, real1,
                                  realr, unsignedp, methods);
-             if (res != realr)
+             if (res == 0)
+               break;
+             else if (res != realr)
                emit_move_insn (realr, res);
 
-             if (imag0)
+             if (imag0 != 0)
                res = expand_binop (submode, binoptab,
                                    real1, imag0, imagr, unsignedp, methods);
              else
                res = expand_binop (submode, binoptab,
                                    real0, imag1, imagr, unsignedp, methods);
-             if (res != imagr)
+
+             if (res == 0)
+               break;
+             else if (res != imagr)
                emit_move_insn (imagr, res);
+
+             ok = 1;
            }
          break;
 
        case DIV:
          /* (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd)) */
          
-         if (! imag1)
-           {   /* (a+ib) / (c+i0) = (a/c) + i(b/c) */
+         if (imag1 == 0)
+           {
+             /* (a+ib) / (c+i0) = (a/c) + i(b/c) */
 
              /* Don't fetch these from memory more than once.  */
              real1 = force_reg (submode, real1);
 
              /* Simply divide the real and imaginary parts by `c' */
-             res = expand_binop (submode, binoptab, real0, real1,
-                                 realr, unsignedp, methods);
-             if (res != realr)
+             if (class == MODE_COMPLEX_FLOAT)
+               res = expand_binop (submode, binoptab, real0, real1,
+                                   realr, unsignedp, methods);
+             else
+               res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+                                    real0, real1, realr, unsignedp);
+
+             if (res == 0)
+               break;
+             else if (res != realr)
                emit_move_insn (realr, res);
 
-             res = expand_binop (submode, binoptab, imag0, real1,
-                                 imagr, unsignedp, methods);
-             if (res != imagr)
+             if (class == MODE_COMPLEX_FLOAT)
+               res = expand_binop (submode, binoptab, imag0, real1,
+                                   imagr, unsignedp, methods);
+             else
+               res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+                                    imag0, real1, imagr, unsignedp);
+
+             if (res == 0)
+               break;
+             else if (res != imagr)
                emit_move_insn (imagr, res);
-           }
-         else                  /* Divisor is of complex type */
-           {                   /* X/(a+ib) */
 
+             ok = 1;
+           }
+         else
+           {
+             /* Divisor is of complex type:
+                X/(a+ib) */
              rtx divisor;
-             rtx real_t;
-             rtx imag_t;
+             rtx real_t, imag_t;
+             rtx lhs, rhs;
+             rtx temp1, temp2;
              
-             optab mulopt = unsignedp ? umul_widen_optab : smul_optab;
-
              /* Don't fetch these from memory more than once.  */
              real0 = force_reg (submode, real0);
              real1 = force_reg (submode, real1);
-             if (imag0)
+
+             if (imag0 != 0)
                imag0 = force_reg (submode, imag0);
+
              imag1 = force_reg (submode, imag1);
 
              /* Divisor: c*c + d*d */
-             divisor = expand_binop (submode, add_optab,
-                                     expand_binop (submode, mulopt,
-                                                   real1, real1,
-                                                   0, unsignedp, methods),
-                                     expand_binop (submode, mulopt,
-                                                   imag1, imag1,
-                                                   0, unsignedp, methods),
-                                     0, unsignedp, methods);
-
-             if (! imag0)      /* ((a)(c-id))/divisor */
-               {       /* (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)) */
+             temp1 = expand_binop (submode, smul_optab, real1, real1,
+                                   NULL_RTX, unsignedp, methods);
+
+             temp2 = expand_binop (submode, smul_optab, imag1, imag1,
+                                   NULL_RTX, unsignedp, methods);
+
+             if (temp1 == 0 || temp2 == 0)
+               break;
+
+             divisor = expand_binop (submode, add_optab, temp1, temp2,
+                                     NULL_RTX, unsignedp, methods);
+             if (divisor == 0)
+               break;
+
+             if (imag0 == 0)
+               {
+                 /* ((a)(c-id))/divisor */
+                 /* (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)) */
+
                  /* Calculate the dividend */
-                 real_t = expand_binop (submode, mulopt, real0, real1,
-                                        0, unsignedp, methods);
+                 real_t = expand_binop (submode, smul_optab, real0, real1,
+                                        NULL_RTX, unsignedp, methods);
                  
-                 imag_t
-                   = expand_unop (submode, neg_optab,
-                                  expand_binop (submode, mulopt, real0, imag1,
-                                                0, unsignedp, methods),
-                                  0, unsignedp);
+                 imag_t = expand_binop (submode, smul_optab, real0, imag1,
+                                        NULL_RTX, unsignedp, methods);
+
+                 if (real_t == 0 || imag_t == 0)
+                   break;
+
+                 imag_t = expand_unop (submode, neg_optab, imag_t,
+                                       NULL_RTX, unsignedp);
                }
-             else              /* ((a+ib)(c-id))/divider */
+             else
                {
+                 /* ((a+ib)(c-id))/divider */
                  /* Calculate the dividend */
-                 real_t = expand_binop (submode, add_optab,
-                                        expand_binop (submode, mulopt,
-                                                      real0, real1,
-                                                      0, unsignedp, methods),
-                                        expand_binop (submode, mulopt,
-                                                      imag0, imag1,
-                                                      0, unsignedp, methods),
-                                        0, unsignedp, methods);
+                 temp1 = expand_binop (submode, smul_optab, real0, real1,
+                                       NULL_RTX, unsignedp, methods);
+
+                 temp2 = expand_binop (submode, smul_optab, imag0, imag1,
+                                       NULL_RTX, unsignedp, methods);
+
+                 if (temp1 == 0 || temp2 == 0)
+                   break;
+
+                 real_t = expand_binop (submode, add_optab, temp1, temp2,
+                                        NULL_RTX, unsignedp, methods);
                  
-                 imag_t = expand_binop (submode, sub_optab,
-                                        expand_binop (submode, mulopt,
-                                                      imag0, real1,
-                                                      0, unsignedp, methods),
-                                        expand_binop (submode, mulopt,
-                                                      real0, imag1,
-                                                      0, unsignedp, methods),
-                                        0, unsignedp, methods);
+                 temp1 = expand_binop (submode, smul_optab, imag0, real1,
+                                       NULL_RTX, unsignedp, methods);
+
+                 temp2 = expand_binop (submode, smul_optab, real0, imag1,
+                                       NULL_RTX, unsignedp, methods);
+
+                 if (temp1 == 0 || temp2 == 0)
+                   break;
 
+                 imag_t = expand_binop (submode, sub_optab, temp1, temp2,
+                                        NULL_RTX, unsignedp, methods);
+
+                 if (real_t == 0 || imag_t == 0)
+                   break;
                }
 
-             res = expand_binop (submode, binoptab, real_t, divisor,
-                                 realr, unsignedp, methods);
-             if (res != realr)
+             if (class == MODE_COMPLEX_FLOAT)
+               res = expand_binop (submode, binoptab, real_t, divisor,
+                                   realr, unsignedp, methods);
+             else
+               res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+                                    real_t, divisor, realr, unsignedp);
+
+             if (res == 0)
+               break;
+             else if (res != realr)
                emit_move_insn (realr, res);
 
-             res = expand_binop (submode, binoptab, imag_t, divisor,
-                                 imagr, unsignedp, methods);
-             if (res != imagr)
+             if (class == MODE_COMPLEX_FLOAT)
+               res = expand_binop (submode, binoptab, imag_t, divisor,
+                                   imagr, unsignedp, methods);
+             else
+               res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+                                    imag_t, divisor, imagr, unsignedp);
+
+             if (res == 0)
+               break;
+             else if (res != imagr)
                emit_move_insn (imagr, res);
+
+             ok = 1;
            }
          break;
          
@@ -1326,15 +1491,18 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
       seq = get_insns ();
       end_sequence ();
 
-      if (binoptab->code != UNKNOWN)
-       equiv_value
-         = gen_rtx (binoptab->code, mode, copy_rtx (op0), copy_rtx (op1));
-      else
-       equiv_value = 0;
+      if (ok)
+       {
+         if (binoptab->code != UNKNOWN)
+           equiv_value
+             = gen_rtx (binoptab->code, mode, copy_rtx (op0), copy_rtx (op1));
+         else
+           equiv_value = 0;
          
-      emit_no_conflict_block (seq, target, op0, op1, equiv_value);
+         emit_no_conflict_block (seq, target, op0, op1, equiv_value);
       
-      return target;
+         return target;
+       }
     }
 
   /* It can't be open-coded in this mode.
@@ -1347,6 +1515,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
       rtx funexp = binoptab->handlers[(int) mode].libfunc;
       rtx op1x = op1;
       enum machine_mode op1_mode = mode;
+      rtx value;
 
       start_sequence ();
 
@@ -1358,16 +1527,21 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
          op1x = convert_to_mode (word_mode, op1, 1);
        }
 
+      if (GET_MODE (op0) != VOIDmode
+         && GET_MODE (op0) != mode)
+       op0 = convert_to_mode (mode, op0, unsignedp);
+
       /* 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, op1x, op1_mode);
+      value = emit_library_call_value (binoptab->handlers[(int) mode].libfunc,
+                                      NULL_RTX, 1, mode, 2,
+                                      op0, mode, op1x, op1_mode);
 
       insns = get_insns ();
       end_sequence ();
 
       target = gen_reg_rtx (mode);
-      emit_libcall_block (insns, target, hard_libcall_value (mode),
+      emit_libcall_block (insns, target, value,
                          gen_rtx (binoptab->code, mode, op0, op1));
 
       return target;
@@ -1413,13 +1587,16 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
              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)
+                  || binoptab == smul_optab || binoptab == ashl_optab)
                  && class == MODE_INT)
                no_extend = 1;
 
-             xop0 = widen_operand (xop0, wider_mode, unsignedp, no_extend);
-             xop1 = widen_operand (xop1, wider_mode, unsignedp, no_extend);
+             xop0 = widen_operand (xop0, wider_mode, mode,
+                                   unsignedp, no_extend);
+
+             /* The second operand of a shift must always be extended.  */
+             xop1 = widen_operand (xop1, wider_mode, mode, unsignedp,
+                                   no_extend && binoptab != ashl_optab);
 
              temp = expand_binop (wider_mode, binoptab, xop0, xop1, NULL_RTX,
                                   unsignedp, methods);
@@ -1613,10 +1790,10 @@ expand_twoval_binop (binoptab, op0, op1, targ0, targ1, unsignedp)
              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),
+                                      convert_modes (wider_mode, mode, op0,
+                                                     unsignedp),
+                                      convert_modes (wider_mode, mode, op1,
+                                                     unsignedp),
                                       t0, t1, unsignedp))
                {
                  convert_move (targ0, t0, unsignedp);
@@ -1725,7 +1902,7 @@ expand_unop (mode, unoptab, op0, target, unsignedp)
               the narrow operand, as long as we will truncate the
               results to the same narrowness.  */
 
-           xop0 = widen_operand (xop0, wider_mode, unsignedp,
+           xop0 = widen_operand (xop0, wider_mode, mode, unsignedp,
                                  (unoptab == neg_optab
                                   || unoptab == one_cmpl_optab)
                                  && class == MODE_INT);
@@ -1832,18 +2009,19 @@ expand_unop (mode, unoptab, op0, target, unsignedp)
     {
       rtx insns;
       rtx funexp = unoptab->handlers[(int) mode].libfunc;
+      rtx value;
 
       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);
+      value = emit_library_call_value (unoptab->handlers[(int) mode].libfunc,
+                                      NULL_RTX, 1, mode, 1, op0, mode);
       insns = get_insns ();
       end_sequence ();
 
       target = gen_reg_rtx (mode);
-      emit_libcall_block (insns, target, hard_libcall_value (mode),
+      emit_libcall_block (insns, target, value,
                          gen_rtx (unoptab->code, mode, op0));
 
       return target;
@@ -1866,7 +2044,7 @@ expand_unop (mode, unoptab, op0, target, unsignedp)
                 the narrow operand, as long as we will truncate the
                 results to the same narrowness.  */
 
-             xop0 = widen_operand (xop0, wider_mode, unsignedp,
+             xop0 = widen_operand (xop0, wider_mode, mode, unsignedp,
                                    (unoptab == neg_optab
                                     || unoptab == one_cmpl_optab)
                                    && class == MODE_INT);
@@ -1892,6 +2070,17 @@ expand_unop (mode, unoptab, op0, target, unsignedp)
        }
     }
 
+  /* If there is no negate operation, try doing a subtract from zero.
+     The US Software GOFAST library needs this.  */
+  if (unoptab == neg_optab)
+    {    
+      rtx temp;
+      temp = expand_binop (mode, sub_optab, CONST0_RTX (mode), op0,
+                          target, unsignedp, OPTAB_LIB_WIDEN);
+      if (temp)
+       return temp;
+    }
+      
   return 0;
 }
 \f
@@ -1902,6 +2091,97 @@ expand_unop (mode, unoptab, op0, target, unsignedp)
    MODE is the mode of the operand; the mode of the result is
    different but can be deduced from MODE.
 
+   UNSIGNEDP is relevant if extension is needed.  */
+
+rtx
+expand_abs (mode, op0, target, unsignedp, safe)
+     enum machine_mode mode;
+     rtx op0;
+     rtx target;
+     int unsignedp;
+     int safe;
+{
+  rtx temp, op1;
+
+  /* First try to do it with a special abs instruction.  */
+  temp = expand_unop (mode, abs_optab, op0, target, 0);
+  if (temp != 0)
+    return temp;
+
+  /* If this machine has expensive jumps, we can do integer absolute
+     value of X as (((signed) x >> (W-1)) ^ x) - ((signed) x >> (W-1)),
+     where W is the width of MODE.  */
+
+  if (GET_MODE_CLASS (mode) == MODE_INT && BRANCH_COST >= 2)
+    {
+      rtx extended = expand_shift (RSHIFT_EXPR, mode, op0,
+                                  size_int (GET_MODE_BITSIZE (mode) - 1),
+                                  NULL_RTX, 0);
+
+      temp = expand_binop (mode, xor_optab, extended, op0, target, 0,
+                          OPTAB_LIB_WIDEN);
+      if (temp != 0)
+       temp = expand_binop (mode, sub_optab, temp, extended, target, 0,
+                            OPTAB_LIB_WIDEN);
+
+      if (temp != 0)
+       return temp;
+    }
+
+  /* If that does not win, use conditional jump and negate.  */
+
+  /* It is safe to use the target if it is the same
+     as the source if this is also a pseudo register */
+  if (op0 == target && GET_CODE (op0) == REG
+      && REGNO (op0) >= FIRST_PSEUDO_REGISTER)
+    safe = 1;
+
+  op1 = gen_label_rtx ();
+  if (target == 0 || ! safe
+      || GET_MODE (target) != mode
+      || (GET_CODE (target) == MEM && MEM_VOLATILE_P (target))
+      || (GET_CODE (target) == REG
+         && REGNO (target) < FIRST_PSEUDO_REGISTER))
+    target = gen_reg_rtx (mode);
+
+  emit_move_insn (target, op0);
+  NO_DEFER_POP;
+
+  /* If this mode is an integer too wide to compare properly,
+     compare word by word.  Rely on CSE to optimize constant cases.  */
+  if (GET_MODE_CLASS (mode) == MODE_INT && ! can_compare_p (mode))
+    do_jump_by_parts_greater_rtx (mode, 0, target, const0_rtx, 
+                                 NULL_RTX, op1);
+  else
+    {
+      temp = compare_from_rtx (target, CONST0_RTX (mode), GE, 0, mode,
+                              NULL_RTX, 0);
+      if (temp == const1_rtx)
+       return target;
+      else if (temp != const0_rtx)
+       {
+         if (bcc_gen_fctn[(int) GET_CODE (temp)] != 0)
+           emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (temp)]) (op1));
+         else
+           abort ();
+       }
+    }
+
+  op0 = expand_unop (mode, neg_optab, target, target, 0);
+  if (op0 != target)
+    emit_move_insn (target, op0);
+  emit_label (op1);
+  OK_DEFER_POP;
+  return target;
+}
+\f
+/* Emit code to compute the absolute value of OP0, with result to
+   TARGET if convenient.  (TARGET may be 0.)  The return value says
+   where the result actually is to be found.
+
+   MODE is the mode of the operand; the mode of the result is
+   different but can be deduced from MODE.
+
    UNSIGNEDP is relevant for complex integer modes.  */
 
 rtx
@@ -1989,7 +2269,7 @@ expand_complex_abs (mode, op0, target, unsignedp)
        {
          rtx xop0 = op0;
 
-         xop0 = convert_to_mode (wider_mode, xop0, unsignedp);
+         xop0 = convert_modes (wider_mode, mode, xop0, unsignedp);
          temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp);
 
          if (temp)
@@ -2017,12 +2297,15 @@ expand_complex_abs (mode, op0, target, unsignedp)
 
       real = gen_realpart (submode, op0);
       imag = gen_imagpart (submode, op0);
+
       /* Square both parts.  */
-      real = expand_mult (mode, real, real, NULL_RTX, 0);
-      imag = expand_mult (mode, imag, imag, NULL_RTX, 0);
+      real = expand_mult (submode, real, real, NULL_RTX, 0);
+      imag = expand_mult (submode, imag, imag, NULL_RTX, 0);
+
       /* Sum the parts.  */
-      total = expand_binop (submode, add_optab, real, imag, 0,
+      total = expand_binop (submode, add_optab, real, imag, NULL_RTX,
                            0, OPTAB_LIB_WIDEN);
+
       /* Get sqrt in TARGET.  Set TARGET to where the result is.  */
       target = expand_unop (submode, sqrt_optab, total, target, 0);
       if (target == 0)
@@ -2036,18 +2319,19 @@ expand_complex_abs (mode, op0, target, unsignedp)
     {
       rtx insns;
       rtx funexp = abs_optab->handlers[(int) mode].libfunc;
+      rtx value;
 
       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 (abs_optab->handlers[(int) mode].libfunc,
-                        1, mode, 1, op0, mode);
+      value = emit_library_call_value (abs_optab->handlers[(int) mode].libfunc,
+                                      NULL_RTX, 1, submode, 1, op0, mode);
       insns = get_insns ();
       end_sequence ();
 
       target = gen_reg_rtx (submode);
-      emit_libcall_block (insns, target, hard_libcall_value (submode),
+      emit_libcall_block (insns, target, value,
                          gen_rtx (abs_optab->code, mode, op0));
 
       return target;
@@ -2064,7 +2348,7 @@ expand_complex_abs (mode, op0, target, unsignedp)
        {
          rtx xop0 = op0;
 
-         xop0 = convert_to_mode (wider_mode, xop0, unsignedp);
+         xop0 = convert_modes (wider_mode, mode, xop0, unsignedp);
 
          temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp);
 
@@ -2110,7 +2394,10 @@ emit_unop_insn (icode, target, op0, code)
 
   op0 = protect_from_queue (op0, 0);
 
-  if (flag_force_mem)
+  /* Sign and zero extension from memory is often done specially on
+     RISC machines, so forcing into a register here can pessimize
+     code.  */
+  if (flag_force_mem && code != SIGN_EXTEND && code != ZERO_EXTEND)
     op0 = force_not_mem (op0);
 
   /* Now, if insn does not accept our operands, put them into pseudos.  */
@@ -2147,9 +2434,7 @@ emit_unop_insn (icode, target, op0, code)
 
    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.
+   are first emitted, followed by the block as described above.  
 
    TARGET, OP0, and OP1 are the output and inputs of the operations,
    respectively.  OP1 may be zero for a unary operation.
@@ -2158,7 +2443,8 @@ emit_unop_insn (icode, target, op0, code)
    on the last insn.
 
    If TARGET is not a register, INSNS is simply emitted with no special
-   processing.
+   processing.  Likewise if anything in INSNS is not an INSN or if
+   there is a libcall block inside INSNS.
 
    The final insn emitted is returned.  */
 
@@ -2173,6 +2459,11 @@ emit_no_conflict_block (insns, target, op0, op1, equiv)
 
   if (GET_CODE (target) != REG || reload_in_progress)
     return emit_insns (insns);
+  else
+    for (insn = insns; insn; insn = NEXT_INSN (insn))
+      if (GET_CODE (insn) != INSN
+         || find_reg_note (insn, REG_LIBCALL, NULL_RTX))
+       return emit_insns (insns);
 
   /* First emit all insns that do not store into words of the output and remove
      these from the list.  */
@@ -2183,9 +2474,6 @@ emit_no_conflict_block (insns, target, op0, op1, equiv)
 
       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)
@@ -2338,8 +2626,10 @@ emit_libcall_block (insns, target, result, equiv)
     }
 
   last = emit_move_insn (target, result);
-  REG_NOTES (last) = gen_rtx (EXPR_LIST,
-                             REG_EQUAL, copy_rtx (equiv), REG_NOTES (last));
+  if (mov_optab->handlers[(int) GET_MODE (target)].insn_code
+      != CODE_FOR_nothing)
+    REG_NOTES (last) = gen_rtx (EXPR_LIST,
+                               REG_EQUAL, copy_rtx (equiv), REG_NOTES (last));
 
   if (prev == 0)
     first = get_insns ();
@@ -2476,18 +2766,32 @@ emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
       else
 #endif
        {
+         rtx result;
+
 #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);
+                            convert_to_mode (TYPE_MODE (sizetype), size,
+                                             TREE_UNSIGNED (sizetype)),
+                            TYPE_MODE (sizetype));
 #else
          emit_library_call (bcmp_libfunc, 0,
                             TYPE_MODE (integer_type_node), 3,
                             XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
-                            size, Pmode);
+                            convert_to_mode (TYPE_MODE (integer_type_node),
+                                             size,
+                                             TREE_UNSIGNED (integer_type_node)),
+                            TYPE_MODE (integer_type_node));
 #endif
-         emit_cmp_insn (hard_libcall_value (TYPE_MODE (integer_type_node)),
+
+         /* Immediately move the result of the libcall into a pseudo
+            register so reload doesn't clobber the value if it needs
+            the return register for a spill reg.  */
+         result = gen_reg_rtx (TYPE_MODE (integer_type_node));
+         emit_move_insn (result,
+                         hard_libcall_value (TYPE_MODE (integer_type_node)));
+         emit_cmp_insn (result,
                         const0_rtx, comparison, NULL_RTX,
                         TYPE_MODE (integer_type_node), 0, 0);
        }
@@ -2549,8 +2853,8 @@ emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
            {
              x = protect_from_queue (x, 0);
              y = protect_from_queue (y, 0);
-             x = convert_to_mode (wider_mode, x, unsignedp);
-             y = convert_to_mode (wider_mode, y, unsignedp);
+             x = convert_modes (wider_mode, mode, x, unsignedp);
+             y = convert_modes (wider_mode, mode, y, unsignedp);
              emit_cmp_insn (x, y, comparison, NULL_RTX,
                             wider_mode, unsignedp, align);
              return;
@@ -2564,6 +2868,8 @@ emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
       && class != MODE_FLOAT)
     {
       rtx libfunc = cmp_optab->handlers[(int) mode].libfunc;
+      rtx result;
+
       /* If we want unsigned, and this mode has a distinct unsigned
         comparison routine, use that.  */
       if (unsignedp && ucmp_optab->handlers[(int) mode].libfunc)
@@ -2572,11 +2878,16 @@ emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
       emit_library_call (libfunc, 1,
                         word_mode, 2, x, mode, y, mode);
 
+      /* Immediately move the result of the libcall into a pseudo
+        register so reload doesn't clobber the value if it needs
+        the return register for a spill reg.  */
+      result = gen_reg_rtx (word_mode);
+      emit_move_insn (result, hard_libcall_value (word_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 (word_mode), const1_rtx,
+      emit_cmp_insn (result, const1_rtx,
                     comparison, NULL_RTX, word_mode, unsignedp, 0);
       return;
     }
@@ -2608,15 +2919,43 @@ can_compare_p (mode)
 /* 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
+void
 emit_float_lib_cmp (x, y, comparison)
      rtx x, y;
      enum rtx_code comparison;
 {
   enum machine_mode mode = GET_MODE (x);
-  rtx libfunc;
+  rtx libfunc = 0;
+  rtx result;
 
-  if (mode == SFmode)
+  if (mode == HFmode)
+    switch (comparison)
+      {
+      case EQ:
+       libfunc = eqhf2_libfunc;
+       break;
+
+      case NE:
+       libfunc = nehf2_libfunc;
+       break;
+
+      case GT:
+       libfunc = gthf2_libfunc;
+       break;
+
+      case GE:
+       libfunc = gehf2_libfunc;
+       break;
+
+      case LT:
+       libfunc = lthf2_libfunc;
+       break;
+
+      case LE:
+       libfunc = lehf2_libfunc;
+       break;
+      }
+  else if (mode == SFmode)
     switch (comparison)
       {
       case EQ:
@@ -2746,10 +3085,19 @@ emit_float_lib_cmp (x, y, comparison)
       abort ();
     }
 
+  if (libfunc == 0)
+    abort ();
+
   emit_library_call (libfunc, 1,
                     word_mode, 2, x, mode, y, mode);
 
-  emit_cmp_insn (hard_libcall_value (word_mode), const0_rtx, comparison,
+  /* Immediately move the result of the libcall into a pseudo
+     register so reload doesn't clobber the value if it needs
+     the return register for a spill reg.  */
+  result = gen_reg_rtx (word_mode);
+  emit_move_insn (result, hard_libcall_value (word_mode));
+
+  emit_cmp_insn (result, const0_rtx, comparison,
                 NULL_RTX, word_mode, 0, 0);
 }
 \f
@@ -2767,6 +3115,148 @@ emit_indirect_jump (loc)
   emit_barrier ();
 }
 \f
+#ifdef HAVE_conditional_move
+
+/* Emit a conditional move instruction if the machine supports one for that
+   condition and machine mode.
+
+   OP0 and OP1 are the operands that should be compared using CODE.  CMODE is
+   the mode to use should they be constants.  If it is VOIDmode, they cannot
+   both be constants.
+
+   OP2 should be stored in TARGET if the comparison is true, otherwise OP3
+   should be stored there.  MODE is the mode to use should they be constants.
+   If it is VOIDmode, they cannot both be constants.
+
+   The result is either TARGET (perhaps modified) or NULL_RTX if the operation
+   is not supported.  */
+
+rtx
+emit_conditional_move (target, code, op0, op1, cmode, op2, op3, mode,
+                      unsignedp)
+     rtx target;
+     enum rtx_code code;
+     rtx op0, op1;
+     enum machine_mode cmode;
+     rtx op2, op3;
+     enum machine_mode mode;
+     int unsignedp;
+{
+  rtx tem, subtarget, comparison, insn;
+  enum insn_code icode;
+
+  /* If one operand is constant, make it the second one.  Only do this
+     if the other operand is not constant as well.  */
+
+  if ((CONSTANT_P (op0) && ! CONSTANT_P (op1))
+      || (GET_CODE (op0) == CONST_INT && GET_CODE (op1) != CONST_INT))
+    {
+      tem = op0;
+      op0 = op1;
+      op1 = tem;
+      code = swap_condition (code);
+    }
+
+  if (cmode == VOIDmode)
+    cmode = GET_MODE (op0);
+
+  if ((CONSTANT_P (op2) && ! CONSTANT_P (op3))
+      || (GET_CODE (op2) == CONST_INT && GET_CODE (op3) != CONST_INT))
+    {
+      tem = op2;
+      op2 = op3;
+      op3 = tem;
+      /* ??? This may not be appropriate (consider IEEE).  Perhaps we should
+        call can_reverse_comparison_p here and bail out if necessary.
+        It's not clear whether we need to do this canonicalization though.  */
+      code = reverse_condition (code);
+    }
+
+  if (mode == VOIDmode)
+    mode = GET_MODE (op2);
+
+  icode = movcc_gen_code[mode];
+
+  if (icode == CODE_FOR_nothing)
+    return 0;
+
+  if (flag_force_mem)
+    {
+      op2 = force_not_mem (op2);
+      op3 = force_not_mem (op3);
+    }
+
+  if (target)
+    target = protect_from_queue (target, 1);
+  else
+    target = gen_reg_rtx (mode);
+
+  subtarget = target;
+
+  emit_queue ();
+
+  op2 = protect_from_queue (op2, 0);
+  op3 = protect_from_queue (op3, 0);
+
+  /* If the insn doesn't accept these operands, put them in pseudos.  */
+
+  if (! (*insn_operand_predicate[icode][0])
+      (subtarget, insn_operand_mode[icode][0]))
+    subtarget = gen_reg_rtx (insn_operand_mode[icode][0]);
+
+  if (! (*insn_operand_predicate[icode][2])
+      (op2, insn_operand_mode[icode][2]))
+    op2 = copy_to_mode_reg (insn_operand_mode[icode][2], op2);
+
+  if (! (*insn_operand_predicate[icode][3])
+      (op3, insn_operand_mode[icode][3]))
+    op3 = copy_to_mode_reg (insn_operand_mode[icode][3], op3);
+
+  /* Everything should now be in the suitable form, so emit the compare insn
+     and then the conditional move.  */
+
+  comparison 
+    = compare_from_rtx (op0, op1, code, unsignedp, cmode, NULL_RTX, 0);
+
+  /* ??? Watch for const0_rtx (nop) and const_true_rtx (unconditional)?  */
+  if (GET_CODE (comparison) != code)
+    /* This shouldn't happen.  */
+    abort ();
+  
+  insn = GEN_FCN (icode) (subtarget, comparison, op2, op3);
+
+  /* If that failed, then give up.  */
+  if (insn == 0)
+    return 0;
+
+  emit_insn (insn);
+
+  if (subtarget != target)
+    convert_move (target, subtarget, 0);
+
+  return target;
+}
+
+/* Return non-zero if a conditional move of mode MODE is supported.
+
+   This function is for combine so it can tell whether an insn that looks
+   like a conditional move is actually supported by the hardware.  If we
+   guess wrong we lose a bit on optimization, but that's it.  */
+/* ??? sparc64 supports conditionally moving integers values based on fp
+   comparisons, and vice versa.  How do we handle them?  */
+
+int
+can_conditionally_move_p (mode)
+     enum machine_mode mode;
+{
+  if (movcc_gen_code[mode] != CODE_FOR_nothing)
+    return 1;
+
+  return 0;
+}
+
+#endif /* HAVE_conditional_move */
+\f
 /* These three functions generate an insn body and return it
    rather than emitting the insn.
 
@@ -3038,23 +3528,75 @@ expand_float (to, from, unsignedp)
       /* Look for a usable floating mode FMODE wider than the source and at
         least as wide as the target.  Using FMODE will avoid rounding woes
         with unsigned values greater than the signed maximum value.  */
+
       for (fmode = GET_MODE (to);  fmode != VOIDmode;
           fmode = GET_MODE_WIDER_MODE (fmode))
        if (GET_MODE_BITSIZE (GET_MODE (from)) < GET_MODE_BITSIZE (fmode)
            && can_float_p (fmode, GET_MODE (from), 0) != CODE_FOR_nothing)
          break;
+
       if (fmode == VOIDmode)
        {
-         /* There is no such mode.  Pretend the target is wide enough.
-            This may cause rounding problems, unfortunately.  */
+         /* There is no such mode.  Pretend the target is wide enough.  */
          fmode = GET_MODE (to);
+
+         /* Avoid double-rounding when TO is narrower than FROM.  */
+         if ((significand_size (fmode) + 1)
+             < GET_MODE_BITSIZE (GET_MODE (from)))
+           {
+             rtx temp1;
+             rtx neglabel = gen_label_rtx ();
+
+             /* Don't use TARGET if it isn't a register, is a hard register, 
+                or is the wrong mode.  */
+             if (GET_CODE (target) != REG
+                 || REGNO (target) < FIRST_PSEUDO_REGISTER
+                 || GET_MODE (target) != fmode)
+               target = gen_reg_rtx (fmode);
+
+             imode = GET_MODE (from);
+             do_pending_stack_adjust ();
+
+             /* Test whether the sign bit is set.  */
+             emit_cmp_insn (from, const0_rtx, GE, NULL_RTX, imode, 0, 0);
+             emit_jump_insn (gen_blt (neglabel));
+
+             /* The sign bit is not set.  Convert as signed.  */
+             expand_float (target, from, 0);
+             emit_jump_insn (gen_jump (label));
+             emit_barrier ();
+
+             /* The sign bit is set.
+                Convert to a usable (positive signed) value by shifting right
+                one bit, while remembering if a nonzero bit was shifted
+                out; i.e., compute  (from & 1) | (from >> 1).  */
+
+             emit_label (neglabel);
+             temp = expand_binop (imode, and_optab, from, const1_rtx,
+                                  NULL_RTX, 1, OPTAB_LIB_WIDEN);
+             temp1 = expand_shift (RSHIFT_EXPR, imode, from, integer_one_node,
+                                   NULL_RTX, 1);
+             temp = expand_binop (imode, ior_optab, temp, temp1, temp, 1, 
+                                  OPTAB_LIB_WIDEN);
+             expand_float (target, temp, 0);
+
+             /* Multiply by 2 to undo the shift above.  */
+             temp = expand_binop (fmode, add_optab, target, target,
+                                    target, 0, OPTAB_LIB_WIDEN);
+             if (temp != target)
+               emit_move_insn (target, temp);
+
+             do_pending_stack_adjust ();
+             emit_label (label);
+             goto done;
+           }
        }
 
       /* If we are about to do some arithmetic to correct for an
         unsigned operand, do it in a pseudo-register.  */
 
       if (GET_MODE (to) != fmode
-         || GET_CODE (to) != REG || REGNO (to) <= LAST_VIRTUAL_REGISTER)
+         || GET_CODE (to) != REG || REGNO (to) < FIRST_PSEUDO_REGISTER)
        target = gen_reg_rtx (fmode);
 
       /* Convert as signed integer to floating.  */
@@ -3066,26 +3608,29 @@ expand_float (to, from, unsignedp)
       do_pending_stack_adjust ();
       emit_cmp_insn (from, const0_rtx, GE, NULL_RTX, 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 (fmode, add_optab, target,
-                          immed_real_const_1 (offset, fmode),
+                          CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode),
                           target, 0, OPTAB_LIB_WIDEN);
       if (temp != target)
        emit_move_insn (target, temp);
+
       do_pending_stack_adjust ();
       emit_label (label);
+      goto done;
     }
-  else
 #endif
 
-  /* No hardware instruction available; call a library rotine to convert from
+  /* No hardware instruction available; call a library routine to convert from
      SImode, DImode, or TImode into SFmode, DFmode, XFmode, or TFmode.  */
     {
       rtx libfcn;
       rtx insns;
+      rtx value;
 
       to = protect_from_queue (to, 1);
       from = protect_from_queue (from, 0);
@@ -3145,14 +3690,18 @@ expand_float (to, from, unsignedp)
 
       start_sequence ();
 
-      emit_library_call (libfcn, 1, GET_MODE (to), 1, from, GET_MODE (from));
+      value = emit_library_call_value (libfcn, NULL_RTX, 1,
+                                      GET_MODE (to),
+                                      1, from, GET_MODE (from));
       insns = get_insns ();
       end_sequence ();
 
-      emit_libcall_block (insns, target, hard_libcall_value (GET_MODE (to)),
+      emit_libcall_block (insns, target, value,
                          gen_rtx (FLOAT, GET_MODE (to), from));
     }
 
+ done:
+
   /* Copy result to requested destination
      if we have been computing in a temp location.  */
 
@@ -3249,7 +3798,7 @@ expand_fix (to, from, unsignedp)
 
          bitsize = GET_MODE_BITSIZE (GET_MODE (to));
          offset = REAL_VALUE_LDEXP (dconst1, bitsize - 1);
-         limit = immed_real_const_1 (offset, fmode);
+         limit = CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode);
          lab1 = gen_label_rtx ();
          lab2 = gen_label_rtx ();
 
@@ -3289,13 +3838,16 @@ expand_fix (to, from, unsignedp)
 
          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),
-                                              copy_rtx (from)),
-                                     REG_NOTES (insn));
-
+         if (mov_optab->handlers[(int) GET_MODE (to)].insn_code
+             != CODE_FOR_nothing)
+           {
+             /* 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),
+                                                  copy_rtx (from)),
+                                         REG_NOTES (insn));
+           }
          return;
        }
 #endif
@@ -3360,6 +3912,7 @@ expand_fix (to, from, unsignedp)
   if (libfcn)
     {
       rtx insns;
+      rtx value;
 
       to = protect_from_queue (to, 1);
       from = protect_from_queue (from, 0);
@@ -3369,19 +3922,24 @@ expand_fix (to, from, unsignedp)
 
       start_sequence ();
 
-      emit_library_call (libfcn, 1, GET_MODE (to), 1, from, GET_MODE (from));
+      value = emit_library_call_value (libfcn, NULL_RTX, 1, 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,
+      emit_libcall_block (insns, target, value,
+                         gen_rtx (unsignedp ? UNSIGNED_FIX : FIX,
                                   GET_MODE (to), from));
     }
       
-  if (GET_MODE (to) == GET_MODE (target))
-    emit_move_insn (to, target);
-  else
-    convert_move (to, target, 0);
+  if (target != to)
+    {
+      if (GET_MODE (to) == GET_MODE (target))
+        emit_move_insn (to, target);
+      else
+        convert_move (to, target, 0);
+    }
 }
 \f
 static optab
@@ -3426,7 +3984,7 @@ init_libfuncs (optable, first_mode, last_mode, opname, suffix)
     register int first_mode;
     register int last_mode;
     register char *opname;
-    register char suffix;
+    register int suffix;
 {
   register int mode;
   register unsigned opname_len = strlen (opname);
@@ -3464,7 +4022,7 @@ static void
 init_integral_libfuncs (optable, opname, suffix)
     register optab optable;
     register char *opname;
-    register char suffix;
+    register int suffix;
 {
   init_libfuncs (optable, SImode, TImode, opname, suffix);
 }
@@ -3478,7 +4036,7 @@ static void
 init_floating_libfuncs (optable, opname, suffix)
     register optab optable;
     register char *opname;
-    register char suffix;
+    register int suffix;
 {
   init_libfuncs (optable, SFmode, TFmode, opname, suffix);
 }
@@ -3492,7 +4050,7 @@ static void
 init_complex_libfuncs (optable, opname, suffix)
     register optab optable;
     register char *opname;
-    register char suffix;
+    register int suffix;
 {
   init_libfuncs (optable, SCmode, TCmode, opname, suffix);
 }
@@ -3531,9 +4089,16 @@ init_optabs ()
   for (i = 0; i < NUM_RTX_CODE; i++)
     setcc_gen_code[i] = CODE_FOR_nothing;
 
+#ifdef HAVE_conditional_move
+  for (i = 0; i < NUM_MACHINE_MODES; i++)
+    movcc_gen_code[i] = CODE_FOR_nothing;
+#endif
+
   add_optab = init_optab (PLUS);
   sub_optab = init_optab (MINUS);
   smul_optab = init_optab (MULT);
+  smul_highpart_optab = init_optab (UNKNOWN);
+  umul_highpart_optab = init_optab (UNKNOWN);
   smul_widen_optab = init_optab (UNKNOWN);
   umul_widen_optab = init_optab (UNKNOWN);
   sdiv_optab = init_optab (DIV);
@@ -3549,7 +4114,6 @@ init_optabs ()
   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);
@@ -3574,6 +4138,7 @@ init_optabs ()
   for (i = 0; i < NUM_MACHINE_MODES; i++)
     {
       movstr_optab[i] = CODE_FOR_nothing;
+      clrstr_optab[i] = CODE_FOR_nothing;
 
 #ifdef HAVE_SECONDARY_RELOADS
       reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing;
@@ -3615,10 +4180,7 @@ init_optabs ()
   init_integral_libfuncs (xor_optab, "xor", '3');
   init_integral_libfuncs (ashl_optab, "ashl", '3');
   init_integral_libfuncs (ashr_optab, "ashr", '3');
-  init_integral_libfuncs (lshl_optab, "lshl", '3');
   init_integral_libfuncs (lshr_optab, "lshr", '3');
-  init_integral_libfuncs (rotl_optab, "rotl", '3');
-  init_integral_libfuncs (rotr_optab, "rotr", '3');
   init_integral_libfuncs (smin_optab, "min", '3');
   init_floating_libfuncs (smin_optab, "min", '3');
   init_integral_libfuncs (smax_optab, "max", '3');
@@ -3643,10 +4205,6 @@ init_optabs ()
   smul_optab->handlers[(int) DImode].libfunc
     = gen_rtx (SYMBOL_REF, Pmode, MULDI3_LIBCALL);
 #endif
-#ifdef MULTI3_LIBCALL
-  smul_optab->handlers[(int) TImode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, MULTI3_LIBCALL);
-#endif
 
 #ifdef DIVSI3_LIBCALL
   sdiv_optab->handlers[(int) SImode].libfunc
@@ -3656,10 +4214,6 @@ init_optabs ()
   sdiv_optab->handlers[(int) DImode].libfunc
     = gen_rtx (SYMBOL_REF, Pmode, DIVDI3_LIBCALL);
 #endif
-#ifdef DIVTI3_LIBCALL
-  sdiv_optab->handlers[(int) TImode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, DIVTI3_LIBCALL);
-#endif
 
 #ifdef UDIVSI3_LIBCALL
   udiv_optab->handlers[(int) SImode].libfunc
@@ -3669,11 +4223,6 @@ init_optabs ()
   udiv_optab->handlers[(int) DImode].libfunc
     = gen_rtx (SYMBOL_REF, Pmode, UDIVDI3_LIBCALL);
 #endif
-#ifdef UDIVTI3_LIBCALL
-  udiv_optab->handlers[(int) TImode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, UDIVTI3_LIBCALL);
-#endif
-
 
 #ifdef MODSI3_LIBCALL
   smod_optab->handlers[(int) SImode].libfunc
@@ -3683,11 +4232,6 @@ init_optabs ()
   smod_optab->handlers[(int) DImode].libfunc
     = gen_rtx (SYMBOL_REF, Pmode, MODDI3_LIBCALL);
 #endif
-#ifdef MODTI3_LIBCALL
-  smod_optab->handlers[(int) TImode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, MODTI3_LIBCALL);
-#endif
-
 
 #ifdef UMODSI3_LIBCALL
   umod_optab->handlers[(int) SImode].libfunc
@@ -3697,17 +4241,17 @@ init_optabs ()
   umod_optab->handlers[(int) DImode].libfunc
     = gen_rtx (SYMBOL_REF, Pmode, UMODDI3_LIBCALL);
 #endif
-#ifdef UMODTI3_LIBCALL
-  umod_optab->handlers[(int) TImode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, UMODTI3_LIBCALL);
-#endif
 
   /* Use cabs for DC complex abs, since systems generally have cabs.
      Don't define any libcall for SCmode, so that cabs will be used.  */
   abs_optab->handlers[(int) DCmode].libfunc
     = gen_rtx (SYMBOL_REF, Pmode, "cabs");
 
-  ffs_optab->handlers[(int) mode_for_size (BITS_PER_WORD, MODE_INT, 0)] .libfunc
+  /* The ffs function operates on `int'.  */
+#ifndef INT_TYPE_SIZE
+#define INT_TYPE_SIZE BITS_PER_WORD
+#endif
+  ffs_optab->handlers[(int) mode_for_size (INT_TYPE_SIZE, MODE_INT, 0)] .libfunc
     = gen_rtx (SYMBOL_REF, Pmode, "ffs");
 
   extendsfdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__extendsfdf2");
@@ -3729,6 +4273,26 @@ init_optabs ()
   memset_libfunc = gen_rtx (SYMBOL_REF, Pmode, "memset");
   bzero_libfunc = gen_rtx (SYMBOL_REF, Pmode, "bzero");
 
+  throw_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__throw");
+  sjthrow_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__sjthrow");
+  sjpopnthrow_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__sjpopnthrow");
+  terminate_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__terminate");
+#ifndef DONT_USE_BUILTIN_SETJMP
+  setjmp_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__builtin_setjmp");
+  longjmp_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__builtin_longjmp");
+#else
+  setjmp_libfunc = gen_rtx (SYMBOL_REF, Pmode, "setjmp");
+  longjmp_libfunc = gen_rtx (SYMBOL_REF, Pmode, "longjmp");
+#endif
+  get_dynamic_handler_chain_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__get_dynamic_handler_chain");
+
+  eqhf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__eqhf2");
+  nehf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__nehf2");
+  gthf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gthf2");
+  gehf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gehf2");
+  lthf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__lthf2");
+  lehf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__lehf2");
+
   eqsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__eqsf2");
   nesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__nesf2");
   gtsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gtsf2");
@@ -3804,11 +4368,16 @@ init_optabs ()
   fixunstfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunstfsi");
   fixunstfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunstfdi");
   fixunstfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunstfti");
+
+#ifdef INIT_TARGET_OPTABS
+  /* Allow the target to add more libcalls or rename some, etc.  */
+  INIT_TARGET_OPTABS;
+#endif
 }
 \f
 #ifdef BROKEN_LDEXP
 
-/* SCO 3.2 apparently has a broken ldexp. */
+/* SCO 3.2 apparently has a broken ldexp.  */
 
 double
 ldexp(x,n)