Fix middle-end/85811: Introduce tree_expr_maybe_non_p et al.
authorRoger Sayle <roger@nextmovesoftware.com>
Wed, 18 Nov 2020 22:09:46 +0000 (15:09 -0700)
committerJeff Law <law@redhat.com>
Wed, 18 Nov 2020 22:09:46 +0000 (15:09 -0700)
The motivation for this patch is PR middle-end/85811, a wrong-code
regression entitled "Invalid optimization with fmax, fabs and nan".
The optimization involves assuming max(x,y) is non-negative if (say)
y is non-negative, i.e. max(x,2.0).  Unfortunately, this is an invalid
assumption in the presence of NaNs.  Hence max(x,+qNaN), with IEEE fmax
semantics will always return x even though the qNaN is non-negative.
Worse, max(x,2.0) may return a negative value if x is -sNaN.

I'll quote Joseph Myers (many thanks) who describes things clearly as:
> (a) When both arguments are NaNs, the return value should be a qNaN,
> but sometimes it is an sNaN if at least one argument is an sNaN.
> (b) Under TS 18661-1 semantics, if either argument is an sNaN then the
> result should be a qNaN (whereas if one argument is a qNaN and the
> other is not a NaN, the result should be the non-NaN argument).
> Various implementations treat sNaNs like qNaNs here.

Under this logic, the tree_expr_nonnegative_p for IEEE fmax should be:

    CASE_CFN_FMAX:
    CASE_CFN_FMAX_FN:
      /* Usually RECURSE (arg0) || RECURSE (arg1) but NaNs complicate
         things.  In the presence of sNaNs, we're only guaranteed to be
         non-negative if both operands are non-negative.  In the presence
         of qNaNs, we're non-negative if either operand is non-negative
         and can't be a qNaN, or if both operands are non-negative.  */
      if (tree_expr_maybe_signaling_nan_p (arg0) ||
          tree_expr_maybe_signaling_nan_p (arg1))
        return RECURSE (arg0) && RECURSE (arg1);
      return RECURSE (arg0) ? (!tree_expr_maybe_nan_p (arg0)
                              || RECURSE (arg1))
                            : (RECURSE (arg1)
                              && !tree_expr_maybe_nan_p (arg1));

Which indeed resolves the wrong code in the PR.  The infrastructure that
makes this possible are the two new functions tree_expr_maybe_nan_p and
tree_expr_maybe_signaling_nan_p which test whether a value may potentially
be a NaN or a signaling NaN respectively.  In fact, this patch adds seven
new predicates to the middle-end:

bool tree_expr_finite_p (const_tree);
bool tree_expr_infinite_p (const_tree);
bool tree_expr_maybe_infinite_p (const_tree);
bool tree_expr_signaling_nan_p (const_tree);
bool tree_expr_maybe_signaling_nan_p (const_tree);
bool tree_expr_nan_p (const_tree);
bool tree_expr_maybe_nan_p (const_tree);

These functions correspond to the "must" and "may" operators in modal logic,
and allow us to triage expressions in the middle-end; definitely a NaN,
definitely not a NaN, and unknown at compile-time, etc.  A prime example of
the utility of these functions is that a IEEE floating point value promoted
from an integer type can't be a NaN or infinite.  Hence (double)i+0.0 where
i is an integer can be simplified to (double)i even with -fsignaling-nans.
Currently in GCC optimizations are enabled/disabled based on whether the
expression's type supports NaNs or sNaNs; with these new predicates they
can be controlled by whether the actual operands may or may not be NaNs.

Having added these extremely useful helper functions to the middle-end,
I couldn't help by use then in a few places in fold-const.c, builtins.c
and match.pd.  In the near term, these can/should be used in places
where the tree optimizers test for HONOR_NANS, HONOR_INFINITIES or
HONOR_SNANS, or explicitly test whether a REAL_CST is a NaN or Inf.
In the longer term (I'm not volunteering) these predicates could perhaps
be hooked into the middle-end's SSA chaining and/or VRP machinery,
allowing finiteness to propagated around the CFG, much like we
currently propagate value ranges.

This patch has been tested on x86_64-pc-linux-gnu with a "make bootstrap"
and "make -k check".
Ok for mainline?

2020-08-15  Roger Sayle  <roger@nextmovesoftware.com>

gcc/ChangeLog
PR middle-end/85811
* fold-const.c (tree_expr_finite_p): New function to test whether
a tree expression must be finite, i.e. not a FP NaN or infinity.
(tree_expr_infinite_p):  New function to test whether a tree
expression must be infinite, i.e. a FP infinity.
(tree_expr_maybe_infinite_p): New function to test whether a tree
expression may be infinite, i.e. a FP infinity.
(tree_expr_signaling_nan_p): New function to test whether a tree
expression must evaluate to a signaling NaN (sNaN).
(tree_expr_maybe_signaling_nan_p): New function to test whether a
tree expression may be a signaling NaN (sNaN).
(tree_expr_nan_p): New function to test whether a tree expression
must evaluate to a (quiet or signaling) NaN.
(tree_expr_maybe_nan_p): New function to test whether a tree
expression me be a (quiet or signaling) NaN.

(tree_binary_nonnegative_warnv_p) [MAX_EXPR]: In the presence
of NaNs, MAX_EXPR is only guaranteed to be non-negative, if both
operands are non-negative.
(tree_call_nonnegative_warnv_p) [CASE_CFN_FMAX,CASE_CFN_FMAX_FN]:
In the presence of signaling NaNs, fmax is only guaranteed to be
non-negative if both operands are negative.  In the presence of
quiet NaNs, fmax is non-negative if either operand is non-negative
and not a qNaN, or both operands are non-negative.

* fold-const.h (tree_expr_finite_p, tree_expr_infinite_p,
tree_expr_maybe_infinite_p, tree_expr_signaling_nan_p,
tree_expr_maybe_signaling_nan_p, tree_expr_nan_p,
tree_expr_maybe_nan_p): Prototype new functions here.

* builtins.c (fold_builtin_classify) [BUILT_IN_ISINF]: Fold to
a constant if argument is known to be (or not to be) an Infinity.
[BUILT_IN_ISFINITE]: Fold to a constant if argument is known to
be (or not to be) finite.
[BUILT_IN_ISNAN]: Fold to a constant if argument is known to be
(or not to be) a NaN.
(fold_builtin_fpclassify): Check tree_expr_maybe_infinite_p and
tree_expr_maybe_nan_p instead of HONOR_INFINITIES and HONOR_NANS
respectively.
(fold_builtin_unordered_cmp): Fold UNORDERED_EXPR to a constant
when its arguments are known to be (or not be) NaNs.  Check
tree_expr_maybe_nan_p instead of HONOR_NANS when choosing between
unordered and regular forms of comparison operators.

* match.pd (ordered(x,y)->true/false): Constant fold ORDERED_EXPR
if its operands are known to be (or not to be) NaNs.
(unordered(x,y)->true/false): Constant fold UNORDERED_EXPR if its
operands are known to be (or not to be) NaNs.
(sqrt(x)*sqrt(x)->x): Check tree_expr_maybe_signaling_nan_p instead
of HONOR_SNANS.

gcc/testsuite/ChangeLog
PR middle-end/85811
* gcc.dg/pr85811.c: New test.
* gcc.dg/fold-isfinite-1.c: New test.
* gcc.dg/fold-isfinite-2.c: New test.
* gcc.dg/fold-isinf-1.c: New test.
* gcc.dg/fold-isinf-2.c: New test.
* gcc.dg/fold-isnan-1.c: New test.
* gcc.dg/fold-isnan-2.c: New test.

gcc/builtins.c
gcc/fold-const.c
gcc/fold-const.h
gcc/match.pd
gcc/testsuite/gcc.dg/fold-isfinite-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fold-isfinite-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fold-isinf-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fold-isinf-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fold-isnan-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fold-isnan-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/pr85811.c [new file with mode: 0644]

index 42c52a1925ec1a6c012ee0f7d4eb08f341345aea..a75b24c549288536205cb67a91f2cbdd2125a3ee 100644 (file)
@@ -10691,9 +10691,10 @@ fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index)
   switch (builtin_index)
     {
     case BUILT_IN_ISINF:
-      if (!HONOR_INFINITIES (arg))
+      if (tree_expr_infinite_p (arg))
+       return omit_one_operand_loc (loc, type, integer_one_node, arg);
+      if (!tree_expr_maybe_infinite_p (arg))
        return omit_one_operand_loc (loc, type, integer_zero_node, arg);
-
       return NULL_TREE;
 
     case BUILT_IN_ISINF_SIGN:
@@ -10729,14 +10730,16 @@ fold_builtin_classify (location_t loc, tree fndecl, tree arg, int builtin_index)
       }
 
     case BUILT_IN_ISFINITE:
-      if (!HONOR_NANS (arg)
-         && !HONOR_INFINITIES (arg))
+      if (tree_expr_finite_p (arg))
        return omit_one_operand_loc (loc, type, integer_one_node, arg);
-
+      if (tree_expr_nan_p (arg) || tree_expr_infinite_p (arg))
+       return omit_one_operand_loc (loc, type, integer_zero_node, arg);
       return NULL_TREE;
 
     case BUILT_IN_ISNAN:
-      if (!HONOR_NANS (arg))
+      if (tree_expr_nan_p (arg))
+       return omit_one_operand_loc (loc, type, integer_one_node, arg);
+      if (!tree_expr_maybe_nan_p (arg))
        return omit_one_operand_loc (loc, type, integer_zero_node, arg);
 
       {
@@ -10810,7 +10813,7 @@ fold_builtin_fpclassify (location_t loc, tree *args, int nargs)
                     arg, build_real (type, r));
   res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, fp_normal, res);
 
-  if (HONOR_INFINITIES (mode))
+  if (tree_expr_maybe_infinite_p (arg))
     {
       real_inf (&r);
       tmp = fold_build2_loc (loc, EQ_EXPR, integer_type_node, arg,
@@ -10819,7 +10822,7 @@ fold_builtin_fpclassify (location_t loc, tree *args, int nargs)
                         fp_infinite, res);
     }
 
-  if (HONOR_NANS (mode))
+  if (tree_expr_maybe_nan_p (arg))
     {
       tmp = fold_build2_loc (loc, ORDERED_EXPR, integer_type_node, arg, arg);
       res = fold_build3_loc (loc, COND_EXPR, integer_type_node, tmp, res, fp_nan);
@@ -10867,12 +10870,15 @@ fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1,
 
   if (unordered_code == UNORDERED_EXPR)
     {
-      if (!HONOR_NANS (arg0))
+      if (tree_expr_nan_p (arg0) || tree_expr_nan_p (arg1))
+       return omit_two_operands_loc (loc, type, integer_one_node, arg0, arg1);
+      if (!tree_expr_maybe_nan_p (arg0) && !tree_expr_maybe_nan_p (arg1))
        return omit_two_operands_loc (loc, type, integer_zero_node, arg0, arg1);
       return fold_build2_loc (loc, UNORDERED_EXPR, type, arg0, arg1);
     }
 
-  code = HONOR_NANS (arg0) ? unordered_code : ordered_code;
+  code = (tree_expr_maybe_nan_p (arg0) || tree_expr_maybe_nan_p (arg1))
+        ? unordered_code : ordered_code;
   return fold_build1_loc (loc, TRUTH_NOT_EXPR, type,
                      fold_build2_loc (loc, code, type, arg0, arg1));
 }
index ddf18f27cb7e0d7f8335908fff0b9ba5e84a68b3..31c48daf3dda7c19bfda44a2e0d2eafbea52c1c9 100644 (file)
@@ -13701,6 +13701,248 @@ multiple_of_p (tree type, const_tree top, const_tree bottom)
     }
 }
 
+/* Return true if expression X cannot be (or contain) a NaN or infinity.
+   This function returns true for integer expressions, and returns
+   false if uncertain.  */
+
+bool
+tree_expr_finite_p (const_tree x)
+{
+  machine_mode mode = element_mode (x);
+  if (!HONOR_NANS (mode) && !HONOR_INFINITIES (mode))
+    return true;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_isfinite (TREE_REAL_CST_PTR (x));
+    case COMPLEX_CST:
+      return tree_expr_finite_p (TREE_REALPART (x))
+            && tree_expr_finite_p (TREE_IMAGPART (x));
+    case FLOAT_EXPR:
+      return true;
+    case ABS_EXPR:
+    case CONVERT_EXPR:
+    case NON_LVALUE_EXPR:
+    case NEGATE_EXPR:
+    case SAVE_EXPR:
+      return tree_expr_finite_p (TREE_OPERAND (x, 0));
+    case MIN_EXPR:
+    case MAX_EXPR:
+      return tree_expr_finite_p (TREE_OPERAND (x, 0))
+            && tree_expr_finite_p (TREE_OPERAND (x, 1));
+    case COND_EXPR:
+      return tree_expr_finite_p (TREE_OPERAND (x, 1))
+            && tree_expr_finite_p (TREE_OPERAND (x, 2));
+    case CALL_EXPR:
+      switch (get_call_combined_fn (x))
+       {
+       CASE_CFN_FABS:
+         return tree_expr_finite_p (CALL_EXPR_ARG (x, 0));
+       CASE_CFN_FMAX:
+       CASE_CFN_FMIN:
+         return tree_expr_finite_p (CALL_EXPR_ARG (x, 0))
+                && tree_expr_finite_p (CALL_EXPR_ARG (x, 1));
+       default:
+         return false;
+       }
+
+    default:
+      return false;
+    }
+}
+
+/* Return true if expression X evaluates to an infinity.
+   This function returns false for integer expressions.  */
+
+bool
+tree_expr_infinite_p (const_tree x)
+{
+  if (!HONOR_INFINITIES (x))
+    return false;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_isinf (TREE_REAL_CST_PTR (x));
+    case ABS_EXPR:
+    case NEGATE_EXPR:
+    case NON_LVALUE_EXPR:
+    case SAVE_EXPR:
+      return tree_expr_infinite_p (TREE_OPERAND (x, 0));
+    case COND_EXPR:
+      return tree_expr_infinite_p (TREE_OPERAND (x, 1))
+            && tree_expr_infinite_p (TREE_OPERAND (x, 2));
+    default:
+      return false;
+    }
+}
+
+/* Return true if expression X could evaluate to an infinity.
+   This function returns false for integer expressions, and returns
+   true if uncertain.  */
+
+bool
+tree_expr_maybe_infinite_p (const_tree x)
+{
+  if (!HONOR_INFINITIES (x))
+    return false;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_isinf (TREE_REAL_CST_PTR (x));
+    case FLOAT_EXPR:
+      return false;
+    case ABS_EXPR:
+    case NEGATE_EXPR:
+      return tree_expr_maybe_infinite_p (TREE_OPERAND (x, 0));
+    case COND_EXPR:
+      return tree_expr_maybe_infinite_p (TREE_OPERAND (x, 1))
+            || tree_expr_maybe_infinite_p (TREE_OPERAND (x, 2));
+    default:
+      return true;
+    }
+}
+
+/* Return true if expression X evaluates to a signaling NaN.
+   This function returns false for integer expressions.  */
+
+bool
+tree_expr_signaling_nan_p (const_tree x)
+{
+  if (!HONOR_SNANS (x))
+    return false;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_issignaling_nan (TREE_REAL_CST_PTR (x));
+    case NON_LVALUE_EXPR:
+    case SAVE_EXPR:
+      return tree_expr_signaling_nan_p (TREE_OPERAND (x, 0));
+    case COND_EXPR:
+      return tree_expr_signaling_nan_p (TREE_OPERAND (x, 1))
+            && tree_expr_signaling_nan_p (TREE_OPERAND (x, 2));
+    default:
+      return false;
+    }
+}
+
+/* Return true if expression X could evaluate to a signaling NaN.
+   This function returns false for integer expressions, and returns
+   true if uncertain.  */
+
+bool
+tree_expr_maybe_signaling_nan_p (const_tree x)
+{
+  if (!HONOR_SNANS (x))
+    return false;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_issignaling_nan (TREE_REAL_CST_PTR (x));
+    case FLOAT_EXPR:
+      return false;
+    case ABS_EXPR:
+    case CONVERT_EXPR:
+    case NEGATE_EXPR:
+    case NON_LVALUE_EXPR:
+    case SAVE_EXPR:
+      return tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 0));
+    case MIN_EXPR:
+    case MAX_EXPR:
+      return tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 0))
+            || tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 1));
+    case COND_EXPR:
+      return tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 1))
+            || tree_expr_maybe_signaling_nan_p (TREE_OPERAND (x, 2));
+    case CALL_EXPR:
+      switch (get_call_combined_fn (x))
+       {
+       CASE_CFN_FABS:
+         return tree_expr_maybe_signaling_nan_p (CALL_EXPR_ARG (x, 0));
+       CASE_CFN_FMAX:
+       CASE_CFN_FMIN:
+         return tree_expr_maybe_signaling_nan_p (CALL_EXPR_ARG (x, 0))
+                || tree_expr_maybe_signaling_nan_p (CALL_EXPR_ARG (x, 1));
+       default:
+         return true;
+       }
+    default:
+      return true;
+    }
+}
+
+/* Return true if expression X evaluates to a NaN.
+   This function returns false for integer expressions.  */
+
+bool
+tree_expr_nan_p (const_tree x)
+{
+  if (!HONOR_NANS (x))
+    return false;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_isnan (TREE_REAL_CST_PTR (x));
+    case NON_LVALUE_EXPR:
+    case SAVE_EXPR:
+      return tree_expr_nan_p (TREE_OPERAND (x, 0));
+    case COND_EXPR:
+      return tree_expr_nan_p (TREE_OPERAND (x, 1))
+            && tree_expr_nan_p (TREE_OPERAND (x, 2));
+    default:
+      return false;
+    }
+}
+
+/* Return true if expression X could evaluate to a NaN.
+   This function returns false for integer expressions, and returns
+   true if uncertain.  */
+
+bool
+tree_expr_maybe_nan_p (const_tree x)
+{
+  if (!HONOR_NANS (x))
+    return false;
+  switch (TREE_CODE (x))
+    {
+    case REAL_CST:
+      return real_isnan (TREE_REAL_CST_PTR (x));
+    case FLOAT_EXPR:
+      return false;
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+    case MULT_EXPR:
+      return !tree_expr_finite_p (TREE_OPERAND (x, 0))
+            || !tree_expr_finite_p (TREE_OPERAND (x, 1));
+    case ABS_EXPR:
+    case CONVERT_EXPR:
+    case NEGATE_EXPR:
+    case NON_LVALUE_EXPR:
+    case SAVE_EXPR:
+      return tree_expr_maybe_nan_p (TREE_OPERAND (x, 0));
+    case MIN_EXPR:
+    case MAX_EXPR:
+      return tree_expr_maybe_nan_p (TREE_OPERAND (x, 0))
+            || tree_expr_maybe_nan_p (TREE_OPERAND (x, 1));
+    case COND_EXPR:
+      return tree_expr_maybe_nan_p (TREE_OPERAND (x, 1))
+            || tree_expr_maybe_nan_p (TREE_OPERAND (x, 2));
+    case CALL_EXPR:
+      switch (get_call_combined_fn (x))
+       {
+       CASE_CFN_FABS:
+         return tree_expr_maybe_nan_p (CALL_EXPR_ARG (x, 0));
+       CASE_CFN_FMAX:
+       CASE_CFN_FMIN:
+         return tree_expr_maybe_nan_p (CALL_EXPR_ARG (x, 0))
+                || tree_expr_maybe_nan_p (CALL_EXPR_ARG (x, 1));
+       default:
+         return true;
+       }
+    default:
+      return true;
+    }
+}
+
 #define tree_expr_nonnegative_warnv_p(X, Y) \
   _Pragma ("GCC error \"Use RECURSE for recursive calls\"") 0
 
@@ -13878,7 +14120,13 @@ tree_binary_nonnegative_warnv_p (enum tree_code code, tree type, tree op0,
       return false;
 
     case BIT_AND_EXPR:
+      return RECURSE (op0) || RECURSE (op1);
+
     case MAX_EXPR:
+      /* Usually RECURSE (op0) || RECURSE (op1) but NaNs complicate
+        things.  */
+      if (tree_expr_maybe_nan_p (op0) || tree_expr_maybe_nan_p (op1))
+       return RECURSE (op0) && RECURSE (op1);
       return RECURSE (op0) || RECURSE (op1);
 
     case BIT_IOR_EXPR:
@@ -14038,8 +14286,18 @@ tree_call_nonnegative_warnv_p (tree type, combined_fn fn, tree arg0, tree arg1,
 
     CASE_CFN_FMAX:
     CASE_CFN_FMAX_FN:
-      /* True if the 1st OR 2nd arguments are nonnegative.  */
-      return RECURSE (arg0) || RECURSE (arg1);
+      /* Usually RECURSE (arg0) || RECURSE (arg1) but NaNs complicate
+        things.  In the presence of sNaNs, we're only guaranteed to be
+        non-negative if both operands are non-negative.  In the presence
+        of qNaNs, we're non-negative if either operand is non-negative
+        and can't be a qNaN, or if both operands are non-negative.  */
+      if (tree_expr_maybe_signaling_nan_p (arg0) ||
+         tree_expr_maybe_signaling_nan_p (arg1))
+        return RECURSE (arg0) && RECURSE (arg1);
+      return RECURSE (arg0) ? (!tree_expr_maybe_nan_p (arg0)
+                              || RECURSE (arg1))
+                           : (RECURSE (arg1)
+                              && !tree_expr_maybe_nan_p (arg1));
 
     CASE_CFN_FMIN:
     CASE_CFN_FMIN_FN:
index 0c0f5fd46ccc7292bf94ed3c84c3f3ebad26d251..e833019475f625b2ca13c3788bdba35a2186bf49 100644 (file)
@@ -186,6 +186,13 @@ extern tree non_lvalue_loc (location_t, tree);
 extern bool tree_expr_nonzero_p (tree);
 extern bool tree_expr_nonnegative_p (tree);
 extern bool tree_expr_nonnegative_warnv_p (tree, bool *, int = 0);
+extern bool tree_expr_finite_p (const_tree);
+extern bool tree_expr_infinite_p (const_tree);
+extern bool tree_expr_maybe_infinite_p (const_tree);
+extern bool tree_expr_signaling_nan_p (const_tree);
+extern bool tree_expr_maybe_signaling_nan_p (const_tree);
+extern bool tree_expr_nan_p (const_tree);
+extern bool tree_expr_maybe_nan_p (const_tree);
 extern tree make_range (tree, int *, tree *, tree *, bool *);
 extern tree make_range_step (location_t, enum tree_code, tree, tree, tree,
                             tree *, tree *, int *, bool *);
index 1dd05c4c2a36a89f0bca1ce7d7c76fee195e4fd8..cbb4bf0b32dea59abfd4bf7502ef4332ef3b8404 100644 (file)
@@ -4932,6 +4932,24 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
    { constant_boolean_node (cmp == ORDERED_EXPR || cmp == LTGT_EXPR
                            ? false : true, type); })))
 
+/* Fold UNORDERED if either operand must be NaN, or neither can be.  */
+(simplify
+  (unordered @0 @1)
+  (switch
+    (if (tree_expr_nan_p (@0) || tree_expr_nan_p (@1))
+       { constant_boolean_node (true, type); })
+    (if (!tree_expr_maybe_nan_p (@0) && !tree_expr_maybe_nan_p (@1))
+       { constant_boolean_node (false, type); })))
+
+/* Fold ORDERED if either operand must be NaN, or neither can be.  */
+(simplify
+  (ordered @0 @1)
+  (switch
+    (if (tree_expr_nan_p (@0) || tree_expr_nan_p (@1))
+       { constant_boolean_node (false, type); })
+    (if (!tree_expr_maybe_nan_p (@0) && !tree_expr_maybe_nan_p (@1))
+       { constant_boolean_node (true, type); })))
+
 /* bool_var != 0 becomes bool_var.  */
 (simplify
  (ne @0 integer_zerop)
@@ -5063,7 +5081,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  /* Simplify sqrt(x) * sqrt(x) -> x.  */
  (simplify
   (mult (SQRT_ALL@1 @0) @1)
-  (if (!HONOR_SNANS (type))
+  (if (!tree_expr_maybe_signaling_nan_p (@0))
    @0))
 
  (for op (plus minus)
diff --git a/gcc/testsuite/gcc.dg/fold-isfinite-1.c b/gcc/testsuite/gcc.dg/fold-isfinite-1.c
new file mode 100644 (file)
index 0000000..2ea0192
--- /dev/null
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target inf } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int foo(int x)
+{
+  return __builtin_finite((double)x);
+}
+
+int foof(int x)
+{
+  return __builtin_finitef((float)x);
+}
+
+int fool(int x)
+{
+  return __builtin_finitel((long double)x);
+}
+
+/* { dg-final { scan-tree-dump-times "_finite" 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " u> " 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/fold-isfinite-2.c b/gcc/testsuite/gcc.dg/fold-isfinite-2.c
new file mode 100644 (file)
index 0000000..ff70d8d
--- /dev/null
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target inf } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int foo(unsigned int x)
+{
+  return __builtin_finite((double)x);
+}
+
+int foof(unsigned int x)
+{
+  return __builtin_finitef((float)x);
+}
+
+int fool(unsigned int x)
+{
+  return __builtin_finitel((long double)x);
+}
+
+/* { dg-final { scan-tree-dump-times "_finite" 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " u> " 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/fold-isinf-1.c b/gcc/testsuite/gcc.dg/fold-isinf-1.c
new file mode 100644 (file)
index 0000000..485816e
--- /dev/null
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target inf } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int foo(int x)
+{
+  return __builtin_isinf((double)x);
+}
+
+int foof(int x)
+{
+  return __builtin_isinff((float)x);
+}
+
+int fool(int x)
+{
+  return __builtin_isinfl((long double)x);
+}
+
+/* { dg-final { scan-tree-dump-times "_isinf" 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " u<= " 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/fold-isinf-2.c b/gcc/testsuite/gcc.dg/fold-isinf-2.c
new file mode 100644 (file)
index 0000000..a236ca1
--- /dev/null
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target inf } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int foo(unsigned int x)
+{
+  return __builtin_isinf((double)x);
+}
+
+int foof(unsigned int x)
+{
+  return __builtin_isinff((float)x);
+}
+
+int fool(unsigned int x)
+{
+  return __builtin_isinfl((long double)x);
+}
+
+/* { dg-final { scan-tree-dump-times "_isinf" 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " u<= " 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/fold-isnan-1.c b/gcc/testsuite/gcc.dg/fold-isnan-1.c
new file mode 100644 (file)
index 0000000..05ee930
--- /dev/null
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target inf } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int foo(int x)
+{
+  return __builtin_isnan((double)x);
+}
+
+int foof(int x)
+{
+  return __builtin_isnanf((float)x);
+}
+
+int fool(int x)
+{
+  return __builtin_isnanl((long double)x);
+}
+
+/* { dg-final { scan-tree-dump-times "_isnan" 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " unord " 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/fold-isnan-2.c b/gcc/testsuite/gcc.dg/fold-isnan-2.c
new file mode 100644 (file)
index 0000000..32b8833
--- /dev/null
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target inf } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int foo(unsigned int x)
+{
+  return __builtin_isnan((double)x);
+}
+
+int foof(unsigned int x)
+{
+  return __builtin_isnanf((float)x);
+}
+
+int fool(unsigned int x)
+{
+  return __builtin_isnanl((long double)x);
+}
+
+/* { dg-final { scan-tree-dump-times "_isnan" 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " unord " 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/pr85811.c b/gcc/testsuite/gcc.dg/pr85811.c
new file mode 100644 (file)
index 0000000..868f66c
--- /dev/null
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+#include <stdio.h>
+
+int main() {
+  const double negval = -1.0;
+  const double nanval = 0.0 / 0.0;
+  const double val = __builtin_fmax(negval, nanval);
+  const double absval = __builtin_fabs(val);
+  printf("fabs(%.16e) = %.16e\n", val, absval);
+  return absval >= 0 ? 0 : 1;
+}
+
+/* We hope not to see:  printf ("fabs(%.16e) = %.16e\n", val_4, val_4); */
+/* { dg-final { scan-tree-dump-not "val_\[0-9\]*, val_\[0-9\]*" "optimized" } } */