Commit 046625fa by Richard Henderson Committed by Richard Henderson

builtins.c (expand_builtin_copysign): New.

        * 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
parent 17a7d6d7
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>
......
......@@ -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:
......
......@@ -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")
......
......@@ -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 *);
......
......@@ -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. */
......
......@@ -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" "")))]
......
......@@ -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;
......
......@@ -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")
......
......@@ -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$)",
......
......@@ -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;
}
/* 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);
......
......@@ -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);
......
#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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment