https://gcc.gnu.org/g:7f3d7bbf2c6a826222af622e9700193ca9afd86c
commit r17-5414-g7f3d7bbf2c6a826222af622e9700193ca9afd86c Author: Igor Shevlyakov <[email protected]> Date: Thu Oct 15 10:58:16 2026 -0700 match.pd: Recognise the signbit-based copysign idiom The pattern added for PR109843 folds (signbit (x) != 0) == (signbit (y) != 0) ? y : -y and its variants to copysign. C code using the <math.h> signbit macro usually compares the raw values instead, signbit (x) == signbit (y) ? y : -y and that is still emitted as a sign test, a branch and a negate. signbit only promises a nonzero value for a negative argument, so the comparison is unspecified when both arguments are negative, and GCC does produce different values: the constant folder and ranger give 1, while expand_builtin_signbit without a signbit optab gives the masked sign bit. The fold, for == and != alike, takes the two nonzero values to be equal, which is one of the allowed choices. Add the raw comparison next to the PR109843 pattern, with the same guards. As copysign only changes the sign bit, the fold is exact for signed zeros, infinities and NaNs and does not need -ffast-math. Assisted-by: Claude Opus 5.5 gcc/ChangeLog: * match.pd (signbit (x) cmp signbit (y) ? y : -y): New simplification. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/copysign-signbit.c: New test. Signed-off-by: Igor Shevlyakov <[email protected]> Diff: --- gcc/match.pd | 15 +++++++++++ gcc/testsuite/gcc.dg/tree-ssa/copysign-signbit.c | 32 ++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/gcc/match.pd b/gcc/match.pd index f3d9e55f1390..2bb64104aa38 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -10737,6 +10737,21 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (if ((cmp == EQ_EXPR) == (icmp1 == icmp2)) (IFN_COPYSIGN @1 @0) (IFN_COPYSIGN @1 (negate @0))))))))) + +/* signbit (x) cmp signbit (y) ? y : -y -> copysign (y, +-x). The nonzero + value of signbit is unspecified, so the comparison is too when both x + and y are negative; the fold takes the two values to be equal. */ +(for cmp (eq ne) + (for sign (SIGNBIT) + (simplify + (cond (cmp:c (sign @0) (sign @1)) @1 (negate @1)) + (if (SCALAR_FLOAT_TYPE_P (type) + && types_match (type, TREE_TYPE (@0)) + && direct_internal_fn_supported_p (IFN_COPYSIGN, type, + OPTIMIZE_FOR_BOTH)) + (if (cmp == EQ_EXPR) + (IFN_COPYSIGN @1 @0) + (IFN_COPYSIGN @1 (negate @0))))))) #endif (simplify diff --git a/gcc/testsuite/gcc.dg/tree-ssa/copysign-signbit.c b/gcc/testsuite/gcc.dg/tree-ssa/copysign-signbit.c new file mode 100644 index 000000000000..e005d4c9819c --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/copysign-signbit.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +/* { dg-additional-options "-msse2 -mfpmath=sse" { target { { i?86-*-* x86_64-*-* } && ilp32 } } } */ + +/* signbit (x) cmp signbit (y) ? y : -y is copysign (y, +-x). */ + +double d_eq (double x, double y) +{ return __builtin_signbit (x) == __builtin_signbit (y) ? y : -y; } + +double d_eq_swapped (double x, double y) +{ return __builtin_signbit (y) == __builtin_signbit (x) ? y : -y; } + +double d_ne (double x, double y) +{ return __builtin_signbit (x) != __builtin_signbit (y) ? -y : y; } + +double d_if (double x, double y) +{ + if (__builtin_signbit (x) == __builtin_signbit (y)) + return y; + return -y; +} + +float f_eq (float x, float y) +{ return __builtin_signbitf (x) == __builtin_signbitf (y) ? y : -y; } + +/* copysign (y, -x). */ + +double d_neg (double x, double y) +{ return __builtin_signbit (x) != __builtin_signbit (y) ? y : -y; } + +/* { dg-final { scan-tree-dump-times " = \\.COPYSIGN " 6 "optimized" { target ifn_copysign } } } */ +/* { dg-final { scan-tree-dump-not "signbit" "optimized" { target ifn_copysign } } } */
