https://gcc.gnu.org/g:9b671c0fdb80d9fd8a60030e7cc9550e903c4fa3

commit 9b671c0fdb80d9fd8a60030e7cc9550e903c4fa3
Author: Alexandre Oliva <ol...@gnu.org>
Date:   Thu Nov 28 21:56:34 2024 -0300

    ifcombine: avoid forwarders with intervening blocks

Diff:
---
 gcc/testsuite/gcc.dg/ifcmb-1.c | 60 ++++++++++++++++++++++++++++++++++++
 gcc/tree-ssa-ifcombine.cc      | 69 ++++++++++++++++++++++++++++++++++++------
 2 files changed, 119 insertions(+), 10 deletions(-)

diff --git a/gcc/testsuite/gcc.dg/ifcmb-1.c b/gcc/testsuite/gcc.dg/ifcmb-1.c
new file mode 100644
index 000000000000..9aaba4de5328
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ifcmb-1.c
@@ -0,0 +1,60 @@
+/* { dg-do run } */
+
+[[gnu::noinline]]
+int f0 (int a, int b) {
+  if ((a & 1))
+    return 0;
+  if (b)
+    return 1;
+  if (!(a & 2))
+    return 0;
+  else
+    return 1;
+}
+
+[[gnu::noinline]]
+int f1 (int a, int b) {
+  if (!(a & 1))
+    return 0;
+  if (b)
+    return 1;
+  if ((a & 2))
+    return 1;
+  else
+    return 0;
+}
+
+[[gnu::noinline]]
+int f2 (int a, int b) {
+  if ((a & 1))
+    return 0;
+  if (b)
+    return 1;
+  if (!(a & 2))
+    return 0;
+  else
+    return 1;
+}
+
+[[gnu::noinline]]
+int f3 (int a, int b) {
+  if (!(a & 1))
+    return 0;
+  if (b)
+    return 1;
+  if ((a & 2))
+    return 1;
+  else
+    return 0;
+}
+
+int main() {
+  if (f0 (0, 1) != 1)
+    __builtin_abort();
+  if (f1 (1, 1) != 1)
+    __builtin_abort();
+  if (f2 (2, 1) != 1)
+    __builtin_abort();
+  if (f3 (3, 1) != 1)
+    __builtin_abort();
+}
diff --git a/gcc/tree-ssa-ifcombine.cc b/gcc/tree-ssa-ifcombine.cc
index b58f63f4707a..fd7f3c245254 100644
--- a/gcc/tree-ssa-ifcombine.cc
+++ b/gcc/tree-ssa-ifcombine.cc
@@ -1089,7 +1089,7 @@ tree_ssa_ifcombine_bb_1 (basic_block inner_cond_bb, 
basic_block outer_cond_bb,
     }
 
   /* The || form is characterized by a common then_bb with the
-     two edges leading to it mergable.  The latter is guaranteed
+     two edges leading to it mergeable.  The latter is guaranteed
      by matching PHI arguments in the then_bb and the inner cond_bb
      having no side-effects.  */
   if (phi_pred_bb != then_bb
@@ -1100,7 +1100,7 @@ tree_ssa_ifcombine_bb_1 (basic_block inner_cond_bb, 
basic_block outer_cond_bb,
           <outer_cond_bb>
             if (q) goto then_bb; else goto inner_cond_bb;
           <inner_cond_bb>
-            if (q) goto then_bb; else goto ...;
+            if (p) goto then_bb; else goto ...;
           <then_bb>
             ...
        */
@@ -1116,7 +1116,7 @@ tree_ssa_ifcombine_bb_1 (basic_block inner_cond_bb, 
basic_block outer_cond_bb,
           <outer_cond_bb>
             if (q) goto inner_cond_bb; else goto then_bb;
           <inner_cond_bb>
-            if (q) goto then_bb; else goto ...;
+            if (p) goto then_bb; else goto ...;
           <then_bb>
             ...
        */
@@ -1139,6 +1139,10 @@ tree_ssa_ifcombine_bb (basic_block inner_cond_bb)
   if (!recognize_if_then_else (inner_cond_bb, &then_bb, &else_bb))
     return ret;
 
+  /* FWD_WHICH will be set along with EXIT_BB to take note of whether THEN_BB
+     (1), ELSE_BB (-1) or neither (0) is a forwarder block to EXIT_BB.  */
+  int fwd_which;
+
   /* Recognize && and || of two conditions with a common
      then/else block which entry edges we can merge.  That is:
        if (a || b)
@@ -1198,10 +1202,14 @@ tree_ssa_ifcombine_bb (basic_block inner_cond_bb)
         checking of same phi args.  */
       if (known_succ_p (outer_cond_bb))
        changed = false;
-      else if (tree_ssa_ifcombine_bb_1 (inner_cond_bb, outer_cond_bb,
-                                       then_bb, else_bb, inner_cond_bb, bb))
-       changed = true;
-      else if (forwarder_block_to (else_bb, then_bb))
+      else if (exit_bb
+              ? fwd_which == 0
+              : tree_ssa_ifcombine_bb_1 (inner_cond_bb, outer_cond_bb,
+                                         then_bb, else_bb, inner_cond_bb, bb))
+       changed = true, fwd_which = 0;
+      else if (exit_bb
+              ? fwd_which < 0
+              : forwarder_block_to (else_bb, then_bb))
        {
          /* Other possibilities for the && form, if else_bb is
             empty forwarder block to then_bb.  Compared to the above simpler
@@ -1211,9 +1219,11 @@ tree_ssa_ifcombine_bb (basic_block inner_cond_bb)
             edge from outer_cond_bb and the forwarder block.  */
          if (tree_ssa_ifcombine_bb_1 (inner_cond_bb, outer_cond_bb, else_bb,
                                       then_bb, else_bb, bb))
-           changed = true;
+           changed = true, fwd_which = -1;
        }
-      else if (forwarder_block_to (then_bb, else_bb))
+      else if (exit_bb
+              ? fwd_which > 0
+              : forwarder_block_to (then_bb, else_bb))
        {
          /* Other possibilities for the || form, if then_bb is
             empty forwarder block to else_bb.  Compared to the above simpler
@@ -1223,7 +1233,7 @@ tree_ssa_ifcombine_bb (basic_block inner_cond_bb)
             edge from outer_cond_bb and the forwarder block.  */
          if (tree_ssa_ifcombine_bb_1 (inner_cond_bb, outer_cond_bb, else_bb,
                                       then_bb, then_bb, bb))
-           changed = true;
+           changed = true, fwd_which = 1;
        }
 
       if (changed)
@@ -1243,6 +1253,45 @@ tree_ssa_ifcombine_bb (basic_block inner_cond_bb)
            exit_bb = else_bb;
          else
            break;
+
+         /* Find out which path from INNER_COND_BB shares PHI args with the
+            edge (OUTER_COND_BB->EXIT_BB).  That path may involve a forwarder
+            block, whether THEN_BB or ELSE_BB, and we need to know which one
+            satisfies the condition to avoid combinations that could use
+            different forwarding arrangements, because they would be unsound.
+            E.g., given (a ? 0 : b ? 1 : c ? 1 : 0), after trying to merge b
+            and c, we test that both share the same exit block, with the same
+            value 1.  Whether or not that involves a forwarder block, if we
+            don't go through the same (possibly absent) forwarder block in
+            subsequent attempted combinations, e.g. a with c, we could find
+            that a and inverted c share the same exit block with a different
+            value, namely 0, which would enable an unsound merge.  We need all
+            of inner, intervening and outer blocks to reach the same exit with
+            the same value for the transformation to be sound.  So here we
+            determine how to get to EXIT_BB from outer and inner with the same
+            PHI values, record that in FWD_WHICH, and then subsequent
+            combination attempts that have OUTER_COND_BB as an intervening
+            block will ensure the same path to exit is taken, skipping unsound
+            transformations.  */
+         if (changed)
+           /* FWD_WHICH was set along with CHANGED, and the successful
+              combination already checked for the same PHI args.  */;
+         else if (same_phi_args_p (outer_cond_bb, inner_cond_bb, exit_bb))
+           fwd_which = 0;
+         else if (then_bb == exit_bb
+                  && forwarder_block_to (else_bb, then_bb)
+                  && same_phi_args_p (outer_cond_bb, else_bb, exit_bb))
+           fwd_which = -1;
+         else if (else_bb == exit_bb
+                  && forwarder_block_to (then_bb, else_bb)
+                  && same_phi_args_p (outer_cond_bb, then_bb, exit_bb))
+           fwd_which = 1;
+         else
+           /* If none of the paths share the same PHI args, no combination is
+              viable.  */
+           break;
+         /* Skip the PHI args test below.  */
+         continue;
        }
 
       /* Before trying an earlier block, make sure INNER_COND_BB and the

Reply via email to