https://gcc.gnu.org/g:8bce5e413a15abb756a1f6d0bc2f6741b6463b3e
commit r17-4521-g8bce5e413a15abb756a1f6d0bc2f6741b6463b3e Author: Matt Turner <[email protected]> Date: Mon Sep 21 07:26:08 2026 -0600 [PATCH 1/5] alpha: implement the FP classification optabs GCC expands __builtin_isnan, __builtin_isinf, __builtin_isfinite and __builtin_isnormal into FP comparisons on alpha, and by default those comparisons carry no software-completion suffix: isnan_d: cmptun $f16,$f16,$f10 fbne $f10,$L11 A subnormal operand raises a denormal-operand exception there, and the outcome depends on the FPCR denormal-operand-disable bit: the operand is flushed to zero, which makes a subnormal indistinguishable from a zero, or the trap is delivered with no completion information, which is fatal: $ cat cls.c #include <math.h> int main (void) { volatile double sub = 0x1p-1050; return fpclassify (sub); } $ ./cls Floating point exception (core dumped) -mieee is the documented way to get subnormal support, so this is a matter of quality of implementation rather than conformance, but the classification built-ins exist to detect exactly these values and should not kill the process when given one. Classify by inspecting the encoding in an integer register instead, as the AArch64 isinf, isfinite and isnan expanders do for PR66462. Defining these patterns also stops fold_builtin_interclass_mathfn from rewriting the built-ins into comparisons in the first place. With -mieee the comparisons are completed by the kernel and are cheaper, so keep the generic expansions there, except with -fsignaling-nans: a comparison raises an invalid-operation exception for a signaling NaN, which is the PR66462 case that AArch64 and LoongArch handle the same way. gcc/ChangeLog: PR middle-end/66462 * config/alpha/alpha-protos.h (enum alpha_fp_class): New. (alpha_expand_fp_classify): Declare. * config/alpha/alpha.cc (alpha_expand_fp_classify): New function. * config/alpha/alpha.md (isfinite<mode>2, isinf<mode>2) (isnan<mode>2, isnormal<mode>2): New expanders. gcc/testsuite/ChangeLog: PR middle-end/66462 * gcc.target/alpha/fp-classify-1.c: New test. * gcc.target/alpha/fp-classify-2.c: New test. * gcc.target/alpha/fp-classify-3.c: New test. * gcc.target/alpha/fp-classify-4.c: New test. Diff: --- gcc/config/alpha/alpha-protos.h | 11 ++++ gcc/config/alpha/alpha.cc | 71 ++++++++++++++++++++++++++ gcc/config/alpha/alpha.md | 44 ++++++++++++++++ gcc/testsuite/gcc.target/alpha/fp-classify-1.c | 17 ++++++ gcc/testsuite/gcc.target/alpha/fp-classify-2.c | 9 ++++ gcc/testsuite/gcc.target/alpha/fp-classify-3.c | 10 ++++ gcc/testsuite/gcc.target/alpha/fp-classify-4.c | 65 +++++++++++++++++++++++ 7 files changed, 227 insertions(+) diff --git a/gcc/config/alpha/alpha-protos.h b/gcc/config/alpha/alpha-protos.h index c64121d1f3f9..66a1b5a6a6b0 100644 --- a/gcc/config/alpha/alpha-protos.h +++ b/gcc/config/alpha/alpha-protos.h @@ -48,6 +48,17 @@ extern bool alpha_expand_mov_nobwx (machine_mode, rtx *); extern void alpha_expand_movmisalign (machine_mode, rtx *); extern void alpha_emit_floatuns (rtx[]); extern rtx alpha_emit_conditional_move (rtx, machine_mode); + +/* Selects the test performed by alpha_expand_fp_classify. */ +enum alpha_fp_class +{ + ALPHA_FPCLASS_FINITE, + ALPHA_FPCLASS_INF, + ALPHA_FPCLASS_NAN, + ALPHA_FPCLASS_NORMAL +}; + +extern void alpha_expand_fp_classify (rtx, rtx, enum alpha_fp_class); extern void alpha_split_tmode_pair (rtx[], machine_mode, bool); extern void alpha_split_tfmode_frobsign (rtx[], rtx (*)(rtx, rtx, rtx)); extern void alpha_expand_unaligned_load (rtx, rtx, HOST_WIDE_INT, diff --git a/gcc/config/alpha/alpha.cc b/gcc/config/alpha/alpha.cc index 15b4a074f21c..3165a1e8d4a0 100644 --- a/gcc/config/alpha/alpha.cc +++ b/gcc/config/alpha/alpha.cc @@ -2806,6 +2806,77 @@ alpha_emit_setcc (rtx operands[], machine_mode cmp_mode) } +/* Expand a floating-point classification of OP, storing 0 or 1 into + TARGET. KIND selects the test performed. + + The FP comparison instructions cannot be used for this. Without + software completion -- which the compiler only arranges for -mieee, + i.e. alpha_fptm >= ALPHA_FPTM_SU -- a subnormal operand is either + flushed to zero, which makes it indistinguishable from a zero, or + traps with no completion information, which is fatal. They also + raise an invalid-operation exception for a signaling NaN. Examine + the encoding in an integer register instead. */ + +void +alpha_expand_fp_classify (rtx target, rtx op, enum alpha_fp_class kind) +{ + machine_mode mode = GET_MODE (op); + const struct real_format *fmt = REAL_MODE_FORMAT (mode); + int width = GET_MODE_BITSIZE (as_a <scalar_float_mode> (mode)); + int mant_bits = fmt->p - 1; + int exp_bits = width - mant_bits - 1; + HOST_WIDE_INT exp_max = (HOST_WIDE_INT_1 << exp_bits) - 1; + rtx bits, mag, exp, mant, res, tmp; + + /* Move the raw encoding into an integer register, zero extended to + DImode so that the shifts below are independent of MODE. */ + bits = force_lowpart_subreg (int_mode_for_mode (mode).require (), op, mode); + bits = convert_to_mode (DImode, bits, 1); + + /* Shifting out the sign bit leaves the magnitude left justified. */ + mag = expand_shift (LSHIFT_EXPR, DImode, bits, 65 - width, NULL_RTX, 1); + exp = expand_shift (RSHIFT_EXPR, DImode, mag, 64 - exp_bits, NULL_RTX, 1); + + switch (kind) + { + case ALPHA_FPCLASS_FINITE: + /* The exponent field is not all ones. */ + res = emit_store_flag_force (NULL_RTX, NE, exp, GEN_INT (exp_max), + DImode, 0, 1); + break; + + case ALPHA_FPCLASS_NORMAL: + /* The exponent field is neither zero nor all ones, i.e. + (unsigned) (exp - 1) < exp_max - 1. */ + tmp = expand_binop (DImode, sub_optab, exp, const1_rtx, NULL_RTX, + 1, OPTAB_LIB_WIDEN); + res = emit_store_flag_force (NULL_RTX, LTU, tmp, GEN_INT (exp_max - 1), + DImode, 1, 1); + break; + + case ALPHA_FPCLASS_INF: + case ALPHA_FPCLASS_NAN: + /* The exponent field is all ones, and the significand field is zero + for an infinity, nonzero for a NaN. */ + mant = expand_shift (LSHIFT_EXPR, DImode, bits, 64 - mant_bits, + NULL_RTX, 1); + tmp = emit_store_flag_force (NULL_RTX, EQ, exp, GEN_INT (exp_max), + DImode, 0, 1); + res = emit_store_flag_force (NULL_RTX, + kind == ALPHA_FPCLASS_NAN ? NE : EQ, + mant, const0_rtx, DImode, 0, 1); + res = expand_binop (DImode, and_optab, tmp, res, NULL_RTX, + 1, OPTAB_LIB_WIDEN); + break; + + default: + gcc_unreachable (); + } + + emit_move_insn (target, gen_lowpart (GET_MODE (target), res)); +} + + /* Rewrite a comparison against zero CMP of the form (CODE (cc0) (const_int 0)) so it can be written validly in a conditional move (if_then_else CMP ...). diff --git a/gcc/config/alpha/alpha.md b/gcc/config/alpha/alpha.md index 880654b36a28..269e2afe4707 100644 --- a/gcc/config/alpha/alpha.md +++ b/gcc/config/alpha/alpha.md @@ -2855,6 +2855,50 @@ FAIL; }) +;; Floating-point classification. Defining these patterns also stops +;; fold_builtin_interclass_mathfn from rewriting the built-ins into FP +;; comparisons, which are unusable here; see alpha_expand_fp_classify. + +(define_expand "isfinite<mode>2" + [(match_operand:SI 0 "register_operand") + (match_operand:FMODE 1 "register_operand")] + "TARGET_FP && !TARGET_FLOAT_VAX + && (alpha_fptm < ALPHA_FPTM_SU || flag_signaling_nans)" +{ + alpha_expand_fp_classify (operands[0], operands[1], ALPHA_FPCLASS_FINITE); + DONE; +}) + +(define_expand "isinf<mode>2" + [(match_operand:SI 0 "register_operand") + (match_operand:FMODE 1 "register_operand")] + "TARGET_FP && !TARGET_FLOAT_VAX + && (alpha_fptm < ALPHA_FPTM_SU || flag_signaling_nans)" +{ + alpha_expand_fp_classify (operands[0], operands[1], ALPHA_FPCLASS_INF); + DONE; +}) + +(define_expand "isnan<mode>2" + [(match_operand:SI 0 "register_operand") + (match_operand:FMODE 1 "register_operand")] + "TARGET_FP && !TARGET_FLOAT_VAX + && (alpha_fptm < ALPHA_FPTM_SU || flag_signaling_nans)" +{ + alpha_expand_fp_classify (operands[0], operands[1], ALPHA_FPCLASS_NAN); + DONE; +}) + +(define_expand "isnormal<mode>2" + [(match_operand:SI 0 "register_operand") + (match_operand:FMODE 1 "register_operand")] + "TARGET_FP && !TARGET_FLOAT_VAX + && (alpha_fptm < ALPHA_FPTM_SU || flag_signaling_nans)" +{ + alpha_expand_fp_classify (operands[0], operands[1], ALPHA_FPCLASS_NORMAL); + DONE; +}) + (define_expand "cstoretf4" [(use (match_operator:DI 1 "alpha_cbranch_operator" [(match_operand:TF 2 "general_operand") diff --git a/gcc/testsuite/gcc.target/alpha/fp-classify-1.c b/gcc/testsuite/gcc.target/alpha/fp-classify-1.c new file mode 100644 index 000000000000..b1bac48cf763 --- /dev/null +++ b/gcc/testsuite/gcc.target/alpha/fp-classify-1.c @@ -0,0 +1,17 @@ +/* The floating-point classification built-ins must not be expanded into FP + comparisons: without software completion (-mieee) a comparison against a + subnormal operand either traps or has the operand flushed to zero. */ + +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +int isnan_f (float x) { return __builtin_isnan (x); } +int isnan_d (double x) { return __builtin_isnan (x); } +int isinf_f (float x) { return __builtin_isinf (x); } +int isinf_d (double x) { return __builtin_isinf (x); } +int isfinite_f (float x) { return __builtin_isfinite (x); } +int isfinite_d (double x) { return __builtin_isfinite (x); } +int isnormal_f (float x) { return __builtin_isnormal (x); } +int isnormal_d (double x) { return __builtin_isnormal (x); } + +/* { dg-final { scan-assembler-not "\tcmpt" } } */ diff --git a/gcc/testsuite/gcc.target/alpha/fp-classify-2.c b/gcc/testsuite/gcc.target/alpha/fp-classify-2.c new file mode 100644 index 000000000000..9bcf2401234d --- /dev/null +++ b/gcc/testsuite/gcc.target/alpha/fp-classify-2.c @@ -0,0 +1,9 @@ +/* With -mieee the comparisons carry the software-completion suffix and are + usable, so the generic expansions are kept. */ + +/* { dg-do compile } */ +/* { dg-options "-O2 -mieee" } */ + +int isnan_d (double x) { return __builtin_isnan (x); } + +/* { dg-final { scan-assembler "cmptun/su" } } */ diff --git a/gcc/testsuite/gcc.target/alpha/fp-classify-3.c b/gcc/testsuite/gcc.target/alpha/fp-classify-3.c new file mode 100644 index 000000000000..47272887359f --- /dev/null +++ b/gcc/testsuite/gcc.target/alpha/fp-classify-3.c @@ -0,0 +1,10 @@ +/* With -fsignaling-nans the comparisons are unusable even with -mieee, + since cmptun raises an invalid-operation exception for a signaling NaN. */ + +/* { dg-do compile } */ +/* { dg-options "-O2 -mieee -fsignaling-nans" } */ + +int isnan_f (float x) { return __builtin_isnan (x); } +int isnan_d (double x) { return __builtin_isnan (x); } + +/* { dg-final { scan-assembler-not "\tcmpt" } } */ diff --git a/gcc/testsuite/gcc.target/alpha/fp-classify-4.c b/gcc/testsuite/gcc.target/alpha/fp-classify-4.c new file mode 100644 index 000000000000..e32ea1d486d0 --- /dev/null +++ b/gcc/testsuite/gcc.target/alpha/fp-classify-4.c @@ -0,0 +1,65 @@ +/* Without -mieee the isnan, isinf, isfinite and isnormal built-ins must + classify subnormals, infinities and NaNs correctly, without trapping. */ + +/* { dg-do run } */ +/* { dg-options "-O2 -fno-inline" } */ + +enum { NAN_, INF_, NORMAL_, SUBNORMAL_, ZERO_ }; + +static void +t_f (float x, int class) +{ + if (__builtin_isnan (x) != (class == NAN_)) + __builtin_abort (); + if (__builtin_isinf (x) != (class == INF_)) + __builtin_abort (); + if (__builtin_isfinite (x) != (class != NAN_ && class != INF_)) + __builtin_abort (); + if (__builtin_isnormal (x) != (class == NORMAL_)) + __builtin_abort (); +} + +static void +t_d (double x, int class) +{ + if (__builtin_isnan (x) != (class == NAN_)) + __builtin_abort (); + if (__builtin_isinf (x) != (class == INF_)) + __builtin_abort (); + if (__builtin_isfinite (x) != (class != NAN_ && class != INF_)) + __builtin_abort (); + if (__builtin_isnormal (x) != (class == NORMAL_)) + __builtin_abort (); +} + +/* Keep the operands away from the constant folders. */ +volatile float vf[] = { + 0.0f, -0.0f, 1.0f, -1.0f, __FLT_MIN__, __FLT_MAX__, -__FLT_MAX__, + __FLT_DENORM_MIN__, -__FLT_DENORM_MIN__, __FLT_MIN__ / 2, + __builtin_inff (), -__builtin_inff (), __builtin_nanf (""), + -__builtin_nanf ("") +}; + +volatile double vd[] = { + 0.0, -0.0, 1.0, -1.0, __DBL_MIN__, __DBL_MAX__, -__DBL_MAX__, + __DBL_DENORM_MIN__, -__DBL_DENORM_MIN__, __DBL_MIN__ / 2, + __builtin_inf (), -__builtin_inf (), __builtin_nan (""), + -__builtin_nan ("") +}; + +const int classes[] = { + ZERO_, ZERO_, NORMAL_, NORMAL_, NORMAL_, NORMAL_, NORMAL_, + SUBNORMAL_, SUBNORMAL_, SUBNORMAL_, + INF_, INF_, NAN_, NAN_ +}; + +int +main () +{ + for (unsigned i = 0; i < sizeof classes / sizeof classes[0]; i++) + { + t_f (vf[i], classes[i]); + t_d (vd[i], classes[i]); + } + return 0; +}
