From 528f6ed05e16f1b5cedad68597b35b4afe04b234 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 25 Apr 2019 14:59:43 -0400
Subject: [PATCH 198/217] FIXME: impl-4: implement shortest_paths

---
 gcc/analyzer/digraph.h |   2 +-
 gcc/analyzer/impl-4.cc | 156 ++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 149 insertions(+), 9 deletions(-)

diff --git a/gcc/analyzer/digraph.h b/gcc/analyzer/digraph.h
index e361453..fff82d1 100644
--- a/gcc/analyzer/digraph.h
+++ b/gcc/analyzer/digraph.h
@@ -54,7 +54,7 @@ class digraph
   void add_node (dnode *node);
   void add_edge (dedge *edge);
 
- protected:
+  //protected:
   auto_delete_vec<dnode> m_nodes;
   auto_delete_vec<dedge> m_edges;
 };
diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index ea497a6..3e71c6b 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -47,6 +47,7 @@
 namespace impl_4 {
 
 class exploded_graph;
+class exploded_edge;
 
 ////////////////////////////////////////////////////////////////////////////
 
@@ -103,6 +104,9 @@ struct pod_hash_traits : typed_noop_remove<Type>
 ////////////////////////////////////////////////////////////////////////////
 
 enum point_kind {
+  /* A "fake" node which has edges to all entrypoints.  */
+  PK_ORIGIN,
+
   PK_BEFORE_SUPERNODE,
   PK_BEFORE_STMT,
   PK_AFTER_STMT,
@@ -283,6 +287,7 @@ class program_state
 {
 public:
   program_state (int num_checkers);
+  program_state (const extrinsic_state &ext_state);
   program_state (const program_state &other);
   program_state& operator= (const program_state &other);
 
@@ -392,6 +397,8 @@ class exploded_node : public dnode
      In theory we could just pass this around instead.  */
   const extrinsic_state &m_ext_state;
   int m_index;
+
+  auto_vec<exploded_edge *> m_succs;
 };
 
 class exploded_edge : public dedge
@@ -401,7 +408,7 @@ class exploded_edge : public dedge
 		 const superedge *sedge);
   void dump_dot (pretty_printer *pp) const FINAL OVERRIDE;
 
-private:
+  //private:
   exploded_node *m_src;
   exploded_node *m_dest;
   const superedge *m_sedge;
@@ -423,7 +430,7 @@ public:
   void add_edge (exploded_node *src, exploded_node *dest,
 		 const superedge *sedge);
 
- private:
+  //private:
   const supergraph &m_sg;
 
   /* Map from point/state to exploded node.  */
@@ -431,11 +438,34 @@ public:
   map_t m_point_and_state_to_node;
 
   auto_vec<exploded_node *> m_worklist;
-  auto_vec<exploded_node *> m_entrypoints;
+
+  exploded_node *m_entrypoint;
 
   const extrinsic_state &m_ext_state;
 };
 
+/* FIXME.  */
+
+class shortest_paths
+{
+public:
+  shortest_paths (const exploded_graph &eg);
+
+  void get_shortest_path (const exploded_node *to,
+			  vec<const exploded_edge *> *out) const;
+
+private:
+  const exploded_graph &m_eg;
+
+  /* For each exploded_node (by index), the minimal distance to that
+     node from the origin.  */
+  auto_vec<int> m_dist;
+
+  /* For each exploded_node (by index), the previous exploded_edge in
+     the shortest path from the origin.  */
+  auto_vec<const exploded_edge *> m_prev;
+};
+
 #if 0
 /* FIXME.  */
 
@@ -481,6 +511,11 @@ program_point::print (pretty_printer *pp) const
     {
     default:
       gcc_unreachable ();
+
+    case PK_ORIGIN:
+      pp_printf (pp, "origin");
+      break;
+
     case PK_BEFORE_SUPERNODE:
       pp_printf (pp, "before SN: %i", m_supernode->m_index);
       break;
@@ -570,13 +605,13 @@ sm_state_map::set_state (path_var pv, state_machine::state_t state)
 
 ////////////////////////////////////////////////////////////////////////////
 
-class impl_sm_context : public sm_context
+class impl_sm_context : public sm_context, public log_user
 {
 public:
   impl_sm_context (const exploded_graph &eg,
 		   const exploded_node *node,
 		   sm_state_map *smap)
-  : m_eg (eg), m_node (node), m_smap (smap)
+  : log_user (eg.get_logger ()), m_eg (eg), m_node (node), m_smap (smap)
   {
   }
 
@@ -585,7 +620,6 @@ public:
 		      state_machine::state_t from,
 		      state_machine::state_t to,
 		      tree origin) FINAL OVERRIDE
-
   {
     path_var pv (var, 0); // FIXME: stack depth
     state_machine::state_t current = m_smap->get_state (pv);
@@ -603,6 +637,18 @@ public:
     if (state == current)
       {
 	/* TODO: generate a path.  */
+	/* TODO: cache the diagnostics, and flush them all at the
+	   end, with one shortest_paths instance, and so finding
+	   the shortest path, rather than just the first path.  */
+	shortest_paths sp (m_eg);
+	auto_vec<const exploded_edge *> path;
+	sp.get_shortest_path (m_node, &path);
+	if (get_logger ())
+	  for (int i = 0; i < path.length (); i++)
+	    log ("EN %i -> EN %i",
+		 path[i]->m_src->m_index,
+		 path[i]->m_dest->m_index);
+
 	gcc_rich_location rich_loc (stmt->location);
 	//rich_loc.set_path (&emission_path);
 
@@ -619,6 +665,13 @@ public:
 
 ////////////////////////////////////////////////////////////////////////////
 
+program_state::program_state (const extrinsic_state &ext_state)
+{
+  int num_states = ext_state.m_checkers.length ();
+  for (int i = 0; i < num_states; i++)
+    m_checker_states.safe_push (new sm_state_map ());
+}
+
 program_state::program_state (int num_states)
 {
   for (int i = 0; i < num_states; i++)
@@ -914,6 +967,8 @@ 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)
 {
+  m_entrypoint = get_or_create_node (program_point (NULL, 0, PK_ORIGIN),
+				     program_state (ext_state));
 }
 
 /* FIXME.  */
@@ -922,9 +977,10 @@ void
 exploded_graph::add_function_entry (function *fun)
 {
   program_point point = program_point::from_function_entry (m_sg, fun);
-  program_state state (m_ext_state.m_checkers.length ());
+  program_state state (m_ext_state);
   point_and_state ps (point, state);
-  m_entrypoints.safe_push (get_or_create_node (ps));
+
+  add_edge (m_entrypoint, get_or_create_node (ps), NULL);
 }
 
 /* Get or create an exploded_node for PS.  If a new node is created, it is
@@ -967,6 +1023,7 @@ exploded_graph::add_edge (exploded_node *src, exploded_node *dest,
 {
   exploded_edge *e = new exploded_edge (src, dest, sedge);
   digraph::add_edge (e);
+  src->m_succs.safe_push (e);
 }
 
 /* FIXME.  */
@@ -1002,6 +1059,11 @@ exploded_graph::process_node (exploded_node *node)
     {
     default:
       gcc_unreachable ();
+    case PK_ORIGIN:
+      /* This node exists to simplify finding the shortest path
+	 to an exploded_node.  */
+      break;
+
     case PK_BEFORE_SUPERNODE:
       {
 	if (point.m_supernode->m_stmts.length () > 0)
@@ -1096,6 +1158,84 @@ exploded_graph::process_node (exploded_node *node)
 
 ////////////////////////////////////////////////////////////////////////////
 
+/* FIXME.  */
+
+shortest_paths::shortest_paths (const exploded_graph &eg)
+: m_eg (eg),
+  m_dist (eg.m_nodes.length ()),
+  m_prev (eg.m_nodes.length ())
+{
+  /* Dijkstra's algorithm.  */
+  auto_vec<int> queue (eg.m_nodes.length ());
+
+  for (int i = 0; i < eg.m_nodes.length (); i++)
+    {
+      m_dist.quick_push (INT_MAX);
+      m_prev.quick_push (NULL);
+      queue.quick_push (i);
+    }
+  m_dist[eg.m_entrypoint->m_index] = 0;
+
+  while (queue.length () > 0)
+    {
+      /* Get minimal distance in queue.
+	 FIXME: this is O(N^2); replace with a priority queue.  */
+      int idx_with_min_dist = -1;
+      int idx_in_queue_with_min_dist = -1;
+      int min_dist = INT_MAX;
+      for (int i = 0; i < queue.length (); i++)
+	{
+	  int idx = queue[i];
+	  if (m_dist[queue[i]] < min_dist)
+	    {
+	      min_dist = m_dist[idx];
+	      idx_with_min_dist = idx;
+	      idx_in_queue_with_min_dist = i;
+	    }
+	}
+      gcc_assert (idx_with_min_dist != -1);
+      gcc_assert (idx_in_queue_with_min_dist != -1);
+
+      // FIXME: this is confusing: there are two indices here
+
+      queue.unordered_remove (idx_in_queue_with_min_dist);
+
+      exploded_node *n
+	= static_cast <exploded_node *> (m_eg.m_nodes[idx_with_min_dist]);
+
+      int i;
+      exploded_edge *succ;
+      FOR_EACH_VEC_ELT (n->m_succs, i, succ)
+	{
+	  // TODO: only for dest still in queue
+	  exploded_node *dest = succ->m_dest;
+	  int alt = m_dist[n->m_index] + 1;
+	  if (alt < m_dist[dest->m_index])
+	    {
+	      m_dist[dest->m_index] = alt;
+	      m_prev[dest->m_index] = succ;
+	    }
+	}
+   }
+}
+
+/* FIXME.  */
+
+void
+shortest_paths::get_shortest_path (const exploded_node *to,
+				   vec<const exploded_edge *> *out) const
+{
+  while (m_prev[to->m_index])
+    {
+      out->safe_push (m_prev[to->m_index]);
+      to = m_prev[to->m_index]->m_src;
+    }
+
+  out->reverse ();
+}
+
+////////////////////////////////////////////////////////////////////////////
+
 #if 0
 /* FIXME.  */
 
-- 
1.8.5.3

