On 11/19/21 11:00, Richard Biener wrote:
On Tue, Nov 16, 2021 at 3:40 PM Martin Liška <mli...@suse.cz> wrote:

On 11/11/21 08:15, Richard Biener wrote:
So I'd try to do no functional change first, improving the costing and
setting up the transform to simply pick up the stmts to "fold" as discovered
during analysis (as I hinted you possibly can use gimple_uid to mark
the stmts that simplify, IIRC gimple_uid is preserved during copying.
gimple_uid would also scale better than gimple_plf in case we do
the analysis for all candidates at once).

Thinking about the analysis. Am I correct that we want to properly calculate
loop size for true and false edge of a potential gcond before the actually 
unswitching?

Yes.

We can do that by finding a first gcond candidate, evaluate (symbolic + irange 
approache)
all other gcond in the loop body and use BB_REACHABLE discovery. Similarly to 
what we do now
at lines 378-446. Then tree_num_loop_insns can be adjusted for only these 
reachable blocks.
Having that, we can calculate # of insns that will live in true/false loops.

So whatever we do here we should record as "this control stmt folds to
{true,false}" (or {true,unknown},
or in future, "this control stmt will lead to edge {e,unknown}"),
recording the simplification
on the true/false loop version in a way we can apply it after the transform.

Then we can call tree_unswitch_loop and make the gcond folding as we do in the 
versioned loops.

Is it a step in good direction? Having that we can then extend it to gswitch 
statements.

One issue I see is that BB_REACHABLE is there only once but you could use
auto_bb_flag reachable_true, reachable_false to distinguish the
true/false loop version
copies.

So yes, I think that sounds reasonable.  At the point we want to
evaluate different
(first) unswitching opportunities against each other storing this only
as BB flag is
likely to hit limits.  When we want to evaluate multiple levels of
unswitching before
doing any transforms even more so (if there are 3 opportunities there'd be
many cases to be considered when going to level 3 ;)).  I _think_ that a sparse
lattice of stmt UID -> edge might do the trick if we change tree_num_loop_insns
do to a DFS walk from the loop header, ignoring not taken edges by
consulting the
lattice.  Or, for speed reason, pre-compute tree_num_loop_insns for each BB
so we just have to sum a different set of BBs rather than walking all
stmts again.

That said, the second step would definitely be to choose the "best" opportunity
on the current level.

Richard.

Cheers,
Martin

Hello.

I'm sending a new version where I changed:
1) all unswitch_predicates are find for a loop
2) context sensitive costing happens based on an unswitch_predicate and BB 
reachability
   is implemented
3) folding happens in recursive invocation once we decide to unswitch
4) the patch folds both symbolic gcond predicates and irange provided by ranger
5) debug counter was added

Patch can bootstrap on x86_64-linux-gnu and survives regression tests. Plus, I 
tested it
on SPEC2006 and SPEC2017 with -size=ref.

Thoughts?
Thanks,
Martin

From 021cac1f6a4c8c0c049f015ab9adce5520cf0f28 Mon Sep 17 00:00:00 2001
From: Martin Liska <mli...@suse.cz>
Date: Mon, 22 Nov 2021 13:54:20 +0100
Subject: [PATCH] Loop unswitching: improve costing model

gcc/ChangeLog:

	* dbgcnt.def (loop_unswitch): Add new debug counter.
	* tree-ssa-loop-unswitch.c (struct unswitch_predicate):
	New struct.
	(tree_unswitch_single_loop): First, do costing evaluation before
	we unswitch.
	(tree_may_unswitch_on): Return unswitch_predicate instead of
	tree.
	(tree_ssa_unswitch_loops): Initialize and disable ranger.
	(simplify_using_entry_checks): Consider both symbolic
	expressions and iranges.
	(find_all_unswitching_predicates): New.
	(evaluate_insns): New.
	(evaluate_loop_insns_for_predicate): New.

gcc/testsuite/ChangeLog:

	* gcc.dg/loop-unswitch-8.c: New test.
	* gcc.dg/loop-unswitch-9.c: New test.
---
 gcc/dbgcnt.def                         |   1 +
 gcc/testsuite/gcc.dg/loop-unswitch-8.c |  31 ++
 gcc/testsuite/gcc.dg/loop-unswitch-9.c |  27 ++
 gcc/tree-ssa-loop-unswitch.c           | 468 +++++++++++++++----------
 4 files changed, 338 insertions(+), 189 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/loop-unswitch-8.c
 create mode 100644 gcc/testsuite/gcc.dg/loop-unswitch-9.c

diff --git a/gcc/dbgcnt.def b/gcc/dbgcnt.def
index f8a15f3d1d1..278fb1112b3 100644
--- a/gcc/dbgcnt.def
+++ b/gcc/dbgcnt.def
@@ -187,6 +187,7 @@ DEBUG_COUNTER (ira_move)
 DEBUG_COUNTER (ivopts_loop)
 DEBUG_COUNTER (lim)
 DEBUG_COUNTER (local_alloc_for_sched)
+DEBUG_COUNTER (loop_unswitch)
 DEBUG_COUNTER (match)
 DEBUG_COUNTER (merged_ipa_icf)
 DEBUG_COUNTER (phiopt_edge_range)
diff --git a/gcc/testsuite/gcc.dg/loop-unswitch-8.c b/gcc/testsuite/gcc.dg/loop-unswitch-8.c
new file mode 100644
index 00000000000..7738a24d849
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/loop-unswitch-8.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -funswitch-loops -fdump-tree-unswitch-details" } */
+
+int
+foo(double *a, double *b, double *c, double *d, double *r, int size, int order)
+{
+  for (int i = 0; i < size; i++)
+  {
+    double tmp;
+
+    if (order < 3)
+      tmp = -8 * a[i];
+    else
+      tmp = -4 * b[i];
+
+    double x = 3 * tmp + d[i] + tmp;
+
+    if (5 > order)
+      x += 2;
+
+    if (order == 12345)
+      x *= 5;
+
+    double y = 3.4f * tmp + d[i];
+    r[i] = x + y;
+  }
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times ";; Unswitching loop with condition: order" 3 "unswitch" } } */
diff --git a/gcc/testsuite/gcc.dg/loop-unswitch-9.c b/gcc/testsuite/gcc.dg/loop-unswitch-9.c
new file mode 100644
index 00000000000..46c93605dc8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/loop-unswitch-9.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -funswitch-loops -fdump-tree-unswitch-details" } */
+
+int
+foo(double *a, double *b, double *c, double *d, double *r, int size, int order)
+{
+  for (int i = 0; i < size; i++)
+  {
+    double tmp;
+
+    if (order == 1)
+      tmp = -8 * a[i];
+    else
+      {
+	if (order == 2)
+	  tmp = -4 * b[i];
+	else
+	  tmp = a[i];
+      }
+
+    r[i] = 3.4f * tmp + d[i];
+  }
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times ";; Unswitching loop with condition: order" 2 "unswitch" } } */
diff --git a/gcc/tree-ssa-loop-unswitch.c b/gcc/tree-ssa-loop-unswitch.c
index fe4dacc0833..6895e5b8bb9 100644
--- a/gcc/tree-ssa-loop-unswitch.c
+++ b/gcc/tree-ssa-loop-unswitch.c
@@ -37,6 +37,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-iterator.h"
 #include "cfghooks.h"
 #include "tree-ssa-loop-manip.h"
+#include "tree-pretty-print.h"
+#include "gimple-range.h"
+#include "dbgcnt.h"
 
 /* This file implements the loop unswitching, i.e. transformation of loops like
 
@@ -74,9 +77,27 @@ along with GCC; see the file COPYING3.  If not see
    tree-ssa-loop-im.c ensures that all the suitable conditions are in this
    shape.  */
 
+/* A tuple that holds GIMPLE condition and value range for an unswitching
+   predicate.  */
+
+struct unswitch_predicate
+{
+  /* Default constructor.  */
+  unswitch_predicate (gcond *s, tree cond)
+  : stmt (s), condition (cond), true_range (), false_range ()
+  {}
+
+  gimple *stmt;
+  tree condition;
+  int_range_max true_range;
+  int_range_max false_range;
+};
+
 static class loop *tree_unswitch_loop (class loop *, basic_block, tree);
-static bool tree_unswitch_single_loop (class loop *, int);
-static tree tree_may_unswitch_on (basic_block, class loop *);
+static bool tree_unswitch_single_loop (class loop *, int, gimple_ranger *,
+				       unswitch_predicate *, bool);
+static unswitch_predicate *tree_may_unswitch_on (basic_block, class loop *,
+						 gimple_ranger *);
 static bool tree_unswitch_outer_loop (class loop *);
 static edge find_loop_guard (class loop *);
 static bool empty_bb_without_guard_p (class loop *, basic_block);
@@ -92,16 +113,20 @@ tree_ssa_unswitch_loops (void)
 {
   bool changed = false;
 
+  gimple_ranger *ranger = enable_ranger (cfun);
+
   /* Go through all loops starting from innermost.  */
   for (auto loop : loops_list (cfun, LI_FROM_INNERMOST))
     {
       if (!loop->inner)
 	/* Unswitch innermost loop.  */
-	changed |= tree_unswitch_single_loop (loop, 0);
+	changed |= tree_unswitch_single_loop (loop, 0, ranger, NULL, false);
       else
 	changed |= tree_unswitch_outer_loop (loop);
     }
 
+  disable_ranger (cfun);
+
   if (changed)
     return TODO_cleanup_cfg;
   return 0;
@@ -182,10 +207,12 @@ is_maybe_undefined (const tree name, gimple *stmt, class loop *loop)
 }
 
 /* Checks whether we can unswitch LOOP on condition at end of BB -- one of its
-   basic blocks (for what it means see comments below).  */
+   basic blocks (for what it means see comments below).
+   RANGER is gimple ranger used in this pass and unswitch_predicate is returned
+   if there is an opportunity for unswitching.  */
 
-static tree
-tree_may_unswitch_on (basic_block bb, class loop *loop)
+static unswitch_predicate *
+tree_may_unswitch_on (basic_block bb, class loop *loop, gimple_ranger *ranger)
 {
   gimple *last, *def;
   gcond *stmt;
@@ -196,14 +223,14 @@ tree_may_unswitch_on (basic_block bb, class loop *loop)
   /* BB must end in a simple conditional jump.  */
   last = last_stmt (bb);
   if (!last || gimple_code (last) != GIMPLE_COND)
-    return NULL_TREE;
+    return NULL;
   stmt = as_a <gcond *> (last);
 
   /* To keep the things simple, we do not directly remove the conditions,
      but just replace tests with 0 != 0 resp. 1 != 0.  Prevent the infinite
      loop where we would unswitch again on such a condition.  */
   if (gimple_cond_true_p (stmt) || gimple_cond_false_p (stmt))
-    return NULL_TREE;
+    return NULL;
 
   /* Condition must be invariant.  */
   FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
@@ -212,64 +239,224 @@ tree_may_unswitch_on (basic_block bb, class loop *loop)
       def_bb = gimple_bb (def);
       if (def_bb
 	  && flow_bb_inside_loop_p (loop, def_bb))
-	return NULL_TREE;
+	return NULL;
       /* Unswitching on undefined values would introduce undefined
 	 behavior that the original program might never exercise.  */
       if (is_maybe_undefined (use, stmt, loop))
-	return NULL_TREE;
+	return NULL;
     }
 
-  cond = build2 (gimple_cond_code (stmt), boolean_type_node,
-		 gimple_cond_lhs (stmt), gimple_cond_rhs (stmt));
+  tree lhs = gimple_cond_lhs (stmt);
+  tree rhs = gimple_cond_rhs (stmt);
+
+  cond = build2 (gimple_cond_code (stmt), boolean_type_node, lhs, rhs);
+  edge edge_true, edge_false;
+  extract_true_false_edges_from_block (bb, &edge_true, &edge_false);
 
-  return cond;
+  unswitch_predicate *predicate = new unswitch_predicate (stmt, cond);
+  if (irange::supports_type_p (TREE_TYPE (lhs)) && CONSTANT_CLASS_P (rhs))
+    {
+      ranger->range_on_edge (predicate->true_range, edge_true, lhs);
+      predicate->false_range = predicate->true_range;
+
+      if (!predicate->false_range.varying_p ()
+	  && !predicate->false_range.undefined_p ())
+	predicate->false_range.invert ();
+    }
+
+  return predicate;
 }
 
-/* Simplifies COND using checks in front of the entry of the LOOP.  Just very
-   simplish (sufficient to prevent us from duplicating loop in unswitching
-   unnecessarily).  */
+/* Simplifies COND using checks in front of the entry of the LOOP.
+   Utilize both symbolic expressions and value ranges calculated by Ranger.  */
 
 static tree
-simplify_using_entry_checks (class loop *loop, tree cond)
+simplify_using_entry_checks (unswitch_predicate *predicate,
+			     unswitch_predicate *parent_predicate,
+			     bool true_edge)
 {
-  edge e = loop_preheader_edge (loop);
-  gimple *stmt;
+  if (parent_predicate == NULL)
+    return NULL_TREE;
+
+  gimple *stmt = predicate->stmt;
+  tree cond = parent_predicate->condition;
+
+  if (gimple_code (stmt) == GIMPLE_COND
+      && operand_equal_p (gimple_cond_lhs (stmt),
+			  TREE_OPERAND (cond, 0), 0))
+	{
+	  if (gimple_cond_code (stmt) == TREE_CODE (cond)
+	      && operand_equal_p (gimple_cond_rhs (stmt),
+				  TREE_OPERAND (cond, 1), 0))
+	    return true_edge ? boolean_true_node : boolean_false_node;
+	  else if (irange::supports_type_p (TREE_TYPE (gimple_cond_lhs (stmt))))
+	    {
+	      int_range_max r;
+	      irange &parent_range = (true_edge ? parent_predicate->true_range :
+				      parent_predicate->false_range);
+	      if (!parent_range.undefined_p ()
+		  && fold_range (r, stmt, parent_range)
+		  && r.singleton_p ())
+		return r.zero_p () ? boolean_false_node : boolean_true_node;
+	    }
+	}
+
+  return NULL_TREE;
+}
+
+/* Find all unswitching predicates for a LOOP that contains BBS.
+   TRUE_EDGE distinguish which PARENT_PREDICATE should be used when
+   asking RANGER infrastructure.  Return unswitch_predicate in the provided
+   CANDIDATES vector.  */
+
+static bool
+find_all_unswitching_predicates (class loop *loop, basic_block *bbs,
+				 bool true_edge,
+				 unswitch_predicate *parent_predicate,
+				 gimple_ranger *ranger,
+				 vec<unswitch_predicate *> &candidates)
+{
+  bool changed = false;
 
-  while (1)
+  for (unsigned i = 0; i != loop->num_nodes; i++)
     {
-      stmt = last_stmt (e->src);
-      if (stmt
-	  && gimple_code (stmt) == GIMPLE_COND
-	  && gimple_cond_code (stmt) == TREE_CODE (cond)
-	  && operand_equal_p (gimple_cond_lhs (stmt),
-			      TREE_OPERAND (cond, 0), 0)
-	  && operand_equal_p (gimple_cond_rhs (stmt),
-			      TREE_OPERAND (cond, 1), 0))
-	return (e->flags & EDGE_TRUE_VALUE
-		? boolean_true_node
-		: boolean_false_node);
-
-      if (!single_pred_p (e->src))
-	return cond;
-
-      e = single_pred_edge (e->src);
-      if (e->src == ENTRY_BLOCK_PTR_FOR_FN (cfun))
-	return cond;
+      /* Find a bb to unswitch on.  */
+      unswitch_predicate *predicate = tree_may_unswitch_on (bbs[i], loop,
+							    ranger);
+      if (predicate == NULL)
+	continue;
+
+      tree folded = simplify_using_entry_checks (predicate,
+						 parent_predicate, true_edge);
+      gimple *stmt = last_stmt (bbs[i]);
+      if (folded != NULL_TREE)
+	{
+	  /* Remove path.  */
+	  gcond *cond = dyn_cast<gcond *> (stmt);
+	  if (integer_nonzerop (folded))
+	    gimple_cond_set_condition_from_tree (cond, boolean_true_node);
+	  else
+	    gimple_cond_set_condition_from_tree (cond, boolean_false_node);
+
+	  update_stmt (cond);
+	  delete predicate;
+	  predicate = NULL;
+	  changed = true;
+	}
+      else
+	candidates.safe_push (predicate);
     }
+
+  return changed;
+}
+
+/* Evaluate how many instructions will be executed if we unswitch
+   LOOP (with BBS) based on PREDICATE.  TRUE_EDGE distinguishes if
+   we calculate taken or not taken edge when asking RANGER.
+   REACHABLE_FLAG is used for marking of the basic blocks.  */
+
+static unsigned
+evaluate_insns (class loop *loop,  basic_block *bbs,
+		unswitch_predicate *candidate, bool true_edge,
+		gimple_ranger *ranger, auto_bb_flag &reachable_flag)
+{
+  auto_vec<basic_block> worklist (loop->num_nodes);
+  worklist.quick_push (bbs[0]);
+
+  while (!worklist.is_empty ())
+    {
+      edge e;
+      edge_iterator ei;
+      int flags = 0;
+      basic_block bb = worklist.pop ();
+
+      if (EDGE_COUNT (bb->succs) == 2)
+	{
+	  gcond *cond = dyn_cast<gcond *> (last_stmt (bb));
+	  if (cond != NULL)
+	    {
+	      if (gimple_cond_true_p (cond))
+		flags = EDGE_FALSE_VALUE;
+	      else if (gimple_cond_false_p (cond))
+		flags = EDGE_TRUE_VALUE;
+	      else
+		{
+		  unswitch_predicate *predicate
+		    = tree_may_unswitch_on (bb, loop, ranger);
+		  if (predicate != NULL)
+		    {
+		      tree folded
+			= simplify_using_entry_checks (predicate, candidate,
+						       true_edge);
+		      if (folded == boolean_true_node)
+			flags = EDGE_FALSE_VALUE;
+		      else if (folded == boolean_false_node)
+			flags = EDGE_TRUE_VALUE;
+
+		      delete predicate;
+		    }
+		}
+	    }
+	}
+
+      FOR_EACH_EDGE (e, ei, bb->succs)
+	{
+	  basic_block dest = e->dest;
+
+	  if (dest->loop_father == loop
+	      && !(dest->flags & reachable_flag)
+	      && !(e->flags & flags))
+	    {
+	      worklist.safe_push (dest);
+	      dest->flags |= reachable_flag;
+	    }
+	}
+    }
+
+  /* Evaluate insns.  */
+  unsigned size = 0;
+
+  for (unsigned i = 0; i < loop->num_nodes; i++)
+    if (bbs[i]->flags & reachable_flag)
+      for (gimple_stmt_iterator gsi = gsi_start_bb (bbs[i]); !gsi_end_p (gsi);
+	   gsi_next (&gsi))
+	size += estimate_num_insns (gsi_stmt (gsi), &eni_size_weights);
+
+  /* Clear the flag from basic blocks.  */
+  for (unsigned i = 0; i < loop->num_nodes; i++)
+    bbs[i]->flags &= ~reachable_flag;
+
+  return size;
+}
+
+/* Evaluate how many instruction will we have if we unswitch LOOP (with BBS)
+   based on CANDIDATE predicate (using RANGER infrastructure).  */
+
+static unsigned
+evaluate_loop_insns_for_predicate (class loop *loop, basic_block *bbs,
+				   gimple_ranger *ranger,
+				   unswitch_predicate *candidate)
+{
+  auto_bb_flag reachable_flag (cfun);
+
+  unsigned true_loop_cost = evaluate_insns (loop, bbs, candidate, true,
+					    ranger, reachable_flag);
+  unsigned false_loop_cost = evaluate_insns (loop, bbs, candidate, false,
+					     ranger, reachable_flag);
+
+  return true_loop_cost + false_loop_cost;
 }
 
 /* Unswitch single LOOP.  NUM is number of unswitchings done; we do not allow
    it to grow too much, it is too easy to create example on that the code would
-   grow exponentially.  */
+   grow exponentially.  RANGER is gimple ranger used in this pass.  */
 
 static bool
-tree_unswitch_single_loop (class loop *loop, int num)
+tree_unswitch_single_loop (class loop *loop, int num, gimple_ranger *ranger,
+			   unswitch_predicate *parent_predicate, bool true_edge)
 {
   basic_block *bbs;
   class loop *nloop;
-  unsigned i, found;
-  tree cond = NULL_TREE;
-  gimple *stmt;
   bool changed = false;
   HOST_WIDE_INT iterations;
 
@@ -284,15 +471,6 @@ tree_unswitch_single_loop (class loop *loop, int num)
 	  return false;
 	}
 
-      /* The loop should not be too large, to limit code growth. */
-      if (tree_num_loop_insns (loop, &eni_size_weights)
-	  > (unsigned) param_max_unswitch_insns)
-	{
-	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    fprintf (dump_file, ";; Not unswitching, loop too big\n");
-	  return false;
-	}
-
       /* If the loop is not expected to iterate, there is no need
 	 for unswitching.  */
       iterations = estimated_loop_iterations_int (loop);
@@ -307,166 +485,78 @@ tree_unswitch_single_loop (class loop *loop, int num)
 	}
     }
 
-  i = 0;
   bbs = get_loop_body (loop);
-  found = loop->num_nodes;
+  auto_vec<unswitch_predicate *> candidates;
 
-  while (1)
-    {
-      /* Find a bb to unswitch on.  */
-      for (; i < loop->num_nodes; i++)
-	if ((cond = tree_may_unswitch_on (bbs[i], loop)))
-	  break;
+  changed = find_all_unswitching_predicates (loop, bbs, true_edge,
+					     parent_predicate, ranger,
+					     candidates);
 
-      if (i == loop->num_nodes)
-	{
-	  if (dump_file
-	      && num > param_max_unswitch_level
-	      && (dump_flags & TDF_DETAILS))
-	    fprintf (dump_file, ";; Not unswitching anymore, hit max level\n");
+  unswitch_predicate *predicate = NULL;
+  if (num > param_max_unswitch_level)
+    {
+      if (dump_file
+	  && (dump_flags & TDF_DETAILS))
+	fprintf (dump_file, ";; Not unswitching anymore, hit max level\n");
+      goto exit;
+    }
 
-	  if (found == loop->num_nodes)
-	    {
-	      free (bbs);
-	      return changed;
-	    }
-	  break;
-	}
+  for (auto pred: candidates)
+    {
+      unsigned cost
+	= evaluate_loop_insns_for_predicate (loop, bbs, ranger, pred);
 
-      cond = simplify_using_entry_checks (loop, cond);
-      stmt = last_stmt (bbs[i]);
-      if (integer_nonzerop (cond))
+      if (cost <= (unsigned)param_max_unswitch_insns)
 	{
-	  /* Remove false path.  */
-	  gimple_cond_set_condition_from_tree (as_a <gcond *> (stmt),
-					       boolean_true_node);
-	  changed = true;
-	}
-      else if (integer_zerop (cond))
-	{
-	  /* Remove true path.  */
-	  gimple_cond_set_condition_from_tree (as_a <gcond *> (stmt),
-					       boolean_false_node);
-	  changed = true;
-	}
-      /* Do not unswitch too much.  */
-      else if (num > param_max_unswitch_level)
-	{
-	  i++;
-	  continue;
-	}
-      /* In nested tree_unswitch_single_loop first optimize all conditions
-	 using entry checks, then discover still reachable blocks in the
-	 loop and find the condition only among those still reachable bbs.  */
-      else if (num != 0)
-	{
-	  if (found == loop->num_nodes)
-	    found = i;
-	  i++;
-	  continue;
+	  predicate = pred;
+	  break;
 	}
-      else
+      else if (dump_file && (dump_flags & TDF_DETAILS))
 	{
-	  found = i;
-	  break;
+	  fprintf (dump_file, ";; Not unswitching condition, loop too big "
+		   "(%d insns): ", cost);
+	  print_generic_expr (dump_file, pred->condition);
+	  fprintf (dump_file, "\n");
 	}
-
-      update_stmt (stmt);
-      i++;
     }
 
-  if (num != 0)
+  if (predicate != NULL)
     {
-      basic_block *tos, *worklist;
-
-      /* When called recursively, first do a quick discovery
-	 of reachable bbs after the above changes and only
-	 consider conditions in still reachable bbs.  */
-      tos = worklist = XNEWVEC (basic_block, loop->num_nodes);
-
-      for (i = 0; i < loop->num_nodes; i++)
-	bbs[i]->flags &= ~BB_REACHABLE;
-
-      /* Start with marking header.  */
-      *tos++ = bbs[0];
-      bbs[0]->flags |= BB_REACHABLE;
+      if (!dbg_cnt (loop_unswitch))
+	goto exit;
 
-      /* Iterate: find everything reachable from what we've already seen
-	 within the same innermost loop.  Don't look through false edges
-	 if condition is always true or true edges if condition is
-	 always false.  */
-      while (tos != worklist)
+      if (dump_file && (dump_flags & TDF_DETAILS))
 	{
-	  basic_block b = *--tos;
-	  edge e;
-	  edge_iterator ei;
-	  int flags = 0;
-
-	  if (EDGE_COUNT (b->succs) == 2)
-	    {
-	      gimple *stmt = last_stmt (b);
-	      if (stmt
-		  && gimple_code (stmt) == GIMPLE_COND)
-		{
-		  gcond *cond_stmt = as_a <gcond *> (stmt);
-		  if (gimple_cond_true_p (cond_stmt))
-		    flags = EDGE_FALSE_VALUE;
-		  else if (gimple_cond_false_p (cond_stmt))
-		    flags = EDGE_TRUE_VALUE;
-		}
-	    }
-
-	  FOR_EACH_EDGE (e, ei, b->succs)
-	    {
-	      basic_block dest = e->dest;
-
-	      if (dest->loop_father == loop
-		  && !(dest->flags & BB_REACHABLE)
-		  && !(e->flags & flags))
-		{
-		  *tos++ = dest;
-		  dest->flags |= BB_REACHABLE;
-		}
-	    }
+	  fprintf (dump_file, ";; Unswitching loop with condition: ");
+	  print_generic_expr (dump_file, predicate->condition);
+	  fprintf (dump_file, "\n");
 	}
 
-      free (worklist);
-
-      /* Find a bb to unswitch on.  */
-      for (; found < loop->num_nodes; found++)
-	if ((bbs[found]->flags & BB_REACHABLE)
-	    && (cond = tree_may_unswitch_on (bbs[found], loop)))
-	  break;
-
-      if (found == loop->num_nodes)
+      initialize_original_copy_tables ();
+      /* Unswitch the loop on this condition.  */
+      nloop = tree_unswitch_loop (loop, gimple_bb (predicate->stmt),
+				  predicate->condition);
+      if (!nloop)
 	{
-	  free (bbs);
-	  return changed;
+	  free_original_copy_tables ();
+	  goto exit;
 	}
-    }
 
-  if (dump_file && (dump_flags & TDF_DETAILS))
-    fprintf (dump_file, ";; Unswitching loop\n");
-
-  initialize_original_copy_tables ();
-  /* Unswitch the loop on this condition.  */
-  nloop = tree_unswitch_loop (loop, bbs[found], cond);
-  if (!nloop)
-    {
+      /* Update the SSA form after unswitching.  */
+      update_ssa (TODO_update_ssa);
       free_original_copy_tables ();
-      free (bbs);
-      return changed;
-    }
 
-  /* Update the SSA form after unswitching.  */
-  update_ssa (TODO_update_ssa);
-  free_original_copy_tables ();
+      /* Invoke itself on modified loops.  */
+      tree_unswitch_single_loop (nloop, num + 1, ranger, predicate, false);
+      tree_unswitch_single_loop (loop, num + 1, ranger, predicate, true);
+      changed = true;
+    }
 
-  /* Invoke itself on modified loops.  */
-  tree_unswitch_single_loop (nloop, num + 1);
-  tree_unswitch_single_loop (loop, num + 1);
+exit:
+  for (auto predicate: candidates)
+    delete predicate;
   free (bbs);
-  return true;
+  return changed;
 }
 
 /* Unswitch a LOOP w.r. to given basic block UNSWITCH_ON.  We only support
-- 
2.33.1

Reply via email to