From 430fe9d37bbef56d5db82f1279bb82003bff9636 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 9 Apr 2019 16:17:34 -0400
Subject: [PATCH 185/217] FIXME: impl-3: enough state transitions for
 malloc-ipa-8-double-free.c to work (kind-of)

---
 gcc/analyzer/constraints.cc | 17 +++++++++
 gcc/analyzer/constraints.h  |  1 +
 gcc/analyzer/impl-3.cc      | 87 +++++++++++++++++++++++++++++++++------------
 3 files changed, 83 insertions(+), 22 deletions(-)

diff --git a/gcc/analyzer/constraints.cc b/gcc/analyzer/constraints.cc
index 75bb944..5750c17 100644
--- a/gcc/analyzer/constraints.cc
+++ b/gcc/analyzer/constraints.cc
@@ -80,6 +80,13 @@ equiv_class::equiv_class (const equiv_class &other)
 
 /* FIXME.  */
 
+equiv_class::~equiv_class ()
+{
+  delete m_extra_state;
+}
+
+/* FIXME.  */
+
 std::string
 equiv_class::to_str () const
 {
@@ -552,6 +559,16 @@ path_state::add_constraint (equiv_class_id lhs_ec_id,
 	    lhs_ec_obj.m_constant = rhs_ec_obj.m_constant;
 	  }
 
+	/* Merge any extra state.  */
+	extra_ec_state *lhs_extra_state = lhs_ec_obj.m_extra_state;
+	extra_ec_state *rhs_extra_state = rhs_ec_obj.m_extra_state;
+
+	/* How to merge two state objects?  */
+	gcc_assert (!(lhs_extra_state && rhs_extra_state));
+
+	if (lhs_extra_state == NULL && rhs_extra_state)
+	  lhs_ec_obj.m_extra_state = rhs_extra_state->clone ();
+
 	/* Drop rhs equivalence class, overwriting it with the
 	   final ec (which might be the same one).  */
 	equiv_class_id final_ec_id = m_equiv_classes.length () - 1;
diff --git a/gcc/analyzer/constraints.h b/gcc/analyzer/constraints.h
index 4a19012..b8ef4ed 100644
--- a/gcc/analyzer/constraints.h
+++ b/gcc/analyzer/constraints.h
@@ -97,6 +97,7 @@ class equiv_class
 public:
   equiv_class ();
   equiv_class (const equiv_class &other);
+  ~equiv_class ();
 
   std::string to_str () const;
 
diff --git a/gcc/analyzer/impl-3.cc b/gcc/analyzer/impl-3.cc
index 21e788c..f3e65ff 100644
--- a/gcc/analyzer/impl-3.cc
+++ b/gcc/analyzer/impl-3.cc
@@ -54,23 +54,37 @@ class ec3_state : public extra_ec_state
 {
  public:
   ec3_state (const state_machine &sm,
-	     state_machine::state_t state)
-  : m_sm (sm), m_state (state)
+	     state_machine::state_t baseline_state,
+	     state_machine::state_t current_state)
+  : m_sm (sm),
+    m_baseline_state (baseline_state),
+    m_current_state (current_state)
   {
   }
 
   extra_ec_state *clone () const FINAL OVERRIDE
   {
-    return new ec3_state (m_sm, m_state);
+    return new ec3_state (m_sm, m_baseline_state, m_current_state);
   }
 
   std::string to_str () const FINAL OVERRIDE
   {
-    return std::string (" (state='") + m_sm.get_state_name (m_state) + "')";
+    return (std::string (" (state='")
+	    + m_sm.get_state_name (m_current_state)
+	    + "')");
   }
 
   const state_machine &m_sm;
-  state_machine::state_t m_state;
+
+  /* For use in engine_sm_context::on_transition so that we only apply
+     changes relative to the state of the EC at the start of a set of
+     transitions.  */
+  state_machine::state_t m_baseline_state;
+
+  state_machine::state_t m_current_state;
+
+  // FIXME: alternatively, do these needs to be sets of states, to cope with
+  // merging of ECs???
 };
 
 /* FIXME.  */
@@ -165,7 +179,7 @@ public:
 
   /* Get the SUPEREDGE_RETURN corresponding to the most recent
      unreturned SUPEREDGE_CALL.  Return NULL if there isn't one.  */
-  const superedge *get_next_return_edge (const supergraph &sg) const
+  const superedge *get_next_return_edge () const
   {
     if (m_active_calls.length () == 0)
       return NULL;
@@ -205,8 +219,10 @@ public:
 
   int get_stack_depth () const { return m_ps.m_stack_depth; }
 
-  state_machine::state_t get_state (tree expr);
-  void set_state (tree expr, state_machine::state_t state);
+  state_machine::state_t get_baseline_state (tree expr);
+  void set_state (tree expr,
+		  state_machine::state_t baseline_state,
+		  state_machine::state_t new_state);
 
   equiv_class &get_ec (tree expr)
   {
@@ -281,7 +297,27 @@ class engine_sm_context : public sm_context
 {
 public:
   engine_sm_context (engine *e, checker3_path *path)
-    : m_engine (e), m_path (path) {}
+  : m_engine (e), m_path (path)
+  {
+    reset_baseline_states ();
+  }
+
+  ~engine_sm_context ()
+  {
+    reset_baseline_states ();
+  }
+
+  void reset_baseline_states ()
+  {
+    int i;
+    equiv_class *ec;
+    FOR_EACH_VEC_ELT (m_path->m_ps.m_equiv_classes, i, ec)
+      {
+	ec3_state *extra_state = (ec3_state *)ec->m_extra_state;
+	if (extra_state)
+	  extra_state->m_baseline_state = extra_state->m_current_state;
+      }
+  }
 
   /* FIXME:  The SM can call this multiple times on one statement, e.g.
      start -> free
@@ -302,21 +338,25 @@ public:
 		    var,
 		    m_engine->m_sm.get_state_name (from),
 		    m_engine->m_sm.get_state_name (to));
-    state_machine::state_t current = m_path->get_state (var);
-    if (current == from)
-      m_path->set_state (var, to);
+    state_machine::state_t baseline = m_path->get_baseline_state (var);
+    if (from == baseline)
+      m_path->set_state (var, baseline, to);
   }
 
   void warn_for_state (const supernode *node, const gimple *stmt,
 		       tree var, state_machine::state_t state,
 		       pending_diagnostic *d) FINAL OVERRIDE
   {
-    JIT_LOG_FUNC (m_engine->get_logger ());
+    JIT_LOG_FUNC_2 (m_engine->get_logger (),
+		    "if var: %qE in state: %s", var,
+		    m_engine->m_sm.get_state_name (state));
+
     gcc_assert (d); // take ownership
 
-    state_machine::state_t current = m_path->get_state (var);
-    if (current == state)
+    state_machine::state_t baseline = m_path->get_baseline_state (var);
+    if (state == baseline)
       {
+	inform (stmt->location, "FIXME: would emit diagnostic here");
 	// TODO:
 #if 0
 	candidate_diagnostic *cd
@@ -427,26 +467,29 @@ checker3_path::to_str () const
 }
 
 state_machine::state_t
-checker3_path::get_state (tree expr)
+checker3_path::get_baseline_state (tree expr)
 {
   equiv_class &ec = get_ec (expr);
   ec3_state *extra_state = (ec3_state *)ec.m_extra_state;
   if (extra_state)
-    return extra_state->m_state;
+    return extra_state->m_baseline_state;
   else
     return 0;  // FIXME "start"
 }
 
 void
-checker3_path::set_state (tree expr, state_machine::state_t state)
+checker3_path::set_state (tree expr,
+			  state_machine::state_t baseline_state,
+			  state_machine::state_t new_state)
 {
   equiv_class &ec = get_ec (expr);
   delete ec.m_extra_state;
-  ec.m_extra_state = new ec3_state (get_sm (), state);
+  ec.m_extra_state = new ec3_state (get_sm (), baseline_state, new_state);
   if (m_engine->get_logger ())
-    m_engine->log ("%qE state -> %qs",
+    m_engine->log ("%qE state: %qs -> %qs",
 		   expr,
-		   get_sm ().get_state_name (state));
+		   get_sm ().get_state_name (baseline_state),
+		   get_sm ().get_state_name (new_state));
 }
 
 const supergraph &
@@ -513,7 +556,7 @@ engine::traverse (checker3_path path)
 	  break;
 	case SUPEREDGE_RETURN:
 	  /* Only follow the correct return edge.  */
-	  if (succ != path.get_next_return_edge (m_sg))
+	  if (succ != path.get_next_return_edge ())
 	    {
 	      log ("rejecting return edge");
 	      continue;
-- 
1.8.5.3

