diff mbox series

[committed] analyzer: support reverse direction in shortest-paths.h

Message ID 20210311225623.3131937-1-dmalcolm@redhat.com
State New
Headers show
Series [committed] analyzer: support reverse direction in shortest-paths.h | expand

Commit Message

David Malcolm March 11, 2021, 10:56 p.m. UTC
This patch generalizes shortest-path.h so that it can be used to
find the shortest path from each node to a given target node (on top
of the existing support for finding the shortest path from a given
origin node to each node).

I've marked this as "analyzer" as this is the only code using
shortest-paths.h.

This patch is required by followup work to fix PR analyzer/96374.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r11-7639-g5e33e5b042a6a830c40cee3d0a925bc49dcfe069.

gcc/analyzer/ChangeLog:
	* diagnostic-manager.cc (epath_finder::epath_finder):
	Update shortest_paths init for new param.

gcc/ChangeLog:
	* digraph.cc (selftest::test_shortest_paths): Update
	shortest_paths init for new param.  Add test of
	SPS_TO_GIVEN_TARGET.
	* shortest-paths.h (enum shortest_path_sense): New.
	(shortest_paths::shortest_paths): Add "sense" param.
	Update for renamings.  Generalize to use "sense" param.
	(shortest_paths::get_shortest_path): Rename param.
	(shortest_paths::m_sense): New field.
	(shortest_paths::m_prev): Rename...
	(shortest_paths::m_best_edge): ...to this.
	(shortest_paths::get_shortest_path): Update for renamings.
	Conditionalize flipping of path on sense of traversal.
---
 gcc/analyzer/diagnostic-manager.cc |   2 +-
 gcc/digraph.cc                     |  39 +++++++++-
 gcc/shortest-paths.h               | 121 +++++++++++++++++++++--------
 3 files changed, 125 insertions(+), 37 deletions(-)
diff mbox series

Patch

diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index 7f20841768b..e84953e82ee 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -73,7 +73,7 @@  class epath_finder
 public:
   epath_finder (const exploded_graph &eg)
   : m_eg (eg),
-    m_sep (eg, eg.get_origin ())
+    m_sep (eg, eg.get_origin (), SPS_FROM_GIVEN_ORIGIN)
   {
   }
 
diff --git a/gcc/digraph.cc b/gcc/digraph.cc
index 3441a8586cb..e6966b076ca 100644
--- a/gcc/digraph.cc
+++ b/gcc/digraph.cc
@@ -147,7 +147,8 @@  test_shortest_paths ()
 
   /* Use "A" as the origin; all nodes should be reachable.  */
   {
-    shortest_paths<test_graph_traits, test_path> sp (g, a);
+    shortest_paths<test_graph_traits, test_path> sp (g, a,
+						     SPS_FROM_GIVEN_ORIGIN);
 
     test_path path_to_a = sp.get_shortest_path (a);
     ASSERT_EQ (path_to_a.m_edges.length (), 0); /* Trivial path.  */
@@ -181,7 +182,8 @@  test_shortest_paths ()
 
   /* Use "B" as the origin, so only E and F are reachable.  */
   {
-    shortest_paths<test_graph_traits, test_path> sp (g, b);
+    shortest_paths<test_graph_traits, test_path> sp (g, b,
+						     SPS_FROM_GIVEN_ORIGIN);
 
     test_path path_to_a = sp.get_shortest_path (a);
     ASSERT_EQ (path_to_a.m_edges.length (), 0); /* No path.  */
@@ -207,7 +209,8 @@  test_shortest_paths ()
 
   /* Use "C" as the origin, so only D and F are reachable.  */
   {
-    shortest_paths<test_graph_traits, test_path> sp (g, c);
+    shortest_paths<test_graph_traits, test_path> sp (g, c,
+						     SPS_FROM_GIVEN_ORIGIN);
 
     test_path path_to_a = sp.get_shortest_path (a);
     ASSERT_EQ (path_to_a.m_edges.length (), 0); /* No path.  */
@@ -229,6 +232,36 @@  test_shortest_paths ()
     ASSERT_EQ (path_to_f.m_edges.length (), 1);
     ASSERT_EQ (path_to_f.m_edges[0], cf);
   }
+
+  /* Test of SPS_TO_GIVEN_TARGET.  Use "F" as the target.  */
+  {
+    shortest_paths<test_graph_traits, test_path> sp (g, f,
+						     SPS_TO_GIVEN_TARGET);
+
+    test_path path_to_a = sp.get_shortest_path (a);
+    ASSERT_EQ (path_to_a.m_edges.length (), 2);
+    ASSERT_EQ (path_to_a.m_edges[0], ac);
+    ASSERT_EQ (path_to_a.m_edges[1], cf);
+
+    test_path path_to_b = sp.get_shortest_path (b);
+    ASSERT_EQ (path_to_b.m_edges.length (), 2);
+    ASSERT_EQ (path_to_b.m_edges[0], be);
+    ASSERT_EQ (path_to_b.m_edges[1], ef);
+
+    test_path path_to_c = sp.get_shortest_path (c);
+    ASSERT_EQ (path_to_c.m_edges.length (), 1);
+    ASSERT_EQ (path_to_c.m_edges[0], cf);
+
+    test_path path_to_d = sp.get_shortest_path (d);
+    ASSERT_EQ (path_to_d.m_edges.length (), 0); /* No path.  */
+
+    test_path path_to_e = sp.get_shortest_path (e);
+    ASSERT_EQ (path_to_e.m_edges.length (), 1);
+    ASSERT_EQ (path_to_e.m_edges[0], ef);
+
+    test_path path_to_f = sp.get_shortest_path (f);
+    ASSERT_EQ (path_to_f.m_edges.length (), 0);
+  }
 }
 
 /* Run all of the selftests within this file.  */
diff --git a/gcc/shortest-paths.h b/gcc/shortest-paths.h
index 5648a959895..40d2c2a015a 100644
--- a/gcc/shortest-paths.h
+++ b/gcc/shortest-paths.h
@@ -23,8 +23,24 @@  along with GCC; see the file COPYING3.  If not see
 
 #include "timevar.h"
 
-/* A record of the shortest path to each node in an graph
-   from the origin node.
+enum shortest_path_sense
+{
+  /* Find the shortest path from the given origin node to each
+     node in the graph.  */
+  SPS_FROM_GIVEN_ORIGIN,
+
+  /* Find the shortest path from each node in the graph to the
+     given target node.  */
+  SPS_TO_GIVEN_TARGET
+};
+
+/* A record of the shortest path for each node relative to a special
+   "given node", either:
+   SPS_FROM_GIVEN_ORIGIN:
+     from the given origin node to each node in a graph, or
+   SPS_TO_GIVEN_TARGET:
+     from each node in a graph to the given target node.
+
    The constructor runs Dijkstra's algorithm, and the results are
    stored in this class.  */
 
@@ -37,35 +53,46 @@  public:
   typedef typename GraphTraits::edge_t edge_t;
   typedef Path_t path_t;
 
-  shortest_paths (const graph_t &graph, const node_t *origin);
+  shortest_paths (const graph_t &graph, const node_t *given_node,
+		  enum shortest_path_sense sense);
 
-  path_t get_shortest_path (const node_t *to) const;
+  path_t get_shortest_path (const node_t *other_node) const;
 
 private:
   const graph_t &m_graph;
 
-  /* For each node (by index), the minimal distance to that node from the
-     origin.  */
+  enum shortest_path_sense m_sense;
+
+  /* For each node (by index), the minimal distance between that node
+     and the given node (with direction depending on m_sense).  */
   auto_vec<int> m_dist;
 
-  /* For each exploded_node (by index), the previous edge in the shortest
-     path from the origin.  */
-  auto_vec<const edge_t *> m_prev;
+  /* For each node (by index):
+     SPS_FROM_GIVEN_ORIGIN:
+       the previous edge in the shortest path from the origin,
+     SPS_TO_GIVEN_TARGET:
+       the next edge in the shortest path to the target.  */
+  auto_vec<const edge_t *> m_best_edge;
 };
 
 /* shortest_paths's constructor.
 
-   Use Dijkstra's algorithm relative to ORIGIN to populate m_dist and
-   m_prev with enough information to be able to generate Path_t instances
-   to give the shortest path to any node in GRAPH from ORIGIN.  */
+   Use Dijkstra's algorithm relative to GIVEN_NODE to populate m_dist and
+   m_best_edge with enough information to be able to generate Path_t instances
+   to give the shortest path...
+   SPS_FROM_GIVEN_ORIGIN: to each node in a graph from the origin node, or
+   SPS_TO_GIVEN_TARGET: from each node in a graph to the target node.  */
 
 template <typename GraphTraits, typename Path_t>
 inline
-shortest_paths<GraphTraits, Path_t>::shortest_paths (const graph_t &graph,
-						     const node_t *origin)
+shortest_paths<GraphTraits, Path_t>::
+shortest_paths (const graph_t &graph,
+		const node_t *given_node,
+		enum shortest_path_sense sense)
 : m_graph (graph),
+  m_sense (sense),
   m_dist (graph.m_nodes.length ()),
-  m_prev (graph.m_nodes.length ())
+  m_best_edge (graph.m_nodes.length ())
 {
   auto_timevar tv (TV_ANALYZER_SHORTEST_PATHS);
 
@@ -74,10 +101,10 @@  shortest_paths<GraphTraits, Path_t>::shortest_paths (const graph_t &graph,
   for (unsigned i = 0; i < graph.m_nodes.length (); i++)
     {
       m_dist.quick_push (INT_MAX);
-      m_prev.quick_push (NULL);
+      m_best_edge.quick_push (NULL);
       queue.quick_push (i);
     }
-  m_dist[origin->m_index] = 0;
+  m_dist[given_node->m_index] = 0;
 
   while (queue.length () > 0)
     {
@@ -107,39 +134,67 @@  shortest_paths<GraphTraits, Path_t>::shortest_paths (const graph_t &graph,
       node_t *n
 	= static_cast <node_t *> (m_graph.m_nodes[idx_with_min_dist]);
 
-      int i;
-      edge_t *succ;
-      FOR_EACH_VEC_ELT (n->m_succs, i, succ)
+      if (m_sense == SPS_FROM_GIVEN_ORIGIN)
 	{
-	  // TODO: only for dest still in queue
-	  node_t *dest = succ->m_dest;
-	  int alt = m_dist[n->m_index] + 1;
-	  if (alt < m_dist[dest->m_index])
+	  int i;
+	  edge_t *succ;
+	  FOR_EACH_VEC_ELT (n->m_succs, i, succ)
 	    {
-	      m_dist[dest->m_index] = alt;
-	      m_prev[dest->m_index] = succ;
+	      // TODO: only for dest still in queue
+	      node_t *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_best_edge[dest->m_index] = succ;
+		}
+	    }
+	}
+      else
+	{
+	  int i;
+	  edge_t *pred;
+	  FOR_EACH_VEC_ELT (n->m_preds, i, pred)
+	    {
+	      // TODO: only for dest still in queue
+	      node_t *src = pred->m_src;
+	      int alt = m_dist[n->m_index] + 1;
+	      if (alt < m_dist[src->m_index])
+		{
+		  m_dist[src->m_index] = alt;
+		  m_best_edge[src->m_index] = pred;
+		}
 	    }
 	}
    }
 }
 
-/* Generate an Path_t instance giving the shortest path to the node
-   TO from the origin node.
+/* Generate an Path_t instance giving the shortest path between OTHER_NODE
+   and the given node.
+
+   SPS_FROM_GIVEN_ORIGIN: shortest path from given origin node to OTHER_NODE
+   SPS_TO_GIVEN_TARGET: shortest path from OTHER_NODE to given target node.
+
    If no such path exists, return an empty path.  */
 
 template <typename GraphTraits, typename Path_t>
 inline Path_t
-shortest_paths<GraphTraits, Path_t>::get_shortest_path (const node_t *to) const
+shortest_paths<GraphTraits, Path_t>::
+get_shortest_path (const node_t *other_node) const
 {
   Path_t result;
 
-  while (m_prev[to->m_index])
+  while (m_best_edge[other_node->m_index])
     {
-      result.m_edges.safe_push (m_prev[to->m_index]);
-      to = m_prev[to->m_index]->m_src;
+      result.m_edges.safe_push (m_best_edge[other_node->m_index]);
+      if (m_sense == SPS_FROM_GIVEN_ORIGIN)
+	other_node = m_best_edge[other_node->m_index]->m_src;
+      else
+	other_node = m_best_edge[other_node->m_index]->m_dest;
     }
 
-  result.m_edges.reverse ();
+  if (m_sense == SPS_FROM_GIVEN_ORIGIN)
+    result.m_edges.reverse ();
 
   return result;
 }