From 75a4835ccfc6e8fad0d56ce967fdf0b73f40209d Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 26 Apr 2019 18:11:35 -0400
Subject: [PATCH 203/217] FIXME: impl-4: add path filtering

---
 gcc/analyzer/impl-4.cc | 249 ++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 192 insertions(+), 57 deletions(-)

diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index 132e300..1908a98 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -48,6 +48,7 @@ namespace impl_4 {
 
 class exploded_graph;
 class exploded_edge;
+class checker4_path;
 
 ////////////////////////////////////////////////////////////////////////////
 
@@ -427,7 +428,8 @@ class exploded_graph : public digraph, public log_user
 {
 public:
   exploded_graph (const supergraph &sg, logger *logger,
-		  const extrinsic_state &ext_state);
+		  const extrinsic_state &ext_state,
+		  int verbosity);
 
   void add_function_entry (function *fun);
   void process_worklist ();
@@ -444,6 +446,9 @@ public:
 			const supernode *node, const gimple *stmt,
 			tree var, state_machine::state_t state,
 			pending_diagnostic *d) const;
+  void prune_path (checker4_path *path,
+		   const state_machine &sm,
+		   tree var, state_machine::state_t state) const;
 
   //private:
   const supergraph &m_sg;
@@ -457,6 +462,8 @@ public:
   exploded_node *m_entrypoint;
 
   const extrinsic_state &m_ext_state;
+
+  int m_verbosity;
 };
 
 /* FIXME.  */
@@ -884,27 +891,7 @@ public:
 class checker4_path : public diagnostic_path
 {
 public:
-#if 0
-  struct link
-  {
-    link (const superedge *edge, const supernode *node)
-    : m_edge (edge), m_node (node)
-    {}
-
-    const superedge *m_edge;
-    const supernode *m_node;
-  };
-#endif
-
-  checker4_path ()
-  : diagnostic_path ()
-#if 0
-    m_links (),
-    m_active_calls (),
-    m_ps (),
-    m_terminator ()
-#endif
-  {}
+  checker4_path () : diagnostic_path () {}
   checker4_path (const checker4_path &other);
   checker4_path& operator= (const checker4_path &other);
 
@@ -935,7 +922,6 @@ public:
     m_events.safe_push (event);
   }
 
-#if 0
   void delete_event (int idx)
   {
     checker4_event *event = m_events[idx];
@@ -943,12 +929,7 @@ public:
     delete event;
   }
 
-  const supernode *get_end_node () const
-  {
-    gcc_assert (m_links.length () > 0);
-    return m_links[m_links.length () - 1].m_node;
-  }
-
+#if 0
   /* Get the SUPEREDGE_RETURN corresponding to the most recent
      unreturned SUPEREDGE_CALL.  Return NULL if there isn't one.  */
   const superedge *get_next_return_edge () const
@@ -1502,8 +1483,9 @@ exploded_edge::dump_dot (pretty_printer *pp) const
 /* FIXME.  */
 
 exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
-				const extrinsic_state &ext_state)
-: log_user (logger), m_sg (sg), m_ext_state (ext_state)
+				const extrinsic_state &ext_state,
+				int verbosity)
+: log_user (logger), m_sg (sg), m_ext_state (ext_state), m_verbosity (verbosity)
 {
   m_entrypoint = get_or_create_node (program_point (NULL, 0, PK_ORIGIN),
 				     program_state (ext_state));
@@ -1816,7 +1798,7 @@ exploded_graph::emit_diagnostic (const state_machine &sm,
 	}
     }
 
-  //m_engine->prune_path (&emission_path, var, state);
+  prune_path (&emission_path, sm, var, state);
   emission_path.add_final_event (sm, node, stmt, var, state, d);
   emission_path.prepare_for_emission (d);
 
@@ -1828,6 +1810,180 @@ exploded_graph::emit_diagnostic (const state_machine &sm,
   delete pp;
 }
 
+/* FIXME.  */
+
+void
+exploded_graph::prune_path (checker4_path *path,
+			    const state_machine &sm,
+			    tree var,
+			    state_machine::state_t state ATTRIBUTE_UNUSED) const
+{
+  JIT_LOG_FUNC (get_logger ());
+  path->maybe_log (get_logger (), "path");
+
+  // TODO
+  /* TODO: go backwards, skipping state change events that aren't var
+     but update the pertinent var when state-copying occurs.  */
+  int idx = path->m_events.length () - 1;
+  while (idx >= 0 && idx < path->m_events.length ())
+    {
+      checker4_event *base_event = path->m_events[idx];
+      if (get_logger ())
+	log  ("considering event %i, with var: %qE, state: %qs",
+	      idx, var, sm.get_state_name (state));
+      switch (base_event->m_kind)
+	{
+	default:
+	  gcc_unreachable ();
+
+	case EK_DEBUG:
+	  if (m_verbosity < 2)
+	    {
+	      log ("filtering event %i: debug event", idx);
+	      path->delete_event (idx);
+	    }
+	  break;
+
+	case EK_STMT:
+	  if (m_verbosity < 2)
+	    {
+	      log ("filtering event %i: statement event", idx);
+	      path->delete_event (idx);
+	    }
+	  break;
+
+	case EK_FUNCTION_ENTRY:
+	  if (m_verbosity < 1)
+	    {
+	      log ("filtering event %i: function entry", idx);
+	      path->delete_event (idx);
+	    }
+	  break;
+
+	case EK_STATE_CHANGE:
+	  {
+	    state_change_event *state_change = (state_change_event *)base_event;
+	    if (state_change->m_var == var)
+	      {
+		if (state_change->m_origin)
+		  {
+		    log ("event %i: switching var of interest from %qE to %qE",
+			 idx, var, state_change->m_origin);
+		    var = state_change->m_origin;
+		  }
+	      }
+	    else if (m_verbosity < 2)
+	      {
+		log ("filtering event %i: state change to %qE unrelated to %qE",
+		     idx, state_change->m_var, var);
+		path->delete_event (idx);
+	      }
+	  }
+	  break;
+
+	case EK_EDGE:
+	  {
+	    edge_event *event = (edge_event *)base_event;
+#if 0
+	    inform (base_event->m_loc, "considering edge_event %i: %s",
+		    idx, base_event->m_desc);
+#endif
+	    switch (event->m_edge->m_kind)
+	      {
+	      case SUPEREDGE_CFG_EDGE:
+		{
+		  const cfg_superedge& cfg_superedge
+		    = event->get_cfg_superedge ();
+		  const supernode *dest = event->m_edge->m_dest;
+		  /* Do we have an SSA_NAME defined via a phi node in
+		     the dest CFG node?  */
+		  if (TREE_CODE (var) == SSA_NAME)
+		    if (SSA_NAME_DEF_STMT (var)->bb == dest->m_bb)
+		      {
+			if (const gphi *phi
+			    = dyn_cast <gphi *> (SSA_NAME_DEF_STMT (var)))
+			  {
+			    /* Update var based on its phi node.  */
+			    tree old_var = var;
+			    var = cfg_superedge.get_phi_arg (phi);
+			    log ("updating from %qE to %qE based on phi node",
+				 old_var, var);
+			    if (get_logger ())
+			      {
+				pretty_printer pp;
+				pp_gimple_stmt_1 (&pp, (gimple *)phi, 0, (dump_flags_t)0);
+				// FIXME: const_cast!
+				log ("  phi: %s", pp_formatted_text (&pp));
+			      }
+			  }
+		      }
+
+		  /* TODO: is this edge significant to var?
+		     See if var can be in other states in the dest, but not
+		     in other states in the src?
+		     Must have multiple sibling edges.  */
+		  // TODO: for now, filter all intraprocedural edges:
+		  if (m_verbosity < 2)
+		    {
+		      log ("filtering event %i: CFG edge", idx);
+		      path->delete_event (idx);
+		    }
+		}
+		break;
+
+	      case SUPEREDGE_CALL:
+		{
+		  const call_superedge& call_superedge
+		    = event->get_call_superedge ();
+		  tree caller_var
+		    = call_superedge.map_expr_from_callee_to_caller (var);
+		  if (caller_var)
+		    var = caller_var;
+		}
+		break;
+	      case SUPEREDGE_RETURN:
+		// TODO: potentially update var/state based on return value,
+		// args etc
+		{
+		  const call_superedge& call_superedge
+		    = event->get_call_superedge ();
+		  tree callee_var
+		    = call_superedge.map_expr_from_caller_to_callee (var);
+		  if (callee_var)
+		    var = callee_var;
+		}
+		break;
+	      case SUPEREDGE_INTRAPROCEDURAL_CALL:
+		break;
+	      }
+	  }
+	  break;
+
+	case EK_WARNING:
+	  break;
+	}
+      idx--;
+    }
+
+  /* Second pass.  If all we're left with is in one function, then
+     filter function entry events.  */
+  if (!path->interprocedural_p ())
+    {
+      // FIXME
+      int idx = path->m_events.length () - 1;
+      while (idx >= 0 && idx < path->m_events.length ())
+	{
+	  checker4_event *base_event = path->m_events[idx];
+	  if (base_event->m_kind == EK_FUNCTION_ENTRY)
+	    {
+	      log ("filtering event %i:"
+		   " function entry for purely intraprocedural path", idx);
+	      path->delete_event (idx);
+	    }
+	  idx--;
+	}
+    }
+}
 
 ////////////////////////////////////////////////////////////////////////////
 
@@ -1968,6 +2124,7 @@ checker4_path::operator= (const checker4_path &other)
   }
   return *this;
 }
+#endif
 
 /* FIXME.  */
 std::string
@@ -1986,33 +2143,11 @@ checker4_path::to_str () const
       event_desc.maybe_free ();
     }
   result += "]";
-  {
-    result += ", m_links=[";
-    link *link;
-    FOR_EACH_VEC_ELT (m_links, i, link)
-      {
-	if (i > 0)
-	  result += ", ";
-	result += std::to_string (link->m_node->m_index);
-      }
-    result += "]";
-  }
-  {
-    result += ", m_active_calls=[";
-    cgraph_edge *edge;
-    FOR_EACH_VEC_ELT (m_active_calls, i, edge)
-      {
-	if (i > 0)
-	  result += ", ";
-	result += m_engine->m_sg.get_edge_for_call (edge)->to_str ();
-      }
-    result += "]";
-  }
-  result += ", m_ps=" + m_ps.to_str ();
-  result += ", m_terminator=" + m_terminator.to_str ();
   return result;
 }
 
+#if 0
+
 /* Record that we've reached NODE, via EDGE.  EDGE may be NULL,
    to cover entrypoints to the analysis.
    Update m_active_calls to reflect call and return edges.
@@ -2153,7 +2288,7 @@ impl_run_checkers (logger *logger)
   //engine e (sg, sm, logger, analyzer_verbosity);
 
   // TODO: add initial nodes
-  impl_4::exploded_graph eg (sg, logger, checkers);
+  impl_4::exploded_graph eg (sg, logger, checkers, analyzer_verbosity);
 
   cgraph_node *node;
   FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
-- 
1.8.5.3

