From 8f85d4edb766ccb6f54c81a965b1f8e618f4c02a Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 29 Apr 2019 14:07:41 -0400
Subject: [PATCH 206/217] FIXME: impl-4: state-copying in on_assignment; start
 adding selftests

---
 gcc/analyzer/analyzer-plugin.cc |   1 +
 gcc/analyzer/analyzer.h         |   1 +
 gcc/analyzer/impl-4.cc          | 123 ++++++++++++++++++++++++++++++++++------
 3 files changed, 109 insertions(+), 16 deletions(-)

diff --git a/gcc/analyzer/analyzer-plugin.cc b/gcc/analyzer/analyzer-plugin.cc
index b7b505c..17b69bd 100644
--- a/gcc/analyzer/analyzer-plugin.cc
+++ b/gcc/analyzer/analyzer-plugin.cc
@@ -40,6 +40,7 @@ analyzer_selftests (void */*gcc_data*/, void */*user_data*/)
   analyzer_checker_script_cc_tests ();
   analyzer_constraints_cc_tests ();
   analyzer_engine_cc_tests ();
+  analyzer_impl_4_cc_tests ();
 }
 
 } /* end of namespace selftest.  */
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 72c55df..32a6ab9 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -53,6 +53,7 @@ namespace selftest {
 extern void analyzer_checker_script_cc_tests ();
 extern void analyzer_constraints_cc_tests ();
 extern void analyzer_engine_cc_tests ();
+extern void analyzer_impl_4_cc_tests ();
 
 } /* end of namespace selftest.  */
 
diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index 1c0c6a0..ddc753a 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -399,6 +399,12 @@ class exploded_node : public dnode
   program_state on_edge (const exploded_graph &eg,
 			 const superedge *succ) const;
 
+  void on_assignment (const exploded_graph &eg,
+		      path_var lhs,
+		      path_var rhs,
+		      const program_state &old_state,
+		      program_state *new_state) const;
+
   const program_point &get_point () const { return m_ps.m_point; }
   const program_state &get_state () const { return m_ps.m_state; }
 
@@ -1104,10 +1110,18 @@ sm_state_map::get_state (path_var pv) const
     return 0;
 }
 
+/* FIXME.  */
+
 void
 sm_state_map::set_state (path_var pv, state_machine::state_t state)
 {
-  gcc_assert (state != 0); // FIXME: purge for this case
+  /* Special-case state 0 as the default value.  */
+  if (state == 0)
+    {
+      if (m_var_to_state.get (pv))
+	m_var_to_state.remove (pv);
+      return;
+    }
   m_var_to_state.put (pv, state);
 }
 
@@ -1366,7 +1380,7 @@ exploded_node::on_stmt (const exploded_graph &eg) const
   const path_state &old_ps = old_state.m_path_state;
 
   program_state new_state (old_state);
-  path_state &new_ps = new_state.m_path_state;
+  path_state *new_ps = &new_state.m_path_state;
 
   const gimple *stmt = point.get_stmt ();
 
@@ -1395,13 +1409,12 @@ exploded_node::on_stmt (const exploded_graph &eg) const
 	case SSA_NAME:
 	case INTEGER_CST:
 	  {
-#if 0
 	    /* Direct assignment of the form LHS = RHS;  */
-	    on_assignment (node, assign, sm_inst, old_sm_inst,
-			   ps->make_path_var (lhs),
-			   ps->make_path_var (rhs1),
-			   b, ps);
-#endif
+	    on_assignment (eg,
+			   new_ps->make_path_var (lhs),
+			   new_ps->make_path_var (rhs1),
+			   old_state,
+			   &new_state);
 	  }
 	  break;
 
@@ -1418,17 +1431,17 @@ exploded_node::on_stmt (const exploded_graph &eg) const
 	    tree rhs2 = gimple_assign_rhs2 (assign);
 	    // FIXME: stack depths:
 	    equiv_class_id rhs1_ec_id
-	      = new_ps.get_or_add_equiv_class (path_var (rhs1, 0));
+	      = new_ps->get_or_add_equiv_class (path_var (rhs1, 0));
 	    equiv_class_id rhs2_ec_id
-	      = new_ps.get_or_add_equiv_class (path_var (rhs2, 0));
-	    tristate t = new_ps.eval_condition (rhs1_ec_id, op, rhs2_ec_id);
+	      = new_ps->get_or_add_equiv_class (path_var (rhs2, 0));
+	    tristate t = new_ps->eval_condition (rhs1_ec_id, op, rhs2_ec_id);
 	    if (eg.get_logger ())
 	      eg.log ("eval condition: %s", t.as_string ());
 	    if (t.is_known ())
 	      // FIXME: stack depth:
-	      new_ps.add_constraint (path_var (lhs, 0),
-				     t.is_true () ? NE_EXPR : EQ_EXPR,
-				     path_var (integer_zero_node, 0));
+	      new_ps->add_constraint (path_var (lhs, 0),
+				      t.is_true () ? NE_EXPR : EQ_EXPR,
+				      path_var (integer_zero_node, 0));
 #endif
 	  }
 	  break;
@@ -1465,7 +1478,7 @@ exploded_node::on_stmt (const exploded_graph &eg) const
       /* Debugging/test support.  */
       if (is_named_call_p (call, "__dump_constraints", 0))
 	inform (stmt->location, "__dump_constraints: %s",
-		new_ps.to_str ().c_str ());
+		new_ps->to_str ().c_str ());
 
       /* Handle the builtin "__analyzer_eval" by evaluating the input
 	 and dumping as a dummy warning, so that test cases can use dg-warning
@@ -1475,7 +1488,7 @@ exploded_node::on_stmt (const exploded_graph &eg) const
 	{
 	  tree t_arg = gimple_call_arg (call, 0);
 	  tristate t
-	    = new_ps.dump_condition (t_arg, NE_EXPR, integer_zero_node);
+	    = new_ps->dump_condition (t_arg, NE_EXPR, integer_zero_node);
 	  warning_at (call->location, 0, "%s", t.as_string ());
 	}
     }
@@ -1484,6 +1497,37 @@ exploded_node::on_stmt (const exploded_graph &eg) const
   return new_state;
 }
 
+void
+exploded_node::on_assignment (const exploded_graph &eg,
+			      path_var lhs,
+			      path_var rhs,
+			      const program_state &old_state,
+			      program_state *new_state) const
+{
+  new_state->m_path_state.on_assignment (lhs, rhs);
+
+  /* Copy SM state (if any) from RHS to LHS.  */
+  const extrinsic_state &ext_state = eg.m_ext_state;
+  gcc_assert (old_state.m_checker_states.length ()
+	      == ext_state.m_checkers.length ());
+  gcc_assert (new_state->m_checker_states.length ()
+	      == ext_state.m_checkers.length ());
+  for (unsigned i = 0; i < ext_state.m_checkers.length (); i++)
+    {
+      const state_machine &sm = ext_state.get_sm (i);
+      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);
+      if (eg.get_logger ())
+	eg.log ("%s: copying state %qs to %s from %s",
+		sm.get_name (),
+		sm.get_state_name (rhs_state),
+		lhs.to_str ().c_str (),
+		rhs.to_str ().c_str ());
+    }
+}
+
 program_state
 exploded_node::on_edge (const exploded_graph &eg,
 			const superedge *succ) const
@@ -2339,3 +2383,50 @@ run_checkers_impl_4 ()
 
   //fclose (dump);
 }
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* FIXME.  */
+
+static void
+test_sm_state_map ()
+{
+  tree x = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		       get_identifier ("x"),
+		       integer_type_node);
+  tree y = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		       get_identifier ("y"),
+		       integer_type_node);
+  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);
+
+  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.set_state (y0, 0);
+  ASSERT_EQ (map.get_state (y0), 0);
+
+  map.set_state (x0, 0);
+  ASSERT_EQ (map.get_state (x0), 0);
+  ASSERT_TRUE (map.is_empty_p ());
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+analyzer_impl_4_cc_tests ()
+{
+  test_sm_state_map ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
-- 
1.8.5.3

