https://gcc.gnu.org/g:be18f56044c5aff8a3670b7af4003b7045d333c6
commit be18f56044c5aff8a3670b7af4003b7045d333c6 Author: Michael Meissner <meiss...@linux.ibm.com> Date: Thu Jan 16 14:17:49 2025 -0500 Carl Love patches. 2025-01-16 Carl Love <c...@linux.ibm.com> gcc * config/rs6000/rs6000-builtins.def (__builtin_isinf_sp, __builtin_isinf_dp, __builtin_isinf_qp): New built-in definitions. * config/rs6000/rs6000-overloaded.def (__builtin_isinf): New overloaded built-in definition. * doc/extend.texi: Add documentation for scalar_isinf built-in. gcc/testsuite * gcc.target/powerpc/is_type.c: New test file. Diff: --- gcc/config/rs6000/rs6000-builtins.def | 48 ++++- gcc/config/rs6000/rs6000-overload.def | 40 ++++ gcc/config/rs6000/vsx.md | 151 ++++++++++++++- gcc/doc/extend.texi | 19 ++ gcc/testsuite/gcc.target/powerpc/is_type.c | 290 +++++++++++++++++++++++++++++ 5 files changed, 545 insertions(+), 3 deletions(-) diff --git a/gcc/config/rs6000/rs6000-builtins.def b/gcc/config/rs6000/rs6000-builtins.def index 3ca63739d317..709de4eb6998 100644 --- a/gcc/config/rs6000/rs6000-builtins.def +++ b/gcc/config/rs6000/rs6000-builtins.def @@ -2615,6 +2615,52 @@ ; Miscellaneous P9 functions [power9] + + signed int __builtin_isfinite_sp (float); + VEC_ISFINITESP isfinitesf2 {} + + signed int __builtin_isfinite_dp (double); + VEC_ISFINITEDP isfinitedf2 {} + + signed int __builtin_isfinite_qp (_Float128); + VEC_ISFINITEQP isfinitekf2 {} + + signed int __builtin_isinf_sp (float); + VEC_ISINFSP isinfsf2 {} + + signed int __builtin_isinf_dp (double); + VEC_ISINFDP isinfdf2 {} + + signed int __builtin_isinf_qp (_Float128); + VEC_ISINFQP isinfkf2 {} + + signed int __builtin_isnan_sp (float); + VEC_ISNANSP isnansf2 {} + + signed int __builtin_isnan_dp (double); + VEC_ISNANDP isnandf2 {} + + signed int __builtin_isnan_qp (_Float128); + VEC_ISNANQP isnankf2 {} + + signed int __builtin_issignaling_sp (float); + VEC_ISSIGNALINGSP issignalingsf2 {} + + signed int __builtin_issignaling_dp (double); + VEC_ISSIGNALINGDP issignalingdf2 {} + + signed int __builtin_issignaling_qp (_Float128); + VEC_ISSIGNALINGQP issignalingkf2 {} + + signed int __builtin_isinf_sign_sp (float); + VEC_ISINFSGNSP isinfsgnsf2 {} + + signed int __builtin_isinf_sign_dp (double); + VEC_ISINFSGNDP isinfsgndf2 {} + + signed int __builtin_isinf_sign_qp (_Float128); + VEC_ISINFSGNQP isinfsgnkf2 {} + signed long __builtin_darn (); DARN darn_64_di {32bit} @@ -2665,7 +2711,7 @@ LXVL lxvl {} const signed long long __builtin_vsx_scalar_extract_sig (double); - VSESDP xsxsigdp {} + VSESDP xsxsigdp_df {} const double __builtin_vsx_scalar_insert_exp (unsigned long long, \ unsigned long long); diff --git a/gcc/config/rs6000/rs6000-overload.def b/gcc/config/rs6000/rs6000-overload.def index b4266c544648..3bf27f56b831 100644 --- a/gcc/config/rs6000/rs6000-overload.def +++ b/gcc/config/rs6000/rs6000-overload.def @@ -4622,6 +4622,46 @@ unsigned int __builtin_vec_scalar_test_neg (_Float128); VSTDCNQP +[VEC_ISFINITE, __builtin_isfinite, __builtin_vec_isfinite] + signed int __builtin_vec_isfinite (float); + VEC_ISFINITESP + signed int __builtin_vec_isfinite (double); + VEC_ISFINITEDP + signed int __builtin_vec_isfinite (_Float128); + VEC_ISFINITEQP + +[VEC_ISINF, __builtin_isinf, __builtin_vec_isinf] + signed int __builtin_vec_isinf (float); + VEC_ISINFSP + signed int __builtin_vec_isinf (double); + VEC_ISINFDP + signed int __builtin_vec_isinf (_Float128); + VEC_ISINFQP + +[VEC_ISNAN, __builtin_isnan, __builtin_vec_isnan] + signed int __builtin_vec_isnan (float); + VEC_ISNANSP + signed int __builtin_vec_isnan (double); + VEC_ISNANDP + signed int __builtin_vec_isnan (_Float128); + VEC_ISNANQP + +[VEC_ISSIGNALING, __builtin_issignaling, __builtin_vec_issignaling] + signed int __builtin_vec_issignaling (float); + VEC_ISSIGNALINGSP + signed int __builtin_vec_issignaling (double); + VEC_ISSIGNALINGDP + signed int __builtin_vec_issignaling (_Float128); + VEC_ISSIGNALINGQP + +[VEC_ISINFSGN, __builtin_isinf_sign, __builtin_vec_isinf_sign] + signed int __builtin_vec_isinf_sign (float); + VEC_ISINFSGNSP + signed int __builtin_vec_isinf_sign (double); + VEC_ISINFSGNDP + signed int __builtin_vec_isinf_sign (_Float128); + VEC_ISINFSGNQP + [VEC_VTDC, vec_test_data_class, __builtin_vec_test_data_class] vbi __builtin_vec_test_data_class (vf, const int); VTDCSP diff --git a/gcc/config/rs6000/vsx.md b/gcc/config/rs6000/vsx.md index dd3573b80868..7991e415aa9e 100644 --- a/gcc/config/rs6000/vsx.md +++ b/gcc/config/rs6000/vsx.md @@ -369,6 +369,7 @@ UNSPEC_XXSPLTI32DX UNSPEC_XXBLEND UNSPEC_XXPERMX + UNSPEC_MFVSRD ]) (define_int_iterator XVCVBF16 [UNSPEC_VSX_XVCVSPBF16 @@ -5221,9 +5222,9 @@ [(set_attr "type" "vecmove")]) ;; VSX Scalar Extract Significand Double-Precision -(define_insn "xsxsigdp" +(define_insn "xsxsigdp_<mode>" [(set (match_operand:DI 0 "register_operand" "=r") - (unspec:DI [(match_operand:DF 1 "vsx_register_operand" "wa")] + (unspec:DI [(match_operand:FL_CONV 1 "vsx_register_operand" "wa")] UNSPEC_VSX_SXSIG))] "TARGET_P9_VECTOR && TARGET_POWERPC64" "xsxsigdp %0,%x1" @@ -5373,6 +5374,32 @@ DONE; }) +(define_expand "isinfsgn<mode>2" + [(use (match_operand:SI 0 "gpc_reg_operand")) + (use (match_operand:IEEE_FP 1 "vsx_register_operand"))] + "TARGET_P9_VECTOR + && (!FLOAT128_IEEE_P (<MODE>mode) || TARGET_FLOAT128_HW)" +{ + int mask_pos = VSX_TEST_DATA_CLASS_POS_INF; + int mask_neg = VSX_TEST_DATA_CLASS_NEG_INF; + rtx tmp = gen_reg_rtx (SImode); + rtx is_pos = gen_reg_rtx (SImode); + rtx is_neg = gen_reg_rtx (SImode); + + /* Set is_pos to 1 if operand is +infinity, zero otherwise. + Set is_net to 1 if operand is 1infinity, zero otherwise. */ + emit_insn (gen_xststdc_<mode> (is_pos, operands[1], + GEN_INT (mask_pos))); + emit_insn (gen_xststdc_<mode> (is_neg, operands[1], + GEN_INT (mask_neg))); + + /* Generate result, +1 if +infinity, -1 if -infinity, 0 otherwise. + Result = is_pos | ( is_neg * -1) */ + emit_insn (gen_mulsi3 (tmp, is_neg, GEN_INT (-1))); + emit_insn (gen_iorsi3 (operands[0], is_pos, tmp)); + DONE; +}) + (define_expand "isfinite<mode>2" [(use (match_operand:SI 0 "gpc_reg_operand")) (use (match_operand:IEEE_FP 1 "<fp_register_op>"))] @@ -5388,6 +5415,126 @@ DONE; }) +(define_insn "mfvsrd<mode>_to_di" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (unspec:DI [(match_operand:IEEE_FP 1 "vsx_register_operand" "wa")] + UNSPEC_MFVSRD))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "mfvsrd %0,%x1" + [(set_attr "type" "mfvsr")]) + +(define_expand "isnan<mode>2" + [(use (match_operand:SI 0 "gpc_reg_operand")) + (use (match_operand:IEEE_FP 1 "vsx_register_operand"))] + "TARGET_P9_VECTOR" +{ + /* Test if the operand is a NaN. Return 1 if it is, zero otherwise. + + The xststdc instruction returns true if the operand is either a NaN or + SNaN. Need to explicitly check the operand bit pattern to determine if + it is a NaN or SNaN. This is done by masking off the top fractional bit. + If the bit is a 1 than the operand is a NaN, if it is a 0, the operand is + a SNaN. */ + + rtx op1 = operands[1]; + +/* The single precision floating point value is actually stored in the + registers in double precision floating point format. So, we handle it + the same as a DF value. */ + int mask = VSX_TEST_DATA_CLASS_NAN; + unsigned long long NaN_bit; /* Mask for top fractional bit */ + unsigned long long shift_bit_to_lsb; /* Shift top fractional bit to lsb */ + + if (<MODE>mode == SFmode || <MODE>mode == DFmode) + { + NaN_bit = 0x0008000000000000; + shift_bit_to_lsb = 51; + } + else + { + NaN_bit = 0x0000800000000000; + shift_bit_to_lsb = 47; + } + + rtx tmp = gen_reg_rtx (SImode); + rtx tmp_and = gen_reg_rtx (DImode); + rtx tmp_shift = gen_reg_rtx (DImode); + + /* Move floaating point mantissa value from fp register to gpr. */ + rtx operand1 = gen_reg_rtx (DImode); +// emit_insn (gen_mfvsrd<mode>_to_di (operand1, op1)); + emit_insn (gen_xsxsigdp_<mode> (operand1, operands[1])); + + /* Get most significant fraction bit, it will be a one if the operand is a + NaN. */ + emit_insn (gen_anddi3 (tmp_and, operand1, GEN_INT (NaN_bit))); + emit_insn (gen_lshrdi3 (tmp_shift, tmp_and, GEN_INT (shift_bit_to_lsb))); + + emit_insn (gen_xststdc_<mode> (tmp, op1, GEN_INT (mask))); + + /* If tmp and tmp_shift_si are both 1 then the value is NaN. Note, the + value in tmp will be 0 or 1 so it will effectively mask off just the + least significant bit in tmp_shift if tmp is a 1. */ + rtx tmp_shift_si = gen_lowpart (SImode, tmp_shift); + emit_insn (gen_andsi3 (operands[0], tmp, tmp_shift_si)); + + DONE; +}) + +(define_expand "issignaling<mode>2" + [(use (match_operand:SI 0 "gpc_reg_operand")) + (use (match_operand:IEEE_FP 1 "vsx_register_operand"))] + "TARGET_P9_VECTOR" +{ + /* Test if the operand is a SNaN. Return 1 if it is, zero otherwise. + + The xststdc instruction returns true if the operand is either a NaN or + SNaN. Need to explicitly check the operand bit pattern to determine if + it is a NaN or SNaN. This is done by masking off the top fractional bit. + If the bit is a 1 than the operand is a NaN, if it is a 0, the operand is + a SNaN. */ + + /* The single precision floating point value is actually stored in the + registers in double precision floating point format. So, we handle it + the same as a DF value. */ + int mask = VSX_TEST_DATA_CLASS_NAN; + unsigned long long NaN_bit; /* Mask for top fractional bit */ + unsigned long long shift_bit_to_lsb; /* Shift top fractional bit to lsb */ + + if (<MODE>mode == SFmode || <MODE>mode == DFmode) + { + NaN_bit = 0x0008000000000000; + shift_bit_to_lsb = 51; + } + else + { + NaN_bit = 0x0000800000000000; + shift_bit_to_lsb = 47; + } + + rtx tmp = gen_reg_rtx (SImode); + rtx tmp_and = gen_reg_rtx (DImode); + rtx tmp_shift = gen_reg_rtx (DImode); + + /* Move floaating point mantissa value from fp register to gpr. */ + rtx operand1 = gen_reg_rtx (DImode); +// emit_insn (gen_mfvsrd<mode>_to_di (operand1, operands[1])); + emit_insn (gen_xsxsigdp_<mode> (operand1, operands[1])); + + /* Get most significant fraction bit, it will be a zero if the operand is a + SNaN, complement it, and move to lease significant bit position. */ + emit_insn (gen_anddi3 (tmp_and, operand1, GEN_INT (NaN_bit))); + emit_insn (gen_one_cmpldi2 (tmp_and, tmp_and)); + emit_insn (gen_lshrdi3 (tmp_shift, tmp_and, GEN_INT (shift_bit_to_lsb))); + + emit_insn (gen_xststdc_<mode> (tmp, operands[1], GEN_INT (mask))); + + /* If tmp and tmp_shift_si are both 1 then the value is NaN. */ + rtx tmp_shift_si = gen_lowpart (SImode, tmp_shift); + emit_insn (gen_andsi3 (operands[0], tmp, tmp_shift_si)); + + DONE; +}) (define_expand "isnormal<mode>2" [(use (match_operand:SI 0 "gpc_reg_operand")) (use (match_operand:IEEE_FP 1 "<fp_register_op>"))] diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index beaf67e59a1b..73b167c9a249 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -23310,6 +23310,18 @@ bool scalar_test_data_class (__ieee128 source, const int condition); bool scalar_test_neg (float source); bool scalar_test_neg (double source); bool scalar_test_neg (__ieee128 source); + +signed int __builtin_isinf (float); +signed int __builtin_isinf (double); +signed int __builtin_isinf (_Float128); + +signed int __builtin_isinf_sign (float); +signed int __builtin_isinf_sign (double); +signed int __builtin_isinf_sign (_Float128); + +signed int __builtin_isfinite (float); +signed int __builtin_isfinite (double); +signed int __builtin_isfinite (_Float128); @end smallexample The @code{scalar_extract_exp} with a 64-bit source argument @@ -23391,6 +23403,13 @@ following: The @code{scalar_test_neg} built-in function returns 1 if its @code{source} argument holds a negative value, 0 otherwise. +The @code{__builtin_isinf}, @code{builtin_isinf} built-in functions return 1 if +its argument is positive or negative infinity, 0 otherwise. + +The @code{__builtin_isinf_sign} returns +1 if +its argument is positive infinity, -1 if it argument is negative infinity, +0 otherwise. + The following built-in functions are also available for the PowerPC family of processors, starting with ISA 3.0 or later (@option{-mcpu=power9}). These string functions are described diff --git a/gcc/testsuite/gcc.target/powerpc/is_type.c b/gcc/testsuite/gcc.target/powerpc/is_type.c new file mode 100644 index 000000000000..672c4edf1dea --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/is_type.c @@ -0,0 +1,290 @@ +/* { dg-do run } */ +/* { dg-require-effective-target p9vector_hw } */ +/* { dg-options "-mdejagnu-cpu=power9 -O2" } */ + +#include <altivec.h> +#include <math.h> +#include <stdlib.h> + +int errors = 0; +int tests = 0; + +#define DEBUG 1 +#ifdef DEBUG +#include <stdio.h> +#endif + +void abort (void); + +/* Force the optimizer to not 'optimize' constants. */ +static void +f_test (float f, int expected_result, + const char *test_name __attribute__ ((unused))) +{ + int result = __builtin_isnan_sp (f); + + tests++; + if (result != expected_result) + { +#if DEBUG + union { + unsigned i; + float f; + } u; + + u.f = f; + errors++; + printf ("ERROR: %s: expected: %d, result: %d, arg = %g (0x%x)\n", + test_name, + expected_result, + result, + f, + u.i); +#else + abort (); +#endif + } + return; +} + +static void +d_test (double d, int expected_result, + const char *test_name __attribute__ ((unused))) +{ + int result = __builtin_isnan_dp (d); + + tests++; + if (result != expected_result) + { +#if DEBUG + union { + unsigned long long ll; + double d; + } u; + + u.d = d; + errors++; + printf ("ERROR: %s: expected: %d, result: %d, arg = %g (0x%llx)\n", + test_name, + expected_result, + result, + d, + u.ll); +#else + abort (); +#endif + } + return; +} + +static void +q_test (_Float128 q, int expected_result, + const char *test_name __attribute__ ((unused))) +{ + int result = __builtin_isnan_qp (q); + + tests++; + if (result != expected_result) + { +#if DEBUG + union { + __uint128_t uin128; + unsigned long long ll[2]; + _Float128 q; + } u; + + u.q = q; + errors++; + printf ("ERROR: %s: expected: %d, result: %d, arg = %g (0x%llx 0x%llx)\n", + test_name, + expected_result, + result, + (double) q, + u.ll[0], u.ll[1]); +#else + abort (); +#endif + } + return; +} + +int main () +{ + + float arg; + double darg; + int result, expected_result; + /* Single precision: + SNaN is a value between X'7FC00001 and X'7FFFFFFF + NaN is a value between X'7F800000 and X'7FFFFFFF + Infinity is X'7FC00000 + + Double precision: + SNaN is a value between X'7FF0000000000001 and X'7FF7FFFFFFFFFFFF + NaN is a value between X'7FF8000000000000 and X'7FF8FFFFFFFFFFFF + Infinity is X'7FF0000000000000 + + IEEE 128-bit: + NaN is a value between X'7FFF8000000000000000000000000001 + and X'7FFF8FFFFFFFFFFFFFFFFFFFFFFFFFFF + SNaN is a value between X'7FFF0000000000000000000000000000 + and X'7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + Infinity is X'7FFF0000000000000000000000000000 + where X represents the sign-bit which is a don't care. */ + +#define SF_NAN_1 0x7FC00000 +#define SF_NAN_2 0x7FFFFFFF +#define SF_SNAN_1 0x7F800001 +#define SF_SNAN_2 0x7F8FFFFF + +#define DF_NAN_1 0x7FF8000000000000ULL +#define DF_NAN_2 0x7FF8FFFFFFFFFFFFULL +#define DF_SNAN_1 0x7FF0000000000001ULL +#define DF_SNAN_2 0x7FF7FFFFFFFFFFFFULL + + union { + unsigned int i; + float f; + } f_value; + + union { + unsigned long long int ll; + double d; + } d_value; + + _Float128 qarg; + + union { + __uint128_t uint128; + _Float128 q; + } q_value; + + /* Test builtin_isnan, single precision */ + arg = 0.0f; + expected_result = 0; + f_test (arg, expected_result, "float test 1"); + + arg = 1.0f; + expected_result = 0; + f_test (arg, expected_result, "float test 2"); + + arg = -100.0f; + expected_result = 0; + f_test (arg, expected_result, "float test 3"); + + arg = __builtin_inff (); + expected_result = 0; + f_test (arg, expected_result, "float test 4"); + + arg = - __builtin_inff (); + expected_result = 0; + f_test (arg, expected_result, "float test 5"); + + arg = __builtin_nanf (""); + expected_result = 1; + f_test (arg, expected_result, "float test 6"); + + arg = __builtin_nansf (""); + expected_result = 0; /* isnan of SNaN should be 0. */ + f_test (arg, expected_result, "float test 7"); + + f_value.i = SF_NAN_1; + expected_result = 1; + f_test (f_value.f, expected_result, "float test 8"); + + f_value.i = SF_NAN_2; + expected_result = 1; + f_test (f_value.f, expected_result, "float test 9"); + + f_value.i = SF_SNAN_1; + expected_result = 0; /* isnan of SNaN should be 0. */ + f_test (f_value.f, expected_result, "float test 10"); + + f_value.i = SF_SNAN_2; + expected_result = 0; /* isnan of SNaN should be 0. */ + f_test (f_value.f, expected_result, "float test 11"); + + /* Test builtin_isnan, double precision */ + darg = 0.0; + expected_result = 0; + d_test (darg, expected_result, "double test 1"); + + darg = 1.0; + expected_result = 0; + d_test (darg, expected_result, "double test 2"); + + darg = -200.0; + expected_result = 0; + d_test (darg, expected_result, "double test 3"); + + darg = __builtin_inf (); + expected_result = 0; + d_test (darg, expected_result, "double test 4"); + + darg = - __builtin_inf (); + expected_result = 0; + d_test (darg, expected_result, "double test 5"); + + darg = __builtin_nan (""); + expected_result = 1; + d_test (darg, expected_result, "double test 6"); + + darg = __builtin_nans (""); + expected_result = 0; /* isnan of SNaN should be 0. */ + d_test (darg, expected_result, "double test 7"); + + d_value.ll = DF_NAN_1; + expected_result = 1; + d_test (d_value.d, expected_result, "double test 8"); + + d_value.ll = DF_NAN_2; + expected_result = 1; + d_test (d_value.d, expected_result, "double test 9"); + + d_value.ll = DF_SNAN_1; + expected_result = 0; /* isnan of SNaN should be 0. */ + d_test (d_value.d, expected_result, "double test 10"); + + d_value.ll = DF_SNAN_2; + expected_result = 0; /* isnan of SNaN should be 0. */ + d_test (d_value.d, expected_result, "double test 11"); + + /* Test builtin_isnan, IEEE-128 precision */ + qarg = 0.0F128; + expected_result = 0; + q_test (qarg, expected_result, "_Float128 test 1"); + + qarg = 1.0F128; + expected_result = 0; + q_test (qarg, expected_result, "_Float128 test 2"); + + qarg = -200.0F128; + expected_result = 0; + q_test (qarg, expected_result, "_Float128 test 3"); + + qarg = __builtin_inff128 (); + expected_result = 0; + q_test (qarg, expected_result, "_Float128 test 4"); + + qarg = - __builtin_inff128 (); + expected_result = 0; + q_test (qarg, expected_result, "_Float128 test 5"); + + qarg = __builtin_nanf128 (""); + expected_result = 1; + q_test (qarg, expected_result, "_Float128 test 6"); + + qarg = __builtin_nansf128 (""); + expected_result = 0; /* isnan of SNaN should be 0. */ + q_test (qarg, expected_result, "_Float128 test 7"); + +#if DEBUG + printf ("%d error%s, %d tests\n", + errors, + errors == 1 ? "" : "s", + tests); +#endif + + return errors; +} +