From d3af03ff4a0866ff45eaffbf42535ba615fbffce Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Sat, 19 Oct 2019 17:25:14 -0400
Subject: [PATCH 103/113] FIXME: WIP on expression_graph experiment

---
 gcc/Makefile.in                               |   1 +
 gcc/analyzer/engine.cc                        |  28 ++
 gcc/analyzer/expression-graph.cc              | 204 ++++++++++++++
 gcc/analyzer/expression-graph.h               | 259 ++++++++++++++++++
 gcc/analyzer/region-model.cc                  |  54 ++--
 gcc/analyzer/region-model.h                   |  16 +-
 gcc/analyzer/sm-malloc.cc                     |   2 +
 gcc/digraph.h                                 |   2 +
 gcc/shortest-paths.h                          |   5 +
 .../NOT_FOR_UPSTREAM-zlib-1.1.3-gzio.c        |   6 +-
 gcc/testsuite/gcc.dg/analyzer/data-model-10.c |  28 +-
 11 files changed, 573 insertions(+), 32 deletions(-)
 create mode 100644 gcc/analyzer/expression-graph.cc
 create mode 100644 gcc/analyzer/expression-graph.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 1b1fac2e05a..a93b0486df2 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1227,6 +1227,7 @@ ANALYZER_OBJS = \
 	analyzer/constraint-manager.o \
 	analyzer/diagnostic-manager.o \
 	analyzer/engine.o \
+	analyzer/expression-graph.o \
 	analyzer/function-set.o \
 	analyzer/pending-diagnostic.o \
 	analyzer/program-point.o \
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index d8e31847d98..7d67178f455 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -58,6 +58,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "analyzer/checker-path.h"
 #include "analyzer/state-purge.h"
+#include "analyzer/expression-graph.h"
 
 /* For an overview, see gcc/doc/analyzer.texi.  */
 
@@ -305,6 +306,33 @@ public:
 
     gcc_assert (m_new_state);
     svalue_id sid = m_new_state->m_region_model->get_rvalue (expr, NULL);
+
+#if 1
+    expression_graph expr_graph (*m_new_state->m_region_model);
+
+#if 0
+    // TODO: print to dot, for testing
+    expr_graph.dump_dot ("/tmp/test.dot", NULL,
+			 expression_graph_traits::dump_args_t (*m_new_state->m_region_model));
+#endif
+    tree expr_for_sid = expr_graph.get_tree_for_sid (sid);
+    if (expr_for_sid)
+      return expr_for_sid;
+    else
+      return expr;
+#endif
+    //#error use expression_graph here?
+    /* TODO: ideally defer it; it's expensive; instead pass in a cloneable
+       object that wraps a region_model *.
+       Or cache the expr_graph within this context?
+      Save that optimization until the idea is proven.  */
+    //TODO: and move the shortest_paths into the expr_graph, so it can be cached too
+    /* TODO: selftests for expr_graph
+       - check that expr_path works
+       - check that it handles failure gracefully
+       - check that shortest_paths handles failure gracefully
+     */
+
     /* Find trees for all regions storing the value.  */
     auto_vec<path_var> pvs;
     m_new_state->m_region_model->get_path_vars_for_svalue (sid, &pvs);
diff --git a/gcc/analyzer/expression-graph.cc b/gcc/analyzer/expression-graph.cc
new file mode 100644
index 00000000000..fa020373d47
--- /dev/null
+++ b/gcc/analyzer/expression-graph.cc
@@ -0,0 +1,204 @@
+/* FIXME.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "gcc-plugin.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "pretty-print.h"
+#include "analyzer/expression-graph.h"
+
+/* class expression_graph : public digraph<expression_graph_traits>.  */
+
+/* FIXME.  */
+
+expression_graph::expression_graph (const region_model &model)
+: m_model (model)
+{
+  /* Create and add nodes.  */
+
+  m_origin = new expression_node_origin ();
+  add_node (m_origin);
+
+  for (unsigned i = 0; i < model.get_num_svalues (); i++)
+    {
+      expression_node_svalue *node
+	= new expression_node_svalue (get_num_nodes (),
+				      svalue_id::from_int (i));
+      add_node (node);
+      m_svalue_nodes.safe_push (node);
+    }
+
+  for (unsigned i = 0; i < model.get_num_regions (); i++)
+    {
+      expression_node_region *node
+	= new expression_node_region (get_num_nodes (),
+				      region_id::from_int (i));
+      add_node (node);
+      m_region_nodes.safe_push (node);
+    }
+
+  /* Add edges.  */
+
+  for (unsigned i = 0; i < model.get_num_regions (); i++)
+    {
+      region *region = model.get_region (region_id::from_int (i));
+
+      /* Add edges from regions to their values.  */
+      svalue_id sid = region->get_value_direct ();
+      if (!sid.null_p ())
+	{
+	  expression_edge *edge
+	    = new expression_edge_value_of (m_region_nodes[i],
+					    m_svalue_nodes[sid.as_int ()]);
+	  add_edge (edge);
+	}
+
+      /* Add edges from map_regions to child regions.  */
+      if (map_region *map = dyn_cast<map_region *> (region))
+	add_expression_edges_for_map (m_region_nodes[i], map);
+    }
+
+  for (unsigned i = 0; i < model.get_num_svalues (); i++)
+    {
+      svalue *sval = model.get_svalue (svalue_id::from_int (i));
+
+      /* Add edges for pointers from svals to their regions.  */
+      if (region_svalue *ptr = sval->dyn_cast_region_svalue ())
+	{
+	  region_id rid = ptr->get_pointee ();
+	  expression_edge *edge
+	    = new expression_edge_deref (m_svalue_nodes[i],
+					 m_region_nodes[rid.as_int ()],
+					 TREE_TYPE (ptr->get_type ()));
+	  add_edge (edge);
+	}
+    }
+
+  // TODO: add edges from origin to globals and to locals in top-most frame.  */
+  // FIXME: this is slightly different to the above, in that we want to add the
+  // edges from the origin, rather than from the map itself
+  region_id globals_rid = model.get_globals_region_id ();
+  if (!globals_rid.null_p ())
+    {
+      map_region *globals = model.get_region<map_region> (globals_rid);
+      add_expression_edges_for_map (m_origin, globals);
+    }
+
+  region_id frame_rid = model.get_current_frame_id ();
+  if (!frame_rid.null_p ())
+    {
+      frame_region *frame = model.get_region<frame_region> (frame_rid);
+      add_expression_edges_for_map (m_origin, frame);
+    }
+
+  m_shortest_paths
+    = new shortest_paths <expression_graph_traits, expression_path>
+        (*this, m_origin);
+}
+
+/* FIXME.  */
+
+expression_graph::~expression_graph ()
+{
+  delete m_shortest_paths;
+}
+
+/* FIXME.  */
+
+tree
+expression_graph::get_tree_for_sid (svalue_id sid) const
+{
+  expression_node *to = get_node_for_sid (sid);
+  expression_path expr_path = m_shortest_paths->get_shortest_path (to);
+  if (expr_path.m_edges.length () > 0)
+    return expr_path.make_tree ();
+  else
+    return NULL_TREE;
+}
+
+/* FIXME.  */
+
+void
+expression_graph::add_expression_edges_for_map (expression_node *from,
+						map_region *map)
+{
+  for (map_region::iterator_t iter = map->begin ();
+       iter != map->end ();
+       ++iter)
+    {
+      tree expr = (*iter).first;
+
+      /* Don't add edges for those keys for which the expr
+	 isn't meaningful to the user.  */
+      if (TREE_CODE (expr) == SSA_NAME)
+	if (SSA_NAME_VAR (expr) == NULL_TREE)
+	  continue;
+
+      region_id child_rid = (*iter).second;
+
+      expression_edge *edge
+	= new expression_edge_by_key (from,
+				      m_region_nodes[child_rid.as_int ()],
+				      expr);
+      add_edge (edge);
+    }
+}
+
+tree
+expression_edge_value_of::extend_expr (tree expr) const
+{
+  return expr;
+}
+
+tree
+expression_edge_deref::extend_expr (tree expr) const
+{
+  return build1 (INDIRECT_REF, m_type, expr);
+}
+
+tree
+expression_edge_by_key::extend_expr (tree expr) const
+{
+  return build3 (COMPONENT_REF, TREE_TYPE (m_key),
+		 expr, m_key, NULL_TREE);
+}
+
+/* class expression_path.  */
+
+tree
+expression_path::make_tree () const
+{
+  gcc_assert (m_edges.length () > 0);
+  const expression_edge_by_key *first_edge
+    = static_cast <const expression_edge_by_key *> (m_edges[0]);
+  tree expr = first_edge->m_key;
+  gcc_assert (DECL_P (expr) || TREE_CODE (expr) == SSA_NAME);
+
+  for (unsigned i = 1; i < m_edges.length (); i++)
+    {
+      const expression_edge *edge = m_edges[i];
+      expr = edge->extend_expr (expr);
+    }
+
+  return expr;
+}
+
diff --git a/gcc/analyzer/expression-graph.h b/gcc/analyzer/expression-graph.h
new file mode 100644
index 00000000000..c3c0f544486
--- /dev/null
+++ b/gcc/analyzer/expression-graph.h
@@ -0,0 +1,259 @@
+/* FIXME.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_ANALYZER_EXPRESSION_GRAPH_H
+#define GCC_ANALYZER_EXPRESSION_GRAPH_H
+
+#include "analyzer/region-model.h"
+#include "analyzer/shortest-paths.h"
+
+class expression_graph;
+class expression_node;
+  class expression_node_origin;
+  class expression_node_region;
+  class expression_node_svalue;
+class expression_edge;
+  class expression_edge_value_of;
+  class expression_edge_deref;
+  class expression_edge_by_key;
+class expression_cluster;
+class expression_path;
+
+struct expression_graph_traits
+{
+  typedef expression_node node_t;
+  typedef expression_edge edge_t;
+  typedef expression_graph graph_t;
+  typedef expression_cluster cluster_t;
+  struct dump_args_t
+  {
+    dump_args_t (const region_model &model) : m_model (model) {}
+    const region_model &m_model;
+  };
+};
+
+class expression_graph : public digraph<expression_graph_traits>
+{
+public:
+  expression_graph (const region_model &model);
+  ~expression_graph ();
+
+  tree get_tree_for_sid (svalue_id sid) const;
+
+  expression_node *get_origin () const { return m_origin; }
+
+  expression_node_svalue *get_node_for_sid (svalue_id sid) const
+  {
+    gcc_assert (!sid.null_p ());
+    return m_svalue_nodes[sid.as_int ()];
+  }
+
+  expression_node_region *get_node_for_rid (region_id rid) const
+  {
+    gcc_assert (!rid.null_p ());
+    return m_region_nodes[rid.as_int ()];
+  }
+
+private:
+  void add_expression_edges_for_map (expression_node *from,
+				     map_region *map);
+
+  const region_model &m_model;
+  expression_node *m_origin;
+  auto_vec<expression_node_svalue *> m_svalue_nodes;
+  auto_vec<expression_node_region *> m_region_nodes;
+  shortest_paths <expression_graph_traits, expression_path> *m_shortest_paths;
+};
+
+class expression_node : public dnode<expression_graph_traits>
+{
+public:
+#if 1
+  virtual void dump_name (pretty_printer *pp) const = 0;
+#endif
+
+#if 0
+  void dump_dot (graphviz_out *gv, const dump_args_t &)
+    const FINAL OVERRIDE
+  {
+    gv->write_indent ();
+    pretty_printer *pp = gv->get_pp ();
+    dump_name (pp);
+    pp_character (pp, ';');
+    pp_newline (pp);
+  }
+#endif
+
+protected:
+  expression_node (int index)
+  : m_index (index) {}
+
+public:
+  int m_index;
+};
+
+class expression_node_origin : public expression_node
+{
+public:
+  expression_node_origin ()
+  : expression_node (0) {}
+
+  void dump_dot (graphviz_out *gv, const dump_args_t &)
+    const FINAL OVERRIDE
+  {
+    gv->println ("origin;");
+  }
+
+  void dump_name (pretty_printer *pp) const FINAL OVERRIDE
+  {
+    pp_string (pp, "origin");
+  }
+};
+
+class expression_node_region : public expression_node
+{
+public:
+  expression_node_region (int index, region_id rid)
+  : expression_node (index), m_rid (rid) {}
+
+  void dump_dot (graphviz_out *gv, const dump_args_t &args)
+    const FINAL OVERRIDE
+  {
+    region *region = args.m_model.get_region (m_rid);
+    region->dump_dot_to_pp (args.m_model, m_rid, gv->get_pp (), false);
+  }
+
+  void dump_name (pretty_printer *pp) const
+  {
+    m_rid.dump_node_name_to_pp (pp);
+  }
+
+  region_id m_rid;
+};
+
+class expression_node_svalue  : public expression_node
+{
+public:
+  expression_node_svalue (int index, svalue_id sid)
+  : expression_node (index), m_sid (sid) {}
+
+  void dump_dot (graphviz_out *gv, const dump_args_t &args)
+    const FINAL OVERRIDE
+  {
+    svalue *sval = args.m_model.get_svalue (m_sid);
+    sval->dump_dot_to_pp (args.m_model, m_sid, gv->get_pp (), false);
+  }
+
+  void dump_name (pretty_printer *pp) const
+  {
+    m_sid.dump_node_name_to_pp (pp);
+  }
+
+  svalue_id m_sid;
+};
+
+class expression_edge : public dedge<expression_graph_traits>
+{
+public:
+  expression_edge (expression_node *src, expression_node *dest)
+  : dedge (src, dest)  {}
+
+  void dump_dot (graphviz_out *gv, const dump_args_t &) const
+  {
+    gv->write_indent ();
+    pretty_printer *pp = gv->get_pp ();
+    m_src->dump_name (pp);
+    pp_string (pp, " -> ");
+    m_dest->dump_name (pp);
+    pp_printf (pp, " [headlabel=\""),
+    dump_label_to_pp (pp);
+    pp_printf (pp, "\"];\n");
+  }
+
+  virtual void dump_label_to_pp (pretty_printer *pp) const = 0;
+
+  virtual tree extend_expr (tree) const = 0;
+
+};
+
+class expression_edge_value_of : public expression_edge
+{
+public:
+  expression_edge_value_of (expression_node *src, expression_node *dest)
+  : expression_edge (src, dest)  {}
+
+  void dump_label_to_pp (pretty_printer *pp) const
+  {
+    pp_string (pp, "value-of");
+  }
+
+  tree extend_expr (tree expr) const FINAL OVERRIDE;
+};
+
+class expression_edge_deref : public expression_edge
+{
+public:
+  expression_edge_deref (expression_node *src, expression_node *dest,
+			 tree type)
+  : expression_edge (src, dest), m_type (type) {}
+
+  void dump_label_to_pp (pretty_printer *pp) const
+  {
+    pp_string (pp, "deref");
+  }
+
+  tree extend_expr (tree expr) const FINAL OVERRIDE;
+
+ private:
+  tree m_type;
+};
+
+class expression_edge_by_key : public expression_edge
+{
+public:
+  expression_edge_by_key (expression_node *src, expression_node *dest, tree key)
+  : expression_edge (src, dest), m_key (key) {}
+
+  void dump_label_to_pp (pretty_printer *pp) const
+  {
+PUSH_IGNORE_WFORMAT
+    pp_printf (pp, "%qE", m_key);
+POP_IGNORE_WFORMAT
+  }
+
+  tree extend_expr (tree expr) const FINAL OVERRIDE;
+
+  tree m_key;
+};
+
+
+struct expression_cluster : public cluster<expression_graph_traits>
+{
+};
+
+class expression_path
+{
+public:
+  tree make_tree () const;
+
+  auto_vec<const expression_edge *> m_edges;
+};
+
+#endif /* GCC_ANALYZER_EXPRESSION_GRAPH_H */
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 4c296d37604..335fd26247a 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -59,6 +59,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-event-id.h"
 #include "analyzer/sm.h"
 #include "analyzer/pending-diagnostic.h"
+#include "analyzer/shortest-paths.h"
 #include "analyzer/analyzer-selftests.h"
 
 #if ENABLE_ANALYZER
@@ -336,7 +337,8 @@ POP_IGNORE_WFORMAT
 void
 svalue::dump_dot_to_pp (const region_model &model,
 			svalue_id this_sid,
-			pretty_printer *pp) const
+			pretty_printer *pp,
+			bool /* add_edges */) const
 {
   this_sid.dump_node_name_to_pp (pp);
   pp_printf (pp, " [label=\"");
@@ -437,12 +439,13 @@ region_svalue::print_details (const region_model &model ATTRIBUTE_UNUSED,
 void
 region_svalue::dump_dot_to_pp (const region_model &model,
 			       svalue_id this_sid,
-			       pretty_printer *pp) const
+			       pretty_printer *pp,
+			       bool add_edges) const
 {
-  svalue::dump_dot_to_pp (model, this_sid, pp);
+  svalue::dump_dot_to_pp (model, this_sid, pp, add_edges);
 
   /* If non-NULL, add an edge to the pointed-to region.  */
-  if (!m_rid.null_p ())
+  if (add_edges && !m_rid.null_p ())
     {
       this_sid.dump_node_name_to_pp (pp);
       pp_string (pp, " -> ");
@@ -1230,7 +1233,8 @@ region::print (const region_model &model,
 void
 region::dump_dot_to_pp (const region_model &model,
 			region_id this_rid,
-			pretty_printer *pp) const
+			pretty_printer *pp,
+			bool add_edges) const
 {
   this_rid.dump_node_name_to_pp (pp);
   pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
@@ -1242,7 +1246,7 @@ region::dump_dot_to_pp (const region_model &model,
   pp_newline (pp);
 
   /* Add edge to svalue.  */
-  if (!m_sval_id.null_p ())
+  if (add_edges && !m_sval_id.null_p ())
     {
       this_rid.dump_node_name_to_pp (pp);
       pp_string (pp, " -> ");
@@ -1252,7 +1256,7 @@ region::dump_dot_to_pp (const region_model &model,
     }
 
   /* Add edge to parent.  */
-  if (!m_parent_rid.null_p ())
+  if (add_edges && !m_parent_rid.null_p ())
     {
       this_rid.dump_node_name_to_pp (pp);
       pp_string (pp, " -> ");
@@ -1599,9 +1603,12 @@ POP_IGNORE_WFORMAT
 void
 map_region::dump_dot_to_pp (const region_model &model,
 			    region_id this_rid,
-			    pretty_printer *pp) const
+			    pretty_printer *pp,
+			    bool add_edges) const
 {
-  region::dump_dot_to_pp (model, this_rid, pp);
+  region::dump_dot_to_pp (model, this_rid, pp, add_edges);
+  if (!add_edges)
+    return;
   for (map_t::iterator iter = m_map.begin ();
        iter != m_map.end ();
        ++iter)
@@ -2278,9 +2285,10 @@ POP_IGNORE_WFORMAT
 void
 array_region::dump_dot_to_pp (const region_model &model,
 			      region_id this_rid,
-			      pretty_printer *pp) const
+			      pretty_printer *pp,
+			      bool add_edges) const
 {
-  region::dump_dot_to_pp (model, this_rid, pp);
+  region::dump_dot_to_pp (model, this_rid, pp, add_edges);
   for (map_t::iterator iter = m_map.begin ();
        iter != m_map.end ();
        ++iter)
@@ -2299,11 +2307,14 @@ POP_IGNORE_WFORMAT
       pp_string (pp, "\"];");
       pp_newline (pp);
 
-      pp_printf (pp, "rid_label_%i", child_rid.as_int ());
-      pp_string (pp, " -> ");
-      child_rid.dump_node_name_to_pp (pp);
-      pp_string (pp, ";");
-      pp_newline (pp);
+      if (add_edges)
+	{
+	  pp_printf (pp, "rid_label_%i", child_rid.as_int ());
+	  pp_string (pp, " -> ");
+	  child_rid.dump_node_name_to_pp (pp);
+	  pp_string (pp, ";");
+	  pp_newline (pp);
+	}
     }
 }
 
@@ -3415,11 +3426,11 @@ region_model::dump_dot_to_pp (pretty_printer *pp) const
 
   svalue *svalue;
   FOR_EACH_VEC_ELT (m_svalues, i, svalue)
-    svalue->dump_dot_to_pp (*this, svalue_id::from_int (i), pp);
+    svalue->dump_dot_to_pp (*this, svalue_id::from_int (i), pp, true);
 
   region *region;
   FOR_EACH_VEC_ELT (m_regions, i, region)
-    region->dump_dot_to_pp (*this, region_id::from_int (i), pp);
+    region->dump_dot_to_pp (*this, region_id::from_int (i), pp, true);
 
   /* TODO: constraints.  */
 
@@ -4654,6 +4665,13 @@ region_model::get_lvalue_1 (path_var pv, region_model_context *ctxt)
       }
       break;
 
+    case INDIRECT_REF:
+      {
+	tree ptr = TREE_OPERAND (expr, 0);
+	return deref_rvalue (ptr, ctxt);
+      }
+      break;
+
     case MEM_REF:
       {
 	tree ptr = TREE_OPERAND (expr, 0);
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 8892d3b791a..56dacd03679 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -464,7 +464,8 @@ public:
 
   virtual void dump_dot_to_pp (const region_model &model,
 			       svalue_id this_sid,
-			       pretty_printer *pp) const;
+			       pretty_printer *pp,
+			       bool add_edges) const;
 
   virtual region_svalue *dyn_cast_region_svalue () { return NULL; }
   virtual constant_svalue *dyn_cast_constant_svalue () { return NULL; }
@@ -520,7 +521,8 @@ public:
 
   void dump_dot_to_pp (const region_model &model,
 		       svalue_id this_sid,
-		       pretty_printer *pp) const
+		       pretty_printer *pp,
+		       bool add_edges) const
     FINAL OVERRIDE;
 
   region_svalue *dyn_cast_region_svalue () FINAL OVERRIDE { return this; }
@@ -839,7 +841,8 @@ public:
 
   virtual void dump_dot_to_pp (const region_model &model,
 			       region_id this_rid,
-			       pretty_printer *pp) const;
+			       pretty_printer *pp,
+			       bool add_edges) const;
 
   void dump_to_pp (const region_model &model,
 		   region_id this_rid,
@@ -932,7 +935,8 @@ public:
 
   void dump_dot_to_pp (const region_model &model,
 		       region_id this_rid,
-		       pretty_printer *pp) const
+		       pretty_printer *pp,
+		       bool add_edges) const
     FINAL OVERRIDE;
 
   void dump_child_label (const region_model &model,
@@ -1279,7 +1283,8 @@ public:
 
   void dump_dot_to_pp (const region_model &model,
 		       region_id this_rid,
-		       pretty_printer *pp) const
+		       pretty_printer *pp,
+		       bool add_edges) const
     FINAL OVERRIDE;
 
   void dump_child_label (const region_model &model,
@@ -1794,6 +1799,7 @@ class region_model
   void dump_summary_of_map (pretty_printer *pp, map_region *map_region,
 			    bool *is_first) const;
 
+ private:
   auto_delete_vec<svalue> m_svalues;
   auto_delete_vec<region> m_regions;
   region_id m_root_rid;
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index 63816797dc8..59fb2d15f68 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -687,6 +687,8 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 		  if (bitmap_empty_p (nonnull_args)
 		      || bitmap_bit_p (nonnull_args, i))
 		    {
+		      arg = sm_ctxt->get_readable_tree (arg);
+
 		      sm_ctxt->warn_for_state
 			(node, stmt, arg, m_unchecked,
 			 new possible_null_arg (*this, arg, callee_fndecl, i));
diff --git a/gcc/digraph.h b/gcc/digraph.h
index 7589d85d3b4..a3e8b34f0b2 100644
--- a/gcc/digraph.h
+++ b/gcc/digraph.h
@@ -101,6 +101,8 @@ class digraph
   void add_node (node_t *node);
   void add_edge (edge_t *edge);
 
+  unsigned get_num_nodes () const { return m_nodes.length (); }
+
   auto_delete_vec<node_t> m_nodes;
   auto_delete_vec<edge_t> m_edges;
 };
diff --git a/gcc/shortest-paths.h b/gcc/shortest-paths.h
index 00c4ad46be4..5060e7140ae 100644
--- a/gcc/shortest-paths.h
+++ b/gcc/shortest-paths.h
@@ -96,8 +96,13 @@ shortest_paths<GraphTraits, Path_t>::shortest_paths (const graph_t &graph,
 	      idx_in_queue_with_min_dist = i;
 	    }
 	}
+#if 1
+      if (idx_with_min_dist == -1)
+	break;
+#else
       gcc_assert (idx_with_min_dist != -1);
       gcc_assert (idx_in_queue_with_min_dist != -1);
+#endif
 
       // FIXME: this is confusing: there are two indices here
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-zlib-1.1.3-gzio.c b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-zlib-1.1.3-gzio.c
index d0dcfcfaacc..48f9e9b8a68 100644
--- a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-zlib-1.1.3-gzio.c
+++ b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-zlib-1.1.3-gzio.c
@@ -2697,8 +2697,7 @@ int whence;
 
     if (s->inbuf == 0) {
       s->inbuf = (Byte *)malloc(16384);
-      memset(s->inbuf, 0, 16384); /* { dg-warning "use of possibly-NULL '<unknown>' where non-null expected" } */
-      /* FIXME: we should print something more precise than "<unknown>" here.  */
+      memset(s->inbuf, 0, 16384); /* { dg-warning "use of possibly-NULL 'file->inbuf' where non-null expected" } */
     }
     while (offset > 0) {
       uInt size = 16384;
@@ -2852,8 +2851,7 @@ int *errnum;
       free(s->msg);
   };
   s->msg = (char *)malloc(strlen(s->path) + strlen(m) + 3);
-  strcpy(s->msg, s->path); /* { dg-warning "use of possibly-NULL '<unknown>' where non-null expected" } */
-  /* FIXME: we should print something more precise than "<unknown>" here.  */
+  strcpy(s->msg, s->path); /* { dg-warning "use of possibly-NULL 'file->msg' where non-null expected" } */
   strcat(s->msg, ": ");
   strcat(s->msg, m);
   return (const char *)s->msg;
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-10.c b/gcc/testsuite/gcc.dg/analyzer/data-model-10.c
index c261edcc827..8c91b7c81ef 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-10.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-10.c
@@ -1,4 +1,5 @@
 #include <stdlib.h>
+#include <string.h>
 
 struct foo
 {
@@ -8,10 +9,27 @@ struct foo
 struct foo *
 test (void)
 {
-  struct foo *new_table = (struct foo *) malloc(sizeof(struct foo));
-  if (!new_table)
+  struct foo *new_foo = (struct foo *) malloc(sizeof(struct foo));
+  if (!new_foo)
     return NULL;
-  new_table->m_f = (char **)malloc(sizeof(char **));
-  *new_table->m_f = NULL; /* { dg-warning "dereference of possibly-NULL '<unknown>'" } */ // FIXME: something better than "unknown" here
-  return new_table;
+  new_foo->m_f = (char **)malloc(sizeof(char **));
+  *new_foo->m_f = NULL; /* { dg-warning "dereference of possibly-NULL '\\(new_foo\\)->m_f'" } */
+  return new_foo;
 }
+
+struct bar
+{
+  char *m_f;
+};
+
+struct bar *
+test_2 (const char *in)
+{
+  struct bar *new_bar = (struct bar *) malloc(sizeof(struct bar));
+  if (!new_bar)
+    return NULL;
+  new_bar->m_f = (char *)malloc(strlen(in));
+  strcpy(new_bar->m_f, in); /* { dg-warning "use of possibly-NULL '\\(new_bar\\)->m_f' where non-null expected" } */
+  return new_bar;
+}
+
-- 
2.21.0

