From cf7eb582ecb33d5c154e66d9e93c6b703566e28f Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 10 May 2019 16:06:21 -0400
Subject: [PATCH 34/54] FIXME: purgable sm state

---
 gcc/analyzer/hardcoded-state-machine-file.cc       |  6 +++
 gcc/analyzer/hardcoded-state-machine-malloc.cc     |  6 +++
 .../hardcoded-state-machine-pattern-test.cc        |  6 +++
 gcc/analyzer/hardcoded-state-machine-sensitive.cc  |  6 +++
 gcc/analyzer/hardcoded-state-machine-taint.cc      |  6 +++
 gcc/analyzer/hardcoded-state-machine.h             | 10 +++++
 gcc/analyzer/impl-4.cc                             | 46 ++++++++++++++++++----
 7 files changed, 78 insertions(+), 8 deletions(-)

diff --git a/gcc/analyzer/hardcoded-state-machine-file.cc b/gcc/analyzer/hardcoded-state-machine-file.cc
index 9cc3cb5..bedde78 100644
--- a/gcc/analyzer/hardcoded-state-machine-file.cc
+++ b/gcc/analyzer/hardcoded-state-machine-file.cc
@@ -193,3 +193,9 @@ fileptr_state_machine::on_leak (sm_context *sm_ctxt,
   sm_ctxt->warn_for_state (node, stmt, var, m_nonnull,
 			   new file_leak (var));
 }
+
+bool
+fileptr_state_machine::can_purge_p (state_t s) const
+{
+  return s != m_unchecked && s != m_nonnull;
+}
diff --git a/gcc/analyzer/hardcoded-state-machine-malloc.cc b/gcc/analyzer/hardcoded-state-machine-malloc.cc
index bb0d16d..017c322 100644
--- a/gcc/analyzer/hardcoded-state-machine-malloc.cc
+++ b/gcc/analyzer/hardcoded-state-machine-malloc.cc
@@ -498,3 +498,9 @@ malloc_state_machine::on_leak (sm_context *sm_ctxt,
   sm_ctxt->warn_for_state (node, stmt, var, m_nonnull,
 			   new malloc_leak (*this, var));
 }
+
+bool
+malloc_state_machine::can_purge_p (state_t s) const
+{
+  return s != m_unchecked && s != m_nonnull;
+}
diff --git a/gcc/analyzer/hardcoded-state-machine-pattern-test.cc b/gcc/analyzer/hardcoded-state-machine-pattern-test.cc
index b53872c..e6c9567 100644
--- a/gcc/analyzer/hardcoded-state-machine-pattern-test.cc
+++ b/gcc/analyzer/hardcoded-state-machine-pattern-test.cc
@@ -130,3 +130,9 @@ pattern_test_state_machine::on_leak (sm_context *sm_ctxt,
 {
   /* Empty.  */
 }
+
+bool
+pattern_test_state_machine::can_purge_p (state_t s) const
+{
+  return true;
+}
diff --git a/gcc/analyzer/hardcoded-state-machine-sensitive.cc b/gcc/analyzer/hardcoded-state-machine-sensitive.cc
index 1e89cfe..775a8d5 100644
--- a/gcc/analyzer/hardcoded-state-machine-sensitive.cc
+++ b/gcc/analyzer/hardcoded-state-machine-sensitive.cc
@@ -137,3 +137,9 @@ sensitive_state_machine::on_leak (sm_context *sm_ctxt,
 {
   /* Empty.  */
 }
+
+bool
+sensitive_state_machine::can_purge_p (state_t s) const
+{
+  return true;
+}
diff --git a/gcc/analyzer/hardcoded-state-machine-taint.cc b/gcc/analyzer/hardcoded-state-machine-taint.cc
index 9d39d24..8ec4719 100644
--- a/gcc/analyzer/hardcoded-state-machine-taint.cc
+++ b/gcc/analyzer/hardcoded-state-machine-taint.cc
@@ -216,3 +216,9 @@ taint_state_machine::on_leak (sm_context *sm_ctxt,
 {
   /* Empty.  */
 }
+
+bool
+taint_state_machine::can_purge_p (state_t s) const
+{
+  return true;
+}
diff --git a/gcc/analyzer/hardcoded-state-machine.h b/gcc/analyzer/hardcoded-state-machine.h
index 81f5084..8832faf 100644
--- a/gcc/analyzer/hardcoded-state-machine.h
+++ b/gcc/analyzer/hardcoded-state-machine.h
@@ -69,6 +69,11 @@ public:
 			tree var,
 			state_machine::state_t state) const = 0;
 
+  /* Return true if it safe to discard the given state (to help
+     when simplifying state objects).
+     States that need leak detection should return false.  */
+  virtual bool can_purge_p (state_t s) const = 0;
+
 protected:
   state_t add_state (const char *name);
 
@@ -240,6 +245,7 @@ public:
 		const gimple *stmt,
 		tree var,
 		state_machine::state_t state) const FINAL OVERRIDE;
+  bool can_purge_p (state_t s) const FINAL OVERRIDE;
 
   state_t m_start;
   state_t m_unchecked;
@@ -274,6 +280,7 @@ public:
 		const gimple *stmt,
 		tree var,
 		state_machine::state_t state) const FINAL OVERRIDE;
+  bool can_purge_p (state_t s) const FINAL OVERRIDE;
 
 private:
   state_t m_start;
@@ -307,6 +314,7 @@ public:
 		const gimple *stmt,
 		tree var,
 		state_machine::state_t state) const FINAL OVERRIDE;
+  bool can_purge_p (state_t s) const FINAL OVERRIDE;
 
 private:
   state_t m_start;
@@ -339,6 +347,7 @@ public:
 		const gimple *stmt,
 		tree var,
 		state_machine::state_t state) const FINAL OVERRIDE;
+  bool can_purge_p (state_t s) const FINAL OVERRIDE;
 
 private:
   void warn_for_any_leakage (sm_context *sm_ctxt,
@@ -374,6 +383,7 @@ public:
 		const gimple *stmt,
 		tree var,
 		state_machine::state_t state) const FINAL OVERRIDE;
+  bool can_purge_p (state_t s) const FINAL OVERRIDE;
 
 private:
   state_t m_start;
diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index 87984a2..643930d 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -640,6 +640,9 @@ public:
 				 const gcall *call, tree fndecl,
 				 path_state *new_ps);
 
+  int purge (const state_machine &sm,
+	     const purge_criteria &p);
+
   iterator_t begin () const { return m_map.begin (); }
   iterator_t end () const { return m_map.end (); }
 
@@ -1994,6 +1997,30 @@ sm_state_map::purge_for_unknown_fncall (const exploded_graph &eg,
 	       0, path_var (NULL_TREE, 0));
 }
 
+int
+sm_state_map::purge (const state_machine &sm,
+		     const purge_criteria &p)
+{
+  /* FIXME: not sure how to safely remove during a traversal, so for now
+     do it in two stages.  */
+  auto_vec<path_var> to_remove;
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      if (p.should_purge_p ((*iter).first)
+	  && sm.can_purge_p ((*iter).second.m_state))
+	to_remove.safe_push ((*iter).first);
+    }
+
+  int i;
+  path_var *pv;
+  FOR_EACH_VEC_ELT (to_remove, i, pv)
+    m_map.remove (*pv);
+
+  return to_remove.length ();
+}
+
 ////////////////////////////////////////////////////////////////////////////
 
 /* FIXME.  */
@@ -2191,10 +2218,12 @@ class purge_at_point_criteria : public purge_criteria
 public:
   purge_at_point_criteria (const program_point &point,
 			   const state_purge_map *purge_map,
+			   const extrinsic_state &ext_state,
 			   const program_state &old_state)
   : m_point (point),
     m_flat_point (point),
     m_purge_map (purge_map),
+    m_ext_state (ext_state),
     m_old_state (old_state)
   {
     m_flat_point.m_call_string = call_string ();
@@ -2220,7 +2249,8 @@ public:
 	sm_state_map *smap;
 	FOR_EACH_VEC_ELT (m_old_state.m_checker_states, i, smap)
 	  {
-	    if (smap->get_state (pv) != 0)
+	    const state_machine &sm = m_ext_state.get_sm (i);
+	    if (!sm.can_purge_p (smap->get_state (pv)))
 	      return false;
 	  }
 
@@ -2233,6 +2263,7 @@ private:
   const program_point &m_point;
   program_point m_flat_point;
   const state_purge_map *m_purge_map;
+  const extrinsic_state &m_ext_state;
   const program_state &m_old_state;
 };
 
@@ -2259,6 +2290,7 @@ program_state::prune_for_point (exploded_graph &eg,
   program_state new_state (*this);
 
   purge_at_point_criteria criteria (point, eg.get_purge_map (),
+				    eg.get_ext_state (),
 				    *this);
   int num_purged = new_state.m_path_state.purge (criteria);
   eg.log ("path_vars purged: %i", num_purged);
@@ -2267,20 +2299,18 @@ program_state::prune_for_point (exploded_graph &eg,
   stats *per_fn_stats = eg.get_or_create_function_stats (point.get_function ());
   per_fn_stats->m_path_vars_purged++;
 
-
-#if 0
   // FIXME: state maps also
   int i;
   sm_state_map *smap;
-  FOR_EACH_VEC_ELT (m_checker_states, i, smap)
+  int num_sm_purged = 0;
+  FOR_EACH_VEC_ELT (new_state.m_checker_states, i, smap)
     {
-      smap->purge (criteria);
+      const state_machine &sm = eg.get_ext_state ().get_sm (i);
+      num_sm_purged += smap->purge (sm, criteria);
     }
-  // FIXME: what about leaks?
-#endif
+  eg.log ("num_sm_purged: %i", num_sm_purged);
 
   return new_state;
-
 }
 
 /* FIXME.  */
-- 
1.8.5.3

