From a0d857f07ab8f4e8200aa9d7f55ee37ff40700ab Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 28 Feb 2019 16:50:35 -0500
Subject: [PATCH 018/161] FIXME: handle equivalence classes in state
 transitions

---
 gcc/analyzer/engine.cc                   | 94 ++++++++++++++++++++------------
 gcc/testsuite/gcc.dg/analyzer/malloc-1.c |  5 +-
 2 files changed, 61 insertions(+), 38 deletions(-)

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 09bc1a9..17d45c3 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -512,9 +512,10 @@ class engine_sm_context : public sm_context
 {
 public:
   engine_sm_context (engine *e, sm_instance *sm_inst,
-		     const sm_instance *old_sm_inst, diagnostic_path *path)
+		     const sm_instance *old_sm_inst, diagnostic_path *path,
+		     path_state *ps)
     : m_engine (e), m_sm_inst (sm_inst), m_old_sm_inst (old_sm_inst),
-      m_path (path) {}
+      m_path (path), m_ps (ps) {}
 
   void on_transition (const supernode *node, const gimple *stmt,
 		      tree var,
@@ -529,41 +530,59 @@ public:
       m_engine->log ("active vars was: %s",
 		     m_old_sm_inst->m_active_vars.to_str (m_engine->m_sm).c_str ());
 
-    // TODO
-    var_state_t vs_from (var, from);
-    if (const_cast <sm_instance *> (m_old_sm_inst)->m_active_vars.m_set.contains (vs_from))
-      {
-        m_engine->log ("from state found");
-        m_sm_inst->m_active_vars.m_set.remove (vs_from);
-      }
-    else
+    // FIXME: do it for everything in the equivalence class
+    // FIXME: stack depth:
+    equiv_class_id ec_id = m_ps->get_or_add_equiv_class (path_var (var, 0));
+    equiv_class &ec = ec_id.get_obj (*m_ps);
+
+    if (m_engine->get_logger ())
+      m_engine->log ("considering equivalence class: %s",
+		     ec.to_str ().c_str ());
+    unsigned i;
+    path_var *ec_var;
+    FOR_EACH_VEC_ELT (ec.m_vars, i, ec_var)
       {
-	m_engine->log ("from state %s not found",
-		       m_engine->m_sm.get_state_name (from));
-         /* If it's the start state and we're non-empty, then start,
-	    otherwise ignore.  */
-         if (from == 0)
-	   {
-             /* Start state.  */
-	     if (m_old_sm_inst->m_active_vars.has_expr (var))
-	       return;
-	   }
-	 else
-	   return;
-      }
+	if (m_engine->get_logger ())
+	  m_engine->log ("considering %s",
+			 ec_var->to_str ().c_str ());
+	// TODO
+	var_state_t vs_from (ec_var->m_tree, from);
+	if (const_cast <sm_instance *> (m_old_sm_inst)->m_active_vars.m_set.contains (vs_from))
+	  {
+	    m_engine->log ("from state found");
+	    // FIXME: why is the check on the old sm_inst vs the new sm_inst?
+	    if (m_sm_inst->m_active_vars.m_set.contains (vs_from))
+	      m_sm_inst->m_active_vars.m_set.remove (vs_from);
+	  }
+	else
+	  {
+	    m_engine->log ("from state %s not found",
+			   m_engine->m_sm.get_state_name (from));
+	    /* If it's the start state and we're non-empty, then start,
+	       otherwise ignore.  */
+	    if (from == 0)
+	      {
+		/* Start state.  */
+		if (m_old_sm_inst->m_active_vars.has_expr (ec_var->m_tree))
+		  continue;
+	      }
+	    else
+	      continue;
+	  }
 
-    m_sm_inst->m_active_vars.m_set.add (var_state_t (var, to));
+	m_sm_inst->m_active_vars.m_set.add (var_state_t (ec_var->m_tree, to));
 
-    if (m_path)
-      m_path->add_event (stmt->location,
-			 node->m_fun->decl, 0,/*state.get_stack_depth ()*/
-			 "state of %qE: %qs -> %qs", var,
-			 m_engine->m_sm.get_state_name (from),
-			 m_engine->m_sm.get_state_name (to));
+	if (m_path)
+	  m_path->add_event (stmt->location,
+			     node->m_fun->decl, 0,/*state.get_stack_depth ()*/
+			     "state of %qE: %qs -> %qs", ec_var->m_tree,
+			     m_engine->m_sm.get_state_name (from),
+			     m_engine->m_sm.get_state_name (to));
+      }
 
-  if (m_engine->get_logger ())
-    m_engine->log ("active vars is now: %s",
-		   m_sm_inst->m_active_vars.to_str (m_engine->m_sm).c_str ());
+    if (m_engine->get_logger ())
+      m_engine->log ("active vars is now: %s",
+		     m_sm_inst->m_active_vars.to_str (m_engine->m_sm).c_str ());
   }
 
   void error (const supernode *node, const gimple *stmt,
@@ -601,6 +620,7 @@ private:
   sm_instance *m_sm_inst;
   const sm_instance *m_old_sm_inst;
   diagnostic_path *m_path;
+  path_state *m_ps;
 };
 
 void
@@ -741,7 +761,7 @@ engine::dfs_traverse (sm_instance sm_inst, backtrace b,
 	   engine_sm_context ctxt (this,
 				   &per_edge_new_sm_inst,
 				   &sm_inst,
-				   &new_b.m_path);
+				   &new_b.m_path, &new_ps);
 	   m_sm.on_edge (&ctxt, last_stmt, succ);
 
 	   dfs_traverse (per_edge_new_sm_inst, new_b, new_ps, succ->m_dest);
@@ -764,7 +784,7 @@ engine::on_stmt (const supernode *node, const gimple *stmt,
       log ("stmt: %s", pp_formatted_text (&pp));
       log ("sm_inst: %s", sm_inst->to_str (m_sm).c_str ());
     }
-  engine_sm_context ctxt (this, sm_inst, old_sm_inst, path);
+  engine_sm_context ctxt (this, sm_inst, old_sm_inst, path, ps);
   m_sm.on_stmt (&ctxt, node, stmt);
   m_on_stmt_calls++;
 
@@ -779,6 +799,7 @@ engine::on_stmt (const supernode *node, const gimple *stmt,
 	  break;
 
 	case NOP_EXPR:
+	case SSA_NAME:
 	  {
 	    /* Direct assignment of the form LHS = RHS;  */
 	    on_assignment (node, assign, sm_inst, old_sm_inst, lhs, rhs1, ps);
@@ -893,7 +914,8 @@ engine::on_assignment (const supernode *node, const gimple *stmt,
       if ((*iter).m_var == rhs)
 	{
 	  log ("got match");
-	  engine_sm_context ctxt (this, sm_inst, old_sm_inst, NULL /* FIXME */);
+	  engine_sm_context ctxt (this, sm_inst, old_sm_inst, NULL /* FIXME */,
+				  ps);
 	  // FIXME: we want to overwrite any old states
 	  // (but respect the gstate, I think):
 	  ctxt.on_transition (node, stmt, lhs, 0/* FIXME */, (*iter).m_state);
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
index 80974c5..ca5da1d 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
@@ -45,8 +45,9 @@ void test_6 (void *ptr)
   void *q;
   q = ptr;
   free (ptr);
-  free (q); /* { dg-error "double-free of 'q'" "" { xfail *-*-* } } */ 
-  // TODO: need to handle equivalence classes in state transitions
+  free (q); /* { dg-error "double-free of 'q'" } */
+  /* The above case requires us to handle equivalence classes in
+     state transitions.  */
 }
 
 void test_7 (void)
-- 
1.8.5.3

