public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/users/marxin/heads/loop-unswitch-improvement-v7)] work in progress.
@ 2021-12-08 18:26 Martin Liska
  0 siblings, 0 replies; 4+ messages in thread
From: Martin Liska @ 2021-12-08 18:26 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:652770047f12d1c4432675de2cb04eedd4460661

commit 652770047f12d1c4432675de2cb04eedd4460661
Author: Martin Liska <mliska@suse.cz>
Date:   Thu Dec 2 14:07:13 2021 +0100

    work in progress.

Diff:
---
 gcc/tree-ssa-loop-unswitch.c | 317 +++++++++++++++++++++++++++++++++----------
 1 file changed, 245 insertions(+), 72 deletions(-)

diff --git a/gcc/tree-ssa-loop-unswitch.c b/gcc/tree-ssa-loop-unswitch.c
index 42df2c4cc11..ba8f3419469 100644
--- a/gcc/tree-ssa-loop-unswitch.c
+++ b/gcc/tree-ssa-loop-unswitch.c
@@ -41,6 +41,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pretty-print.h"
 #include "gimple-range.h"
 #include "dbgcnt.h"
+#include "cfganal.h"
 
 /* This file implements the loop unswitching, i.e. transformation of loops like
 
@@ -84,14 +85,26 @@ along with GCC; see the file COPYING3.  If not see
 struct unswitch_predicate
 {
   /* Default constructor.  */
-  unswitch_predicate (tree cond, tree lhs_)
-  : condition (cond), lhs (lhs_), true_range (), false_range ()
+  unswitch_predicate (tree cond, tree lhs_, int edge_index_ = -1)
+  : condition (cond), lhs (lhs_), true_range (), false_range (),
+    edge_index (edge_index_)
   {}
 
+  inline void
+  init_false_edge (void)
+  {
+    false_range = true_range;
+
+    if (!false_range.varying_p ()
+	&& !false_range.undefined_p ())
+      false_range.invert ();
+  }
+
   tree condition;
   tree lhs;
   int_range_max true_range;
   int_range_max false_range;
+  int edge_index;
 };
 
 /* Cache storage for unswitch_predicate belonging to a basic block.  */
@@ -106,10 +119,12 @@ typedef vec<std::pair<unswitch_predicate *, bool>> predicate_vector;
 static class loop *tree_unswitch_loop (class loop *, basic_block, tree);
 static bool tree_unswitch_single_loop (class loop *, int,
 				       predicate_vector &predicate_path,
-				       unsigned budget);
+				       unsigned budget,
+				       const auto_edge_flag &ignored_edge_flag);
 static void
 find_unswitching_predicates_for_bb (basic_block bb, class loop *loop,
-				    vec<unswitch_predicate *> &candidates);
+				    vec<unswitch_predicate *> &candidates,
+				    const auto_edge_flag &ignored_edge_flag);
 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);
@@ -140,7 +155,8 @@ set_predicates_for_bb (basic_block bb, vec<unswitch_predicate *> predicates)
    Return total number of instructions in the loop.  */
 
 static unsigned
-init_loop_unswitch_info (class loop *loop)
+init_loop_unswitch_info (class loop *loop,
+			 const auto_edge_flag &ignored_edge_flag)
 {
   unsigned total_insns = 0;
 
@@ -163,7 +179,8 @@ init_loop_unswitch_info (class loop *loop)
       /* Find a bb to unswitch on.  */
       vec<unswitch_predicate *> candidates;
       candidates.create (1);
-      find_unswitching_predicates_for_bb (bbs[i], loop, candidates);
+      find_unswitching_predicates_for_bb (bbs[i], loop, candidates,
+					  ignored_edge_flag);
       if (!candidates.is_empty ())
 	set_predicates_for_bb (bbs[i], candidates);
       else
@@ -185,6 +202,7 @@ unsigned int
 tree_ssa_unswitch_loops (void)
 {
   bool changed = false;
+  auto_edge_flag ignored_edge_flag (cfun);
 
   ranger = enable_ranger (cfun);
 
@@ -198,12 +216,13 @@ tree_ssa_unswitch_loops (void)
 
 	  /* Unswitch innermost loop.  */
 	  unsigned int budget
-	    = init_loop_unswitch_info (loop) + param_max_unswitch_insns;
+	    = (init_loop_unswitch_info (loop, ignored_edge_flag)
+	       + param_max_unswitch_insns);
 
 	  predicate_vector predicate_path;
 	  predicate_path.create (8);
 	  changed |= tree_unswitch_single_loop (loop, 0, predicate_path,
-						budget);
+						budget, ignored_edge_flag);
 
 	  delete bb_predicates;
 	  bb_predicates = NULL;
@@ -301,59 +320,130 @@ is_maybe_undefined (const tree name, gimple *stmt, class loop *loop)
 
 static void
 find_unswitching_predicates_for_bb (basic_block bb, class loop *loop,
-				    vec<unswitch_predicate *> &candidates)
+				    vec<unswitch_predicate *> &candidates,
+				    const auto_edge_flag &ignored_edge_flag)
 {
   gimple *last, *def;
-  gcond *stmt;
   tree cond, use;
   basic_block def_bb;
   ssa_op_iter iter;
 
   /* BB must end in a simple conditional jump.  */
   last = last_stmt (bb);
-  if (!last || gimple_code (last) != GIMPLE_COND)
+  if (!last)
     return;
-  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;
+  if (gcond *stmt = safe_dyn_cast <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;
+
+      /* Condition must be invariant.  */
+      FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
+	{
+	  def = SSA_NAME_DEF_STMT (use);
+	  def_bb = gimple_bb (def);
+	  if (def_bb
+	      && flow_bb_inside_loop_p (loop, def_bb))
+	    return;
+	  /* Unswitching on undefined values would introduce undefined
+	     behavior that the original program might never exercise.  */
+	  if (is_maybe_undefined (use, stmt, loop))
+	    return;
+	}
+
+      tree lhs = gimple_cond_lhs (stmt);
+      tree rhs = gimple_cond_rhs (stmt);
 
-  /* Condition must be invariant.  */
-  FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
+      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);
+
+      unswitch_predicate *predicate = new unswitch_predicate (cond, lhs);
+      if (irange::supports_type_p (TREE_TYPE (lhs)) && CONSTANT_CLASS_P (rhs))
+	{
+	  ranger->range_on_edge (predicate->true_range, edge_true, lhs);
+	  predicate->init_false_edge ();
+	}
+
+      candidates.safe_push (predicate);
+    }
+  else if (gswitch *stmt = safe_dyn_cast <gswitch *> (last))
     {
-      def = SSA_NAME_DEF_STMT (use);
+      unsigned nlabels = gimple_switch_num_labels (stmt);
+      tree idx = gimple_switch_index (stmt);
+      if (TREE_CODE (idx) != SSA_NAME
+	  || nlabels < 1)
+	return;
+      /* Index must be invariant.  */
+      def = SSA_NAME_DEF_STMT (idx);
       def_bb = gimple_bb (def);
       if (def_bb
 	  && flow_bb_inside_loop_p (loop, def_bb))
 	return;
       /* Unswitching on undefined values would introduce undefined
 	 behavior that the original program might never exercise.  */
-      if (is_maybe_undefined (use, stmt, loop))
-	return;
-    }
-
-  tree lhs = gimple_cond_lhs (stmt);
-  tree rhs = gimple_cond_rhs (stmt);
+      if (is_maybe_undefined (idx, stmt, loop))
+ 	return;
 
-  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);
-
-  unswitch_predicate *predicate = new unswitch_predicate (cond, lhs);
-  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;
+      edge e;
+      edge_iterator ei;
+      unsigned edge_index = 0;
+      FOR_EACH_EDGE (e, ei, gimple_bb (stmt)->succs)
+	{
+	  if (!(e->flags & ignored_edge_flag))
+	    {
+	      /* Build compound expression for all cases leading
+		 to this edge.  */
+	      tree expr = NULL_TREE;
+	      for (unsigned i = 1; i < gimple_switch_num_labels (stmt); ++i)
+		{
+		  tree lab = gimple_switch_label (stmt, i);
+		  basic_block dest = label_to_block (cfun, CASE_LABEL (lab));
+		  edge e2 = find_edge (gimple_bb (stmt), dest);
+		  if (e == e2)
+		    {
+		      tree cmp;
+		      if (CASE_HIGH (lab) != NULL_TREE)
+			{
+			  tree cmp1 = build2 (GE_EXPR, boolean_type_node, idx,
+					      CASE_LOW (lab));
+			  tree cmp2 = build2 (LE_EXPR, boolean_type_node, idx,
+					      CASE_HIGH (lab));
+			  cmp = build2 (BIT_AND_EXPR, boolean_type_node, cmp1,
+					cmp2);
+			}
+		      else
+			cmp = build2 (EQ_EXPR, boolean_type_node, idx,
+				      CASE_LOW (lab));
+
+		      /* Combine the expression with the existing one.  */
+		      if (expr == NULL_TREE)
+			expr = cmp;
+		      else
+			expr = build2 (BIT_IOR_EXPR, boolean_type_node, expr,
+				       cmp);
+		    }
+		}
 
-      if (!predicate->false_range.varying_p ()
-	  && !predicate->false_range.undefined_p ())
-	predicate->false_range.invert ();
+	      if (expr != NULL_TREE)
+		{
+		  unswitch_predicate *predicate = new unswitch_predicate (expr, idx, edge_index);
+		  if (irange::supports_type_p (TREE_TYPE (idx)))
+		    {
+		      ranger->range_on_edge (predicate->true_range, e, idx);
+		      predicate->init_false_edge ();
+		    }
+
+		  candidates.safe_push (predicate);
+		}
+	    }
+	  edge_index++;
+	}
     }
-
-  candidates.safe_push (predicate);
 }
 
 static void
@@ -399,22 +489,44 @@ find_range_for_lhs (predicate_vector &predicate_path, tree lhs,
 
 static tree
 evaluate_control_stmt_using_entry_checks (gimple *stmt,
-					  predicate_vector &predicate_path)
+					  predicate_vector &predicate_path,
+					  hash_set<edge> *ignored_edges)
 {
+  tree lhs = NULL_TREE;
+
   if (predicate_path.is_empty ())
     return NULL_TREE;
 
-  tree lhs = gimple_cond_lhs (stmt);
+  gcond *condition = dyn_cast<gcond *> (stmt);
+  gswitch *swtch = dyn_cast<gswitch *> (stmt);
+
   unswitch_predicate *last_predicate = predicate_path.last ().first;
   bool true_edge = predicate_path.last ().second;
 
-  if (gimple_code (stmt) == GIMPLE_COND
+  if (condition != NULL
+      && (lhs = gimple_cond_lhs (stmt))
       && operand_equal_p (lhs, last_predicate->lhs, 0))
     {
       if (gimple_cond_code (stmt) == TREE_CODE (last_predicate->condition)
 	  && operand_equal_p (gimple_cond_rhs (stmt),
 			      TREE_OPERAND (last_predicate->condition, 1), 0))
-	return true_edge ? boolean_true_node : boolean_false_node;
+	{
+	  edge edge_true, edge_false;
+	  extract_true_false_edges_from_block (gimple_bb (stmt),
+					       &edge_true, &edge_false);
+	  if (true_edge)
+	    {
+	      if (ignored_edges != NULL)
+		ignored_edges->add (edge_true);
+	      return boolean_true_node;
+	    }
+	  else
+	    {
+	      if (ignored_edges != NULL)
+		ignored_edges->add (edge_false);
+	      return boolean_false_node;
+	    }
+	}
       else if (irange::supports_type_p (TREE_TYPE (lhs)))
 	{
 	  int_range_max r;
@@ -426,6 +538,40 @@ evaluate_control_stmt_using_entry_checks (gimple *stmt,
 	    return r.zero_p () ? boolean_false_node : boolean_true_node;
 	}
     }
+  else if (swtch != NULL)
+    {
+      unsigned nlabels = gimple_switch_num_labels (swtch);
+      unsigned ignored = 0;
+
+      for (unsigned i = 0; i < nlabels; ++i)
+	{
+	  tree lab = gimple_switch_label (swtch, i);
+	  basic_block dest = label_to_block (cfun, CASE_LABEL (lab));
+	  edge e = find_edge (gimple_bb (stmt), dest);
+
+	  int_range_max r;
+	  int_range_max path_range;
+	  tree idx = gimple_switch_index (swtch);
+	  find_range_for_lhs (predicate_path, idx, path_range);
+
+	  int_range_max xx;
+	  fold_range (xx, stmt, e);
+
+	  if (!path_range.undefined_p ()
+	      && fold_range (r, stmt, path_range)
+	      && r.singleton_p ()
+	      && r.zero_p ())
+	    {
+	      ignored_edges->add (e);
+	      ignored++;
+	    }
+	}
+
+      /* Only one edge from the switch is alive.  */
+      gcc_checking_assert (ignored + 1 <= nlabels);
+      if (ignored + 1 == nlabels)
+	return true_edge ? boolean_true_node : boolean_false_node;
+    }
 
   return NULL_TREE;
 }
@@ -434,24 +580,30 @@ evaluate_control_stmt_using_entry_checks (gimple *stmt,
    marked.  */
 
 static bool
-simplify_loop_version (class loop *loop, predicate_vector &predicate_path)
+simplify_loop_version (class loop *loop, predicate_vector &predicate_path,
+		       const auto_edge_flag &ignored_edge_flag)
 {
   bool changed = false;
   basic_block *bbs = get_loop_body (loop);
 
   for (unsigned i = 0; i != loop->num_nodes; i++)
     {
-      // FIXME: works only for gconds
-      if (!get_predicates_for_bb (bbs[i]).is_empty ())
+      vec<unswitch_predicate *> &predicates = get_predicates_for_bb (bbs[i]);
+      if (!predicates.is_empty ())
 	{
+	  hash_set<edge> ignored_edges;
 	  gimple *stmt = last_stmt (bbs[i]);
-
 	  tree folded = evaluate_control_stmt_using_entry_checks (stmt,
-								  predicate_path);
-	  if (folded != NULL_TREE)
+								  predicate_path,
+								  &ignored_edges);
+
+	  gcond *cond = dyn_cast<gcond *> (stmt);
+	  gswitch *swtch = dyn_cast<gswitch *> (stmt);
+
+	  if (cond != NULL
+	      && 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
@@ -461,6 +613,25 @@ simplify_loop_version (class loop *loop, predicate_vector &predicate_path)
 	      update_stmt (cond);
 	      changed = true;
 	    }
+	  else if (swtch != NULL)
+	    {
+	      for (int j = predicates.length () - 1; j>= 0; j--)
+		{
+		  edge e = EDGE_SUCC (bbs[i], predicates[j]->edge_index);
+		  if (ignored_edges.contains (e))
+		    {
+		      e->flags = ignored_edge_flag;
+		      predicates.unordered_remove (j);
+		    }
+		}
+
+	      if (folded)
+		{
+		  gimple_switch_set_index (swtch, folded);
+		  update_stmt (swtch);
+		  changed = true;
+		}
+	    }
 	}
     }
 
@@ -479,6 +650,7 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 {
   auto_vec<basic_block> worklist (loop->num_nodes);
   worklist.quick_push (bbs[0]);
+  hash_set<edge> ignored_edges;
 
   while (!worklist.is_empty ())
     {
@@ -489,6 +661,8 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 
       gimple *last = last_stmt (bb);
       gcond *cond = last != NULL ? dyn_cast<gcond *> (last) : NULL;
+      gswitch *swtch = last != NULL ? dyn_cast<gswitch *> (last) : NULL;
+
       if (cond != NULL)
 	{
 	  if (gimple_cond_true_p (cond))
@@ -497,23 +671,16 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 	    flags = EDGE_TRUE_VALUE;
 	  else
 	    {
-	      // FIXME: works only for gconds
-	      unswitch_predicate *predicate = NULL;
 	      if (!get_predicates_for_bb (bb).is_empty ())
-		predicate = get_predicates_for_bb (bb)[0];
-
-	      if (predicate != NULL)
-		{
-		  tree folded
-		    = evaluate_control_stmt_using_entry_checks (cond,
-								predicate_path);
-		  if (folded == boolean_true_node)
-		    flags = EDGE_FALSE_VALUE;
-		  else if (folded == boolean_false_node)
-		    flags = EDGE_TRUE_VALUE;
-		}
+		evaluate_control_stmt_using_entry_checks (cond,
+							  predicate_path,
+							  &ignored_edges);
 	    }
 	}
+      else if (swtch != NULL
+	       && !get_predicates_for_bb (bb).is_empty ())
+	evaluate_control_stmt_using_entry_checks (swtch, predicate_path,
+						  &ignored_edges);
 
       FOR_EACH_EDGE (e, ei, bb->succs)
 	{
@@ -521,7 +688,8 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 
 	  if (dest->loop_father == loop
 	      && !(dest->flags & reachable_flag)
-	      && !(e->flags & flags))
+	      && !(e->flags & flags)
+	      && !ignored_edges.contains (e))
 	    {
 	      worklist.safe_push (dest);
 	      dest->flags |= reachable_flag;
@@ -576,7 +744,8 @@ evaluate_loop_insns_for_predicate (class loop *loop, basic_block *bbs,
 
 static bool
 tree_unswitch_single_loop (class loop *loop, int num,
-			   predicate_vector &predicate_path, unsigned budget)
+			   predicate_vector &predicate_path, unsigned budget,
+			   const auto_edge_flag &ignored_edge_flag)
 {
   basic_block *bbs = NULL;
   class loop *nloop;
@@ -687,14 +856,18 @@ tree_unswitch_single_loop (class loop *loop, int num,
       /* Invoke itself on modified loops.  */
       predicate_path.safe_push (std::make_pair (predicate, false));
       merge_last (predicate_path);
-      changed |= simplify_loop_version (nloop, predicate_path);
-      tree_unswitch_single_loop (nloop, num + 1, predicate_path, budget);
+      changed |= simplify_loop_version (nloop, predicate_path,
+					ignored_edge_flag);
+      tree_unswitch_single_loop (nloop, num + 1, predicate_path, budget,
+				 ignored_edge_flag);
       predicate_path.pop ();
 
       predicate_path.safe_push (std::make_pair (predicate, true));
       merge_last (predicate_path);
-      changed |= simplify_loop_version (loop, predicate_path);
-      tree_unswitch_single_loop (loop, num + 1, predicate_path, budget);
+      changed |= simplify_loop_version (loop, predicate_path,
+					ignored_edge_flag);
+      tree_unswitch_single_loop (loop, num + 1, predicate_path, budget,
+				 ignored_edge_flag);
       predicate_path.pop ();
       changed = true;
     }
@@ -718,7 +891,7 @@ tree_unswitch_loop (class loop *loop,
 
   /* Some sanity checking.  */
   gcc_assert (flow_bb_inside_loop_p (loop, unswitch_on));
-  gcc_assert (EDGE_COUNT (unswitch_on->succs) == 2);
+  gcc_assert (EDGE_COUNT (unswitch_on->succs) >= 2);
   gcc_assert (loop->inner == NULL);
 
   extract_true_false_edges_from_block (unswitch_on, &edge_true, &edge_false);


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [gcc(refs/users/marxin/heads/loop-unswitch-improvement-v7)] work in progress.
@ 2021-12-09 12:48 Martin Liska
  0 siblings, 0 replies; 4+ messages in thread
From: Martin Liska @ 2021-12-09 12:48 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:071ebef81a89cec0ac52b6f82a8dd6474db3a182

commit 071ebef81a89cec0ac52b6f82a8dd6474db3a182
Author: Martin Liska <mliska@suse.cz>
Date:   Thu Dec 2 14:07:13 2021 +0100

    work in progress.

Diff:
---
 gcc/tree-ssa-loop-unswitch.c | 317 +++++++++++++++++++++++++++++++++----------
 1 file changed, 245 insertions(+), 72 deletions(-)

diff --git a/gcc/tree-ssa-loop-unswitch.c b/gcc/tree-ssa-loop-unswitch.c
index 42df2c4cc11..ba8f3419469 100644
--- a/gcc/tree-ssa-loop-unswitch.c
+++ b/gcc/tree-ssa-loop-unswitch.c
@@ -41,6 +41,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pretty-print.h"
 #include "gimple-range.h"
 #include "dbgcnt.h"
+#include "cfganal.h"
 
 /* This file implements the loop unswitching, i.e. transformation of loops like
 
@@ -84,14 +85,26 @@ along with GCC; see the file COPYING3.  If not see
 struct unswitch_predicate
 {
   /* Default constructor.  */
-  unswitch_predicate (tree cond, tree lhs_)
-  : condition (cond), lhs (lhs_), true_range (), false_range ()
+  unswitch_predicate (tree cond, tree lhs_, int edge_index_ = -1)
+  : condition (cond), lhs (lhs_), true_range (), false_range (),
+    edge_index (edge_index_)
   {}
 
+  inline void
+  init_false_edge (void)
+  {
+    false_range = true_range;
+
+    if (!false_range.varying_p ()
+	&& !false_range.undefined_p ())
+      false_range.invert ();
+  }
+
   tree condition;
   tree lhs;
   int_range_max true_range;
   int_range_max false_range;
+  int edge_index;
 };
 
 /* Cache storage for unswitch_predicate belonging to a basic block.  */
@@ -106,10 +119,12 @@ typedef vec<std::pair<unswitch_predicate *, bool>> predicate_vector;
 static class loop *tree_unswitch_loop (class loop *, basic_block, tree);
 static bool tree_unswitch_single_loop (class loop *, int,
 				       predicate_vector &predicate_path,
-				       unsigned budget);
+				       unsigned budget,
+				       const auto_edge_flag &ignored_edge_flag);
 static void
 find_unswitching_predicates_for_bb (basic_block bb, class loop *loop,
-				    vec<unswitch_predicate *> &candidates);
+				    vec<unswitch_predicate *> &candidates,
+				    const auto_edge_flag &ignored_edge_flag);
 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);
@@ -140,7 +155,8 @@ set_predicates_for_bb (basic_block bb, vec<unswitch_predicate *> predicates)
    Return total number of instructions in the loop.  */
 
 static unsigned
-init_loop_unswitch_info (class loop *loop)
+init_loop_unswitch_info (class loop *loop,
+			 const auto_edge_flag &ignored_edge_flag)
 {
   unsigned total_insns = 0;
 
@@ -163,7 +179,8 @@ init_loop_unswitch_info (class loop *loop)
       /* Find a bb to unswitch on.  */
       vec<unswitch_predicate *> candidates;
       candidates.create (1);
-      find_unswitching_predicates_for_bb (bbs[i], loop, candidates);
+      find_unswitching_predicates_for_bb (bbs[i], loop, candidates,
+					  ignored_edge_flag);
       if (!candidates.is_empty ())
 	set_predicates_for_bb (bbs[i], candidates);
       else
@@ -185,6 +202,7 @@ unsigned int
 tree_ssa_unswitch_loops (void)
 {
   bool changed = false;
+  auto_edge_flag ignored_edge_flag (cfun);
 
   ranger = enable_ranger (cfun);
 
@@ -198,12 +216,13 @@ tree_ssa_unswitch_loops (void)
 
 	  /* Unswitch innermost loop.  */
 	  unsigned int budget
-	    = init_loop_unswitch_info (loop) + param_max_unswitch_insns;
+	    = (init_loop_unswitch_info (loop, ignored_edge_flag)
+	       + param_max_unswitch_insns);
 
 	  predicate_vector predicate_path;
 	  predicate_path.create (8);
 	  changed |= tree_unswitch_single_loop (loop, 0, predicate_path,
-						budget);
+						budget, ignored_edge_flag);
 
 	  delete bb_predicates;
 	  bb_predicates = NULL;
@@ -301,59 +320,130 @@ is_maybe_undefined (const tree name, gimple *stmt, class loop *loop)
 
 static void
 find_unswitching_predicates_for_bb (basic_block bb, class loop *loop,
-				    vec<unswitch_predicate *> &candidates)
+				    vec<unswitch_predicate *> &candidates,
+				    const auto_edge_flag &ignored_edge_flag)
 {
   gimple *last, *def;
-  gcond *stmt;
   tree cond, use;
   basic_block def_bb;
   ssa_op_iter iter;
 
   /* BB must end in a simple conditional jump.  */
   last = last_stmt (bb);
-  if (!last || gimple_code (last) != GIMPLE_COND)
+  if (!last)
     return;
-  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;
+  if (gcond *stmt = safe_dyn_cast <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;
+
+      /* Condition must be invariant.  */
+      FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
+	{
+	  def = SSA_NAME_DEF_STMT (use);
+	  def_bb = gimple_bb (def);
+	  if (def_bb
+	      && flow_bb_inside_loop_p (loop, def_bb))
+	    return;
+	  /* Unswitching on undefined values would introduce undefined
+	     behavior that the original program might never exercise.  */
+	  if (is_maybe_undefined (use, stmt, loop))
+	    return;
+	}
+
+      tree lhs = gimple_cond_lhs (stmt);
+      tree rhs = gimple_cond_rhs (stmt);
 
-  /* Condition must be invariant.  */
-  FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
+      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);
+
+      unswitch_predicate *predicate = new unswitch_predicate (cond, lhs);
+      if (irange::supports_type_p (TREE_TYPE (lhs)) && CONSTANT_CLASS_P (rhs))
+	{
+	  ranger->range_on_edge (predicate->true_range, edge_true, lhs);
+	  predicate->init_false_edge ();
+	}
+
+      candidates.safe_push (predicate);
+    }
+  else if (gswitch *stmt = safe_dyn_cast <gswitch *> (last))
     {
-      def = SSA_NAME_DEF_STMT (use);
+      unsigned nlabels = gimple_switch_num_labels (stmt);
+      tree idx = gimple_switch_index (stmt);
+      if (TREE_CODE (idx) != SSA_NAME
+	  || nlabels < 1)
+	return;
+      /* Index must be invariant.  */
+      def = SSA_NAME_DEF_STMT (idx);
       def_bb = gimple_bb (def);
       if (def_bb
 	  && flow_bb_inside_loop_p (loop, def_bb))
 	return;
       /* Unswitching on undefined values would introduce undefined
 	 behavior that the original program might never exercise.  */
-      if (is_maybe_undefined (use, stmt, loop))
-	return;
-    }
-
-  tree lhs = gimple_cond_lhs (stmt);
-  tree rhs = gimple_cond_rhs (stmt);
+      if (is_maybe_undefined (idx, stmt, loop))
+ 	return;
 
-  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);
-
-  unswitch_predicate *predicate = new unswitch_predicate (cond, lhs);
-  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;
+      edge e;
+      edge_iterator ei;
+      unsigned edge_index = 0;
+      FOR_EACH_EDGE (e, ei, gimple_bb (stmt)->succs)
+	{
+	  if (!(e->flags & ignored_edge_flag))
+	    {
+	      /* Build compound expression for all cases leading
+		 to this edge.  */
+	      tree expr = NULL_TREE;
+	      for (unsigned i = 1; i < gimple_switch_num_labels (stmt); ++i)
+		{
+		  tree lab = gimple_switch_label (stmt, i);
+		  basic_block dest = label_to_block (cfun, CASE_LABEL (lab));
+		  edge e2 = find_edge (gimple_bb (stmt), dest);
+		  if (e == e2)
+		    {
+		      tree cmp;
+		      if (CASE_HIGH (lab) != NULL_TREE)
+			{
+			  tree cmp1 = build2 (GE_EXPR, boolean_type_node, idx,
+					      CASE_LOW (lab));
+			  tree cmp2 = build2 (LE_EXPR, boolean_type_node, idx,
+					      CASE_HIGH (lab));
+			  cmp = build2 (BIT_AND_EXPR, boolean_type_node, cmp1,
+					cmp2);
+			}
+		      else
+			cmp = build2 (EQ_EXPR, boolean_type_node, idx,
+				      CASE_LOW (lab));
+
+		      /* Combine the expression with the existing one.  */
+		      if (expr == NULL_TREE)
+			expr = cmp;
+		      else
+			expr = build2 (BIT_IOR_EXPR, boolean_type_node, expr,
+				       cmp);
+		    }
+		}
 
-      if (!predicate->false_range.varying_p ()
-	  && !predicate->false_range.undefined_p ())
-	predicate->false_range.invert ();
+	      if (expr != NULL_TREE)
+		{
+		  unswitch_predicate *predicate = new unswitch_predicate (expr, idx, edge_index);
+		  if (irange::supports_type_p (TREE_TYPE (idx)))
+		    {
+		      ranger->range_on_edge (predicate->true_range, e, idx);
+		      predicate->init_false_edge ();
+		    }
+
+		  candidates.safe_push (predicate);
+		}
+	    }
+	  edge_index++;
+	}
     }
-
-  candidates.safe_push (predicate);
 }
 
 static void
@@ -399,22 +489,44 @@ find_range_for_lhs (predicate_vector &predicate_path, tree lhs,
 
 static tree
 evaluate_control_stmt_using_entry_checks (gimple *stmt,
-					  predicate_vector &predicate_path)
+					  predicate_vector &predicate_path,
+					  hash_set<edge> *ignored_edges)
 {
+  tree lhs = NULL_TREE;
+
   if (predicate_path.is_empty ())
     return NULL_TREE;
 
-  tree lhs = gimple_cond_lhs (stmt);
+  gcond *condition = dyn_cast<gcond *> (stmt);
+  gswitch *swtch = dyn_cast<gswitch *> (stmt);
+
   unswitch_predicate *last_predicate = predicate_path.last ().first;
   bool true_edge = predicate_path.last ().second;
 
-  if (gimple_code (stmt) == GIMPLE_COND
+  if (condition != NULL
+      && (lhs = gimple_cond_lhs (stmt))
       && operand_equal_p (lhs, last_predicate->lhs, 0))
     {
       if (gimple_cond_code (stmt) == TREE_CODE (last_predicate->condition)
 	  && operand_equal_p (gimple_cond_rhs (stmt),
 			      TREE_OPERAND (last_predicate->condition, 1), 0))
-	return true_edge ? boolean_true_node : boolean_false_node;
+	{
+	  edge edge_true, edge_false;
+	  extract_true_false_edges_from_block (gimple_bb (stmt),
+					       &edge_true, &edge_false);
+	  if (true_edge)
+	    {
+	      if (ignored_edges != NULL)
+		ignored_edges->add (edge_true);
+	      return boolean_true_node;
+	    }
+	  else
+	    {
+	      if (ignored_edges != NULL)
+		ignored_edges->add (edge_false);
+	      return boolean_false_node;
+	    }
+	}
       else if (irange::supports_type_p (TREE_TYPE (lhs)))
 	{
 	  int_range_max r;
@@ -426,6 +538,40 @@ evaluate_control_stmt_using_entry_checks (gimple *stmt,
 	    return r.zero_p () ? boolean_false_node : boolean_true_node;
 	}
     }
+  else if (swtch != NULL)
+    {
+      unsigned nlabels = gimple_switch_num_labels (swtch);
+      unsigned ignored = 0;
+
+      for (unsigned i = 0; i < nlabels; ++i)
+	{
+	  tree lab = gimple_switch_label (swtch, i);
+	  basic_block dest = label_to_block (cfun, CASE_LABEL (lab));
+	  edge e = find_edge (gimple_bb (stmt), dest);
+
+	  int_range_max r;
+	  int_range_max path_range;
+	  tree idx = gimple_switch_index (swtch);
+	  find_range_for_lhs (predicate_path, idx, path_range);
+
+	  int_range_max xx;
+	  fold_range (xx, stmt, e);
+
+	  if (!path_range.undefined_p ()
+	      && fold_range (r, stmt, path_range)
+	      && r.singleton_p ()
+	      && r.zero_p ())
+	    {
+	      ignored_edges->add (e);
+	      ignored++;
+	    }
+	}
+
+      /* Only one edge from the switch is alive.  */
+      gcc_checking_assert (ignored + 1 <= nlabels);
+      if (ignored + 1 == nlabels)
+	return true_edge ? boolean_true_node : boolean_false_node;
+    }
 
   return NULL_TREE;
 }
@@ -434,24 +580,30 @@ evaluate_control_stmt_using_entry_checks (gimple *stmt,
    marked.  */
 
 static bool
-simplify_loop_version (class loop *loop, predicate_vector &predicate_path)
+simplify_loop_version (class loop *loop, predicate_vector &predicate_path,
+		       const auto_edge_flag &ignored_edge_flag)
 {
   bool changed = false;
   basic_block *bbs = get_loop_body (loop);
 
   for (unsigned i = 0; i != loop->num_nodes; i++)
     {
-      // FIXME: works only for gconds
-      if (!get_predicates_for_bb (bbs[i]).is_empty ())
+      vec<unswitch_predicate *> &predicates = get_predicates_for_bb (bbs[i]);
+      if (!predicates.is_empty ())
 	{
+	  hash_set<edge> ignored_edges;
 	  gimple *stmt = last_stmt (bbs[i]);
-
 	  tree folded = evaluate_control_stmt_using_entry_checks (stmt,
-								  predicate_path);
-	  if (folded != NULL_TREE)
+								  predicate_path,
+								  &ignored_edges);
+
+	  gcond *cond = dyn_cast<gcond *> (stmt);
+	  gswitch *swtch = dyn_cast<gswitch *> (stmt);
+
+	  if (cond != NULL
+	      && 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
@@ -461,6 +613,25 @@ simplify_loop_version (class loop *loop, predicate_vector &predicate_path)
 	      update_stmt (cond);
 	      changed = true;
 	    }
+	  else if (swtch != NULL)
+	    {
+	      for (int j = predicates.length () - 1; j>= 0; j--)
+		{
+		  edge e = EDGE_SUCC (bbs[i], predicates[j]->edge_index);
+		  if (ignored_edges.contains (e))
+		    {
+		      e->flags = ignored_edge_flag;
+		      predicates.unordered_remove (j);
+		    }
+		}
+
+	      if (folded)
+		{
+		  gimple_switch_set_index (swtch, folded);
+		  update_stmt (swtch);
+		  changed = true;
+		}
+	    }
 	}
     }
 
@@ -479,6 +650,7 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 {
   auto_vec<basic_block> worklist (loop->num_nodes);
   worklist.quick_push (bbs[0]);
+  hash_set<edge> ignored_edges;
 
   while (!worklist.is_empty ())
     {
@@ -489,6 +661,8 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 
       gimple *last = last_stmt (bb);
       gcond *cond = last != NULL ? dyn_cast<gcond *> (last) : NULL;
+      gswitch *swtch = last != NULL ? dyn_cast<gswitch *> (last) : NULL;
+
       if (cond != NULL)
 	{
 	  if (gimple_cond_true_p (cond))
@@ -497,23 +671,16 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 	    flags = EDGE_TRUE_VALUE;
 	  else
 	    {
-	      // FIXME: works only for gconds
-	      unswitch_predicate *predicate = NULL;
 	      if (!get_predicates_for_bb (bb).is_empty ())
-		predicate = get_predicates_for_bb (bb)[0];
-
-	      if (predicate != NULL)
-		{
-		  tree folded
-		    = evaluate_control_stmt_using_entry_checks (cond,
-								predicate_path);
-		  if (folded == boolean_true_node)
-		    flags = EDGE_FALSE_VALUE;
-		  else if (folded == boolean_false_node)
-		    flags = EDGE_TRUE_VALUE;
-		}
+		evaluate_control_stmt_using_entry_checks (cond,
+							  predicate_path,
+							  &ignored_edges);
 	    }
 	}
+      else if (swtch != NULL
+	       && !get_predicates_for_bb (bb).is_empty ())
+	evaluate_control_stmt_using_entry_checks (swtch, predicate_path,
+						  &ignored_edges);
 
       FOR_EACH_EDGE (e, ei, bb->succs)
 	{
@@ -521,7 +688,8 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 
 	  if (dest->loop_father == loop
 	      && !(dest->flags & reachable_flag)
-	      && !(e->flags & flags))
+	      && !(e->flags & flags)
+	      && !ignored_edges.contains (e))
 	    {
 	      worklist.safe_push (dest);
 	      dest->flags |= reachable_flag;
@@ -576,7 +744,8 @@ evaluate_loop_insns_for_predicate (class loop *loop, basic_block *bbs,
 
 static bool
 tree_unswitch_single_loop (class loop *loop, int num,
-			   predicate_vector &predicate_path, unsigned budget)
+			   predicate_vector &predicate_path, unsigned budget,
+			   const auto_edge_flag &ignored_edge_flag)
 {
   basic_block *bbs = NULL;
   class loop *nloop;
@@ -687,14 +856,18 @@ tree_unswitch_single_loop (class loop *loop, int num,
       /* Invoke itself on modified loops.  */
       predicate_path.safe_push (std::make_pair (predicate, false));
       merge_last (predicate_path);
-      changed |= simplify_loop_version (nloop, predicate_path);
-      tree_unswitch_single_loop (nloop, num + 1, predicate_path, budget);
+      changed |= simplify_loop_version (nloop, predicate_path,
+					ignored_edge_flag);
+      tree_unswitch_single_loop (nloop, num + 1, predicate_path, budget,
+				 ignored_edge_flag);
       predicate_path.pop ();
 
       predicate_path.safe_push (std::make_pair (predicate, true));
       merge_last (predicate_path);
-      changed |= simplify_loop_version (loop, predicate_path);
-      tree_unswitch_single_loop (loop, num + 1, predicate_path, budget);
+      changed |= simplify_loop_version (loop, predicate_path,
+					ignored_edge_flag);
+      tree_unswitch_single_loop (loop, num + 1, predicate_path, budget,
+				 ignored_edge_flag);
       predicate_path.pop ();
       changed = true;
     }
@@ -718,7 +891,7 @@ tree_unswitch_loop (class loop *loop,
 
   /* Some sanity checking.  */
   gcc_assert (flow_bb_inside_loop_p (loop, unswitch_on));
-  gcc_assert (EDGE_COUNT (unswitch_on->succs) == 2);
+  gcc_assert (EDGE_COUNT (unswitch_on->succs) >= 2);
   gcc_assert (loop->inner == NULL);
 
   extract_true_false_edges_from_block (unswitch_on, &edge_true, &edge_false);


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [gcc(refs/users/marxin/heads/loop-unswitch-improvement-v7)] work in progress.
@ 2021-12-08 10:17 Martin Liska
  0 siblings, 0 replies; 4+ messages in thread
From: Martin Liska @ 2021-12-08 10:17 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:235a1621cb8927ec871c86403c5cca278cf3ff67

commit 235a1621cb8927ec871c86403c5cca278cf3ff67
Author: Martin Liska <mliska@suse.cz>
Date:   Thu Dec 2 14:07:13 2021 +0100

    work in progress.

Diff:
---
 gcc/tree-ssa-loop-unswitch.c | 317 +++++++++++++++++++++++++++++++++----------
 1 file changed, 245 insertions(+), 72 deletions(-)

diff --git a/gcc/tree-ssa-loop-unswitch.c b/gcc/tree-ssa-loop-unswitch.c
index 1383f2d355d..a17ede748e6 100644
--- a/gcc/tree-ssa-loop-unswitch.c
+++ b/gcc/tree-ssa-loop-unswitch.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pretty-print.h"
 #include "gimple-range.h"
 #include "dbgcnt.h"
+#include "cfganal.h"
 
 /* This file implements the loop unswitching, i.e. transformation of loops like
 
@@ -83,14 +84,26 @@ along with GCC; see the file COPYING3.  If not see
 struct unswitch_predicate
 {
   /* Default constructor.  */
-  unswitch_predicate (tree cond, tree lhs_)
-  : condition (cond), lhs (lhs_), true_range (), false_range ()
+  unswitch_predicate (tree cond, tree lhs_, int edge_index_ = -1)
+  : condition (cond), lhs (lhs_), true_range (), false_range (),
+    edge_index (edge_index_)
   {}
 
+  inline void
+  init_false_edge (void)
+  {
+    false_range = true_range;
+
+    if (!false_range.varying_p ()
+	&& !false_range.undefined_p ())
+      false_range.invert ();
+  }
+
   tree condition;
   tree lhs;
   int_range_max true_range;
   int_range_max false_range;
+  int edge_index;
 };
 
 /* Cache storage for unswitch_predicate belonging to a basic block.  */
@@ -105,10 +118,12 @@ typedef vec<std::pair<unswitch_predicate *, bool>> predicate_vector;
 static class loop *tree_unswitch_loop (class loop *, basic_block, tree);
 static bool tree_unswitch_single_loop (class loop *, int,
 				       predicate_vector &predicate_path,
-				       unsigned budget);
+				       unsigned budget,
+				       const auto_edge_flag &ignored_edge_flag);
 static void
 find_unswitching_predicates_for_bb (basic_block bb, class loop *loop,
-				    vec<unswitch_predicate *> &candidates);
+				    vec<unswitch_predicate *> &candidates,
+				    const auto_edge_flag &ignored_edge_flag);
 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);
@@ -139,7 +154,8 @@ set_predicates_for_bb (basic_block bb, vec<unswitch_predicate *> predicates)
    Return total number of instructions in the loop.  */
 
 static unsigned
-init_loop_unswitch_info (class loop *loop)
+init_loop_unswitch_info (class loop *loop,
+			 const auto_edge_flag &ignored_edge_flag)
 {
   unsigned total_insns = 0;
 
@@ -162,7 +178,8 @@ init_loop_unswitch_info (class loop *loop)
       /* Find a bb to unswitch on.  */
       vec<unswitch_predicate *> candidates;
       candidates.create (1);
-      find_unswitching_predicates_for_bb (bbs[i], loop, candidates);
+      find_unswitching_predicates_for_bb (bbs[i], loop, candidates,
+					  ignored_edge_flag);
       if (!candidates.is_empty ())
 	set_predicates_for_bb (bbs[i], candidates);
       else
@@ -184,6 +201,7 @@ unsigned int
 tree_ssa_unswitch_loops (void)
 {
   bool changed = false;
+  auto_edge_flag ignored_edge_flag (cfun);
 
   ranger = enable_ranger (cfun);
 
@@ -197,12 +215,13 @@ tree_ssa_unswitch_loops (void)
 
 	  /* Unswitch innermost loop.  */
 	  unsigned int budget
-	    = init_loop_unswitch_info (loop) + param_max_unswitch_insns;
+	    = (init_loop_unswitch_info (loop, ignored_edge_flag)
+	       + param_max_unswitch_insns);
 
 	  predicate_vector predicate_path;
 	  predicate_path.create (8);
 	  changed |= tree_unswitch_single_loop (loop, 0, predicate_path,
-						budget);
+						budget, ignored_edge_flag);
 
 	  delete bb_predicates;
 	  bb_predicates = NULL;
@@ -300,59 +319,130 @@ is_maybe_undefined (const tree name, gimple *stmt, class loop *loop)
 
 static void
 find_unswitching_predicates_for_bb (basic_block bb, class loop *loop,
-				    vec<unswitch_predicate *> &candidates)
+				    vec<unswitch_predicate *> &candidates,
+				    const auto_edge_flag &ignored_edge_flag)
 {
   gimple *last, *def;
-  gcond *stmt;
   tree cond, use;
   basic_block def_bb;
   ssa_op_iter iter;
 
   /* BB must end in a simple conditional jump.  */
   last = last_stmt (bb);
-  if (!last || gimple_code (last) != GIMPLE_COND)
+  if (!last)
     return;
-  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;
+  if (gcond *stmt = safe_dyn_cast <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;
+
+      /* Condition must be invariant.  */
+      FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
+	{
+	  def = SSA_NAME_DEF_STMT (use);
+	  def_bb = gimple_bb (def);
+	  if (def_bb
+	      && flow_bb_inside_loop_p (loop, def_bb))
+	    return;
+	  /* Unswitching on undefined values would introduce undefined
+	     behavior that the original program might never exercise.  */
+	  if (is_maybe_undefined (use, stmt, loop))
+	    return;
+	}
+
+      tree lhs = gimple_cond_lhs (stmt);
+      tree rhs = gimple_cond_rhs (stmt);
 
-  /* Condition must be invariant.  */
-  FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
+      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);
+
+      unswitch_predicate *predicate = new unswitch_predicate (cond, lhs);
+      if (irange::supports_type_p (TREE_TYPE (lhs)) && CONSTANT_CLASS_P (rhs))
+	{
+	  ranger->range_on_edge (predicate->true_range, edge_true, lhs);
+	  predicate->init_false_edge ();
+	}
+
+      candidates.safe_push (predicate);
+    }
+  else if (gswitch *stmt = safe_dyn_cast <gswitch *> (last))
     {
-      def = SSA_NAME_DEF_STMT (use);
+      unsigned nlabels = gimple_switch_num_labels (stmt);
+      tree idx = gimple_switch_index (stmt);
+      if (TREE_CODE (idx) != SSA_NAME
+	  || nlabels < 1)
+	return;
+      /* Index must be invariant.  */
+      def = SSA_NAME_DEF_STMT (idx);
       def_bb = gimple_bb (def);
       if (def_bb
 	  && flow_bb_inside_loop_p (loop, def_bb))
 	return;
       /* Unswitching on undefined values would introduce undefined
 	 behavior that the original program might never exercise.  */
-      if (is_maybe_undefined (use, stmt, loop))
-	return;
-    }
-
-  tree lhs = gimple_cond_lhs (stmt);
-  tree rhs = gimple_cond_rhs (stmt);
+      if (is_maybe_undefined (idx, stmt, loop))
+ 	return;
 
-  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);
-
-  unswitch_predicate *predicate = new unswitch_predicate (cond, lhs);
-  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;
+      edge e;
+      edge_iterator ei;
+      unsigned edge_index = 0;
+      FOR_EACH_EDGE (e, ei, gimple_bb (stmt)->succs)
+	{
+	  if (!(e->flags & ignored_edge_flag))
+	    {
+	      /* Build compound expression for all cases leading
+		 to this edge.  */
+	      tree expr = NULL_TREE;
+	      for (unsigned i = 1; i < gimple_switch_num_labels (stmt); ++i)
+		{
+		  tree lab = gimple_switch_label (stmt, i);
+		  basic_block dest = label_to_block (cfun, CASE_LABEL (lab));
+		  edge e2 = find_edge (gimple_bb (stmt), dest);
+		  if (e == e2)
+		    {
+		      tree cmp;
+		      if (CASE_HIGH (lab) != NULL_TREE)
+			{
+			  tree cmp1 = build2 (GE_EXPR, boolean_type_node, idx,
+					      CASE_LOW (lab));
+			  tree cmp2 = build2 (LE_EXPR, boolean_type_node, idx,
+					      CASE_HIGH (lab));
+			  cmp = build2 (BIT_AND_EXPR, boolean_type_node, cmp1,
+					cmp2);
+			}
+		      else
+			cmp = build2 (EQ_EXPR, boolean_type_node, idx,
+				      CASE_LOW (lab));
+
+		      /* Combine the expression with the existing one.  */
+		      if (expr == NULL_TREE)
+			expr = cmp;
+		      else
+			expr = build2 (BIT_IOR_EXPR, boolean_type_node, expr,
+				       cmp);
+		    }
+		}
 
-      if (!predicate->false_range.varying_p ()
-	  && !predicate->false_range.undefined_p ())
-	predicate->false_range.invert ();
+	      if (expr != NULL_TREE)
+		{
+		  unswitch_predicate *predicate = new unswitch_predicate (expr, idx, edge_index);
+		  if (irange::supports_type_p (TREE_TYPE (idx)))
+		    {
+		      ranger->range_on_edge (predicate->true_range, e, idx);
+		      predicate->init_false_edge ();
+		    }
+
+		  candidates.safe_push (predicate);
+		}
+	    }
+	  edge_index++;
+	}
     }
-
-  candidates.safe_push (predicate);
 }
 
 static void
@@ -398,22 +488,44 @@ find_range_for_lhs (predicate_vector &predicate_path, tree lhs,
 
 static tree
 evaluate_control_stmt_using_entry_checks (gimple *stmt,
-					  predicate_vector &predicate_path)
+					  predicate_vector &predicate_path,
+					  hash_set<edge> *ignored_edges)
 {
+  tree lhs = NULL_TREE;
+
   if (predicate_path.is_empty ())
     return NULL_TREE;
 
-  tree lhs = gimple_cond_lhs (stmt);
+  gcond *condition = dyn_cast<gcond *> (stmt);
+  gswitch *swtch = dyn_cast<gswitch *> (stmt);
+
   unswitch_predicate *last_predicate = predicate_path.last ().first;
   bool true_edge = predicate_path.last ().second;
 
-  if (gimple_code (stmt) == GIMPLE_COND
+  if (condition != NULL
+      && (lhs = gimple_cond_lhs (stmt))
       && operand_equal_p (lhs, last_predicate->lhs, 0))
     {
       if (gimple_cond_code (stmt) == TREE_CODE (last_predicate->condition)
 	  && operand_equal_p (gimple_cond_rhs (stmt),
 			      TREE_OPERAND (last_predicate->condition, 1), 0))
-	return true_edge ? boolean_true_node : boolean_false_node;
+	{
+	  edge edge_true, edge_false;
+	  extract_true_false_edges_from_block (gimple_bb (stmt),
+					       &edge_true, &edge_false);
+	  if (true_edge)
+	    {
+	      if (ignored_edges != NULL)
+		ignored_edges->add (edge_true);
+	      return boolean_true_node;
+	    }
+	  else
+	    {
+	      if (ignored_edges != NULL)
+		ignored_edges->add (edge_false);
+	      return boolean_false_node;
+	    }
+	}
       else if (irange::supports_type_p (TREE_TYPE (lhs)))
 	{
 	  int_range_max r;
@@ -425,6 +537,40 @@ evaluate_control_stmt_using_entry_checks (gimple *stmt,
 	    return r.zero_p () ? boolean_false_node : boolean_true_node;
 	}
     }
+  else if (swtch != NULL)
+    {
+      unsigned nlabels = gimple_switch_num_labels (swtch);
+      unsigned ignored = 0;
+
+      for (unsigned i = 0; i < nlabels; ++i)
+	{
+	  tree lab = gimple_switch_label (swtch, i);
+	  basic_block dest = label_to_block (cfun, CASE_LABEL (lab));
+	  edge e = find_edge (gimple_bb (stmt), dest);
+
+	  int_range_max r;
+	  int_range_max path_range;
+	  tree idx = gimple_switch_index (swtch);
+	  find_range_for_lhs (predicate_path, idx, path_range);
+
+	  int_range_max xx;
+	  fold_range (xx, stmt, e);
+
+	  if (!path_range.undefined_p ()
+	      && fold_range (r, stmt, path_range)
+	      && r.singleton_p ()
+	      && r.zero_p ())
+	    {
+	      ignored_edges->add (e);
+	      ignored++;
+	    }
+	}
+
+      /* Only one edge from the switch is alive.  */
+      gcc_checking_assert (ignored + 1 <= nlabels);
+      if (ignored + 1 == nlabels)
+	return true_edge ? boolean_true_node : boolean_false_node;
+    }
 
   return NULL_TREE;
 }
@@ -433,24 +579,30 @@ evaluate_control_stmt_using_entry_checks (gimple *stmt,
    marked.  */
 
 static bool
-simplify_loop_version (class loop *loop, predicate_vector &predicate_path)
+simplify_loop_version (class loop *loop, predicate_vector &predicate_path,
+		       const auto_edge_flag &ignored_edge_flag)
 {
   bool changed = false;
   basic_block *bbs = get_loop_body (loop);
 
   for (unsigned i = 0; i != loop->num_nodes; i++)
     {
-      // FIXME: works only for gconds
-      if (!get_predicates_for_bb (bbs[i]).is_empty ())
+      vec<unswitch_predicate *> &predicates = get_predicates_for_bb (bbs[i]);
+      if (!predicates.is_empty ())
 	{
+	  hash_set<edge> ignored_edges;
 	  gimple *stmt = last_stmt (bbs[i]);
-
 	  tree folded = evaluate_control_stmt_using_entry_checks (stmt,
-								  predicate_path);
-	  if (folded != NULL_TREE)
+								  predicate_path,
+								  &ignored_edges);
+
+	  gcond *cond = dyn_cast<gcond *> (stmt);
+	  gswitch *swtch = dyn_cast<gswitch *> (stmt);
+
+	  if (cond != NULL
+	      && 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
@@ -460,6 +612,25 @@ simplify_loop_version (class loop *loop, predicate_vector &predicate_path)
 	      update_stmt (cond);
 	      changed = true;
 	    }
+	  else if (swtch != NULL)
+	    {
+	      for (int j = predicates.length () - 1; j>= 0; j--)
+		{
+		  edge e = EDGE_SUCC (bbs[i], predicates[j]->edge_index);
+		  if (ignored_edges.contains (e))
+		    {
+		      e->flags = ignored_edge_flag;
+		      predicates.unordered_remove (j);
+		    }
+		}
+
+	      if (folded)
+		{
+		  gimple_switch_set_index (swtch, folded);
+		  update_stmt (swtch);
+		  changed = true;
+		}
+	    }
 	}
     }
 
@@ -478,6 +649,7 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 {
   auto_vec<basic_block> worklist (loop->num_nodes);
   worklist.quick_push (bbs[0]);
+  hash_set<edge> ignored_edges;
 
   while (!worklist.is_empty ())
     {
@@ -488,6 +660,8 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 
       gimple *last = last_stmt (bb);
       gcond *cond = last != NULL ? dyn_cast<gcond *> (last) : NULL;
+      gswitch *swtch = last != NULL ? dyn_cast<gswitch *> (last) : NULL;
+
       if (cond != NULL)
 	{
 	  if (gimple_cond_true_p (cond))
@@ -496,23 +670,16 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 	    flags = EDGE_TRUE_VALUE;
 	  else
 	    {
-	      // FIXME: works only for gconds
-	      unswitch_predicate *predicate = NULL;
 	      if (!get_predicates_for_bb (bb).is_empty ())
-		predicate = get_predicates_for_bb (bb)[0];
-
-	      if (predicate != NULL)
-		{
-		  tree folded
-		    = evaluate_control_stmt_using_entry_checks (cond,
-								predicate_path);
-		  if (folded == boolean_true_node)
-		    flags = EDGE_FALSE_VALUE;
-		  else if (folded == boolean_false_node)
-		    flags = EDGE_TRUE_VALUE;
-		}
+		evaluate_control_stmt_using_entry_checks (cond,
+							  predicate_path,
+							  &ignored_edges);
 	    }
 	}
+      else if (swtch != NULL
+	       && !get_predicates_for_bb (bb).is_empty ())
+	evaluate_control_stmt_using_entry_checks (swtch, predicate_path,
+						  &ignored_edges);
 
       FOR_EACH_EDGE (e, ei, bb->succs)
 	{
@@ -520,7 +687,8 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 
 	  if (dest->loop_father == loop
 	      && !(dest->flags & reachable_flag)
-	      && !(e->flags & flags))
+	      && !(e->flags & flags)
+	      && !ignored_edges.contains (e))
 	    {
 	      worklist.safe_push (dest);
 	      dest->flags |= reachable_flag;
@@ -575,7 +743,8 @@ evaluate_loop_insns_for_predicate (class loop *loop, basic_block *bbs,
 
 static bool
 tree_unswitch_single_loop (class loop *loop, int num,
-			   predicate_vector &predicate_path, unsigned budget)
+			   predicate_vector &predicate_path, unsigned budget,
+			   const auto_edge_flag &ignored_edge_flag)
 {
   basic_block *bbs = NULL;
   class loop *nloop;
@@ -686,14 +855,18 @@ tree_unswitch_single_loop (class loop *loop, int num,
       /* Invoke itself on modified loops.  */
       predicate_path.safe_push (std::make_pair (predicate, false));
       merge_last (predicate_path);
-      changed |= simplify_loop_version (nloop, predicate_path);
-      tree_unswitch_single_loop (nloop, num + 1, predicate_path, budget);
+      changed |= simplify_loop_version (nloop, predicate_path,
+					ignored_edge_flag);
+      tree_unswitch_single_loop (nloop, num + 1, predicate_path, budget,
+				 ignored_edge_flag);
       predicate_path.pop ();
 
       predicate_path.safe_push (std::make_pair (predicate, true));
       merge_last (predicate_path);
-      changed |= simplify_loop_version (loop, predicate_path);
-      tree_unswitch_single_loop (loop, num + 1, predicate_path, budget);
+      changed |= simplify_loop_version (loop, predicate_path,
+					ignored_edge_flag);
+      tree_unswitch_single_loop (loop, num + 1, predicate_path, budget,
+				 ignored_edge_flag);
       predicate_path.pop ();
       changed = true;
     }
@@ -717,7 +890,7 @@ tree_unswitch_loop (class loop *loop,
 
   /* Some sanity checking.  */
   gcc_assert (flow_bb_inside_loop_p (loop, unswitch_on));
-  gcc_assert (EDGE_COUNT (unswitch_on->succs) == 2);
+  gcc_assert (EDGE_COUNT (unswitch_on->succs) >= 2);
   gcc_assert (loop->inner == NULL);
 
   extract_true_false_edges_from_block (unswitch_on, &edge_true, &edge_false);


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [gcc(refs/users/marxin/heads/loop-unswitch-improvement-v7)] work in progress.
@ 2021-12-07 16:50 Martin Liska
  0 siblings, 0 replies; 4+ messages in thread
From: Martin Liska @ 2021-12-07 16:50 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:1ffcf681dd93901c55305bcc8b5e39a2a369cfc9

commit 1ffcf681dd93901c55305bcc8b5e39a2a369cfc9
Author: Martin Liska <mliska@suse.cz>
Date:   Thu Dec 2 14:07:13 2021 +0100

    work in progress.

Diff:
---
 gcc/tree-ssa-loop-unswitch.c | 317 +++++++++++++++++++++++++++++++++----------
 1 file changed, 245 insertions(+), 72 deletions(-)

diff --git a/gcc/tree-ssa-loop-unswitch.c b/gcc/tree-ssa-loop-unswitch.c
index 1383f2d355d..a17ede748e6 100644
--- a/gcc/tree-ssa-loop-unswitch.c
+++ b/gcc/tree-ssa-loop-unswitch.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pretty-print.h"
 #include "gimple-range.h"
 #include "dbgcnt.h"
+#include "cfganal.h"
 
 /* This file implements the loop unswitching, i.e. transformation of loops like
 
@@ -83,14 +84,26 @@ along with GCC; see the file COPYING3.  If not see
 struct unswitch_predicate
 {
   /* Default constructor.  */
-  unswitch_predicate (tree cond, tree lhs_)
-  : condition (cond), lhs (lhs_), true_range (), false_range ()
+  unswitch_predicate (tree cond, tree lhs_, int edge_index_ = -1)
+  : condition (cond), lhs (lhs_), true_range (), false_range (),
+    edge_index (edge_index_)
   {}
 
+  inline void
+  init_false_edge (void)
+  {
+    false_range = true_range;
+
+    if (!false_range.varying_p ()
+	&& !false_range.undefined_p ())
+      false_range.invert ();
+  }
+
   tree condition;
   tree lhs;
   int_range_max true_range;
   int_range_max false_range;
+  int edge_index;
 };
 
 /* Cache storage for unswitch_predicate belonging to a basic block.  */
@@ -105,10 +118,12 @@ typedef vec<std::pair<unswitch_predicate *, bool>> predicate_vector;
 static class loop *tree_unswitch_loop (class loop *, basic_block, tree);
 static bool tree_unswitch_single_loop (class loop *, int,
 				       predicate_vector &predicate_path,
-				       unsigned budget);
+				       unsigned budget,
+				       const auto_edge_flag &ignored_edge_flag);
 static void
 find_unswitching_predicates_for_bb (basic_block bb, class loop *loop,
-				    vec<unswitch_predicate *> &candidates);
+				    vec<unswitch_predicate *> &candidates,
+				    const auto_edge_flag &ignored_edge_flag);
 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);
@@ -139,7 +154,8 @@ set_predicates_for_bb (basic_block bb, vec<unswitch_predicate *> predicates)
    Return total number of instructions in the loop.  */
 
 static unsigned
-init_loop_unswitch_info (class loop *loop)
+init_loop_unswitch_info (class loop *loop,
+			 const auto_edge_flag &ignored_edge_flag)
 {
   unsigned total_insns = 0;
 
@@ -162,7 +178,8 @@ init_loop_unswitch_info (class loop *loop)
       /* Find a bb to unswitch on.  */
       vec<unswitch_predicate *> candidates;
       candidates.create (1);
-      find_unswitching_predicates_for_bb (bbs[i], loop, candidates);
+      find_unswitching_predicates_for_bb (bbs[i], loop, candidates,
+					  ignored_edge_flag);
       if (!candidates.is_empty ())
 	set_predicates_for_bb (bbs[i], candidates);
       else
@@ -184,6 +201,7 @@ unsigned int
 tree_ssa_unswitch_loops (void)
 {
   bool changed = false;
+  auto_edge_flag ignored_edge_flag (cfun);
 
   ranger = enable_ranger (cfun);
 
@@ -197,12 +215,13 @@ tree_ssa_unswitch_loops (void)
 
 	  /* Unswitch innermost loop.  */
 	  unsigned int budget
-	    = init_loop_unswitch_info (loop) + param_max_unswitch_insns;
+	    = (init_loop_unswitch_info (loop, ignored_edge_flag)
+	       + param_max_unswitch_insns);
 
 	  predicate_vector predicate_path;
 	  predicate_path.create (8);
 	  changed |= tree_unswitch_single_loop (loop, 0, predicate_path,
-						budget);
+						budget, ignored_edge_flag);
 
 	  delete bb_predicates;
 	  bb_predicates = NULL;
@@ -300,59 +319,130 @@ is_maybe_undefined (const tree name, gimple *stmt, class loop *loop)
 
 static void
 find_unswitching_predicates_for_bb (basic_block bb, class loop *loop,
-				    vec<unswitch_predicate *> &candidates)
+				    vec<unswitch_predicate *> &candidates,
+				    const auto_edge_flag &ignored_edge_flag)
 {
   gimple *last, *def;
-  gcond *stmt;
   tree cond, use;
   basic_block def_bb;
   ssa_op_iter iter;
 
   /* BB must end in a simple conditional jump.  */
   last = last_stmt (bb);
-  if (!last || gimple_code (last) != GIMPLE_COND)
+  if (!last)
     return;
-  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;
+  if (gcond *stmt = safe_dyn_cast <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;
+
+      /* Condition must be invariant.  */
+      FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
+	{
+	  def = SSA_NAME_DEF_STMT (use);
+	  def_bb = gimple_bb (def);
+	  if (def_bb
+	      && flow_bb_inside_loop_p (loop, def_bb))
+	    return;
+	  /* Unswitching on undefined values would introduce undefined
+	     behavior that the original program might never exercise.  */
+	  if (is_maybe_undefined (use, stmt, loop))
+	    return;
+	}
+
+      tree lhs = gimple_cond_lhs (stmt);
+      tree rhs = gimple_cond_rhs (stmt);
 
-  /* Condition must be invariant.  */
-  FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
+      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);
+
+      unswitch_predicate *predicate = new unswitch_predicate (cond, lhs);
+      if (irange::supports_type_p (TREE_TYPE (lhs)) && CONSTANT_CLASS_P (rhs))
+	{
+	  ranger->range_on_edge (predicate->true_range, edge_true, lhs);
+	  predicate->init_false_edge ();
+	}
+
+      candidates.safe_push (predicate);
+    }
+  else if (gswitch *stmt = safe_dyn_cast <gswitch *> (last))
     {
-      def = SSA_NAME_DEF_STMT (use);
+      unsigned nlabels = gimple_switch_num_labels (stmt);
+      tree idx = gimple_switch_index (stmt);
+      if (TREE_CODE (idx) != SSA_NAME
+	  || nlabels < 1)
+	return;
+      /* Index must be invariant.  */
+      def = SSA_NAME_DEF_STMT (idx);
       def_bb = gimple_bb (def);
       if (def_bb
 	  && flow_bb_inside_loop_p (loop, def_bb))
 	return;
       /* Unswitching on undefined values would introduce undefined
 	 behavior that the original program might never exercise.  */
-      if (is_maybe_undefined (use, stmt, loop))
-	return;
-    }
-
-  tree lhs = gimple_cond_lhs (stmt);
-  tree rhs = gimple_cond_rhs (stmt);
+      if (is_maybe_undefined (idx, stmt, loop))
+ 	return;
 
-  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);
-
-  unswitch_predicate *predicate = new unswitch_predicate (cond, lhs);
-  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;
+      edge e;
+      edge_iterator ei;
+      unsigned edge_index = 0;
+      FOR_EACH_EDGE (e, ei, gimple_bb (stmt)->succs)
+	{
+	  if (!(e->flags & ignored_edge_flag))
+	    {
+	      /* Build compound expression for all cases leading
+		 to this edge.  */
+	      tree expr = NULL_TREE;
+	      for (unsigned i = 1; i < gimple_switch_num_labels (stmt); ++i)
+		{
+		  tree lab = gimple_switch_label (stmt, i);
+		  basic_block dest = label_to_block (cfun, CASE_LABEL (lab));
+		  edge e2 = find_edge (gimple_bb (stmt), dest);
+		  if (e == e2)
+		    {
+		      tree cmp;
+		      if (CASE_HIGH (lab) != NULL_TREE)
+			{
+			  tree cmp1 = build2 (GE_EXPR, boolean_type_node, idx,
+					      CASE_LOW (lab));
+			  tree cmp2 = build2 (LE_EXPR, boolean_type_node, idx,
+					      CASE_HIGH (lab));
+			  cmp = build2 (BIT_AND_EXPR, boolean_type_node, cmp1,
+					cmp2);
+			}
+		      else
+			cmp = build2 (EQ_EXPR, boolean_type_node, idx,
+				      CASE_LOW (lab));
+
+		      /* Combine the expression with the existing one.  */
+		      if (expr == NULL_TREE)
+			expr = cmp;
+		      else
+			expr = build2 (BIT_IOR_EXPR, boolean_type_node, expr,
+				       cmp);
+		    }
+		}
 
-      if (!predicate->false_range.varying_p ()
-	  && !predicate->false_range.undefined_p ())
-	predicate->false_range.invert ();
+	      if (expr != NULL_TREE)
+		{
+		  unswitch_predicate *predicate = new unswitch_predicate (expr, idx, edge_index);
+		  if (irange::supports_type_p (TREE_TYPE (idx)))
+		    {
+		      ranger->range_on_edge (predicate->true_range, e, idx);
+		      predicate->init_false_edge ();
+		    }
+
+		  candidates.safe_push (predicate);
+		}
+	    }
+	  edge_index++;
+	}
     }
-
-  candidates.safe_push (predicate);
 }
 
 static void
@@ -398,22 +488,44 @@ find_range_for_lhs (predicate_vector &predicate_path, tree lhs,
 
 static tree
 evaluate_control_stmt_using_entry_checks (gimple *stmt,
-					  predicate_vector &predicate_path)
+					  predicate_vector &predicate_path,
+					  hash_set<edge> *ignored_edges)
 {
+  tree lhs = NULL_TREE;
+
   if (predicate_path.is_empty ())
     return NULL_TREE;
 
-  tree lhs = gimple_cond_lhs (stmt);
+  gcond *condition = dyn_cast<gcond *> (stmt);
+  gswitch *swtch = dyn_cast<gswitch *> (stmt);
+
   unswitch_predicate *last_predicate = predicate_path.last ().first;
   bool true_edge = predicate_path.last ().second;
 
-  if (gimple_code (stmt) == GIMPLE_COND
+  if (condition != NULL
+      && (lhs = gimple_cond_lhs (stmt))
       && operand_equal_p (lhs, last_predicate->lhs, 0))
     {
       if (gimple_cond_code (stmt) == TREE_CODE (last_predicate->condition)
 	  && operand_equal_p (gimple_cond_rhs (stmt),
 			      TREE_OPERAND (last_predicate->condition, 1), 0))
-	return true_edge ? boolean_true_node : boolean_false_node;
+	{
+	  edge edge_true, edge_false;
+	  extract_true_false_edges_from_block (gimple_bb (stmt),
+					       &edge_true, &edge_false);
+	  if (true_edge)
+	    {
+	      if (ignored_edges != NULL)
+		ignored_edges->add (edge_true);
+	      return boolean_true_node;
+	    }
+	  else
+	    {
+	      if (ignored_edges != NULL)
+		ignored_edges->add (edge_false);
+	      return boolean_false_node;
+	    }
+	}
       else if (irange::supports_type_p (TREE_TYPE (lhs)))
 	{
 	  int_range_max r;
@@ -425,6 +537,40 @@ evaluate_control_stmt_using_entry_checks (gimple *stmt,
 	    return r.zero_p () ? boolean_false_node : boolean_true_node;
 	}
     }
+  else if (swtch != NULL)
+    {
+      unsigned nlabels = gimple_switch_num_labels (swtch);
+      unsigned ignored = 0;
+
+      for (unsigned i = 0; i < nlabels; ++i)
+	{
+	  tree lab = gimple_switch_label (swtch, i);
+	  basic_block dest = label_to_block (cfun, CASE_LABEL (lab));
+	  edge e = find_edge (gimple_bb (stmt), dest);
+
+	  int_range_max r;
+	  int_range_max path_range;
+	  tree idx = gimple_switch_index (swtch);
+	  find_range_for_lhs (predicate_path, idx, path_range);
+
+	  int_range_max xx;
+	  fold_range (xx, stmt, e);
+
+	  if (!path_range.undefined_p ()
+	      && fold_range (r, stmt, path_range)
+	      && r.singleton_p ()
+	      && r.zero_p ())
+	    {
+	      ignored_edges->add (e);
+	      ignored++;
+	    }
+	}
+
+      /* Only one edge from the switch is alive.  */
+      gcc_checking_assert (ignored + 1 <= nlabels);
+      if (ignored + 1 == nlabels)
+	return true_edge ? boolean_true_node : boolean_false_node;
+    }
 
   return NULL_TREE;
 }
@@ -433,24 +579,30 @@ evaluate_control_stmt_using_entry_checks (gimple *stmt,
    marked.  */
 
 static bool
-simplify_loop_version (class loop *loop, predicate_vector &predicate_path)
+simplify_loop_version (class loop *loop, predicate_vector &predicate_path,
+		       const auto_edge_flag &ignored_edge_flag)
 {
   bool changed = false;
   basic_block *bbs = get_loop_body (loop);
 
   for (unsigned i = 0; i != loop->num_nodes; i++)
     {
-      // FIXME: works only for gconds
-      if (!get_predicates_for_bb (bbs[i]).is_empty ())
+      vec<unswitch_predicate *> &predicates = get_predicates_for_bb (bbs[i]);
+      if (!predicates.is_empty ())
 	{
+	  hash_set<edge> ignored_edges;
 	  gimple *stmt = last_stmt (bbs[i]);
-
 	  tree folded = evaluate_control_stmt_using_entry_checks (stmt,
-								  predicate_path);
-	  if (folded != NULL_TREE)
+								  predicate_path,
+								  &ignored_edges);
+
+	  gcond *cond = dyn_cast<gcond *> (stmt);
+	  gswitch *swtch = dyn_cast<gswitch *> (stmt);
+
+	  if (cond != NULL
+	      && 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
@@ -460,6 +612,25 @@ simplify_loop_version (class loop *loop, predicate_vector &predicate_path)
 	      update_stmt (cond);
 	      changed = true;
 	    }
+	  else if (swtch != NULL)
+	    {
+	      for (int j = predicates.length () - 1; j>= 0; j--)
+		{
+		  edge e = EDGE_SUCC (bbs[i], predicates[j]->edge_index);
+		  if (ignored_edges.contains (e))
+		    {
+		      e->flags = ignored_edge_flag;
+		      predicates.unordered_remove (j);
+		    }
+		}
+
+	      if (folded)
+		{
+		  gimple_switch_set_index (swtch, folded);
+		  update_stmt (swtch);
+		  changed = true;
+		}
+	    }
 	}
     }
 
@@ -478,6 +649,7 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 {
   auto_vec<basic_block> worklist (loop->num_nodes);
   worklist.quick_push (bbs[0]);
+  hash_set<edge> ignored_edges;
 
   while (!worklist.is_empty ())
     {
@@ -488,6 +660,8 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 
       gimple *last = last_stmt (bb);
       gcond *cond = last != NULL ? dyn_cast<gcond *> (last) : NULL;
+      gswitch *swtch = last != NULL ? dyn_cast<gswitch *> (last) : NULL;
+
       if (cond != NULL)
 	{
 	  if (gimple_cond_true_p (cond))
@@ -496,23 +670,16 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 	    flags = EDGE_TRUE_VALUE;
 	  else
 	    {
-	      // FIXME: works only for gconds
-	      unswitch_predicate *predicate = NULL;
 	      if (!get_predicates_for_bb (bb).is_empty ())
-		predicate = get_predicates_for_bb (bb)[0];
-
-	      if (predicate != NULL)
-		{
-		  tree folded
-		    = evaluate_control_stmt_using_entry_checks (cond,
-								predicate_path);
-		  if (folded == boolean_true_node)
-		    flags = EDGE_FALSE_VALUE;
-		  else if (folded == boolean_false_node)
-		    flags = EDGE_TRUE_VALUE;
-		}
+		evaluate_control_stmt_using_entry_checks (cond,
+							  predicate_path,
+							  &ignored_edges);
 	    }
 	}
+      else if (swtch != NULL
+	       && !get_predicates_for_bb (bb).is_empty ())
+	evaluate_control_stmt_using_entry_checks (swtch, predicate_path,
+						  &ignored_edges);
 
       FOR_EACH_EDGE (e, ei, bb->succs)
 	{
@@ -520,7 +687,8 @@ evaluate_insns (class loop *loop, basic_block *bbs,
 
 	  if (dest->loop_father == loop
 	      && !(dest->flags & reachable_flag)
-	      && !(e->flags & flags))
+	      && !(e->flags & flags)
+	      && !ignored_edges.contains (e))
 	    {
 	      worklist.safe_push (dest);
 	      dest->flags |= reachable_flag;
@@ -575,7 +743,8 @@ evaluate_loop_insns_for_predicate (class loop *loop, basic_block *bbs,
 
 static bool
 tree_unswitch_single_loop (class loop *loop, int num,
-			   predicate_vector &predicate_path, unsigned budget)
+			   predicate_vector &predicate_path, unsigned budget,
+			   const auto_edge_flag &ignored_edge_flag)
 {
   basic_block *bbs = NULL;
   class loop *nloop;
@@ -686,14 +855,18 @@ tree_unswitch_single_loop (class loop *loop, int num,
       /* Invoke itself on modified loops.  */
       predicate_path.safe_push (std::make_pair (predicate, false));
       merge_last (predicate_path);
-      changed |= simplify_loop_version (nloop, predicate_path);
-      tree_unswitch_single_loop (nloop, num + 1, predicate_path, budget);
+      changed |= simplify_loop_version (nloop, predicate_path,
+					ignored_edge_flag);
+      tree_unswitch_single_loop (nloop, num + 1, predicate_path, budget,
+				 ignored_edge_flag);
       predicate_path.pop ();
 
       predicate_path.safe_push (std::make_pair (predicate, true));
       merge_last (predicate_path);
-      changed |= simplify_loop_version (loop, predicate_path);
-      tree_unswitch_single_loop (loop, num + 1, predicate_path, budget);
+      changed |= simplify_loop_version (loop, predicate_path,
+					ignored_edge_flag);
+      tree_unswitch_single_loop (loop, num + 1, predicate_path, budget,
+				 ignored_edge_flag);
       predicate_path.pop ();
       changed = true;
     }
@@ -717,7 +890,7 @@ tree_unswitch_loop (class loop *loop,
 
   /* Some sanity checking.  */
   gcc_assert (flow_bb_inside_loop_p (loop, unswitch_on));
-  gcc_assert (EDGE_COUNT (unswitch_on->succs) == 2);
+  gcc_assert (EDGE_COUNT (unswitch_on->succs) >= 2);
   gcc_assert (loop->inner == NULL);
 
   extract_true_false_edges_from_block (unswitch_on, &edge_true, &edge_false);


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-12-09 12:48 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-08 18:26 [gcc(refs/users/marxin/heads/loop-unswitch-improvement-v7)] work in progress Martin Liska
  -- strict thread matches above, loose matches on Subject: below --
2021-12-09 12:48 Martin Liska
2021-12-08 10:17 Martin Liska
2021-12-07 16:50 Martin Liska

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).