]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
builtins.c (expand_builtin_copysign): New.
authorRichard Henderson <rth@redhat.com>
Fri, 28 Jan 2005 00:55:07 +0000 (16:55 -0800)
committerRichard Henderson <rth@gcc.gnu.org>
Fri, 28 Jan 2005 00:55:07 +0000 (16:55 -0800)
        * builtins.c (expand_builtin_copysign): New.
        (expand_builtin): Call it.
        * genopinit.c (optabs): Add copysign_optab.
        * optabs.c (init_optabs): Initialize it.
        (expand_copysign): New.
        * optabs.h (OTI_copysign, copysign_optab): New.
        (expand_copysign): Declare.

        * config/alpha/alpha.md (UNSPEC_COPYSIGN): New.
        (copysignsf3, ncopysignsf3, copysigndf3, ncopysigndf3): New.

        * config/i386/i386.c (ix86_build_signbit_mask): Split from ...
        (ix86_expand_fp_absneg_operator): ... here.
        (ix86_split_copysign): New.
        * config/i386/i386-protos.h: Update.
        * config/i386/i386.md (UNSPEC_COPYSIGN): New.
        (copysignsf3, copysigndf3): New.

        * config/ia64/ia64.md (UNSPEC_COPYSIGN): New.
        (copysignsf3, ncopysignsf3): New.
        (copysigndf3, ncopysigndf3): New.
        (copysignxf3, ncopysignxf3): New.
        * config/ia64/ia64.c (rtx_needs_barrier): Handle UNSPEC_COPYSIGN.

From-SVN: r94357

12 files changed:
gcc/ChangeLog
gcc/builtins.c
gcc/config/alpha/alpha.md
gcc/config/i386/i386-protos.h
gcc/config/i386/i386.c
gcc/config/i386/i386.md
gcc/config/ia64/ia64.c
gcc/config/ia64/ia64.md
gcc/genopinit.c
gcc/optabs.c
gcc/optabs.h
gcc/testsuite/gcc.c-torture/execute/ieee/copysign1.c [new file with mode: 0644]

index 0d063b0e3c854dcb763c871a1662726f01520eed..469ebae808ececa8162a0740e800a0ac326fbaf6 100644 (file)
@@ -1,3 +1,29 @@
+2005-01-27  Richard Henderson  <rth@redhat.com>
+
+       * builtins.c (expand_builtin_copysign): New.
+       (expand_builtin): Call it.
+       * genopinit.c (optabs): Add copysign_optab.
+       * optabs.c (init_optabs): Initialize it.
+       (expand_copysign): New.
+       * optabs.h (OTI_copysign, copysign_optab): New.
+       (expand_copysign): Declare.
+
+       * config/alpha/alpha.md (UNSPEC_COPYSIGN): New.
+       (copysignsf3, ncopysignsf3, copysigndf3, ncopysigndf3): New.
+
+       * config/i386/i386.c (ix86_build_signbit_mask): Split from ...
+       (ix86_expand_fp_absneg_operator): ... here.
+       (ix86_split_copysign): New.
+       * config/i386/i386-protos.h: Update.
+       * config/i386/i386.md (UNSPEC_COPYSIGN): New.
+       (copysignsf3, copysigndf3): New.
+
+       * config/ia64/ia64.md (UNSPEC_COPYSIGN): New.
+       (copysignsf3, ncopysignsf3): New.
+       (copysigndf3, ncopysigndf3): New.
+       (copysignxf3, ncopysignxf3): New.
+       * config/ia64/ia64.c (rtx_needs_barrier): Handle UNSPEC_COPYSIGN.
+
 2005-01-27  Arend Bayer  <arend.bayer@web.de>
            Kazu Hirata  <kazu@cs.umass.edu>
 
index 78076db2bf28db6ad34e48c323e86e32fb0223c0..dada41ef84897b2a5e6576841d993ba7db61d8ce 100644 (file)
@@ -4436,6 +4436,29 @@ expand_builtin_fabs (tree arglist, rtx target, rtx subtarget)
   return expand_abs (mode, op0, target, 0, safe_from_p (target, arg, 1));
 }
 
+/* Expand a call to copysign, copysignf, or copysignl with arguments ARGLIST.
+   Return NULL is a normal call should be emitted rather than expanding the
+   function inline.  If convenient, the result should be placed in TARGET.
+   SUBTARGET may be used as the target for computing the operand.  */
+
+static rtx
+expand_builtin_copysign (tree arglist, rtx target, rtx subtarget)
+{
+  rtx op0, op1;
+  tree arg;
+
+  if (!validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE))
+    return 0;
+
+  arg = TREE_VALUE (arglist);
+  op0 = expand_expr (arg, subtarget, VOIDmode, 0);
+
+  arg = TREE_VALUE (TREE_CHAIN (arglist));
+  op1 = expand_expr (arg, NULL, VOIDmode, 0);
+
+  return expand_copysign (op0, op1, target);
+}
+
 /* Create a new constant string literal and return a char* pointer to it.
    The STRING_CST value is the LEN characters at STR.  */
 static tree
@@ -5065,6 +5088,14 @@ expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
         return target;
       break;
 
+    case BUILT_IN_COPYSIGN:
+    case BUILT_IN_COPYSIGNF:
+    case BUILT_IN_COPYSIGNL:
+      target = expand_builtin_copysign (arglist, target, subtarget);
+      if (target)
+       return target;
+      break;
+
       /* Just do a normal library call if we were unable to fold
         the values.  */
     case BUILT_IN_CABS:
index a8f28a2f1acb026d62338373c1a18ce00f4ab309..c643828acad54b0d853b5f3a8fe6b7a71932677f 100644 (file)
@@ -58,6 +58,7 @@
    (UNSPEC_PERR                26)
    (UNSPEC_CTLZ                27)
    (UNSPEC_CTPOP       28)
+   (UNSPEC_COPYSIGN     29)
   ])
 
 ;; UNSPEC_VOLATILE:
   [(const_int 0)]
   "alpha_split_tfmode_frobsign (operands, gen_xordi3); DONE;")
 
+(define_insn "copysignsf3"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (unspec:SF [(match_operand:SF 1 "reg_or_0_operand" "fG")
+                   (match_operand:SF 2 "reg_or_0_operand" "fG")]
+                  UNSPEC_COPYSIGN))]
+  "TARGET_FP"
+  "cpys %R2,%R1,%0"
+  [(set_attr "type" "fadd")])
+
+(define_insn "*ncopysignsf3"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (neg:SF (unspec:SF [(match_operand:SF 1 "reg_or_0_operand" "fG")
+                           (match_operand:SF 2 "reg_or_0_operand" "fG")]
+                          UNSPEC_COPYSIGN)))]
+  "TARGET_FP"
+  "cpysn %R2,%R1,%0"
+  [(set_attr "type" "fadd")])
+
+(define_insn "copysigndf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (unspec:DF [(match_operand:DF 1 "reg_or_0_operand" "fG")
+                   (match_operand:DF 2 "reg_or_0_operand" "fG")]
+                  UNSPEC_COPYSIGN))]
+  "TARGET_FP"
+  "cpys %R2,%R1,%0"
+  [(set_attr "type" "fadd")])
+
+(define_insn "*ncopysigndf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (neg:DF (unspec:DF [(match_operand:DF 1 "reg_or_0_operand" "fG")
+                           (match_operand:DF 2 "reg_or_0_operand" "fG")]
+                          UNSPEC_COPYSIGN)))]
+  "TARGET_FP"
+  "cpysn %R2,%R1,%0"
+  [(set_attr "type" "fadd")])
+
 (define_insn "*addsf_ieee"
   [(set (match_operand:SF 0 "register_operand" "=&f")
        (plus:SF (match_operand:SF 1 "reg_or_0_operand" "%fG")
index 8be5408dd0e1da31e52c0dde08b23efed78affeb..fcbb822dd11cdc0d290f779fc50907c6e3c78b29 100644 (file)
@@ -136,8 +136,10 @@ extern void ix86_expand_binary_operator (enum rtx_code,
 extern int ix86_binary_operator_ok (enum rtx_code, enum machine_mode, rtx[]);
 extern void ix86_expand_unary_operator (enum rtx_code, enum machine_mode,
                                        rtx[]);
+extern rtx ix86_build_signbit_mask (enum machine_mode, bool, bool);
 extern void ix86_expand_fp_absneg_operator (enum rtx_code, enum machine_mode,
                                            rtx[]);
+extern void ix86_split_copysign (rtx []);
 extern int ix86_unary_operator_ok (enum rtx_code, enum machine_mode, rtx[]);
 extern int ix86_match_ccmode (rtx, enum machine_mode);
 extern rtx ix86_expand_compare (enum rtx_code, rtx *, rtx *);
index d9c18d05a307eb4acc879202efc902968567b8d4..2bc334bd388878d54f07c8be2212ad640dc9bea8 100644 (file)
@@ -8000,6 +8000,56 @@ ix86_unary_operator_ok (enum rtx_code code ATTRIBUTE_UNUSED,
   return TRUE;
 }
 
+/* A subroutine of ix86_expand_fp_absneg_operator and copysign expanders.
+   Create a mask for the sign bit in MODE for an SSE register.  If VECT is
+   true, then replicate the mask for all elements of the vector register.
+   If INVERT is true, then create a mask excluding the sign bit.  */
+
+rtx
+ix86_build_signbit_mask (enum machine_mode mode, bool vect, bool invert)
+{
+  enum machine_mode vec_mode;
+  HOST_WIDE_INT hi, lo;
+  int shift = 63;
+  rtvec v;
+  rtx mask;
+
+  /* Find the sign bit, sign extended to 2*HWI.  */
+  if (mode == SFmode)
+    lo = 0x80000000, hi = lo < 0;
+  else if (HOST_BITS_PER_WIDE_INT >= 64)
+    lo = (HOST_WIDE_INT)1 << shift, hi = -1;
+  else
+    lo = 0, hi = (HOST_WIDE_INT)1 << (shift - HOST_BITS_PER_WIDE_INT);
+
+  if (invert)
+    lo = ~lo, hi = ~hi;
+
+  /* Force this value into the low part of a fp vector constant.  */
+  mask = immed_double_const (lo, hi, mode == SFmode ? SImode : DImode);
+  mask = gen_lowpart (mode, mask);
+
+  if (mode == SFmode)
+    {
+      if (vect)
+       v = gen_rtvec (4, mask, mask, mask, mask);
+      else
+       v = gen_rtvec (4, mask, CONST0_RTX (SFmode),
+                      CONST0_RTX (SFmode), CONST0_RTX (SFmode));
+      vec_mode = V4SFmode;
+    }
+  else
+    {
+      if (vect)
+       v = gen_rtvec (2, mask, mask);
+      else
+       v = gen_rtvec (2, mask, CONST0_RTX (DFmode));
+      vec_mode = V2DFmode;
+    }
+
+  return force_reg (vec_mode, gen_rtx_CONST_VECTOR (vec_mode, v));
+}
+
 /* Generate code for floating point ABS or NEG.  */
 
 void
@@ -8011,79 +8061,19 @@ ix86_expand_fp_absneg_operator (enum rtx_code code, enum machine_mode mode,
   bool use_sse = false;
   bool vector_mode = VECTOR_MODE_P (mode);
   enum machine_mode elt_mode = mode;
-  enum machine_mode vec_mode = VOIDmode;
 
   if (vector_mode)
     {
       elt_mode = GET_MODE_INNER (mode);
-      vec_mode = mode;
       use_sse = true;
     }
-  if (TARGET_SSE_MATH)
-    {
-      if (mode == SFmode)
-       {
-         use_sse = true;
-         vec_mode = V4SFmode;
-       }
-      else if (mode == DFmode && TARGET_SSE2)
-       {
-         use_sse = true;
-         vec_mode = V2DFmode;
-       }
-    }
+  else if (TARGET_SSE_MATH)
+    use_sse = SSE_REG_MODE_P (mode);
 
   /* NEG and ABS performed with SSE use bitwise mask operations.
      Create the appropriate mask now.  */
   if (use_sse)
-    {
-      HOST_WIDE_INT hi, lo;
-      int shift = 63;
-      rtvec v;
-
-      /* Find the sign bit, sign extended to 2*HWI.  */
-      if (elt_mode == SFmode)
-        lo = 0x80000000, hi = lo < 0;
-      else if (HOST_BITS_PER_WIDE_INT >= 64)
-        lo = (HOST_WIDE_INT)1 << shift, hi = -1;
-      else
-        lo = 0, hi = (HOST_WIDE_INT)1 << (shift - HOST_BITS_PER_WIDE_INT);
-
-      /* If we're looking for the absolute value, then we want
-        the compliment.  */
-      if (code == ABS)
-        lo = ~lo, hi = ~hi;
-
-      /* Force this value into the low part of a fp vector constant.  */
-      mask = immed_double_const (lo, hi, elt_mode == SFmode ? SImode : DImode);
-      mask = gen_lowpart (elt_mode, mask);
-
-      switch (mode)
-       {
-       case SFmode:
-         v = gen_rtvec (4, mask, CONST0_RTX (SFmode),
-                        CONST0_RTX (SFmode), CONST0_RTX (SFmode));
-         break;
-
-       case DFmode:
-         v = gen_rtvec (2, mask, CONST0_RTX (DFmode));
-         break;
-
-       case V4SFmode:
-         v = gen_rtvec (4, mask, mask, mask, mask);
-         break;
-
-       case V4DFmode:
-         v = gen_rtvec (2, mask, mask);
-         break;
-
-       default:
-         gcc_unreachable ();
-       }
-
-      mask = gen_rtx_CONST_VECTOR (vec_mode, v);
-      mask = force_reg (vec_mode, mask);
-    }
+    mask = ix86_build_signbit_mask (elt_mode, vector_mode, code == ABS);
   else
     {
       /* When not using SSE, we don't use the mask, but prefer to keep the
@@ -8127,6 +8117,78 @@ ix86_expand_fp_absneg_operator (enum rtx_code code, enum machine_mode mode,
     emit_move_insn (operands[0], dst);
 }
 
+/* Deconstruct a copysign operation into bit masks.  */
+
+void
+ix86_split_copysign (rtx operands[])
+{
+  enum machine_mode mode, vmode;
+  rtx dest, scratch, op0, op1, mask, nmask, x;
+
+  dest = operands[0];
+  scratch = operands[1];
+  op0 = operands[2];
+  nmask = operands[3];
+  op1 = operands[4];
+  mask = operands[5];
+
+  mode = GET_MODE (dest);
+  vmode = GET_MODE (mask);
+
+  if (rtx_equal_p (op0, op1))
+    {
+      /* Shouldn't happen often (it's useless, obviously), but when it does
+        we'd generate incorrect code if we continue below.  */
+      emit_move_insn (dest, op0);
+      return;
+    }
+
+  if (REG_P (mask) && REGNO (dest) == REGNO (mask))    /* alternative 0 */
+    {
+      gcc_assert (REGNO (op1) == REGNO (scratch));
+
+      x = gen_rtx_AND (vmode, scratch, mask);
+      emit_insn (gen_rtx_SET (VOIDmode, scratch, x));
+
+      dest = mask;
+      op0 = simplify_gen_subreg (vmode, op0, mode, 0);
+      x = gen_rtx_NOT (vmode, dest);
+      x = gen_rtx_AND (vmode, x, op0);
+      emit_insn (gen_rtx_SET (VOIDmode, dest, x));
+    }
+  else
+    {
+      if (REGNO (op1) == REGNO (scratch))              /* alternative 1,3 */
+       {
+         x = gen_rtx_AND (vmode, scratch, mask);
+       }
+      else                                             /* alternative 2,4 */
+       {
+          gcc_assert (REGNO (mask) == REGNO (scratch));
+          op1 = simplify_gen_subreg (vmode, op1, mode, 0);
+         x = gen_rtx_AND (vmode, scratch, op1);
+       }
+      emit_insn (gen_rtx_SET (VOIDmode, scratch, x));
+
+      if (REGNO (op0) == REGNO (dest))                 /* alternative 1,2 */
+       {
+         dest = simplify_gen_subreg (vmode, op0, mode, 0);
+         x = gen_rtx_AND (vmode, dest, nmask);
+       }
+      else                                             /* alternative 3,4 */
+       {
+          gcc_assert (REGNO (nmask) == REGNO (dest));
+         dest = nmask;
+         op0 = simplify_gen_subreg (vmode, op0, mode, 0);
+         x = gen_rtx_AND (vmode, dest, op0);
+       }
+      emit_insn (gen_rtx_SET (VOIDmode, dest, x));
+    }
+
+  x = gen_rtx_IOR (vmode, dest, scratch);
+  emit_insn (gen_rtx_SET (VOIDmode, dest, x));
+}
+
 /* Return TRUE or FALSE depending on whether the first SET in INSN
    has source and destination with matching CC modes, and that the
    CC mode is at least as constrained as REQ_MODE.  */
index 1a07002c768013f817ef0fdd43d6a77539f3e92b..d09a539ccb0ca6527d9ce8a2fa932f21bd40a778 100644 (file)
    (UNSPEC_REP                 75)
 
    (UNSPEC_EH_RETURN           76)
+
+   (UNSPEC_COPYSIGN            100)
   ])
 
 (define_constants
    && ix86_unary_operator_ok (GET_CODE (operands[3]), SFmode, operands)"
   "#")
 
+(define_expand "copysignsf3"
+  [(parallel [(set (match_operand:SF 0 "register_operand" "")
+                  (unspec:SF [(match_operand:SF 1 "register_operand" "")
+                              (match_dup 4)
+                              (match_operand:SF 2 "register_operand" "")
+                              (match_dup 5)]
+                             UNSPEC_COPYSIGN))
+             (clobber (match_scratch:V4SF 3 ""))])]
+  "TARGET_SSE_MATH"
+{
+  operands[4] = ix86_build_signbit_mask (SFmode, 0, 1);
+  operands[5] = ix86_build_signbit_mask (SFmode, 0, 0);
+})
+
+(define_insn_and_split "*copysignsf3"
+  [(set (match_operand:SF 0 "register_operand"          "=x, x, x, x,x")
+       (unspec:SF
+         [(match_operand:SF 2 "register_operand"       " x, 0, 0, x,x")
+          (match_operand:V4SF 3 "nonimmediate_operand" " X,xm,xm, 0,0")
+          (match_operand:SF 4 "register_operand"       " 1, 1, x, 1,x")
+          (match_operand:V4SF 5 "nonimmediate_operand" " 0,xm, 1,xm,1")]
+         UNSPEC_COPYSIGN))
+   (clobber (match_scratch:V4SF 1                      "=x, x, x, x,x"))]
+  "TARGET_SSE_MATH"
+  "#"
+  "&& reload_completed"
+  [(const_int 0)]
+{
+  ix86_split_copysign (operands);
+  DONE;
+})
+
 (define_expand "negdf2"
   [(set (match_operand:DF 0 "nonimmediate_operand" "")
        (neg:DF (match_operand:DF 1 "nonimmediate_operand" "")))]
    && ix86_unary_operator_ok (GET_CODE (operands[3]), DFmode, operands)"
   "#")
 
+(define_expand "copysigndf3"
+  [(parallel [(set (match_operand:DF 0 "register_operand" "")
+                  (unspec:DF [(match_operand:DF 1 "register_operand" "")
+                              (match_dup 4)
+                              (match_operand:DF 2 "register_operand" "")
+                              (match_dup 5)]
+                             UNSPEC_COPYSIGN))
+             (clobber (match_scratch:V2DF 3 ""))])]
+  "TARGET_SSE2 && TARGET_SSE_MATH"
+{
+  operands[4] = ix86_build_signbit_mask (DFmode, 0, 1);
+  operands[5] = ix86_build_signbit_mask (DFmode, 0, 0);
+})
+
+(define_insn_and_split "*copysigndf3"
+  [(set (match_operand:DF 0 "register_operand"          "=x, x, x, x,x")
+       (unspec:DF
+         [(match_operand:DF 2 "register_operand"       " x, 0, 0, x,x")
+          (match_operand:V2DF 3 "nonimmediate_operand" " X,xm,xm, 0,0")
+          (match_operand:DF 4 "register_operand"       " 1, 1, x, 1,x")
+          (match_operand:V2DF 5 "nonimmediate_operand" " 0,xm, 1,xm,1")]
+         UNSPEC_COPYSIGN))
+   (clobber (match_scratch:V2DF 1                      "=x, x, x, x,x"))]
+  "TARGET_SSE2 && TARGET_SSE_MATH"
+  "#"
+  "&& reload_completed"
+  [(const_int 0)]
+{
+  ix86_split_copysign (operands);
+  DONE;
+})
+
 (define_expand "negxf2"
   [(set (match_operand:XF 0 "nonimmediate_operand" "")
        (neg:XF (match_operand:XF 1 "nonimmediate_operand" "")))]
index 76504c17be786a0158db9bcded23aaf24b9e547e..4502a4600fb5959543f996008f7555630f09d968 100644 (file)
@@ -5350,6 +5350,7 @@ rtx_needs_barrier (rtx x, struct reg_flags flags, int pred)
 
        case UNSPEC_FR_RECIP_APPROX:
        case UNSPEC_SHRP:
+       case UNSPEC_COPYSIGN:
          need_barrier = rtx_needs_barrier (XVECEXP (x, 0, 0), flags, pred);
          need_barrier |= rtx_needs_barrier (XVECEXP (x, 0, 1), flags, pred);
          break;
index 90b1cc59921335b03fdb629f80e21d4e74f98342..cb5dd10abc292a751fb3f93ea31c00834c58aa59 100644 (file)
@@ -78,6 +78,7 @@
    (UNSPEC_SETF_EXP             27)
    (UNSPEC_FR_SQRT_RECIP_APPROX 28)
    (UNSPEC_SHRP                        29)
+   (UNSPEC_COPYSIGN            30)
   ])
 
 (define_constants
   "fnegabs %0 = %1"
   [(set_attr "itanium_class" "fmisc")])
 
+(define_insn "copysignsf3"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (unspec:SF [(match_operand:SF 1 "fr_reg_or_fp01_operand" "fG")
+                   (match_operand:SF 2 "fr_reg_or_fp01_operand" "fG")]
+                  UNSPEC_COPYSIGN))]
+  ""
+  "fmerge.s %0 = %F2, %F1"
+  [(set_attr "itanium_class" "fmisc")])
+
+(define_insn "*ncopysignsf3"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (neg:SF (unspec:SF [(match_operand:SF 1 "fr_reg_or_fp01_operand" "fG")
+                           (match_operand:SF 2 "fr_reg_or_fp01_operand" "fG")]
+                          UNSPEC_COPYSIGN)))]
+  ""
+  "fmerge.ns %0 = %F2, %F1"
+  [(set_attr "itanium_class" "fmisc")])
+
 (define_insn "sminsf3"
   [(set (match_operand:SF 0 "fr_register_operand" "=f")
        (smin:SF (match_operand:SF 1 "fr_register_operand" "f")
   "fnegabs %0 = %1"
   [(set_attr "itanium_class" "fmisc")])
 
+(define_insn "copysigndf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (unspec:DF [(match_operand:DF 1 "fr_reg_or_fp01_operand" "fG")
+                   (match_operand:DF 2 "fr_reg_or_fp01_operand" "fG")]
+                  UNSPEC_COPYSIGN))]
+  ""
+  "fmerge.s %0 = %F2, %F1"
+  [(set_attr "itanium_class" "fmisc")])
+
+(define_insn "*ncopysigndf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (neg:DF (unspec:DF [(match_operand:DF 1 "fr_reg_or_fp01_operand" "fG")
+                           (match_operand:DF 2 "fr_reg_or_fp01_operand" "fG")]
+                          UNSPEC_COPYSIGN)))]
+  ""
+  "fmerge.ns %0 = %F2, %F1"
+  [(set_attr "itanium_class" "fmisc")])
+
 (define_insn "smindf3"
   [(set (match_operand:DF 0 "fr_register_operand" "=f")
        (smin:DF (match_operand:DF 1 "fr_register_operand" "f")
   "fnegabs %0 = %F1"
   [(set_attr "itanium_class" "fmisc")])
 
+(define_insn "copysignxf3"
+  [(set (match_operand:XF 0 "register_operand" "=f")
+       (unspec:XF [(match_operand:XF 1 "fr_reg_or_fp01_operand" "fG")
+                   (match_operand:XF 2 "fr_reg_or_fp01_operand" "fG")]
+                  UNSPEC_COPYSIGN))]
+  ""
+  "fmerge.s %0 = %F2, %F1"
+  [(set_attr "itanium_class" "fmisc")])
+
+(define_insn "*ncopysignxf3"
+  [(set (match_operand:XF 0 "register_operand" "=f")
+       (neg:XF (unspec:XF [(match_operand:XF 1 "fr_reg_or_fp01_operand" "fG")
+                           (match_operand:XF 2 "fr_reg_or_fp01_operand" "fG")]
+                          UNSPEC_COPYSIGN)))]
+  ""
+  "fmerge.ns %0 = %F2, %F1"
+  [(set_attr "itanium_class" "fmisc")])
+
 (define_insn "sminxf3"
   [(set (match_operand:XF 0 "fr_register_operand" "=f")
        (smin:XF (match_operand:XF 1 "xfreg_or_fp01_operand" "fG")
index 61f039328d58e44d2f37debe4da3010ac845c9e1..f56f13c9a01bcc91dbb12e11535d64badaa7d967 100644 (file)
@@ -116,6 +116,7 @@ static const char * const optabs[] =
   "absv_optab->handlers[$A].insn_code =\n\
     abs_optab->handlers[$A].insn_code = CODE_FOR_$(abs$F$a2$)",
   "absv_optab->handlers[$A].insn_code = CODE_FOR_$(absv$I$a2$)",
+  "copysign_optab->handlers[$A].insn_code = CODE_FOR_$(copysign$F$a3$)",
   "sqrt_optab->handlers[$A].insn_code = CODE_FOR_$(sqrt$a2$)",
   "floor_optab->handlers[$A].insn_code = CODE_FOR_$(floor$a2$)",
   "ceil_optab->handlers[$A].insn_code = CODE_FOR_$(ceil$a2$)",
index e18b42bf75fe14c89750ac9ec947b4c533211b15..d92cc907868b3c5c0d7ad209153e74333aeedf44 100644 (file)
@@ -2631,6 +2631,90 @@ expand_abs (enum machine_mode mode, rtx op0, rtx target,
   OK_DEFER_POP;
   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;
+  enum machine_mode imode;
+  int bitpos;
+  HOST_WIDE_INT hi, lo;
+  rtx last, 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;
+
+  /* Otherwise, use bit operations to move the sign from one to the other.  */
+  if (GET_MODE_BITSIZE (mode) > 2 * HOST_BITS_PER_WIDE_INT)
+    return NULL_RTX;
+
+  imode = int_mode_for_mode (mode);
+  if (imode == BLKmode)
+    return NULL_RTX;
+
+  fmt = REAL_MODE_FORMAT (mode);
+  bitpos = (fmt != 0) ? fmt->signbit : -1;
+  if (bitpos < 0)
+    return NULL_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;
+    }
+
+  op0 = expand_binop (imode, and_optab,
+                     gen_lowpart (imode, op0),
+                     immed_double_const (~lo, ~hi, imode),
+                     NULL_RTX, 1, OPTAB_LIB_WIDEN);
+  op1 = expand_binop (imode, and_optab,
+                     gen_lowpart (imode, op1),
+                     immed_double_const (lo, hi, imode),
+                     NULL_RTX, 1, OPTAB_LIB_WIDEN);
+  if (op0 && op1)
+    {
+      target = expand_binop (imode, ior_optab, op0, op1, NULL,
+                            1, OPTAB_LIB_WIDEN);
+      if (target)
+       {
+         target = force_reg (imode, target);
+         target = gen_lowpart (mode, target);
+       }
+    }
+  else
+    target = NULL_RTX;
+
+  if (!target)
+    delete_insns_since (last);
+
+  return target;
+}
 \f
 /* Generate an instruction whose insn-code is INSN_CODE,
    with two operands: an output TARGET and an input OP0.
@@ -4776,6 +4860,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);
index 56f7e4c7b812f0cc859e09d7e84ed2f2542af679..a378a963c729f761cef8b436e136b4e6d5172a80 100644 (file)
@@ -193,6 +193,8 @@ enum optab_index
   OTI_tan,
   /* Inverse tangent */
   OTI_atan,
+  /* Copy sign */
+  OTI_copysign,
 
   /* Compare insn; two operands.  */
   OTI_cmp,
@@ -311,6 +313,7 @@ extern GTY(()) optab optab_table[OTI_MAX];
 #define rint_optab (optab_table[OTI_rint])
 #define tan_optab (optab_table[OTI_tan])
 #define atan_optab (optab_table[OTI_atan])
+#define copysign_optab (optab_table[OTI_copysign])
 
 #define cmp_optab (optab_table[OTI_cmp])
 #define ucmp_optab (optab_table[OTI_ucmp])
@@ -450,6 +453,9 @@ extern rtx expand_unop (enum machine_mode, optab, rtx, rtx, int);
 extern rtx expand_abs_nojump (enum machine_mode, rtx, rtx, int);
 extern rtx expand_abs (enum machine_mode, rtx, rtx, int, int);
 
+/* Expand the copysign operation.  */
+extern rtx expand_copysign (rtx, rtx, rtx);
+
 /* Generate an instruction with a given INSN_CODE with an output and
    an input.  */
 extern void emit_unop_insn (int, rtx, rtx, enum rtx_code);
diff --git a/gcc/testsuite/gcc.c-torture/execute/ieee/copysign1.c b/gcc/testsuite/gcc.c-torture/execute/ieee/copysign1.c
new file mode 100644 (file)
index 0000000..3197d2e
--- /dev/null
@@ -0,0 +1,48 @@
+#include <string.h>
+#include <stdlib.h>
+
+#define TEST(TYPE, EXT)                                                \
+TYPE c##EXT (TYPE x, TYPE y)                                   \
+{                                                              \
+  return __builtin_copysign##EXT (x, y);                       \
+}                                                              \
+                                                               \
+struct D##EXT { TYPE x, y, z; };                               \
+                                                               \
+static const struct D##EXT T##EXT[] = {                                \
+  { 1.0, 2.0, 1.0 },                                           \
+  { 1.0, -2.0, -1.0 },                                         \
+  { -1.0, -2.0, -1.0 },                                                \
+  { 0.0, -2.0, -0.0 },                                         \
+  { -0.0, -2.0, -0.0 },                                                \
+  { -0.0, 2.0, 0.0 },                                          \
+  { __builtin_inf##EXT (), -0.0, -__builtin_inf##EXT () },     \
+  { -__builtin_nan##EXT (""), __builtin_inf##EXT (),           \
+    __builtin_nan##EXT ("") }                                  \
+};                                                             \
+                                                               \
+void test##EXT (void)                                          \
+{                                                              \
+  int i, n = sizeof (T##EXT) / sizeof (T##EXT[0]);             \
+  TYPE r;                                                      \
+  /* Make sure to avoid comparing unused bits in the type.  */ \
+  memset (&r, 0, sizeof r);                                    \
+  for (i = 0; i < n; ++i)                                      \
+    {                                                          \
+      r = c##EXT (T##EXT[i].x, T##EXT[i].y);                   \
+      if (memcmp (&r, &T##EXT[i].z, sizeof r) != 0)            \
+       abort ();                                               \
+    }                                                          \
+}
+
+TEST(float, f)
+TEST(double, )
+TEST(long double, l)
+
+int main()
+{
+  testf();
+  test();
+  testl();
+  return 0;
+}