From 1e2f2d2d1523344ba70b53bf3a06f1e5cf529605 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 24 Apr 2019 12:01:39 -0400
Subject: [PATCH 195/217] FIXME: impl-4: work on sm state

---
 gcc/analyzer/impl-4.cc | 309 ++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 279 insertions(+), 30 deletions(-)

diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index c1ede9e..7e966f0 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -180,11 +180,96 @@ public:
   }
 };
 
-#if 0
+/* Data shared by all program_state instances.  */
+
+class extrinsic_state
+{
+public:
+  extrinsic_state (auto_delete_vec <state_machine> &checkers)
+  : m_checkers (checkers)
+  {
+  }
+
+  const state_machine &get_sm (int idx) const
+  {
+    return *m_checkers[idx];
+  }
+
+  const char *get_name (int idx) const
+  {
+    return m_checkers[idx]->get_name ();
+  }
+
+  /* The state machines.  */
+  auto_delete_vec <state_machine> &m_checkers;
+};
+
+} // namespace impl_4
+
+using impl_4::pod_hash_traits;
+
+template <> struct default_hash_traits<path_var>
+: public pod_hash_traits<path_var>
+{
+};
+
+template <>
+inline hashval_t
+pod_hash_traits<path_var>::hash (value_type v)
+{
+  return (hashval_t)((intptr_t)v.m_tree >> 3) ^ v.m_stack_depth;
+}
+
+template <>
+inline bool
+pod_hash_traits<path_var>::equal (const value_type &existing,
+				  const value_type &candidate)
+{
+  return existing == candidate;
+}
+template <>
+inline void
+pod_hash_traits<path_var>::mark_deleted (value_type &v)
+{
+  v = path_var (reinterpret_cast<tree> (1), 0);
+}
+template <>
+inline void
+pod_hash_traits<path_var>::mark_empty (value_type &v)
+{
+  v = path_var (NULL_TREE, 0);
+}
+template <>
+inline bool
+pod_hash_traits<path_var>::is_deleted (value_type v)
+{
+  return v.m_tree == reinterpret_cast<tree> (1);
+}
+template <>
+inline bool
+pod_hash_traits<path_var>::is_empty (value_type v)
+{
+  return v.m_tree == NULL_TREE;
+}
+
+namespace impl_4 {
+
+#if 1
 /* Map from var to state machine state.  */
 
 class sm_state_map
 {
+public:
+  sm_state_map *clone () const;
+
+  void print (const state_machine &sm, pretty_printer *pp) const;
+
+  bool is_empty_p () const;
+
+  state_machine::state_t get_state (path_var pv);
+  void set_state (path_var pv, state_machine::state_t state);
+
+private:
   typedef hash_map <path_var, state_machine::state_t> map_t;
   map_t m_var_to_state;
 };
@@ -195,25 +280,31 @@ class sm_state_map
 class program_state
 {
 public:
-  void print (pretty_printer *pp) const;
-  std::string to_str () const;
+  program_state (int num_checkers);
+  program_state (const program_state &other);
+  program_state& operator= (const program_state &other);
+
+  void print (const extrinsic_state &ext_state,
+	      pretty_printer *pp) const;
+  std::string to_str (const extrinsic_state &ext_state) const;
+
+  program_state on_stmt (const extrinsic_state &ext_state,
+			 const supernode *node,
+			 const gimple *stmt) const;
 
 #if 0
   path_state m_path_state;
 #endif
 
-#if 0
-  // TODO: maybe have multiple of these; do all state machines at once
-  sm_state_map m_sm_map;
-#endif
+  auto_vec<sm_state_map *> m_checker_states;
 
   // TODO: add "backtrace" of calls
 };
 
 struct point_and_state
 {
-  point_and_state (program_point point,
-		   program_state state)
+  point_and_state (const program_point &point,
+		   const program_state &state)
   : m_point (point),
     m_state (state)
   {
@@ -258,13 +349,13 @@ template <>
 inline void
 pod_hash_traits<point_and_state>::mark_deleted (value_type &v)
 {
-  v = point_and_state (program_point::deleted (), program_state ());
+  v = point_and_state (program_point::deleted (), program_state (0));
 }
 template <>
 inline void
 pod_hash_traits<point_and_state>::mark_empty (value_type &v)
 {
-  v = point_and_state (program_point::empty (), program_state ());
+  v = point_and_state (program_point::empty (), program_state (0));
 }
 template <>
 inline bool
@@ -284,8 +375,10 @@ namespace impl_4 {
 class exploded_node : public dnode
 {
  public:
-  exploded_node (point_and_state ps, int index)
-  : m_ps (ps), m_index (index)
+  exploded_node (point_and_state ps,
+		 const extrinsic_state &ext_state,
+		 int index)
+  : m_ps (ps), m_ext_state (ext_state), m_index (index)
   {
   }
 
@@ -293,6 +386,9 @@ class exploded_node : public dnode
   void dump_dot_id (pretty_printer *pp) const;
 
   point_and_state m_ps;
+  /* FIXME: this is within the node since dump_dot is a vfunc
+     In theory we could just pass this around instead.  */
+  const extrinsic_state &m_ext_state;
   int m_index;
 };
 
@@ -311,7 +407,7 @@ class exploded_graph : public digraph, public log_user
 {
 public:
   exploded_graph (const supergraph &sg, logger *logger,
-		  auto_delete_vec <state_machine> &checkers);
+		  const extrinsic_state &ext_state);
 
   void add_function_entry (function *fun);
   void process_worklist ();
@@ -326,18 +422,16 @@ public:
   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;
 
-  /* The state machines.  */
-  auto_delete_vec <state_machine> &m_checkers;
+  const extrinsic_state &m_ext_state;
 };
 
+#if 0
 /* FIXME.  */
 
 class engine : public log_user
@@ -371,6 +465,7 @@ public:
   int m_num_path_copies;
   int m_num_event_copies;
 };
+#endif
 
 ////////////////////////////////////////////////////////////////////////////
 
@@ -421,17 +516,169 @@ program_point::get_stmt () const
 
 ////////////////////////////////////////////////////////////////////////////
 
+sm_state_map *
+sm_state_map::clone () const
+{
+  return new sm_state_map (*this);
+}
+
+void
+sm_state_map::print (const state_machine &sm, pretty_printer *pp) const
+{
+  pp_string (pp, "{");
+  for (map_t::iterator iter = m_var_to_state.begin ();
+       iter != m_var_to_state.end ();
+       ++iter)
+    {
+      if (iter != m_var_to_state.begin ())
+	pp_string (pp, ", ");
+      pp_printf (pp, "%s: %s",
+		 (*iter).first.to_str ().c_str (),
+		 sm.get_state_name ((*iter).second));
+    }
+
+  // TODO
+  pp_string (pp, "}");
+}
+
+bool
+sm_state_map::is_empty_p () const
+{
+  return m_var_to_state.elements () == 0;
+}
+
+state_machine::state_t
+sm_state_map::get_state (path_var pv)
+{
+  if (state_machine::state_t *slot = m_var_to_state.get (pv))
+    return *slot;
+  else
+    return 0;
+}
+
 void
-program_state::print (pretty_printer *pp) const
+sm_state_map::set_state (path_var pv, state_machine::state_t state)
 {
-  pp_string (pp, to_str ().c_str ());
+  gcc_assert (state != 0); // FIXME: purge for this case
+  m_var_to_state.put (pv, state);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+class impl_sm_context : public sm_context
+{
+public:
+  impl_sm_context (sm_state_map *smap)
+  : m_smap (smap)
+  {
+  }
+
+  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
+
+  {
+    path_var pv (var, 0); // FIXME: stack depth
+    state_machine::state_t current = m_smap->get_state (pv);
+    if (current == from)
+      m_smap->set_state (pv, to);
+  }
+
+  void warn_for_state (const supernode *node, const gimple *stmt,
+		       tree var, state_machine::state_t state,
+		       pending_diagnostic *d) FINAL OVERRIDE
+  {
+    gcc_assert (d); // take ownership
+    path_var pv (var, 0); // FIXME: stack depth
+    state_machine::state_t current = m_smap->get_state (pv);
+    if (state == current)
+      {
+	/* TODO: generate a path.  */
+	gcc_rich_location rich_loc (stmt->location);
+	//rich_loc.set_path (&emission_path);
+
+	auto_cfun sentinel (node->m_fun);
+	d->emit (&rich_loc);
+      }
+    delete d;
+  }
+
+  sm_state_map *m_smap;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+program_state::program_state (int num_states)
+{
+  for (int i = 0; i < num_states; i++)
+    m_checker_states.safe_push (new sm_state_map ());
+}
+
+program_state::program_state (const program_state &other)
+{
+  int i;
+  sm_state_map *smap;
+  FOR_EACH_VEC_ELT (other.m_checker_states, i, smap)
+    m_checker_states.safe_push (smap->clone ());
+}
+
+program_state&
+program_state::operator= (const program_state &other)
+{
+  // TODO: eliminate existing things
+  gcc_assert (m_checker_states.length () == 0);
+
+  int i;
+  sm_state_map *smap;
+  FOR_EACH_VEC_ELT (other.m_checker_states, i, smap)
+    m_checker_states.safe_push (smap->clone ());
+}
+
+void
+program_state::print (const extrinsic_state &ext_state,
+		      pretty_printer *pp) const
+{
+  int i;
+  sm_state_map *smap;
+  FOR_EACH_VEC_ELT (m_checker_states, i, smap)
+    {
+      if (!smap->is_empty_p ())
+	{
+	  pp_printf (pp, "%s: ", ext_state.get_name (i));
+	  smap->print (ext_state.get_sm (i), pp);
+	  pp_newline (pp);
+	}
+    }
 }
 
 std::string
-program_state::to_str () const
+program_state::to_str (const extrinsic_state &ext_state) const
 {
-  return "STATE(TODO)";
-  //  return m_path_state.to_str ();
+  pretty_printer pp;
+  print (ext_state, &pp);
+  return std::string (pp_formatted_text (&pp));
+}
+
+program_state
+program_state::on_stmt (const extrinsic_state &ext_state,
+			const supernode *node,
+			const gimple *stmt) const
+{
+  program_state result (*this);
+
+  int i;
+  sm_state_map *smap;
+  FOR_EACH_VEC_ELT (m_checker_states, i, smap)
+    {
+      impl_sm_context ctxt (result.m_checker_states[i]);
+      const state_machine &sm = ext_state.get_sm (i);
+      sm.on_stmt (&ctxt, node, stmt);
+    }
+
+  // TODO
+  return result;
 }
 
 ////////////////////////////////////////////////////////////////////////////
@@ -456,7 +703,7 @@ exploded_node::dump_dot (pretty_printer *pp) const
   m_ps.m_point.print (pp);
   pp_newline (pp);
 
-  m_ps.m_state.print (pp);
+  m_ps.m_state.print (m_ext_state, pp);
   pp_newline (pp);
 
   pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
@@ -676,8 +923,8 @@ exploded_edge::dump_dot (pretty_printer *pp) const
 /* FIXME.  */
 
 exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
-				auto_delete_vec <state_machine> &checkers)
-: log_user (logger), m_sg (sg), m_checkers (checkers)
+				const extrinsic_state &ext_state)
+: log_user (logger), m_sg (sg), m_ext_state (ext_state)
 {
 }
 
@@ -687,7 +934,7 @@ void
 exploded_graph::add_function_entry (function *fun)
 {
   program_point point = program_point::from_function_entry (m_sg, fun);
-  program_state state;
+  program_state state (m_ext_state.m_checkers.length ());
   point_and_state ps (point, state);
   m_entrypoints.safe_push (get_or_create_node (ps));
 }
@@ -705,7 +952,7 @@ exploded_graph::get_or_create_node (const point_and_state &ps)
     }
 
   /* An exploded_node for PS doesn't already exist; create one.  */
-  exploded_node *node = new exploded_node (ps, m_nodes.length ());
+  exploded_node *node = new exploded_node (ps, m_ext_state, m_nodes.length ());
   add_node (node);
   m_point_and_state_to_node.put (ps, node);
 
@@ -759,7 +1006,7 @@ exploded_graph::process_node (exploded_node *node)
     {
       log ("point: %s, state: %s",
 	   point.to_str ().c_str (),
-	   state.to_str ().c_str ());
+	   state.to_str (m_ext_state).c_str ());
     }
 
   switch (point.m_kind)
@@ -790,7 +1037,9 @@ exploded_graph::process_node (exploded_node *node)
 	// 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);
+	program_state next_state
+	  = state.on_stmt (m_ext_state, point.m_supernode, point.get_stmt ());
+	exploded_node *next = get_or_create_node (next_point, next_state);
 	add_edge (node, next);
       }
       break;
-- 
1.8.5.3

