OSDN Git Service

2003-02-12 Phil Edwards <pme@gcc.gnu.org>
[pf3gnuchains/gcc-fork.git] / gcc / optabs.c
index 88cdf2f..93b1498 100644 (file)
@@ -1,6 +1,6 @@
 /* Expand the basic unary and binary arithmetic operations, for GNU compiler.
    Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -22,6 +22,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "toplev.h"
 
 /* Include insn-config.h before expr.h so that HAVE_conditional_move
@@ -40,6 +42,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "reload.h"
 #include "ggc.h"
 #include "real.h"
+#include "basic-block.h"
 
 /* Each optab contains info on how this target machine
    can perform a particular operation
@@ -108,8 +111,6 @@ static rtx ftruncify        PARAMS ((rtx));
 static optab new_optab PARAMS ((void));
 static inline optab init_optab PARAMS ((enum rtx_code));
 static inline optab init_optabv        PARAMS ((enum rtx_code));
-static inline int complex_part_zero_p PARAMS ((rtx, enum mode_class,
-                                               enum machine_mode));
 static void init_libfuncs PARAMS ((optab, int, int, const char *, int));
 static void init_integral_libfuncs PARAMS ((optab, const char *, int));
 static void init_floating_libfuncs PARAMS ((optab, const char *, int));
@@ -120,6 +121,13 @@ static void emit_cmp_and_jump_insn_1 PARAMS ((rtx, rtx, enum machine_mode,
                                            enum rtx_code, int, rtx));
 static void prepare_float_lib_cmp PARAMS ((rtx *, rtx *, enum rtx_code *,
                                         enum machine_mode *, int *));
+static rtx expand_vector_binop PARAMS ((enum machine_mode, optab,
+                                       rtx, rtx, rtx, int,
+                                       enum optab_methods));
+static rtx expand_vector_unop PARAMS ((enum machine_mode, optab, rtx, rtx,
+                                      int));
+static rtx widen_clz PARAMS ((enum machine_mode, rtx, rtx));
+static rtx expand_parity PARAMS ((enum machine_mode, rtx, rtx));
 \f
 /* Add a REG_EQUAL note to the last insn in INSNS.  TARGET is being set to
    the result of operation CODE applied to OP0 (and OP1 if it is a binary
@@ -236,33 +244,17 @@ widen_operand (op, mode, oldmode, unsignedp, no_extend)
   return result;
 }
 \f
-/* Test whether either the real or imaginary part of a complex floating
-   point number is 0.0, so that it can be ignored (when compiling
-   with -funsafe-math-optimizations). */
-
-static inline int
-complex_part_zero_p (part, class, submode)
-  rtx part;
-  enum mode_class class;
-  enum machine_mode submode;
-{
-  return part == 0 ||
-         (flag_unsafe_math_optimizations
-          && class == MODE_COMPLEX_FLOAT
-          && part == CONST0_RTX (submode));
-}
-
 /* Generate code to perform a straightforward complex divide.  */
 
 static int
 expand_cmplxdiv_straight (real0, real1, imag0, imag1, realr, imagr, submode,
                          unsignedp, methods, class, binoptab)
-  rtx real0, real1, imag0, imag1, realr, imagr;
-  enum machine_mode submode;
-  int unsignedp;
-  enum optab_methods methods;
-  enum mode_class class;
-  optab binoptab;
+     rtx real0, real1, imag0, imag1, realr, imagr;
+     enum machine_mode submode;
+     int unsignedp;
+     enum optab_methods methods;
+     enum mode_class class;
+     optab binoptab;
 {
   rtx divisor;
   rtx real_t, imag_t;
@@ -305,7 +297,7 @@ expand_cmplxdiv_straight (real0, real1, imag0, imag1, realr, imagr, submode,
   if (divisor == 0)
     return 0;
 
-  if (complex_part_zero_p (imag0, class, submode))
+  if (imag0 == 0)
     {
       /* Mathematically, ((a)(c-id))/divisor.  */
       /* Computationally, (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)).  */
@@ -389,12 +381,12 @@ expand_cmplxdiv_straight (real0, real1, imag0, imag1, realr, imagr, submode,
 static int
 expand_cmplxdiv_wide (real0, real1, imag0, imag1, realr, imagr, submode,
                      unsignedp, methods, class, binoptab)
-  rtx real0, real1, imag0, imag1, realr, imagr;
-  enum machine_mode submode;
-  int unsignedp;
-  enum optab_methods methods;
-  enum mode_class class;
-  optab binoptab;
+     rtx real0, real1, imag0, imag1, realr, imagr;
+     enum machine_mode submode;
+     int unsignedp;
+     enum optab_methods methods;
+     enum mode_class class;
+     optab binoptab;
 {
   rtx ratio, divisor;
   rtx real_t, imag_t;
@@ -471,7 +463,7 @@ expand_cmplxdiv_wide (real0, real1, imag0, imag1, realr, imagr, submode,
 
   /* Calculate dividend.  */
 
-  if (complex_part_zero_p (imag0, class, submode))
+  if (imag0 == 0)
     {
       real_t = real0;
 
@@ -576,7 +568,7 @@ expand_cmplxdiv_wide (real0, real1, imag0, imag1, realr, imagr, submode,
 
   /* Calculate dividend.  */
 
-  if (complex_part_zero_p (imag0, class, submode))
+  if (imag0 == 0)
     {
       /* Compute a / (c+id) as a(c/d) / (c(c/d)+d) + i (-a) / (c(c/d)+d).  */
 
@@ -659,7 +651,7 @@ expand_simple_binop (mode, code, op0, op1, target, unsignedp, methods)
      int unsignedp;
      enum optab_methods methods;
 {
-  optab binop = code_to_optab [(int) code];
+  optab binop = code_to_optab[(int) code];
   if (binop == 0)
     abort ();
 
@@ -693,7 +685,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
   enum machine_mode wider_mode;
   rtx temp;
   int commutative_op = 0;
-  int shift_op = (binoptab->code ==  ASHIFT
+  int shift_op = (binoptab->code == ASHIFT
                  || binoptab->code == ASHIFTRT
                  || binoptab->code == LSHIFTRT
                  || binoptab->code == ROTATE
@@ -792,34 +784,23 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
        }
 
       /* In case the insn wants input operands in modes different from
-        the result, convert the operands.  It would seem that we
-        don't need to convert CONST_INTs, but we do, so that they're
-        a properly sign-extended for their modes; we choose the
-        widest mode between mode and mode[01], so that, in a widening
-        operation, we call convert_modes with different FROM and TO
-        modes, which ensures the value is sign-extended.  Shift
-        operations are an exception, because the second operand needs
-        not be extended to the mode of the result.  */
-
-      if (GET_MODE (op0) != mode0
-         && mode0 != VOIDmode)
+        those of the actual operands, convert the operands.  It would
+        seem that we don't need to convert CONST_INTs, but we do, so
+        that they're properly zero-extended, sign-extended or truncated
+        for their mode.  */
+
+      if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
        xop0 = convert_modes (mode0,
                              GET_MODE (op0) != VOIDmode
                              ? GET_MODE (op0)
-                             : GET_MODE_SIZE (mode) > GET_MODE_SIZE (mode0)
-                             ? mode
-                             : mode0,
+                             : mode,
                              xop0, unsignedp);
 
-      if (GET_MODE (xop1) != mode1
-         && mode1 != VOIDmode)
+      if (GET_MODE (op1) != mode1 && mode1 != VOIDmode)
        xop1 = convert_modes (mode1,
                              GET_MODE (op1) != VOIDmode
                              ? GET_MODE (op1)
-                             : (GET_MODE_SIZE (mode) > GET_MODE_SIZE (mode1)
-                                && ! shift_op)
-                             ? mode
-                             : mode1,
+                             : mode,
                              xop1, unsignedp);
 
       /* Now, if insn's predicates don't allow our operands, put them into
@@ -1161,7 +1142,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
          if (shift_count > BITS_PER_WORD)
            {
              first_shift_count = GEN_INT (shift_count - BITS_PER_WORD);
-             second_shift_count = GEN_INT (2*BITS_PER_WORD - shift_count);
+             second_shift_count = GEN_INT (2 * BITS_PER_WORD - shift_count);
            }
          else
            {
@@ -1174,7 +1155,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
                                     NULL_RTX, unsignedp, next_methods);
          into_temp2 = expand_binop (word_mode, reverse_unsigned_shift,
                                     into_input, second_shift_count,
-                                    into_target, unsignedp, next_methods);
+                                    NULL_RTX, unsignedp, next_methods);
 
          if (into_temp1 != 0 && into_temp2 != 0)
            inter = expand_binop (word_mode, ior_optab, into_temp1, into_temp2,
@@ -1190,7 +1171,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
                                      NULL_RTX, unsignedp, next_methods);
          outof_temp2 = expand_binop (word_mode, reverse_unsigned_shift,
                                      outof_input, second_shift_count,
-                                     outof_target, unsignedp, next_methods);
+                                     NULL_RTX, unsignedp, next_methods);
 
          if (inter != 0 && outof_temp1 != 0 && outof_temp2 != 0)
            inter = expand_binop (word_mode, ior_optab,
@@ -1232,7 +1213,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
     {
       unsigned int i;
       optab otheroptab = binoptab == add_optab ? sub_optab : add_optab;
-      int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
+      const unsigned int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
       rtx carry_in = NULL_RTX, carry_out = NULL_RTX;
       rtx xop0, xop1, xtarget;
 
@@ -1330,6 +1311,8 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
                                                   copy_rtx (xop0),
                                                   copy_rtx (xop1)));
            }
+         else
+           target = xtarget;
 
          return target;
        }
@@ -1531,6 +1514,12 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
       delete_insns_since (last);
     }
 
+  /* Open-code the vector operations if we have no hardware support
+     for them.  */
+  if (class == MODE_VECTOR_INT || class == MODE_VECTOR_FLOAT)
+    return expand_vector_binop (mode, binoptab, op0, op1, target,
+                               unsignedp, methods);
+
   /* We need to open-code the complex type operations: '+, -, * and /' */
 
   /* At this point we allow operations between two similar complex
@@ -1549,10 +1538,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
       int ok = 0;
 
       /* Find the correct mode for the real and imaginary parts */
-      enum machine_mode submode
-       = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT,
-                        class == MODE_COMPLEX_INT ? MODE_INT : MODE_FLOAT,
-                        0);
+      enum machine_mode submode = GET_MODE_INNER(mode);
 
       if (submode == BLKmode)
        abort ();
@@ -1598,11 +1584,10 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
          else if (res != realr)
            emit_move_insn (realr, res);
 
-         if (!complex_part_zero_p (imag0, class, submode)
-             && !complex_part_zero_p (imag1, class, submode))
+         if (imag0 != 0 && imag1 != 0)
            res = expand_binop (submode, binoptab, imag0, imag1,
                                imagr, unsignedp, methods);
-         else if (!complex_part_zero_p (imag0, class, submode))
+         else if (imag0 != 0)
            res = imag0;
          else if (binoptab->code == MINUS)
             res = expand_unop (submode,
@@ -1622,8 +1607,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
        case MULT:
          /* (a+ib) * (c+id) = (ac-bd) + i(ad+cb) */
 
-         if (!complex_part_zero_p (imag0, class, submode)
-              && !complex_part_zero_p (imag1, class, submode))
+         if (imag0 != 0 && imag1 != 0)
            {
              rtx temp1, temp2;
 
@@ -1659,7 +1643,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
                                    NULL_RTX, unsignedp, methods);
 
              if (temp1 == 0 || temp2 == 0)
-                 break;
+               break;
 
              res = (expand_binop
                      (submode,
@@ -1686,7 +1670,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
              else if (res != realr)
                emit_move_insn (realr, res);
 
-             if (!complex_part_zero_p (imag0, class, submode))
+             if (imag0 != 0)
                res = expand_binop (submode, binoptab,
                                    real1, imag0, imagr, unsignedp, methods);
              else
@@ -1705,7 +1689,7 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
        case DIV:
          /* (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd)) */
          
-         if (complex_part_zero_p (imag1, class, submode))
+         if (imag1 == 0)
            {
              /* (a+ib) / (c+i0) = (a/c) + i(b/c) */
 
@@ -1900,6 +1884,232 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
   delete_insns_since (entry_last);
   return 0;
 }
+
+/* Like expand_binop, but for open-coding vectors binops.  */
+
+static rtx
+expand_vector_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 machine_mode submode, tmode;
+  int size, elts, subsize, subbitsize, i;
+  rtx t, a, b, res, seq;
+  enum mode_class class;
+
+  class = GET_MODE_CLASS (mode);
+
+  size = GET_MODE_SIZE (mode);
+  submode = GET_MODE_INNER (mode);
+
+  /* Search for the widest vector mode with the same inner mode that is
+     still narrower than MODE and that allows to open-code this operator.
+     Note, if we find such a mode and the handler later decides it can't
+     do the expansion, we'll be called recursively with the narrower mode.  */
+  for (tmode = GET_CLASS_NARROWEST_MODE (class);
+       GET_MODE_SIZE (tmode) < GET_MODE_SIZE (mode);
+       tmode = GET_MODE_WIDER_MODE (tmode))
+    {
+      if (GET_MODE_INNER (tmode) == GET_MODE_INNER (mode)
+         && binoptab->handlers[(int) tmode].insn_code != CODE_FOR_nothing)
+       submode = tmode;
+    }
+
+  switch (binoptab->code)
+    {
+    case AND:
+    case IOR:
+    case XOR:
+      tmode = int_mode_for_mode (mode);
+      if (tmode != BLKmode)
+       submode = tmode;
+    case PLUS:
+    case MINUS:
+    case MULT:
+    case DIV:
+      subsize = GET_MODE_SIZE (submode);
+      subbitsize = GET_MODE_BITSIZE (submode);
+      elts = size / subsize;
+
+      /* If METHODS is OPTAB_DIRECT, we don't insist on the exact mode,
+        but that we operate on more than one element at a time.  */
+      if (subsize == GET_MODE_UNIT_SIZE (mode) && methods == OPTAB_DIRECT)
+       return 0;
+
+      start_sequence ();
+
+      /* Errors can leave us with a const0_rtx as operand.  */
+      if (GET_MODE (op0) != mode)
+       op0 = copy_to_mode_reg (mode, op0);
+      if (GET_MODE (op1) != mode)
+       op1 = copy_to_mode_reg (mode, op1);
+
+      if (!target)
+       target = gen_reg_rtx (mode);
+
+      for (i = 0; i < elts; ++i)
+       {
+         /* If this is part of a register, and not the first item in the
+            word, we can't store using a SUBREG - that would clobber
+            previous results.
+            And storing with a SUBREG is only possible for the least
+            significant part, hence we can't do it for big endian
+            (unless we want to permute the evaluation order.  */
+         if (GET_CODE (target) == REG
+             && (BYTES_BIG_ENDIAN
+                 ? subsize < UNITS_PER_WORD
+                 : ((i * subsize) % UNITS_PER_WORD) != 0))
+           t = NULL_RTX;
+         else
+           t = simplify_gen_subreg (submode, target, mode, i * subsize);
+         if (CONSTANT_P (op0))
+           a = simplify_gen_subreg (submode, op0, mode, i * subsize);
+         else
+           a = extract_bit_field (op0, subbitsize, i * subbitsize, unsignedp,
+                                  NULL_RTX, submode, submode, size);
+         if (CONSTANT_P (op1))
+           b = simplify_gen_subreg (submode, op1, mode, i * subsize);
+         else
+           b = extract_bit_field (op1, subbitsize, i * subbitsize, unsignedp,
+                                  NULL_RTX, submode, submode, size);
+
+         if (binoptab->code == DIV)
+           {
+             if (class == MODE_VECTOR_FLOAT)
+               res = expand_binop (submode, binoptab, a, b, t,
+                                   unsignedp, methods);
+             else
+               res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
+                                    a, b, t, unsignedp);
+           }
+         else
+           res = expand_binop (submode, binoptab, a, b, t,
+                               unsignedp, methods);
+
+         if (res == 0)
+           break;
+
+         if (t)
+           emit_move_insn (t, res);
+         else
+           store_bit_field (target, subbitsize, i * subbitsize, submode, res,
+                            size);
+       }
+      break;
+
+    default:
+      abort ();
+    }
+
+  seq = get_insns ();
+  end_sequence ();
+  emit_insn (seq);
+
+  return target;
+}
+
+/* Like expand_unop but for open-coding vector unops.  */
+
+static rtx
+expand_vector_unop (mode, unoptab, op0, target, unsignedp)
+     enum machine_mode mode;
+     optab unoptab;
+     rtx op0;
+     rtx target;
+     int unsignedp;
+{
+  enum machine_mode submode, tmode;
+  int size, elts, subsize, subbitsize, i;
+  rtx t, a, res, seq;
+
+  size = GET_MODE_SIZE (mode);
+  submode = GET_MODE_INNER (mode);
+
+  /* Search for the widest vector mode with the same inner mode that is
+     still narrower than MODE and that allows to open-code this operator.
+     Note, if we find such a mode and the handler later decides it can't
+     do the expansion, we'll be called recursively with the narrower mode.  */
+  for (tmode = GET_CLASS_NARROWEST_MODE (GET_MODE_CLASS (mode));
+       GET_MODE_SIZE (tmode) < GET_MODE_SIZE (mode);
+       tmode = GET_MODE_WIDER_MODE (tmode))
+    {
+      if (GET_MODE_INNER (tmode) == GET_MODE_INNER (mode)
+         && unoptab->handlers[(int) tmode].insn_code != CODE_FOR_nothing)
+       submode = tmode;
+    }
+  /* If there is no negate operation, try doing a subtract from zero.  */
+  if (unoptab == neg_optab && GET_MODE_CLASS (submode) == MODE_INT
+      /* Avoid infinite recursion when an
+        error has left us with the wrong mode.  */
+      && GET_MODE (op0) == mode)
+    {    
+      rtx temp;
+      temp = expand_binop (mode, sub_optab, CONST0_RTX (mode), op0,
+                           target, unsignedp, OPTAB_DIRECT);
+      if (temp)
+       return temp;
+    }
+
+  if (unoptab == one_cmpl_optab)
+    {
+      tmode = int_mode_for_mode (mode);
+      if (tmode != BLKmode)
+       submode = tmode;
+    }
+
+  subsize = GET_MODE_SIZE (submode);
+  subbitsize = GET_MODE_BITSIZE (submode);
+  elts = size / subsize;
+
+  /* Errors can leave us with a const0_rtx as operand.  */
+  if (GET_MODE (op0) != mode)
+    op0 = copy_to_mode_reg (mode, op0);
+
+  if (!target)
+    target = gen_reg_rtx (mode);
+
+  start_sequence ();
+
+  for (i = 0; i < elts; ++i)
+    {
+      /* If this is part of a register, and not the first item in the
+        word, we can't store using a SUBREG - that would clobber
+        previous results.
+        And storing with a SUBREG is only possible for the least
+        significant part, hence we can't do it for big endian
+        (unless we want to permute the evaluation order.  */
+      if (GET_CODE (target) == REG
+         && (BYTES_BIG_ENDIAN
+             ?  subsize < UNITS_PER_WORD
+             : ((i * subsize) % UNITS_PER_WORD) != 0))
+       t = NULL_RTX;
+      else
+       t = simplify_gen_subreg (submode, target, mode, i * subsize);
+      if (CONSTANT_P (op0))
+       a = simplify_gen_subreg (submode, op0, mode, i * subsize);
+      else
+       a = extract_bit_field (op0, subbitsize, i * subbitsize, unsignedp,
+                              t, submode, submode, size);
+
+      res = expand_unop (submode, unoptab, a, t, unsignedp);
+
+      if (t)
+       emit_move_insn (t, res);
+      else
+       store_bit_field (target, subbitsize, i * subbitsize, submode, res,
+                        size);
+    }
+
+  seq = get_insns ();
+  end_sequence ();
+  emit_insn (seq);
+
+  return target;
+}
 \f
 /* Expand a binary operator which has both signed and unsigned forms.
    UOPTAB is the optab for unsigned operations, and SOPTAB is for
@@ -1910,11 +2120,11 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
 
 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;
+     enum machine_mode mode;
+     optab uoptab, soptab;
+     rtx op0, op1, target;
+     int unsignedp;
+     enum optab_methods methods;
 {
   rtx temp;
   optab direct_optab = unsignedp ? uoptab : soptab;
@@ -2024,13 +2234,25 @@ expand_twoval_binop (binoptab, op0, op1, targ0, targ1, unsignedp)
       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);
+      /* In case the insn wants input operands in modes different from
+        those of the actual operands, convert the operands.  It would
+        seem that we don't need to convert CONST_INTs, but we do, so
+        that they're properly zero-extended, sign-extended or truncated
+        for their mode.  */
 
-      if (GET_MODE (op1) != VOIDmode && GET_MODE (op1) != mode1)
-       xop1 = convert_to_mode (mode1, xop1, unsignedp);
+      if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
+       xop0 = convert_modes (mode0,
+                             GET_MODE (op0) != VOIDmode
+                             ? GET_MODE (op0)
+                             : mode,
+                             xop0, unsignedp);
+
+      if (GET_MODE (op1) != mode1 && mode1 != VOIDmode)
+       xop1 = convert_modes (mode1,
+                             GET_MODE (op1) != VOIDmode
+                             ? GET_MODE (op1)
+                             : mode,
+                             xop1, unsignedp);
 
       /* Now, if insn doesn't accept these operands, put them into pseudos.  */
       if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0))
@@ -2098,13 +2320,96 @@ expand_simple_unop (mode, code, op0, target, unsignedp)
      rtx target;
      int unsignedp;
 {
-  optab unop = code_to_optab [(int) code];
+  optab unop = code_to_optab[(int) code];
   if (unop == 0)
     abort ();
 
   return expand_unop (mode, unop, op0, target, unsignedp);
 }
 
+/* Try calculating
+       (clz:narrow x)
+   as
+       (clz:wide (zero_extend:wide x)) - ((width wide) - (width narrow)).  */
+static rtx
+widen_clz (mode, op0, target)
+     enum machine_mode mode;
+     rtx op0;
+     rtx target;
+{
+  enum mode_class class = GET_MODE_CLASS (mode);
+  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+    {
+      enum machine_mode wider_mode;
+      for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+          wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+       {
+         if (clz_optab->handlers[(int) wider_mode].insn_code
+             != CODE_FOR_nothing)
+           {
+             rtx xop0, temp, last;
+
+             last = get_last_insn ();
+
+             if (target == 0)
+               target = gen_reg_rtx (mode);
+             xop0 = widen_operand (op0, wider_mode, mode, true, false);
+             temp = expand_unop (wider_mode, clz_optab, xop0, NULL_RTX, true);
+             if (temp != 0)
+               temp = expand_binop (wider_mode, sub_optab, temp,
+                                    GEN_INT (GET_MODE_BITSIZE (wider_mode)
+                                             - GET_MODE_BITSIZE (mode)),
+                                    target, true, OPTAB_DIRECT);
+             if (temp == 0)
+               delete_insns_since (last);
+
+             return temp;
+           }
+       }
+    }
+  return 0;
+}
+
+/* Try calculating (parity x) as (and (popcount x) 1), where
+   popcount can also be done in a wider mode.  */
+static rtx
+expand_parity (mode, op0, target)
+     enum machine_mode mode;
+     rtx op0;
+     rtx target;
+{
+  enum mode_class class = GET_MODE_CLASS (mode);
+  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+    {
+      enum machine_mode wider_mode;
+      for (wider_mode = mode; wider_mode != VOIDmode;
+          wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+       {
+         if (popcount_optab->handlers[(int) wider_mode].insn_code
+             != CODE_FOR_nothing)
+           {
+             rtx xop0, temp, last;
+
+             last = get_last_insn ();
+
+             if (target == 0)
+               target = gen_reg_rtx (mode);
+             xop0 = widen_operand (op0, wider_mode, mode, true, false);
+             temp = expand_unop (wider_mode, popcount_optab, xop0, NULL_RTX,
+                                 true);
+             if (temp != 0)
+               temp = expand_binop (wider_mode, and_optab, temp, GEN_INT (1),
+                                    target, true, OPTAB_DIRECT);
+             if (temp == 0)
+               delete_insns_since (last);
+
+             return temp;
+           }
+       }
+    }
+  return 0;
+}
+
 /* Generate code to perform an operation specified by UNOPTAB
    on operand OP0, with result having machine-mode MODE.
 
@@ -2185,6 +2490,16 @@ expand_unop (mode, unoptab, op0, target, unsignedp)
 
   /* It can't be done in this mode.  Can we open-code it in a wider mode?  */
 
+  /* Widening clz needs special treatment.  */
+  if (unoptab == clz_optab)
+    {
+      temp = widen_clz (mode, op0, target);
+      if (temp)
+       return temp;
+      else
+       goto try_libcall;
+    }
+
   if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
     for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
         wider_mode = GET_MODE_WIDER_MODE (wider_mode))
@@ -2266,10 +2581,7 @@ expand_unop (mode, unoptab, op0, target, unsignedp)
       rtx seq;
 
       /* Find the correct mode for the real and imaginary parts */
-      enum machine_mode submode
-       = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT,
-                        class == MODE_COMPLEX_INT ? MODE_INT : MODE_FLOAT,
-                        0);
+      enum machine_mode submode = GET_MODE_INNER (mode);
 
       if (submode == BLKmode)
        abort ();
@@ -2302,28 +2614,89 @@ expand_unop (mode, unoptab, op0, target, unsignedp)
       return target;
     }
 
+  /* Try negating floating point values by flipping the sign bit.  */
+  if (unoptab->code == NEG && class == MODE_FLOAT
+      && GET_MODE_BITSIZE (mode) <= 2 * HOST_BITS_PER_WIDE_INT)
+    {
+      const struct real_format *fmt = real_format_for_mode[mode - QFmode];
+      enum machine_mode imode = int_mode_for_mode (mode);
+      int bitpos = (fmt != 0) ? fmt->signbit : -1;
+
+      if (imode != BLKmode && bitpos >= 0 && fmt->has_signed_zero)
+       {
+         HOST_WIDE_INT hi, lo;
+         rtx last = get_last_insn ();
+
+         /* Handle targets with different FP word orders.  */
+         if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN)
+           {
+             int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
+             int word = nwords - (bitpos / BITS_PER_WORD) - 1;
+             bitpos = word * BITS_PER_WORD + bitpos % BITS_PER_WORD;
+           }
+
+         if (bitpos < HOST_BITS_PER_WIDE_INT)
+           {
+             hi = 0;
+             lo = (HOST_WIDE_INT) 1 << bitpos;
+           }
+         else
+           {
+             hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
+             lo = 0;
+           }
+         temp = expand_binop (imode, xor_optab,
+                              gen_lowpart (imode, op0),
+                              immed_double_const (lo, hi, imode),
+                              NULL_RTX, 1, OPTAB_LIB_WIDEN);
+         if (temp != 0)
+           return gen_lowpart (mode, temp);
+         delete_insns_since (last);
+        }
+    }
+
+  /* Try calculating parity (x) as popcount (x) % 2.  */
+  if (unoptab == parity_optab)
+    {
+      temp = expand_parity (mode, op0, target);
+      if (temp)
+       return temp;
+    }
+
+ try_libcall:
   /* Now try a library call in this mode.  */
   if (unoptab->handlers[(int) mode].libfunc)
     {
       rtx insns;
       rtx value;
+      enum machine_mode outmode = mode;
+
+      /* All of these functions return small values.  Thus we choose to
+        have them return something that isn't a double-word.  */
+      if (unoptab == ffs_optab || unoptab == clz_optab || unoptab == ctz_optab
+         || unoptab == popcount_optab || unoptab == parity_optab)
+       outmode = TYPE_MODE (integer_type_node);
 
       start_sequence ();
 
       /* Pass 1 for NO_QUEUE so we don't lose any increments
         if the libcall is cse'd or moved.  */
       value = emit_library_call_value (unoptab->handlers[(int) mode].libfunc,
-                                      NULL_RTX, LCT_CONST, mode, 1, op0, mode);
+                                      NULL_RTX, LCT_CONST, outmode,
+                                      1, op0, mode);
       insns = get_insns ();
       end_sequence ();
 
-      target = gen_reg_rtx (mode);
+      target = gen_reg_rtx (outmode);
       emit_libcall_block (insns, target, value,
                          gen_rtx_fmt_e (unoptab->code, mode, op0));
 
       return target;
     }
 
+  if (class == MODE_VECTOR_FLOAT || class == MODE_VECTOR_INT)
+    return expand_vector_unop (mode, unoptab, op0, target, unsignedp);
+
   /* It can't be done in this mode.  Can we do it in a wider mode?  */
 
   if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
@@ -2349,6 +2722,14 @@ expand_unop (mode, unoptab, op0, target, unsignedp)
              temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX,
                                  unsignedp);
 
+             /* If we are generating clz using wider mode, adjust the
+                result.  */
+             if (unoptab == clz_optab && temp != 0)
+               temp = expand_binop (wider_mode, sub_optab, temp,
+                                    GEN_INT (GET_MODE_BITSIZE (wider_mode)
+                                             - GET_MODE_BITSIZE (mode)),
+                                    target, true, OPTAB_DIRECT);
+
              if (temp)
                {
                  if (class != MODE_INT)
@@ -2411,6 +2792,47 @@ expand_abs (mode, op0, target, result_unsignedp, safe)
   if (temp != 0)
     return temp;
 
+  /* For floating point modes, try clearing the sign bit.  */
+  if (GET_MODE_CLASS (mode) == MODE_FLOAT
+      && GET_MODE_BITSIZE (mode) <= 2 * HOST_BITS_PER_WIDE_INT)
+    {
+      const struct real_format *fmt = real_format_for_mode[mode - QFmode];
+      enum machine_mode imode = int_mode_for_mode (mode);
+      int bitpos = (fmt != 0) ? fmt->signbit : -1;
+
+      if (imode != BLKmode && bitpos >= 0)
+       {
+         HOST_WIDE_INT hi, lo;
+         rtx last = get_last_insn ();
+
+         /* Handle targets with different FP word orders.  */
+         if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN)
+           {
+             int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
+             int word = nwords - (bitpos / BITS_PER_WORD) - 1;
+             bitpos = word * BITS_PER_WORD + bitpos % BITS_PER_WORD;
+           }
+
+         if (bitpos < HOST_BITS_PER_WIDE_INT)
+           {
+             hi = 0;
+             lo = (HOST_WIDE_INT) 1 << bitpos;
+           }
+         else
+           {
+             hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
+             lo = 0;
+           }
+         temp = expand_binop (imode, and_optab,
+                              gen_lowpart (imode, op0),
+                              immed_double_const (~lo, ~hi, imode),
+                              NULL_RTX, 1, OPTAB_LIB_WIDEN);
+         if (temp != 0)
+           return gen_lowpart (mode, temp);
+         delete_insns_since (last);
+       }
+    }
+
   /* If we have a MAX insn, we can do this as MAX (x, -x).  */
   if (smax_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
     {
@@ -2510,10 +2932,7 @@ expand_complex_abs (mode, op0, target, unsignedp)
   optab this_abs_optab;
 
   /* Find the correct mode for the real and imaginary parts.  */
-  enum machine_mode submode
-    = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT,
-                    class == MODE_COMPLEX_INT ? MODE_INT : MODE_FLOAT,
-                    0);
+  enum machine_mode submode = GET_MODE_INNER (mode);
 
   if (submode == BLKmode)
     abort ();
@@ -2757,7 +3176,7 @@ emit_unop_insn (icode, target, op0, code)
    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
+   EQUIV, if nonzero, 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
@@ -3029,10 +3448,26 @@ emit_libcall_block (insns, target, result, equiv)
   /* Encapsulate the block so it gets manipulated as a unit.  */
   if (!flag_non_call_exceptions || !may_trap_p (equiv))
     {
-      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));
+      /* We can't attach the REG_LIBCALL and REG_RETVAL notes
+        when the encapsulated region would not be in one basic block,
+        i.e. when there is a control_flow_insn_p insn between FIRST and LAST.
+       */
+      bool attach_libcall_retval_notes = true;
+      next = NEXT_INSN (last);
+      for (insn = first; insn != next; insn = NEXT_INSN (insn))
+       if (control_flow_insn_p (insn))
+         {
+           attach_libcall_retval_notes = false;
+           break;
+         }
+
+      if (attach_libcall_retval_notes)
+       {
+         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
@@ -3071,24 +3506,24 @@ can_compare_p (code, mode, purpose)
 {
   do
     {
-      if (cmp_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing)
+      if (cmp_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
        {
          if (purpose == ccp_jump)
-           return bcc_gen_fctn[(int)code] != NULL;
+           return bcc_gen_fctn[(int) code] != NULL;
          else if (purpose == ccp_store_flag)
-           return setcc_gen_code[(int)code] != CODE_FOR_nothing;
+           return setcc_gen_code[(int) code] != CODE_FOR_nothing;
          else
            /* There's only one cmov entry point, and it's allowed to fail.  */
            return 1;
        }
       if (purpose == ccp_jump
-         && cbranch_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing)
+         && cbranch_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
        return 1;
       if (purpose == ccp_cmov
-         && cmov_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing)
+         && cmov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
        return 1;
       if (purpose == ccp_store_flag
-         && cstore_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing)
+         && cstore_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
        return 1;
 
       mode = GET_MODE_WIDER_MODE (mode);
@@ -3156,7 +3591,7 @@ prepare_cmp_insn (px, py, pcomparison, size, pmode, punsignedp, purpose)
      states that canonical comparisons are required only for targets which
      have cc0.  */
   if (CONSTANT_P (x) && ! CONSTANT_P (y))
-    abort();
+    abort ();
 #endif
 
   /* Don't let both operands fail to indicate the mode.  */
@@ -3214,29 +3649,23 @@ prepare_cmp_insn (px, py, pcomparison, size, pmode, punsignedp, purpose)
 #endif
        {
 #ifdef TARGET_MEM_FUNCTIONS
-         emit_library_call (memcmp_libfunc, LCT_PURE_MAKE_BLOCK,
-                            TYPE_MODE (integer_type_node), 3,
-                            XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
-                            convert_to_mode (TYPE_MODE (sizetype), size,
-                                             TREE_UNSIGNED (sizetype)),
-                            TYPE_MODE (sizetype));
+         result = emit_library_call_value (memcmp_libfunc, NULL_RTX, LCT_PURE_MAKE_BLOCK,
+                                           TYPE_MODE (integer_type_node), 3,
+                                           XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
+                                           convert_to_mode (TYPE_MODE (sizetype), size,
+                                                            TREE_UNSIGNED (sizetype)),
+                                           TYPE_MODE (sizetype));
 #else
-         emit_library_call (bcmp_libfunc, LCT_PURE_MAKE_BLOCK,
-                            TYPE_MODE (integer_type_node), 3,
-                            XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
-                            convert_to_mode (TYPE_MODE (integer_type_node),
-                                             size,
-                                             TREE_UNSIGNED (integer_type_node)),
-                            TYPE_MODE (integer_type_node));
+         result = emit_library_call_value (bcmp_libfunc, NULL_RTX, LCT_PURE_MAKE_BLOCK,
+                                           TYPE_MODE (integer_type_node), 3,
+                                           XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
+                                           convert_to_mode (TYPE_MODE (integer_type_node),
+                                                            size,
+                                                            TREE_UNSIGNED (integer_type_node)),
+                                           TYPE_MODE (integer_type_node));
 #endif
 
-         /* 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));
          result_mode = TYPE_MODE (integer_type_node);
-         emit_move_insn (result,
-                         hard_libcall_value (result_mode));
        }
       *px = result;
       *py = const0_rtx;
@@ -3261,14 +3690,8 @@ prepare_cmp_insn (px, py, pcomparison, size, pmode, punsignedp, purpose)
       if (unsignedp && ucmp_optab->handlers[(int) mode].libfunc)
        libfunc = ucmp_optab->handlers[(int) mode].libfunc;
 
-      emit_library_call (libfunc, LCT_CONST_MAKE_BLOCK, 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));
+      result = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST_MAKE_BLOCK,
+                                       word_mode, 2, x, mode, y, mode);
 
       /* Integer comparison returns a result that must be compared against 1,
         so that even if we do an unsigned compare afterward,
@@ -3335,7 +3758,7 @@ emit_cmp_and_jump_insn_1 (x, y, mode, comparison, unsignedp, label)
 
       if (label)
        {         
-         icode = cbranch_optab->handlers[(int)wider_mode].insn_code;
+         icode = cbranch_optab->handlers[(int) wider_mode].insn_code;
          
          if (icode != CODE_FOR_nothing
              && (*insn_data[icode].operand[0].predicate) (test, wider_mode))
@@ -3376,7 +3799,8 @@ emit_cmp_and_jump_insn_1 (x, y, mode, comparison, unsignedp, label)
        break;
 
       wider_mode = GET_MODE_WIDER_MODE (wider_mode);
-    } while (wider_mode != VOIDmode);
+    }
+  while (wider_mode != VOIDmode);
 
   abort ();
 }
@@ -3784,14 +4208,8 @@ prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp)
   if (libfunc == 0)
     abort ();
 
-  emit_library_call (libfunc, LCT_CONST_MAKE_BLOCK, 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));
+  result = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST_MAKE_BLOCK,
+                                   word_mode, 2, x, mode, y, mode);
   *px = result;
   *py = const0_rtx;
   *pmode = word_mode;
@@ -3810,7 +4228,7 @@ void
 emit_indirect_jump (loc)
      rtx loc;
 {
-  if (! ((*insn_data[(int)CODE_FOR_indirect_jump].operand[0].predicate)
+  if (! ((*insn_data[(int) CODE_FOR_indirect_jump].operand[0].predicate)
         (loc, Pmode)))
     loc = copy_to_mode_reg (Pmode, loc);
 
@@ -3948,7 +4366,7 @@ emit_conditional_move (target, code, op0, op1, cmode, op2, op3, mode,
   return target;
 }
 
-/* Return non-zero if a conditional move of mode MODE is supported.
+/* Return nonzero 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
@@ -3967,6 +4385,134 @@ can_conditionally_move_p (mode)
 }
 
 #endif /* HAVE_conditional_move */
+
+/* Emit a conditional addition 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 OP2+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_add (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;
+  enum rtx_code reversed;
+
+  /* If one operand is constant, make it the second one.  Only do this
+     if the other operand is not constant as well.  */
+
+  if (swap_commutative_operands_p (op0, op1))
+    {
+      tem = op0;
+      op0 = op1;
+      op1 = tem;
+      code = swap_condition (code);
+    }
+
+  /* get_condition will prefer to generate LT and GT even if the old
+     comparison was against zero, so undo that canonicalization here since
+     comparisons against zero are cheaper.  */
+  if (code == LT && GET_CODE (op1) == CONST_INT && INTVAL (op1) == 1)
+    code = LE, op1 = const0_rtx;
+  else if (code == GT && GET_CODE (op1) == CONST_INT && INTVAL (op1) == -1)
+    code = GE, op1 = const0_rtx;
+
+  if (cmode == VOIDmode)
+    cmode = GET_MODE (op0);
+
+  if (swap_commutative_operands_p (op2, op3)
+      && ((reversed = reversed_comparison_code_parts (code, op0, op1, NULL))
+          != UNKNOWN))
+    {
+      tem = op2;
+      op2 = op3;
+      op3 = tem;
+      code = reversed;
+    }
+
+  if (mode == VOIDmode)
+    mode = GET_MODE (op2);
+
+  icode = addcc_optab->handlers[(int) mode].insn_code;
+
+  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_data[icode].operand[0].predicate)
+      (subtarget, insn_data[icode].operand[0].mode))
+    subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode);
+
+  if (! (*insn_data[icode].operand[2].predicate)
+      (op2, insn_data[icode].operand[2].mode))
+    op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2);
+
+  if (! (*insn_data[icode].operand[3].predicate)
+      (op3, insn_data[icode].operand[3].mode))
+    op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3);
+
+  /* Everything should now be in the suitable form, so emit the compare insn
+     and then the conditional move.  */
+
+  comparison 
+    = compare_from_rtx (op0, op1, code, unsignedp, cmode, NULL_RTX);
+
+  /* ??? Watch for const0_rtx (nop) and const_true_rtx (unconditional)?  */
+  /* We can get const0_rtx or const_true_rtx in some circumstances.  Just
+     return NULL and let the caller figure out how best to deal with this
+     situation.  */
+  if (GET_CODE (comparison) != code)
+    return NULL_RTX;
+  
+  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;
+}
 \f
 /* These functions generate an insn body and return it
    rather than emitting the insn.
@@ -4002,7 +4548,7 @@ gen_add3_insn (r0, r1, c)
 {
   int icode = (int) add_optab->handlers[(int) GET_MODE (r0)].insn_code;
 
-    if (icode == CODE_FOR_nothing
+  if (icode == CODE_FOR_nothing
       || ! ((*insn_data[icode].operand[0].predicate)
            (r0, insn_data[icode].operand[0].mode))
       || ! ((*insn_data[icode].operand[1].predicate)
@@ -4066,7 +4612,7 @@ gen_sub3_insn (r0, r1, c)
 {
   int icode = (int) sub_optab->handlers[(int) GET_MODE (r0)].insn_code;
 
-    if (icode == CODE_FOR_nothing
+  if (icode == CODE_FOR_nothing
       || ! ((*insn_data[icode].operand[0].predicate)
            (r0, insn_data[icode].operand[0].mode))
       || ! ((*insn_data[icode].operand[1].predicate)
@@ -4269,10 +4815,10 @@ expand_float (to, from, unsignedp)
      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))
+  for (fmode = GET_MODE (to); fmode != VOIDmode;
+       fmode = GET_MODE_WIDER_MODE (fmode))
+    for (imode = GET_MODE (from); imode != VOIDmode;
+        imode = GET_MODE_WIDER_MODE (imode))
       {
        int doing_unsigned = unsignedp;
 
@@ -4302,7 +4848,7 @@ expand_float (to, from, unsignedp)
              convert_move (to, target, 0);
            return;
          }
-    }
+      }
 
   /* Unsigned integer, and no way to convert directly.
      Convert as signed, then conditionally adjust the result.  */
@@ -4377,7 +4923,7 @@ expand_float (to, from, unsignedp)
 
              /* Multiply by 2 to undo the shift above.  */
              temp = expand_binop (fmode, add_optab, target, target,
-                                    target, 0, OPTAB_LIB_WIDEN);
+                                  target, 0, OPTAB_LIB_WIDEN);
              if (temp != target)
                emit_move_insn (target, temp);
 
@@ -4404,10 +4950,8 @@ expand_float (to, from, unsignedp)
       emit_cmp_and_jump_insns (from, const0_rtx, GE, NULL_RTX, GET_MODE (from),
                               0, 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)));
+      
+      real_2expN (&offset, GET_MODE_BITSIZE (GET_MODE (from)));
       temp = expand_binop (fmode, add_optab, target,
                           CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode),
                           target, 0, OPTAB_LIB_WIDEN);
@@ -4590,7 +5134,7 @@ expand_fix (to, from, unsignedp)
          rtx limit, lab1, lab2, insn;
 
          bitsize = GET_MODE_BITSIZE (GET_MODE (to));
-         offset = REAL_VALUE_LDEXP (dconst1, bitsize - 1);
+         real_2expN (&offset, bitsize - 1);
          limit = CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode);
          lab1 = gen_label_rtx ();
          lab2 = gen_label_rtx ();
@@ -4807,11 +5351,11 @@ init_optabv (code)
 
 static void
 init_libfuncs (optable, first_mode, last_mode, opname, suffix)
-    optab optable;
-    int first_mode;
-    int last_mode;
-    const char *opname;
-    int suffix;
+     optab optable;
+     int first_mode;
+     int last_mode;
+     const char *opname;
+     int suffix;
 {
   int mode;
   unsigned opname_len = strlen (opname);
@@ -4819,7 +5363,7 @@ init_libfuncs (optable, first_mode, last_mode, opname, suffix)
   for (mode = first_mode; (int) mode <= (int) last_mode;
        mode = (enum machine_mode) ((int) mode + 1))
     {
-      const char *mname = GET_MODE_NAME(mode);
+      const char *mname = GET_MODE_NAME (mode);
       unsigned mname_len = strlen (mname);
       char *libfunc_name = alloca (2 + opname_len + mname_len + 1 + 1);
       char *p;
@@ -4848,11 +5392,13 @@ init_libfuncs (optable, first_mode, last_mode, opname, suffix)
 
 static void
 init_integral_libfuncs (optable, opname, suffix)
-    optab optable;
-    const char *opname;
-    int suffix;
+     optab optable;
+     const char *opname;
+     int suffix;
 {
-  init_libfuncs (optable, SImode, TImode, opname, suffix);
+  init_libfuncs (optable, word_mode,
+                mode_for_size (2*BITS_PER_WORD, MODE_INT, 0),
+                opname, suffix);
 }
 
 /* Initialize the libfunc fields of an entire group of entries in some
@@ -4862,11 +5408,22 @@ init_integral_libfuncs (optable, opname, suffix)
 
 static void
 init_floating_libfuncs (optable, opname, suffix)
-    optab optable;
-    const char *opname;
-    int suffix;
+     optab optable;
+     const char *opname;
+     int suffix;
 {
-  init_libfuncs (optable, SFmode, TFmode, opname, suffix);
+  enum machine_mode fmode, dmode, lmode;
+
+  fmode = float_type_node ? TYPE_MODE (float_type_node) : VOIDmode;
+  dmode = double_type_node ? TYPE_MODE (double_type_node) : VOIDmode;
+  lmode = long_double_type_node ? TYPE_MODE (long_double_type_node) : VOIDmode;
+
+  if (fmode != VOIDmode)
+    init_libfuncs (optable, fmode, fmode, opname, suffix);
+  if (dmode != fmode && dmode != VOIDmode)
+    init_libfuncs (optable, dmode, dmode, opname, suffix);
+  if (lmode != dmode && lmode != VOIDmode)
+    init_libfuncs (optable, lmode, lmode, opname, suffix);
 }
 
 rtx
@@ -4955,6 +5512,8 @@ init_optabs ()
   smax_optab = init_optab (SMAX);
   umin_optab = init_optab (UMIN);
   umax_optab = init_optab (UMAX);
+  pow_optab = init_optab (UNKNOWN);
+  atan2_optab = init_optab (UNKNOWN);
 
   /* These three have codes assigned exclusively for the sake of
      have_insn_for.  */
@@ -4968,11 +5527,23 @@ init_optabs ()
   negv_optab = init_optabv (NEG);
   abs_optab = init_optab (ABS);
   absv_optab = init_optabv (ABS);
+  addcc_optab = init_optab (UNKNOWN);
   one_cmpl_optab = init_optab (NOT);
   ffs_optab = init_optab (FFS);
+  clz_optab = init_optab (CLZ);
+  ctz_optab = init_optab (CTZ);
+  popcount_optab = init_optab (POPCOUNT);
+  parity_optab = init_optab (PARITY);
   sqrt_optab = init_optab (SQRT);
+  floor_optab = init_optab (UNKNOWN);
+  ceil_optab = init_optab (UNKNOWN);
+  round_optab = init_optab (UNKNOWN);
+  trunc_optab = init_optab (UNKNOWN);
+  nearbyint_optab = init_optab (UNKNOWN);
   sin_optab = init_optab (UNKNOWN);
   cos_optab = init_optab (UNKNOWN);
+  exp_optab = init_optab (UNKNOWN);
+  log_optab = init_optab (UNKNOWN);
   strlen_optab = init_optab (UNKNOWN);
   cbranch_optab = init_optab (UNKNOWN);
   cmov_optab = init_optab (UNKNOWN);
@@ -5040,6 +5611,10 @@ init_optabs ()
   init_floating_libfuncs (negv_optab, "neg", '2');
   init_integral_libfuncs (one_cmpl_optab, "one_cmpl", '2');
   init_integral_libfuncs (ffs_optab, "ffs", '2');
+  init_integral_libfuncs (clz_optab, "clz", '2');
+  init_integral_libfuncs (ctz_optab, "ctz", '2');
+  init_integral_libfuncs (popcount_optab, "popcount", '2');
+  init_integral_libfuncs (parity_optab, "parity", '2');
 
   /* Comparison libcalls for integers MUST come in pairs, signed/unsigned.  */
   init_integral_libfuncs (cmp_optab, "cmp", '2');
@@ -5262,8 +5837,8 @@ init_traps ()
 
 rtx
 gen_cond_trap (code, op1, op2, tcode)
-  enum rtx_code code ATTRIBUTE_UNUSED;
-  rtx op1, op2 ATTRIBUTE_UNUSED, tcode ATTRIBUTE_UNUSED;
+     enum rtx_code code ATTRIBUTE_UNUSED;
+     rtx op1, op2 ATTRIBUTE_UNUSED, tcode ATTRIBUTE_UNUSED;
 {
   enum machine_mode mode = GET_MODE (op1);
 
@@ -5275,7 +5850,7 @@ gen_cond_trap (code, op1, op2, tcode)
       && cmp_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
     {
       rtx insn;
-      start_sequence();
+      start_sequence ();
       emit_insn (GEN_FCN (cmp_optab->handlers[(int) mode].insn_code) (op1, op2));
       PUT_CODE (trap_rtx, code);
       insn = gen_conditional_trap (trap_rtx, tcode);
@@ -5284,7 +5859,7 @@ gen_cond_trap (code, op1, op2, tcode)
          emit_insn (insn);
          insn = get_insns ();
        }
-      end_sequence();
+      end_sequence ();
       return insn;
     }
 #endif