OSDN Git Service

* gcc.dg/tls/tls.exp: Remove temporary file.
[pf3gnuchains/gcc-fork.git] / gcc / optabs.c
index 14f2b72..ecaff98 100644 (file)
@@ -1,6 +1,6 @@
 /* Expand the basic unary and binary arithmetic operations, for GNU compiler.
    Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -294,9 +294,6 @@ optab_for_tree_code (enum tree_code code, tree type)
     case MIN_EXPR:
       return TYPE_UNSIGNED (type) ? umin_optab : smin_optab;
 
-    case REALIGN_STORE_EXPR:
-      return vec_realign_store_optab;
-
     case REALIGN_LOAD_EXPR:
       return vec_realign_load_optab;
 
@@ -759,6 +756,168 @@ expand_doubleword_shift (enum machine_mode op1_mode, optab binoptab,
   return true;
 }
 \f
+/* Subroutine of expand_binop.  Perform a double word multiplication of
+   operands OP0 and OP1 both of mode MODE, which is exactly twice as wide
+   as the target's word_mode.  This function return NULL_RTX if anything
+   goes wrong, in which case it may have already emitted instructions
+   which need to be deleted.
+
+   If we want to multiply two two-word values and have normal and widening
+   multiplies of single-word values, we can do this with three smaller
+   multiplications.  Note that we do not make a REG_NO_CONFLICT block here
+   because we are not operating on one word at a time.
+
+   The multiplication proceeds as follows:
+                                _______________________
+                               [__op0_high_|__op0_low__]
+                                _______________________
+        *                      [__op1_high_|__op1_low__]
+        _______________________________________________
+                                _______________________
+    (1)                                [__op0_low__*__op1_low__]
+                    _______________________
+    (2a)           [__op0_low__*__op1_high_]
+                    _______________________
+    (2b)           [__op0_high_*__op1_low__]
+         _______________________
+    (3) [__op0_high_*__op1_high_]
+
+
+  This gives a 4-word result.  Since we are only interested in the
+  lower 2 words, partial result (3) and the upper words of (2a) and
+  (2b) don't need to be calculated.  Hence (2a) and (2b) can be
+  calculated using non-widening multiplication.
+
+  (1), however, needs to be calculated with an unsigned widening
+  multiplication.  If this operation is not directly supported we
+  try using a signed widening multiplication and adjust the result.
+  This adjustment works as follows:
+
+      If both operands are positive then no adjustment is needed.
+
+      If the operands have different signs, for example op0_low < 0 and
+      op1_low >= 0, the instruction treats the most significant bit of
+      op0_low as a sign bit instead of a bit with significance
+      2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low
+      with 2**BITS_PER_WORD - op0_low, and two's complements the
+      result.  Conclusion: We need to add op1_low * 2**BITS_PER_WORD to
+      the result.
+
+      Similarly, if both operands are negative, we need to add
+      (op0_low + op1_low) * 2**BITS_PER_WORD.
+
+      We use a trick to adjust quickly.  We logically shift op0_low right
+      (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to
+      op0_high (op1_high) before it is used to calculate 2b (2a).  If no
+      logical shift exists, we do an arithmetic right shift and subtract
+      the 0 or -1.  */
+
+static rtx
+expand_doubleword_mult (enum machine_mode mode, rtx op0, rtx op1, rtx target,
+                      bool umulp, enum optab_methods methods)
+{
+  int low = (WORDS_BIG_ENDIAN ? 1 : 0);
+  int high = (WORDS_BIG_ENDIAN ? 0 : 1);
+  rtx wordm1 = umulp ? NULL_RTX : GEN_INT (BITS_PER_WORD - 1);
+  rtx product, adjust, product_high, temp;
+
+  rtx op0_high = operand_subword_force (op0, high, mode);
+  rtx op0_low = operand_subword_force (op0, low, mode);
+  rtx op1_high = operand_subword_force (op1, high, mode);
+  rtx op1_low = operand_subword_force (op1, low, mode);
+
+  /* If we're using an unsigned multiply to directly compute the product
+     of the low-order words of the operands and perform any required
+     adjustments of the operands, we begin by trying two more multiplications
+     and then computing the appropriate sum.
+
+     We have checked above that the required addition is provided.
+     Full-word addition will normally always succeed, especially if
+     it is provided at all, so we don't worry about its failure.  The
+     multiplication may well fail, however, so we do handle that.  */
+
+  if (!umulp)
+    {
+      /* ??? This could be done with emit_store_flag where available.  */
+      temp = expand_binop (word_mode, lshr_optab, op0_low, wordm1,
+                          NULL_RTX, 1, methods);
+      if (temp)
+       op0_high = expand_binop (word_mode, add_optab, op0_high, temp,
+                                NULL_RTX, 0, OPTAB_DIRECT);
+      else
+       {
+         temp = expand_binop (word_mode, ashr_optab, op0_low, wordm1,
+                              NULL_RTX, 0, methods);
+         if (!temp)
+           return NULL_RTX;
+         op0_high = expand_binop (word_mode, sub_optab, op0_high, temp,
+                                  NULL_RTX, 0, OPTAB_DIRECT);
+       }
+
+      if (!op0_high)
+       return NULL_RTX;
+    }
+
+  adjust = expand_binop (word_mode, smul_optab, op0_high, op1_low,
+                        NULL_RTX, 0, OPTAB_DIRECT);
+  if (!adjust)
+    return NULL_RTX;
+
+  /* OP0_HIGH should now be dead.  */
+
+  if (!umulp)
+    {
+      /* ??? This could be done with emit_store_flag where available.  */
+      temp = expand_binop (word_mode, lshr_optab, op1_low, wordm1,
+                          NULL_RTX, 1, methods);
+      if (temp)
+       op1_high = expand_binop (word_mode, add_optab, op1_high, temp,
+                                NULL_RTX, 0, OPTAB_DIRECT);
+      else
+       {
+         temp = expand_binop (word_mode, ashr_optab, op1_low, wordm1,
+                              NULL_RTX, 0, methods);
+         if (!temp)
+           return NULL_RTX;
+         op1_high = expand_binop (word_mode, sub_optab, op1_high, temp,
+                                  NULL_RTX, 0, OPTAB_DIRECT);
+       }
+
+      if (!op1_high)
+       return NULL_RTX;
+    }
+
+  temp = expand_binop (word_mode, smul_optab, op1_high, op0_low,
+                      NULL_RTX, 0, OPTAB_DIRECT);
+  if (!temp)
+    return NULL_RTX;
+
+  /* OP1_HIGH should now be dead.  */
+
+  adjust = expand_binop (word_mode, add_optab, adjust, temp,
+                        adjust, 0, OPTAB_DIRECT);
+
+  if (target && !REG_P (target))
+    target = NULL_RTX;
+
+  if (umulp)
+    product = expand_binop (mode, umul_widen_optab, op0_low, op1_low,
+                           target, 1, OPTAB_DIRECT);
+  else
+    product = expand_binop (mode, smul_widen_optab, op0_low, op1_low,
+                           target, 1, OPTAB_DIRECT);
+
+  if (!product)
+    return NULL_RTX;
+
+  product_high = operand_subword (product, high, 1, mode);
+  adjust = expand_binop (word_mode, add_optab, product_high, adjust,
+                        REG_P (product_high) ? product_high : adjust,
+                        0, OPTAB_DIRECT);
+  emit_move_insn (product_high, adjust);
+  return product;
+}
+\f
 /* Wrapper around expand_binop which takes an rtx code to specify
    the operation to perform, not an optab pointer.  All other
    arguments are the same.  */
@@ -1375,6 +1534,11 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
                }
              emit_move_insn (target_piece, newx);
            }
+         else
+           {
+             if (x != target_piece)
+               emit_move_insn (target_piece, x);
+           }
 
          carry_in = carry_out;
        }
@@ -1402,197 +1566,51 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
        delete_insns_since (last);
     }
 
-  /* If we want to multiply two two-word values and have normal and widening
-     multiplies of single-word values, we can do this with three smaller
-     multiplications.  Note that we do not make a REG_NO_CONFLICT block here
-     because we are not operating on one word at a time.
-
-     The multiplication proceeds as follows:
-                                _______________________
-                               [__op0_high_|__op0_low__]
-                                _______________________
-        *                      [__op1_high_|__op1_low__]
-        _______________________________________________
-                                _______________________
-    (1)                                [__op0_low__*__op1_low__]
-                    _______________________
-    (2a)           [__op0_low__*__op1_high_]
-                    _______________________
-    (2b)           [__op0_high_*__op1_low__]
-         _______________________
-    (3) [__op0_high_*__op1_high_]
-
-
-    This gives a 4-word result.  Since we are only interested in the
-    lower 2 words, partial result (3) and the upper words of (2a) and
-    (2b) don't need to be calculated.  Hence (2a) and (2b) can be
-    calculated using non-widening multiplication.
-
-    (1), however, needs to be calculated with an unsigned widening
-    multiplication.  If this operation is not directly supported we
-    try using a signed widening multiplication and adjust the result.
-    This adjustment works as follows:
-
-      If both operands are positive then no adjustment is needed.
-
-      If the operands have different signs, for example op0_low < 0 and
-      op1_low >= 0, the instruction treats the most significant bit of
-      op0_low as a sign bit instead of a bit with significance
-      2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low
-      with 2**BITS_PER_WORD - op0_low, and two's complements the
-      result.  Conclusion: We need to add op1_low * 2**BITS_PER_WORD to
-      the result.
-
-      Similarly, if both operands are negative, we need to add
-      (op0_low + op1_low) * 2**BITS_PER_WORD.
-
-      We use a trick to adjust quickly.  We logically shift op0_low right
-      (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to
-      op0_high (op1_high) before it is used to calculate 2b (2a).  If no
-      logical shift exists, we do an arithmetic right shift and subtract
-      the 0 or -1.  */
+  /* Attempt to synthesize double word multiplies using a sequence of word
+     mode multiplications.  We first attempt to generate a sequence using a
+     more efficient unsigned widening multiply, and if that fails we then
+     try using a signed widening multiply.  */
 
   if (binoptab == smul_optab
       && class == MODE_INT
       && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
       && smul_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
-      && add_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
-      && ((umul_widen_optab->handlers[(int) mode].insn_code
-          != CODE_FOR_nothing)
-         || (smul_widen_optab->handlers[(int) mode].insn_code
-             != CODE_FOR_nothing)))
+      && add_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
     {
-      int low = (WORDS_BIG_ENDIAN ? 1 : 0);
-      int high = (WORDS_BIG_ENDIAN ? 0 : 1);
-      rtx op0_high = operand_subword_force (op0, high, mode);
-      rtx op0_low = operand_subword_force (op0, low, mode);
-      rtx op1_high = operand_subword_force (op1, high, mode);
-      rtx op1_low = operand_subword_force (op1, low, mode);
-      rtx product = 0;
-      rtx op0_xhigh = NULL_RTX;
-      rtx op1_xhigh = NULL_RTX;
-
-      /* If the target is the same as one of the inputs, don't use it.  This
-        prevents problems with the REG_EQUAL note.  */
-      if (target == op0 || target == op1
-         || (target != 0 && !REG_P (target)))
-       target = 0;
-
-      /* Multiply the two lower words to get a double-word product.
-        If unsigned widening multiplication is available, use that;
-        otherwise use the signed form and compensate.  */
-
-      if (umul_widen_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
-       {
-         product = expand_binop (mode, umul_widen_optab, op0_low, op1_low,
-                                 target, 1, OPTAB_DIRECT);
+      rtx product = NULL_RTX;
 
-         /* If we didn't succeed, delete everything we did so far.  */
-         if (product == 0)
+      if (umul_widen_optab->handlers[(int) mode].insn_code
+         != CODE_FOR_nothing)
+       {
+         product = expand_doubleword_mult (mode, op0, op1, target,
+                                           true, methods);
+         if (!product)
            delete_insns_since (last);
-         else
-           op0_xhigh = op0_high, op1_xhigh = op1_high;
        }
 
-      if (product == 0
+      if (product == NULL_RTX
          && smul_widen_optab->handlers[(int) mode].insn_code
-              != CODE_FOR_nothing)
+            != CODE_FOR_nothing)
        {
-         rtx wordm1 = GEN_INT (BITS_PER_WORD - 1);
-         product = expand_binop (mode, smul_widen_optab, op0_low, op1_low,
-                                 target, 1, OPTAB_DIRECT);
-         op0_xhigh = expand_binop (word_mode, lshr_optab, op0_low, wordm1,
-                                   NULL_RTX, 1, next_methods);
-         if (op0_xhigh)
-           op0_xhigh = expand_binop (word_mode, add_optab, op0_high,
-                                     op0_xhigh, op0_xhigh, 0, next_methods);
-         else
-           {
-             op0_xhigh = expand_binop (word_mode, ashr_optab, op0_low, wordm1,
-                                       NULL_RTX, 0, next_methods);
-             if (op0_xhigh)
-               op0_xhigh = expand_binop (word_mode, sub_optab, op0_high,
-                                         op0_xhigh, op0_xhigh, 0,
-                                         next_methods);
-           }
-
-         op1_xhigh = expand_binop (word_mode, lshr_optab, op1_low, wordm1,
-                                   NULL_RTX, 1, next_methods);
-         if (op1_xhigh)
-           op1_xhigh = expand_binop (word_mode, add_optab, op1_high,
-                                     op1_xhigh, op1_xhigh, 0, next_methods);
-         else
-           {
-             op1_xhigh = expand_binop (word_mode, ashr_optab, op1_low, wordm1,
-                                       NULL_RTX, 0, next_methods);
-             if (op1_xhigh)
-               op1_xhigh = expand_binop (word_mode, sub_optab, op1_high,
-                                         op1_xhigh, op1_xhigh, 0,
-                                         next_methods);
-           }
+         product = expand_doubleword_mult (mode, op0, op1, target,
+                                           false, methods);
+         if (!product)
+           delete_insns_since (last);
        }
 
-      /* If we have been able to directly compute the product of the
-        low-order words of the operands and perform any required adjustments
-        of the operands, we proceed by trying two more multiplications
-        and then computing the appropriate sum.
-
-        We have checked above that the required addition is provided.
-        Full-word addition will normally always succeed, especially if
-        it is provided at all, so we don't worry about its failure.  The
-        multiplication may well fail, however, so we do handle that.  */
-
-      if (product && op0_xhigh && op1_xhigh)
+      if (product != NULL_RTX)
        {
-         rtx product_high = operand_subword (product, high, 1, mode);
-         rtx temp = expand_binop (word_mode, binoptab, op0_low, op1_xhigh,
-                                  NULL_RTX, 0, OPTAB_DIRECT);
-
-         if (!REG_P (product_high))
-           product_high = force_reg (word_mode, product_high);
-
-         if (temp != 0)
-           temp = expand_binop (word_mode, add_optab, temp, product_high,
-                                product_high, 0, next_methods);
-
-         if (temp != 0 && temp != product_high)
-           emit_move_insn (product_high, temp);
-
-         if (temp != 0)
-           temp = expand_binop (word_mode, binoptab, op1_low, op0_xhigh,
-                                NULL_RTX, 0, OPTAB_DIRECT);
-
-         if (temp != 0)
-           temp = expand_binop (word_mode, add_optab, temp,
-                                product_high, product_high,
-                                0, next_methods);
-
-         if (temp != 0 && temp != product_high)
-           emit_move_insn (product_high, temp);
-
-         emit_move_insn (operand_subword (product, high, 1, mode), product_high);
-
-         if (temp != 0)
+         if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
            {
-             if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
-               {
-                 temp = emit_move_insn (product, product);
-                 set_unique_reg_note (temp,
-                                      REG_EQUAL,
-                                      gen_rtx_fmt_ee (MULT, mode,
-                                                      copy_rtx (op0),
-                                                      copy_rtx (op1)));
-               }
-
-             return product;
+             temp = emit_move_insn (target ? target : product, product);
+             set_unique_reg_note (temp,
+                                  REG_EQUAL,
+                                  gen_rtx_fmt_ee (MULT, mode,
+                                                  copy_rtx (op0),
+                                                  copy_rtx (op1)));
            }
+         return product;
        }
-
-      /* If we get here, we couldn't do it for some reason even though we
-        originally thought we could.  Delete anything we've emitted in
-        trying to do it.  */
-
-      delete_insns_since (last);
     }
 
   /* It can't be open-coded in this mode.
@@ -2131,6 +2149,131 @@ expand_parity (enum machine_mode mode, rtx op0, rtx target)
   return 0;
 }
 
+/* Extract the OMODE lowpart from VAL, which has IMODE.  Under certain 
+   conditions, VAL may already be a SUBREG against which we cannot generate
+   a further SUBREG.  In this case, we expect forcing the value into a
+   register will work around the situation.  */
+
+static rtx
+lowpart_subreg_maybe_copy (enum machine_mode omode, rtx val,
+                          enum machine_mode imode)
+{
+  rtx ret;
+  ret = lowpart_subreg (omode, val, imode);
+  if (ret == NULL)
+    {
+      val = force_reg (imode, val);
+      ret = lowpart_subreg (omode, val, imode);
+      gcc_assert (ret != NULL);
+    }
+  return ret;
+}
+
+/* Expand a floating point absolute value or negation operation via a
+   logical operation on the sign bit.  */
+
+static rtx
+expand_absneg_bit (enum rtx_code code, enum machine_mode mode,
+                  rtx op0, rtx target)
+{
+  const struct real_format *fmt;
+  int bitpos, word, nwords, i;
+  enum machine_mode imode;
+  HOST_WIDE_INT hi, lo;
+  rtx temp, insns;
+
+  /* The format has to have a simple sign bit.  */
+  fmt = REAL_MODE_FORMAT (mode);
+  if (fmt == NULL)
+    return NULL_RTX;
+
+  bitpos = fmt->signbit_rw;
+  if (bitpos < 0)
+    return NULL_RTX;
+
+  /* Don't create negative zeros if the format doesn't support them.  */
+  if (code == NEG && !fmt->has_signed_zero)
+    return NULL_RTX;
+
+  if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
+    {
+      imode = int_mode_for_mode (mode);
+      if (imode == BLKmode)
+       return NULL_RTX;
+      word = 0;
+      nwords = 1;
+    }
+  else
+    {
+      imode = word_mode;
+
+      if (FLOAT_WORDS_BIG_ENDIAN)
+       word = (GET_MODE_BITSIZE (mode) - bitpos) / BITS_PER_WORD;
+      else
+       word = bitpos / BITS_PER_WORD;
+      bitpos = bitpos % BITS_PER_WORD;
+      nwords = (GET_MODE_BITSIZE (mode) + BITS_PER_WORD - 1) / BITS_PER_WORD;
+    }
+
+  if (bitpos < HOST_BITS_PER_WIDE_INT)
+    {
+      hi = 0;
+      lo = (HOST_WIDE_INT) 1 << bitpos;
+    }
+  else
+    {
+      hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
+      lo = 0;
+    }
+  if (code == ABS)
+    lo = ~lo, hi = ~hi;
+
+  if (target == 0 || target == op0)
+    target = gen_reg_rtx (mode);
+
+  if (nwords > 1)
+    {
+      start_sequence ();
+
+      for (i = 0; i < nwords; ++i)
+       {
+         rtx targ_piece = operand_subword (target, i, 1, mode);
+         rtx op0_piece = operand_subword_force (op0, i, mode);
+       
+         if (i == word)
+           {
+             temp = expand_binop (imode, code == ABS ? and_optab : xor_optab,
+                                  op0_piece,
+                                  immed_double_const (lo, hi, imode),
+                                  targ_piece, 1, OPTAB_LIB_WIDEN);
+             if (temp != targ_piece)
+               emit_move_insn (targ_piece, temp);
+           }
+         else
+           emit_move_insn (targ_piece, op0_piece);
+       }
+
+      insns = get_insns ();
+      end_sequence ();
+
+      temp = gen_rtx_fmt_e (code, mode, copy_rtx (op0));
+      emit_no_conflict_block (insns, target, op0, NULL_RTX, temp);
+    }
+  else
+    {
+      temp = expand_binop (imode, code == ABS ? and_optab : xor_optab,
+                          gen_lowpart (imode, op0),
+                          immed_double_const (lo, hi, imode),
+                          gen_lowpart (imode, target), 1, OPTAB_LIB_WIDEN);
+      target = lowpart_subreg_maybe_copy (mode, temp, imode);
+
+      set_unique_reg_note (get_last_insn (), REG_EQUAL,
+                          gen_rtx_fmt_e (code, mode, copy_rtx (op0)));
+    }
+
+  return target;
+}
+
 /* Generate code to perform an operation specified by UNOPTAB
    on operand OP0, with result having machine-mode MODE.
 
@@ -2282,54 +2425,27 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
       return target;
     }
 
-  /* Try negating floating point values by flipping the sign bit.  */
-  if (unoptab->code == NEG && class == MODE_FLOAT
-      && GET_MODE_BITSIZE (mode) <= 2 * HOST_BITS_PER_WIDE_INT)
+  if (unoptab->code == NEG)
     {
-      const struct real_format *fmt = REAL_MODE_FORMAT (mode);
-      enum machine_mode imode = int_mode_for_mode (mode);
-      int bitpos = (fmt != 0) ? fmt->signbit : -1;
-
-      if (imode != BLKmode && bitpos >= 0 && fmt->has_signed_zero)
+      /* Try negating floating point values by flipping the sign bit.  */
+      if (class == MODE_FLOAT)
        {
-         HOST_WIDE_INT hi, lo;
-         rtx last = get_last_insn ();
-
-         /* Handle targets with different FP word orders.  */
-         if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN)
-           {
-             int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
-             int word = nwords - (bitpos / BITS_PER_WORD) - 1;
-             bitpos = word * BITS_PER_WORD + bitpos % BITS_PER_WORD;
-           }
+         temp = expand_absneg_bit (NEG, mode, op0, target);
+         if (temp)
+           return temp;
+       }
 
-         if (bitpos < HOST_BITS_PER_WIDE_INT)
-           {
-             hi = 0;
-             lo = (HOST_WIDE_INT) 1 << bitpos;
-           }
-         else
-           {
-             hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
-             lo = 0;
-           }
-         temp = expand_binop (imode, xor_optab,
-                              gen_lowpart (imode, op0),
-                              immed_double_const (lo, hi, imode),
-                              NULL_RTX, 1, OPTAB_LIB_WIDEN);
-         if (temp != 0)
-           {
-             rtx insn;
-             if (target == 0)
-               target = gen_reg_rtx (mode);
-             insn = emit_move_insn (target, gen_lowpart (mode, temp));
-             set_unique_reg_note (insn, REG_EQUAL,
-                                  gen_rtx_fmt_e (NEG, mode,
-                                                 copy_rtx (op0)));
-             return target;
-           }
-         delete_insns_since (last);
-        }
+      /* If there is no negation pattern, and we have no negative zero,
+        try subtracting from zero.  */
+      if (!HONOR_SIGNED_ZEROS (mode))
+       {
+         temp = expand_binop (mode, (unoptab == negv_optab
+                                     ? subv_optab : sub_optab),
+                              CONST0_RTX (mode), op0, target,
+                              unsignedp, OPTAB_DIRECT);
+         if (temp)
+           return temp;
+       }
     }
 
   /* Try calculating parity (x) as popcount (x) % 2.  */
@@ -2340,15 +2456,6 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
        return temp;
     }
 
-  /* If there is no negation pattern, try subtracting from zero.  */
-  if (unoptab == neg_optab && class == MODE_INT)
-    {
-      temp = expand_binop (mode, sub_optab, CONST0_RTX (mode), op0,
-                           target, unsignedp, OPTAB_DIRECT);
-      if (temp)
-       return temp;
-    }
-
  try_libcall:
   /* Now try a library call in this mode.  */
   if (unoptab->handlers[(int) mode].libfunc)
@@ -2432,10 +2539,9 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
        }
     }
 
-  /* If there is no negate operation, try doing a subtract from zero.
-     The US Software GOFAST library needs this.  FIXME: This is *wrong*
-     for floating-point operations due to negative zeros!  */
-  if (unoptab->code == NEG)
+  /* One final attempt at implementing negation via subtraction,
+     this time allowing widening of the operand.  */
+  if (unoptab->code == NEG && !HONOR_SIGNED_ZEROS (mode))
     {
       rtx temp;
       temp = expand_binop (mode,
@@ -2443,7 +2549,7 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
                            CONST0_RTX (mode), op0,
                            target, unsignedp, OPTAB_LIB_WIDEN);
       if (temp)
-       return temp;
+        return temp;
     }
 
   return 0;
@@ -2474,57 +2580,16 @@ expand_abs_nojump (enum machine_mode mode, rtx op0, rtx target,
     return temp;
 
   /* For floating point modes, try clearing the sign bit.  */
-  if (GET_MODE_CLASS (mode) == MODE_FLOAT
-      && GET_MODE_BITSIZE (mode) <= 2 * HOST_BITS_PER_WIDE_INT)
+  if (GET_MODE_CLASS (mode) == MODE_FLOAT)
     {
-      const struct real_format *fmt = REAL_MODE_FORMAT (mode);
-      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)
-           {
-             rtx insn;
-             if (target == 0)
-               target = gen_reg_rtx (mode);
-             insn = emit_move_insn (target, gen_lowpart (mode, temp));
-             set_unique_reg_note (insn, REG_EQUAL,
-                                  gen_rtx_fmt_e (ABS, mode,
-                                                 copy_rtx (op0)));
-             return target;
-           }
-         delete_insns_since (last);
-       }
+      temp = expand_absneg_bit (ABS, mode, op0, target);
+      if (temp)
+       return temp;
     }
 
   /* If we have a MAX insn, we can do this as MAX (x, -x).  */
-  if (smax_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+  if (smax_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing
+      && !HONOR_SIGNED_ZEROS (mode))
     {
       rtx last = get_last_insn ();
 
@@ -2612,6 +2677,239 @@ expand_abs (enum machine_mode mode, rtx op0, rtx target,
   OK_DEFER_POP;
   return target;
 }
+
+/* A subroutine of expand_copysign, perform the copysign operation using the
+   abs and neg primitives advertised to exist on the target.  The assumption
+   is that we have a split register file, and leaving op0 in fp registers,
+   and not playing with subregs so much, will help the register allocator.  */
+
+static rtx
+expand_copysign_absneg (enum machine_mode mode, rtx op0, rtx op1, rtx target,
+                       int bitpos, bool op0_is_abs)
+{
+  enum machine_mode imode;
+  HOST_WIDE_INT hi, lo;
+  int word;
+  rtx label;
+
+  if (target == op1)
+    target = NULL_RTX;
+
+  if (!op0_is_abs)
+    {
+      op0 = expand_unop (mode, abs_optab, op0, target, 0);
+      if (op0 == NULL)
+       return NULL_RTX;
+      target = op0;
+    }
+  else
+    {
+      if (target == NULL_RTX)
+        target = copy_to_reg (op0);
+      else
+       emit_move_insn (target, op0);
+    }
+
+  if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
+    {
+      imode = int_mode_for_mode (mode);
+      if (imode == BLKmode)
+       return NULL_RTX;
+      op1 = gen_lowpart (imode, op1);
+    }
+  else
+    {
+      imode = word_mode;
+      if (FLOAT_WORDS_BIG_ENDIAN)
+       word = (GET_MODE_BITSIZE (mode) - bitpos) / BITS_PER_WORD;
+      else
+       word = bitpos / BITS_PER_WORD;
+      bitpos = bitpos % BITS_PER_WORD;
+      op1 = operand_subword_force (op1, word, mode);
+    }
+
+  if (bitpos < HOST_BITS_PER_WIDE_INT)
+    {
+      hi = 0;
+      lo = (HOST_WIDE_INT) 1 << bitpos;
+    }
+  else
+    {
+      hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
+      lo = 0;
+    }
+
+  op1 = expand_binop (imode, and_optab, op1,
+                     immed_double_const (lo, hi, imode),
+                     NULL_RTX, 1, OPTAB_LIB_WIDEN);
+
+  label = gen_label_rtx ();
+  emit_cmp_and_jump_insns (op1, const0_rtx, EQ, NULL_RTX, imode, 1, label);
+
+  if (GET_CODE (op0) == CONST_DOUBLE)
+    op0 = simplify_unary_operation (NEG, mode, op0, mode);
+  else
+    op0 = expand_unop (mode, neg_optab, op0, target, 0);
+  if (op0 != target)
+    emit_move_insn (target, op0);
+
+  emit_label (label);
+
+  return target;
+}
+
+
+/* A subroutine of expand_copysign, perform the entire copysign operation
+   with integer bitmasks.  BITPOS is the position of the sign bit; OP0_IS_ABS
+   is true if op0 is known to have its sign bit clear.  */
+
+static rtx
+expand_copysign_bit (enum machine_mode mode, rtx op0, rtx op1, rtx target,
+                    int bitpos, bool op0_is_abs)
+{
+  enum machine_mode imode;
+  HOST_WIDE_INT hi, lo;
+  int word, nwords, i;
+  rtx temp, insns;
+
+  if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
+    {
+      imode = int_mode_for_mode (mode);
+      if (imode == BLKmode)
+       return NULL_RTX;
+      word = 0;
+      nwords = 1;
+    }
+  else
+    {
+      imode = word_mode;
+
+      if (FLOAT_WORDS_BIG_ENDIAN)
+       word = (GET_MODE_BITSIZE (mode) - bitpos) / BITS_PER_WORD;
+      else
+       word = bitpos / BITS_PER_WORD;
+      bitpos = bitpos % BITS_PER_WORD;
+      nwords = (GET_MODE_BITSIZE (mode) + BITS_PER_WORD - 1) / BITS_PER_WORD;
+    }
+
+  if (bitpos < HOST_BITS_PER_WIDE_INT)
+    {
+      hi = 0;
+      lo = (HOST_WIDE_INT) 1 << bitpos;
+    }
+  else
+    {
+      hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
+      lo = 0;
+    }
+
+  if (target == 0 || target == op0 || target == op1)
+    target = gen_reg_rtx (mode);
+
+  if (nwords > 1)
+    {
+      start_sequence ();
+
+      for (i = 0; i < nwords; ++i)
+       {
+         rtx targ_piece = operand_subword (target, i, 1, mode);
+         rtx op0_piece = operand_subword_force (op0, i, mode);
+       
+         if (i == word)
+           {
+             if (!op0_is_abs)
+               op0_piece = expand_binop (imode, and_optab, op0_piece,
+                                         immed_double_const (~lo, ~hi, imode),
+                                         NULL_RTX, 1, OPTAB_LIB_WIDEN);
+
+             op1 = expand_binop (imode, and_optab,
+                                 operand_subword_force (op1, i, mode),
+                                 immed_double_const (lo, hi, imode),
+                                 NULL_RTX, 1, OPTAB_LIB_WIDEN);
+
+             temp = expand_binop (imode, ior_optab, op0_piece, op1,
+                                  targ_piece, 1, OPTAB_LIB_WIDEN);
+             if (temp != targ_piece)
+               emit_move_insn (targ_piece, temp);
+           }
+         else
+           emit_move_insn (targ_piece, op0_piece);
+       }
+
+      insns = get_insns ();
+      end_sequence ();
+
+      emit_no_conflict_block (insns, target, op0, op1, NULL_RTX);
+    }
+  else
+    {
+      op1 = expand_binop (imode, and_optab, gen_lowpart (imode, op1),
+                         immed_double_const (lo, hi, imode),
+                         NULL_RTX, 1, OPTAB_LIB_WIDEN);
+
+      op0 = gen_lowpart (imode, op0);
+      if (!op0_is_abs)
+       op0 = expand_binop (imode, and_optab, op0,
+                           immed_double_const (~lo, ~hi, imode),
+                           NULL_RTX, 1, OPTAB_LIB_WIDEN);
+
+      temp = expand_binop (imode, ior_optab, op0, op1,
+                          gen_lowpart (imode, target), 1, OPTAB_LIB_WIDEN);
+      target = lowpart_subreg_maybe_copy (mode, temp, imode);
+    }
+
+  return target;
+}
+
+/* Expand the C99 copysign operation.  OP0 and OP1 must be the same 
+   scalar floating point mode.  Return NULL if we do not know how to
+   expand the operation inline.  */
+
+rtx
+expand_copysign (rtx op0, rtx op1, rtx target)
+{
+  enum machine_mode mode = GET_MODE (op0);
+  const struct real_format *fmt;
+  bool op0_is_abs;
+  rtx temp;
+
+  gcc_assert (SCALAR_FLOAT_MODE_P (mode));
+  gcc_assert (GET_MODE (op1) == mode);
+
+  /* First try to do it with a special instruction.  */
+  temp = expand_binop (mode, copysign_optab, op0, op1,
+                      target, 0, OPTAB_DIRECT);
+  if (temp)
+    return temp;
+
+  fmt = REAL_MODE_FORMAT (mode);
+  if (fmt == NULL || !fmt->has_signed_zero)
+    return NULL_RTX;
+
+  op0_is_abs = false;
+  if (GET_CODE (op0) == CONST_DOUBLE)
+    {
+      if (real_isneg (CONST_DOUBLE_REAL_VALUE (op0)))
+       op0 = simplify_unary_operation (ABS, mode, op0, mode);
+      op0_is_abs = true;
+    }
+
+  if (fmt->signbit_ro >= 0
+      && (GET_CODE (op0) == CONST_DOUBLE
+         || (neg_optab->handlers[mode].insn_code != CODE_FOR_nothing
+             && abs_optab->handlers[mode].insn_code != CODE_FOR_nothing)))
+    {
+      temp = expand_copysign_absneg (mode, op0, op1, target,
+                                    fmt->signbit_ro, op0_is_abs);
+      if (temp)
+       return temp;
+    }
+
+  if (fmt->signbit_rw < 0)
+    return NULL_RTX;
+  return expand_copysign_bit (mode, op0, op1, target,
+                             fmt->signbit_rw, op0_is_abs);
+}
 \f
 /* Generate an instruction whose insn-code is INSN_CODE,
    with two operands: an output TARGET and an input OP0.
@@ -4740,6 +5038,7 @@ init_optabs (void)
   btrunc_optab = init_optab (UNKNOWN);
   nearbyint_optab = init_optab (UNKNOWN);
   rint_optab = init_optab (UNKNOWN);
+  lrint_optab = init_optab (UNKNOWN);
   sincos_optab = init_optab (UNKNOWN);
   sin_optab = init_optab (UNKNOWN);
   asin_optab = init_optab (UNKNOWN);
@@ -4749,6 +5048,7 @@ init_optabs (void)
   exp10_optab = init_optab (UNKNOWN);
   exp2_optab = init_optab (UNKNOWN);
   expm1_optab = init_optab (UNKNOWN);
+  ldexp_optab = init_optab (UNKNOWN);
   logb_optab = init_optab (UNKNOWN);
   ilogb_optab = init_optab (UNKNOWN);
   log_optab = init_optab (UNKNOWN);
@@ -4757,6 +5057,8 @@ init_optabs (void)
   log1p_optab = init_optab (UNKNOWN);
   tan_optab = init_optab (UNKNOWN);
   atan_optab = init_optab (UNKNOWN);
+  copysign_optab = init_optab (UNKNOWN);
+
   strlen_optab = init_optab (UNKNOWN);
   cbranch_optab = init_optab (UNKNOWN);
   cmov_optab = init_optab (UNKNOWN);
@@ -4767,6 +5069,9 @@ init_optabs (void)
   vec_set_optab = init_optab (UNKNOWN);
   vec_init_optab = init_optab (UNKNOWN);
   vec_realign_load_optab = init_optab (UNKNOWN);
+  movmisalign_optab = init_optab (UNKNOWN);
+
+  powi_optab = init_optab (UNKNOWN);
 
   /* Conversions.  */
   sext_optab = init_convert_optab (SIGN_EXTEND);
@@ -4854,6 +5159,8 @@ init_optabs (void)
   init_floating_libfuncs (le_optab, "le", '2');
   init_floating_libfuncs (unord_optab, "unord", '2');
 
+  init_floating_libfuncs (powi_optab, "powi", '2');
+
   /* Conversions.  */
   init_interclass_conv_libfuncs (sfloat_optab, "float",
                                 MODE_INT, MODE_FLOAT);