diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 1d672944981732fce743cc9b1c3485b26dab41cf..793c4933f79fb0f02472b05e908a5cecf45409e6 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,23 @@
+2012-11-02  Jan Hubicka  <jh@suse.cz>
+
+	PR middle-end/55079
+	* tree-ssa-loop-niter.c (number_of_iterations_exit): Update
+	MAX field if NITER was folded to contant.
+	(record_estimate): Sanity check.
+	* tree-ssa-loop-ivcanon.c (remove_exits_and_undefined_stmts): New
+	function.
+	(remove_redundant_iv_test): New function.
+	(loops_to_unloop, loops_to_unloop_nunroll): New static vars.
+	(unloop_loops): Break out from ...
+	(try_unroll_loop_completely): ... here; Pass in MAXITER; use
+	remove_exits_and_undefined_stmts; do not unloop.
+	(canonicalize_loop_induction_variables): Compute MAXITER;
+	use remove_redundant_iv_test; remove loop_close_ssa_invalidated
+	and irred_invalidated arguments.
+	(canonicalize_induction_variables): Compute fresh bound estimates;
+	unloop; walk from innermost.
+	(tree_unroll_loops_completely): Likewise.
+
 2012-11-02  Vladimir Makarov  <vmakarov@redhat.com>
 
 	PR middle-end/55130
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 26fec16950ef32fca2444bd59bfa6ad60642faa5..cc75c0b64756f318cff1a60dab51d97e0f743589 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2012-11-02  Jan Hubicka  <jh@suse.cz>
+
+	* gcc.dg/tree-ssa/cunroll-10.c: New testcase.
+	* gcc.dg/tree-ssa/cunroll-9.c: New testcase.
+
 2012-11-02  Vladimir Makarov  <vmakarov@redhat.com>
 
 	PR middle-end/55130
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/cunroll-10.c b/gcc/testsuite/gcc.dg/tree-ssa/cunroll-10.c
new file mode 100644
index 0000000000000000000000000000000000000000..7893565918a68387c462d265072a589787b45a48
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/cunroll-10.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -Warray-bounds -fdump-tree-cunroll-details" } */
+int a[3];
+int b[4];
+main()
+{
+  int i;
+  for (i=0;i<4;i++)
+    if (b[i]==2)
+     a[i]++;
+}
+/* { dg-final { scan-tree-dump-times "Forced statement unreachable" 2 "cunroll" } } */
+/* { dg-final { cleanup-tree-dump "cunroll" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/cunroll-9.c b/gcc/testsuite/gcc.dg/tree-ssa/cunroll-9.c
new file mode 100644
index 0000000000000000000000000000000000000000..3205b8d396515d1f0e4b76547b790cd2b87974e7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/cunroll-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-cunrolli" } */
+void abort (void);
+int a[10];
+int b[11];
+t (int n)
+{
+  int i;
+  int sum = 0;
+  for (i = 0; i < n; i++)
+    {
+      if (i > 1000)
+	abort ();
+      if (q ())
+	sum += a[i];
+      else
+	sum += b[i];
+    }
+  return sum;
+}
+/* { dg-final { scan-tree-dump-times 1 "Removed pointless exit:" "cunroli" } } */
+/* { dg-final { cleanup-tree-dump "cunroli" } } */
diff --git a/gcc/tree-ssa-loop-ivcanon.c b/gcc/tree-ssa-loop-ivcanon.c
index 6f37f37cbaaf52ac5c45e8ccb61d3d9b8ab23387..433bb7783cf3320e54af5ca18b2f1ebd2eec49c4 100644
--- a/gcc/tree-ssa-loop-ivcanon.c
+++ b/gcc/tree-ssa-loop-ivcanon.c
@@ -389,33 +389,192 @@ loop_edge_to_cancel (struct loop *loop)
   return NULL;
 }
 
-/* Tries to unroll LOOP completely, i.e. NITER times.
-   UL determines which loops we are allowed to unroll.
-   EXIT is the exit of the loop that should be eliminated.  
+/* Remove all tests for exits that are known to be taken after LOOP was
+   peeled NPEELED times. Put gcc_unreachable before every statement
+   known to not be executed.  */
+
+static bool
+remove_exits_and_undefined_stmts (struct loop *loop, unsigned int npeeled)
+{
+  struct nb_iter_bound *elt;
+  bool changed = false;
+
+  for (elt = loop->bounds; elt; elt = elt->next)
+    {
+      /* If statement is known to be undefined after peeling, turn it
+	 into unreachable (or trap when debugging experience is supposed
+	 to be good).  */
+      if (!elt->is_exit
+	  && elt->bound.ult (double_int::from_uhwi (npeeled)))
+	{
+	  gimple_stmt_iterator gsi = gsi_for_stmt (elt->stmt);
+	  gimple stmt = gimple_build_call
+	      (builtin_decl_implicit (BUILT_IN_UNREACHABLE), 0);
+
+	  gimple_set_location (stmt, gimple_location (elt->stmt));
+	  gsi_insert_before (&gsi, stmt, GSI_NEW_STMT);
+	  changed = true;
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    {
+	      fprintf (dump_file, "Forced statement unreachable: ");
+	      print_gimple_stmt (dump_file, elt->stmt, 0, 0);
+	    }
+	}
+      /* If we know the exit will be taken after peeling, update.  */
+      else if (elt->is_exit
+	       && elt->bound.ule (double_int::from_uhwi (npeeled)))
+	{
+	  basic_block bb = gimple_bb (elt->stmt);
+	  edge exit_edge = EDGE_SUCC (bb, 0);
+
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    {
+	      fprintf (dump_file, "Forced exit to be taken: ");
+	      print_gimple_stmt (dump_file, elt->stmt, 0, 0);
+	    }
+	  if (!loop_exit_edge_p (loop, exit_edge))
+	    exit_edge = EDGE_SUCC (bb, 1);
+	  gcc_checking_assert (loop_exit_edge_p (loop, exit_edge));
+	  if (exit_edge->flags & EDGE_TRUE_VALUE)
+	    gimple_cond_make_true (elt->stmt);
+	  else
+	    gimple_cond_make_false (elt->stmt);
+	  update_stmt (elt->stmt);
+	  changed = true;
+	}
+    }
+  return changed;
+}
+
+/* Remove all exits that are known to be never taken because of the loop bound
+   discovered.  */
+
+static bool
+remove_redundant_iv_tests (struct loop *loop)
+{
+  struct nb_iter_bound *elt;
+  bool changed = false;
+
+  if (!loop->any_upper_bound)
+    return false;
+  for (elt = loop->bounds; elt; elt = elt->next)
+    {
+      /* Exit is pointless if it won't be taken before loop reaches
+	 upper bound.  */
+      if (elt->is_exit && loop->any_upper_bound
+          && loop->nb_iterations_upper_bound.ult (elt->bound))
+	{
+	  basic_block bb = gimple_bb (elt->stmt);
+	  edge exit_edge = EDGE_SUCC (bb, 0);
+	  struct tree_niter_desc niter;
+
+	  if (!loop_exit_edge_p (loop, exit_edge))
+	    exit_edge = EDGE_SUCC (bb, 1);
+
+	  /* Only when we know the actual number of iterations, not
+	     just a bound, we can remove the exit.  */
+	  if (!number_of_iterations_exit (loop, exit_edge,
+					  &niter, false, false))
+	    gcc_unreachable ();
+	  if (!integer_onep (niter.assumptions)
+	      || !integer_zerop (niter.may_be_zero)
+	      || !niter.niter
+	      || TREE_CODE (niter.niter) != INTEGER_CST
+	      || !loop->nb_iterations_upper_bound.ult
+		   (tree_to_double_int (niter.niter)))
+	    continue;
+	  
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    {
+	      fprintf (dump_file, "Removed pointless exit: ");
+	      print_gimple_stmt (dump_file, elt->stmt, 0, 0);
+	    }
+	  if (exit_edge->flags & EDGE_TRUE_VALUE)
+	    gimple_cond_make_false (elt->stmt);
+	  else
+	    gimple_cond_make_true (elt->stmt);
+	  update_stmt (elt->stmt);
+	  changed = true;
+	}
+    }
+  return changed;
+}
+
+/* Stores loops that will be unlooped after we process whole loop tree. */
+static VEC(loop_p, heap) *loops_to_unloop;
+static VEC(int, heap) *loops_to_unloop_nunroll;
+
+/* Cancel all fully unrolled loops by putting __builtin_unreachable
+   on the latch edge.  
+   We do it after all unrolling since unlooping moves basic blocks
+   across loop boundaries trashing loop closed SSA form as well
+   as SCEV info needed to be intact during unrolling. 
+
    IRRED_INVALIDATED is used to bookkeep if information about
    irreducible regions may become invalid as a result
    of the transformation.  
    LOOP_CLOSED_SSA_INVALIDATED is used to bookkepp the case
    when we need to go into loop closed SSA form.  */
 
+void
+unloop_loops (bitmap loop_closed_ssa_invalidated,
+	      bool *irred_invalidated)
+{
+  while (VEC_length (loop_p, loops_to_unloop))
+    {
+      struct loop *loop = VEC_pop (loop_p, loops_to_unloop);
+      int n_unroll = VEC_pop (int, loops_to_unloop_nunroll);
+      basic_block latch = loop->latch;
+      edge latch_edge = loop_latch_edge (loop);
+      int flags = latch_edge->flags;
+      location_t locus = latch_edge->goto_locus;
+      gimple stmt;
+      gimple_stmt_iterator gsi;
+
+      remove_exits_and_undefined_stmts (loop, n_unroll);
+
+      /* Unloop destroys the latch edge.  */
+      unloop (loop, irred_invalidated, loop_closed_ssa_invalidated);
+
+      /* Create new basic block for the latch edge destination and wire
+	 it in.  */
+      stmt = gimple_build_call (builtin_decl_implicit (BUILT_IN_UNREACHABLE), 0);
+      latch_edge = make_edge (latch, create_basic_block (NULL, NULL, latch), flags);
+      latch_edge->probability = 0;
+      latch_edge->count = 0;
+      latch_edge->flags |= flags;
+      latch_edge->goto_locus = locus;
+
+      latch_edge->dest->loop_father = current_loops->tree_root;
+      latch_edge->dest->count = 0;
+      latch_edge->dest->frequency = 0;
+      set_immediate_dominator (CDI_DOMINATORS, latch_edge->dest, latch_edge->src);
+
+      gsi = gsi_start_bb (latch_edge->dest);
+      gsi_insert_after (&gsi, stmt, GSI_NEW_STMT);
+    }
+  VEC_free (loop_p, heap, loops_to_unloop);
+  loops_to_unloop = NULL;
+  VEC_free (int, heap, loops_to_unloop_nunroll);
+  loops_to_unloop_nunroll = NULL;
+}
+
+/* Tries to unroll LOOP completely, i.e. NITER times.
+   UL determines which loops we are allowed to unroll.
+   EXIT is the exit of the loop that should be eliminated.  
+   MAXITER specfy bound on number of iterations, -1 if it is
+   not known or too large for HOST_WIDE_INT.  */
+
 static bool
 try_unroll_loop_completely (struct loop *loop,
 			    edge exit, tree niter,
 			    enum unroll_level ul,
-			    bool *irred_invalidated,
-			    bitmap loop_closed_ssa_invalidated)
+			    HOST_WIDE_INT maxiter)
 {
   unsigned HOST_WIDE_INT n_unroll, ninsns, max_unroll, unr_insns;
   gimple cond;
   struct loop_size size;
   bool n_unroll_found = false;
-  HOST_WIDE_INT maxiter;
-  basic_block latch;
-  edge latch_edge;
-  location_t locus;
-  int flags;
-  gimple stmt;
-  gimple_stmt_iterator gsi;
   edge edge_to_cancel = NULL;
   int num = loop->num;
 
@@ -445,7 +604,6 @@ try_unroll_loop_completely (struct loop *loop,
     exit = NULL;
 
   /* See if we can improve our estimate by using recorded loop bounds.  */
-  maxiter = max_loop_iterations_int (loop);
   if (maxiter >= 0
       && (!n_unroll_found || (unsigned HOST_WIDE_INT)maxiter < n_unroll))
     {
@@ -545,6 +703,7 @@ try_unroll_loop_completely (struct loop *loop,
       free_original_copy_tables ();
     }
 
+
   /* Remove the conditional from the last copy of the loop.  */
   if (edge_to_cancel)
     {
@@ -557,37 +716,10 @@ try_unroll_loop_completely (struct loop *loop,
       /* Do not remove the path. Doing so may remove outer loop
 	 and confuse bookkeeping code in tree_unroll_loops_completelly.  */
     }
-  /* We did not manage to cancel the loop.
-     The loop latch remains reachable even if it will never be reached
-     at runtime.  We must redirect it to somewhere, so create basic
-     block containg __builtin_unreachable call for this reason.  */
-  else
-    {
-      latch = loop->latch;
-      latch_edge = loop_latch_edge (loop);
-      flags = latch_edge->flags;
-      locus = latch_edge->goto_locus;
 
-      /* Unloop destroys the latch edge.  */
-      unloop (loop, irred_invalidated, loop_closed_ssa_invalidated);
-
-      /* Create new basic block for the latch edge destination and wire
-	 it in.  */
-      stmt = gimple_build_call (builtin_decl_implicit (BUILT_IN_UNREACHABLE), 0);
-      latch_edge = make_edge (latch, create_basic_block (NULL, NULL, latch), flags);
-      latch_edge->probability = 0;
-      latch_edge->count = 0;
-      latch_edge->flags |= flags;
-      latch_edge->goto_locus = locus;
-
-      latch_edge->dest->loop_father = current_loops->tree_root;
-      latch_edge->dest->count = 0;
-      latch_edge->dest->frequency = 0;
-      set_immediate_dominator (CDI_DOMINATORS, latch_edge->dest, latch_edge->src);
-
-      gsi = gsi_start_bb (latch_edge->dest);
-      gsi_insert_after (&gsi, stmt, GSI_NEW_STMT);
-    }
+  /* Store the loop for later unlooping and exit removal.  */
+  VEC_safe_push (loop_p, heap, loops_to_unloop, loop);
+  VEC_safe_push (int, heap, loops_to_unloop_nunroll, n_unroll);
 
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
@@ -614,19 +746,17 @@ try_unroll_loop_completely (struct loop *loop,
    CREATE_IV is true if we may create a new iv.  UL determines
    which loops we are allowed to completely unroll.  If TRY_EVAL is true, we try
    to determine the number of iterations of a loop by direct evaluation.
-   Returns true if cfg is changed.  
-
-   IRRED_INVALIDATED is used to keep if irreducible reginos needs to be recomputed.  */
+   Returns true if cfg is changed.   */
 
 static bool
 canonicalize_loop_induction_variables (struct loop *loop,
 				       bool create_iv, enum unroll_level ul,
-				       bool try_eval,
-				       bool *irred_invalidated,
-				       bitmap loop_closed_ssa_invalidated)
+				       bool try_eval)
 {
   edge exit = NULL;
   tree niter;
+  HOST_WIDE_INT maxiter;
+  bool modified = false;
 
   niter = number_of_latch_executions (loop);
   if (TREE_CODE (niter) == INTEGER_CST)
@@ -657,6 +787,9 @@ canonicalize_loop_induction_variables (struct loop *loop,
   if (niter && TREE_CODE (niter) == INTEGER_CST)
     record_niter_bound (loop, tree_to_double_int (niter), false, true);
 
+  /* Force re-computation of loop bounds so we can remove redundant exits.  */
+  maxiter = max_loop_iterations_int (loop);
+
   if (dump_file && (dump_flags & TDF_DETAILS)
       && TREE_CODE (niter) == INTEGER_CST)
     {
@@ -665,21 +798,25 @@ canonicalize_loop_induction_variables (struct loop *loop,
       fprintf (dump_file, " times.\n");
     }
   if (dump_file && (dump_flags & TDF_DETAILS)
-      && max_loop_iterations_int (loop) >= 0)
+      && maxiter >= 0)
     {
       fprintf (dump_file, "Loop %d iterates at most %i times.\n", loop->num,
-	       (int)max_loop_iterations_int (loop));
+	       (int)maxiter);
     }
 
-  if (try_unroll_loop_completely (loop, exit, niter, ul, irred_invalidated,
-				  loop_closed_ssa_invalidated))
+  /* Remove exits that are known to be never taken based on loop bound.
+     Needs to be called after compilation of max_loop_iterations_int that
+     populates the loop bounds.  */
+  modified |= remove_redundant_iv_tests (loop);
+
+  if (try_unroll_loop_completely (loop, exit, niter, ul, maxiter))
     return true;
 
   if (create_iv
       && niter && !chrec_contains_undetermined (niter))
     create_canonical_iv (loop, exit, niter);
 
-  return false;
+  return modified;
 }
 
 /* The main entry point of the pass.  Adds canonical induction variables
@@ -694,16 +831,18 @@ canonicalize_induction_variables (void)
   bool irred_invalidated = false;
   bitmap loop_closed_ssa_invalidated = BITMAP_ALLOC (NULL);
 
-  FOR_EACH_LOOP (li, loop, 0)
+  free_numbers_of_iterations_estimates ();
+  estimate_numbers_of_iterations ();
+
+  FOR_EACH_LOOP (li, loop, LI_FROM_INNERMOST)
     {
       changed |= canonicalize_loop_induction_variables (loop,
 							true, UL_SINGLE_ITER,
-							true,
-							&irred_invalidated,
-							loop_closed_ssa_invalidated);
+							true);
     }
   gcc_assert (!need_ssa_update_p (cfun));
 
+  unloop_loops (loop_closed_ssa_invalidated, &irred_invalidated);
   if (irred_invalidated
       && loops_state_satisfies_p (LOOPS_HAVE_MARKED_IRREDUCIBLE_REGIONS))
     mark_irreducible_loops ();
@@ -822,7 +961,10 @@ tree_unroll_loops_completely (bool may_increase_size, bool unroll_outer)
       if (loops_state_satisfies_p (LOOP_CLOSED_SSA))
 	loop_closed_ssa_invalidated = BITMAP_ALLOC (NULL);
 
-      FOR_EACH_LOOP (li, loop, 0)
+      free_numbers_of_iterations_estimates ();
+      estimate_numbers_of_iterations ();
+
+      FOR_EACH_LOOP (li, loop, LI_FROM_INNERMOST)
 	{
 	  struct loop *loop_father = loop_outer (loop);
 
@@ -835,8 +977,7 @@ tree_unroll_loops_completely (bool may_increase_size, bool unroll_outer)
 	    ul = UL_NO_GROWTH;
 
 	  if (canonicalize_loop_induction_variables
-		 (loop, false, ul, !flag_tree_loop_ivcanon,
-		  &irred_invalidated, loop_closed_ssa_invalidated))
+		 (loop, false, ul, !flag_tree_loop_ivcanon))
 	    {
 	      changed = true;
 	      /* If we'll continue unrolling, we need to propagate constants
@@ -856,8 +997,16 @@ tree_unroll_loops_completely (bool may_increase_size, bool unroll_outer)
 	  struct loop **iter;
 	  unsigned i;
 
-	  /* We can not use TODO_update_ssa_no_phi because VOPS gets confused.  */
+	  /* Be sure to skip unlooped loops while procesing father_stack
+	     array.  */
+	  FOR_EACH_VEC_ELT (loop_p, loops_to_unloop, i, iter)
+	    (*iter)->aux = NULL;
+	  FOR_EACH_VEC_ELT (loop_p, father_stack, i, iter)
+	    if (!(*iter)->aux)
+	      *iter = NULL;
+          unloop_loops (loop_closed_ssa_invalidated, &irred_invalidated);
 
+	  /* We can not use TODO_update_ssa_no_phi because VOPS gets confused.  */
 	  if (loop_closed_ssa_invalidated
 	      && !bitmap_empty_p (loop_closed_ssa_invalidated))
             rewrite_into_loop_closed_ssa (loop_closed_ssa_invalidated,
@@ -867,14 +1016,15 @@ tree_unroll_loops_completely (bool may_increase_size, bool unroll_outer)
 
 	  /* Propagate the constants within the new basic blocks.  */
 	  FOR_EACH_VEC_ELT (loop_p, father_stack, i, iter)
-	    {
-	      unsigned j;
-	      basic_block *body = get_loop_body_in_dom_order (*iter);
-	      for (j = 0; j < (*iter)->num_nodes; j++)
-		propagate_constants_for_unrolling (body[j]);
-	      free (body);
-	      (*iter)->aux = NULL;
-	    }
+	    if (*iter)
+	      {
+		unsigned j;
+		basic_block *body = get_loop_body_in_dom_order (*iter);
+		for (j = 0; j < (*iter)->num_nodes; j++)
+		  propagate_constants_for_unrolling (body[j]);
+		free (body);
+		(*iter)->aux = NULL;
+	      }
 	  VEC_truncate (loop_p, father_stack, 0);
 
 	  /* This will take care of removing completely unrolled loops
diff --git a/gcc/tree-ssa-loop-niter.c b/gcc/tree-ssa-loop-niter.c
index 20681a9140a33b4837a2ed5b507d2d13fce84b9c..b77bcbbe5bade64ea315c2fa20ad3008689829cd 100644
--- a/gcc/tree-ssa-loop-niter.c
+++ b/gcc/tree-ssa-loop-niter.c
@@ -1880,6 +1880,10 @@ number_of_iterations_exit (struct loop *loop, edge exit,
 
   fold_undefer_and_ignore_overflow_warnings ();
 
+  /* If NITER has simplified into a constant, update MAX.  */
+  if (TREE_CODE (niter->niter) == INTEGER_CST)
+    niter->max = tree_to_double_int (niter->niter);
+
   if (integer_onep (niter->assumptions))
     return true;
 
@@ -2556,6 +2560,8 @@ record_estimate (struct loop *loop, tree bound, double_int i_bound,
      real number of iterations.  */
   if (TREE_CODE (bound) != INTEGER_CST)
     realistic = false;
+  else
+    gcc_checking_assert (i_bound == tree_to_double_int (bound));
   if (!upper && !realistic)
     return;