From d92ec819f2d31e8f3c6f76fa3f6acebaa593ce3a Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Sun, 26 Jul 2020 17:20:10 -0400
Subject: [PATCH 335/377] FIXME: implement loop_replay_hack

---
 gcc/analyzer/diagnostic-manager.cc            |  5 +-
 gcc/analyzer/engine.cc                        | 16 +++++-
 gcc/analyzer/exploded-graph.h                 |  2 +-
 gcc/analyzer/region-model2.cc                 |  8 +++
 gcc/analyzer/region-model2.h                  |  5 ++
 gcc/analyzer/store2.cc                        | 53 +++++++++++++++++++
 gcc/analyzer/store2.h                         |  2 +
 gcc/testsuite/gcc.dg/analyzer/feasibility-1.c | 10 ++++
 8 files changed, 97 insertions(+), 4 deletions(-)

diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index 9ecb4832478..f7c63080caf 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -356,6 +356,7 @@ public:
 
   void add (logger *logger,
 	    const shortest_exploded_paths &sp,
+	    const exploded_graph *eg,
 	    saved_diagnostic *sd)
   {
     /* Build a dedupe_candidate for SD.
@@ -379,7 +380,7 @@ public:
     feasibility_problem *p = NULL;
     bool feasible;
     if (m_engine2)
-      feasible = dc->get_path ().feasible2_p (logger, &p, m_engine2);
+      feasible = dc->get_path ().feasible2_p (logger, &p, m_engine2, eg);
     else
       feasible = dc->get_path ().feasible_p (logger, &p);
     if (!feasible)
@@ -519,7 +520,7 @@ diagnostic_manager::emit_saved_diagnostics (const exploded_graph &eg)
   int i;
   saved_diagnostic *sd;
   FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
-    best_candidates.add (get_logger (), sp, sd);
+    best_candidates.add (get_logger (), sp, &eg, sd);
 
   /* For each dedupe-key, call emit_saved_diagnostic on the "best"
      saved_diagnostic.  */
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 42d80bdeac7..6a357223efe 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -4013,10 +4013,12 @@ exploded_path::feasible_p (logger *logger, feasibility_problem **out) const
 
 bool
 exploded_path::feasible2_p (logger *logger, feasibility_problem **out,
-			    engine2 *eng) const
+			    engine2 *eng, const exploded_graph *eg) const
 {
   LOG_SCOPE (logger);
 
+  auto_sbitmap snodes_visited (eg->get_supergraph ().m_nodes.length ());
+
   /* Traverse the path, updating this model.  */
   region_model2 model (eng->get_model_manager ());
   for (unsigned edge_idx = 0; edge_idx < m_edges.length (); edge_idx++)
@@ -4110,6 +4112,8 @@ exploded_path::feasible2_p (logger *logger, feasibility_problem **out,
 	{
 	  const cfg_superedge *last_cfg_superedge
 	    = src_point.get_from_edge ()->dyn_cast_cfg_superedge ();
+	  const exploded_node &dst_enode = *eedge->m_dest;
+	  const unsigned dst_snode_idx = dst_enode.get_supernode ()->m_index;
 	  if (last_cfg_superedge)
 	    {
 	      if (logger)
@@ -4117,7 +4121,17 @@ exploded_path::feasible2_p (logger *logger, feasibility_problem **out,
 	      model.update_for_phis (src_enode.get_supernode (),
 				     last_cfg_superedge,
 				     NULL);
+	      /* If we've entering an snode that we've already visited on this
+		 epath, then we need do fix things up for loops; see the
+		 comment for store2::loop_replay_hack.
+		 Perhaps we should probably also verify the callstring,
+		 and track program_points,  but hopefully doing it by supernode
+		 is good enough.  */
+	      if (bitmap_bit_p (snodes_visited, dst_snode_idx))
+		model.loop_replay_hack
+		  (dst_enode.get_state ().m_region_model2);
 	    }
+	  bitmap_set_bit (snodes_visited, dst_snode_idx);
 	}
 
       if (logger)
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index b2056e411a7..744c2cfd8aa 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -957,7 +957,7 @@ public:
 
   bool feasible_p (logger *logger, feasibility_problem **out) const;
   bool feasible2_p (logger *logger, feasibility_problem **out,
-		    engine2 *eng) const;
+		    engine2 *eng, const exploded_graph *eg) const;
 
   auto_vec<const exploded_edge *> m_edges;
 };
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index e29b62b3c9d..5156ba1b146 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -4889,6 +4889,14 @@ region_model2::canonicalized_p () const
 
 /* FIXME.  */
 
+void
+region_model2::loop_replay_hack (const region_model2 *dst_state)
+{
+  m_store.loop_replay_hack (dst_state->get_store (), m_mgr);
+}
+
+/* FIXME.  */
+
 bool
 region_model2::on_stmt (const gimple *stmt,
 			region_model2_context *ctxt)
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index eebb7867744..7b1d610e683 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -2681,6 +2681,9 @@ class region_model2
     return m_constraints2;
   }
 
+  store2 *get_store () { return &m_store; }
+  const store2 *get_store () const { return &m_store; }
+
   region_model2_manager *get_manager () const { return m_mgr; }
 
 #if 0
@@ -2728,6 +2731,8 @@ class region_model2
   void canonicalize ();
   bool canonicalized_p () const;
 
+  void loop_replay_hack (const region_model2 *dst_state);
+
 private:
   const region2 *get_lvalue_1 (path_var pv,
 			       region_model2_context *ctxt);
diff --git a/gcc/analyzer/store2.cc b/gcc/analyzer/store2.cc
index d4b1c37217a..32fd04e4583 100644
--- a/gcc/analyzer/store2.cc
+++ b/gcc/analyzer/store2.cc
@@ -1784,6 +1784,59 @@ store2::canonicalize ()
     }
 }
 
+/* Subroutine for use by exploded_path::feasible2_p.
+
+   We need to deal with state differences between:
+   (a) when the exploded_graph is being initially constructed and
+   (b) when replaying the state changes along a specific path in
+   in exploded_path::feasible2_p.
+
+   In (a), state merging happens, so when exploring a loop
+     for (i = 0; i < 1024; i++)
+   on successive iterations we have i == 0, then i == WIDENING.
+
+   In (b), no state merging happens, so naively replaying the path
+   that goes twice through the loop then exits it
+   would lead to i == 0, then i == 1, and then a (i >= 1024) eedge
+   that exits the loop, which would be found to be infeasible as i == 1,
+   and the path would be rejected.
+
+   We need to fix up state during replay.  This subroutine is
+   called whenever we enter a supernode that we've already
+   visited along this exploded_path, passing in OTHER_STORE
+   from the destination enode's state.
+
+   Find bindings to widening values in OTHER_STORE.
+   For all that are found, update the binding in this store to UNKNOWN.  */
+
+void
+store2::loop_replay_hack (const store2 *other_store,
+			  region_model2_manager *mgr)
+{
+  gcc_assert (other_store);
+  for (cluster_map_t::iterator iter = other_store->m_cluster_map.begin ();
+       iter != other_store->m_cluster_map.end (); ++iter)
+    {
+      const region2 *base_reg = (*iter).first;
+      binding_cluster2 *cluster = (*iter).second;
+      for (binding_cluster2::iterator_t bind_iter = cluster->m_map.begin ();
+	   bind_iter != cluster->m_map.end (); ++bind_iter)
+	{
+	  const binding_key2 *key = (*bind_iter).first;
+	  const svalue2 *sval = (*bind_iter).second;
+	  if (sval->get_kind () == svalue2::SK_WIDENING)
+	    {
+	      binding_cluster2 *this_cluster
+		= get_or_create_cluster (base_reg);
+	      const svalue2 *unknown
+		= mgr->get_or_create_unknown_svalue2 (sval->get_type ());
+	      this_cluster->bind_key (key, unknown);
+	      // TODO
+	    }
+	}
+    }
+}
+
 #if CHECKING_P
 
 namespace selftest {
diff --git a/gcc/analyzer/store2.h b/gcc/analyzer/store2.h
index 7981dd665b2..2377973d1e9 100644
--- a/gcc/analyzer/store2.h
+++ b/gcc/analyzer/store2.h
@@ -531,6 +531,8 @@ public:
   }
 
   void canonicalize ();
+  void loop_replay_hack (const store2 *other_store,
+			 region_model2_manager *mgr);
 
 private:
   void remove_overlapping_bindings (store2_manager *mgr, const region2 *reg);
diff --git a/gcc/testsuite/gcc.dg/analyzer/feasibility-1.c b/gcc/testsuite/gcc.dg/analyzer/feasibility-1.c
index 60c4e92a5ef..ea60b5a1148 100644
--- a/gcc/testsuite/gcc.dg/analyzer/feasibility-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/feasibility-1.c
@@ -31,3 +31,13 @@ void test_4 (void)
   if (global_for_test_4)
     __analyzer_dump_path (); /* { dg-message "path" } */
 }
+
+/* Verify that loops don't confuse the feasibility checker.  */
+
+void test_5 (void)
+{
+  for (int i = 0; i < 1024; i++)
+    {
+    }
+  __analyzer_dump_path (); /* { dg-message "path" } */  
+}
-- 
2.26.2

