From 7e08e8775fc5f0950c1b0ceaea32620a9be5e672 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 1 May 2020 07:16:43 -0400
Subject: [PATCH 097/193] FIXME: initial implementation of state purging

---
 gcc/analyzer/analyzer.h            |   2 +
 gcc/analyzer/diagnostic-manager.cc |   9 +-
 gcc/analyzer/program-state.cc      | 151 ++++++++++++++++++++++++++++-
 gcc/analyzer/program-state.h       |   3 +
 gcc/analyzer/region-model2.cc      |  44 +++++++++
 gcc/analyzer/region-model2.h       |  13 +++
 gcc/analyzer/store2.cc             |  30 ++++++
 gcc/analyzer/store2.h              |   4 +-
 8 files changed, 251 insertions(+), 5 deletions(-)

diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 8df49a0b631..851068e169a 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -64,6 +64,7 @@ class svalue2;
 class region2;
   class function_region2;
   class label_region2;
+  class decl_region2;
   class symbolic_region2;
   class element_region2;
   class offset_region2;
@@ -83,6 +84,7 @@ class extrinsic_state;
 class sm_state_map;
 class stmt_finder;
 class program_point;
+class function_point;
 class program_state;
 class exploded_graph;
 class exploded_node;
diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index 400acd3d465..054baa42f44 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -1047,9 +1047,12 @@ diagnostic_manager::add_events_for_eedge (const path_builder &pb,
   for_each_state_change (src_state, dst_state, pb.get_ext_state (),
 			 &visitor);
 
-  state_change_event_creator2 visitor2 (eedge, emission_path);
-  for_each_state_change2 (src_state, dst_state, pb.get_ext_state (),
-			  &visitor2);
+  if (src_state.m_region_model2)
+    {
+      state_change_event_creator2 visitor2 (eedge, emission_path);
+      for_each_state_change2 (src_state, dst_state, pb.get_ext_state (),
+			      &visitor2);
+    }
 
   /* Allow non-standard edges to add events, e.g. when rewinding from
      longjmp to a setjmp.  */
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index 214efbd22d2..e14811367a2 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -1123,7 +1123,8 @@ program_state::program_state (program_state &&other)
 : m_revamp_state (other.m_revamp_state),
   m_region_model2 (other.m_region_model2),
   m_region_model (other.m_region_model),
-  m_checker_states (other.m_checker_states.length ())
+  m_checker_states (other.m_checker_states.length ()),
+  m_checker_states2 (other.m_checker_states2.length ())
 {
   other.m_region_model2 = NULL;
   other.m_region_model = NULL;
@@ -1461,6 +1462,9 @@ program_state::prune_for_point (exploded_graph &eg,
   if (!fun)
     return *this;
 
+  if (m_region_model2)
+    return prune_for_point2 (eg, point, change);
+
   if (!m_region_model)
     return *this;
 
@@ -1567,6 +1571,151 @@ program_state::prune_for_point (exploded_graph &eg,
   return new_state;
 }
 
+/* FIXME.  */
+
+program_state
+program_state::prune_for_point2 (exploded_graph &eg,
+				 const program_point &point,
+				 state_change *change) const
+{
+  logger * const logger = eg.get_logger ();
+  LOG_SCOPE (logger);
+
+  function *fun = point.get_function ();
+  if (!fun)
+    return *this;
+
+  gcc_assert (m_region_model2);
+
+  program_state new_state (*this);
+
+  purge_stats2 stats;
+
+  const state_purge_map *pm = eg.get_purge_map ();
+  if (pm)
+    {
+      auto_vec<const decl_region2 *> ssa_name_regs;
+      new_state.m_region_model2->get_ssa_name_regions_for_current_frame
+	(&ssa_name_regs);
+      unsigned i;
+      const decl_region2 *reg;
+      FOR_EACH_VEC_ELT (ssa_name_regs, i, reg)
+	{
+	  tree ssa_name = reg->get_decl ();
+	  const state_purge_per_ssa_name &per_ssa
+	    = pm->get_data_for_ssa_name (ssa_name);
+	  if (!per_ssa.needed_at_point_p (point.get_function_point ()))
+	    {
+	      // TODO: see logic below on sm-state
+	      new_state.m_region_model2->purge_region (reg);
+	      // TODO: update stats
+	      if (logger)
+		logger->log ("purging region: TODO");
+	    }
+	}
+#if 0
+      region_id_set purgeable_ssa_regions (new_state.m_region_model);
+      region_id frame_rid
+	= new_state.m_region_model->get_current_frame_id ();
+      frame_region *frame
+	= new_state.m_region_model->get_region <frame_region>(frame_rid);
+
+      /* TODO: maybe move to a member of region_model?  */
+
+      auto_vec<tree> ssa_names_to_purge;
+      for (frame_region::map_t::iterator iter = frame->begin ();
+	   iter != frame->end ();
+	   ++iter)
+	{
+	  tree var = (*iter).first;
+	  region_id rid = (*iter).second;
+	  if (TREE_CODE (var) == SSA_NAME)
+	    {
+	      const state_purge_per_ssa_name &per_ssa
+		= pm->get_data_for_ssa_name (var);
+	      if (!per_ssa.needed_at_point_p (point.get_function_point ()))
+		{
+		  region *region
+		    = new_state.m_region_model->get_region (rid);
+		  svalue_id sid = region->get_value_direct ();
+		  if (!sid.null_p ())
+		    {
+		      if (!new_state.can_purge_p (eg.get_ext_state (), sid))
+			{
+			  /* (currently only state maps can keep things
+			     alive).  */
+			  if (logger)
+			    logger->log ("not purging RID: %i for %qE"
+					 " (used by state map)",
+					 rid.as_int (), var);
+			  continue;
+			}
+
+		      /* Don't purge regions containing svalues that
+			 have a change of sm-state, to make it easier to
+			 generate state_change_event messages.  */
+		      if (change)
+			if (change->affects_p (sid))
+			  {
+			    if (logger)
+			      logger->log ("not purging RID: %i for %qE"
+					   " (affected by change)",
+					   rid.as_int (), var);
+			    continue;
+			  }
+		    }
+		  purgeable_ssa_regions.add_region (rid);
+		  ssa_names_to_purge.safe_push (var);
+		  if (logger)
+		    logger->log ("purging RID: %i for %qE", rid.as_int (), var);
+		  /* We also need to remove the region from the map.
+		     We're in mid-traversal, so the removal is done in
+		     unbind below.  */
+		}
+	    }
+	}
+
+      /* Unbind the regions from the frame's map of vars-to-regions.  */
+      unsigned i;
+      tree var;
+      FOR_EACH_VEC_ELT (ssa_names_to_purge, i, var)
+	frame->unbind (var);
+
+      /* Purge the regions.  Nothing should point to them, and they
+	 should have no children, as they are for SSA names.  */
+      new_state.m_region_model->purge_regions (purgeable_ssa_regions,
+					       &stats,
+					       eg.get_logger ());
+#endif
+    }
+
+  /* Purge unused svalues.  */
+  /* FIXME: this makes no sense in the revamped model.
+     Instead, we're interested in which svalues are no longer referenced
+     relative to another state.  */
+#if 0
+  // TODO: which enode to use, if any?
+  impl_region_model2_context ctxt (eg, NULL,
+				   this,
+				   &new_state,
+				   change,
+				   NULL);
+  new_state.m_region_model2->purge_unused_svalues (&stats, &ctxt);
+  if (logger)
+    {
+      logger->log ("num svalues purged: %i", stats.m_num_svalues);
+      logger->log ("num regions purged: %i", stats.m_num_regions);
+      logger->log ("num equiv_classes purged: %i", stats.m_num_equiv_classes);
+      logger->log ("num constraints purged: %i", stats.m_num_constraints);
+      logger->log ("num sm map items purged: %i", stats.m_num_client_items);
+    }
+
+  new_state.m_region_model->canonicalize (&ctxt);
+#endif
+
+  return new_state;
+}
+
 /* Remap all svalue_ids in this state's m_checker_states according to MAP.
    The svalues_ids in the region_model are assumed to already have been
    remapped.  */
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 8a49531d42d..b2fcfcaee70 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -378,6 +378,9 @@ public:
   program_state prune_for_point (exploded_graph &eg,
 				 const program_point &point,
 				 state_change *change) const;
+  program_state prune_for_point2 (exploded_graph &eg,
+				  const program_point &point,
+				  state_change *change) const;
 
   void remap_svalue_ids (const svalue_id_map &map);
 
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index e597c033716..14e45f62537 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -5163,6 +5163,14 @@ region_model2::clobber_region (region2 *reg)
   m_store.clobber_region (m_mgr->get_store2_manager(), reg);
 }
 
+/* FIXME.  */
+
+void
+region_model2::purge_region (const region2 *reg)
+{
+  m_store.purge_region (m_mgr->get_store2_manager(), reg);
+}
+
 /* Determine what is known about the condition "LHS_SVAL OP RHS_SVAL" within
    this model.  */
 
@@ -6751,6 +6759,42 @@ region_model2::get_fndecl_for_call (const gcall *call,
   return NULL_TREE;
 }
 
+/* Would be much simpler to use a lambda here, if it were supported.  */
+
+struct append_ssa_names_cb_data
+{
+  const region_model2 *model;
+  auto_vec<const decl_region2 *> *out;
+};
+
+/* FIXME.  */
+
+void
+region_model2::
+get_ssa_name_regions_for_current_frame (auto_vec<const decl_region2 *> *out)
+  const
+{
+  append_ssa_names_cb_data data;
+  data.model = this;
+  data.out = out;
+  m_store.for_each_cluster (append_ssa_names_cb, &data);
+}
+
+/* FIXME.  */
+
+void
+region_model2::append_ssa_names_cb (const region2 *base_reg,
+				    append_ssa_names_cb_data *cb_data)
+{
+  if (base_reg->get_parent_region () != cb_data->model->m_current_frame)
+    return;
+  if (const decl_region2 *decl_reg = base_reg->dyn_cast_decl_region2 ())
+    {
+      if (TREE_CODE (decl_reg->get_decl ()) == SSA_NAME)
+	cb_data->out->safe_push (decl_reg);
+    }
+}
+
 /* FIXME.  */
 
 region2 *
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index 4378a9eb820..5d00c7d9bf0 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -815,6 +815,7 @@ public:
   virtual function_region2 *dyn_cast_function_region2 () { return NULL; }
   virtual symbolic_region2 *dyn_cast_symbolic_region2 () { return NULL; }
   virtual const symbolic_region2 *dyn_cast_symbolic_region2 () const { return NULL; }
+  virtual const decl_region2 *dyn_cast_decl_region2 () const { return NULL; }
   virtual field_region2 *dyn_cast_field_region2 () { return NULL; }
   virtual element_region2 *dyn_cast_element_region2 () { return NULL; }
   virtual offset_region2 *dyn_cast_offset_region2 () { return NULL; }
@@ -1360,10 +1361,14 @@ public:
   bool compare_fields (const decl_region2 &other) const;
 
   enum region2::kind get_kind () const FINAL OVERRIDE { return RK_DECL; }
+  const decl_region2 *dyn_cast_decl_region2 () const FINAL OVERRIDE
+  { return this; }
 
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
   path_var get_representative_path_var () const FINAL OVERRIDE;
 
+  tree get_decl () const { return m_decl; }
+
 protected:
   void add_to_hash (inchash::hash &hstate) const OVERRIDE;
 
@@ -1659,6 +1664,8 @@ private:
   auto_delete_vec<region2> m_managed_dynamic_regions;
 };
 
+struct append_ssa_names_cb_data;
+
 /* A region_model2 encapsulates a representation of the state of memory, with
    a tree of region2s, along with their associated values.
    The representation is graph-like because values can be pointers to
@@ -1782,6 +1789,7 @@ class region_model2
 		  region_model2_context *ctxt);
   void set_value (tree lhs, tree rhs, region_model2_context *ctxt);
   void clobber_region (region2 *reg);
+  void purge_region (const region2 *reg);
   svalue2 *set_to_new_unknown_value (region2 *dst_reg, tree type,
 				     region_model2_context *ctxt);
   void copy_region (region2 *dst_reg, region2 *src_reg,
@@ -1860,6 +1868,11 @@ class region_model2
   tree get_fndecl_for_call (const gcall *call,
 			    region_model2_context *ctxt);
 
+  void get_ssa_name_regions_for_current_frame
+    (auto_vec<const decl_region2 *> *out) const;
+  static void append_ssa_names_cb (const region2 *base_reg,
+				   struct append_ssa_names_cb_data *data);
+
  private:
   region2 *get_lvalue_1 (path_var pv,
 			 region_model2_context *ctxt);
diff --git a/gcc/analyzer/store2.cc b/gcc/analyzer/store2.cc
index 69a2b2c2afe..039177c6267 100644
--- a/gcc/analyzer/store2.cc
+++ b/gcc/analyzer/store2.cc
@@ -230,6 +230,18 @@ binding_cluster2::clobber_region (store2_manager *mgr, region2 *reg)
   remove_overlapping_bindings (mgr, reg);
 }
 
+/* FIXME.  */
+
+void
+binding_cluster2::purge_region (store2_manager *mgr, const region2 *reg)
+{
+  gcc_assert (reg->get_kind () == region2::RK_DECL);
+  const binding_key2 *binding
+    = binding_key2::make (mgr, const_cast<region2 *> (reg),
+			  binding_key2::BK_direct);
+  m_map.remove (binding);
+}
+
 svalue2 *
 binding_cluster2::get_binding (store2_manager *mgr,
 			       region2 *reg,
@@ -576,6 +588,24 @@ store2::clobber_region (store2_manager *mgr, region2 *reg)
 
 /* FIXME.  */
 
+void
+store2::purge_region (store2_manager *mgr, const region2 *reg)
+{
+  const region2 *base_reg = reg->get_base_region ();
+  binding_cluster2 **slot = m_cluster_map.get (base_reg);
+  if (!slot)
+    return;
+  binding_cluster2 *cluster = *slot;
+  cluster->purge_region (mgr, reg);
+  if (cluster->redundant_p ())
+    {
+      delete cluster;
+      m_cluster_map.remove (base_reg);
+    }
+}
+
+/* FIXME.  */
+
 binding_cluster2 *
 store2::get_cluster (const region2 *base_reg)
 {
diff --git a/gcc/analyzer/store2.h b/gcc/analyzer/store2.h
index 9a986b94022..c4c3c8b78c5 100644
--- a/gcc/analyzer/store2.h
+++ b/gcc/analyzer/store2.h
@@ -173,6 +173,7 @@ public:
 #endif
 
   void clobber_region (store2_manager *mgr, region2 *reg);
+  void purge_region (store2_manager *mgr, const region2 *reg);
 
   svalue2 *get_binding (store2_manager *mgr, region2 *reg,
 			binding_key2::kind kind) const;
@@ -238,12 +239,13 @@ public:
 
   void set_value (store2_manager *mgr, region2 *lhs_reg, svalue2 *rhs_sval);
   void clobber_region (store2_manager *mgr, region2 *reg);
+  void purge_region (store2_manager *mgr, const region2 *reg);
 
   binding_cluster2 *get_cluster (const region2 *base_reg);
 
   template <typename T>
   void for_each_cluster (void (*cb) (const region2 *base_reg, T user_data),
-			 T user_data)
+			 T user_data) const
   {
     for (cluster_map_t::iterator iter = m_cluster_map.begin ();
 	 iter != m_cluster_map.end (); ++iter)
-- 
2.21.0

