From 924d75d695db92eae9f3eaec17222d82c154a80d Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 15 Jan 2020 22:32:22 -0500
Subject: [PATCH 12/68] FIXME: yet more WIP on enode bloat: ensure pruned
 during validation, and prune during merging

---
 gcc/analyzer/engine.cc        | 16 +++++++-----
 gcc/analyzer/exploded-graph.h |  3 ++-
 gcc/analyzer/program-state.cc | 49 ++++++++++++++++++++++++++++++++---
 gcc/analyzer/program-state.h  |  3 ++-
 4 files changed, 58 insertions(+), 13 deletions(-)

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 23e92891134..827ffdd209d 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -665,10 +665,12 @@ impl_region_model_context::on_condition (tree lhs, enum tree_code op, tree rhs)
 
 /* struct point_and_state.  */
 
-/* Assert that this object is sane.  */
+/* Assert that this object is sane.
+   Additionally, if PRUNED is true, also assert that it has been pruned.  */
 
 void
-point_and_state::validate (const extrinsic_state &ext_state) const
+point_and_state::validate (const extrinsic_state &ext_state,
+			   bool pruned) const
 {
   /* Skip this in a release build.  */
 #if !CHECKING_P
@@ -677,7 +679,7 @@ point_and_state::validate (const extrinsic_state &ext_state) const
 
   m_point.validate ();
 
-  m_state.validate (ext_state);
+  m_state.validate (ext_state, pruned);
 
   /* Verify that the callstring's model of the stack corresponds to that
      of the region_model.  */
@@ -1824,7 +1826,7 @@ exploded_graph::get_or_create_node (const program_point &point,
 
   auto_cfun sentinel (point.get_function ());
 
-  state.validate (get_ext_state ());
+  state.validate (get_ext_state (), false);
 
   //state.dump (get_ext_state ());
 
@@ -1832,7 +1834,7 @@ exploded_graph::get_or_create_node (const program_point &point,
      avoiding generating redundant nodes.  */
   program_state pruned_state = state.prune_for_point (*this, point, change);
 
-  pruned_state.validate (get_ext_state ());
+  pruned_state.validate (get_ext_state (), true);
 
   //pruned_state.dump (get_ext_state ());
 
@@ -1852,7 +1854,7 @@ exploded_graph::get_or_create_node (const program_point &point,
     = &get_or_create_per_call_string_data (point.get_call_string ())->m_stats;
 
   point_and_state ps (point, pruned_state);
-  ps.validate (m_ext_state);
+  ps.validate (m_ext_state, true);
   if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
     {
       /* An exploded_node for PS already exists.  */
@@ -1929,7 +1931,7 @@ exploded_graph::get_or_create_node (const program_point &point,
       return NULL;
     }
 
-  ps.validate (m_ext_state);
+  ps.validate (m_ext_state, true);
 
   /* An exploded_node for "ps" doesn't already exist; create one.  */
   exploded_node *node = new exploded_node (ps, m_nodes.length (), m_ext_state);
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index cc775164e52..fba3d0c8252 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -118,7 +118,8 @@ public:
     m_hash = m_point.hash () ^ m_state.hash ();
   }
 
-  void validate (const extrinsic_state &ext_state) const;
+  void validate (const extrinsic_state &ext_state,
+		 bool pruned) const;
 
 private:
   program_point m_point;
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index f41f105ce6b..7716c1a6d80 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -978,15 +978,18 @@ program_state::can_merge_with_p (const program_state &other,
     }
 
   impl_region_model_context ctxt (out, NULL, ext_state);
+  out->m_region_model->purge_unused_svalues (NULL, &ctxt, NULL);
   out->m_region_model->canonicalize (&ctxt);
 
   return true;
 }
 
-/* Assert that this object is valid.  */
+/* Assert that this object is valid.
+   Additionally, if PRUNED is true, also assert that it has been pruned.  */
 
 void
-program_state::validate (const extrinsic_state &ext_state) const
+program_state::validate (const extrinsic_state &ext_state,
+			 bool pruned) const
 {
   /* Skip this in a release build.  */
 #if !CHECKING_P
@@ -1002,6 +1005,44 @@ program_state::validate (const extrinsic_state &ext_state) const
       const state_machine &sm = ext_state.get_sm (sm_idx);
       smap->validate (sm, m_region_model->get_num_svalues ());
     }
+
+  if (pruned)
+    {
+      /* Assert that there are no redundant svalues.  */
+      for (unsigned sv_idx = 0 ; sv_idx < m_region_model->get_num_svalues ();
+	   sv_idx++)
+	{
+	  svalue_id sid (svalue_id::from_int (sv_idx));
+	  bool found_sid = false;
+
+	  /* Try within the regions.  */
+	  for (unsigned r_idx = 0; r_idx < m_region_model->get_num_regions ();
+	       r_idx++)
+	    {
+	      region *r
+		= m_region_model->get_region (region_id::from_int (r_idx));
+	      if (r->get_value_direct () == sid)
+		{
+		  found_sid = true;
+		  break;
+		}
+	    }
+	  if (found_sid)
+	    continue;
+
+	  /* Try within the ECs.  */
+	  constraint_manager *cm = m_region_model->get_constraints ();
+	  equiv_class_id out (equiv_class_id::null ());
+	  if (cm->get_equiv_class_by_sid (sid, &out))
+	    continue;
+
+	  /* Try within the sm-states.  */
+	  // TODO
+
+	  /* Not found?  Then sid is redundant.  */
+	  gcc_unreachable ();
+	}
+    }
 }
 
 /* Dump this sm_change to PP.  */
@@ -1344,7 +1385,7 @@ test_program_state_merging ()
      They ought to be mergeable, preserving the sm-state.  */
   program_state merged (ext_state);
   ASSERT_TRUE (s0.can_merge_with_p (s1, ext_state, &merged));
-  merged.validate (ext_state);
+  merged.validate (ext_state, true);
 
   /* Verify that the merged state has the sm-state for "p".  */
   region_model *merged_model = merged.m_region_model;
@@ -1355,7 +1396,7 @@ test_program_state_merging ()
   /* Try canonicalizing.  */
   impl_region_model_context merged_ctxt (&merged, NULL, ext_state);
   merged.m_region_model->canonicalize (&merged_ctxt);
-  merged.validate (ext_state);
+  merged.validate (ext_state, true);
 
   /* Verify that the merged state still has the sm-state for "p".  */
   ASSERT_EQ (merged_smap->get_state (merged_model->get_rvalue (p, NULL)),
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 0a4e35f3d5d..da8a3816cae 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -276,7 +276,8 @@ public:
 			 const extrinsic_state &ext_state,
 			 program_state *out) const;
 
-  void validate (const extrinsic_state &ext_state) const;
+  void validate (const extrinsic_state &ext_state,
+		 bool pruned) const;
 
   /* TODO: lose the pointer here (const-correctness issues?).  */
   region_model *m_region_model;
-- 
2.21.0

