From 94e30df6c90bae83d5a481b9ebbe1fe2b30f6125 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 23 Apr 2019 12:25:03 -0400
Subject: [PATCH 192/217] FIXME: impl-4: initial impl

---
 gcc/analyzer/Make-lang.in      |   2 +-
 gcc/analyzer/analyzer-pass.cc  |   3 +
 gcc/analyzer/digraph.cc        |  16 +-
 gcc/analyzer/digraph.h         |   9 +-
 gcc/analyzer/engine.cc         |  10 -
 gcc/analyzer/engine.h          |   1 +
 gcc/analyzer/exploded-graph.cc |  45 --
 gcc/analyzer/exploded-graph.h  |  59 ---
 gcc/analyzer/impl-3.cc         |   1 -
 gcc/analyzer/impl-4.cc         | 986 +++++++++++++++++++++++++++++++++++++++++
 10 files changed, 1011 insertions(+), 121 deletions(-)
 delete mode 100644 gcc/analyzer/exploded-graph.cc
 delete mode 100644 gcc/analyzer/exploded-graph.h
 create mode 100644 gcc/analyzer/impl-4.cc

diff --git a/gcc/analyzer/Make-lang.in b/gcc/analyzer/Make-lang.in
index 95c14d8..f742b9d 100644
--- a/gcc/analyzer/Make-lang.in
+++ b/gcc/analyzer/Make-lang.in
@@ -60,7 +60,6 @@ ANALYZER_PLUGIN_SO_OBJS = \
 	analyzer/constraints.o \
 	analyzer/digraph.o \
 	analyzer/engine.o \
-	analyzer/exploded-graph.o \
 	analyzer/graphviz.o \
 	analyzer/hardcoded-state-machine.o \
 	analyzer/hardcoded-state-machine-malloc.o \
@@ -69,6 +68,7 @@ ANALYZER_PLUGIN_SO_OBJS = \
 	analyzer/hardcoded-state-machine-sensitive.o \
 	analyzer/hardcoded-state-machine-taint.o \
 	analyzer/impl-3.o \
+	analyzer/impl-4.o \
 	analyzer/supergraph.o \
 	analyzer/tristate.o \
 
diff --git a/gcc/analyzer/analyzer-pass.cc b/gcc/analyzer/analyzer-pass.cc
index 8417ef1..b5f3f8c 100644
--- a/gcc/analyzer/analyzer-pass.cc
+++ b/gcc/analyzer/analyzer-pass.cc
@@ -91,6 +91,9 @@ pass_analyzer::execute (function *)
     case 3:
       run_checkers_impl_3 ();
       break;
+    case 4:
+      run_checkers_impl_4 ();
+      break;
     }
   return 0;
 }
diff --git a/gcc/analyzer/digraph.cc b/gcc/analyzer/digraph.cc
index 09218f3..b1e7c32 100644
--- a/gcc/analyzer/digraph.cc
+++ b/gcc/analyzer/digraph.cc
@@ -72,12 +72,12 @@ digraph::dump_dot_to_pp (pretty_printer *pp) const
   int i;
   dnode *n;
   FOR_EACH_VEC_ELT (m_nodes, i, n)
-    n->dump_dot ();
+    n->dump_dot (pp);
 
   /* Edges.  */
   dedge *e;
   FOR_EACH_VEC_ELT (m_edges, i, e)
-    e->dump_dot ();
+    e->dump_dot (pp);
 
   /* Terminate "digraph" */
   pp_string (pp, "}");
@@ -100,3 +100,15 @@ digraph::dump_dot (const char *path) const
   dump_dot_to_file (fp);
   fclose (fp);
 }
+
+void
+digraph::add_node (dnode *node)
+{
+  m_nodes.safe_push (node);
+}
+
+void
+digraph::add_edge (dedge *edge)
+{
+  m_edges.safe_push (edge);
+}
diff --git a/gcc/analyzer/digraph.h b/gcc/analyzer/digraph.h
index 50f5390..e361453 100644
--- a/gcc/analyzer/digraph.h
+++ b/gcc/analyzer/digraph.h
@@ -30,7 +30,7 @@ class dnode
  public:
   virtual ~dnode () {}
 
-  virtual void dump_dot () const = 0;
+  virtual void dump_dot (pretty_printer *pp) const = 0;
 };
 
 class dedge
@@ -38,7 +38,7 @@ class dedge
  public:
   virtual ~dedge () {}
 
-  virtual void dump_dot () const = 0;
+  virtual void dump_dot (pretty_printer *pp) const = 0;
 };
 
 class digraph
@@ -51,7 +51,10 @@ class digraph
   void dump_dot_to_file (FILE *fp) const;
   void dump_dot (const char *path) const;
 
- private:
+  void add_node (dnode *node);
+  void add_edge (dedge *edge);
+
+ protected:
   auto_delete_vec<dnode> m_nodes;
   auto_delete_vec<dedge> m_edges;
 };
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index b549fc8..cc5bb13 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -41,7 +41,6 @@
 #include "fold-const.h"
 #include "tristate.h"
 #include "selftest.h"
-#include "exploded-graph.h"
 
 ////////////////////////////////////////////////////////////////////////////
 
@@ -3763,15 +3762,6 @@ run_one_checker (const supergraph &sg, const state_machine &sm, logger *logger)
       free (filename);
     }
 
-  if (1)
-    {
-      exploded_graph eg;
-      char *filename
-	= concat (dump_base_name, ".", sm.get_name (), ".eg.dot", NULL);
-      eg.dump_dot (filename);
-      free (filename);
-    }
-
   if (logger)
     {
       log_scope s (logger, "per-node state");
diff --git a/gcc/analyzer/engine.h b/gcc/analyzer/engine.h
index 8f0b1f4..853f1c1 100644
--- a/gcc/analyzer/engine.h
+++ b/gcc/analyzer/engine.h
@@ -1,2 +1,3 @@
 extern void run_checkers_impl_2 ();
 extern void run_checkers_impl_3 ();
+extern void run_checkers_impl_4 ();
diff --git a/gcc/analyzer/exploded-graph.cc b/gcc/analyzer/exploded-graph.cc
deleted file mode 100644
index d4771dd..0000000
--- a/gcc/analyzer/exploded-graph.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-#include "config.h"
-#include "gcc-plugin.h"
-#include "system.h"
-#include "coretypes.h"
-#include "tree.h"
-#include "tm.h"
-#include "toplev.h"
-#include "hash-table.h"
-#include "vec.h"
-#include "ggc.h"
-#include "basic-block.h"
-#include "tree-ssa-alias.h"
-#include "internal-fn.h"
-#include "gimple-fold.h"
-#include "tree-eh.h"
-#include "gimple-expr.h"
-#include "is-a.h"
-#include "gimple.h"
-#include "tree-pass.h"
-#include "intl.h"
-#include "context.h"
-#include "diagnostic.h"
-#include "bitmap.h"
-#include "gimple.h"
-#include "gimple-iterator.h"
-#include "gimple-pretty-print.h"
-#include "tree-pretty-print.h"
-#include "analyzer.h"
-#include "graphviz.h"
-#include "cgraph.h"
-#include "exploded-graph.h"
-
-void
-exploded_node::dump_dot () const
-{
-}
-
-void
-exploded_edge::dump_dot () const
-{
-}
-
-exploded_graph::exploded_graph ()
-{
-}
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
deleted file mode 100644
index 1f71dcf..0000000
--- a/gcc/analyzer/exploded-graph.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* FIXME.
-   Copyright (C) 2019 Free Software Foundation, Inc.
-   Contributed by David Malcolm <dmalcolm@redhat.com>.
-
-This file is part of GCC.
-
-GCC is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3, or (at your option)
-any later version.
-
-GCC is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING3.  If not see
-<http://www.gnu.org/licenses/>.  */
-
-#ifndef GCC_ANALYZER_EXPLODED_GRAPH_H
-#define GCC_ANALYZER_EXPLODED_GRAPH_H
-
-#include "digraph.h"
-#include "supergraph.h"
-#include "hardcoded-state-machine.h"
-
-class exploded_node : public dnode
-{
- public:
-  exploded_node (const supernode *supernode,
-		 tree var,
-		 state_machine::state_t state)
-  : m_supernode (supernode),
-    m_var (var),
-    m_state (state)
-  {
-  }
-
-  void dump_dot () const FINAL OVERRIDE;
-
-  const supernode *m_supernode;
-  tree m_var;
-  state_machine::state_t m_state;
-};
-
-class exploded_edge : public dedge
-{
- public:
-  void dump_dot () const FINAL OVERRIDE;
-};
-
-class exploded_graph : public digraph
-{
- public:
-  exploded_graph ();
-};
-
-#endif /* GCC_ANALYZER_EXPLODED_GRAPH_H */
diff --git a/gcc/analyzer/impl-3.cc b/gcc/analyzer/impl-3.cc
index bfdacb5..cb97a39 100644
--- a/gcc/analyzer/impl-3.cc
+++ b/gcc/analyzer/impl-3.cc
@@ -41,7 +41,6 @@
 #include "fold-const.h"
 #include "tristate.h"
 #include "selftest.h"
-#include "exploded-graph.h"
 #include "constraints.h"
 
 namespace impl_3 {
diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
new file mode 100644
index 0000000..a98353e
--- /dev/null
+++ b/gcc/analyzer/impl-4.cc
@@ -0,0 +1,986 @@
+/* FIXME.  */
+#include "config.h"
+#include "gcc-plugin.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tm.h"
+#include "toplev.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "context.h"
+#include "diagnostic.h"
+#include "bitmap.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-pretty-print.h"
+#include "tree-pretty-print.h"
+#include "analyzer.h"
+#include "graphviz.h"
+#include "cgraph.h"
+#include "diagnostic-path.h"
+#include "gcc-rich-location.h"
+#include "tree-dfa.h"
+#include <string>
+#include "supergraph.h"
+#include "hardcoded-state-machine.h"
+#include "analyzer-logging.h"
+#include "stringpool.h"
+#include "selftest.h"
+#include "fold-const.h"
+#include "tristate.h"
+#include "selftest.h"
+#include "digraph.h"
+#include "constraints.h"
+
+namespace impl_4 {
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+char *print (const char *fmt, ...)
+{
+  /* FIXME: all this cloning is likely to be expensive.  */
+  pretty_printer *pp = global_dc->printer->clone ();
+  pp_clear_output_area (pp);
+
+  text_info ti;
+  rich_location rich_loc (line_table, UNKNOWN_LOCATION);
+
+  va_list ap;
+
+  va_start (ap, fmt);
+
+  ti.format_spec = _(fmt);
+  ti.args_ptr = &ap;
+  ti.err_no = 0;
+  ti.x_data = NULL;
+  ti.m_richloc = &rich_loc;
+
+  pp_format (pp, &ti);
+  pp_output_formatted_text (pp);
+
+  va_end (ap);
+  char *result = xstrdup (pp_formatted_text (pp));
+
+  pp_clear_output_area (pp);
+
+  delete pp;
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+template <typename Type>
+struct pod_hash_traits : typed_noop_remove<Type>
+{
+  typedef Type value_type;
+  typedef Type compare_type;
+  static inline hashval_t hash (value_type);
+  static inline bool equal (const value_type &existing,
+			    const value_type &candidate);
+  static inline void mark_deleted (Type &);
+  static inline void mark_empty (Type &);
+  static inline bool is_deleted (Type);
+  static inline bool is_empty (Type);
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+enum point_kind {
+  PK_BEFORE_SUPERNODE,
+  PK_BEFORE_STMT,
+  PK_AFTER_STMT,
+  PK_AFTER_SUPERNODE,
+
+  /* Special values used for hash_map:  */
+  PK_EMPTY,
+  PK_DELETED
+};
+
+class program_point
+{
+public:
+  program_point (const supernode *supernode,
+		 unsigned stmt_idx,
+		 enum point_kind kind)
+  : m_supernode (supernode), m_stmt_idx (stmt_idx), m_kind (kind)
+  {
+  }
+
+  const supernode *m_supernode;
+  //const superedge *m_from_edge; /* for PK_BEFORE_SUPERNODE.  */
+  unsigned m_stmt_idx;
+  enum point_kind m_kind;
+
+  void print (pretty_printer *pp) const;
+  std::string to_str () const;
+
+  hashval_t hash () const
+  {
+    return (hashval_t)((intptr_t)m_supernode >> 3) ^ m_stmt_idx ^ m_kind;
+  }
+  bool operator== (const program_point &other) const
+  {
+    return (m_supernode == other.m_supernode
+	    && m_stmt_idx == other.m_stmt_idx
+	    && m_kind == other.m_kind);
+  }
+
+  const gimple *get_stmt () const;
+
+  static program_point from_function_entry (const supergraph &sg,
+					    function *fun)
+  {
+    return before_supernode (sg.get_node_for_function_entry (fun));
+  }
+
+  static program_point before_supernode (const supernode *supernode)
+  {
+    return program_point (supernode, 0, PK_BEFORE_SUPERNODE);
+  }
+
+  static program_point before_stmt (const supernode *supernode,
+				    unsigned stmt_idx)
+  {
+    return program_point (supernode, stmt_idx, PK_BEFORE_STMT);
+  }
+
+  static program_point after_stmt (const supernode *supernode,
+				   unsigned stmt_idx)
+  {
+    return program_point (supernode, stmt_idx, PK_AFTER_STMT);
+  }
+
+  static program_point after_supernode (const supernode *supernode)
+  {
+    return program_point (supernode, 0, PK_AFTER_SUPERNODE);
+  }
+
+  static program_point empty ()
+  {
+    return program_point (NULL, 0, PK_EMPTY);
+  }
+  static program_point deleted ()
+  {
+    return program_point (NULL, 0, PK_DELETED);
+  }
+};
+
+class program_state
+{
+public:
+  void print (pretty_printer *pp) const;
+  std::string to_str () const;
+
+  path_state m_path_state;
+
+  // TODO: add "backtrace" of calls
+};
+
+struct point_and_state
+{
+  point_and_state (program_point point,
+		   program_state state)
+  : m_point (point),
+    m_state (state)
+  {
+  }
+
+  hashval_t hash () const
+  {
+    return m_point.hash ();// ^ m_state.hash ();
+  }
+
+  program_point m_point;
+  program_state m_state;
+};
+
+} // namespace impl_4
+
+using impl_4::point_and_state;
+using impl_4::pod_hash_traits;
+
+template <> struct default_hash_traits<point_and_state>
+: public pod_hash_traits<point_and_state>
+{
+};
+
+template <>
+inline hashval_t
+pod_hash_traits<point_and_state>::hash (value_type v)
+{
+  return v.hash ();
+}
+
+template <>
+inline bool
+pod_hash_traits<point_and_state>::equal (const value_type &existing,
+				     const value_type &candidate)
+{
+  return (existing.m_point == candidate.m_point
+	  // FIXME:
+	  /*&& existing.m_state == candidate.m_state */);
+}
+template <>
+inline void
+pod_hash_traits<point_and_state>::mark_deleted (value_type &v)
+{
+  v = point_and_state (program_point::deleted (), program_state ());
+}
+template <>
+inline void
+pod_hash_traits<point_and_state>::mark_empty (value_type &v)
+{
+  v = point_and_state (program_point::empty (), program_state ());
+}
+template <>
+inline bool
+pod_hash_traits<point_and_state>::is_deleted (value_type v)
+{
+  return (v.m_point.m_kind == PK_DELETED);
+}
+template <>
+inline bool
+pod_hash_traits<point_and_state>::is_empty (value_type v)
+{
+  return (v.m_point.m_kind == PK_EMPTY);
+}
+
+namespace impl_4 {
+
+class exploded_node : public dnode
+{
+ public:
+  exploded_node (point_and_state ps, int index)
+  : m_ps (ps), m_index (index)
+  {
+  }
+
+  void dump_dot (pretty_printer *pp) const FINAL OVERRIDE;
+  void dump_dot_id (pretty_printer *pp) const;
+
+  point_and_state m_ps;
+  int m_index;
+};
+
+class exploded_edge : public dedge
+{
+ public:
+  exploded_edge (exploded_node *src, exploded_node *dest);
+  void dump_dot (pretty_printer *pp) const FINAL OVERRIDE;
+
+private:
+  exploded_node *m_src;
+  exploded_node *m_dest;
+};
+
+class exploded_graph : public digraph, public log_user
+{
+public:
+  exploded_graph (const supergraph &sg, logger *logger);
+
+  void add_function_entry (function *fun);
+  void process_worklist ();
+  void process_node (exploded_node *node);
+
+  exploded_node *get_or_create_node (const point_and_state &ps);
+  exploded_node *get_or_create_node (const program_point &point,
+				     const program_state &state);
+  void add_edge (exploded_node *src, exploded_node *dest);
+
+ private:
+  const supergraph &m_sg;
+
+  /* Map from point/state to exploded node.  */
+#if 1
+  typedef hash_map <point_and_state, exploded_node *> map_t;
+  map_t m_point_and_state_to_node;
+#endif
+
+  auto_vec<exploded_node *> m_worklist;
+  auto_vec<exploded_node *> m_entrypoints;
+};
+
+/* FIXME.  */
+
+class engine : public log_user
+{
+public:
+  engine (const supergraph &sg, const state_machine &sm,
+	  logger *logger, int verbosity)
+  : log_user (logger), m_sg (sg), m_sm (sm),
+    m_verbosity (verbosity),
+    m_num_traverse_calls (0),
+    m_on_stmt_calls (0),
+    m_num_path_copies (0),
+    m_num_event_copies (0)
+  {
+  }
+
+  void log_stats () const
+  {
+  }
+
+  const supergraph &m_sg;
+
+  // FIXME: this implies one traversal per state machine; should we instead
+  // do multiple state machines in one traversal?
+  const state_machine &m_sm;
+
+  int m_verbosity;
+
+  int m_num_traverse_calls;
+  int m_on_stmt_calls;
+  int m_num_path_copies;
+  int m_num_event_copies;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+void
+program_point::print (pretty_printer *pp) const
+{
+  switch (m_kind)
+    {
+    default:
+      gcc_unreachable ();
+    case PK_BEFORE_SUPERNODE:
+      pp_printf (pp, "before SN: %i", m_supernode->m_index);
+      break;
+
+    case PK_BEFORE_STMT:
+      pp_printf (pp, "before (SN: %i stmt: %i): ", m_supernode->m_index,
+		 m_stmt_idx);
+      pp_gimple_stmt_1 (pp, const_cast <gimple *> (get_stmt ()),
+			0, (dump_flags_t)0);
+      break;
+
+    case PK_AFTER_STMT:
+      pp_printf (pp, "after (SN: %i stmt: %i): ", m_supernode->m_index,
+		 m_stmt_idx);
+      pp_gimple_stmt_1 (pp, const_cast <gimple *> (get_stmt ()),
+			0, (dump_flags_t)0);
+      break;
+
+    case PK_AFTER_SUPERNODE:
+      pp_printf (pp, "after SN: %i", m_supernode->m_index);
+      break;
+    }
+}
+
+std::string
+program_point::to_str () const
+{
+  pretty_printer pp;
+  print (&pp);
+  return std::string (pp_formatted_text (&pp));
+}
+
+const gimple *
+program_point::get_stmt () const
+{
+  return m_supernode->m_stmts[m_stmt_idx];
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+void
+program_state::print (pretty_printer *pp) const
+{
+  pp_string (pp, to_str ().c_str ());
+}
+
+std::string
+program_state::to_str () const
+{
+  return m_path_state.to_str ();
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+void
+exploded_node::dump_dot (pretty_printer *pp) const
+{
+  graphviz_out tmp_gv (pp);
+  graphviz_out *gv = &tmp_gv;
+
+  // TODO: we'll probably want to add clusters for functions and for supernodes
+  // TODO: maybe have an explicit hierarchy of cluster objects?
+
+  dump_dot_id (pp);
+  pp_printf (pp, " [shape=%s,style=filled,fillcolor=%s,label=\"",
+	     "record", "lightgrey");
+  pp_left_brace (pp);
+  pp_write_text_to_stream (pp);
+
+  m_ps.m_point.print (pp);
+  pp_newline (pp);
+
+  m_ps.m_state.print (pp);
+  pp_newline (pp);
+
+  pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+
+  //TODO: dump_bb_for_graph (pp, bb);
+#if 0
+  if (m_returning_call)
+    {
+      pp_string (pp, "returning call: ");
+      pp_gimple_stmt_1 (pp, m_returning_call, 0, (dump_flags_t)0);
+      if (a)
+	a->add_stmt_annotations (pp, m_returning_call);
+      pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+      pp_newline (pp);
+    }
+
+  if (entry_p ())
+    {
+      pp_string (pp, "ENTRY");
+      pp_newline (pp);
+    }
+
+  if (return_p ())
+    {
+      pp_string (pp, "EXIT");
+      pp_newline (pp);
+    }
+
+  /* Phi nodes.  */
+  for (gphi_iterator gpi = const_cast<supernode *> (this)->start_phis ();
+       !gsi_end_p (gpi); gsi_next (&gpi))
+    {
+      gimple *stmt = gsi_stmt (gpi);
+      pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0);
+      pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+      pp_newline (pp);
+    }
+
+  /* Statements.  */
+  int i;
+  gimple *stmt;
+  FOR_EACH_VEC_ELT (m_stmts, i, stmt)
+    {
+      pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0);
+      if (a)
+	a->add_stmt_annotations (pp, stmt);
+      pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+      pp_newline (pp);
+    }
+#endif
+
+  pp_right_brace (pp);
+  pp_string (pp, "\"];\n\n");
+  pp_flush (pp);
+
+  /* Potentially visualize additional per-node data.  */
+#if 0
+  // compare with:
+  m_state->add_extra_nodes_and_edges (this, gv);
+#endif
+
+#if 0
+  if (0)
+    {
+      /* Begin a subgraph.  */
+      gv->println ("subgraph \"cluster_state_%p\" {", (const void *)this);
+      gv->indent ();
+      gv->println ("style=\"dashed\";");
+      gv->println ("color=\"green\";");
+      gv->println ("label=\"state\";");
+      gv->println ("clusterrank=\"local\";");
+
+      pretty_printer *pp = gv->m_pp;
+
+      /* Create dummy node.  */
+      gv->write_indent ();
+      pp_printf (pp,
+		 "state_%p_foo "
+		 "[shape=%s, style=\"filled\" fillcolor=\"yellow\" label=\"",
+		 (const void *)this, "record");
+      pp_left_brace (pp);
+      pp_write_text_to_stream (pp);
+      //dump_generic_node (pp, TREE_TYPE (expr), 0, (dump_flags_t)0, false);
+      pp_string (pp, " ");
+      //dump_generic_node (pp, expr, 0, (dump_flags_t)0, false);
+      pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+      pp_right_brace (pp);
+      pp_string (pp, "\"];");
+      pp_flush (pp);
+      pp_newline (pp);
+
+      /* Terminate subgraph.  */
+      gv->outdent ();
+      gv->println ("}");
+    }
+#endif
+
+}
+
+void
+exploded_node::dump_dot_id (pretty_printer *pp) const
+{
+  pp_printf (pp, "exploded_node_%i", m_index);
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest)
+: m_src (src), m_dest (dest)
+{
+}
+
+/* FIXME.  */
+
+void
+exploded_edge::dump_dot (pretty_printer *pp) const
+{
+  // TODO
+  graphviz_out tmp_gv (pp);
+  graphviz_out *gv = &tmp_gv;
+
+  const char *style = "\"solid,bold\"";
+  const char *color = "black";
+  int weight = 10;
+  const char *constraint = "true";
+
+#if 0
+  switch (m_kind)
+    {
+    default:
+      gcc_unreachable ();
+    case SUPEREDGE_CFG_EDGE:
+      break;
+    case SUPEREDGE_CALL:
+      color = "red";
+      //constraint = "false";
+      break;
+    case SUPEREDGE_RETURN:
+      color = "green";
+      //constraint = "false";
+      break;
+    case SUPEREDGE_INTRAPROCEDURAL_CALL:
+      style = "\"dotted\"";
+      break;
+    }
+
+  /* Adapted from graph.c:draw_cfg_node_succ_edges.  */
+  if (::edge cfg_edge = get_any_cfg_edge ())
+    {
+      if (cfg_edge->flags & EDGE_FAKE)
+	{
+	  style = "dotted";
+	  color = "green";
+	  weight = 0;
+	}
+      else if (cfg_edge->flags & EDGE_DFS_BACK)
+	{
+	  style = "\"dotted,bold\"";
+	  color = "blue";
+	  weight = 10;
+	}
+      else if (cfg_edge->flags & EDGE_FALLTHRU)
+	{
+	  color = "blue";
+	  weight = 100;
+	}
+
+      if (cfg_edge->flags & EDGE_ABNORMAL)
+	color = "red";
+    }
+
+  gv->write_indent ();
+#endif
+
+  m_src->dump_dot_id (gv->m_pp);
+#if 0
+  if (m_kind == SUPEREDGE_CFG_EDGE)
+    pp_string (gv->m_pp, ":s");
+#endif
+  /* FIXME: was seeing:
+       dot: compound.c:461: makeCompoundEdge: Assertion `bez->sflag' failed.
+     when experimenting with these.  */
+#if 0
+  else if (m_kind == SUPEREDGE_CALL)
+    pp_string (gv->m_pp, ":e");
+  else if (m_kind == SUPEREDGE_RETURN)
+    pp_string (gv->m_pp, ":w");
+#endif
+  pp_string (gv->m_pp, " -> ");
+  m_dest->dump_dot_id (gv->m_pp);
+#if 0
+  if (m_kind == SUPEREDGE_CFG_EDGE)
+    pp_string (gv->m_pp, ":n");
+#endif
+#if 0
+  else if (m_kind == SUPEREDGE_CALL)
+    pp_string (gv->m_pp, ":w");
+  else if (m_kind == SUPEREDGE_RETURN)
+    pp_string (gv->m_pp, ":e");
+#endif
+  pp_printf (gv->m_pp,
+	     (" [style=%s, color=%s, weight=%d, constraint=%s,"
+	      " headlabel=\""),
+	     style, color, weight, constraint);
+
+  //dump_label_to_pp (gv->m_pp);
+
+  pp_printf (gv->m_pp, "\"];\n");
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+exploded_graph::exploded_graph (const supergraph &sg, logger *logger)
+: log_user (logger), m_sg (sg)
+{
+}
+
+/* FIXME.  */
+
+void
+exploded_graph::add_function_entry (function *fun)
+{
+  program_point point = program_point::from_function_entry (m_sg, fun);
+  program_state state;
+  point_and_state ps (point, state);
+  m_entrypoints.safe_push (get_or_create_node (ps));
+}
+
+/* FIXME.  */
+
+exploded_node *
+exploded_graph::get_or_create_node (const point_and_state &ps)
+{
+  /* adding nodes if necessary, adding them to worklist,
+     and adding edges  */
+
+#if 0
+  if ()
+    {
+      // TODO: already exists
+    }
+#endif
+
+  exploded_node *node = new exploded_node (ps, m_nodes.length ());
+  add_node (node);
+  m_worklist.safe_push (node);
+  return node;
+}
+
+/* FIXME.  */
+
+exploded_node *
+exploded_graph::get_or_create_node (const program_point &point,
+				    const program_state &state)
+{
+  return get_or_create_node (point_and_state (point, state));
+}
+
+/* FIXME.  */
+
+void
+exploded_graph::add_edge (exploded_node *src, exploded_node *dest)
+{
+  exploded_edge *e = new exploded_edge (src, dest);
+  digraph::add_edge (e);
+}
+
+/* FIXME.  */
+
+void
+exploded_graph::process_worklist ()
+{
+  while (m_worklist.length () > 0)
+    {
+      exploded_node *node = m_worklist.pop ();
+      process_node (node);
+    }
+}
+
+/* get successor point/state pairs, calling get_or_create on them (which will
+   add them to the worklist), adding edges  */
+
+void
+exploded_graph::process_node (exploded_node *node)
+{
+  JIT_LOG_FUNC (get_logger ());
+
+  const program_point &point = node->m_ps.m_point;
+  const program_state &state = node->m_ps.m_state;
+  if (get_logger ())
+    {
+      log ("point: %s, state: %s",
+	   point.to_str ().c_str (),
+	   state.to_str ().c_str ());
+    }
+
+  switch (point.m_kind)
+    {
+    default:
+      gcc_unreachable ();
+    case PK_BEFORE_SUPERNODE:
+      {
+	if (point.m_supernode->m_stmts.length () > 0)
+	  {
+	    program_point next_point
+	      = program_point::before_stmt (point.m_supernode, 0);
+	    exploded_node *next
+	      = get_or_create_node (next_point, state);
+	    add_edge (node, next);
+	  }
+	else
+	  {
+	    program_point next_point
+	      = program_point::after_supernode (point.m_supernode);
+	    exploded_node *next = get_or_create_node (next_point, state);
+	    add_edge (node, next);
+	  }
+      }
+      break;
+    case PK_BEFORE_STMT:
+      {
+	// TODO: update state based on stmt!
+	program_point next_point
+	  = program_point::after_stmt (point.m_supernode, point.m_stmt_idx);
+	exploded_node *next = get_or_create_node (next_point, state);
+	add_edge (node, next);
+      }
+      break;
+    case PK_AFTER_STMT:
+      {
+	unsigned next_idx = point.m_stmt_idx + 1;
+	if (next_idx < point.m_supernode->m_stmts.length ())
+	  {
+	    program_point next_point
+	      = program_point::before_stmt (point.m_supernode, next_idx);
+	    exploded_node *next
+	      = get_or_create_node (next_point, state);
+	    add_edge (node, next);
+	  }
+	else
+	  {
+	    program_point next_point
+	      = program_point::after_supernode (point.m_supernode);
+	    exploded_node *next = get_or_create_node (next_point, state);
+	    add_edge (node, next);
+	  }
+      }
+      break;
+    case PK_AFTER_SUPERNODE:
+      {
+      /* Traverse into successors of the supernode.  */
+       int i;
+       superedge *succ;
+       FOR_EACH_VEC_ELT (point.m_supernode->m_succs, i, succ)
+	 {
+	   log ("considering SN: %i -> SN: %i",
+		succ->m_src->m_index, succ->m_dest->m_index);
+
+	   /* Don't traverse interprocedural edges here.  */
+	   if (succ->m_kind == SUPEREDGE_CALL)
+	     {
+	       log ("rejecting interprocedural edge");
+	       continue;
+	     }
+
+	   program_state next_state (state);
+	   /* For conditionals and switch statements, add the
+	      relevant conditions (for the specific edge) to new_state;
+	      skip edges for which the resulting constraints
+	      are impossible.  */
+	   const gimple *last_stmt = point.m_supernode->get_last_stmt ();
+	   if (!next_state.m_path_state.apply_constraints_for_edge (*succ,
+								    last_stmt))
+	     {
+	       log ("skipping impossible edge to SN: %i",
+		    succ->m_dest->m_index);
+	       continue;
+	     }
+
+	   // TODO: what about state transitions on edges?
+
+	    program_point next_point
+	      = program_point::before_supernode (succ->m_dest);
+	    exploded_node *next = get_or_create_node (next_point, next_state);
+	    add_edge (node, next);
+	 }
+      }
+      break;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+#if 0
+/* FIXME.  */
+
+static int
+function_comparator (const void *pn1, const void *pn2)
+{
+  const cgraph_node *node1 = *(const cgraph_node * const *)pn1;
+  const cgraph_node *node2 = *(const cgraph_node * const *)pn2;
+
+  location_t loc1 = node1->get_fun ()->function_start_locus;
+  location_t loc2 = node2->get_fun ()->function_start_locus;
+
+  return linemap_compare_locations (line_table, loc2, loc1);
+}
+#endif
+
+/* FIXME.  */
+/* TODO: should we run them all at once, or serially?
+   For now, this does them serially.  */
+
+void
+run_one_checker (const supergraph &sg, const state_machine &sm, logger *logger)
+{
+  JIT_LOG_FUNC_1 (logger, "sm: %s", sm.get_name ());
+
+  if (flag_analyzer_checker)
+    {
+      if (0 != strcmp (flag_analyzer_checker, sm.get_name ()))
+	{
+	  if (logger)
+	    logger->log ("skipping checker %s (does not match %s)",
+			 sm.get_name (), flag_analyzer_checker);
+	  return;
+	}
+    }
+
+  engine e (sg, sm, logger, analyzer_verbosity);
+
+  // TODO: add initial nodes
+  exploded_graph eg (sg, logger);
+
+  cgraph_node *node;
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+  {
+    function *fun = node->get_fun ();
+
+    if (!TREE_PUBLIC (fun->decl))
+      {
+	if (logger)
+	  logger->log ("not traversing %qE (static)", fun->decl);
+	continue;
+      }
+    eg.add_function_entry (fun);
+  }
+
+  eg.process_worklist ();
+
+  if (1)
+    {
+      char *filename
+	= concat (dump_base_name, ".", sm.get_name (), ".eg.dot", NULL);
+      eg.dump_dot (filename);
+      free (filename);
+    }
+
+  e.log_stats ();
+}
+
+} // namespace impl_4
+
+/* FIXME.  */
+
+void
+run_checkers_impl_4 ()
+{
+  //FILE *dump = fopen ("test.out", "w"); // FIXME
+  auto_client_timevar tv ("run_checkers");
+  {
+    log_user the_logger (NULL);
+    if (flag_dump_analyzer)
+      {
+	FILE *dump_file = /* 0 ? dump : */stderr; // FIXME
+	the_logger.set_logger (new logger (dump_file, 0, 0,
+					   *global_dc->printer));
+      }
+    JIT_LOG_SCOPE (the_logger.get_logger ());
+
+    /* Create the supergraph.  */
+    supergraph sg (the_logger.get_logger ());
+
+    if (1)
+      {
+	char *filename = concat (dump_base_name, ".supergraph.dot", NULL);
+	sg.dump_dot (filename, (enum supergraph_dot_flags)0);
+	free (filename);
+      }
+
+#if 0
+    // TODO: selftest for auto_delete_vec
+    auto_delete_vec <state_machine> checkers;
+    make_checkers (checkers, the_logger.get_logger ());
+
+    int i;
+    state_machine *checker;
+    FOR_EACH_VEC_ELT (checkers, i, checker)
+      // FIXME: this requires better timevar ID management
+#endif
+
+    // TODO
+    // For now, just count paths through the supergraph
+
+#if 1
+    {
+      auto_client_timevar tv ("malloc_state_machine");
+      malloc_state_machine sm (the_logger.get_logger ());
+      impl_4::run_one_checker (sg, sm, the_logger.get_logger ());
+    }
+
+    {
+      auto_client_timevar tv ("fileptr_state_machine");
+      fileptr_state_machine sm (the_logger.get_logger ());
+      impl_4::run_one_checker (sg, sm, the_logger.get_logger ());
+    }
+
+    {
+      auto_client_timevar tv ("taint_state_machine");
+      taint_state_machine sm (the_logger.get_logger ());
+      impl_4::run_one_checker (sg, sm, the_logger.get_logger ());
+    }
+
+    {
+      auto_client_timevar tv ("sensitive_state_machine");
+      sensitive_state_machine sm (the_logger.get_logger ());
+      impl_4::run_one_checker (sg, sm, the_logger.get_logger ());
+    }
+
+    /* We only attempt to run the pattern tests if it might have been manually
+       enabled (for DejaGnu purposes).  */
+    if (flag_analyzer_checker)
+      {
+	auto_client_timevar tv ("pattern_test_state_machine");
+	pattern_test_state_machine sm (the_logger.get_logger ());
+	impl_4::run_one_checker (sg, sm, the_logger.get_logger ());
+      }
+#endif
+
+    /* end of lifetime of the_logger (so that dump file is closed after the
+       various dtors run).  */
+  }
+
+  //fclose (dump);
+}
-- 
1.8.5.3

