[COMMITTED] Ranger converted to a local edge flag instead of EDGE_EXECUTABLE.

Message ID c490d8fc-3b5e-6923-5f79-e17c19d161df@redhat.com
State Committed
Headers
Series [COMMITTED] Ranger converted to a local edge flag instead of EDGE_EXECUTABLE. |

Commit Message

Andrew MacLeod Sept. 23, 2021, 5:35 p.m. UTC
  This patch converts the Ranger ecosystem to use a local edge flag to 
represent a "not executable" edge rather than reusing the 
EDGE_EXECUTABLE flag.

This has multiple benefits,
  - No need to set all the flags on every edge before starting
  - No chance of interference from other passes which might be using the 
flag in a slightly different manner
  - IF we don't actual set the flag somewhere, it will have no impact 
anywhere
  - Multiple instances wouldn't interfere with each other..  but that 
probably not an issue anyway.

The implementation works as follows:

* Ranger will create a publicly available,  instance-local flag when it 
is instantiated.  When checking is enabled, it also verifies every edge 
is already clear, as expected.
* Ranger and the GORI engine check this flag to determine if an edge is 
not executable, and will return UNDEFINED for any requests on such edges.
* The gimple-range-fold and relation oracle classes all utilize 
UNDEFINED values for their work, so this is transparent to them and 
doesn't prevent them from being used by other clients without a flag.
* The simplifier accepts an optional flag value which it will set on 
edges and propagate when it changes the IL to cause an edge to be not 
executable.  This is done in parallel with whatever it use to do with 
EDGE_EXECUTABLE, and is thus transparent to other uses.  If no flag is 
provided, it does nothing.
* The simplifier also tracks when it sets this flag, and upon 
destruction, clears all flags which were set.
* the VRP pass provides the simplifier with the flag so that only during 
a VRP pass will the simplifer set the flag, and thus only then will the 
unexecutable flag work currently be triggered.

Ranger manages the non-executable flag independent of EDGE_EXECUTABLE 
now, and doesn't need to do any additional work unless its paired with a 
simplfier (or other mechanism) to set the flag on an edge.  It becomes a 
consumer of a flag which the client can optionally utilize.

Bootstraps on x86_64-pc-linux-gnu with no regressions, in both ranger 
only and hybrid modes.  Pushed.

Andrew
  

Patch

From 053e1d642104d19d5f9e5fb08a9e7354a0db28f5 Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <amacleod@redhat.com>
Date: Wed, 22 Sep 2021 16:58:22 -0400
Subject: [PATCH 1/2] Create a ranger-local flag for non-executable edges.

Instead of repurposing EDGE_EXECUTABLE, ranger creates a local flag and
ultizes it throughout.

	* gimple-range-cache.cc (ranger_cache::ranger_cache): Take
	non-executable_edge flag as parameter.
	* gimple-range-cache.h (ranger_cache): Adjust prototype.
	* gimple-range-gori.cc (gori_compute::gori_compute): Take
	non-executable_edge flag as parameter.
	(gori_compute::outgoing_edge_range_p): Check new flag.
	* gimple-range-gori.h (gori_compute): Adjust prototype.
	* gimple-range.cc (gimple_ranger::gimple_ranger): Create new flag.
	(gimple_ranger::range_on_edge): Check new flag.
	* gimple-range.h (gimple_ranger::non_executable_edge_flag): New.
	* gimple-ssa-evrp.c (rvrp_folder): Pass ranger flag to simplifer.
	(hybrid_folder::hybrid_folder): Set ranger non-executable flag value.
	(hybrid_folder::fold_stmt): Set flag value in the simplifer.
	* vr-values.c (simplify_using_ranges::set_and_propagate_unexecutable):
	Use not_executable flag if provided inmstead of EDGE_EXECUTABLE.
	(simplify_using_ranges::simplify_switch_using_ranges): Clear
	EDGE_EXECUTABLE like it originally did.
	(simplify_using_ranges::cleanup_edges_and_switches): Clear any
	NON_EXECUTABLE flags.
	(simplify_using_ranges::simplify_using_ranges): Adjust.
	* vr-values.h (class simplify_using_ranges): Adjust.
	(simplify_using_ranges::set_range_query): Add non-executable flag param.
---
 gcc/gimple-range-cache.cc |  3 ++-
 gcc/gimple-range-cache.h  |  2 +-
 gcc/gimple-range-gori.cc  |  5 +++--
 gcc/gimple-range-gori.h   |  7 ++++++-
 gcc/gimple-range.cc       | 22 ++++++++++++++++++----
 gcc/gimple-range.h        |  1 +
 gcc/gimple-ssa-evrp.c     | 12 +++++++++---
 gcc/vr-values.c           | 24 ++++++++++++++++++------
 gcc/vr-values.h           |  8 ++++++--
 9 files changed, 64 insertions(+), 20 deletions(-)

diff --git a/gcc/gimple-range-cache.cc b/gcc/gimple-range-cache.cc
index b856b212169..61043d3f375 100644
--- a/gcc/gimple-range-cache.cc
+++ b/gcc/gimple-range-cache.cc
@@ -750,7 +750,8 @@  temporal_cache::set_always_current (tree name)
 
 // --------------------------------------------------------------------------
 
-ranger_cache::ranger_cache ()
+ranger_cache::ranger_cache (int not_executable_flag)
+						: m_gori (not_executable_flag)
 {
   m_workback.create (0);
   m_workback.safe_grow_cleared (last_basic_block_for_fn (cfun));
diff --git a/gcc/gimple-range-cache.h b/gcc/gimple-range-cache.h
index 3b55673fd29..4937a0b305a 100644
--- a/gcc/gimple-range-cache.h
+++ b/gcc/gimple-range-cache.h
@@ -92,7 +92,7 @@  private:
 class ranger_cache : public range_query
 {
 public:
-  ranger_cache ();
+  ranger_cache (int not_executable_flag);
   ~ranger_cache ();
 
   virtual bool range_of_expr (irange &r, tree name, gimple *stmt);
diff --git a/gcc/gimple-range-gori.cc b/gcc/gimple-range-gori.cc
index f5a35287bed..4a1ade7f921 100644
--- a/gcc/gimple-range-gori.cc
+++ b/gcc/gimple-range-gori.cc
@@ -634,8 +634,9 @@  debug (gori_map &g)
 
 // Construct a gori_compute object.
 
-gori_compute::gori_compute () : tracer ("GORI ")
+gori_compute::gori_compute (int not_executable_flag) : tracer ("GORI ")
 {
+  m_not_executable_flag = not_executable_flag;
   // Create a boolean_type true and false range.
   m_bool_zero = int_range<2> (boolean_false_node, boolean_false_node);
   m_bool_one = int_range<2> (boolean_true_node, boolean_true_node);
@@ -1214,7 +1215,7 @@  gori_compute::outgoing_edge_range_p (irange &r, edge e, tree name,
   int_range_max lhs;
   unsigned idx;
 
-  if ((e->flags & EDGE_EXECUTABLE) == 0)
+  if ((e->flags & m_not_executable_flag))
     {
       r.set_undefined ();
       if (dump_file && (dump_flags & TDF_DETAILS))
diff --git a/gcc/gimple-range-gori.h b/gcc/gimple-range-gori.h
index 688468c8790..ec0b95145f0 100644
--- a/gcc/gimple-range-gori.h
+++ b/gcc/gimple-range-gori.h
@@ -147,12 +147,16 @@  private:
 // expr_range_in_bb is simply a wrapper which calls ssa_range_in_bb for 
 // SSA_NAMES and otherwise simply calculates the range of the expression.
 //
+// The constructor takes a flag value to use on edges to check for the
+// NON_EXECUTABLE_EDGE property.  The zero default means no flag is checked.
+// All value requests from NON_EXECUTABLE_EDGE edges are returned UNDEFINED.
+//
 // The remaining routines are internal use only.
 
 class gori_compute : public gori_map
 {
 public:
-  gori_compute ();
+  gori_compute (int not_executable_flag = 0);
   bool outgoing_edge_range_p (irange &r, edge e, tree name, range_query &q);
   bool has_edge_range_p (tree name, edge e = NULL);
   void dump (FILE *f);
@@ -181,6 +185,7 @@  private:
 
   gimple_outgoing_range outgoing;	// Edge values for COND_EXPR & SWITCH_EXPR.
   range_tracer tracer;
+  int m_not_executable_flag;
 };
 
 // These routines provide a GIMPLE interface to the range-ops code.
diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc
index 625d13647f7..d4108db1855 100644
--- a/gcc/gimple-range.cc
+++ b/gcc/gimple-range.cc
@@ -34,15 +34,29 @@  along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "tree-scalar-evolution.h"
 #include "gimple-range.h"
-#include "domwalk.h"
 
-gimple_ranger::gimple_ranger () : tracer ("")
+gimple_ranger::gimple_ranger () :
+	non_executable_edge_flag (cfun),
+	m_cache (non_executable_edge_flag),
+	tracer ("")
 {
   // If the cache has a relation oracle, use it.
   m_oracle = m_cache.oracle ();
   if (dump_file && (param_evrp_mode & EVRP_MODE_TRACE))
     tracer.enable_trace ();
-  set_all_edges_as_executable (cfun);
+
+  // Ensure the not_executable flag is clear everywhere.
+  if (flag_checking)
+    {
+      basic_block bb;
+      FOR_ALL_BB_FN (bb, cfun)
+	{
+	  edge_iterator ei;
+	  edge e;
+	  FOR_EACH_EDGE (e, ei, bb->succs)
+	    gcc_checking_assert ((e->flags & non_executable_edge_flag) == 0);
+	}
+    }
 }
 
 bool
@@ -174,7 +188,7 @@  gimple_ranger::range_on_edge (irange &r, edge e, tree name)
     }
 
   // Check to see if the edge is executable.
-  if ((e->flags & EDGE_EXECUTABLE) == 0)
+  if ((e->flags & non_executable_edge_flag))
     {
       r.set_undefined ();
       if (idx)
diff --git a/gcc/gimple-range.h b/gcc/gimple-range.h
index eaebb9c5833..191a075a1b0 100644
--- a/gcc/gimple-range.h
+++ b/gcc/gimple-range.h
@@ -56,6 +56,7 @@  public:
   inline gori_compute &gori ()  { return m_cache.m_gori; }
   virtual void dump (FILE *f) OVERRIDE;
   void dump_bb (FILE *f, basic_block bb);
+  auto_edge_flag non_executable_edge_flag;
 protected:
   bool fold_range_internal (irange &r, gimple *s, tree name);
   ranger_cache m_cache;
diff --git a/gcc/gimple-ssa-evrp.c b/gcc/gimple-ssa-evrp.c
index 254542ef4cc..437f19471f1 100644
--- a/gcc/gimple-ssa-evrp.c
+++ b/gcc/gimple-ssa-evrp.c
@@ -121,7 +121,7 @@  public:
   rvrp_folder () : substitute_and_fold_engine (), m_simplifier ()
   {
     m_ranger = enable_ranger (cfun);
-    m_simplifier.set_range_query (m_ranger);
+    m_simplifier.set_range_query (m_ranger, m_ranger->non_executable_edge_flag);
     m_pta = new pointer_equiv_analyzer (m_ranger);
   }
       
@@ -205,12 +205,16 @@  public:
     if (evrp_first)
       {
 	first = &m_range_analyzer;
+	first_exec_flag = 0;
 	second = m_ranger;
+	second_exec_flag = m_ranger->non_executable_edge_flag;
       }
      else
       {
 	first = m_ranger;
+	first_exec_flag = m_ranger->non_executable_edge_flag;
 	second = &m_range_analyzer;
+	second_exec_flag = 0;
       }
     m_pta = new pointer_equiv_analyzer (m_ranger);
   }
@@ -227,11 +231,11 @@  public:
 
   bool fold_stmt (gimple_stmt_iterator *gsi) OVERRIDE
     {
-      simplifier.set_range_query (first);
+      simplifier.set_range_query (first, first_exec_flag);
       if (simplifier.simplify (gsi))
 	return true;
 
-      simplifier.set_range_query (second);
+      simplifier.set_range_query (second, second_exec_flag);
       if (simplifier.simplify (gsi))
 	{
 	  if (dump_file)
@@ -267,7 +271,9 @@  private:
   DISABLE_COPY_AND_ASSIGN (hybrid_folder);
   gimple_ranger *m_ranger;
   range_query *first;
+  int first_exec_flag;
   range_query *second;
+  int second_exec_flag;
   pointer_equiv_analyzer *m_pta;
   tree choose_value (tree evrp_val, tree ranger_val);
 };
diff --git a/gcc/vr-values.c b/gcc/vr-values.c
index 3b8d0674471..9bf58f416f2 100644
--- a/gcc/vr-values.c
+++ b/gcc/vr-values.c
@@ -3460,19 +3460,21 @@  range_fits_type_p (const value_range *vr,
 void
 simplify_using_ranges::set_and_propagate_unexecutable (edge e)
 {
-  // If EXECUUTABLE is already clear, we're done.
-  if ((e->flags & EDGE_EXECUTABLE) == 0)
+  // If not_executable is already set, we're done.
+  // This works in the absence of a flag as well.
+  if ((e->flags & m_not_executable_flag) == m_not_executable_flag)
     return;
 
-  e->flags &= ~EDGE_EXECUTABLE;
+  e->flags |= m_not_executable_flag;
+  m_flag_set_edges.safe_push (e);
 
   // Check if the destination block needs to propagate the property.
   basic_block bb = e->dest;
 
-  // If any entry edge is marked EXECUTABLE, we are done.
+  // If any incoming edge is executable, we are done.
   edge_iterator ei;
   FOR_EACH_EDGE (e, ei, bb->preds)
-    if (e->flags & EDGE_EXECUTABLE)
+    if ((e->flags & m_not_executable_flag) == 0)
       return;
 
   // This block is also unexecutable, propagate to all exit edges as well.
@@ -3805,6 +3807,7 @@  simplify_using_ranges::simplify_switch_using_ranges (gswitch *stmt)
 	}
       to_remove_edges.safe_push (e);
       set_and_propagate_unexecutable (e);
+      e->flags &= ~EDGE_EXECUTABLE;
       e->flags |= EDGE_IGNORE;
     }
 
@@ -3822,6 +3825,12 @@  simplify_using_ranges::cleanup_edges_and_switches (void)
   edge e;
   switch_update *su;
 
+  /* Clear any edges marked as not executable.  */
+  if (m_not_executable_flag)
+    {
+      FOR_EACH_VEC_ELT (m_flag_set_edges, i, e)
+	e->flags &= ~m_not_executable_flag;
+    }
   /* Remove dead edges from SWITCH_EXPR optimization.  This leaves the
      CFG in a broken state and requires a cfg_cleanup run.  */
   FOR_EACH_VEC_ELT (to_remove_edges, i, e)
@@ -4124,11 +4133,14 @@  simplify_using_ranges::two_valued_val_range_p (tree var, tree *a, tree *b,
   return false;
 }
 
-simplify_using_ranges::simplify_using_ranges (range_query *query)
+simplify_using_ranges::simplify_using_ranges (range_query *query,
+					      int not_executable_flag)
   : query (query)
 {
   to_remove_edges = vNULL;
   to_update_switch_stmts = vNULL;
+  m_not_executable_flag = not_executable_flag;
+  m_flag_set_edges = vNULL;
 }
 
 simplify_using_ranges::~simplify_using_ranges ()
diff --git a/gcc/vr-values.h b/gcc/vr-values.h
index 46939081c61..821bcb9d58d 100644
--- a/gcc/vr-values.h
+++ b/gcc/vr-values.h
@@ -30,9 +30,11 @@  along with GCC; see the file COPYING3.  If not see
 class simplify_using_ranges
 {
 public:
-  simplify_using_ranges (class range_query *query = NULL);
+  simplify_using_ranges (range_query *query = NULL,
+			 int not_executable_flag = 0);
   ~simplify_using_ranges ();
-  void set_range_query (class range_query *q) { query = q; }
+  void set_range_query (class range_query *q, int not_executable_flag = 0)
+      { query = q; m_not_executable_flag = not_executable_flag; }
 
   bool simplify (gimple_stmt_iterator *);
 
@@ -82,6 +84,8 @@  private:
   vec<edge> to_remove_edges;
   vec<switch_update> to_update_switch_stmts;
   class range_query *query;
+  int m_not_executable_flag;   // Non zero if not_executable flag exists.
+  vec<edge> m_flag_set_edges;  // List of edges with flag to be cleared.
 };
 
 /* The VR_VALUES class holds the current view of range information
-- 
2.17.2