re PR tree-optimization/87074 (Unroll and jam bug: O3 result differ from O2)
authorMichael Matz <matz@suse.de>
Sat, 1 Sep 2018 17:22:05 +0000 (17:22 +0000)
committerMichael Matz <matz@gcc.gnu.org>
Sat, 1 Sep 2018 17:22:05 +0000 (17:22 +0000)
Fix PR87074

PR tree-optimization/87074
* gimple-loop-jam.c (unroll_jam_possible_p): Check loop exit
PHIs for outer-loop uses.

testsuite/
* gcc.dg/pr87074.c: New test.

From-SVN: r264029

gcc/ChangeLog
gcc/gimple-loop-jam.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/pr87074.c [new file with mode: 0644]

index 1755989..4d432b8 100644 (file)
@@ -1,3 +1,9 @@
+2018-09-01  Michael Matz  <matz@suse.de>
+
+       PR tree-optimization/87074
+       * gimple-loop-jam.c (unroll_jam_possible_p): Check loop exit
+       PHIs for outer-loop uses.
+
 2018-09-01  Gerald Pfeifer  <gerald@pfeifer.com>
 
        * doc/install.texi (Prerequisites): Adjust link mpfr.org.
index 2ecd1bb..c6bd042 100644 (file)
@@ -161,7 +161,7 @@ bb_prevents_fusion_p (basic_block bb)
   gimple_stmt_iterator gsi;
   /* BB is duplicated by outer unrolling and then all N-1 first copies
      move into the body of the fused inner loop.  If BB exits the outer loop
-     the last copy still doess so, and the first N-1 copies are cancelled
+     the last copy still does so, and the first N-1 copies are cancelled
      by loop unrolling, so also after fusion it's the exit block.
      But there might be other reasons that prevent fusion:
        * stores or unknown side-effects prevent fusion
@@ -227,6 +227,33 @@ unroll_jam_possible_p (struct loop *outer, struct loop *loop)
       || !expr_invariant_in_loop_p (outer, niter.niter))
     return false;
 
+  /* If the inner loop produces any values that are used inside the
+     outer loop (except the virtual op) then it can flow
+     back (perhaps indirectly) into the inner loop.  This prevents
+     fusion: without fusion the value at the last iteration is used,
+     with fusion the value after the initial iteration is used.
+
+     If all uses are outside the outer loop this doesn't prevent fusion;
+     the value of the last iteration is still used (and the values from
+     all intermediate iterations are dead).  */
+  gphi_iterator psi;
+  for (psi = gsi_start_phis (single_exit (loop)->dest);
+       !gsi_end_p (psi); gsi_next (&psi))
+    {
+      imm_use_iterator imm_iter;
+      use_operand_p use_p;
+      tree op = gimple_phi_result (psi.phi ());
+      if (virtual_operand_p (op))
+       continue;
+      FOR_EACH_IMM_USE_FAST (use_p, imm_iter, op)
+       {
+         gimple *use_stmt = USE_STMT (use_p);
+         if (!is_gimple_debug (use_stmt)
+             && flow_bb_inside_loop_p (outer, gimple_bb (use_stmt)))
+           return false;
+       }
+    }
+
   /* And check blocks belonging to just outer loop.  */
   bbs = XNEWVEC (basic_block, n_basic_blocks_for_fn (cfun));
   n = get_loop_body_with_size (outer, bbs, n_basic_blocks_for_fn (cfun));
@@ -245,7 +272,6 @@ unroll_jam_possible_p (struct loop *outer, struct loop *loop)
      body would be the after-iter value of the first body) if it's over
      an associative and commutative operation.  We wouldn't
      be able to handle unknown cycles.  */
-  gphi_iterator psi;
   for (psi = gsi_start_phis (loop->header); !gsi_end_p (psi); gsi_next (&psi))
     {
       affine_iv iv;
index 757dd1b..83bab17 100644 (file)
@@ -1,3 +1,8 @@
+2018-09-01  Michael Matz  <matz@suse.de>
+
+       PR tree-optimization/87074
+       * gcc.dg/pr87074.c: New test.
+
 2018-08-31  Richard Biener  <rguenther@suse.de>
 
        PR tree-optimization/87168
diff --git a/gcc/testsuite/gcc.dg/pr87074.c b/gcc/testsuite/gcc.dg/pr87074.c
new file mode 100644 (file)
index 0000000..d838fcd
--- /dev/null
@@ -0,0 +1,25 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -floop-unroll-and-jam --param unroll-jam-min-percent=0" } */
+long b;
+unsigned c[5];
+unsigned long long d = 3;
+int e, f, g;
+
+void h() {
+  for (; f < 11; f++) {
+    b = g;
+    for (e = 0; e < 5; e++) {
+      c[e] = e - b - (c[e] >> 5);
+      g = c[e];
+    }
+  }
+  if (c[0])
+    d = 0;
+}
+
+extern void abort(void);
+int main() {
+  h();
+  if (d != 0)
+    abort ();
+}