From ba4280f94e56edf89be18d344507d11cd63cfd65 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 20 Feb 2020 10:49:01 -0500
Subject: [PATCH 27/31] FIXME: purge fndecls that were used but aren't
 referenced any more

---
 gcc/analyzer/analyzer.h         |   1 +
 gcc/analyzer/canonicalization.h |  41 +++++++++
 gcc/analyzer/region-model.cc    | 149 +++++++++++++++++++++++++++++---
 gcc/analyzer/region-model.h     |   6 ++
 4 files changed, 185 insertions(+), 12 deletions(-)

diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 1ae76cc4ea0..190660c1dce 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -52,6 +52,7 @@ class equiv_class;
 struct model_merger;
 struct svalue_id_merger_mapping;
 struct canonicalization;
+struct purge;
 class pending_diagnostic;
 class state_change_event;
 class checker_path;
diff --git a/gcc/analyzer/canonicalization.h b/gcc/analyzer/canonicalization.h
index 205036623e1..2eadfbfedc2 100644
--- a/gcc/analyzer/canonicalization.h
+++ b/gcc/analyzer/canonicalization.h
@@ -57,6 +57,47 @@ struct canonicalization
   int m_next_ec_id_int;
 };
 
+/* FIXME.  */
+
+struct purge
+{
+  purge (svalue_id first_unused_sid,
+	 region_id first_unused_rid,
+	 equiv_class_id first_unused_ec_id)
+  : m_first_unused_sid (first_unused_sid),
+    m_first_unused_rid (first_unused_rid),
+    m_first_unused_ec_id (first_unused_ec_id)
+  {}
+
+  bool purging_sid_p (svalue_id sid) const
+  {
+    return sid.as_int () >= m_first_unused_sid.as_int ();
+  }
+  bool purging_rid_p (region_id rid) const
+  {
+    return rid.as_int () >= m_first_unused_rid.as_int ();
+  }
+  bool purging_ec_id_p (equiv_class_id ec_id) const
+  {
+    return ec_id.as_int () >= m_first_unused_ec_id.as_int ();
+  }
+
+  void maybe_purge (svalue_id *sid) const
+  {
+    if (purging_sid_p (*sid))
+      *sid = svalue_id::null ();
+  }
+  void maybe_purge (region_id *rid) const
+  {
+    if (purging_rid_p (*rid))
+      *rid = region_id::null ();
+  }
+
+  svalue_id m_first_unused_sid;
+  region_id m_first_unused_rid;
+  equiv_class_id m_first_unused_ec_id;
+};
+
 } // namespace ana
 
 #endif /* GCC_ANALYZER_CANONICALIZATION_H */
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index adb4d0afa16..30c016a4f95 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1379,6 +1379,21 @@ region::validate (const region_model &model) const
   m_active_view_rid.validate (model);
 }
 
+/* Base implementation of region::validate vfunc.  */
+
+void
+region::on_purge (const purge &p)
+{
+  p.maybe_purge (&m_parent_rid);
+  p.maybe_purge (&m_sval_id);
+
+  unsigned read_index, write_index;
+  region_id *view_rid;
+  VEC_ORDERED_REMOVE_IF (m_view_rids, read_index, write_index, view_rid,
+			 p.purging_rid_p (*view_rid));
+  p.maybe_purge (&m_active_view_rid);
+}
+
 /* Apply MAP to svalue_ids to this region.  This updates the value
    for the region (if any).  */
 
@@ -1630,6 +1645,32 @@ map_region::validate (const region_model &model) const
     }
 }
 
+/* Implementation of region::on_purge vfunc for map_region.  */
+
+void
+map_region::on_purge (const purge &p)
+{
+  region::on_purge (p);
+
+  auto_vec<tree> keys_to_remove;
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      region_id rid = (*iter).second;
+      if (p.purging_rid_p (rid))
+	{
+	  tree key = (*iter).first;
+	  keys_to_remove.safe_push (key);
+	}
+    }
+
+  unsigned i;
+  tree key;
+  FOR_EACH_VEC_ELT (keys_to_remove, i, key)
+    m_map.remove (key);
+}
+
 /* Implementation of region::dump_dot_to_pp vfunc for map_region.  */
 
 void
@@ -2154,6 +2195,15 @@ frame_region::validate (const region_model &model) const
   // TODO
 }
 
+/* Implementation of region::on_purge vfunc for frame_region.  */
+
+void
+frame_region::on_purge (const purge &p)
+{
+  region::on_purge (p);
+  // TODO
+}
+
 /* Implementation of region::add_to_hash vfunc for frame_region.  */
 
 void
@@ -2322,6 +2372,32 @@ array_region::validate (const region_model &model) const
     }
 }
 
+/* Implementation of region::on_purge vfunc for array_region.  */
+
+void
+array_region::on_purge (const purge &p)
+{
+  region::on_purge (p);
+
+  auto_vec<key_t> keys_to_remove;
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      region_id rid = (*iter).second;
+      if (p.purging_rid_p (rid))
+	{
+	  key_t key = (*iter).first;
+	  keys_to_remove.safe_push (key);
+	}
+    }
+
+  unsigned i;
+  key_t key;
+  FOR_EACH_VEC_ELT (keys_to_remove, i, key)
+    m_map.remove (key);
+}
+
 /* Implementation of region::dump_dot_to_pp vfunc for array_region.  */
 
 void
@@ -2610,6 +2686,15 @@ stack_region::validate (const region_model &model) const
     m_frame_rids[i].validate (model);
 }
 
+/* Implementation of region::on_purge vfunc for stack_region.  */
+
+void
+stack_region::on_purge (const purge &p)
+{
+  region::on_purge (p);
+  // TODO
+}
+
 /* Push FRAME_RID (for a frame_region) onto this stack.  */
 
 void
@@ -2914,6 +2999,19 @@ root_region::validate (const region_model &model) const
   m_heap_rid.validate (model);
 }
 
+/* Implementation of region::on_purge vfunc for root_region.  */
+
+void
+root_region::on_purge (const purge &p)
+{
+  region::on_purge (p);
+
+  p.maybe_purge (&m_stack_rid);
+  p.maybe_purge (&m_globals_rid);
+  p.maybe_purge (&m_code_rid);
+  p.maybe_purge (&m_heap_rid);
+}
+
 /* Implementation of region::dump_child_label vfunc for root_region.  */
 
 void
@@ -3245,8 +3343,9 @@ root_region::walk_for_canonicalization (canonicalization *c) const
   region::walk_for_canonicalization (c);
   c->walk_rid (m_stack_rid);
   c->walk_rid (m_globals_rid);
-  c->walk_rid (m_code_rid);
-  c->walk_rid (m_heap_rid);
+  /* We deliberately don't walk m_code_rid and m_heap_rid here:
+     if something references them, they will be visited, otherwise
+     we would prefer to purge them.  */
 }
 
 /* For debugging purposes: look for a descendant region for a local
@@ -6210,10 +6309,9 @@ region_model::canonicalize_and_purge (region_model_context *ctxt,
       c.walk_sid (*known_used_sid);
 
   /* Mark where we got to.  Anything beyond this point will be purged.  */
-  svalue_id first_unused_sid = svalue_id::from_int (c.m_next_sid_int);
-  region_id first_unused_rid = region_id::from_int (c.m_next_rid_int);
-  equiv_class_id first_unused_ec_id
-    = equiv_class_id::from_int (c.m_next_ec_id_int);
+  purge p (svalue_id::from_int (c.m_next_sid_int),
+	   region_id::from_int (c.m_next_rid_int),
+	   equiv_class_id::from_int (c.m_next_ec_id_int));
 
   /* Now walk all regions, svalues and equiv_classes, so that we visit all of
      them.
@@ -6249,7 +6347,7 @@ region_model::canonicalize_and_purge (region_model_context *ctxt,
     {
       ctxt->remap_svalue_ids (c.m_sid_map);
       int num_client_items_purged
-	= ctxt->on_svalue_purge (first_unused_sid);
+	= ctxt->on_svalue_purge (p.m_first_unused_sid);
       if (stats)
 	stats->m_num_client_items += num_client_items_purged;
     }
@@ -6261,8 +6359,8 @@ region_model::canonicalize_and_purge (region_model_context *ctxt,
        con_idx < get_constraints ()->m_constraints.length (); )
     {
       constraint *c = &get_constraints ()->m_constraints[con_idx];
-      if (c->m_lhs.as_int () >= first_unused_ec_id.as_int ()
-	  || c->m_rhs.as_int () >= first_unused_ec_id.as_int ()
+      if (p.purging_ec_id_p (c->m_lhs)
+	  || p.purging_ec_id_p (c->m_rhs)
 	  || c->compares_constants_p (*get_constraints ()))
 	{
 	  get_constraints ()->m_constraints.ordered_remove (con_idx);
@@ -6275,11 +6373,19 @@ region_model::canonicalize_and_purge (region_model_context *ctxt,
   /* Sort the remaining constraints by sid.  */
   get_constraints ()->m_constraints.qsort (constraint_cmp);
 
-  get_constraints ()->delete_redundant_ecs (c.m_ec_id_map, first_unused_ec_id,
+  get_constraints ()->delete_redundant_ecs (c.m_ec_id_map,
+					    p.m_first_unused_ec_id,
 					    stats, logger);
 
+  /* Notify surviving regions about the entities that we're about to purge.  */
+  for (int i = 0; i < p.m_first_unused_rid.as_int (); i++)
+    {
+      region *r = get_region (region_id::from_int (i));
+      r->on_purge (p);
+    }
+
   /* Drop the redundant svalues from the end of the vector.  */
-  while ((signed)m_svalues.length () > first_unused_sid.as_int ())
+  while ((signed)m_svalues.length () > p.m_first_unused_sid.as_int ())
     {
       if (logger)
 	{
@@ -6294,7 +6400,7 @@ region_model::canonicalize_and_purge (region_model_context *ctxt,
     }
 
   /* Drop the redundant regions from the end of the vector.  */
-  while ((signed)m_regions.length () > first_unused_rid.as_int ())
+  while ((signed)m_regions.length () > p.m_first_unused_rid.as_int ())
     {
       if (logger)
 	{
@@ -8124,6 +8230,24 @@ test_canonicalization_6 ()
   ASSERT_CONDITION_TRUE (model0, x, EQ_EXPR, y);
 }
 
+/* Verify that mere uses of fndecls are purged after canonicalization.  */
+
+static void
+test_canonicalization_7 ()
+{
+  auto_vec <tree> param_types;
+  tree fndecl = make_fndecl (integer_type_node,
+			     "factorial",
+			     param_types);
+
+  region_model model0;
+  region_model model1;
+  region_id fn_rid = model1.get_region_for_fndecl (fndecl);
+  ASSERT_FALSE (fn_rid.null_p ());
+  model1.canonicalize_and_purge (NULL);
+  ASSERT_EQ (model0, model1);
+}
+
 /* Assert that if we have two region_model instances
    with values VAL_A and VAL_B for EXPR that they are
    mergable.  Write the merged model to *OUT_MERGED_MODEL,
@@ -8616,6 +8740,7 @@ analyzer_region_model_cc_tests ()
   test_canonicalization_4 ();
   test_canonicalization_5 ();
   test_canonicalization_6 ();
+  test_canonicalization_7 ();
   test_state_merging ();
   test_constraint_merging ();
   test_constraint_merging_2 ();
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 82411e95286..65502d0d798 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -995,6 +995,7 @@ public:
   bool is_view_p () const { return m_is_view; }
 
   virtual void validate (const region_model &model) const;
+  virtual void on_purge (const purge &p);
 
   bool non_null_p (const region_model &model) const;
 
@@ -1117,6 +1118,7 @@ public:
 		     pretty_printer *pp) const
     OVERRIDE;
   void validate (const region_model &model) const OVERRIDE;
+  void on_purge (const purge &p) OVERRIDE;
 
  private:
   /* Mapping from tree to child region.  */
@@ -1269,6 +1271,7 @@ public:
 		     pretty_printer *pp) const
     FINAL OVERRIDE;
   void validate (const region_model &model) const FINAL OVERRIDE;
+  void on_purge (const purge &p) FINAL OVERRIDE;
   void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
 
   /* map_region vfuncs.  */
@@ -1500,6 +1503,7 @@ public:
 		     pretty_printer *pp) const
     OVERRIDE;
   void validate (const region_model &model) const FINAL OVERRIDE;
+  void on_purge (const purge &p) FINAL OVERRIDE;
 
   static key_t key_from_constant (tree cst);
 
@@ -1567,6 +1571,7 @@ public:
 			       const region_model &model) const;
 
   void validate (const region_model &model) const FINAL OVERRIDE;
+  void on_purge (const purge &p) FINAL OVERRIDE;
 
  private:
   void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
@@ -1684,6 +1689,7 @@ public:
 			       const region_model &model) const;
 
   void validate (const region_model &model) const FINAL OVERRIDE;
+  void on_purge (const purge &p) FINAL OVERRIDE;
 
 private:
   void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
-- 
2.21.0

