]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
ifcombine: Handle trapping ifcombining better. [PR126138]
authorAndrea Pinski <andrew.pinski@oss.qualcomm.com>
Sun, 12 Jul 2026 05:57:39 +0000 (22:57 -0700)
committerAndrea Pinski <andrew.pinski@oss.qualcomm.com>
Sat, 25 Jul 2026 20:34:29 +0000 (13:34 -0700)
We can sometimes combine comparisons that are trapping.
This adds that optimization to ifcombine; using combine_comparison
from fold-const to do most of the work.

This does not handle all cases though; mostly dealing with where we need an
inversion of the outer comparison and it does not invert is not currently handled.
This will be handled later on as it needs some extra code added to combine_comparisons.

Bootstrapped and tested on x86_64-linux-gnu.

PR tree-optimization/126138
gcc/ChangeLog:

* tree-ssa-ifcombine.cc (bb_no_side_effects_p): Don't reject GIMPLE_COND
that can trap.
(ifcombine_ifandif): Handle trapping inner conditional specially.

gcc/testsuite/ChangeLog:

* gcc.dg/tree-ssa/fp-trapping-cmp-2.c: New test.
* gcc.dg/tree-ssa/fp-trapping-cmp-3.c: New test.

Signed-off-by: Andrea Pinski <andrew.pinski@oss.qualcomm.com>
gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-3.c [new file with mode: 0644]
gcc/tree-ssa-ifcombine.cc

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-2.c b/gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-2.c
new file mode 100644 (file)
index 0000000..4b1cbd7
--- /dev/null
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O1 -ftrapping-math -fdump-tree-optimized -fdump-tree-ifcombine-details" } */
+/* PR tree-optimization/126138 */
+
+/* (eq || trap) -> trap is fine as eq will be false for NaN
+   which means it is not short circuit and will cause a trap
+   on the trapping instruction always.  */
+int
+f (double i, double j, int n, int m)
+{
+  // (i == j) || (i < j)
+  if (i == j)
+    return m;
+  if (i < j)
+    return m;
+  return n;
+}
+/* (ne && trap) -> trap has a story. */
+int
+f1 (double i, double j, int n, int m)
+{
+  // (i != j) || (i < j)
+  if (i != j)
+    if (i < j)
+      return m;
+  return n;
+}
+/* { dg-final { scan-tree-dump-times " if " 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " < " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " <= " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "optimizing trapping cond to" 2 "ifcombine" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-3.c b/gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-3.c
new file mode 100644 (file)
index 0000000..55bbe17
--- /dev/null
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-O1 -ftrapping-math -fdump-tree-optimized -fdump-tree-ifcombine-details" } */
+/* PR tree-optimization/126138 */
+
+/* (eq || trap) -> trap is fine as eq will be false for NaN
+   which means it is not short circuit and will cause a trap
+   on the trapping instruction always.  */
+int
+f (double i, double j, int n, int m)
+{
+  // (i <= j) || (i < j) -> i <= j
+  if (i <= j)
+    return m;
+  if (i < j)
+    return m;
+  return n;
+}
+/* (ne && trap) -> trap has a story. */
+int
+f1 (double i, double j, int n, int m)
+{
+  // (i <= j) && (i < j) -> i <= j
+  if (i <= j)
+    if (i < j)
+      return m;
+  return n;
+}
+
+/* { dg-final { scan-tree-dump-times "optimizing trapping cond to" 2 "ifcombine" } } */
+/* { dg-final { scan-tree-dump-times " if " 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " < " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " <= " 1 "optimized" } } */
+
index 0c3e78ef331d7191df38e92cdf7360a1c72302d7..829baa66416a28d34382f55449ba0a78f4fdcf0e 100644 (file)
@@ -155,7 +155,9 @@ bb_no_side_effects_p (basic_block bb)
       gassign *ass;
       enum tree_code rhs_code;
       if (gimple_has_side_effects (stmt)
-         || gimple_could_trap_p (stmt)
+         /* Ignore GIMPLE_COND for trapping.  */
+         || (!is_a<gcond*>(stmt)
+             && gimple_could_trap_p (stmt))
          || gimple_vdef (stmt)
          /* We need to rewrite stmts with undefined overflow to use
             unsigned arithmetic but cannot do so for signed division.  */
@@ -842,6 +844,99 @@ ifcombine_ifandif (basic_block inner_cond_bb, bool inner_inv,
   if (!outer_cond)
     return false;
 
+  /* If the inner condition can trap, there is no combining unless
+     the operands are the same.  */
+  if (gimple_could_trap_p (inner_cond))
+    {
+      if (!operand_equal_p (gimple_cond_lhs (inner_cond),
+                           gimple_cond_lhs (outer_cond))
+         || !operand_equal_p (gimple_cond_rhs (inner_cond),
+                              gimple_cond_rhs (outer_cond)))
+       return false;
+     // We don't check if the outer will cause a trap as combine_comparisons
+     // will take care if the combining happens or not. Specifically in the
+     // case of losing a trap or cause a trap that was not there before.
+     tree res = NULL_TREE;
+     tree_code outer_cond_code = gimple_cond_code (outer_cond);
+     tree_code inner_cond_code = gimple_cond_code (inner_cond);
+     tree larg = gimple_cond_lhs (inner_cond);
+     tree rarg = gimple_cond_rhs (inner_cond);
+     // Handle `(a && b)`, no inverse
+     if (!inner_inv && !outer_inv)
+       res = combine_comparisons (UNKNOWN_LOCATION, TRUTH_ANDIF_EXPR,
+                                  outer_cond_code, inner_cond_code,
+                                  boolean_type_node, larg, rarg);
+      // If both are inverse, `!a && !b`, then handle it as `!(a || b)`
+      // As that !a or !b are most likely not producing a comparison code.
+      else if (inner_inv && outer_inv)
+       {
+         res = combine_comparisons (UNKNOWN_LOCATION, TRUTH_ORIF_EXPR,
+                                    outer_cond_code, inner_cond_code,
+                                    boolean_type_node, larg, rarg);
+         if (res)
+           res = fold_build1 (TRUTH_NOT_EXPR, TREE_TYPE (res), res);
+       }
+      else
+       {
+         // Handles the case where one is inverted and the other is not.
+         tree_code inner_cond_code1 = inner_cond_code;
+         tree_code outer_cond_code1 = outer_cond_code;
+         // Try first `!a && b` and `a && !b`, those might be invertable.
+         if (inner_inv)
+           inner_cond_code1 = invert_tree_comparison (inner_cond_code1,
+                                                      HONOR_NANS (larg));
+         else if (outer_inv)
+           outer_cond_code1 = invert_tree_comparison (outer_cond_code1,
+                                                      HONOR_NANS (larg));
+         if (inner_cond_code1 != ERROR_MARK && outer_cond_code1 != ERROR_MARK)
+           res = combine_comparisons (UNKNOWN_LOCATION, TRUTH_ANDIF_EXPR,
+                                      outer_cond_code1, inner_cond_code1,
+                                      boolean_type_node, larg, rarg);
+         // Otherwise, we need to try `!(!a || b)
+         else if (inner_cond_code1 == ERROR_MARK)
+           {
+             // a && !b -> !(!a || b)
+             outer_cond_code1 = invert_tree_comparison (outer_cond_code,
+                                                        HONOR_NANS (larg));
+             if (outer_cond_code1 != ERROR_MARK)
+               res = combine_comparisons (UNKNOWN_LOCATION, TRUTH_ORIF_EXPR,
+                                          outer_cond_code1, inner_cond_code,
+                                          boolean_type_node, larg, rarg);
+             if (res)
+               res = fold_build1 (TRUTH_NOT_EXPR, TREE_TYPE (res), res);
+           }
+         // Or `!(a || !b)`
+         else
+           {
+             // !a && b -> !(a || !b)
+             inner_cond_code1 = invert_tree_comparison (inner_cond_code,
+                                                        HONOR_NANS (larg));
+             if (inner_cond_code1 != ERROR_MARK)
+               res = combine_comparisons (UNKNOWN_LOCATION, TRUTH_ORIF_EXPR,
+                                          outer_cond_code, inner_cond_code1,
+                                          boolean_type_node, larg, rarg);
+             if (res)
+               res = fold_build1 (TRUTH_NOT_EXPR, TREE_TYPE (res), res);
+           }
+       }
+      if (res)
+       {
+         if (!ifcombine_replace_cond (inner_cond, inner_inv,
+                                      outer_cond, outer_inv,
+                                      res, true, NULL_TREE))
+           return false;
+
+         if (dump_file)
+           {
+             fprintf (dump_file, "optimizing trapping cond to ");
+             print_generic_expr (dump_file, res);
+             fprintf (dump_file, "\n");
+           }
+         return true;
+       }
+      return false;
+    }
+
   /* niter analysis does not cope with boolean typed loop exit conditions, nor
      with boolean loop guards.  Avoid turning an analyzable loop exit or guard
      into an unanalyzable one.  */