diff options
-rw-r--r-- | gcc/ChangeLog | 26 | ||||
-rw-r--r-- | gcc/builtins.c | 31 | ||||
-rw-r--r-- | gcc/config/alpha/alpha.md | 37 | ||||
-rw-r--r-- | gcc/config/i386/i386-protos.h | 2 | ||||
-rw-r--r-- | gcc/config/i386/i386.c | 188 | ||||
-rw-r--r-- | gcc/config/i386/i386.md | 66 | ||||
-rw-r--r-- | gcc/config/ia64/ia64.c | 1 | ||||
-rw-r--r-- | gcc/config/ia64/ia64.md | 55 | ||||
-rw-r--r-- | gcc/genopinit.c | 1 | ||||
-rw-r--r-- | gcc/optabs.c | 86 | ||||
-rw-r--r-- | gcc/optabs.h | 6 | ||||
-rw-r--r-- | gcc/testsuite/gcc.c-torture/execute/ieee/copysign1.c | 48 |
12 files changed, 484 insertions, 63 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0d063b0e3c8..469ebae808e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -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> diff --git a/gcc/builtins.c b/gcc/builtins.c index 78076db2bf2..dada41ef848 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -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: diff --git a/gcc/config/alpha/alpha.md b/gcc/config/alpha/alpha.md index a8f28a2f1ac..c643828acad 100644 --- a/gcc/config/alpha/alpha.md +++ b/gcc/config/alpha/alpha.md @@ -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") diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index 8be5408dd0e..fcbb822dd11 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -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 *); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index d9c18d05a30..2bc334bd388 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -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. */ diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 1a07002c768..d09a539ccb0 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -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" "")))] diff --git a/gcc/config/ia64/ia64.c b/gcc/config/ia64/ia64.c index 76504c17be7..4502a4600fb 100644 --- a/gcc/config/ia64/ia64.c +++ b/gcc/config/ia64/ia64.c @@ -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; diff --git a/gcc/config/ia64/ia64.md b/gcc/config/ia64/ia64.md index 90b1cc59921..cb5dd10abc2 100644 --- a/gcc/config/ia64/ia64.md +++ b/gcc/config/ia64/ia64.md @@ -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") diff --git a/gcc/genopinit.c b/gcc/genopinit.c index 61f039328d5..f56f13c9a01 100644 --- a/gcc/genopinit.c +++ b/gcc/genopinit.c @@ -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$)", diff --git a/gcc/optabs.c b/gcc/optabs.c index e18b42bf75f..d92cc907868 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -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); diff --git a/gcc/optabs.h b/gcc/optabs.h index 56f7e4c7b81..a378a963c72 100644 --- a/gcc/optabs.h +++ b/gcc/optabs.h @@ -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 index 00000000000..3197d2e813b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/ieee/copysign1.c @@ -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; +} |