From c404a49ee235a7de7ea3770ac0022f29d59621ad Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 29 Apr 2019 17:56:19 -0400
Subject: [PATCH 208/217] FIXME: impl-4: add clustering to dot output

---
 gcc/analyzer/digraph.cc |  40 ++++++++---
 gcc/analyzer/digraph.h  |  19 ++++--
 gcc/analyzer/impl-4.cc  | 178 +++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 211 insertions(+), 26 deletions(-)

diff --git a/gcc/analyzer/digraph.cc b/gcc/analyzer/digraph.cc
index b1e7c32..d03fd6b 100644
--- a/gcc/analyzer/digraph.cc
+++ b/gcc/analyzer/digraph.cc
@@ -51,14 +51,18 @@ along with GCC; see the file COPYING3.  If not see
 #include "digraph.h"
 
 void
-digraph::dump_dot_to_pp (pretty_printer *pp) const
+digraph::dump_dot_to_pp (pretty_printer *pp, cluster *root_cluster) const
 {
+  graphviz_out gv (pp);
+
   pp_string (pp, "digraph \"");
   pp_write_text_to_stream (pp);
   pp_string (pp, "FIXME: base");
   pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
   pp_string (pp, "\" {\n");
 
+  gv.indent ();
+
   pp_string (pp, "overlap=false;\n");
   pp_string (pp, "compound=true;\n");
 
@@ -68,36 +72,50 @@ digraph::dump_dot_to_pp (pretty_printer *pp) const
      https://gcc-python-plugin.readthedocs.io/en/latest/_images/sample-supergraph.png
   */
 
-  /* Nodes.  */
-  int i;
-  dnode *n;
-  FOR_EACH_VEC_ELT (m_nodes, i, n)
-    n->dump_dot (pp);
+  /* If using clustering, emit all nodes via clusters.  */
+  if (root_cluster)
+    {
+      int i;
+      dnode *n;
+      FOR_EACH_VEC_ELT (m_nodes, i, n)
+	root_cluster->add_node (n);
+      root_cluster->dump_dot (&gv);
+    }
+  else
+    {
+      /* Otherwise, display all nodes at top level.  */
+      int i;
+      dnode *n;
+      FOR_EACH_VEC_ELT (m_nodes, i, n)
+	n->dump_dot (&gv);
+    }
 
   /* Edges.  */
+  int i;
   dedge *e;
   FOR_EACH_VEC_ELT (m_edges, i, e)
-    e->dump_dot (pp);
+    e->dump_dot (&gv);
 
   /* Terminate "digraph" */
+  gv.outdent ();
   pp_string (pp, "}");
   pp_newline (pp);
 }
 
 void
-digraph::dump_dot_to_file (FILE *fp) const
+digraph::dump_dot_to_file (FILE *fp, cluster *root_cluster) const
 {
   pretty_printer pp;
   pp.buffer->stream = fp;
-  dump_dot_to_pp (&pp);
+  dump_dot_to_pp (&pp, root_cluster);
   pp_flush (&pp);
 }
 
 void
-digraph::dump_dot (const char *path) const
+digraph::dump_dot (const char *path, cluster *root_cluster) const
 {
   FILE *fp = fopen (path, "w");
-  dump_dot_to_file (fp);
+  dump_dot_to_file (fp, root_cluster);
   fclose (fp);
 }
 
diff --git a/gcc/analyzer/digraph.h b/gcc/analyzer/digraph.h
index fff82d1..2d9cb6a 100644
--- a/gcc/analyzer/digraph.h
+++ b/gcc/analyzer/digraph.h
@@ -30,7 +30,7 @@ class dnode
  public:
   virtual ~dnode () {}
 
-  virtual void dump_dot (pretty_printer *pp) const = 0;
+  virtual void dump_dot (graphviz_out *gv) const = 0;
 };
 
 class dedge
@@ -38,7 +38,16 @@ class dedge
  public:
   virtual ~dedge () {}
 
-  virtual void dump_dot (pretty_printer *pp) const = 0;
+  virtual void dump_dot (graphviz_out *gv) const = 0;
+};
+
+class cluster
+{
+ public:
+  virtual void add_node (dnode *node) = 0;
+
+  /* Recursively dump the cluster, all nodes, and child clusters.  */
+  virtual void dump_dot (graphviz_out *gv) const = 0;
 };
 
 class digraph
@@ -47,9 +56,9 @@ class digraph
   digraph () {}
   virtual ~digraph () {}
 
-  void dump_dot_to_pp (pretty_printer *pp) const;
-  void dump_dot_to_file (FILE *fp) const;
-  void dump_dot (const char *path) const;
+  void dump_dot_to_pp (pretty_printer *pp, cluster *root_cluster) const;
+  void dump_dot_to_file (FILE *fp, cluster *root_cluster) const;
+  void dump_dot (const char *path, cluster *root_cluster) const;
 
   void add_node (dnode *node);
   void add_edge (dedge *edge);
diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index 9230de5..5406993 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -147,6 +147,15 @@ public:
 	    && m_kind == other.m_kind);
   }
 
+  const supernode *get_supernode () const { return m_supernode; }
+  function *get_function () const
+  {
+    if (m_supernode)
+      return m_supernode->m_fun;
+    else
+      return NULL;
+  }
+
   const gimple *get_stmt () const;
 
   static program_point from_function_entry (const supergraph &sg,
@@ -399,7 +408,7 @@ class exploded_node : public dnode
   {
   }
 
-  void dump_dot (pretty_printer *pp) const FINAL OVERRIDE;
+  void dump_dot (graphviz_out *gv) const FINAL OVERRIDE;
   void dump_dot_id (pretty_printer *pp) const;
 
   program_state on_stmt (const exploded_graph &eg) const;
@@ -413,6 +422,15 @@ class exploded_node : public dnode
 		      program_state *new_state) const;
 
   const program_point &get_point () const { return m_ps.m_point; }
+  const supernode *get_supernode () const
+  {
+    return get_point ().get_supernode ();
+  }
+  function *get_function () const
+  {
+    return get_point ().get_function ();
+  }
+
   const program_state &get_state () const { return m_ps.m_state; }
 
   point_and_state m_ps;
@@ -429,7 +447,7 @@ class exploded_edge : public dedge
  public:
   exploded_edge (exploded_node *src, exploded_node *dest,
 		 const superedge *sedge);
-  void dump_dot (pretty_printer *pp) const FINAL OVERRIDE;
+  void dump_dot (graphviz_out *gv) const FINAL OVERRIDE;
 
   //private:
   exploded_node *m_src;
@@ -1282,10 +1300,9 @@ program_state::to_str (const extrinsic_state &ext_state) const
 /* FIXME.  */
 
 void
-exploded_node::dump_dot (pretty_printer *pp) const
+exploded_node::dump_dot (graphviz_out *gv) const
 {
-  graphviz_out tmp_gv (pp);
-  graphviz_out *gv = &tmp_gv;
+  pretty_printer *pp = gv->m_pp;
 
   // TODO: we'll probably want to add clusters for functions and for supernodes
   // TODO: maybe have an explicit hierarchy of cluster objects?
@@ -1605,11 +1622,9 @@ exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest,
 /* FIXME.  */
 
 void
-exploded_edge::dump_dot (pretty_printer *pp) const
+exploded_edge::dump_dot (graphviz_out *gv) const
 {
-  // TODO
-  graphviz_out tmp_gv (pp);
-  graphviz_out *gv = &tmp_gv;
+  pretty_printer *pp = gv->m_pp;
 
   const char *style = "\"solid,bold\"";
   const char *color = "black";
@@ -2321,6 +2336,148 @@ checker4_path::to_str () const
 
 ////////////////////////////////////////////////////////////////////////////
 
+class en_cluster : public cluster
+{
+public:
+  void add_node (dnode *node) FINAL OVERRIDE
+  {
+    exploded_node *en = static_cast <exploded_node *> (node);
+    add_exploded_node (en);
+  }
+  virtual void add_exploded_node (exploded_node *en) = 0;
+};
+
+class supernode_cluster : public en_cluster
+{
+public:
+  supernode_cluster (const supernode *supernode) : m_supernode (supernode) {}
+
+  // TODO: dtor?
+
+  void dump_dot (graphviz_out *gv) const FINAL OVERRIDE
+  {
+    gv->println ("subgraph \"cluster_supernode_%i\" {",
+		 m_supernode->m_index);
+    gv->indent ();
+    gv->println ("style=\"dashed\";");
+    gv->println ("label=\"SN: %i\";", m_supernode->m_index);
+
+    int i;
+    exploded_node *enode;
+    FOR_EACH_VEC_ELT (m_enodes, i, enode)
+      enode->dump_dot (gv);
+
+    /* Terminate subgraph.  */
+    gv->outdent ();
+    gv->println ("}");
+  }
+
+  void add_exploded_node (exploded_node *en) FINAL OVERRIDE
+  {
+    m_enodes.safe_push (en);
+  }
+
+private:
+  const supernode *m_supernode;
+  auto_vec <exploded_node *> m_enodes;
+};
+
+class function_cluster : public en_cluster
+{
+public:
+  function_cluster (function *fun) : m_fun (fun) {}
+
+  // TODO: dtor
+
+  void dump_dot (graphviz_out *gv) const FINAL OVERRIDE
+  {
+    const char *funcname = function_name (m_fun);
+
+    gv->println ("subgraph \"cluster_function_%p\" {", (const void *)m_fun);
+    gv->indent ();
+    gv->println ("label=\"function: %s\";", funcname);
+
+    for (map_t::iterator iter = m_map.begin ();
+	 iter != m_map.end ();
+	 ++iter)
+      (*iter).second->dump_dot (gv);
+
+    /* Terminate subgraph.  */
+    gv->outdent ();
+    gv->println ("}");
+  }
+
+  void add_exploded_node (exploded_node *en) FINAL OVERRIDE
+  {
+    const supernode *supernode = en->get_supernode ();
+    gcc_assert (supernode);
+    supernode_cluster **slot = m_map.get (supernode);
+    if (slot)
+      (*slot)->add_exploded_node (en);
+    else
+      {
+	supernode_cluster *child = new supernode_cluster (supernode);
+	m_map.put (supernode, child);
+	child->add_exploded_node (en);
+      }
+  }
+
+private:
+  function *m_fun;
+  typedef hash_map<const supernode *, supernode_cluster *> map_t;
+  map_t m_map;
+};
+
+class root_cluster : public en_cluster
+{
+public:
+  ~root_cluster ()
+  {
+    // TODO: delete the function_cluster instances in the map
+  }
+
+  void dump_dot (graphviz_out *gv) const FINAL OVERRIDE
+  {
+    int i;
+    exploded_node *enode;
+    FOR_EACH_VEC_ELT (m_enodes, i, enode)
+      enode->dump_dot (gv);
+
+    for (map_t::iterator iter = m_map.begin ();
+	 iter != m_map.end ();
+	 ++iter)
+      (*iter).second->dump_dot (gv);
+  }
+
+  void add_exploded_node (exploded_node *en) FINAL OVERRIDE
+  {
+    function *fun = en->get_function ();
+    if (!fun)
+      {
+	m_enodes.safe_push (en);
+	return;
+      }
+
+    function_cluster **slot = m_map.get (fun);
+    if (slot)
+      (*slot)->add_exploded_node (en);
+    else
+      {
+	function_cluster *child = new function_cluster (fun);
+	m_map.put (fun, child);
+	child->add_exploded_node (en);
+      }
+  }
+
+private:
+  typedef hash_map<function *, function_cluster *> map_t;
+  map_t m_map;
+
+  auto_vec <exploded_node *> m_enodes;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
 #if 0
 /* FIXME.  */
 
@@ -2387,7 +2544,8 @@ impl_run_checkers (logger *logger)
     {
       char *filename
 	= concat (dump_base_name, ".eg.dot", NULL);
-      eg.dump_dot (filename);
+      root_cluster c;
+      eg.dump_dot (filename, &c);
       free (filename);
     }
 
-- 
1.8.5.3

