From 0760b5cd41cfb362eb63e68001f80aff187d0eef Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 9 Apr 2019 13:25:22 -0400
Subject: [PATCH 184/217] FIXME: impl-3: start wiring up state transitions

---
 gcc/analyzer/constraints.cc |   8 +-
 gcc/analyzer/constraints.h  |  15 +++
 gcc/analyzer/impl-3.cc      | 293 ++++++++++++++++++++++++++++++++++----------
 3 files changed, 246 insertions(+), 70 deletions(-)

diff --git a/gcc/analyzer/constraints.cc b/gcc/analyzer/constraints.cc
index 1fd3792..75bb944 100644
--- a/gcc/analyzer/constraints.cc
+++ b/gcc/analyzer/constraints.cc
@@ -58,7 +58,8 @@ path_var::to_str () const
 
 /* FIXME.  */
 
-equiv_class::equiv_class () : m_constant (NULL_TREE), m_vars (), m_escaped (false)
+equiv_class::equiv_class ()
+: m_constant (NULL_TREE), m_vars (), m_escaped (false), m_extra_state (NULL)
 {
 }
 
@@ -67,7 +68,8 @@ equiv_class::equiv_class () : m_constant (NULL_TREE), m_vars (), m_escaped (fals
 equiv_class::equiv_class (const equiv_class &other)
 : m_constant (other.m_constant),
   m_vars (),
-  m_escaped (other.m_escaped)
+  m_escaped (other.m_escaped),
+  m_extra_state (other.m_extra_state ? other.m_extra_state->clone () : NULL)
 {
   // FIXME: copy-ctor for auto_vec!
   int i;
@@ -99,6 +101,8 @@ equiv_class::to_str () const
     }
   if (m_escaped)
     result += " (escaped)";
+  if (m_extra_state)
+    result += m_extra_state->to_str ();
   return result + "}";
 }
 
diff --git a/gcc/analyzer/constraints.h b/gcc/analyzer/constraints.h
index 0c2e032..4a19012 100644
--- a/gcc/analyzer/constraints.h
+++ b/gcc/analyzer/constraints.h
@@ -79,6 +79,17 @@ public:
   int m_stack_depth; // or -1 for globals?
 };
 
+/* Optional per-engine extra state (to avoid making path_state a
+   template).  */
+
+class extra_ec_state
+{
+ public:
+  virtual ~extra_ec_state () {}
+  virtual extra_ec_state *clone () const = 0;
+  virtual std::string to_str () const = 0;
+};
+
 /* FIXME.  */
 
 class equiv_class
@@ -108,6 +119,10 @@ public:
 
   /* Has this value escaped from this function?  */
   bool m_escaped;
+
+  /* Optional per-engine extra state (to avoid making path_state a
+     template).  */
+  extra_ec_state *m_extra_state;
 };
 
 enum constraint_op
diff --git a/gcc/analyzer/impl-3.cc b/gcc/analyzer/impl-3.cc
index 8e5a5b6..21e788c 100644
--- a/gcc/analyzer/impl-3.cc
+++ b/gcc/analyzer/impl-3.cc
@@ -46,6 +46,33 @@
 
 namespace impl_3 {
 
+class engine;
+
+/* FIXME.  */
+
+class ec3_state : public extra_ec_state
+{
+ public:
+  ec3_state (const state_machine &sm,
+	     state_machine::state_t state)
+  : m_sm (sm), m_state (state)
+  {
+  }
+
+  extra_ec_state *clone () const FINAL OVERRIDE
+  {
+    return new ec3_state (m_sm, m_state);
+  }
+
+  std::string to_str () const FINAL OVERRIDE
+  {
+    return std::string (" (state='") + m_sm.get_state_name (m_state) + "')";
+  }
+
+  const state_machine &m_sm;
+  state_machine::state_t m_state;
+};
+
 /* FIXME.  */
 
 class checker3_event : public simple_diagnostic_event //diagnostic_event
@@ -98,50 +125,17 @@ public:
     const supernode *m_node;
   };
 
-  checker3_path (const supergraph &sg) : diagnostic_path (), m_sg (sg), m_links (), m_active_calls (), m_ps () {}
+  checker3_path (engine *engine)
+  : diagnostic_path (),
+    m_engine (engine),
+    m_links (),
+    m_active_calls (),
+    m_ps ()
+  {}
   checker3_path (const checker3_path &other);
   checker3_path& operator= (const checker3_path &other);
 
-  std::string to_str () const
-  {
-    std::string result ("[");
-
-    checker3_event *e;
-    int i;
-    FOR_EACH_VEC_ELT (m_events, i, e)
-      {
-	if (i > 0)
-	  result += ", ";
-	label_text event_desc (e->get_desc ());
-	result += std::string ("\"") + event_desc.m_buffer + std::string ("\"");
-	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_sg.get_edge_for_call (edge)->to_str ();
-	}
-      result += "]";
-    }
-    result += ", m_ps=" + m_ps.to_str ();
-    return result;
-  }
+  std::string to_str () const;
 
   void maybe_log (logger *logger, const char *desc) const
   {
@@ -176,7 +170,7 @@ public:
     if (m_active_calls.length () == 0)
       return NULL;
     cgraph_edge *cedge = m_active_calls[m_active_calls.length () - 1];
-    return sg.get_edge_for_return (cedge);
+    return get_sg ().get_edge_for_return (cedge);
   }
 
   /* Record that we've reached NODE, via EDGE.  EDGE may be NULL,
@@ -209,9 +203,24 @@ public:
       }
   }
 
+  int get_stack_depth () const { return m_ps.m_stack_depth; }
+
+  state_machine::state_t get_state (tree expr);
+  void set_state (tree expr, state_machine::state_t state);
+
+  equiv_class &get_ec (tree expr)
+  {
+    path_var pv (expr, get_stack_depth ());
+    equiv_class_id ec_id = m_ps.get_or_add_equiv_class (pv);
+    return ec_id.get_obj (m_ps);
+  }
+
+  const supergraph &get_sg () const;
+  const state_machine &get_sm () const;
+
   ////////////////////////////////////////////////////////////////////////////
 
-  const supergraph &m_sg;
+  engine *m_engine;
 
   /* The events that have occurred along this path.  */
   auto_delete_vec<checker3_event> m_events;
@@ -226,15 +235,107 @@ public:
   /* The current state of memory.  */
   path_state m_ps;
 
-  // TODO: add map from EC to state (or subclass path_state?)
+  /* FIXME: map from EC to state (or subclass path_state?)
+     No, store them in EC's m_extra_state.  */
 
   /* TODO: add additional things, like edge counts, etc
      (as an optimization; these could be derived by walking
      the events).  */
 };
 
+////////////////////////////////////////////////////////////////////////////
+
+/* 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)
+  {
+  }
+
+  void traverse (checker3_path path);
+  void on_stmt (checker3_path *path, const gimple *stmt);
+  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;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+class engine_sm_context : public sm_context
+{
+public:
+  engine_sm_context (engine *e, checker3_path *path)
+    : m_engine (e), m_path (path) {}
+
+  /* FIXME:  The SM can call this multiple times on one statement, e.g.
+     start -> free
+     free: warn_for_state
+     free -> stop.
+
+     We're interested in the states relative to when the sm_context
+     was created.  */
+
+  void on_transition (const supernode *node, const gimple *stmt,
+		      tree var,
+		      state_machine::state_t from,
+		      state_machine::state_t to,
+		      tree origin) FINAL OVERRIDE
+  {
+    JIT_LOG_FUNC_3 (m_engine->get_logger (),
+		    "var: %qE from: %s to: %s",
+		    var,
+		    m_engine->m_sm.get_state_name (from),
+		    m_engine->m_sm.get_state_name (to));
+    state_machine::state_t current = m_path->get_state (var);
+    if (current == from)
+      m_path->set_state (var, to);
+  }
+
+  void warn_for_state (const supernode *node, const gimple *stmt,
+		       tree var, state_machine::state_t state,
+		       pending_diagnostic *d) FINAL OVERRIDE
+  {
+    JIT_LOG_FUNC (m_engine->get_logger ());
+    gcc_assert (d); // take ownership
+
+    state_machine::state_t current = m_path->get_state (var);
+    if (current == state)
+      {
+	// TODO:
+#if 0
+	candidate_diagnostic *cd
+	  = new candidate_diagnostic (node, stmt, var, state, d, m_path);
+	m_engine->maybe_emit (*cd);
+#endif
+      }
+
+    delete d;
+  }
+
+  engine *m_engine;
+  checker3_path *m_path;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
 checker3_path::checker3_path (const checker3_path &other)
-: m_sg (other.m_sg),
+: m_engine (other.m_engine),
   m_links (),
   m_ps (other.m_ps)
 {
@@ -260,7 +361,7 @@ checker3_path::checker3_path (const checker3_path &other)
 checker3_path&
 checker3_path::operator= (const checker3_path &other)
 {
-  gcc_assert (&m_sg == &other.m_sg);
+  m_engine = other.m_engine;
   m_ps = other.m_ps;
 
   unsigned i;
@@ -283,34 +384,84 @@ checker3_path::operator= (const checker3_path &other)
 }
 
 /* FIXME.  */
-
-class engine : public log_user
+std::string
+checker3_path::to_str () const
 {
-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)
+  std::string result ("[");
+
+  checker3_event *e;
+  int i;
+  FOR_EACH_VEC_ELT (m_events, i, e)
+    {
+      if (i > 0)
+	result += ", ";
+      label_text event_desc (e->get_desc ());
+      result += std::string ("\"") + event_desc.m_buffer + std::string ("\"");
+      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 ();
+  return result;
+}
 
-  void traverse (checker3_path path);
-  void on_stmt (checker3_path *path, const gimple *stmt);
-  void log_stats () const;
+state_machine::state_t
+checker3_path::get_state (tree expr)
+{
+  equiv_class &ec = get_ec (expr);
+  ec3_state *extra_state = (ec3_state *)ec.m_extra_state;
+  if (extra_state)
+    return extra_state->m_state;
+  else
+    return 0;  // FIXME "start"
+}
 
-  const supergraph &m_sg;
+void
+checker3_path::set_state (tree expr, state_machine::state_t state)
+{
+  equiv_class &ec = get_ec (expr);
+  delete ec.m_extra_state;
+  ec.m_extra_state = new ec3_state (get_sm (), state);
+  if (m_engine->get_logger ())
+    m_engine->log ("%qE state -> %qs",
+		   expr,
+		   get_sm ().get_state_name (state));
+}
 
-  // FIXME: this implies one traversal per state machine; should we instead
-  // do multiple state machines in one traversal?
-  const state_machine &m_sm;
+const supergraph &
+checker3_path::get_sg () const
+{
+  return m_engine->m_sg;
+}
 
-  int m_verbosity;
+const state_machine &
+checker3_path::get_sm () const
+{
+  return m_engine->m_sm;
+}
 
-  int m_num_traverse_calls;
-  int m_on_stmt_calls;
-};
+////////////////////////////////////////////////////////////////////////////
 
 /* FIXME.  */
 
@@ -434,7 +585,6 @@ engine::traverse (checker3_path path)
 void
 engine::on_stmt (checker3_path *path, const gimple *stmt)
 {
-  m_on_stmt_calls++;
   JIT_LOG_FUNC (get_logger ());
   if (get_logger ())
     {
@@ -444,8 +594,15 @@ engine::on_stmt (checker3_path *path, const gimple *stmt)
       // FIXME: const_cast!
       log ("stmt: %s", pp_formatted_text (&pp));
     }
+
+  const supernode *node = path->get_end_node ();
+
   // TODO
 
+  engine_sm_context ctxt (this, path);
+  m_sm.on_stmt (&ctxt, node, stmt);
+  m_on_stmt_calls++;
+
   if (const gcall *call = dyn_cast <const gcall *> (stmt))
     {
       /* Debugging/test support.  */
@@ -549,7 +706,7 @@ run_one_checker (const supergraph &sg, const state_machine &sm, logger *logger)
 
       log_scope s (logger, "top-level traversal", "%s", function_name (fun));
 
-      checker3_path path (sg);
+      checker3_path path (&e);
       path.add_link (NULL, sg.get_node_for_function_entry (fun));
       e.traverse (path);
     }
-- 
1.8.5.3

