This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

implement __builtin_copysign


This implements the C99 copysign function as a builtin.

While I'm on the topic of fixing up our complex arithmetic,
I thought I'd go ahead and implement the C99 requirements for
complex multiplication and division.  Both of which require
new routines in libgcc, and require this function for proper
operation.  It seemed easier, and more useful, to implement
this builtin than to negotiate the system's libm.

Tested on alpha, ia64, x86-64, and i686 linux.  The last of
which excercises the default code in optabs.c.


r~


        * 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.

Index: gcc/builtins.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/builtins.c,v
retrieving revision 1.414
diff -u -p -d -r1.414 builtins.c
--- gcc/builtins.c	24 Jan 2005 08:59:19 -0000	1.414
+++ gcc/builtins.c	28 Jan 2005 00:43:04 -0000
@@ -4436,6 +4436,29 @@ expand_builtin_fabs (tree arglist, rtx t
   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, rt
         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: gcc/genopinit.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/genopinit.c,v
retrieving revision 1.85
diff -u -p -d -r1.85 genopinit.c
--- gcc/genopinit.c	22 Jan 2005 22:48:57 -0000	1.85
+++ gcc/genopinit.c	28 Jan 2005 00:43:04 -0000
@@ -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: gcc/optabs.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/optabs.c,v
retrieving revision 1.253
diff -u -p -d -r1.253 optabs.c
--- gcc/optabs.c	23 Dec 2004 07:58:37 -0000	1.253
+++ gcc/optabs.c	28 Jan 2005 00:43:05 -0000
@@ -2631,6 +2631,90 @@ expand_abs (enum machine_mode mode, rtx 
   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;
+}
 
 /* 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: gcc/optabs.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/optabs.h,v
retrieving revision 1.45
diff -u -p -d -r1.45 optabs.h
--- gcc/optabs.h	18 Jan 2005 11:36:16 -0000	1.45
+++ gcc/optabs.h	28 Jan 2005 00:43:05 -0000
@@ -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_mod
 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);
Index: gcc/config/alpha/alpha.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/alpha/alpha.md,v
retrieving revision 1.234
diff -u -p -d -r1.234 alpha.md
--- gcc/config/alpha/alpha.md	22 Jan 2005 22:49:01 -0000	1.234
+++ gcc/config/alpha/alpha.md	28 Jan 2005 00:43:06 -0000
@@ -58,6 +58,7 @@
    (UNSPEC_PERR		26)
    (UNSPEC_CTLZ		27)
    (UNSPEC_CTPOP	28)
+   (UNSPEC_COPYSIGN     29)
   ])
 
 ;; UNSPEC_VOLATILE:
@@ -2231,6 +2232,42 @@
   [(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: gcc/config/i386/i386-protos.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/i386/i386-protos.h,v
retrieving revision 1.128
diff -u -p -d -r1.128 i386-protos.h
--- gcc/config/i386/i386-protos.h	20 Jan 2005 18:20:42 -0000	1.128
+++ gcc/config/i386/i386-protos.h	28 Jan 2005 00:43:06 -0000
@@ -136,8 +136,10 @@ extern void ix86_expand_binary_operator 
 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: gcc/config/i386/i386.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/i386/i386.c,v
retrieving revision 1.790
diff -u -p -d -r1.790 i386.c
--- gcc/config/i386/i386.c	25 Jan 2005 18:43:55 -0000	1.790
+++ gcc/config/i386/i386.c	28 Jan 2005 00:43:09 -0000
@@ -8000,6 +8000,56 @@ ix86_unary_operator_ok (enum rtx_code co
   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
   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
     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: gcc/config/i386/i386.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/i386/i386.md,v
retrieving revision 1.613
diff -u -p -d -r1.613 i386.md
--- gcc/config/i386/i386.md	25 Jan 2005 18:43:59 -0000	1.613
+++ gcc/config/i386/i386.md	28 Jan 2005 00:43:13 -0000
@@ -145,6 +145,8 @@
    (UNSPEC_REP			75)
 
    (UNSPEC_EH_RETURN		76)
+
+   (UNSPEC_COPYSIGN		100)
   ])
 
 (define_constants
@@ -9449,6 +9451,38 @@
    && 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" "")))]
@@ -9491,6 +9525,38 @@
    && 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: gcc/config/ia64/ia64.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/ia64/ia64.c,v
retrieving revision 1.344
diff -u -p -d -r1.344 ia64.c
--- gcc/config/ia64/ia64.c	23 Jan 2005 15:05:46 -0000	1.344
+++ gcc/config/ia64/ia64.c	28 Jan 2005 00:43:15 -0000
@@ -5350,6 +5350,7 @@ rtx_needs_barrier (rtx x, struct reg_fla
 
 	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: gcc/config/ia64/ia64.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/ia64/ia64.md,v
retrieving revision 1.145
diff -u -p -d -r1.145 ia64.md
--- gcc/config/ia64/ia64.md	22 Jan 2005 22:49:02 -0000	1.145
+++ gcc/config/ia64/ia64.md	28 Jan 2005 00:43:16 -0000
@@ -78,6 +78,7 @@
    (UNSPEC_SETF_EXP             27)
    (UNSPEC_FR_SQRT_RECIP_APPROX 28)
    (UNSPEC_SHRP			29)
+   (UNSPEC_COPYSIGN		30)
   ])
 
 (define_constants
@@ -2586,6 +2587,24 @@
   "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")
@@ -3000,6 +3019,24 @@
   "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")
@@ -3556,6 +3593,24 @@
   "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: gcc/testsuite/gcc.c-torture/execute/ieee/copysign1.c
===================================================================
RCS file: gcc/testsuite/gcc.c-torture/execute/ieee/copysign1.c
diff -N gcc/testsuite/gcc.c-torture/execute/ieee/copysign1.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gcc/testsuite/gcc.c-torture/execute/ieee/copysign1.c	28 Jan 2005 00:43:16 -0000
@@ -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;
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]