From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtp-out1.suse.de (smtp-out1.suse.de [IPv6:2001:67c:2178:6::1c]) by sourceware.org (Postfix) with ESMTPS id 1F39E3858429 for ; Mon, 15 Aug 2022 11:24:16 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 1F39E3858429 Received: from relay2.suse.de (relay2.suse.de [149.44.160.134]) by smtp-out1.suse.de (Postfix) with ESMTP id 556D034C7D; Mon, 15 Aug 2022 11:24:15 +0000 (UTC) Received: from wotan.suse.de (wotan.suse.de [10.160.0.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by relay2.suse.de (Postfix) with ESMTPS id 5051F2C1B9; Mon, 15 Aug 2022 11:24:14 +0000 (UTC) Date: Mon, 15 Aug 2022 11:24:14 +0000 (UTC) From: Richard Biener To: Jakub Jelinek cc: "Joseph S. Myers" , Jeff Law , gcc-patches@gcc.gnu.org, FX Subject: Re: [PATCH] Implement __builtin_issignaling In-Reply-To: Message-ID: References: User-Agent: Alpine 2.22 (LSU 394 2020-01-19) MIME-Version: 1.0 X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, KAM_SHORT, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org Content-Type: text/plain; charset=ISO-8859-7 Content-Transfer-Encoding: 8BIT X-Content-Filtered-By: Mailman/MimeDel 2.1.29 X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 15 Aug 2022 11:24:21 -0000 On Mon, 15 Aug 2022, Jakub Jelinek wrote: > Hi! > > The following patch implements a new builtin, __builtin_issignaling, > which can be used to implement the ISO/IEC TS 18661-1 issignaling > macro. > > It is implemented as type-generic function, so there is just one > builtin, not many with various suffixes. > This patch doesn't address PR56831 nor PR58416, but I think compared to > using glibc issignaling macro could make some cases better (as > the builtin is expanded always inline and for SFmode/DFmode just > reinterprets a memory or pseudo register as SImode/DImode, so could > avoid some raising of exception + turning sNaN into qNaN before the > builtin can analyze it). > > For floading point modes that do not have NaNs it will return 0, > otherwise I've tried to implement this for all the other supported > real formats. > It handles both the MIPS/PA floats where a sNaN has the mantissa > MSB set and the rest where a sNaN has it cleared, with the exception > of format which are known never to be in the MIPS/PA form. > The MIPS/PA floats are handled using a test like > (x & mask) == mask, > the other usually as > ((x ^ bit) & mask) > val > where bit, mask and val are some constants. > IBM double double is done by doing DFmode test on the most significant > half, and Intel/Motorola extended (12 or 16 bytes) and IEEE quad are > handled by extracting 32-bit/16-bit words or 64-bit parts from the > value and testing those. > On x86, XFmode is handled by a special optab so that even pseudo numbers > are considered signaling, like in glibc and like the i386 specific testcase > tests. > > Bootstrapped/regtested on x86_64-linux, i686-linux, powerpc64le-linux and > powerpc64-linux (the last tested with -m32/-m64), ok for trunk? Unlike the issignalling macro from glibc the builtin will return false for sNaN arguments when -fno-signalling-nans is used (similar to isinf, isnan, etc.). I think this deserves mentioning in the documentation (and I have my reservations about this long-time behavior of FP classification builtins we have). Generally it looks OK - what does it do to size optimized code? glibc 2.31 seems to silently accept #include int foo(_Complex double x) { return issignaling (x); } for vector double we get t.c:5:23: error: incompatible type for argument 1 of ¡__issignalingf¢ 5 | return issignaling (x); | ^ | | | v2df {aka __vector(2) double} /usr/include/bits/mathcalls-helper-functions.h:42:1: note: expected ¡float¢ but argument is of type ¡v2df¢ {aka ¡__vector(2) double¢} 42 | __MATHDECL_1 (int, __issignaling,, (_Mdouble_ __value)) | ^ as far as I can see your __builtin silently accepts all arguments without diagnostics and eventually dispatches to 'issignaling' which isn't in glibc which instead seems to have __issignaling? Richard. > 2022-08-15 Jakub Jelinek > > gcc/ > * builtins.def (BUILT_IN_ISSIGNALING): New built-in. > * builtins.cc (expand_builtin_issignaling): New function. > (expand_builtin_signbit): Don't overwrite target. > (expand_builtin): Handle BUILT_IN_ISSIGNALING. > (fold_builtin_classify): Likewise. > (fold_builtin_1): Likewise. > * optabs.def (issignaling_optab): New. > * fold-const-call.cc (fold_const_call_ss): Handle > BUILT_IN_ISSIGNALING. > * config/i386/i386.md (issignalingxf2): New expander. > * doc/extend.texi (__builtin_issignaling): Document. > * doc/md.texi (issignaling2): Likewise. > gcc/c-family/ > * c-common.cc (check_builtin_function_arguments): Handle > BUILT_IN_ISSIGNALING. > gcc/c/ > * c-typeck.cc (convert_arguments): Handle BUILT_IN_ISSIGNALING. > gcc/fortran/ > * f95-lang.cc (gfc_init_builtin_functions): Initialize > BUILT_IN_ISSIGNALING. > gcc/testsuite/ > * gcc.dg/torture/builtin-issignaling-1.c: New test. > * gcc.dg/torture/builtin-issignaling-2.c: New test. > * gcc.target/i386/builtin-issignaling-1.c: New test. > > --- gcc/builtins.def.jj 2022-01-11 23:11:21.548301986 +0100 > +++ gcc/builtins.def 2022-08-11 12:15:14.200908656 +0200 > @@ -908,6 +908,7 @@ DEF_GCC_BUILTIN (BUILT_IN_ISLESS, > DEF_GCC_BUILTIN (BUILT_IN_ISLESSEQUAL, "islessequal", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF) > DEF_GCC_BUILTIN (BUILT_IN_ISLESSGREATER, "islessgreater", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF) > DEF_GCC_BUILTIN (BUILT_IN_ISUNORDERED, "isunordered", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF) > +DEF_GCC_BUILTIN (BUILT_IN_ISSIGNALING, "issignaling", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF) > DEF_LIB_BUILTIN (BUILT_IN_LABS, "labs", BT_FN_LONG_LONG, ATTR_CONST_NOTHROW_LEAF_LIST) > DEF_C99_BUILTIN (BUILT_IN_LLABS, "llabs", BT_FN_LONGLONG_LONGLONG, ATTR_CONST_NOTHROW_LEAF_LIST) > DEF_GCC_BUILTIN (BUILT_IN_LONGJMP, "longjmp", BT_FN_VOID_PTR_INT, ATTR_NORETURN_NOTHROW_LIST) > --- gcc/builtins.cc.jj 2022-07-26 10:32:23.250277352 +0200 > +++ gcc/builtins.cc 2022-08-12 17:13:06.158423558 +0200 > @@ -123,6 +123,7 @@ static rtx expand_builtin_fegetround (tr > static rtx expand_builtin_feclear_feraise_except (tree, rtx, machine_mode, > optab); > static rtx expand_builtin_cexpi (tree, rtx); > +static rtx expand_builtin_issignaling (tree, rtx); > static rtx expand_builtin_int_roundingfn (tree, rtx); > static rtx expand_builtin_int_roundingfn_2 (tree, rtx); > static rtx expand_builtin_next_arg (void); > @@ -2747,6 +2748,294 @@ build_call_nofold_loc (location_t loc, t > return fn; > } > > +/* Expand the __builtin_issignaling builtin. This needs to handle > + all floating point formats that do support NaNs (for those that > + don't it just sets target to 0). */ > + > +static rtx > +expand_builtin_issignaling (tree exp, rtx target) > +{ > + if (!validate_arglist (exp, REAL_TYPE, VOID_TYPE)) > + return NULL_RTX; > + > + tree arg = CALL_EXPR_ARG (exp, 0); > + scalar_float_mode fmode = SCALAR_FLOAT_TYPE_MODE (TREE_TYPE (arg)); > + const struct real_format *fmt = REAL_MODE_FORMAT (fmode); > + > + /* Expand the argument yielding a RTX expression. */ > + rtx temp = expand_normal (arg); > + > + /* If mode doesn't support NaN, always return 0. */ > + if (!HONOR_NANS (fmode)) > + { > + emit_move_insn (target, const0_rtx); > + return target; > + } > + > + /* Check if the back end provides an insn that handles issignaling for the > + argument's mode. */ > + enum insn_code icode = optab_handler (issignaling_optab, fmode); > + if (icode != CODE_FOR_nothing) > + { > + rtx_insn *last = get_last_insn (); > + rtx this_target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp))); > + if (maybe_emit_unop_insn (icode, this_target, temp, UNKNOWN)) > + return this_target; > + delete_insns_since (last); > + } > + > + if (DECIMAL_FLOAT_MODE_P (fmode)) > + { > + scalar_int_mode imode; > + rtx hi; > + switch (fmt->ieee_bits) > + { > + case 32: > + case 64: > + imode = int_mode_for_mode (fmode).require (); > + temp = gen_lowpart (imode, temp); > + break; > + case 128: > + imode = int_mode_for_size (64, 1).require (); > + hi = NULL_RTX; > + /* For decimal128, TImode support isn't always there and even when > + it is, working on the DImode high part is usually better. */ > + if (!MEM_P (temp)) > + { > + if (rtx t = simplify_gen_subreg (imode, temp, fmode, > + subreg_highpart_offset (imode, > + fmode))) > + hi = t; > + else > + { > + scalar_int_mode imode2; > + if (int_mode_for_mode (fmode).exists (&imode2)) > + { > + rtx temp2 = gen_lowpart (imode2, temp); > + poly_uint64 off = subreg_highpart_offset (imode, imode2); > + if (rtx t = simplify_gen_subreg (imode, temp2, > + imode2, off)) > + hi = t; > + } > + } > + if (!hi) > + { > + rtx mem = assign_stack_temp (fmode, GET_MODE_SIZE (fmode)); > + emit_move_insn (mem, temp); > + temp = mem; > + } > + } > + if (!hi) > + { > + poly_int64 offset > + = subreg_highpart_offset (imode, GET_MODE (temp)); > + hi = adjust_address (temp, imode, offset); > + } > + temp = hi; > + break; > + default: > + gcc_unreachable (); > + } > + /* In all of decimal{32,64,128}, there is MSB sign bit and sNaN > + have 6 bits below it all set. */ > + rtx val > + = GEN_INT (HOST_WIDE_INT_C (0x3f) << (GET_MODE_BITSIZE (imode) - 7)); > + temp = expand_binop (imode, and_optab, temp, val, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + temp = emit_store_flag_force (target, EQ, temp, val, imode, 1, 1); > + return temp; > + } > + > + /* Only PDP11 has these defined differently but doesn't support NaNs. */ > + gcc_assert (FLOAT_WORDS_BIG_ENDIAN == WORDS_BIG_ENDIAN); > + gcc_assert (fmt->signbit_ro > 0 && fmt->b == 2); > + gcc_assert (MODE_COMPOSITE_P (fmode) > + || (fmt->pnan == fmt->p > + && fmt->signbit_ro == fmt->signbit_rw)); > + > + switch (fmt->p) > + { > + case 106: /* IBM double double */ > + /* For IBM double double, recurse on the most significant double. */ > + gcc_assert (MODE_COMPOSITE_P (fmode)); > + temp = convert_modes (DFmode, fmode, temp, 0); > + fmode = DFmode; > + fmt = REAL_MODE_FORMAT (DFmode); > + /* FALLTHRU */ > + case 8: /* bfloat */ > + case 11: /* IEEE half */ > + case 24: /* IEEE single */ > + case 53: /* IEEE double or Intel extended with rounding to double */ > + if (fmt->p == 53 && fmt->signbit_ro == 79) > + goto extended; > + { > + scalar_int_mode imode = int_mode_for_mode (fmode).require (); > + temp = gen_lowpart (imode, temp); > + rtx val = GEN_INT ((HOST_WIDE_INT_M1U << (fmt->p - 2)) > + & ~(HOST_WIDE_INT_M1U << fmt->signbit_ro)); > + if (fmt->qnan_msb_set) > + { > + rtx mask = GEN_INT (~(HOST_WIDE_INT_M1U << fmt->signbit_ro)); > + rtx bit = GEN_INT (HOST_WIDE_INT_1U << (fmt->p - 2)); > + /* For non-MIPS/PA IEEE single/double/half or bfloat, expand to: > + ((temp ^ bit) & mask) > val. */ > + temp = expand_binop (imode, xor_optab, temp, bit, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + temp = expand_binop (imode, and_optab, temp, mask, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + temp = emit_store_flag_force (target, GTU, temp, val, imode, > + 1, 1); > + } > + else > + { > + /* For MIPS/PA IEEE single/double, expand to: > + (temp & val) == val. */ > + temp = expand_binop (imode, and_optab, temp, val, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + temp = emit_store_flag_force (target, EQ, temp, val, imode, > + 1, 1); > + } > + } > + break; > + case 113: /* IEEE quad */ > + { > + rtx hi = NULL_RTX, lo = NULL_RTX; > + scalar_int_mode imode = int_mode_for_size (64, 1).require (); > + /* For IEEE quad, TImode support isn't always there and even when > + it is, working on DImode parts is usually better. */ > + if (!MEM_P (temp)) > + { > + hi = simplify_gen_subreg (imode, temp, fmode, > + subreg_highpart_offset (imode, fmode)); > + lo = simplify_gen_subreg (imode, temp, fmode, > + subreg_lowpart_offset (imode, fmode)); > + if (!hi || !lo) > + { > + scalar_int_mode imode2; > + if (int_mode_for_mode (fmode).exists (&imode2)) > + { > + rtx temp2 = gen_lowpart (imode2, temp); > + hi = simplify_gen_subreg (imode, temp2, imode2, > + subreg_highpart_offset (imode, > + imode2)); > + lo = simplify_gen_subreg (imode, temp2, imode2, > + subreg_lowpart_offset (imode, > + imode2)); > + } > + } > + if (!hi || !lo) > + { > + rtx mem = assign_stack_temp (fmode, GET_MODE_SIZE (fmode)); > + emit_move_insn (mem, temp); > + temp = mem; > + } > + } > + if (!hi || !lo) > + { > + poly_int64 offset > + = subreg_highpart_offset (imode, GET_MODE (temp)); > + hi = adjust_address (temp, imode, offset); > + offset = subreg_lowpart_offset (imode, GET_MODE (temp)); > + lo = adjust_address (temp, imode, offset); > + } > + rtx val = GEN_INT ((HOST_WIDE_INT_M1U << (fmt->p - 2 - 64)) > + & ~(HOST_WIDE_INT_M1U << (fmt->signbit_ro - 64))); > + if (fmt->qnan_msb_set) > + { > + rtx mask = GEN_INT (~(HOST_WIDE_INT_M1U << (fmt->signbit_ro > + - 64))); > + rtx bit = GEN_INT (HOST_WIDE_INT_1U << (fmt->p - 2 - 64)); > + /* For non-MIPS/PA IEEE quad, expand to: > + (((hi ^ bit) | ((lo | -lo) >> 63)) & mask) > val. */ > + rtx nlo = expand_unop (imode, neg_optab, lo, NULL_RTX, 0); > + lo = expand_binop (imode, ior_optab, lo, nlo, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + lo = expand_shift (RSHIFT_EXPR, imode, lo, 63, NULL_RTX, 1); > + temp = expand_binop (imode, xor_optab, hi, bit, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + temp = expand_binop (imode, ior_optab, temp, lo, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + temp = expand_binop (imode, and_optab, temp, mask, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + temp = emit_store_flag_force (target, GTU, temp, val, imode, > + 1, 1); > + } > + else > + { > + /* For MIPS/PA IEEE quad, expand to: > + (hi & val) == val. */ > + temp = expand_binop (imode, and_optab, hi, val, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + temp = emit_store_flag_force (target, EQ, temp, val, imode, > + 1, 1); > + } > + } > + break; > + case 64: /* Intel or Motorola extended */ > + extended: > + { > + rtx ex, hi, lo; > + scalar_int_mode imode = int_mode_for_size (32, 1).require (); > + scalar_int_mode iemode = int_mode_for_size (16, 1).require (); > + if (!MEM_P (temp)) > + { > + rtx mem = assign_stack_temp (fmode, GET_MODE_SIZE (fmode)); > + emit_move_insn (mem, temp); > + temp = mem; > + } > + if (fmt->signbit_ro == 95) > + { > + /* Motorola, always big endian, with 16-bit gap in between > + 16-bit sign+exponent and 64-bit mantissa. */ > + ex = adjust_address (temp, iemode, 0); > + hi = adjust_address (temp, imode, 4); > + lo = adjust_address (temp, imode, 8); > + } > + else if (!WORDS_BIG_ENDIAN) > + { > + /* Intel little endian, 64-bit mantissa followed by 16-bit > + sign+exponent and then either 16 or 48 bits of gap. */ > + ex = adjust_address (temp, iemode, 8); > + hi = adjust_address (temp, imode, 4); > + lo = adjust_address (temp, imode, 0); > + } > + else > + { > + /* Big endian Itanium. */ > + ex = adjust_address (temp, iemode, 0); > + hi = adjust_address (temp, imode, 2); > + lo = adjust_address (temp, imode, 6); > + } > + rtx val = GEN_INT (HOST_WIDE_INT_M1U << 30); > + gcc_assert (fmt->qnan_msb_set); > + rtx mask = GEN_INT (0x7fff); > + rtx bit = GEN_INT (HOST_WIDE_INT_1U << 30); > + /* For Intel/Motorola extended format, expand to: > + (ex & mask) == mask && ((hi ^ bit) | ((lo | -lo) >> 31)) > val. */ > + rtx nlo = expand_unop (imode, neg_optab, lo, NULL_RTX, 0); > + lo = expand_binop (imode, ior_optab, lo, nlo, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + lo = expand_shift (RSHIFT_EXPR, imode, lo, 31, NULL_RTX, 1); > + temp = expand_binop (imode, xor_optab, hi, bit, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + temp = expand_binop (imode, ior_optab, temp, lo, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + temp = emit_store_flag_force (target, GTU, temp, val, imode, 1, 1); > + ex = expand_binop (iemode, and_optab, ex, mask, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + ex = emit_store_flag_force (gen_reg_rtx (GET_MODE (temp)), EQ, > + ex, mask, iemode, 1, 1); > + temp = expand_binop (GET_MODE (temp), and_optab, temp, ex, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + } > + break; > + default: > + gcc_unreachable (); > + } > + > + return temp; > +} > + > /* Expand a call to one of the builtin rounding functions gcc defines > as an extension (lfloor and lceil). As these are gcc extensions we > do not need to worry about setting errno to EDOM. > @@ -5508,9 +5797,9 @@ expand_builtin_signbit (tree exp, rtx ta > if (icode != CODE_FOR_nothing) > { > rtx_insn *last = get_last_insn (); > - target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp))); > - if (maybe_emit_unop_insn (icode, target, temp, UNKNOWN)) > - return target; > + rtx this_target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp))); > + if (maybe_emit_unop_insn (icode, this_target, temp, UNKNOWN)) > + return this_target; > delete_insns_since (last); > } > > @@ -7120,6 +7409,12 @@ expand_builtin (tree exp, rtx target, rt > return target; > break; > > + case BUILT_IN_ISSIGNALING: > + target = expand_builtin_issignaling (exp, target); > + if (target) > + return target; > + break; > + > CASE_FLT_FN (BUILT_IN_ICEIL): > CASE_FLT_FN (BUILT_IN_LCEIL): > CASE_FLT_FN (BUILT_IN_LLCEIL): > @@ -8963,6 +9258,11 @@ fold_builtin_classify (location_t loc, t > arg = builtin_save_expr (arg); > return fold_build2_loc (loc, UNORDERED_EXPR, type, arg, arg); > > + case BUILT_IN_ISSIGNALING: > + if (!tree_expr_maybe_nan_p (arg)) > + return omit_one_operand_loc (loc, type, integer_zero_node, arg); > + return NULL_TREE; > + > default: > gcc_unreachable (); > } > @@ -9399,6 +9699,9 @@ fold_builtin_1 (location_t loc, tree exp > case BUILT_IN_ISNAND128: > return fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISNAN); > > + case BUILT_IN_ISSIGNALING: > + return fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISSIGNALING); > + > case BUILT_IN_FREE: > if (integer_zerop (arg0)) > return build_empty_stmt (loc); > --- gcc/optabs.def.jj 2022-02-04 14:36:55.424599447 +0100 > +++ gcc/optabs.def 2022-08-11 13:06:09.888416939 +0200 > @@ -313,6 +313,7 @@ OPTAB_D (fmod_optab, "fmod$a3") > OPTAB_D (hypot_optab, "hypot$a3") > OPTAB_D (ilogb_optab, "ilogb$a2") > OPTAB_D (isinf_optab, "isinf$a2") > +OPTAB_D (issignaling_optab, "issignaling$a2") > OPTAB_D (ldexp_optab, "ldexp$a3") > OPTAB_D (log10_optab, "log10$a2") > OPTAB_D (log1p_optab, "log1p$a2") > --- gcc/fold-const-call.cc.jj 2022-01-18 11:58:59.510983085 +0100 > +++ gcc/fold-const-call.cc 2022-08-11 12:31:07.294918860 +0200 > @@ -952,6 +952,10 @@ fold_const_call_ss (wide_int *result, co > *result = wi::shwi (real_isfinite (arg) ? 1 : 0, precision); > return true; > > + case CFN_BUILT_IN_ISSIGNALING: > + *result = wi::shwi (real_issignaling_nan (arg) ? 1 : 0, precision); > + return true; > + > CASE_CFN_ISINF: > case CFN_BUILT_IN_ISINFD32: > case CFN_BUILT_IN_ISINFD64: > --- gcc/config/i386/i386.md.jj 2022-08-10 09:06:51.463232943 +0200 > +++ gcc/config/i386/i386.md 2022-08-12 11:56:14.763951760 +0200 > @@ -24720,6 +24720,58 @@ (define_expand "spaceshipxf3" > DONE; > }) > > +;; Defined because the generic expand_builtin_issignaling for XFmode > +;; only tests for sNaNs, but i387 treats also pseudo numbers as always > +;; signaling. > +(define_expand "issignalingxf2" > + [(match_operand:SI 0 "register_operand") > + (match_operand:XF 1 "general_operand")] > + "" > +{ > + rtx temp = operands[1]; > + if (!MEM_P (temp)) > + { > + rtx mem = assign_stack_temp (XFmode, GET_MODE_SIZE (XFmode)); > + emit_move_insn (mem, temp); > + temp = mem; > + } > + rtx ex = adjust_address (temp, HImode, 8); > + rtx hi = adjust_address (temp, SImode, 4); > + rtx lo = adjust_address (temp, SImode, 0); > + rtx val = GEN_INT (HOST_WIDE_INT_M1U << 30); > + rtx mask = GEN_INT (0x7fff); > + rtx bit = GEN_INT (HOST_WIDE_INT_1U << 30); > + /* Expand to: > + ((ex & mask) && (int) hi >= 0) > + || ((ex & mask) == mask && ((hi ^ bit) | ((lo | -lo) >> 31)) > val). */ > + rtx nlo = expand_unop (SImode, neg_optab, lo, NULL_RTX, 0); > + lo = expand_binop (SImode, ior_optab, lo, nlo, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + lo = expand_shift (RSHIFT_EXPR, SImode, lo, 31, NULL_RTX, 1); > + temp = expand_binop (SImode, xor_optab, hi, bit, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + temp = expand_binop (SImode, ior_optab, temp, lo, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + temp = emit_store_flag_force (gen_reg_rtx (SImode), GTU, temp, val, > + SImode, 1, 1); > + ex = expand_binop (HImode, and_optab, ex, mask, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + rtx temp2 = emit_store_flag_force (gen_reg_rtx (SImode), NE, > + ex, const0_rtx, SImode, 1, 1); > + ex = emit_store_flag_force (gen_reg_rtx (SImode), EQ, > + ex, mask, HImode, 1, 1); > + temp = expand_binop (SImode, and_optab, temp, ex, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + rtx temp3 = emit_store_flag_force (gen_reg_rtx (SImode), GE, > + hi, const0_rtx, SImode, 0, 1); > + temp2 = expand_binop (SImode, and_optab, temp2, temp3, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + temp = expand_binop (SImode, ior_optab, temp, temp2, > + NULL_RTX, 1, OPTAB_LIB_WIDEN); > + emit_move_insn (operands[0], temp); > + DONE; > +}) > + > (include "mmx.md") > (include "sse.md") > (include "sync.md") > --- gcc/doc/extend.texi.jj 2022-07-26 10:32:23.642272293 +0200 > +++ gcc/doc/extend.texi 2022-08-11 21:57:06.727147454 +0200 > @@ -13001,6 +13001,7 @@ is called and the @var{flag} argument pa > @findex __builtin_isless > @findex __builtin_islessequal > @findex __builtin_islessgreater > +@findex __builtin_issignaling > @findex __builtin_isunordered > @findex __builtin_object_size > @findex __builtin_powi > @@ -14489,6 +14490,14 @@ Similar to @code{__builtin_nans}, except > @code{_Float@var{n}x}. > @end deftypefn > > +@deftypefn {Built-in Function} int __builtin_issignaling (...) > +Return non-zero if the argument is a signaling NaN and zero otherwise. > +Note while the parameter list is an > +ellipsis, this function only accepts exactly one floating-point > +argument. GCC treats this parameter as type-generic, which means it > +does not do default promotion from float to double. > +@end deftypefn > + > @deftypefn {Built-in Function} int __builtin_ffs (int x) > Returns one plus the index of the least significant 1-bit of @var{x}, or > if @var{x} is zero, returns zero. > --- gcc/doc/md.texi.jj 2022-06-27 11:18:02.610059335 +0200 > +++ gcc/doc/md.texi 2022-08-11 22:00:11.470708501 +0200 > @@ -6184,6 +6184,10 @@ floating-point mode. > > This pattern is not allowed to @code{FAIL}. > > +@cindex @code{issignaling@var{m}2} instruction pattern > +@item @samp{issignaling@var{m}2} > +Set operand 0 to 1 if operand 1 is a signaling NaN and to 0 otherwise. > + > @cindex @code{cadd90@var{m}3} instruction pattern > @item @samp{cadd90@var{m}3} > Perform vector add and subtract on even/odd number pairs. The operation being > --- gcc/c-family/c-common.cc.jj 2022-08-10 09:06:51.214236184 +0200 > +++ gcc/c-family/c-common.cc 2022-08-11 12:19:06.471714333 +0200 > @@ -6294,6 +6294,7 @@ check_builtin_function_arguments (locati > case BUILT_IN_ISINF_SIGN: > case BUILT_IN_ISNAN: > case BUILT_IN_ISNORMAL: > + case BUILT_IN_ISSIGNALING: > case BUILT_IN_SIGNBIT: > if (builtin_function_validate_nargs (loc, fndecl, nargs, 1)) > { > --- gcc/c/c-typeck.cc.jj 2022-08-10 09:06:51.331234661 +0200 > +++ gcc/c/c-typeck.cc 2022-08-11 12:16:20.586995677 +0200 > @@ -3546,6 +3546,7 @@ convert_arguments (location_t loc, vec case BUILT_IN_ISINF_SIGN: > case BUILT_IN_ISNAN: > case BUILT_IN_ISNORMAL: > + case BUILT_IN_ISSIGNALING: > case BUILT_IN_FPCLASSIFY: > type_generic_remove_excess_precision = true; > break; > --- gcc/fortran/f95-lang.cc.jj 2022-06-03 11:20:13.207071074 +0200 > +++ gcc/fortran/f95-lang.cc 2022-08-11 12:20:57.243190944 +0200 > @@ -1013,6 +1013,8 @@ gfc_init_builtin_functions (void) > "__builtin_isnan", ATTR_CONST_NOTHROW_LEAF_LIST); > gfc_define_builtin ("__builtin_isnormal", ftype, BUILT_IN_ISNORMAL, > "__builtin_isnormal", ATTR_CONST_NOTHROW_LEAF_LIST); > + gfc_define_builtin ("__builtin_issignaling", ftype, BUILT_IN_ISSIGNALING, > + "__builtin_issignaling", ATTR_CONST_NOTHROW_LEAF_LIST); > gfc_define_builtin ("__builtin_signbit", ftype, BUILT_IN_SIGNBIT, > "__builtin_signbit", ATTR_CONST_NOTHROW_LEAF_LIST); > > --- gcc/testsuite/gcc.dg/torture/builtin-issignaling-1.c.jj 2022-08-11 21:20:48.790870018 +0200 > +++ gcc/testsuite/gcc.dg/torture/builtin-issignaling-1.c 2022-08-12 12:11:34.800702002 +0200 > @@ -0,0 +1,95 @@ > +/* { dg-do run } */ > +/* { dg-add-options ieee } */ > +/* { dg-additional-options "-fsignaling-nans" } */ > +/* Workaround for PR57484 on ia32: */ > +/* { dg-additional-options "-msse2 -mfpmath=sse" { target { ia32 && sse2_runtime } } } */ > + > +int > +f1 (void) > +{ > + return __builtin_issignaling (__builtin_nansf ("")); > +} > + > +int > +f2 (void) > +{ > + return __builtin_issignaling (__builtin_nan ("")); > +} > + > +int > +f3 (void) > +{ > + return __builtin_issignaling (0.0L); > +} > + > +int > +f4 (float x) > +{ > + return __builtin_issignaling (x); > +} > + > +int > +f5 (double x) > +{ > + return __builtin_issignaling (x); > +} > + > +int > +f6 (long double x) > +{ > + return __builtin_issignaling (x); > +} > + > +#ifdef __SIZEOF_FLOAT128__ > +int > +f7 (_Float128 x) > +{ > + return __builtin_issignaling (x); > +} > +#endif > + > +float x; > +double y; > +long double z; > +#ifdef __SIZEOF_FLOAT128__ > +_Float128 w; > +#endif > + > +int > +main () > +{ > + if (!f1 () || f2 () || f3 ()) > + __builtin_abort (); > + asm volatile ("" : : : "memory"); > + if (f4 (x) || !f4 (__builtin_nansf ("0x123")) || f4 (42.0f) || f4 (__builtin_nanf ("0x234")) > + || f4 (__builtin_inff ()) || f4 (-__builtin_inff ()) || f4 (-42.0f) || f4 (-0.0f) || f4 (0.0f)) > + __builtin_abort (); > + x = __builtin_nansf (""); > + asm volatile ("" : : : "memory"); > + if (!f4 (x)) > + __builtin_abort (); > + if (f5 (y) || !f5 (__builtin_nans ("0x123")) || f5 (42.0) || f5 (__builtin_nan ("0x234")) > + || f5 (__builtin_inf ()) || f5 (-__builtin_inf ()) || f5 (-42.0) || f5 (-0.0) || f5 (0.0)) > + __builtin_abort (); > + y = __builtin_nans (""); > + asm volatile ("" : : : "memory"); > + if (!f5 (y)) > + __builtin_abort (); > + if (f6 (z) || !f6 (__builtin_nansl ("0x123")) || f6 (42.0L) || f6 (__builtin_nanl ("0x234")) > + || f6 (__builtin_infl ()) || f6 (-__builtin_infl ()) || f6 (-42.0L) || f6 (-0.0L) || f6 (0.0L)) > + __builtin_abort (); > + z = __builtin_nansl (""); > + asm volatile ("" : : : "memory"); > + if (!f6 (z)) > + __builtin_abort (); > +#ifdef __SIZEOF_FLOAT128__ > + if (f7 (w) || !f7 (__builtin_nansf128 ("0x123")) || f7 (42.0Q) || f7 (__builtin_nanf128 ("0x234")) > + || f7 (__builtin_inff128 ()) || f7 (-__builtin_inff128 ()) || f7 (-42.0Q) || f7 (-0.0Q) || f7 (0.0Q)) > + __builtin_abort (); > + w = __builtin_nansf128 (""); > + asm volatile ("" : : : "memory"); > + if (!f7 (w)) > + __builtin_abort (); > +#endif > + return 0; > +} > --- gcc/testsuite/gcc.dg/torture/builtin-issignaling-2.c.jj 2022-08-11 21:21:34.673265283 +0200 > +++ gcc/testsuite/gcc.dg/torture/builtin-issignaling-2.c 2022-08-12 12:13:10.349427951 +0200 > @@ -0,0 +1,73 @@ > +/* { dg-do run } */ > +/* { dg-require-effective-target dfp } */ > +/* { dg-additional-options "-fsignaling-nans" } */ > + > +int > +f1 (void) > +{ > + return __builtin_issignaling (__builtin_nansd32 ("")); > +} > + > +int > +f2 (void) > +{ > + return __builtin_issignaling (__builtin_nand64 ("")); > +} > + > +int > +f3 (void) > +{ > + return __builtin_issignaling (0.0DD); > +} > + > +int > +f4 (_Decimal32 x) > +{ > + return __builtin_issignaling (x); > +} > + > +int > +f5 (_Decimal64 x) > +{ > + return __builtin_issignaling (x); > +} > + > +int > +f6 (_Decimal128 x) > +{ > + return __builtin_issignaling (x); > +} > + > +_Decimal32 x; > +_Decimal64 y; > +_Decimal128 z; > + > +int > +main () > +{ > + if (!f1 () || f2 () || f3 ()) > + __builtin_abort (); > + asm volatile ("" : : : "memory"); > + if (f4 (x) || !f4 (__builtin_nansd32 ("0x123")) || f4 (42.0DF) || f4 (__builtin_nand32 ("0x234")) > + || f4 (__builtin_infd32 ()) || f4 (-__builtin_infd32 ()) || f4 (-42.0DF) || f4 (-0.0DF) || f4 (0.0DF)) > + __builtin_abort (); > + x = __builtin_nansd32 (""); > + asm volatile ("" : : : "memory"); > + if (!f4 (x)) > + __builtin_abort (); > + if (f5 (y) || !f5 (__builtin_nansd64 ("0x123")) || f5 (42.0DD) || f5 (__builtin_nand64 ("0x234")) > + || f5 (__builtin_infd64 ()) || f5 (-__builtin_infd64 ()) || f5 (-42.0DD) || f5 (-0.0DD) || f5 (0.0DD)) > + __builtin_abort (); > + y = __builtin_nansd64 (""); > + asm volatile ("" : : : "memory"); > + if (!f5 (y)) > + __builtin_abort (); > + if (f6 (z) || !f6 (__builtin_nansd128 ("0x123")) || f6 (42.0DL) || f6 (__builtin_nand128 ("0x234")) > + || f6 (__builtin_infd128 ()) || f6 (-__builtin_infd128 ()) || f6 (-42.0DL) || f6 (-0.0DL) || f6 (0.0DL)) > + __builtin_abort (); > + z = __builtin_nansd128 (""); > + asm volatile ("" : : : "memory"); > + if (!f6 (z)) > + __builtin_abort (); > + return 0; > +} > --- gcc/testsuite/gcc.target/i386/builtin-issignaling-1.c.jj 2022-08-12 12:19:19.723502685 +0200 > +++ gcc/testsuite/gcc.target/i386/builtin-issignaling-1.c 2022-08-12 12:40:51.499218604 +0200 > @@ -0,0 +1,80 @@ > +/* { dg-do run } */ > +/* { dg-options "-O2 -fsignaling-nans" } */ > + > +#if __LDBL_MANT_DIG__ == 64 > +union U { struct { unsigned long long m; unsigned short e; } p; long double l; }; > +union U zero = { { 0, 0 } }; > +union U mzero = { { 0, 0x8000 } }; > +union U denorm = { { 42, 0 } }; > +union U mdenorm = { { 42, 0x8000 } }; > +union U pseudodenorm = { { 0x8000000000000000ULL, 0 } }; > +union U mpseudodenorm = { { 0x8000000000000000ULL, 0x8000 } }; > +union U pseudodenorm1 = { { 0x8000000000000042ULL, 0 } }; > +union U mpseudodenorm1 = { { 0x8000000000000042ULL, 0x8000 } }; > +union U pseudoinf = { { 0, 0x7fff } }; > +union U mpseudoinf = { { 0, 0xffff } }; > +union U pseudonan = { { 42, 0x7fff } }; > +union U mpseudonan = { { 42, 0xffff } }; > +union U pseudonan1 = { { 0x4000000000000000ULL, 0x7fff } }; > +union U mpseudonan1 = { { 0x4000000000000000ULL, 0xffff } }; > +union U pseudonan2 = { { 0x4000000000000042ULL, 0x7fff } }; > +union U mpseudonan2 = { { 0x4000000000000042ULL, 0xffff } }; > +union U inf = { { 0x8000000000000000ULL, 0x7fff } }; > +union U minf = { { 0x8000000000000000ULL, 0xffff } }; > +union U snan = { { 0x8000000000000042ULL, 0x7fff } }; > +union U msnan = { { 0x8000000000000042ULL, 0xffff } }; > +union U indefinite = { { 0xc000000000000000ULL, 0x7fff } }; > +union U mindefinite = { { 0xc000000000000000ULL, 0xffff } }; > +union U qnan = { { 0xc000000000000042ULL, 0x7fff } }; > +union U mqnan = { { 0xc000000000000042ULL, 0xffff } }; > +union U unnormal = { { 0, 0x42 } }; > +union U munnormal = { { 0, 0x8042 } }; > +union U unnormal1 = { { 42, 0x42 } }; > +union U munnormal1 = { { 42, 0x8042 } }; > +union U normal = { { 0x8000000000000000ULL, 0x42 } }; > +union U mnormal = { { 0x8000000000000000ULL, 0x8042 } }; > +union U normal1 = { { 0x8000000000000042ULL, 0x42 } }; > +union U mnormal1 = { { 0x8000000000000042ULL, 0x8042 } }; > +#endif > + > +int > +main () > +{ > +#if __LDBL_MANT_DIG__ == 64 > + asm volatile ("" : : : "memory"); > + if (__builtin_issignaling (zero.l) > + || __builtin_issignaling (mzero.l) > + || __builtin_issignaling (denorm.l) > + || __builtin_issignaling (mdenorm.l) > + || __builtin_issignaling (pseudodenorm.l) > + || __builtin_issignaling (mpseudodenorm.l) > + || __builtin_issignaling (pseudodenorm1.l) > + || __builtin_issignaling (mpseudodenorm1.l) > + || !__builtin_issignaling (pseudoinf.l) > + || !__builtin_issignaling (mpseudoinf.l) > + || !__builtin_issignaling (pseudonan.l) > + || !__builtin_issignaling (mpseudonan.l) > + || !__builtin_issignaling (pseudonan1.l) > + || !__builtin_issignaling (mpseudonan1.l) > + || !__builtin_issignaling (pseudonan2.l) > + || !__builtin_issignaling (mpseudonan2.l) > + || __builtin_issignaling (inf.l) > + || __builtin_issignaling (minf.l) > + || !__builtin_issignaling (snan.l) > + || !__builtin_issignaling (msnan.l) > + || __builtin_issignaling (indefinite.l) > + || __builtin_issignaling (mindefinite.l) > + || __builtin_issignaling (qnan.l) > + || __builtin_issignaling (mqnan.l) > + || !__builtin_issignaling (unnormal.l) > + || !__builtin_issignaling (munnormal.l) > + || !__builtin_issignaling (unnormal1.l) > + || !__builtin_issignaling (munnormal1.l) > + || __builtin_issignaling (normal.l) > + || __builtin_issignaling (mnormal.l) > + || __builtin_issignaling (normal1.l) > + || __builtin_issignaling (mnormal1.l)) > + __builtin_abort (); > +#endif > + return 0; > +} > > Jakub > > -- Richard Biener SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg, Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman; HRB 36809 (AG Nuernberg)