public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Implement __builtin_issignaling
@ 2022-08-15 10:12 Jakub Jelinek
  2022-08-15 11:24 ` Richard Biener
                   ` (3 more replies)
  0 siblings, 4 replies; 19+ messages in thread
From: Jakub Jelinek @ 2022-08-15 10:12 UTC (permalink / raw)
  To: Richard Biener, Joseph S. Myers, Jeff Law; +Cc: gcc-patches, FX

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?

2022-08-15  Jakub Jelinek  <jakub@redhat.com>

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 (issignaling<mode>2): 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<l
 	  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


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-15 10:12 [PATCH] Implement __builtin_issignaling Jakub Jelinek
@ 2022-08-15 11:24 ` Richard Biener
  2022-08-15 11:47   ` Jakub Jelinek
  2022-08-15 16:14 ` [PATCH] " FX
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 19+ messages in thread
From: Richard Biener @ 2022-08-15 11:24 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Joseph S. Myers, Jeff Law, gcc-patches, FX

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 <tgmath.h>

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  <jakub@redhat.com>
> 
> 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 (issignaling<mode>2): 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<l
>  	  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 <rguenther@suse.de>
SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
HRB 36809 (AG Nuernberg)

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-15 11:24 ` Richard Biener
@ 2022-08-15 11:47   ` Jakub Jelinek
  2022-08-15 12:04     ` Andreas Schwab
  2022-08-15 12:07     ` Richard Biener
  0 siblings, 2 replies; 19+ messages in thread
From: Jakub Jelinek @ 2022-08-15 11:47 UTC (permalink / raw)
  To: Richard Biener; +Cc: Joseph S. Myers, Jeff Law, gcc-patches, FX

On Mon, Aug 15, 2022 at 11:24:14AM +0000, Richard Biener wrote:
> 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).

I have actually tried to make the builtin working even with
-fno-signaling-nans (i.e. the default).
That is why the folding is done only if the argument is REAL_CST
or if !tree_expr_maybe_nan_p (arg).
At one point I was doing the folding when
tree_expr_signaling_nan_p (arg) (to true) or
!tree_expr_maybe_signaling_nan_p (arg) (to false) and in that
case indeed -fsignaling-nans was a requirement.
-fsignaling-nans is used in the tests nevertheless because the
tests really do care about sNaNs, so I've turned on the option
that says they should be honored.

> Generally it looks OK - what does it do to size optimized code?

The problem is that except for the glibc __issignaling{f,,l,f128}
entrypoints, other C libraries don't implement it, so there is nothing to
fallback to (unless we want to also implement it in libgcc.a).

For float/double, it is relatively short:
        movd    %xmm0, %eax
        xorl    $4194304, %eax
        andl    $2147483647, %eax
        cmpl    $2143289344, %eax
        seta    %al
        movzbl  %al, %eax
which e.g. for if (__builtin_issignaling (arg)) could be even simplified
further by just doing ja or jna, resp.
        movabsq $9221120237041090560, %rdx
        movq    %xmm0, %rax
        btcq    $51, %rax
        btrq    $63, %rax
        cmpq    %rax, %rdx
        setb    %al
        movzbl  %al, %eax
For long double (especially Intel) / _Float128 it is larger (26 insns for XFmode,
15 for _Float128), sure.

> glibc 2.31 seems to silently accept
> 
> #include <tgmath.h>
> 
> int foo(_Complex double x)
> {
>   return issignaling (x);
> }

That seems like a glibc bug/weird feature in the __MATH_TG macro
or _Generic.
When compiled with C++ it is rejected.

	Jakub


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-15 11:47   ` Jakub Jelinek
@ 2022-08-15 12:04     ` Andreas Schwab
  2022-08-15 12:07     ` Richard Biener
  1 sibling, 0 replies; 19+ messages in thread
From: Andreas Schwab @ 2022-08-15 12:04 UTC (permalink / raw)
  To: Jakub Jelinek via Gcc-patches
  Cc: Richard Biener, Jakub Jelinek, FX, Joseph S. Myers

On Aug 15 2022, Jakub Jelinek via Gcc-patches wrote:

> That seems like a glibc bug/weird feature in the __MATH_TG macro
> or _Generic.

__MATH_TG is only defined for real floating types, since all of the type
generic macros in <math.h> only accept real floating types.

-- 
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-15 11:47   ` Jakub Jelinek
  2022-08-15 12:04     ` Andreas Schwab
@ 2022-08-15 12:07     ` Richard Biener
  2022-08-15 13:06       ` Jakub Jelinek
  1 sibling, 1 reply; 19+ messages in thread
From: Richard Biener @ 2022-08-15 12:07 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Joseph S. Myers, Jeff Law, gcc-patches, FX

On Mon, 15 Aug 2022, Jakub Jelinek wrote:

> On Mon, Aug 15, 2022 at 11:24:14AM +0000, Richard Biener wrote:
> > 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).
> 
> I have actually tried to make the builtin working even with
> -fno-signaling-nans (i.e. the default).
> That is why the folding is done only if the argument is REAL_CST
> or if !tree_expr_maybe_nan_p (arg).
> At one point I was doing the folding when
> tree_expr_signaling_nan_p (arg) (to true) or
> !tree_expr_maybe_signaling_nan_p (arg) (to false) and in that
> case indeed -fsignaling-nans was a requirement.
> -fsignaling-nans is used in the tests nevertheless because the
> tests really do care about sNaNs, so I've turned on the option
> that says they should be honored.

Ah, I misread

+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;

which doesn't use HONOR_SNANS but still HONOR_NANS and thus
-ffinite-math-only.  You possibly want MODE_HAS_NANS instead here?

> > Generally it looks OK - what does it do to size optimized code?
> 
> The problem is that except for the glibc __issignaling{f,,l,f128}
> entrypoints, other C libraries don't implement it, so there is nothing to
> fallback to (unless we want to also implement it in libgcc.a).
> 
> For float/double, it is relatively short:
>         movd    %xmm0, %eax
>         xorl    $4194304, %eax
>         andl    $2147483647, %eax
>         cmpl    $2143289344, %eax
>         seta    %al
>         movzbl  %al, %eax
> which e.g. for if (__builtin_issignaling (arg)) could be even simplified
> further by just doing ja or jna, resp.
>         movabsq $9221120237041090560, %rdx
>         movq    %xmm0, %rax
>         btcq    $51, %rax
>         btrq    $63, %rax
>         cmpq    %rax, %rdx
>         setb    %al
>         movzbl  %al, %eax
> For long double (especially Intel) / _Float128 it is larger (26 insns for XFmode,
> 15 for _Float128), sure.
> 
> > glibc 2.31 seems to silently accept
> > 
> > #include <tgmath.h>
> > 
> > int foo(_Complex double x)
> > {
> >   return issignaling (x);
> > }
> 
> That seems like a glibc bug/weird feature in the __MATH_TG macro
> or _Generic.
> When compiled with C++ it is rejected.

So what about __builtin_issignaling then?  Do we want to silently
ignore errors there?

Richard.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-15 12:07     ` Richard Biener
@ 2022-08-15 13:06       ` Jakub Jelinek
  2022-08-16 11:33         ` Jakub Jelinek
  0 siblings, 1 reply; 19+ messages in thread
From: Jakub Jelinek @ 2022-08-15 13:06 UTC (permalink / raw)
  To: Richard Biener; +Cc: Joseph S. Myers, Jeff Law, gcc-patches, FX

On Mon, Aug 15, 2022 at 12:07:38PM +0000, Richard Biener wrote:
> Ah, I misread
> 
> +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;

I think I can expand on the comment why HONOR_NANS instead of HONOR_SNANS
and also add comment to the folding case.

> which doesn't use HONOR_SNANS but still HONOR_NANS and thus
> -ffinite-math-only.  You possibly want MODE_HAS_NANS instead here?

But I'm not sure we want this.  With -ffast-math/-ffinite-math-only etc.,
__builtin_isnan or __builtin_fpclassify for NaNs/Infs will just return 0,
so it would be strange if __builtin_issignaling didn't.
People usually only call __builtin_issignaling when they know they
have a NaN, so they want it guarded with
__builtin_isnan/__builtin_fpclassify or say <=> unordered.

> > That seems like a glibc bug/weird feature in the __MATH_TG macro
> > or _Generic.
> > When compiled with C++ it is rejected.
> 
> So what about __builtin_issignaling then?  Do we want to silently
> ignore errors there?

I think we should just restrict it to the scalar floating point types.
After all, other typegeneric builtins that are or can be used similarly
do the same thing.

	Jakub


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-15 10:12 [PATCH] Implement __builtin_issignaling Jakub Jelinek
  2022-08-15 11:24 ` Richard Biener
@ 2022-08-15 16:14 ` FX
  2022-08-15 16:23   ` Jakub Jelinek
  2022-08-15 20:52 ` Uros Bizjak
  2022-08-25 19:23 ` Michael Meissner
  3 siblings, 1 reply; 19+ messages in thread
From: FX @ 2022-08-15 16:14 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, Joseph S. Myers, Jeff Law, gcc-patches

Hi Jakub,

Thank you for taking this on (this patch and the Fortran ones), it is really appreciated and will improve gfortran's IEEE conformance.

- "Implement __builtin_issignaling”: is approved for the tiny Fortran part
- "libgfortran: Use __builtin_issignaling in libgfortran”: was approved by Thomas
- I will have to look at the next two patches (ieee_value and ieee_class) in the next few days. I think we can make adjustments to the runtime library as well.

Related: there are several tests in our testsuite that have { dg-require-effective-target issignaling }
This is checked by check_effective_target_issignaling in gcc/testsuite/lib/target-supports.exp, and probes the support for the issignaling macro in <math.h>. I think it would make sense to adjust those tests to run on a wider range of targets, using the new built-in.

FX

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-15 16:14 ` [PATCH] " FX
@ 2022-08-15 16:23   ` Jakub Jelinek
  0 siblings, 0 replies; 19+ messages in thread
From: Jakub Jelinek @ 2022-08-15 16:23 UTC (permalink / raw)
  To: FX; +Cc: Richard Biener, Joseph S. Myers, Jeff Law, gcc-patches

On Mon, Aug 15, 2022 at 06:14:22PM +0200, FX wrote:
> Thank you for taking this on (this patch and the Fortran ones), it is really appreciated and will improve gfortran's IEEE conformance.
> 
> - "Implement __builtin_issignaling”: is approved for the tiny Fortran part
> - "libgfortran: Use __builtin_issignaling in libgfortran”: was approved by Thomas

Thanks.

> - I will have to look at the next two patches (ieee_value and ieee_class) in the next few days. I think we can make adjustments to the runtime library as well.

I think we can, but we need to make it in an ABI compatible way until
libgfortran bumps soname.
That is why I haven't used _gfortrani_whatever as name etc.
> 
> Related: there are several tests in our testsuite that have { dg-require-effective-target issignaling }
> This is checked by check_effective_target_issignaling in gcc/testsuite/lib/target-supports.exp, and probes the support for the issignaling macro in <math.h>. I think it would make sense to adjust those tests to run on a wider range of targets, using the new built-in.

We could replace it with some other effective target that would test if the
target supports sNaNs (or perhaps just NaNs might be enough).

	Jakub


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-15 10:12 [PATCH] Implement __builtin_issignaling Jakub Jelinek
  2022-08-15 11:24 ` Richard Biener
  2022-08-15 16:14 ` [PATCH] " FX
@ 2022-08-15 20:52 ` Uros Bizjak
  2022-08-25 19:23 ` Michael Meissner
  3 siblings, 0 replies; 19+ messages in thread
From: Uros Bizjak @ 2022-08-15 20:52 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, Joseph S. Myers, Jeff Law, gcc-patches, FX

On Mon, Aug 15, 2022 at 12:12 PM Jakub Jelinek via Gcc-patches
<gcc-patches@gcc.gnu.org> 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?
>
> 2022-08-15  Jakub Jelinek  <jakub@redhat.com>
>
> 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 (issignaling<mode>2): 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.

OK for x86 part.

Thanks,
Uros.

>
> --- 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<l
>           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
>

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-15 13:06       ` Jakub Jelinek
@ 2022-08-16 11:33         ` Jakub Jelinek
  2022-08-16 11:41           ` Richard Biener
  0 siblings, 1 reply; 19+ messages in thread
From: Jakub Jelinek @ 2022-08-16 11:33 UTC (permalink / raw)
  To: Richard Biener, gcc-patches, FX, Joseph S. Myers

On Mon, Aug 15, 2022 at 03:06:16PM +0200, Jakub Jelinek via Gcc-patches wrote:
> On Mon, Aug 15, 2022 at 12:07:38PM +0000, Richard Biener wrote:
> > Ah, I misread
> > 
> > +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;
> 
> I think I can expand on the comment why HONOR_NANS instead of HONOR_SNANS
> and also add comment to the folding case.

So what about like in this incremental patch:

--- gcc/builtins.cc.jj	2022-08-16 13:23:04.220103861 +0200
+++ gcc/builtins.cc	2022-08-16 13:32:03.411257574 +0200
@@ -2765,7 +2765,13 @@ expand_builtin_issignaling (tree exp, rt
   /* Expand the argument yielding a RTX expression. */
   rtx temp = expand_normal (arg);
 
-  /* If mode doesn't support NaN, always return 0.  */
+  /* If mode doesn't support NaN, always return 0.
+     Don't use !HONOR_SNANS (fmode) here, so there is some possibility of
+     __builtin_issignaling working without -fsignaling-nans.  Especially
+     when -fno-signaling-nans is the default.
+     On the other side, MODE_HAS_NANS (fmode) is unnecessary, with
+     -ffinite-math-only even __builtin_isnan or __builtin_fpclassify
+     fold to 0 or non-NaN/Inf classification.  */
   if (!HONOR_NANS (fmode))
     {
       emit_move_insn (target, const0_rtx);
@@ -9259,6 +9265,12 @@ fold_builtin_classify (location_t loc, t
       return fold_build2_loc (loc, UNORDERED_EXPR, type, arg, arg);
 
     case BUILT_IN_ISSIGNALING:
+      /* Folding to true for REAL_CST is done in fold_const_call_ss.
+	 Don't use tree_expr_signaling_nan_p (arg) -> integer_one_node
+	 and !tree_expr_maybe_signaling_nan_p (arg) -> integer_zero_node
+	 here, so there is some possibility of __builtin_issignaling working
+	 without -fsignaling-nans.  Especially when -fno-signaling-nans is
+	 the default.  */
       if (!tree_expr_maybe_nan_p (arg))
 	return omit_one_operand_loc (loc, type, integer_zero_node, arg);
       return NULL_TREE;

> > > That seems like a glibc bug/weird feature in the __MATH_TG macro
> > > or _Generic.
> > > When compiled with C++ it is rejected.
> > 
> > So what about __builtin_issignaling then?  Do we want to silently
> > ignore errors there?
> 
> I think we should just restrict it to the scalar floating point types.
> After all, other typegeneric builtins that are or can be used similarly
> do the same thing.

Note, that is what the patch does currently (rejecting _Complex
{float,double,long double} etc. arguments).

	Jakub


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-16 11:33         ` Jakub Jelinek
@ 2022-08-16 11:41           ` Richard Biener
  2022-08-16 11:57             ` Jakub Jelinek
  2022-08-23  7:58             ` Patch ping (Re: [PATCH] Implement __builtin_issignaling) Jakub Jelinek
  0 siblings, 2 replies; 19+ messages in thread
From: Richard Biener @ 2022-08-16 11:41 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches, FX, Joseph S. Myers

On Tue, 16 Aug 2022, Jakub Jelinek wrote:

> On Mon, Aug 15, 2022 at 03:06:16PM +0200, Jakub Jelinek via Gcc-patches wrote:
> > On Mon, Aug 15, 2022 at 12:07:38PM +0000, Richard Biener wrote:
> > > Ah, I misread
> > > 
> > > +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;
> > 
> > I think I can expand on the comment why HONOR_NANS instead of HONOR_SNANS
> > and also add comment to the folding case.
> 
> So what about like in this incremental patch:
> 
> --- gcc/builtins.cc.jj	2022-08-16 13:23:04.220103861 +0200
> +++ gcc/builtins.cc	2022-08-16 13:32:03.411257574 +0200
> @@ -2765,7 +2765,13 @@ expand_builtin_issignaling (tree exp, rt
>    /* Expand the argument yielding a RTX expression. */
>    rtx temp = expand_normal (arg);
>  
> -  /* If mode doesn't support NaN, always return 0.  */
> +  /* If mode doesn't support NaN, always return 0.
> +     Don't use !HONOR_SNANS (fmode) here, so there is some possibility of
> +     __builtin_issignaling working without -fsignaling-nans.  Especially
> +     when -fno-signaling-nans is the default.
> +     On the other side, MODE_HAS_NANS (fmode) is unnecessary, with
> +     -ffinite-math-only even __builtin_isnan or __builtin_fpclassify
> +     fold to 0 or non-NaN/Inf classification.  */
>    if (!HONOR_NANS (fmode))
>      {
>        emit_move_insn (target, const0_rtx);
> @@ -9259,6 +9265,12 @@ fold_builtin_classify (location_t loc, t
>        return fold_build2_loc (loc, UNORDERED_EXPR, type, arg, arg);
>  
>      case BUILT_IN_ISSIGNALING:
> +      /* Folding to true for REAL_CST is done in fold_const_call_ss.
> +	 Don't use tree_expr_signaling_nan_p (arg) -> integer_one_node
> +	 and !tree_expr_maybe_signaling_nan_p (arg) -> integer_zero_node
> +	 here, so there is some possibility of __builtin_issignaling working
> +	 without -fsignaling-nans.  Especially when -fno-signaling-nans is
> +	 the default.  */
>        if (!tree_expr_maybe_nan_p (arg))
>  	return omit_one_operand_loc (loc, type, integer_zero_node, arg);
>        return NULL_TREE;

Can you also amend the extend.texi documentation?  I think the
behavior will be special enough to worth mentioning it (I don't see
any of -ffinite-math-only effect on isnan/isinf mentioned though).

I'm OK with the rest of the patch if Joseph doesn't have comments
on the actual issignaling lowerings (which I didn't review for
correctness due to lack of knowledge).

> > > > That seems like a glibc bug/weird feature in the __MATH_TG macro
> > > > or _Generic.
> > > > When compiled with C++ it is rejected.
> > > 
> > > So what about __builtin_issignaling then?  Do we want to silently
> > > ignore errors there?
> > 
> > I think we should just restrict it to the scalar floating point types.
> > After all, other typegeneric builtins that are or can be used similarly
> > do the same thing.
> 
> Note, that is what the patch does currently (rejecting _Complex
> {float,double,long double} etc. arguments).

I see.

Richard.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-16 11:41           ` Richard Biener
@ 2022-08-16 11:57             ` Jakub Jelinek
  2022-08-16 12:04               ` Richard Biener
  2022-08-23  7:58             ` Patch ping (Re: [PATCH] Implement __builtin_issignaling) Jakub Jelinek
  1 sibling, 1 reply; 19+ messages in thread
From: Jakub Jelinek @ 2022-08-16 11:57 UTC (permalink / raw)
  To: Richard Biener; +Cc: gcc-patches, FX, Joseph S. Myers

On Tue, Aug 16, 2022 at 11:41:06AM +0000, Richard Biener wrote:
> Can you also amend the extend.texi documentation?  I think the
> behavior will be special enough to worth mentioning it (I don't see
> any of -ffinite-math-only effect on isnan/isinf mentioned though).

Like this?

--- gcc/doc/extend.texi.jj	2022-08-16 13:23:04.227103773 +0200
+++ gcc/doc/extend.texi	2022-08-16 13:56:01.250769807 +0200
@@ -13557,6 +13557,8 @@ In the same fashion, GCC provides @code{
 @code{isinf_sign}, @code{isnormal} and @code{signbit} built-ins used with
 @code{__builtin_} prefixed.  The @code{isinf} and @code{isnan}
 built-in functions appear both with and without the @code{__builtin_} prefix.
+With @code{-ffinite-math-only} option the @code{isinf} and @code{isnan}
+built-in functions will always return 0.
 
 GCC provides built-in versions of the ISO C99 floating-point rounding and
 exceptions handling functions @code{fegetround}, @code{feclearexcept} and
@@ -14496,6 +14498,12 @@ 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.
+This built-in function can work even without the non-default
+@code{-fsignaling-nans} option, although if a signaling NaN is computed,
+stored or passed as argument to some function other than this built-in
+in the current translation unit, it is safer to use @code{-fsignaling-nans}.
+With @code{-ffinite-math-only} option this built-in function will always
+return 0.
 @end deftypefn
 
 @deftypefn {Built-in Function} int __builtin_ffs (int x)

	Jakub


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-16 11:57             ` Jakub Jelinek
@ 2022-08-16 12:04               ` Richard Biener
  0 siblings, 0 replies; 19+ messages in thread
From: Richard Biener @ 2022-08-16 12:04 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches, FX, Joseph S. Myers

On Tue, 16 Aug 2022, Jakub Jelinek wrote:

> On Tue, Aug 16, 2022 at 11:41:06AM +0000, Richard Biener wrote:
> > Can you also amend the extend.texi documentation?  I think the
> > behavior will be special enough to worth mentioning it (I don't see
> > any of -ffinite-math-only effect on isnan/isinf mentioned though).
> 
> Like this?

Yes.

Thanks,
Richard.

> --- gcc/doc/extend.texi.jj	2022-08-16 13:23:04.227103773 +0200
> +++ gcc/doc/extend.texi	2022-08-16 13:56:01.250769807 +0200
> @@ -13557,6 +13557,8 @@ In the same fashion, GCC provides @code{
>  @code{isinf_sign}, @code{isnormal} and @code{signbit} built-ins used with
>  @code{__builtin_} prefixed.  The @code{isinf} and @code{isnan}
>  built-in functions appear both with and without the @code{__builtin_} prefix.
> +With @code{-ffinite-math-only} option the @code{isinf} and @code{isnan}
> +built-in functions will always return 0.
>  
>  GCC provides built-in versions of the ISO C99 floating-point rounding and
>  exceptions handling functions @code{fegetround}, @code{feclearexcept} and
> @@ -14496,6 +14498,12 @@ 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.
> +This built-in function can work even without the non-default
> +@code{-fsignaling-nans} option, although if a signaling NaN is computed,
> +stored or passed as argument to some function other than this built-in
> +in the current translation unit, it is safer to use @code{-fsignaling-nans}.
> +With @code{-ffinite-math-only} option this built-in function will always
> +return 0.
>  @end deftypefn
>  
>  @deftypefn {Built-in Function} int __builtin_ffs (int x)
> 
> 	Jakub
> 
> 

-- 
Richard Biener <rguenther@suse.de>
SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg,
Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman;
HRB 36809 (AG Nuernberg)

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Patch ping (Re: [PATCH] Implement __builtin_issignaling)
  2022-08-16 11:41           ` Richard Biener
  2022-08-16 11:57             ` Jakub Jelinek
@ 2022-08-23  7:58             ` Jakub Jelinek
  2022-08-23 18:23               ` Joseph Myers
  1 sibling, 1 reply; 19+ messages in thread
From: Jakub Jelinek @ 2022-08-23  7:58 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: gcc-patches

On Tue, Aug 16, 2022 at 11:41:06AM +0000, Richard Biener wrote:
> I'm OK with the rest of the patch if Joseph doesn't have comments
> on the actual issignaling lowerings (which I didn't review for
> correctness due to lack of knowledge).

I'd like to ping this patch.
Patch: https://gcc.gnu.org/pipermail/gcc-patches/2022-August/599697.html
with
https://gcc.gnu.org/pipermail/gcc-patches/2022-August/599794.html
https://gcc.gnu.org/pipermail/gcc-patches/2022-August/599800.html
incremental additions.
gcc/testsuite/gcc.dg/torture/builtin-issignaling-1.c's testcase f4-f7
functions (i.e. functions which just return __builtin_issignaling of
the argument which is float, double, long double and _Float128 on
x86_64-linux) with -O2 are in assembly:

	.p2align 4
	.globl	f4
	.type	f4, @function
f4:
.LFB0:
	.cfi_startproc
	movd	%xmm0, %eax
	xorl	$4194304, %eax
	andl	$2147483647, %eax
	cmpl	$2143289344, %eax
	seta	%al
	movzbl	%al, %eax
	ret
	.cfi_endproc
.LFE0:
	.size	f4, .-f4
	.p2align 4
	.globl	f5
	.type	f5, @function
f5:
.LFB1:
	.cfi_startproc
	movabsq	$9221120237041090560, %rdx
	movq	%xmm0, %rax
	btcq	$51, %rax
	btrq	$63, %rax
	cmpq	%rax, %rdx
	setb	%al
	movzbl	%al, %eax
	ret
	.cfi_endproc
.LFE1:
	.size	f5, .-f5
	.p2align 4
	.globl	f6
	.type	f6, @function
f6:
.LFB2:
	.cfi_startproc
	fldt	8(%rsp)
	fstpt	-24(%rsp)
	movl	-20(%rsp), %edx
	movl	-24(%rsp), %eax
	movl	%edx, %ecx
	negl	%eax
	orl	-24(%rsp), %eax
	notl	%edx
	xorl	$1073741824, %ecx
	shrl	$31, %eax
	orl	%ecx, %eax
	movzwl	-16(%rsp), %ecx
	cmpl	$-1073741824, %eax
	movl	%ecx, %esi
	seta	%al
	notl	%ecx
	andl	$32767, %esi
	movzbl	%al, %eax
	negl	%esi
	testw	$32767, %cx
	sete	%cl
	andl	%esi, %edx
	movzbl	%cl, %ecx
	shrl	$31, %edx
	andl	%ecx, %eax
	orl	%edx, %eax
	ret
	.cfi_endproc
.LFE2:
	.size	f6, .-f6
	.p2align 4
	.globl	f7
	.type	f7, @function
f7:
.LFB3:
	.cfi_startproc
	movaps	%xmm0, -24(%rsp)
	movq	-24(%rsp), %rsi
	movq	-16(%rsp), %rdi
	movq	%rsi, %rdx
	movq	%rdi, %rax
	negq	%rdx
	btcq	$47, %rax
	orq	%rsi, %rdx
	shrq	$63, %rdx
	orq	%rdx, %rax
	movabsq	$9223231299366420480, %rdx
	btrq	$63, %rax
	cmpq	%rax, %rdx
	setb	%al
	movzbl	%al, %eax
	ret
	.cfi_endproc
.LFE3:
	.size	f7, .-f7

Thanks

	Jakub


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: Patch ping (Re: [PATCH] Implement __builtin_issignaling)
  2022-08-23  7:58             ` Patch ping (Re: [PATCH] Implement __builtin_issignaling) Jakub Jelinek
@ 2022-08-23 18:23               ` Joseph Myers
  2022-08-23 19:21                 ` [PATCH] v2: Implement __builtin_issignaling Jakub Jelinek
  0 siblings, 1 reply; 19+ messages in thread
From: Joseph Myers @ 2022-08-23 18:23 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On Tue, 23 Aug 2022, Jakub Jelinek via Gcc-patches wrote:

> On Tue, Aug 16, 2022 at 11:41:06AM +0000, Richard Biener wrote:
> > I'm OK with the rest of the patch if Joseph doesn't have comments
> > on the actual issignaling lowerings (which I didn't review for
> > correctness due to lack of knowledge).
> 
> I'd like to ping this patch.
> Patch: https://gcc.gnu.org/pipermail/gcc-patches/2022-August/599697.html
> with
> https://gcc.gnu.org/pipermail/gcc-patches/2022-August/599794.html
> https://gcc.gnu.org/pipermail/gcc-patches/2022-August/599800.html
> incremental additions.

One testsuite comment:

> +#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 ();

__SIZEOF_FLOAT128__ is a target-specific macro for two targets.  I think 
it's better to model things on the existing _FloatN and _FloatNx tests, so 
have such a test for each such type, preferably with most of the test 
implementation type-generic (see e.g. floatn-builtin.h) and then with the 
individual tests including e.g.

/* { dg-add-options float128 } */
/* { dg-require-effective-target float128_runtime } */

to enable and test for the relevant support.  That would mean _Float128 
gets tested wherever supported - and also that the support is properly 
tested for _Float16, which doesn't look like it's covered by the tests in 
this patch at all at present.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH] v2: Implement __builtin_issignaling
  2022-08-23 18:23               ` Joseph Myers
@ 2022-08-23 19:21                 ` Jakub Jelinek
  0 siblings, 0 replies; 19+ messages in thread
From: Jakub Jelinek @ 2022-08-23 19:21 UTC (permalink / raw)
  To: Joseph Myers; +Cc: gcc-patches

On Tue, Aug 23, 2022 at 06:23:23PM +0000, Joseph Myers wrote:
> On Tue, 23 Aug 2022, Jakub Jelinek via Gcc-patches wrote:
> 
> > On Tue, Aug 16, 2022 at 11:41:06AM +0000, Richard Biener wrote:
> > > I'm OK with the rest of the patch if Joseph doesn't have comments
> > > on the actual issignaling lowerings (which I didn't review for
> > > correctness due to lack of knowledge).
> > 
> > I'd like to ping this patch.
> > Patch: https://gcc.gnu.org/pipermail/gcc-patches/2022-August/599697.html
> > with
> > https://gcc.gnu.org/pipermail/gcc-patches/2022-August/599794.html
> > https://gcc.gnu.org/pipermail/gcc-patches/2022-August/599800.html
> > incremental additions.
> 
> One testsuite comment:
> 
> > +#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 ();
> 
> __SIZEOF_FLOAT128__ is a target-specific macro for two targets.  I think 
> it's better to model things on the existing _FloatN and _FloatNx tests, so 
> have such a test for each such type, preferably with most of the test 
> implementation type-generic (see e.g. floatn-builtin.h) and then with the 
> individual tests including e.g.
> 
> /* { dg-add-options float128 } */
> /* { dg-require-effective-target float128_runtime } */
> 
> to enable and test for the relevant support.  That would mean _Float128 
> gets tested wherever supported - and also that the support is properly 
> tested for _Float16, which doesn't look like it's covered by the tests in 
> this patch at all at present.

Ok, implemented in the updated patch.
Tested with
make check-gcc RUNTESTFLAGS='--target_board=unix\{-m32,-m64\} dg-torture.exp=*builtin-issignaling* i386.exp=*builtin-issignaling*'
ok if it passes full bootstrap/regtest?

2022-08-23  Jakub Jelinek  <jakub@redhat.com>

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.
	(__builtin_isinf, __builtin_isnan): Clarify behavior with
	-ffinite-math-only.
	* doc/md.texi (issignaling<mode>2): 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.dg/torture/float16-builtin-issignaling-1.c: New test.
	* gcc.dg/torture/float32-builtin-issignaling-1.c: New test.
	* gcc.dg/torture/float32x-builtin-issignaling-1.c: New test.
	* gcc.dg/torture/float64-builtin-issignaling-1.c: New test.
	* gcc.dg/torture/float64x-builtin-issignaling-1.c: New test.
	* gcc.dg/torture/float128-builtin-issignaling-1.c: New test.
	* gcc.dg/torture/float128x-builtin-issignaling-1.c: New test.
	* gcc.target/i386/builtin-issignaling-1.c: New test.

--- gcc/builtins.def.jj	2022-08-17 16:58:47.666820640 +0200
+++ gcc/builtins.def	2022-08-23 20:59:35.286564045 +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-08-17 16:58:47.637821012 +0200
+++ gcc/builtins.cc	2022-08-23 20:59:35.287564032 +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,300 @@ 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.
+     Don't use !HONOR_SNANS (fmode) here, so there is some possibility of
+     __builtin_issignaling working without -fsignaling-nans.  Especially
+     when -fno-signaling-nans is the default.
+     On the other side, MODE_HAS_NANS (fmode) is unnecessary, with
+     -ffinite-math-only even __builtin_isnan or __builtin_fpclassify
+     fold to 0 or non-NaN/Inf classification.  */
+  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 +5803,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 +7415,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 +9264,17 @@ 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:
+      /* Folding to true for REAL_CST is done in fold_const_call_ss.
+	 Don't use tree_expr_signaling_nan_p (arg) -> integer_one_node
+	 and !tree_expr_maybe_signaling_nan_p (arg) -> integer_zero_node
+	 here, so there is some possibility of __builtin_issignaling working
+	 without -fsignaling-nans.  Especially when -fno-signaling-nans is
+	 the default.  */
+      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 +9711,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-08-17 16:58:48.065815537 +0200
+++ gcc/optabs.def	2022-08-23 20:59:35.288564018 +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-08-17 16:58:47.914817468 +0200
+++ gcc/fold-const-call.cc	2022-08-23 20:59:35.288564018 +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-19 16:00:05.035390340 +0200
+++ gcc/config/i386/i386.md	2022-08-23 20:59:35.291563978 +0200
@@ -24732,6 +24732,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-08-17 16:58:47.857818198 +0200
+++ gcc/doc/extend.texi	2022-08-23 20:59:35.294563938 +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
@@ -13556,6 +13557,8 @@ In the same fashion, GCC provides @code{
 @code{isinf_sign}, @code{isnormal} and @code{signbit} built-ins used with
 @code{__builtin_} prefixed.  The @code{isinf} and @code{isnan}
 built-in functions appear both with and without the @code{__builtin_} prefix.
+With @code{-ffinite-math-only} option the @code{isinf} and @code{isnan}
+built-in functions will always return 0.
 
 GCC provides built-in versions of the ISO C99 floating-point rounding and
 exceptions handling functions @code{fegetround}, @code{feclearexcept} and
@@ -14489,6 +14492,20 @@ 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.
+This built-in function can work even without the non-default
+@code{-fsignaling-nans} option, although if a signaling NaN is computed,
+stored or passed as argument to some function other than this built-in
+in the current translation unit, it is safer to use @code{-fsignaling-nans}.
+With @code{-ffinite-math-only} option this built-in function will always
+return 0.
+@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-08-17 16:58:47.887817814 +0200
+++ gcc/doc/md.texi	2022-08-23 20:59:35.296563911 +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-23 20:51:23.454172400 +0200
+++ gcc/c-family/c-common.cc	2022-08-23 20:59:35.297563898 +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-17 16:59:08.701551580 +0200
+++ gcc/c/c-typeck.cc	2022-08-23 20:59:35.299563871 +0200
@@ -3546,6 +3546,7 @@ convert_arguments (location_t loc, vec<l
 	  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-08-17 16:58:47.919817405 +0200
+++ gcc/fortran/f95-lang.cc	2022-08-23 20:59:35.299563871 +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-23 20:59:35.299563871 +0200
+++ gcc/testsuite/gcc.dg/torture/builtin-issignaling-1.c	2022-08-23 21:14:30.082546374 +0200
@@ -0,0 +1,130 @@
+/* { 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 } } } */
+
+#ifndef EXT
+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);
+}
+#else
+#define CONCATX(X, Y) X ## Y
+#define CONCAT(X, Y) CONCATX (X, Y)
+#define CONCAT3(X, Y, Z) CONCAT (CONCAT (X, Y), Z)
+#define CONCAT4(W, X, Y, Z) CONCAT (CONCAT (CONCAT (W, X), Y), Z)
+
+#if EXT
+# define TYPE CONCAT3 (_Float, WIDTH, x)
+# define CST(C) CONCAT4 (C, f, WIDTH, x)
+# define FN(F) CONCAT4 (F, f, WIDTH, x)
+#else
+# define TYPE CONCAT (_Float, WIDTH)
+# define CST(C) CONCAT3 (C, f, WIDTH)
+# define FN(F) CONCAT3 (F, f, WIDTH)
+#endif
+
+int
+f1 (void)
+{
+  return __builtin_issignaling (FN (__builtin_nans) (""));
+}
+
+int
+f2 (void)
+{
+  return __builtin_issignaling (FN (__builtin_nan) (""));
+}
+
+int
+f3 (void)
+{
+  return __builtin_issignaling (CST (0.0));
+}
+
+int
+f4 (TYPE x)
+{
+  return __builtin_issignaling (x);
+}
+#endif
+
+#ifndef EXT
+float x;
+double y;
+long double z;
+#else
+TYPE w;
+#endif
+
+int
+main ()
+{
+  if (!f1 () || f2 () || f3 ())
+    __builtin_abort ();
+  asm volatile ("" : : : "memory");
+#ifndef EXT
+  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 ();
+#else
+  if (f4 (w) || !f4 (FN (__builtin_nans) ("0x123")) || f4 (CST (42.0)) || f4 (FN (__builtin_nan) ("0x234"))
+      || f4 (FN (__builtin_inf) ()) || f4 (-FN (__builtin_inf) ()) || f4 (CST (-42.0)) || f4 (CST (-0.0)) || f4 (CST (0.0)))
+    __builtin_abort ();
+  w = FN (__builtin_nans) ("");
+  asm volatile ("" : : : "memory");
+  if (!f4 (w))
+    __builtin_abort ();
+#endif
+  return 0;
+}
--- gcc/testsuite/gcc.dg/torture/builtin-issignaling-2.c.jj	2022-08-23 20:59:35.299563871 +0200
+++ gcc/testsuite/gcc.dg/torture/builtin-issignaling-2.c	2022-08-23 20:59:35.299563871 +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.dg/torture/float16-builtin-issignaling-1.c.jj	2022-08-23 21:09:05.116909141 +0200
+++ gcc/testsuite/gcc.dg/torture/float16-builtin-issignaling-1.c	2022-08-23 21:09:16.396757702 +0200
@@ -0,0 +1,13 @@
+/* Test _Float16 __builtin_issignaling.  */
+/* { dg-do run } */
+/* { dg-options "" } */
+/* { dg-add-options float16 } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target float16_runtime } */
+/* { dg-additional-options "-fsignaling-nans" } */
+/* Workaround for PR57484 on ia32: */
+/* { dg-additional-options "-msse2 -mfpmath=sse" { target { ia32 && sse2_runtime } } } */
+
+#define WIDTH 16
+#define EXT 0
+#include "builtin-issignaling-1.c"
--- gcc/testsuite/gcc.dg/torture/float32-builtin-issignaling-1.c.jj	2022-08-23 21:09:31.685552450 +0200
+++ gcc/testsuite/gcc.dg/torture/float32-builtin-issignaling-1.c	2022-08-23 21:09:52.587271839 +0200
@@ -0,0 +1,13 @@
+/* Test _Float32 __builtin_issignaling.  */
+/* { dg-do run } */
+/* { dg-options "" } */
+/* { dg-add-options float32 } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target float32_runtime } */
+/* { dg-additional-options "-fsignaling-nans" } */
+/* Workaround for PR57484 on ia32: */
+/* { dg-additional-options "-msse2 -mfpmath=sse" { target { ia32 && sse2_runtime } } } */
+
+#define WIDTH 32
+#define EXT 0
+#include "builtin-issignaling-1.c"
--- gcc/testsuite/gcc.dg/torture/float32x-builtin-issignaling-1.c.jj	2022-08-23 21:10:09.350046788 +0200
+++ gcc/testsuite/gcc.dg/torture/float32x-builtin-issignaling-1.c	2022-08-23 21:10:20.044903199 +0200
@@ -0,0 +1,13 @@
+/* Test _Float32x __builtin_issignaling.  */
+/* { dg-do run } */
+/* { dg-options "" } */
+/* { dg-add-options float32x } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target float32x_runtime } */
+/* { dg-additional-options "-fsignaling-nans" } */
+/* Workaround for PR57484 on ia32: */
+/* { dg-additional-options "-msse2 -mfpmath=sse" { target { ia32 && sse2_runtime } } } */
+
+#define WIDTH 32
+#define EXT 1
+#include "builtin-issignaling-1.c"
--- gcc/testsuite/gcc.dg/torture/float64-builtin-issignaling-1.c.jj	2022-08-23 21:10:40.218632372 +0200
+++ gcc/testsuite/gcc.dg/torture/float64-builtin-issignaling-1.c	2022-08-23 21:10:50.546493715 +0200
@@ -0,0 +1,13 @@
+/* Test _Float64 __builtin_issignaling.  */
+/* { dg-do run } */
+/* { dg-options "" } */
+/* { dg-add-options float64 } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target float64_runtime } */
+/* { dg-additional-options "-fsignaling-nans" } */
+/* Workaround for PR57484 on ia32: */
+/* { dg-additional-options "-msse2 -mfpmath=sse" { target { ia32 && sse2_runtime } } } */
+
+#define WIDTH 64
+#define EXT 0
+#include "builtin-issignaling-1.c"
--- gcc/testsuite/gcc.dg/torture/float64x-builtin-issignaling-1.c.jj	2022-08-23 21:10:55.573426226 +0200
+++ gcc/testsuite/gcc.dg/torture/float64x-builtin-issignaling-1.c	2022-08-23 21:11:06.605278121 +0200
@@ -0,0 +1,13 @@
+/* Test _Float64x __builtin_issignaling.  */
+/* { dg-do run } */
+/* { dg-options "" } */
+/* { dg-add-options float64x } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target float64x_runtime } */
+/* { dg-additional-options "-fsignaling-nans" } */
+/* Workaround for PR57484 on ia32: */
+/* { dg-additional-options "-msse2 -mfpmath=sse" { target { ia32 && sse2_runtime } } } */
+
+#define WIDTH 64
+#define EXT 1
+#include "builtin-issignaling-1.c"
--- gcc/testsuite/gcc.dg/torture/float128-builtin-issignaling-1.c.jj	2022-08-23 21:08:06.440696884 +0200
+++ gcc/testsuite/gcc.dg/torture/float128-builtin-issignaling-1.c	2022-08-23 21:08:00.824772287 +0200
@@ -0,0 +1,13 @@
+/* Test _Float128 __builtin_issignaling.  */
+/* { dg-do run } */
+/* { dg-options "" } */
+/* { dg-add-options float128 } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target float128_runtime } */
+/* { dg-additional-options "-fsignaling-nans" } */
+/* Workaround for PR57484 on ia32: */
+/* { dg-additional-options "-msse2 -mfpmath=sse" { target { ia32 && sse2_runtime } } } */
+
+#define WIDTH 128
+#define EXT 0
+#include "builtin-issignaling-1.c"
--- gcc/testsuite/gcc.dg/torture/float128x-builtin-issignaling-1.c.jj	2022-08-23 21:08:23.663465667 +0200
+++ gcc/testsuite/gcc.dg/torture/float128x-builtin-issignaling-1.c	2022-08-23 21:08:43.922193659 +0200
@@ -0,0 +1,13 @@
+/* Test _Float128x __builtin_issignaling.  */
+/* { dg-do run } */
+/* { dg-options "" } */
+/* { dg-add-options float128x } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target float128x_runtime } */
+/* { dg-additional-options "-fsignaling-nans" } */
+/* Workaround for PR57484 on ia32: */
+/* { dg-additional-options "-msse2 -mfpmath=sse" { target { ia32 && sse2_runtime } } } */
+
+#define WIDTH 128
+#define EXT 1
+#include "builtin-issignaling-1.c"
--- gcc/testsuite/gcc.target/i386/builtin-issignaling-1.c.jj	2022-08-23 20:59:35.299563871 +0200
+++ gcc/testsuite/gcc.target/i386/builtin-issignaling-1.c	2022-08-23 20:59:35.299563871 +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


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-15 10:12 [PATCH] Implement __builtin_issignaling Jakub Jelinek
                   ` (2 preceding siblings ...)
  2022-08-15 20:52 ` Uros Bizjak
@ 2022-08-25 19:23 ` Michael Meissner
  2022-08-25 19:56   ` Jakub Jelinek
  3 siblings, 1 reply; 19+ messages in thread
From: Michael Meissner @ 2022-08-25 19:23 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, Joseph S. Myers, Jeff Law, gcc-patches, FX

On Mon, Aug 15, 2022 at 12:12:02PM +0200, Jakub Jelinek via Gcc-patches 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.

I haven't looked in detail at the patch, but from the description I think it
needs a way for machine dependent parts to optimize this for a given mode when
various switches are used.

For example on the PowerPC (and at least older AMD processors) it can often
times be expensive to move data from a floating point register to a GPR
register to do the masking operation.  I implemented special purpose
recognizers a few years ago to speed up the SFmode math library to recognize
when the glibc tests were being done.

If the machine has a faster way to implement it, it should be allowed to do
this.

On some of the PowerPC's, I might want to generate code like (note, I might
have some of the tests backwards, but the idea is test if the value is a NaN,
and if it is a NaN, look at the signalling bit.  Otherwise return 0:

	if (__builtin_isnan (x)) {
		long long mantissa = <extract mantissa>
		return ~((mantissa >> 52) & 1);

	} else {
		return 0;
	}

That way it doesn't have to do a direct move to a GPR register to do the
extraction in general.

For SFmode/DFmode, you should be able to optimize it to something like to avoid
any explicit jumps on a power10 (note, I may have some of the tests/shifts
wrong, but that is the general method you could use):

	static int inline
	is_signalling_nan (double value)
	{
	  int exponent = __builtin_vec_scalar_extract_exp (value);
	  long long mantissa = __builtin_vec_scalar_extract_sig (value);
	  return (mantissa >> 52) & (exponent == 0);
	}

-- 
Michael Meissner, IBM
PO Box 98, Ayer, Massachusetts, USA, 01432
email: meissner@linux.ibm.com

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-25 19:23 ` Michael Meissner
@ 2022-08-25 19:56   ` Jakub Jelinek
  2022-08-26 17:25     ` Michael Meissner
  0 siblings, 1 reply; 19+ messages in thread
From: Jakub Jelinek @ 2022-08-25 19:56 UTC (permalink / raw)
  To: Michael Meissner, Richard Biener, Joseph S. Myers, Jeff Law,
	gcc-patches, FX

On Thu, Aug 25, 2022 at 03:23:12PM -0400, Michael Meissner wrote:
> On Mon, Aug 15, 2022 at 12:12:02PM +0200, Jakub Jelinek via Gcc-patches 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.
> 
> I haven't looked in detail at the patch, but from the description I think it
> needs a way for machine dependent parts to optimize this for a given mode when
> various switches are used.

There is an optab which takes priority over the generic code.
So if you have something better than the generic expansion, feel free to add
the named pattern(s).
Currently it is used just on x86 for xf mode.

	Jakub


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Implement __builtin_issignaling
  2022-08-25 19:56   ` Jakub Jelinek
@ 2022-08-26 17:25     ` Michael Meissner
  0 siblings, 0 replies; 19+ messages in thread
From: Michael Meissner @ 2022-08-26 17:25 UTC (permalink / raw)
  To: Jakub Jelinek
  Cc: Michael Meissner, Richard Biener, Joseph S. Myers, Jeff Law,
	gcc-patches, FX

On Thu, Aug 25, 2022 at 09:56:18PM +0200, Jakub Jelinek wrote:
> On Thu, Aug 25, 2022 at 03:23:12PM -0400, Michael Meissner wrote:
> > On Mon, Aug 15, 2022 at 12:12:02PM +0200, Jakub Jelinek via Gcc-patches 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.
> > 
> > I haven't looked in detail at the patch, but from the description I think it
> > needs a way for machine dependent parts to optimize this for a given mode when
> > various switches are used.
> 
> There is an optab which takes priority over the generic code.
> So if you have something better than the generic expansion, feel free to add
> the named pattern(s).
> Currently it is used just on x86 for xf mode.

That is good to know.  When I looked into before, I didn't notice there was an
optab for the case.

-- 
Michael Meissner, IBM
PO Box 98, Ayer, Massachusetts, USA, 01432
email: meissner@linux.ibm.com

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2022-08-26 17:25 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-15 10:12 [PATCH] Implement __builtin_issignaling Jakub Jelinek
2022-08-15 11:24 ` Richard Biener
2022-08-15 11:47   ` Jakub Jelinek
2022-08-15 12:04     ` Andreas Schwab
2022-08-15 12:07     ` Richard Biener
2022-08-15 13:06       ` Jakub Jelinek
2022-08-16 11:33         ` Jakub Jelinek
2022-08-16 11:41           ` Richard Biener
2022-08-16 11:57             ` Jakub Jelinek
2022-08-16 12:04               ` Richard Biener
2022-08-23  7:58             ` Patch ping (Re: [PATCH] Implement __builtin_issignaling) Jakub Jelinek
2022-08-23 18:23               ` Joseph Myers
2022-08-23 19:21                 ` [PATCH] v2: Implement __builtin_issignaling Jakub Jelinek
2022-08-15 16:14 ` [PATCH] " FX
2022-08-15 16:23   ` Jakub Jelinek
2022-08-15 20:52 ` Uros Bizjak
2022-08-25 19:23 ` Michael Meissner
2022-08-25 19:56   ` Jakub Jelinek
2022-08-26 17:25     ` Michael Meissner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).