tree-optimization/112282 - wrong-code with ifcvt hoisting

Message ID 20231115121218.09387385B531@sourceware.org
State Accepted
Headers
Series tree-optimization/112282 - wrong-code with ifcvt hoisting |

Checks

Context Check Description
snail/gcc-patch-check success Github commit url

Commit Message

Richard Biener Nov. 15, 2023, 12:11 p.m. UTC
  The following avoids hoisting of invariants from conditionally
executed parts of an if-converted loop.  That now makes a difference
since we perform bitfield lowering even when we do not actually
if-convert the loop.  if-conversion deals with resetting flow-sensitive
info when necessary already.

Bootstrapped and tested on x86_64-unknown-linux-gnu, pushed.

	PR tree-optimization/112282
	* tree-if-conv.cc (ifcvt_hoist_invariants): Only hoist from
	the loop header.

	* gcc.dg/torture/pr112282.c: New testcase.
---
 gcc/testsuite/gcc.dg/torture/pr112282.c | 132 ++++++++++++++++++++++++
 gcc/tree-if-conv.cc                     |  44 ++++----
 2 files changed, 153 insertions(+), 23 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/torture/pr112282.c
  

Patch

diff --git a/gcc/testsuite/gcc.dg/torture/pr112282.c b/gcc/testsuite/gcc.dg/torture/pr112282.c
new file mode 100644
index 00000000000..23e0ed64b82
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr112282.c
@@ -0,0 +1,132 @@ 
+/* { dg-do run } */
+
+int printf(const char *, ...);
+void __assert_fail();
+int a, g, h, i, v, w = 2, x, y, ab, ac, ad, ae, af, ag;
+static int f, j, m, n, p, r, u, aa;
+struct b {
+  int c : 20;
+  int d : 20;
+  int e : 10;
+};
+static struct b l, o, q = {3, 3, 5};
+int s(int z) {
+  struct b ah;
+  int ai = 1, aj[7] = {1, 1, 1, 1, 1, 1, 1};
+ak:
+  for (u = -22; u < 2; ++u) {
+    struct b al[8] = {{2, 7, 9}, {8, 7, 1}, {2, 7, 9}, {8, 7, 1}, {2, 7, 9}, {8, 7, 1}, {2, 7, 9}};
+    y = z = 0;
+    for (; z < 2; z++) {
+      int am[18], k;
+      ab = ac = 0;
+      for (; ac < 1; ac++)
+        for (k = 0; k < 9; k++)
+          am[k] = 0;
+      n = 0;
+      while (1) {
+        v = u < 0 || a;
+        h = z < ~u && 4 & q.c;
+        if ((aa <= l.c) > q.d && p)
+          return o.c;
+        if (w)
+          break;
+        return q.e;
+      }
+      a = j;
+    }
+  }
+  for (x = 0; x < 2; x++) {
+    struct b an = {1, 8, 4};
+    int ao[28] = {5, 0, 0, 9, 0, 3, 0, 5, 0, 0, 9, 0, 3, 0, 5, 0, 0, 9, 0, 3, 0, 5, 0, 0, 9, 0, 3, 0};
+    if (q.e) {
+      int ap = ai || l.c + q.c, aq = q.d, ar = p & f;
+      q.d = q.d || ar || ap;
+      p = 0;
+      if (!j && ai)
+        goto as;
+      if (q.d) {
+        printf("", l);
+        q.d = f >> j;
+      }
+      p = l.c = aq;
+      an = q;
+    } else {
+      int at[12][1] = {{9}, {9}, {5}, {9}, {9}, {5}, {9}, {9}, {5}, {9}, {9}, {5}};
+      struct b au;
+      if (o.c)
+        aa = ah.e;
+      if (an.d)
+        ah.e = (j & (aa * m)) ^ au.d;
+      o.c = m + aa;
+      int av = o.c || 0, aw = ai || q.c & l.c, ax = n;
+      if (q.e < ai)
+        q = an;
+      if (r)
+        break;
+      ai = aw - av;
+      an.e = 0;
+      if (ai) {
+        an.e = l.c || 0;
+        f = q.c;
+        ah.e = l.c % q.d;
+        q.c = au.e;
+        if ((q.d && q.c) || ah.e)
+          __assert_fail();
+        q.c = 0;
+        if (au.d > m || ah.e)
+          w = au.c | (n & ah.c);
+      as:
+        ae = af = ah.c;
+        int ay = au.d & q.e & au.c || o.c, az = 0 || o.c, ba = m & ah.d;
+        if (n)
+          au.c = au.e = (q.e || ah.d) ^ (o.c + (az / au.e));
+        n = au.c || au.e;
+        if (ba) {
+          printf("", ax);
+          x = q.e | m;
+          continue;
+        }
+        m = ay;
+        n = printf("", au);
+      }
+      if (ah.d)
+        o.c = l.c & o.c & q.c;
+      if (q.d)
+        __assert_fail();
+      printf("", an);
+      printf("", q);
+      printf("", au);
+      if (ah.e)
+        while (u++) {
+          struct b al[7] = {{7, 9, 8}, {7, 1, 2}, {7, 9, 8}, {7, 1, 2}, {7, 9, 8}, {7, 1, 2}, {7, 9, 0}};
+          if (an.d) {
+            int d[8] = {0, 1, 0, 1, 0, 1, 0, 1};
+            if (ad)
+              goto ak;
+            while (ag)
+              g = an.d = i = m;
+            f = j;
+          }
+          n++;
+        }
+      f = q.d;
+    }
+    if (l.c && m) {
+      int d[7] = {1, 0, 1, 0, 1, 0, 1};
+      if (x)
+        h = an.d;
+      else
+        g = 0;
+    }
+  }
+  int bb = (q.d ^ ah.c) | aa | (q.e & q.c) | (f & ah.d);
+  if (bb)
+    return x;
+  return 0;
+}
+int main() {
+  j = 1;
+  s(0);
+  return 0;
+}
diff --git a/gcc/tree-if-conv.cc b/gcc/tree-if-conv.cc
index 0190cf2369e..0bde281c246 100644
--- a/gcc/tree-if-conv.cc
+++ b/gcc/tree-if-conv.cc
@@ -3468,30 +3468,28 @@  ifcvt_can_hoist (class loop *loop, edge pe, gimple *stmt)
 static void
 ifcvt_hoist_invariants (class loop *loop, edge pe)
 {
+  /* Only hoist from the now unconditionally executed part of the loop.  */
+  basic_block bb = loop->header;
   gimple_stmt_iterator hoist_gsi = {};
-  unsigned int num_blocks = loop->num_nodes;
-  basic_block *body = get_loop_body (loop);
-  for (unsigned int i = 0; i < num_blocks; ++i)
-    for (gimple_stmt_iterator gsi = gsi_start_bb (body[i]); !gsi_end_p (gsi);)
-      {
-	gimple *stmt = gsi_stmt (gsi);
-	if (ifcvt_can_hoist (loop, pe, stmt))
-	  {
-	    /* Once we've hoisted one statement, insert other statements
-	       after it.  */
-	    gsi_remove (&gsi, false);
-	    if (hoist_gsi.ptr)
-	      gsi_insert_after (&hoist_gsi, stmt, GSI_NEW_STMT);
-	    else
-	      {
-		gsi_insert_on_edge_immediate (pe, stmt);
-		hoist_gsi = gsi_for_stmt (stmt);
-	      }
-	    continue;
-	  }
-	gsi_next (&gsi);
-      }
-  free (body);
+  for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
+    {
+      gimple *stmt = gsi_stmt (gsi);
+      if (ifcvt_can_hoist (loop, pe, stmt))
+	{
+	  /* Once we've hoisted one statement, insert other statements
+	     after it.  */
+	  gsi_remove (&gsi, false);
+	  if (hoist_gsi.ptr)
+	    gsi_insert_after (&hoist_gsi, stmt, GSI_NEW_STMT);
+	  else
+	    {
+	      gsi_insert_on_edge_immediate (pe, stmt);
+	      hoist_gsi = gsi_for_stmt (stmt);
+	    }
+	  continue;
+	}
+      gsi_next (&gsi);
+    }
 }
 
 /* Returns the DECL_FIELD_BIT_OFFSET of the bitfield accesse in stmt iff its