From a6427bc4269c2c6d1c32635a35fe061b8c53ec40 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 14 Jan 2020 21:07:32 -0500
Subject: [PATCH 16/31] FIXME: implement checking for duplicate enode debug
 string

---
 gcc/analyzer/engine.cc        | 52 ++++++++++++++++++++++++++++++++++-
 gcc/analyzer/exploded-graph.h | 16 +++++++++--
 gcc/configure.ac              | 16 +++++++----
 gcc/doc/install.texi          |  4 +--
 4 files changed, 78 insertions(+), 10 deletions(-)

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index de6bf1d394f..79c5ba0155a 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -705,6 +705,23 @@ impl_region_model_context::on_unexpected_tree_code (tree t,
 
 /* struct point_and_state.  */
 
+/* Make a string describing this point_and_state,
+   to be "free"-d by the caller.  */
+
+char *
+point_and_state::make_debug_str (const extrinsic_state &ext_state) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+
+  format f (false);
+  m_point.print (&pp, f);
+  pp_string (&pp, ": ");
+  m_state.dump_to_pp (ext_state, true, &pp);
+
+  return xstrdup (pp_formatted_text (&pp));
+}
+
 /* Assert that this object is sane.  */
 
 void
@@ -1983,10 +2000,43 @@ exploded_graph::get_or_create_node (const program_point &point,
   ps.validate (m_ext_state);
 
   /* An exploded_node for "ps" doesn't already exist; create one.  */
-  exploded_node *node = new exploded_node (ps, m_nodes.length ());
+  exploded_node *node = new exploded_node (ps, m_nodes.length (), m_ext_state);
   add_node (node);
   m_point_and_state_to_node.put (node->get_ps_key (), node);
 
+#if defined ENABLE_ANALYZER_CHECKING
+  {
+    /* Check that we don't have duplicate of the m_debug_str.  */
+    unsigned i;
+    exploded_node *existing;
+    FOR_EACH_VEC_ELT (per_point_data->m_enodes, i, existing)
+      {
+	gcc_assert (node->get_debug_str ());
+	if (!strcmp (node->get_debug_str (),
+		     existing->get_debug_str ()))
+	  {
+	    /* We have two non-equal nodes for which the debug string is equal.
+	       This is likely to indicate that something has gone wrong during
+	       state purging or canonicalization, and will lead to the
+	       exploded_graph "exploding", with multiple redundant copies of
+	       paths.
+	       Issue an internal error for this case.  */
+	    pretty_printer pp;
+	    pp_format_decoder (&pp) = default_tree_printer;
+	    pp_show_color (&pp) = pp_show_color (global_dc->printer);
+	    pp_printf (&pp, "debug string: %qs\n", node->get_debug_str ());
+	    node->dump_to_pp (&pp, m_ext_state);
+	    existing->dump_to_pp (&pp, m_ext_state);
+	    internal_error ("failed analyzer check:"
+			    " new EN %i is non-equal to existing EN %i"
+			    " but they have the same debug string\n%s",
+			    node->m_index, existing->m_index,
+			    pp_formatted_text (&pp));
+	  }
+      }
+  }
+#endif
+
   /* Update per-program_point data.  */
   per_point_data->m_enodes.safe_push (node);
 
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index a851dd929c9..c5d8328c71b 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -126,6 +126,8 @@ public:
     m_hash = m_point.hash () ^ m_state.hash ();
   }
 
+  char *make_debug_str (const extrinsic_state &ext_state) const;
+
   void validate (const extrinsic_state &ext_state) const;
 
 private:
@@ -175,11 +177,16 @@ class exploded_node : public dnode<eg_traits>
   };
 
   exploded_node (point_and_state ps,
-		 int index)
-  : m_ps (ps), m_status (STATUS_WORKLIST), m_index (index)
+		 int index,
+		 const extrinsic_state &ext_state ATTRIBUTE_UNUSED)
+  : m_ps (ps), m_debug_str (NULL), m_status (STATUS_WORKLIST), m_index (index)
   {
     gcc_checking_assert (ps.get_state ().m_region_model->canonicalized_p ());
+#if defined ENABLE_ANALYZER_CHECKING
+    m_debug_str = m_ps.make_debug_str (ext_state);
+#endif
   }
+  ~exploded_node () { free (m_debug_str); }
 
   hashval_t hash () const { return m_ps.hash (); }
 
@@ -263,6 +270,8 @@ class exploded_node : public dnode<eg_traits>
 
   void dump_succs_and_preds (FILE *outf) const;
 
+  const char *get_debug_str () const { return m_debug_str; }
+
   enum status get_status () const { return m_status; }
   void set_status (enum status status)
   {
@@ -279,6 +288,9 @@ private:
      is immutable once the exploded_node has been created.  */
   const point_and_state m_ps;
 
+  /* FIXME.  */
+  char *m_debug_str;
+
   enum status m_status;
 
 public:
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 0e6e475950d..97e76dd44a8 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -527,7 +527,7 @@ AC_ARG_ENABLE(checking,
 		 enable only specific categories of checks.
 		 Categories are: yes,no,all,none,release.
 		 Flags are: assert,df,extra,fold,gc,gcac,gimple,misc,
-		 rtlflag,rtl,runtime,tree,valgrind,types])],
+		 rtlflag,rtl,runtime,tree,valgrind,types,analyzer])],
 [ac_checking_flags="${enableval}"],[
 # Determine the default checks.
 if test x$is_release = x ; then
@@ -545,26 +545,27 @@ do
 			ac_gc_always_collect= ; ac_gimple_checking=1 ; ac_rtl_checking= ;
 			ac_rtlflag_checking=1 ; ac_runtime_checking=1 ;
 			ac_tree_checking=1 ; ac_valgrind_checking= ;
-			ac_types_checking=1 ;;
+			ac_types_checking=1 ; ac_analyzer_checking=1 ; ;;
 	no|none)	ac_assert_checking= ; ac_checking= ; ac_df_checking= ;
 			ac_fold_checking= ; ac_gc_checking= ; ac_extra_checking= ;
 			ac_gc_always_collect= ; ac_gimple_checking= ; ac_rtl_checking= ;
 			ac_rtlflag_checking= ; ac_runtime_checking= ;
 			ac_tree_checking= ; ac_valgrind_checking= ;
-			ac_types_checking= ;;
+			ac_types_checking= ; ac_analyzer_checking= ; ;;
 	all)		ac_assert_checking=1 ; ac_checking=1 ; ac_df_checking=1 ;
 			ac_fold_checking=1 ; ac_gc_checking=1 ; ac_extra_checking=1 ;
 			ac_gc_always_collect=1 ; ac_gimple_checking=1 ; ac_rtl_checking=1 ;
 			ac_rtlflag_checking=1 ; ac_runtime_checking=1 ;
 			ac_tree_checking=1 ; ac_valgrind_checking= ;
-			ac_types_checking=1 ;;
+			ac_types_checking=1 ; ac_analyzer_checking=1 ; ;;
 	release)	ac_assert_checking=1 ; ac_checking= ; ac_df_checking= ;
 			ac_fold_checking= ; ac_gc_checking= ; ac_extra_checking= ;
 			ac_gc_always_collect= ; ac_gimple_checking= ; ac_rtl_checking= ;
 			ac_rtlflag_checking= ; ac_runtime_checking=1 ;
 			ac_tree_checking= ; ac_valgrind_checking= ;
-			ac_types_checking= ;;
+			ac_types_checking= ; ac_analyzer_checking= ; ;;
 	# these enable particular checks
+	analyzer) 	ac_analyzer_checking=1 ;;
 	assert) 	ac_assert_checking=1 ;;
 	df)		ac_df_checking=1 ;;
 	extra)		ac_extra_checking=1 ;;
@@ -609,6 +610,11 @@ if test x$ac_assert_checking != x ; then
   AC_DEFINE(ENABLE_ASSERT_CHECKING, 1,
 [Define if you want assertions enabled.  This is a cheap check.])
 fi
+if test x$ac_analyzer_checking != x ; then
+  AC_DEFINE(ENABLE_ANALYZER_CHECKING, 1,
+[Define if you want the analyzer to check for duplicate point/states
+pairs.  This is moderately expensive, but only when running the analyzer.])
+fi
 if test x$ac_gimple_checking != x ; then
   AC_DEFINE(ENABLE_GIMPLE_CHECKING, 1,
 [Define if you want operations on GIMPLE (the basic data structure of
diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi
index cdea733bba3..3e140f7a99f 100644
--- a/gcc/doc/install.texi
+++ b/gcc/doc/install.texi
@@ -1849,10 +1849,10 @@ from the source repository or snapshots, but @samp{release} for releases.  The d
 for building the stage1 compiler is @samp{yes}.  More control
 over the checks may be had by specifying @var{list}.  The categories of
 checks available are @samp{yes} (most common checks
-@samp{assert,misc,tree,gc,rtlflag,runtime}), @samp{no} (no checks at
+@samp{analyzer,assert,misc,tree,gc,rtlflag,runtime}), @samp{no} (no checks at
 all), @samp{all} (all but @samp{valgrind}), @samp{release} (cheapest
 checks @samp{assert,runtime}) or @samp{none} (same as @samp{no}).
-Individual checks can be enabled with these flags @samp{assert},
+Individual checks can be enabled with these flags @samp{analyzer}, @samp{assert},
 @samp{df}, @samp{fold}, @samp{gc}, @samp{gcac}, @samp{misc}, @samp{rtl},
 @samp{rtlflag}, @samp{runtime}, @samp{tree}, @samp{extra} and @samp{valgrind}.
 @samp{extra} adds for @samp{misc} checking extra checks that might affect
-- 
2.21.0

