From f1ad08a54d0141d915ba2033e65e2e560af5c297 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 19 Feb 2020 16:15:01 -0500
Subject: [PATCH 24/31] FIXME: canon&purge: and svalue_id_map to
 impl_region_model_context

---
 gcc/analyzer/engine.cc        | 20 ++++++++++-------
 gcc/analyzer/exploded-graph.h |  5 ++---
 gcc/analyzer/program-state.cc |  5 ++---
 gcc/analyzer/program-state.h  |  1 -
 gcc/analyzer/region-model.cc  | 41 +++++++++++++++++++++++++++++++++--
 gcc/analyzer/region-model.h   | 21 ++++++++++--------
 6 files changed, 67 insertions(+), 26 deletions(-)

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 79c5ba0155a..6602882b46f 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -80,11 +80,14 @@ impl_region_model_context (exploded_graph &eg,
   m_enode_for_diag (enode_for_diag),
   m_old_state (old_state),
   m_new_state (new_state),
+  m_svalue_id_map (old_state
+		   ? old_state->m_region_model->get_num_svalues () : 0),
   m_change (change),
   m_stmt (stmt),
   m_stmt_finder (stmt_finder),
   m_ext_state (eg.get_ext_state ())
 {
+  m_svalue_id_map.make_identity ();
 }
 
 impl_region_model_context::
@@ -94,6 +97,7 @@ impl_region_model_context (program_state *state,
 : m_eg (NULL), m_logger (NULL), m_enode_for_diag (NULL),
   m_old_state (NULL),
   m_new_state (state),
+  m_svalue_id_map (0),
   m_change (change),
   m_stmt (NULL),
   m_stmt_finder (NULL),
@@ -117,11 +121,11 @@ impl_region_model_context::remap_svalue_ids (const svalue_id_map &map)
   m_new_state->remap_svalue_ids (map);
   if (m_change)
     m_change->remap_svalue_ids (map);
+  m_svalue_id_map.compose (map);
 }
 
 int
-impl_region_model_context::on_svalue_purge (svalue_id first_unused_sid,
-					    const svalue_id_map &map)
+impl_region_model_context::on_svalue_purge (svalue_id first_unused_sid)
 {
   int total = 0;
   int sm_idx;
@@ -130,7 +134,7 @@ impl_region_model_context::on_svalue_purge (svalue_id first_unused_sid,
     {
       const state_machine &sm = m_ext_state.get_sm (sm_idx);
       total += smap->on_svalue_purge (sm, sm_idx, first_unused_sid,
-				      map, this);
+				      this);
     }
   if (m_change)
     total += m_change->on_svalue_purge (first_unused_sid);
@@ -488,15 +492,13 @@ readability_comparator (const void *p1, const void *p2)
 
 /* Create an sm_context and use it to call SM's on_leak vfunc, so that
    it can potentially complain about a leak of DST_SID (in a new region_model)
-   in the given STATE, where MAP can be used to map SID back to an "old"
-   region_model.  */
+   in the given STATE.  */
 
 void
 impl_region_model_context::on_state_leak (const state_machine &sm,
 					  int sm_idx,
 					  svalue_id dst_sid,
 					  svalue_id first_unused_sid,
-					  const svalue_id_map &map,
 					  state_machine::state_t state)
 {
   logger * const logger = get_logger ();
@@ -545,11 +547,13 @@ impl_region_model_context::on_state_leak (const state_machine &sm,
 
   /* SID has leaked within the new state: no regions use it.
      We need to convert it back to a tree, but since no regions use it, we
-     have to use MAP to convert it back to an svalue_id within the old state.
+     have to use m_svalue_id_map to convert it back to an svalue_id within the
+     old state.
      We can then look that svalue_id up to locate regions and thus tree(s)
      that use it.  */
 
-  svalue_id old_sid = map.get_src_for_dst (dst_sid);
+  svalue_id old_sid = m_svalue_id_map.get_src_for_dst (dst_sid);
+  gcc_assert (!old_sid.null_p ());
 
   auto_vec<path_var> leaked_pvs;
   m_old_state->m_region_model->get_path_vars_for_svalue (old_sid, &leaked_pvs);
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index c5d8328c71b..985548f4d42 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -49,8 +49,7 @@ class impl_region_model_context : public region_model_context
 
   void remap_svalue_ids (const svalue_id_map &map) FINAL OVERRIDE;
 
-  int on_svalue_purge (svalue_id first_unused_sid,
-		       const svalue_id_map &map) FINAL OVERRIDE;
+  int on_svalue_purge (svalue_id first_unused_sid) FINAL OVERRIDE;
 
   logger *get_logger () FINAL OVERRIDE
   {
@@ -61,7 +60,6 @@ class impl_region_model_context : public region_model_context
 		      int sm_idx,
 		      svalue_id sid,
 		      svalue_id first_unused_sid,
-		      const svalue_id_map &map,
 		      state_machine::state_t state);
 
   void on_inherited_svalue (svalue_id parent_sid,
@@ -84,6 +82,7 @@ class impl_region_model_context : public region_model_context
   const exploded_node *m_enode_for_diag;
   const program_state *m_old_state;
   program_state *m_new_state;
+  svalue_id_map m_svalue_id_map;
   state_change *m_change;
   const gimple *m_stmt;
   stmt_finder *m_stmt_finder;
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index ad8ea0407fc..b550af9d496 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -472,14 +472,13 @@ sm_state_map::remap_svalue_ids (const svalue_id_map &map)
 
 /* Purge any state for svalue_ids >= FIRST_UNUSED_SID.
    If !SM::can_purge_p, then report the state as leaking,
-   using SM_IDX, CTXT, and MAP.
+   using SM_IDX, CTXT.
    Return the number of states that were purged.  */
 
 int
 sm_state_map::on_svalue_purge (const state_machine &sm,
 			       int sm_idx,
 			       svalue_id first_unused_sid,
-			       const svalue_id_map &map,
 			       impl_region_model_context *ctxt)
 {
   /* TODO: ideally remove the slot directly; for now
@@ -497,7 +496,7 @@ sm_state_map::on_svalue_purge (const state_machine &sm,
 
 	  if (!sm.can_purge_p (e.m_state))
 	    ctxt->on_state_leak (sm, sm_idx, dst_sid, first_unused_sid,
-				 map, e.m_state);
+				 e.m_state);
 
 	  to_remove.safe_push (dst_sid);
 	}
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index 2c778ccb9ac..42ce0b31853 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -187,7 +187,6 @@ public:
   int on_svalue_purge (const state_machine &sm,
 		       int sm_idx,
 		       svalue_id first_unused_sid,
-		       const svalue_id_map &map,
 		       impl_region_model_context *ctxt);
 
   void on_inherited_svalue (svalue_id parent_sid,
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 0ca0bb51a59..511f230db72 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -5579,6 +5579,7 @@ region_model::get_representative_path_var (region_id rid) const
 void
 region_model::get_path_vars_for_svalue (svalue_id sid, vec<path_var> *out) const
 {
+  gcc_assert (!sid.null_p ());
   unsigned i;
   region *region;
   FOR_EACH_VEC_ELT (m_regions, i, region)
@@ -6184,7 +6185,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, c.m_sid_map);
+	= ctxt->on_svalue_purge (first_unused_sid);
       if (stats)
 	stats->m_num_client_items += num_client_items_purged;
     }
@@ -7244,7 +7245,7 @@ test_tree_cmp_on_constants ()
 	}
 }
 
-/* FIXME.  */
+/* Verify that id_map works as expected.  */
 
 static void
 test_id_map ()
@@ -7291,6 +7292,41 @@ test_id_map ()
   }
 }
 
+/* Verify that we can compose id_maps with non-equal size.  */
+
+static void
+test_id_map_2 ()
+{
+  svalue_id sid0 = svalue_id::from_int (0);
+  svalue_id sid1 = svalue_id::from_int (1);
+  svalue_id sid2 = svalue_id::from_int (2);
+
+  /* A swap of a pair of IDs: sid0 and sid1.  */
+  svalue_id_map swap (2);
+  swap.put (sid0, sid1);
+  swap.put (sid1, sid0);
+
+  /* A rotation of 3 IDs: sid0 -> sid1 -> sid2.  */
+  svalue_id_map rot (3);
+  rot.put (sid0, sid1);
+  rot.put (sid1, sid2);
+  rot.put (sid2, sid0);
+
+  ASSERT_NE (swap, rot);
+
+  /* Swap then rotate.  */
+  svalue_id_map c (swap);
+  c.compose (rot);
+
+  ASSERT_EQ (c.get_dst_for_src (sid0), sid2);
+  ASSERT_EQ (c.get_dst_for_src (sid1), sid1);
+  ASSERT_EQ (c.get_dst_for_src (sid2), svalue_id::null ());
+
+  ASSERT_EQ (c.get_src_for_dst (sid0), svalue_id::null ());
+  ASSERT_EQ (c.get_src_for_dst (sid1), sid1);
+  ASSERT_EQ (c.get_src_for_dst (sid2), sid0);
+}
+
 /* Implementation detail of the ASSERT_CONDITION_* macros.  */
 
 void
@@ -8465,6 +8501,7 @@ analyzer_region_model_cc_tests ()
 {
   test_tree_cmp_on_constants ();
   test_id_map ();
+  test_id_map_2 ();
   test_dump ();
   test_unique_constants ();
   test_svalue_equality ();
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 992864276bd..8542d75ccd3 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -214,7 +214,8 @@ template <typename T>
 inline id_map<T>&
 id_map<T>::operator= (const id_map &other)
 {
-  gcc_assert (m_src_to_dst.length () == other.m_src_to_dst.length ());
+  m_src_to_dst.safe_grow (other.m_src_to_dst.length ());
+  m_dst_to_src.safe_grow (other.m_dst_to_src.length ());
   gcc_assert (other.m_src_to_dst.length () == other.m_dst_to_src.length ());
   for (unsigned i = 0; i < other.m_src_to_dst.length (); i++)
     {
@@ -256,8 +257,9 @@ id_map<T>::compose (const id_map &other)
 {
   gcc_assert (m_src_to_dst.length () == m_dst_to_src.length ());
 
-  id_map result (m_src_to_dst.length ());
-  for (unsigned i = 0; i < m_src_to_dst.length (); i++)
+  id_map result (MAX (m_src_to_dst.length (),
+		      other.m_src_to_dst.length ()));
+  for (unsigned i = 0; i < result.m_src_to_dst.length (); i++)
     {
       T id = T::from_int (i);
       result.m_src_to_dst[i] = other.get_dst_for_src (get_dst_for_src (id));
@@ -275,6 +277,8 @@ id_map<T>::get_dst_for_src (T src) const
 {
   if (src.null_p ())
     return src;
+  if (src.as_int () >= (int)m_src_to_dst.length ())
+    return T::null ();
   return m_src_to_dst[src.as_int ()];
 }
 
@@ -287,6 +291,8 @@ id_map<T>::get_src_for_dst (T dst) const
 {
   if (dst.null_p ())
     return dst;
+  if (dst.as_int () >= (int)m_dst_to_src.length ())
+    return T::null ();
   return m_dst_to_src[dst.as_int ()];
 }
 
@@ -2001,11 +2007,8 @@ class region_model_context
      All SIDs from FIRST_PURGED_SID numerically upwards are being
      purged.
      The return values is a count of how many items of data the client
-     has purged (potentially for use in selftests).
-     MAP has already been applied to the IDs, but is provided in case
-     the client needs to figure out the old IDs.  */
-  virtual int on_svalue_purge (svalue_id first_purged_sid,
-			       const svalue_id_map &map) = 0;
+     has purged (potentially for use in selftests).  */
+  virtual int on_svalue_purge (svalue_id first_purged_sid) = 0;
 
   virtual logger *get_logger () = 0;
 
@@ -2161,7 +2164,7 @@ public:
   }
 #endif
 
-  int on_svalue_purge (svalue_id, const svalue_id_map &) FINAL OVERRIDE
+  int on_svalue_purge (svalue_id) FINAL OVERRIDE
   {
     /* Empty.  */
     return 0;
-- 
2.21.0

