From 4f9290e3a850f5657ee06eab28b1ba533ddcd19e Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 29 Apr 2019 14:42:41 -0400
Subject: [PATCH 207/217] FIXME: impl-4: update states based on equivalence
 classes

---
 gcc/analyzer/constraints.h |   6 +++
 gcc/analyzer/impl-4.cc     | 109 +++++++++++++++++++++++++++++++++++++--------
 2 files changed, 97 insertions(+), 18 deletions(-)

diff --git a/gcc/analyzer/constraints.h b/gcc/analyzer/constraints.h
index 2d114c3..7269a39 100644
--- a/gcc/analyzer/constraints.h
+++ b/gcc/analyzer/constraints.h
@@ -202,6 +202,12 @@ public:
     return *m_equiv_classes[idx];
   }
 
+  equiv_class &get_equiv_class (path_var pv)
+  {
+    equiv_class_id ec_id = get_or_add_equiv_class (pv);
+    return ec_id.get_obj (*this);
+  }
+
   path_var make_path_var (tree expr) const
   {
     return path_var (expr, m_stack_depth);
diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index ddc753a..9230de5 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -261,7 +261,6 @@ pod_hash_traits<path_var>::is_empty (value_type v)
 
 namespace impl_4 {
 
-#if 1
 /* Map from var to state machine state.  */
 
 class sm_state_map
@@ -277,7 +276,9 @@ public:
   bool is_empty_p () const;
 
   state_machine::state_t get_state (path_var pv) const;
-  void set_state (path_var pv, state_machine::state_t state);
+  void set_state (path_state *ps, path_var pv, state_machine::state_t state);
+  void set_state (const equiv_class &ec, state_machine::state_t state);
+  void impl_set_state (path_var pv, state_machine::state_t state);
 
   iterator_t begin () const { return m_var_to_state.begin (); }
   iterator_t end () const { return m_var_to_state.end (); }
@@ -285,8 +286,8 @@ public:
   //private:
   map_t m_var_to_state;
 
+
 };
-#endif
 
 /* FIXME.  */
 
@@ -302,6 +303,12 @@ public:
 	      pretty_printer *pp) const;
   std::string to_str (const extrinsic_state &ext_state) const;
 
+  equiv_class &get_ec (path_var pv)
+  {
+    equiv_class_id ec_id = m_path_state.get_or_add_equiv_class (pv);
+    return ec_id.get_obj (m_path_state);
+  }
+
 #if 1
   path_state m_path_state;
 #endif
@@ -1113,7 +1120,29 @@ sm_state_map::get_state (path_var pv) const
 /* FIXME.  */
 
 void
-sm_state_map::set_state (path_var pv, state_machine::state_t state)
+sm_state_map::set_state (path_state *ps,
+			 path_var pv,
+			 state_machine::state_t state)
+{
+  equiv_class &ec = ps->get_equiv_class (pv);
+  set_state (ec, state);
+}
+
+/* FIXME.  */
+
+void
+sm_state_map::set_state (const equiv_class &ec, state_machine::state_t state)
+{
+  int i;
+  path_var *pv;
+  FOR_EACH_VEC_ELT (ec.m_vars, i, pv)
+    impl_set_state (*pv, state);
+}
+
+/* Set state of PV to STATE, bypassing equivalend classes.  */
+
+void
+sm_state_map::impl_set_state (path_var pv, state_machine::state_t state)
 {
   /* Special-case state 0 as the default value.  */
   if (state == 0)
@@ -1133,9 +1162,15 @@ public:
   impl_sm_context (const exploded_graph &eg,
 		   const state_machine &sm,
 		   const exploded_node *node,
+
+		   /* FIXME: should we be getting the ECs from the
+		      old state, rather than the new?  */
+		   program_state *new_state,
+
 		   const sm_state_map &old_smap,
 		   sm_state_map *new_smap)
   : log_user (eg.get_logger ()), m_eg (eg), m_sm (sm), m_node (node),
+    m_new_state (new_state),
     m_old_smap (old_smap), m_new_smap (new_smap)
   {
   }
@@ -1149,7 +1184,7 @@ public:
     path_var pv (var, 0); // FIXME: stack depth
     state_machine::state_t current = m_old_smap.get_state (pv);
     if (current == from)
-      m_new_smap->set_state (pv, to);
+      m_new_smap->set_state (&m_new_state->m_path_state, pv, to);
   }
 
   void warn_for_state (const supernode *node, const gimple *stmt,
@@ -1169,6 +1204,7 @@ public:
   const exploded_graph &m_eg;
   const state_machine &m_sm;
   const exploded_node *m_node;
+  program_state *m_new_state;
   const sm_state_map &m_old_smap;
   sm_state_map *m_new_smap;
 };
@@ -1390,6 +1426,7 @@ exploded_node::on_stmt (const exploded_graph &eg) const
     {
       const state_machine &sm = m_ext_state.get_sm (i);
       impl_sm_context ctxt (eg, sm, this,
+			    &new_state,
 			    *old_state.m_checker_states[i],
 			    new_state.m_checker_states[i]);
       sm.on_stmt (&ctxt, point.m_supernode, stmt);
@@ -1518,7 +1555,7 @@ exploded_node::on_assignment (const exploded_graph &eg,
       const sm_state_map *src_smap = old_state.m_checker_states[i];
       sm_state_map *dst_smap = new_state->m_checker_states[i];
       state_machine::state_t rhs_state = src_smap->get_state (rhs);
-      dst_smap->set_state (lhs, rhs_state);
+      dst_smap->set_state (&new_state->m_path_state, lhs, rhs_state);
       if (eg.get_logger ())
 	eg.log ("%s: copying state %qs to %s from %s",
 		sm.get_name (),
@@ -1545,6 +1582,7 @@ exploded_node::on_edge (const exploded_graph &eg,
     {
       const state_machine &sm = m_ext_state.get_sm (i);
       impl_sm_context ctxt (eg, sm, this,
+			    &new_state,
 			    *old_state.m_checker_states[i],
 			    new_state.m_checker_states[i]);
       sm.on_edge (&ctxt, last_stmt, succ);
@@ -2402,21 +2440,55 @@ test_sm_state_map ()
   path_var x0 (x, 0);
   path_var y0 (y, 0);
 
-  impl_4::sm_state_map map;
-  ASSERT_TRUE (map.is_empty_p ());
-  ASSERT_EQ (map.get_state (x0), 0);
+  /* Test setting states on path_var instances directly.  */
+  {
+    impl_4::sm_state_map map;
+    ASSERT_TRUE (map.is_empty_p ());
+    ASSERT_EQ (map.get_state (x0), 0);
 
-  map.set_state (x0, 42);
-  ASSERT_EQ (map.get_state (x0), 42);
-  ASSERT_EQ (map.get_state (y0), 0);
-  ASSERT_FALSE (map.is_empty_p ());
+    map.impl_set_state (x0, 42);
+    ASSERT_EQ (map.get_state (x0), 42);
+    ASSERT_EQ (map.get_state (y0), 0);
+    ASSERT_FALSE (map.is_empty_p ());
 
-  map.set_state (y0, 0);
-  ASSERT_EQ (map.get_state (y0), 0);
+    map.impl_set_state (y0, 0);
+    ASSERT_EQ (map.get_state (y0), 0);
+
+    map.impl_set_state (x0, 0);
+    ASSERT_EQ (map.get_state (x0), 0);
+    ASSERT_TRUE (map.is_empty_p ());
+  }
+
+  /* Test setting states via equivalence classes.  */
+  {
+    impl_4::sm_state_map map;
+    ASSERT_TRUE (map.is_empty_p ());
+    ASSERT_EQ (map.get_state (x0), 0);
+    ASSERT_EQ (map.get_state (y0), 0);
+
+    path_state ps;
+    ps.add_constraint (x0, EQ_EXPR, y0);
+
+    /* Setting x to a state should also update y, as they
+       are in the same equivalence class.  */
+    map.set_state (&ps, x0, 5);
+    ASSERT_EQ (map.get_state (x0), 5);
+    ASSERT_EQ (map.get_state (y0), 5);
+  }
+}
 
-  map.set_state (x0, 0);
-  ASSERT_EQ (map.get_state (x0), 0);
-  ASSERT_TRUE (map.is_empty_p ());
+static void
+test_assignments ()
+{
+  /* Verify X = Y, then Y = Z.
+     Should propagate states accordingly.  */
+#if 0
+exploded_node::on_assignment (const exploded_graph &eg,
+			      path_var lhs,
+			      path_var rhs,
+			      const program_state &old_state,
+			      program_state *new_state) const
+#endif
 }
 
 /* Run all of the selftests within this file.  */
@@ -2425,6 +2497,7 @@ void
 analyzer_impl_4_cc_tests ()
 {
   test_sm_state_map ();
+  test_assignments ();
 }
 
 } // namespace selftest
-- 
1.8.5.3

