Hi,

Here's an updated version of the patch which warns the user if the
removing of redundant exits results in an infinite loop.  I have added
an additional flag in struct loop called external_exits to record if
an exit edge is moved outside the loop body.  This currently happens in
the loop-unswitch pass and was the root cause of the regression in
torture/pr49518.c that I talked about earlier.  The patch now passes all
regression tests except a mudflap case (fail37-frag).  The test is
already broken due to removal of all exits so I haven't attempted to
fix it as part of this patch.  How does this version look?

Regards,
Siddhesh

gcc/ChangeLog:

        * cfgloop.h (struct loop): New member EXTERNAL_EXITS.
        * tree-ssa-loop-ivcanon.c (remove_redundant_iv_tests) Warn when
        loop is left without any exits.
        * tree-ssa-loop-unswitch.c (tree_unswitch_single_loop): Set
        EXTERNAL_EXITS when moving a statement with an exit edge out of
        the loop body.
diff --git a/gcc/cfgloop.h b/gcc/cfgloop.h
index 5cd62b3..dab3565 100644
--- a/gcc/cfgloop.h
+++ b/gcc/cfgloop.h
@@ -173,6 +173,9 @@ struct GTY ((chain_next ("%h.next"))) loop {
 
   /* Head of the cyclic list of the exits of the loop.  */
   struct loop_exit *exits;
+
+  /* True if an exit branch was moved out of the loop.  */
+  bool external_exits;
 };
 
 /* Flags for state of loop structure.  */
diff --git a/gcc/tree-ssa-loop-ivcanon.c b/gcc/tree-ssa-loop-ivcanon.c
index 601223b..8448234 100644
--- a/gcc/tree-ssa-loop-ivcanon.c
+++ b/gcc/tree-ssa-loop-ivcanon.c
@@ -50,6 +50,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "flags.h"
 #include "tree-inline.h"
 #include "target.h"
+#include "diagnostic.h"
+#include "intl.h"
 
 /* Specifies types of loops that may be unrolled.  */
 
@@ -525,10 +527,21 @@ static bool
 remove_redundant_iv_tests (struct loop *loop)
 {
   struct nb_iter_bound *elt;
-  bool changed = false;
+  loop_exit *exit;
+  VEC(gimple, stack) *exit_stmts = VEC_alloc (gimple, stack, 16);
+  int exits_left = 0, num_exits = 0;
 
   if (!loop->any_upper_bound)
-    return false;
+    goto out;
+
+  /* Count our exits.  */
+  for (exit = loop->exits->next; exit->e; exit = exit->next)
+    num_exits++;
+
+  if (num_exits == 0)
+    goto out;
+
+  exits_left = num_exits;
   for (elt = loop->bounds; elt; elt = elt->next)
     {
       /* Exit is pointless if it won't be taken before loop reaches
@@ -555,7 +568,11 @@ remove_redundant_iv_tests (struct loop *loop)
 	      || !loop->nb_iterations_upper_bound.ult
 		   (tree_to_double_int (niter.niter)))
 	    continue;
-	  
+
+	  exits_left--;
+
+	  VEC_safe_push (gimple, stack, exit_stmts, elt->stmt);
+
 	  if (dump_file && (dump_flags & TDF_DETAILS))
 	    {
 	      fprintf (dump_file, "Removed pointless exit: ");
@@ -566,10 +583,31 @@ remove_redundant_iv_tests (struct loop *loop)
 	  else
 	    gimple_cond_make_true (elt->stmt);
 	  update_stmt (elt->stmt);
-	  changed = true;
 	}
     }
-  return changed;
+
+  /* We removed all exit points, so tell the user.  */
+  if (exits_left == 0 && !loop->external_exits)
+    {
+      gimple stmt;
+      const char *wording;
+      unsigned i;
+      location_t loc;
+
+      FOR_EACH_VEC_ELT (gimple, exit_stmts, i, stmt)
+	{
+	  loc = gimple_location (stmt);
+	  wording = N_("Loop behavior is undefined before exit condition; "
+		       "turned into infinite loop");
+	  warning_at ((LOCATION_LINE (loc) > 0) ? loc : input_location, 0,
+		      gettext (wording));
+	}
+    }
+
+out:
+  VEC_free (gimple, stack, exit_stmts);
+
+  return exits_left < num_exits;
 }
 
 /* Stores loops that will be unlooped after we process whole loop tree. */
diff --git a/gcc/tree-ssa-loop-unswitch.c b/gcc/tree-ssa-loop-unswitch.c
index b24f3d7..fb95ab7 100644
--- a/gcc/tree-ssa-loop-unswitch.c
+++ b/gcc/tree-ssa-loop-unswitch.c
@@ -234,6 +234,14 @@ tree_unswitch_single_loop (struct loop *loop, int num)
 
       cond = simplify_using_entry_checks (loop, cond);
       stmt = last_stmt (bbs[i]);
+
+      /* We're switching out an exit point, so note that the loop has exits
+         outside its body.  */
+      if (loop_exit_edge_p (loop, EDGE_SUCC (bbs[i], 0))
+	  || loop_exit_edge_p (loop, EDGE_SUCC (bbs[i], 1)))
+	loop->external_exits = true;
+
+      /* TODO If this is a loop exit statement then note it.  */
       if (integer_nonzerop (cond))
 	{
 	  /* Remove false path.  */

Reply via email to