From 033f0b7e0228956f2690fe1965dddfbc7fe55005 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 25 Jun 2019 15:50:25 -0400
Subject: [PATCH 46/63] FIXME: implement __analyzer_dump_exploded_nodes and
 other ways to measure enode growth

---
 gcc/analyzer/impl-4.cc                 | 139 +++++++++++++++++++++++++++++++++
 gcc/builtins.def                       |   5 ++
 gcc/testsuite/gcc.dg/analyzer/zlib-1.c |  52 ++++++++++++
 3 files changed, 196 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/zlib-1.c

diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index 90e219f..b272f4a 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -1052,6 +1052,7 @@ public:
   void log_stats () const;
   void dump_stats (FILE *) const;
   void dump_states_for_supernode (FILE *, const supernode *snode) const;
+  void dump_exploded_nodes () const;
 
 private:
   const supergraph &m_sg;
@@ -6095,6 +6096,142 @@ function_comparator (const void *pn1, const void *pn2)
 
 /* FIXME.  */
 
+class enode_label : public range_label
+{
+ public:
+  enode_label (exploded_node *enode) : m_enode (enode) {}
+
+  label_text get_text (unsigned) const FINAL OVERRIDE
+  {
+    return label_text::take (print (false, "EN: %i", m_enode->m_index));
+  }
+
+private:
+  exploded_node *m_enode;
+};
+
+/* Handle __analyzer_dump_exploded_nodes.  */
+
+void
+exploded_graph::dump_exploded_nodes () const
+{
+  // TODO
+  /* Locate calls to __analyzer_dump_exploded_nodes.  */
+  // Print how many egs there are for them?
+  /* Better: log them as we go, and record the exploded nodes
+     in question.  */
+
+  /* Show every enode.  */
+
+  /* Gather them by stmt, so that we can more clearly see the
+     "hotspots" requiring numerous exploded nodes.  */
+
+  /* Alternatively, simply throw them all into one big rich_location
+     and see if the label-printing will sort it out...
+     This requires them all to be in the same source file.  */
+
+  if (0)
+    {
+      gcc_rich_location richloc (UNKNOWN_LOCATION);
+      unsigned i;
+      dnode *node;
+      FOR_EACH_VEC_ELT (m_nodes, i, node)
+	{
+	  exploded_node *enode = (exploded_node *)node;
+	  //const supernode *iter_snode = enode->get_supernode ();
+	  if (const gimple *stmt = enode->get_stmt ())
+	    {
+	      if (richloc.get_loc () == UNKNOWN_LOCATION)
+		richloc.set_range (0, stmt->location, SHOW_RANGE_WITH_CARET);
+	      else
+		richloc.add_range (stmt->location,
+				   SHOW_RANGE_WITHOUT_CARET,
+				   new enode_label (enode));
+	    }
+	}
+      warning_at (&richloc, 0, "%i exploded nodes", m_nodes.length ());
+
+      /* Repeat the warning without all the labels, so that message is visible
+	 (the other one may well have scrolled past the terminal limit).  */
+      warning_at (richloc.get_loc (), 0,
+		  "%i exploded nodes", m_nodes.length ());
+
+      if (m_worklist.length () > 0)
+	warning_at (richloc.get_loc (), 0,
+		    "worklist still contains %i nodes", m_worklist.length ());
+    }
+
+  /* Emit a warning at any call to "__analyzer_dump_exploded_nodes",
+     giving the number of exploded nodes for "before-stmt", and their
+     IDs.  */
+
+  unsigned i;
+  dnode *node;
+  hash_set<const gimple *> seen;
+  FOR_EACH_VEC_ELT (m_nodes, i, node)
+    {
+      exploded_node *enode = (exploded_node *)node;
+      if (enode->get_point ().m_kind != PK_BEFORE_STMT)
+	continue;
+
+      if (const gimple *stmt = enode->get_stmt ())
+	if (const gcall *call = dyn_cast <const gcall *> (stmt))
+	  if (is_named_call_p (call, "__analyzer_dump_exploded_nodes", 1))
+	    {
+	      if (seen.contains (stmt))
+		continue;
+
+	      /* This is O(N^2).  */
+	      pretty_printer pp;
+	      unsigned j;
+	      auto_vec<exploded_node *> enodes;
+	      dnode *other_node;
+	      FOR_EACH_VEC_ELT (m_nodes, j, other_node)
+		{
+		  exploded_node *other_enode = (exploded_node *)other_node;
+		  if (other_enode->get_point ().m_kind != PK_BEFORE_STMT)
+		    continue;
+		  if (other_enode->get_stmt () == stmt)
+		    {
+		      if (enodes.length () > 0)
+			pp_string (&pp, ", ");
+		      pp_printf (&pp, "EN: %i", other_enode->m_index);
+		      enodes.safe_push (other_enode);
+		    }
+		}
+
+	      warning_n (stmt->location, 0, enodes.length (),
+			 "%i exploded node: %s",
+			 "%i exploded nodes: %s",
+			 enodes.length (), pp_formatted_text (&pp));
+	      seen.add (stmt);
+
+	      /* If the argument is non-zero, then print all of the states
+		 of the various enodes.  */
+	      tree t_arg = fold (gimple_call_arg (call, 0));
+	      if (TREE_CODE (t_arg) != INTEGER_CST)
+		{
+		  error_at (call->location,
+			    "integer constant required for arg 1");
+		  return;
+		}
+	      int i_arg = TREE_INT_CST_LOW (t_arg);
+	      if (i_arg)
+		{
+		  exploded_node *other_enode;
+		  FOR_EACH_VEC_ELT (enodes, j, other_enode)
+		    {
+		      fprintf (stderr, "%i of %i: EN %i:\n",
+			       j + 1, enodes.length (), other_enode->m_index);
+		      other_enode->get_state ().dump (m_ext_state);
+		    }
+		}
+	    }
+    }
+}
+
+/* FIXME.  */
+
 void
 impl_run_checkers (logger *logger)
 {
@@ -6167,6 +6304,8 @@ impl_run_checkers (logger *logger)
   /* Now emit any saved diagnostics.  */
   eg.get_diagnostic_manager ().emit_saved_diagnostics (eg);
 
+  eg.dump_exploded_nodes ();
+
   eg.log_stats ();
 
   delete purge_map;
diff --git a/gcc/builtins.def b/gcc/builtins.def
index fef730d..74e0f34 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -1117,4 +1117,9 @@ DEF_BUILTIN (BUILT_IN_ANALYZER_EVAL, "__analyzer_eval", BUILT_IN_NORMAL, BT_FN_V
 DEF_BUILTIN (BUILT_IN_ANALYZER_BREAK, "__analyzer_break", BUILT_IN_NORMAL, BT_FN_VOID, BT_LAST,
 	     false, false, false, ATTR_NULL, true, true)
 
+DEF_BUILTIN (BUILT_IN_ANALYZER_DUMP_EXPLODED_NODES,
+	     "__analyzer_dump_exploded_nodes",
+	     BUILT_IN_NORMAL, BT_FN_VOID_INT, BT_LAST,
+	     false, false, false, ATTR_NULL, true, true)
+
 #undef DEF_BUILTIN
diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-1.c b/gcc/testsuite/gcc.dg/analyzer/zlib-1.c
new file mode 100644
index 0000000..b1f665b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/zlib-1.c
@@ -0,0 +1,52 @@
+typedef void (*free_func)(void *opaque, void *address);
+
+typedef struct z_stream_s {
+  struct internal_state *state;
+  free_func zfree;
+  void *opaque;
+} z_stream;
+
+struct internal_state {
+  z_stream *strm;
+  int status;
+  unsigned char *pending_buf;
+  unsigned char *window;
+  unsigned short *prev;
+  unsigned short *head;
+};
+
+int deflateEnd(z_stream *strm)
+{
+  int status;
+
+  if (strm == 0 || strm->state == 0)
+    return (-2);
+
+  status = strm->state->status;
+  if (status != 42 && status != 113 && status != 666) {
+    return (-2);
+  }
+
+  if (strm->state->pending_buf)
+    (*(strm->zfree))(strm->opaque, (void *)(strm->state->pending_buf));
+
+  if (strm->state->head)
+      (*(strm->zfree))(strm->opaque, (void *)(strm->state->head));
+
+  if (strm->state->prev)
+    (*(strm->zfree))(strm->opaque, (void *)(strm->state->prev));
+
+  if (strm->state->window)
+    (*(strm->zfree))(strm->opaque, (void *)(strm->state->window));
+
+  (*(strm->zfree))(strm->opaque, (void *)(strm->state));
+  strm->state = 0;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "16 exploded nodes" } */
+  /* TODO: consolidate them; we ought to be able to get away with just 1.  */
+
+  return status == 113 ? (-3) : 0;
+}
+
+/* { dg-regexp "\[^\n\r\]+: warning: analysis bailed out early \\(\[0-9\]+ nodes\\)" } */
+/* TODO: we ought go be able to fully analyze this function.  */
-- 
1.8.5.3

