https://gcc.gnu.org/g:400a1d5536978e7392781151bd14aea85f9f90d1

commit r17-5641-g400a1d5536978e7392781151bd14aea85f9f90d1
Author: Igor Shevlyakov <[email protected]>
Date:   Thu Oct 22 20:18:15 2026 -0700

    gimple-fold: Let callers say whether comparisons short-circuit [PR127786]
    
    and_comparisons_1 and or_comparisons_1 pass TRUTH_ANDIF_EXPR and
    TRUTH_ORIF_EXPR to combine_comparisons, whatever the caller.  Under
    -ftrapping-math combine_comparisons keeps the result trapping on a NaN
    exactly when the original evaluation did.  It needs to know which
    comparisons that evaluation runs.  With TRUTH_AND_EXPR both run, so
    the original traps when either one traps.  With TRUTH_ANDIF_EXPR the
    second runs only when the first is true, so it sees a NaN only if the
    first is true for unordered operands.  Each code is exact for its
    own case and wrong for the other.  TRUTH_ANDIF_EXPR on operands that
    are both evaluated drops the exception of the second comparison,
    which is what reassoc does with BIT_AND_EXPR and BIT_IOR_EXPR:
    
    - !__builtin_isgreaterequal (a, b) | (a > b) became a quiet a != b,
      dropping the FE_INVALID that a > b raises for a NaN;
    - (a <= b) & (a == b) became a quiet a == b;
    - a < b || __builtin_isunordered (a, b), once phiopt has made both
      comparisons unconditional, became a quiet a u< b.
    
    TRUTH_AND_EXPR on short-circuited operands would add an exception
    instead: ORDERED (a, b) && a < b would become a < b.
    
    So add a CODE argument to maybe_fold_and_comparisons and
    maybe_fold_or_comparisons, pass it down to combine_comparisons, and
    have each caller say how it evaluates the comparisons:
    
    - ifcombine: TRUTH_ANDIF_EXPR, as before.  The inner condition is
      only evaluated when the outer one is true.
    - reassoc, both for scalar operands and for VEC_COND_EXPR conditions:
      TRUTH_AND_EXPR and TRUTH_OR_EXPR, since it combines the operands of
      a BIT_AND_EXPR or BIT_IOR_EXPR.
    - if-conversion's fold_or_predicates: TRUTH_OR_EXPR, since the
      if-converted code computes the block predicates unconditionally.
    - The uninitialized-use predicate analysis: TRUTH_ANDIF_EXPR, as
      before.  It only uses the value of the result, so this just keeps
      its behavior.
    
    The recursion keeps the code.  It swaps AND and OR, keeping the
    short-circuit, where De Morgan's law turns one into the other.
    maybe_fold_or_comparisons also tries the two comparisons in the other
    order.  That is only valid when both are evaluated, so it now does so
    only for TRUTH_OR_EXPR.  Every current OR caller passes TRUTH_OR_EXPR,
    so that only restricts the internal recursion reached from ifcombine
    through an inverted AND.
    
    A combination that folds to a constant, e.g. (a < b) & (a > b) -> 0,
    can still lose the exception, as x < x -> 0 does.
    
    Assisted-by: Claude Opus 5.5
    
            PR middle-end/127786
    
    gcc/ChangeLog:
    
            * gimple-fold.h (maybe_fold_and_comparisons)
            (maybe_fold_or_comparisons): Add a tree_code argument.
            * gimple-fold.cc (truth_andor_compl): New function.
            (and_comparisons_1, and_var_with_comparison)
            (and_var_with_comparison_1, or_comparisons_1)
            (or_var_with_comparison, or_var_with_comparison_1): Add a
            tree_code argument and pass it down; pass its complement when
            switching between AND and OR.
            (maybe_fold_and_comparisons): Add a tree_code argument and pass it
            to and_comparisons_1.
            (maybe_fold_or_comparisons): Likewise for or_comparisons_1.  Try
            the swapped order only for TRUTH_OR_EXPR.
            * tree-ssa-ifcombine.cc (ifcombine_ifandif): Pass TRUTH_ANDIF_EXPR.
            * gimple-predicate-analysis.cc (simplify_1b): Likewise.
            * tree-ssa-reassoc.cc (eliminate_redundant_comparison)
            (optimize_vec_cond_expr): Pass TRUTH_AND_EXPR or TRUTH_OR_EXPR.
            * tree-if-conv.cc (fold_or_predicates): Pass TRUTH_OR_EXPR.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/torture/cmp-of-cmp-fenv-2.c: New test.
            * gcc.dg/tree-ssa/fp-trapping-cmp-9.c: New test.
    
    Signed-off-by: Igor Shevlyakov <[email protected]>

Diff:
---
 gcc/gimple-fold.cc                                | 163 +++++++++++++---------
 gcc/gimple-fold.h                                 |   6 +-
 gcc/gimple-predicate-analysis.cc                  |   2 +-
 gcc/testsuite/gcc.dg/torture/cmp-of-cmp-fenv-2.c  |  59 ++++++++
 gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-9.c |  16 +++
 gcc/tree-if-conv.cc                               |   4 +-
 gcc/tree-ssa-ifcombine.cc                         |   3 +-
 gcc/tree-ssa-reassoc.cc                           |  10 +-
 8 files changed, 190 insertions(+), 73 deletions(-)

diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index ed84dab7f378..71dd8f1da74f 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -7360,38 +7360,56 @@ same_bool_result_p (const_tree op1, const_tree op2)
 /* Forward declarations for some mutually recursive functions.  */
 
 static tree
-and_comparisons_1 (tree type, enum tree_code code1, tree op1a, tree op1b,
+and_comparisons_1 (tree type, enum tree_code code,
+                  enum tree_code code1, tree op1a, tree op1b,
                   enum tree_code code2, tree op2a, tree op2b, basic_block);
 static tree
-and_var_with_comparison (tree type, tree var, bool invert,
-                        enum tree_code code2, tree op2a, tree op2b,
-                        basic_block);
+and_var_with_comparison (tree type, enum tree_code code, tree var,
+                        bool invert, enum tree_code code2, tree op2a,
+                        tree op2b, basic_block);
 static tree
-and_var_with_comparison_1 (tree type, gimple *stmt,
+and_var_with_comparison_1 (tree type, enum tree_code code, gimple *stmt,
                           enum tree_code code2, tree op2a, tree op2b,
                           basic_block);
 static tree
-or_comparisons_1 (tree, enum tree_code code1, tree op1a, tree op1b,
+or_comparisons_1 (tree, enum tree_code code,
+                 enum tree_code code1, tree op1a, tree op1b,
                  enum tree_code code2, tree op2a, tree op2b,
                  basic_block);
 static tree
-or_var_with_comparison (tree, tree var, bool invert,
-                       enum tree_code code2, tree op2a, tree op2b,
-                       basic_block);
+or_var_with_comparison (tree, enum tree_code code, tree var,
+                       bool invert, enum tree_code code2, tree op2a,
+                       tree op2b, basic_block);
 static tree
-or_var_with_comparison_1 (tree, gimple *stmt,
+or_var_with_comparison_1 (tree, enum tree_code code, gimple *stmt,
                          enum tree_code code2, tree op2a, tree op2b,
                          basic_block);
 
+/* Return the OR code that evaluates its operands as the AND code CODE
+   does, or the AND code for the OR code CODE.  */
+
+static enum tree_code
+truth_andor_compl (enum tree_code code)
+{
+  switch (code)
+    {
+    case TRUTH_AND_EXPR: return TRUTH_OR_EXPR;
+    case TRUTH_ANDIF_EXPR: return TRUTH_ORIF_EXPR;
+    case TRUTH_OR_EXPR: return TRUTH_AND_EXPR;
+    case TRUTH_ORIF_EXPR: return TRUTH_ANDIF_EXPR;
+    default: gcc_unreachable ();
+    }
+}
+
 /* Helper function for and_comparisons_1:  try to simplify the AND of the
    ssa variable VAR with the comparison specified by (OP2A CODE2 OP2B).
    If INVERT is true, invert the value of the VAR before doing the AND.
    Return NULL_EXPR if we can't simplify this to a single expression.  */
 
 static tree
-and_var_with_comparison (tree type, tree var, bool invert,
-                        enum tree_code code2, tree op2a, tree op2b,
-                        basic_block outer_cond_bb)
+and_var_with_comparison (tree type, enum tree_code code, tree var,
+                        bool invert, enum tree_code code2, tree op2a,
+                        tree op2b, basic_block outer_cond_bb)
 {
   tree t;
   gimple *stmt = SSA_NAME_DEF_STMT (var);
@@ -7404,11 +7422,11 @@ and_var_with_comparison (tree type, tree var, bool 
invert,
      !var AND (op2a code2 op2b) => !(var OR !(op2a code2 op2b))
      Then we only have to consider the simpler non-inverted cases.  */
   if (invert)
-    t = or_var_with_comparison_1 (type, stmt,
+    t = or_var_with_comparison_1 (type, truth_andor_compl (code), stmt,
                                  invert_tree_comparison (code2, false),
                                  op2a, op2b, outer_cond_bb);
   else
-    t = and_var_with_comparison_1 (type, stmt, code2, op2a, op2b,
+    t = and_var_with_comparison_1 (type, code, stmt, code2, op2a, op2b,
                                   outer_cond_bb);
   return canonicalize_bool (t, invert);
 }
@@ -7418,7 +7436,7 @@ and_var_with_comparison (tree type, tree var, bool invert,
    Return NULL_EXPR if we can't simplify this to a single expression.  */
 
 static tree
-and_var_with_comparison_1 (tree type, gimple *stmt,
+and_var_with_comparison_1 (tree type, enum tree_code code, gimple *stmt,
                           enum tree_code code2, tree op2a, tree op2b,
                           basic_block outer_cond_bb)
 {
@@ -7450,7 +7468,7 @@ and_var_with_comparison_1 (tree type, gimple *stmt,
   /* If the definition is a comparison, recurse on it.  */
   if (TREE_CODE_CLASS (innercode) == tcc_comparison)
     {
-      tree t = and_comparisons_1 (type, innercode,
+      tree t = and_comparisons_1 (type, code, innercode,
                                  gimple_assign_rhs1 (stmt),
                                  gimple_assign_rhs2 (stmt),
                                  code2,
@@ -7486,20 +7504,21 @@ and_var_with_comparison_1 (tree type, gimple *stmt,
       else if (inner1 == false_test_var)
        return (is_and
                ? boolean_false_node
-               : and_var_with_comparison (type, inner2, false, code2, op2a,
-                                          op2b, outer_cond_bb));
+               : and_var_with_comparison (type, code, inner2, false, code2,
+                                          op2a, op2b, outer_cond_bb));
       else if (inner2 == false_test_var)
        return (is_and
                ? boolean_false_node
-               : and_var_with_comparison (type, inner1, false, code2, op2a,
-                                          op2b, outer_cond_bb));
+               : and_var_with_comparison (type, code, inner1, false, code2,
+                                          op2a, op2b, outer_cond_bb));
 
       /* Next, redistribute/reassociate the AND across the inner tests.
         Compute the first partial result, (inner1 AND (op2a code op2b))  */
       if (TREE_CODE (inner1) == SSA_NAME
          && is_gimple_assign (s = SSA_NAME_DEF_STMT (inner1))
          && TREE_CODE_CLASS (gimple_assign_rhs_code (s)) == tcc_comparison
-         && (t = maybe_fold_and_comparisons (type, gimple_assign_rhs_code (s),
+         && (t = maybe_fold_and_comparisons (type, code,
+                                             gimple_assign_rhs_code (s),
                                              gimple_assign_rhs1 (s),
                                              gimple_assign_rhs2 (s),
                                              code2, op2a, op2b,
@@ -7532,7 +7551,8 @@ and_var_with_comparison_1 (tree type, gimple *stmt,
       if (TREE_CODE (inner2) == SSA_NAME
          && is_gimple_assign (s = SSA_NAME_DEF_STMT (inner2))
          && TREE_CODE_CLASS (gimple_assign_rhs_code (s)) == tcc_comparison
-         && (t = maybe_fold_and_comparisons (type, gimple_assign_rhs_code (s),
+         && (t = maybe_fold_and_comparisons (type, code,
+                                             gimple_assign_rhs_code (s),
                                              gimple_assign_rhs1 (s),
                                              gimple_assign_rhs2 (s),
                                              code2, op2a, op2b,
@@ -7586,10 +7606,12 @@ and_var_with_comparison_1 (tree type, gimple *stmt,
    If this can be done without constructing an intermediate value,
    return the resulting tree; otherwise NULL_TREE is returned.
    This function is deliberately asymmetric as it recurses on SSA_DEFs
-   in the first comparison but not the second.  */
+   in the first comparison but not the second.  CODE is as for
+   maybe_fold_and_comparisons.  */
 
 static tree
-and_comparisons_1 (tree type, enum tree_code code1, tree op1a, tree op1b,
+and_comparisons_1 (tree type, enum tree_code code,
+                  enum tree_code code1, tree op1a, tree op1b,
                   enum tree_code code2, tree op2a, tree op2b,
                   basic_block outer_cond_bb)
 {
@@ -7601,7 +7623,7 @@ and_comparisons_1 (tree type, enum tree_code code1, tree 
op1a, tree op1b,
     {
       /* Result will be either NULL_TREE, or a combined comparison.  */
       tree t = combine_comparisons (UNKNOWN_LOCATION,
-                                   TRUTH_ANDIF_EXPR, code1, code2,
+                                   code, code1, code2,
                                    truth_type, truth_type, op1a, op1b);
       if (t)
        return t;
@@ -7613,7 +7635,7 @@ and_comparisons_1 (tree type, enum tree_code code1, tree 
op1a, tree op1b,
     {
       /* Result will be either NULL_TREE, or a combined comparison.  */
       tree t = combine_comparisons (UNKNOWN_LOCATION,
-                                   TRUTH_ANDIF_EXPR, code1,
+                                   code, code1,
                                    swap_tree_comparison (code2),
                                    truth_type, truth_type, op1a, op1b);
       if (t)
@@ -7637,8 +7659,8 @@ and_comparisons_1 (tree type, enum tree_code code1, tree 
op1a, tree op1b,
        {
        case GIMPLE_ASSIGN:
          /* Try to simplify by copy-propagating the definition.  */
-         return and_var_with_comparison (type, op1a, invert, code2, op2a,
-                                         op2b, outer_cond_bb);
+         return and_var_with_comparison (type, code, op1a, invert, code2,
+                                         op2a, op2b, outer_cond_bb);
 
        case GIMPLE_PHI:
          /* If every argument to the PHI produces the same result when
@@ -7688,8 +7710,8 @@ and_comparisons_1 (tree type, enum tree_code code1, tree 
op1a, tree op1b,
                                             gimple_bb (def_stmt),
                                             gimple_bb (stmt)))
                        return NULL_TREE;
-                     temp = and_var_with_comparison (type, arg, invert, code2,
-                                                     op2a, op2b,
+                     temp = and_var_with_comparison (type, code, arg, invert,
+                                                     code2, op2a, op2b,
                                                      outer_cond_bb);
                      if (!temp)
                        return NULL_TREE;
@@ -9336,16 +9358,20 @@ fold_truth_andor_for_ifcombine (enum tree_code code, 
tree truth_type,
    If this can be simplified to a single expression (without requiring
    introducing more SSA variables to hold intermediate values),
    return the resulting tree.  Otherwise return NULL_TREE.
-   If the result expression is non-null, it has boolean type.  */
+   If the result expression is non-null, it has boolean type.
+   CODE is TRUTH_AND_EXPR if both comparisons are evaluated and
+   TRUTH_ANDIF_EXPR if the second one is only evaluated when the first
+   one is true; with -ftrapping-math the result raises an exception
+   for a NaN exactly when the evaluated comparisons do.  */
 
 tree
-maybe_fold_and_comparisons (tree type,
+maybe_fold_and_comparisons (tree type, enum tree_code code,
                            enum tree_code code1, tree op1a, tree op1b,
                            enum tree_code code2, tree op2a, tree op2b,
                            basic_block outer_cond_bb)
 {
-  if (tree t = and_comparisons_1 (type, code1, op1a, op1b, code2, op2a, op2b,
-                                 outer_cond_bb))
+  if (tree t = and_comparisons_1 (type, code, code1, op1a, op1b,
+                                 code2, op2a, op2b, outer_cond_bb))
     return t;
 
   if (tree t = maybe_fold_comparisons_from_match_pd (type, BIT_AND_EXPR, code1,
@@ -9362,9 +9388,9 @@ maybe_fold_and_comparisons (tree type,
    Return NULL_EXPR if we can't simplify this to a single expression.  */
 
 static tree
-or_var_with_comparison (tree type, tree var, bool invert,
-                       enum tree_code code2, tree op2a, tree op2b,
-                       basic_block outer_cond_bb)
+or_var_with_comparison (tree type, enum tree_code code, tree var,
+                       bool invert, enum tree_code code2, tree op2a,
+                       tree op2b, basic_block outer_cond_bb)
 {
   tree t;
   gimple *stmt = SSA_NAME_DEF_STMT (var);
@@ -9377,11 +9403,11 @@ or_var_with_comparison (tree type, tree var, bool 
invert,
      !var OR (op2a code2 op2b) => !(var AND !(op2a code2 op2b))
      Then we only have to consider the simpler non-inverted cases.  */
   if (invert)
-    t = and_var_with_comparison_1 (type, stmt,
+    t = and_var_with_comparison_1 (type, truth_andor_compl (code), stmt,
                                   invert_tree_comparison (code2, false),
                                   op2a, op2b, outer_cond_bb);
   else
-    t = or_var_with_comparison_1 (type, stmt, code2, op2a, op2b,
+    t = or_var_with_comparison_1 (type, code, stmt, code2, op2a, op2b,
                                  outer_cond_bb);
   return canonicalize_bool (t, invert);
 }
@@ -9391,7 +9417,7 @@ or_var_with_comparison (tree type, tree var, bool invert,
    Return NULL_EXPR if we can't simplify this to a single expression.  */
 
 static tree
-or_var_with_comparison_1 (tree type, gimple *stmt,
+or_var_with_comparison_1 (tree type, enum tree_code code, gimple *stmt,
                          enum tree_code code2, tree op2a, tree op2b,
                          basic_block outer_cond_bb)
 {
@@ -9423,7 +9449,7 @@ or_var_with_comparison_1 (tree type, gimple *stmt,
   /* If the definition is a comparison, recurse on it.  */
   if (TREE_CODE_CLASS (innercode) == tcc_comparison)
     {
-      tree t = or_comparisons_1 (type, innercode,
+      tree t = or_comparisons_1 (type, code, innercode,
                                 gimple_assign_rhs1 (stmt),
                                 gimple_assign_rhs2 (stmt),
                                 code2, op2a, op2b, outer_cond_bb);
@@ -9457,20 +9483,21 @@ or_var_with_comparison_1 (tree type, gimple *stmt,
       else if (inner1 == false_test_var)
        return (is_or
                ? boolean_true_node
-               : or_var_with_comparison (type, inner2, false, code2, op2a,
-                                         op2b, outer_cond_bb));
+               : or_var_with_comparison (type, code, inner2, false, code2,
+                                         op2a, op2b, outer_cond_bb));
       else if (inner2 == false_test_var)
        return (is_or
                ? boolean_true_node
-               : or_var_with_comparison (type, inner1, false, code2, op2a,
-                                         op2b, outer_cond_bb));
+               : or_var_with_comparison (type, code, inner1, false, code2,
+                                         op2a, op2b, outer_cond_bb));
 
       /* Next, redistribute/reassociate the OR across the inner tests.
         Compute the first partial result, (inner1 OR (op2a code op2b))  */
       if (TREE_CODE (inner1) == SSA_NAME
          && is_gimple_assign (s = SSA_NAME_DEF_STMT (inner1))
          && TREE_CODE_CLASS (gimple_assign_rhs_code (s)) == tcc_comparison
-         && (t = maybe_fold_or_comparisons (type, gimple_assign_rhs_code (s),
+         && (t = maybe_fold_or_comparisons (type, code,
+                                            gimple_assign_rhs_code (s),
                                             gimple_assign_rhs1 (s),
                                             gimple_assign_rhs2 (s),
                                             code2, op2a, op2b,
@@ -9503,7 +9530,8 @@ or_var_with_comparison_1 (tree type, gimple *stmt,
       if (TREE_CODE (inner2) == SSA_NAME
          && is_gimple_assign (s = SSA_NAME_DEF_STMT (inner2))
          && TREE_CODE_CLASS (gimple_assign_rhs_code (s)) == tcc_comparison
-         && (t = maybe_fold_or_comparisons (type, gimple_assign_rhs_code (s),
+         && (t = maybe_fold_or_comparisons (type, code,
+                                            gimple_assign_rhs_code (s),
                                             gimple_assign_rhs1 (s),
                                             gimple_assign_rhs2 (s),
                                             code2, op2a, op2b,
@@ -9558,10 +9586,12 @@ or_var_with_comparison_1 (tree type, gimple *stmt,
    If this can be done without constructing an intermediate value,
    return the resulting tree; otherwise NULL_TREE is returned.
    This function is deliberately asymmetric as it recurses on SSA_DEFs
-   in the first comparison but not the second.  */
+   in the first comparison but not the second.  CODE is as for
+   maybe_fold_or_comparisons.  */
 
 static tree
-or_comparisons_1 (tree type, enum tree_code code1, tree op1a, tree op1b,
+or_comparisons_1 (tree type, enum tree_code code,
+                 enum tree_code code1, tree op1a, tree op1b,
                  enum tree_code code2, tree op2a, tree op2b,
                  basic_block outer_cond_bb)
 {
@@ -9573,7 +9603,7 @@ or_comparisons_1 (tree type, enum tree_code code1, tree 
op1a, tree op1b,
     {
       /* Result will be either NULL_TREE, or a combined comparison.  */
       tree t = combine_comparisons (UNKNOWN_LOCATION,
-                                   TRUTH_ORIF_EXPR, code1, code2,
+                                   code, code1, code2,
                                    truth_type, truth_type, op1a, op1b);
       if (t)
        return t;
@@ -9585,7 +9615,7 @@ or_comparisons_1 (tree type, enum tree_code code1, tree 
op1a, tree op1b,
     {
       /* Result will be either NULL_TREE, or a combined comparison.  */
       tree t = combine_comparisons (UNKNOWN_LOCATION,
-                                   TRUTH_ORIF_EXPR, code1,
+                                   code, code1,
                                    swap_tree_comparison (code2),
                                    truth_type, truth_type, op1a, op1b);
       if (t)
@@ -9606,8 +9636,8 @@ or_comparisons_1 (tree type, enum tree_code code1, tree 
op1a, tree op1b,
        {
        case GIMPLE_ASSIGN:
          /* Try to simplify by copy-propagating the definition.  */
-         return or_var_with_comparison (type, op1a, invert, code2, op2a,
-                                        op2b, outer_cond_bb);
+         return or_var_with_comparison (type, code, op1a, invert, code2,
+                                        op2a, op2b, outer_cond_bb);
 
        case GIMPLE_PHI:
          /* If every argument to the PHI produces the same result when
@@ -9657,8 +9687,9 @@ or_comparisons_1 (tree type, enum tree_code code1, tree 
op1a, tree op1b,
                                             gimple_bb (def_stmt),
                                             gimple_bb (stmt)))
                        return NULL_TREE;
-                     temp = or_var_with_comparison (type, arg, invert, code2,
-                                                    op2a, op2b, outer_cond_bb);
+                     temp = or_var_with_comparison (type, code, arg, invert,
+                                                    code2, op2a, op2b,
+                                                    outer_cond_bb);
                      if (!temp)
                        return NULL_TREE;
                      else if (!result)
@@ -9684,21 +9715,27 @@ or_comparisons_1 (tree type, enum tree_code code1, tree 
op1a, tree op1b,
    If this can be simplified to a single expression (without requiring
    introducing more SSA variables to hold intermediate values),
    return the resulting tree.  Otherwise return NULL_TREE.
-   If the result expression is non-null, it has boolean type.  */
+   If the result expression is non-null, it has boolean type.
+   CODE is TRUTH_OR_EXPR if both comparisons are evaluated and
+   TRUTH_ORIF_EXPR if the second one is only evaluated when the first
+   one is false, as for maybe_fold_and_comparisons.  */
 
 tree
-maybe_fold_or_comparisons (tree type,
+maybe_fold_or_comparisons (tree type, enum tree_code code,
                           enum tree_code code1, tree op1a, tree op1b,
                           enum tree_code code2, tree op2a, tree op2b,
                           basic_block outer_cond_bb)
 {
-  if (tree t = or_comparisons_1 (type, code1, op1a, op1b, code2, op2a, op2b,
-                                outer_cond_bb))
+  if (tree t = or_comparisons_1 (type, code, code1, op1a, op1b,
+                                code2, op2a, op2b, outer_cond_bb))
     return t;
 
-  if (tree t = or_comparisons_1 (type, code2, op2a, op2b, code1, op1a, op1b,
-                                outer_cond_bb))
-    return t;
+  /* Also try the other order, unless the second comparison is only
+     evaluated when the first one is false.  */
+  if (code == TRUTH_OR_EXPR)
+    if (tree t = or_comparisons_1 (type, code, code2, op2a, op2b,
+                                  code1, op1a, op1b, outer_cond_bb))
+      return t;
 
   if (tree t = maybe_fold_comparisons_from_match_pd (type, BIT_IOR_EXPR, code1,
                                                     op1a, op1b, code2, op2a,
diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h
index 058e87c55005..2cb93087712b 100644
--- a/gcc/gimple-fold.h
+++ b/gcc/gimple-fold.h
@@ -36,10 +36,12 @@ extern tree follow_all_ssa_edges (tree);
 extern bool fold_stmt (gimple_stmt_iterator *, bitmap = nullptr);
 extern bool fold_stmt (gimple_stmt_iterator *, tree (*) (tree), bitmap = 
nullptr);
 extern bool fold_stmt_inplace (gimple_stmt_iterator *, tree (*) (tree) = 
no_follow_ssa_edges);
-extern tree maybe_fold_and_comparisons (tree, enum tree_code, tree, tree,
+extern tree maybe_fold_and_comparisons (tree, enum tree_code,
+                                       enum tree_code, tree, tree,
                                        enum tree_code, tree, tree,
                                        basic_block = nullptr);
-extern tree maybe_fold_or_comparisons (tree, enum tree_code, tree, tree,
+extern tree maybe_fold_or_comparisons (tree, enum tree_code,
+                                      enum tree_code, tree, tree,
                                       enum tree_code, tree, tree,
                                       basic_block = nullptr);
 extern bool optimize_atomic_compare_exchange_p (gimple *);
diff --git a/gcc/gimple-predicate-analysis.cc b/gcc/gimple-predicate-analysis.cc
index 6ecb1bc03877..dd66c1f307d8 100644
--- a/gcc/gimple-predicate-analysis.cc
+++ b/gcc/gimple-predicate-analysis.cc
@@ -1318,7 +1318,7 @@ simplify_1b (pred_chain &chain)
            b_code = invert_tree_comparison (b_code, false);
          /* Try to combine X a_code Y && X b_code Y'.  */
          tree comb = maybe_fold_and_comparisons (boolean_type_node,
-                                                 a_code,
+                                                 TRUTH_ANDIF_EXPR, a_code,
                                                  a_pred.pred_lhs,
                                                  a_pred.pred_rhs,
                                                  b_code,
diff --git a/gcc/testsuite/gcc.dg/torture/cmp-of-cmp-fenv-2.c 
b/gcc/testsuite/gcc.dg/torture/cmp-of-cmp-fenv-2.c
new file mode 100644
index 000000000000..2b16ed1350e7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/cmp-of-cmp-fenv-2.c
@@ -0,0 +1,59 @@
+/* PR middle-end/127786 */
+/* Combining two comparisons that are both evaluated must not change
+   whether FE_INVALID is raised for a quiet NaN.  */
+/* { dg-do run } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target fenv_exceptions_double } */
+
+#include <fenv.h>
+
+#define NOIPA __attribute__ ((noipa))
+
+/* Quiet | signaling: > is evaluated, so NE would not signal.  */
+NOIPA int unlt_or_gt (double a, double b)
+{ return !__builtin_isgreaterequal (a, b) | (a > b); }
+/* Signaling & quiet: EQ would not signal.  */
+NOIPA int le_and_eq (double a, double b) { return (a <= b) & (a == b); }
+/* Signaling, then quiet in an inner if: EQ would not signal.  */
+NOIPA int le_then_eq (double a, double b)
+{
+  if (a <= b)
+    if (a == b)
+      return 1;
+  return 0;
+}
+/* Signaling || quiet: < is always evaluated, UNLT would not signal.  */
+NOIPA int lt_orif_uno (double a, double b)
+{ return a < b || __builtin_isunordered (a, b); }
+
+static const struct
+{
+  int (*fn) (double, double);
+  /* Results for (NaN, 1), (0, 1), (1, 1) and (2, 1).  */
+  int res[4];
+} tests[] = {
+  { unlt_or_gt,  { 1, 1, 0, 1 } },
+  { le_and_eq,   { 0, 0, 1, 0 } },
+  { le_then_eq,  { 0, 0, 1, 0 } },
+  { lt_orif_uno, { 1, 1, 0, 0 } },
+};
+
+int
+main (void)
+{
+  static const double as[4] = { __builtin_nan (""), 0.0, 1.0, 2.0 };
+
+  for (unsigned i = 0; i < sizeof (tests) / sizeof (tests[0]); i++)
+    for (unsigned j = 0; j < 4; j++)
+      {
+       feclearexcept (FE_INVALID);
+       int r = tests[i].fn (as[j], 1.0);
+       int invalid = fetestexcept (FE_INVALID) != 0;
+       if (r != tests[i].res[j])
+         __builtin_abort ();
+       /* Each of them raises FE_INVALID for the NaN only.  */
+       if (invalid != (j == 0))
+         __builtin_abort ();
+      }
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-9.c 
b/gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-9.c
new file mode 100644
index 000000000000..2a13f3b85a2f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-9.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O1 -ftrapping-math -fdump-tree-ifcombine-details" } */
+
+/* a <= b is only evaluated when a == b is true, so the operands are
+   ordered and it cannot trap: ifcombine may still merge the two.  */
+
+int
+f (double a, double b)
+{
+  if (a == b)
+    if (a <= b)
+      return 1;
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump "optimizing trapping cond to a_\[0-9\]+\\(D\\) 
== b_\[0-9\]+\\(D\\)" "ifcombine" } } */
diff --git a/gcc/tree-if-conv.cc b/gcc/tree-if-conv.cc
index 30b378098045..da8d0d20aca8 100644
--- a/gcc/tree-if-conv.cc
+++ b/gcc/tree-if-conv.cc
@@ -477,8 +477,8 @@ fold_or_predicates (location_t loc, tree c1, tree c2)
 
   if (code1 != ERROR_MARK && code2 != ERROR_MARK)
     {
-      tree t = maybe_fold_or_comparisons (boolean_type_node, code1, op1a, op1b,
-                                         code2, op2a, op2b);
+      tree t = maybe_fold_or_comparisons (boolean_type_node, TRUTH_OR_EXPR,
+                                         code1, op1a, op1b, code2, op2a, op2b);
       if (t)
        return t;
     }
diff --git a/gcc/tree-ssa-ifcombine.cc b/gcc/tree-ssa-ifcombine.cc
index 0d663f4130bb..d5e3d91e9b5b 100644
--- a/gcc/tree-ssa-ifcombine.cc
+++ b/gcc/tree-ssa-ifcombine.cc
@@ -1117,7 +1117,8 @@ ifcombine_ifandif (basic_block inner_cond_bb, bool 
inner_inv,
        return false;
       /* Don't return false so fast, try maybe_fold_or_comparisons?  */
 
-      if (!(t = maybe_fold_and_comparisons (boolean_type_node, outer_cond_code,
+      if (!(t = maybe_fold_and_comparisons (boolean_type_node,
+                                           TRUTH_ANDIF_EXPR, outer_cond_code,
                                            gimple_cond_lhs (outer_cond),
                                            gimple_cond_rhs (outer_cond),
                                            inner_cond_code,
diff --git a/gcc/tree-ssa-reassoc.cc b/gcc/tree-ssa-reassoc.cc
index 10c9c721a4e6..858f9f906ede 100644
--- a/gcc/tree-ssa-reassoc.cc
+++ b/gcc/tree-ssa-reassoc.cc
@@ -2270,12 +2270,12 @@ eliminate_redundant_comparison (enum tree_code opcode,
         two comparisons.  */
       tree type = TREE_TYPE (gimple_assign_lhs (def1));
       if (opcode == BIT_IOR_EXPR)
-       t = maybe_fold_or_comparisons (type,
+       t = maybe_fold_or_comparisons (type, TRUTH_OR_EXPR,
                                       lcode, op1, op2,
                                       rcode, gimple_assign_rhs1 (def2),
                                       gimple_assign_rhs2 (def2));
       else
-       t = maybe_fold_and_comparisons (type,
+       t = maybe_fold_and_comparisons (type, TRUTH_AND_EXPR,
                                        lcode, op1, op2,
                                        rcode, gimple_assign_rhs1 (def2),
                                        gimple_assign_rhs2 (def2));
@@ -4271,10 +4271,12 @@ optimize_vec_cond_expr (tree_code opcode, 
vec<operand_entry *> *ops)
 
          tree comb;
          if (opcode == BIT_AND_EXPR)
-           comb = maybe_fold_and_comparisons (type, cmp0, lhs0, rhs0,
+           comb = maybe_fold_and_comparisons (type, TRUTH_AND_EXPR,
+                                              cmp0, lhs0, rhs0,
                                               cmp1, lhs1, rhs1);
          else if (opcode == BIT_IOR_EXPR)
-           comb = maybe_fold_or_comparisons (type, cmp0, lhs0, rhs0,
+           comb = maybe_fold_or_comparisons (type, TRUTH_OR_EXPR,
+                                             cmp0, lhs0, rhs0,
                                              cmp1, lhs1, rhs1);
          else
            gcc_unreachable ();

Reply via email to