From: Andrea Pinski Date: Sun, 12 Jul 2026 05:57:39 +0000 (-0700) Subject: ifcombine: Handle trapping ifcombining better. [PR126138] X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ea33efbbdcf69794b9962bf4926b07e73618d016;p=thirdparty%2Fgcc.git 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 --- 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 00000000000..4b1cbd71d74 --- /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 00000000000..55bbe179925 --- /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 0c3e78ef331..829baa66416 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(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. */