[gcc r17-2708] ifcombine: Handle trapping ifcombining better. [PR126138]

Andrea Pinski via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:ea33efbbdcf69794b9962bf4926b07e73618d016

commit r17-2708-gea33efbbdcf69794b9962bf4926b07e73618d016
Author: Andrea Pinski <[email protected]>
Date:   Sat Jul 11 22:57:39 2026 -0700

    ifcombine: Handle trapping ifcombining better. [PR126138]
    
    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 <[email protected]>

Diff:
---
 gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-2.c | 31 ++++++++
 gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-3.c | 33 ++++++++
 gcc/tree-ssa-ifcombine.cc                         | 97 ++++++++++++++++++++++-
 3 files changed, 160 insertions(+), 1 deletion(-)

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
index 000000000000..4b1cbd71d74e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-2.c
@@ -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
index 000000000000..55bbe1799257
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/fp-trapping-cmp-3.c
@@ -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" } } */
+
diff --git a/gcc/tree-ssa-ifcombine.cc b/gcc/tree-ssa-ifcombine.cc
index 0c3e78ef331d..829baa66416a 100644
--- a/gcc/tree-ssa-ifcombine.cc
+++ b/gcc/tree-ssa-ifcombine.cc
@@ -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.  */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.