From 1dcc4b0985a39ca26f48ac9b1d584d206e951392 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 16 Jul 2019 18:00:03 -0400
Subject: [PATCH 096/108] FIXME: add region_model::get_value_by_name

---
 gcc/analyzer/region-model.cc | 105 +++++++++++++++++++++++++++++++++++++++++--
 gcc/analyzer/region-model.h  |  11 +++++
 notes.txt                    |  14 ++++++
 3 files changed, 126 insertions(+), 4 deletions(-)

diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 6bad1b2..d417908 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1613,6 +1613,37 @@ map_region::walk_for_canonicalization (canonicalization *c) const
     }
 }
 
+/* For debugging purposes: look for a child region for a decl named
+   IDENTIFIER (or an SSA_NAME for such a decl), returning its value,
+   or svalue_id::null if none are found.  */
+
+svalue_id
+map_region::get_value_by_name (tree identifier,
+			       const region_model &model) const
+{
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      tree key = (*iter).first;
+      if (TREE_CODE (key) == SSA_NAME)
+	if (SSA_NAME_VAR (key))
+	  key = SSA_NAME_VAR (key);
+      if (DECL_P (key))
+	if (DECL_NAME (key) == identifier)
+	  {
+	    region_id rid = (*iter).second;
+	    region *region = model.get_region (rid);
+	    return region->get_value (const_cast<region_model &>(model),
+				      false, NULL);
+
+	  }
+    }
+  return svalue_id::null ();
+}
+
+/* class frame_region : public map_region.  */
+
 /* FIXME.  */
 
 bool
@@ -1853,6 +1884,28 @@ stack_region::walk_for_canonicalization (canonicalization *c) const
     c->walk_rid (*frame_rid);
 }
 
+/* For debugging purposes: look for a grandchild region within one of
+   the child frame regions, where the grandchild is for a decl named
+   IDENTIFIER (or an SSA_NAME for such a decl), returning its value,
+   or svalue_id::null if none are found.  */
+
+svalue_id
+stack_region::get_value_by_name (tree identifier,
+				 const region_model &model) const
+{
+  int i;
+  region_id *frame_rid;
+  FOR_EACH_VEC_ELT (m_frame_rids, i, frame_rid)
+    {
+      frame_region *frame = model.get_region<frame_region> (*frame_rid);
+      svalue_id sid = frame->get_value_by_name (identifier, model);
+      if (!sid.null_p ())
+	return sid;
+    }
+
+  return svalue_id::null ();
+}
+
 /* class heap_region : public region.  */
 
 /* FIXME.  */
@@ -2237,6 +2290,30 @@ root_region::walk_for_canonicalization (canonicalization *c) const
   c->walk_rid (m_heap_rid);
 }
 
+/* For debugging purposes: look for a descendant region for a local
+   or global decl named IDENTIFIER (or an SSA_NAME for such a decl),
+   returning its value, or svalue_id::null if none are found.  */
+
+svalue_id
+root_region::get_value_by_name (tree identifier,
+				const region_model &model) const
+{
+  if (stack_region *stack = get_stack_region (&model))
+    {
+      svalue_id sid = stack->get_value_by_name (identifier, model);
+      if (!sid.null_p ())
+	return sid;
+    }
+  if (map_region *globals = get_globals_region (&model))
+    {
+      svalue_id sid = globals->get_value_by_name (identifier, model);
+      if (!sid.null_p ())
+	return sid;
+    }
+  return svalue_id::null ();
+}
+
+
 /* class symbolic_region : public map_region.  */
 
 /* FIXME.  */
@@ -4375,6 +4452,18 @@ region_model::can_merge_with_p (const region_model &other_model,
   return can_merge_with_p (other_model, out_model, &sid_mapping);
 }
 
+/* For debugging purposes: look for a region within this region_model
+   for a decl named NAME (or an SSA_NAME for such a decl),
+   returning its value, or svalue_id::null if none are found.  */
+
+svalue_id
+region_model::get_value_by_name (const char *name) const
+{
+  gcc_assert (name);
+  tree identifier = get_identifier (name);
+  return get_root_region ()->get_value_by_name (identifier, *this);
+}
+
 ////////////////////////////////////////////////////////////////////////////
 
 /* struct model_merger.  */
@@ -5352,12 +5441,16 @@ test_state_merging ()
     region_model model1;
     model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL);
     model1.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL);
-    model0.set_to_new_unknown_value (model0.get_lvalue (a, &ctxt),
-				     integer_type_node);
+    svalue_id sid_a
+      = model0.set_to_new_unknown_value (model0.get_lvalue (a, &ctxt),
+					 integer_type_node);
     model1.set_to_new_unknown_value (model1.get_lvalue (a, &ctxt),
 				     integer_type_node);
     ASSERT_EQ (model0, model1);
 
+    /* Check that get_value_by_name works for locals.  */
+    ASSERT_EQ (model0.get_value_by_name ("a"), sid_a);
+
     /* They should be mergeable, and the result should be the same.  */
     region_model merged;
     ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
@@ -5372,12 +5465,16 @@ test_state_merging ()
     test_region_model_context ctxt;
     region_model model0;
     region_model model1;
-    model0.set_to_new_unknown_value (model0.get_lvalue (x, &ctxt),
-				     integer_type_node);
+    svalue_id sid_x
+      = model0.set_to_new_unknown_value (model0.get_lvalue (x, &ctxt),
+					 integer_type_node);
     model1.set_to_new_unknown_value (model1.get_lvalue (x, &ctxt),
 				     integer_type_node);
     ASSERT_EQ (model0, model1);
 
+    /* Check that get_value_by_name works for globals.  */
+    ASSERT_EQ (model0.get_value_by_name ("x"), sid_x);
+
     /* They should be mergeable, and the result should be the same.  */
     region_model merged;
     ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 643299a..f1f86b9 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -684,6 +684,9 @@ public:
 
   void walk_for_canonicalization (canonicalization *c) const FINAL OVERRIDE;
 
+  svalue_id get_value_by_name (tree identifier,
+			       const region_model &model) const;
+
  protected:
   void add_to_hash (inchash::hash &hstate) const OVERRIDE;
   void print_fields (const region_model &model,
@@ -771,6 +774,9 @@ public:
 
   void walk_for_canonicalization (canonicalization *c) const FINAL OVERRIDE;
 
+  svalue_id get_value_by_name (tree identifier,
+			       const region_model &model) const;
+
  private:
   void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
   void print_fields (const region_model &model,
@@ -853,6 +859,9 @@ public:
 
   void walk_for_canonicalization (canonicalization *c) const FINAL OVERRIDE;
 
+  svalue_id get_value_by_name (tree identifier,
+			       const region_model &model) const;
+
 private:
   void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
   void print_fields (const region_model &model,
@@ -1028,6 +1037,8 @@ class region_model
   bool can_merge_with_p (const region_model &other_model,
 			 region_model *out_model) const;
 
+  svalue_id get_value_by_name (const char *name) const;
+
  private:
   void update_for_call_superedge (const call_superedge &call_edge,
 				  region_model_context *ctxt);
diff --git a/notes.txt b/notes.txt
index 6c1b343..2be7cce 100644
--- a/notes.txt
+++ b/notes.txt
@@ -30,6 +30,20 @@ __analyzer_dump ();
 will dump the copious information about the analyzer's state each time it
 reaches the call in its traversal of the source.
 
+The builtin __analyzer_dump_exploded_nodes will dump information after
+analysis on all of the exploded nodes at that program point:
+
+  __analyzer_dump_exploded_nodes (0);
+
+will dump just the number of nodes, and their IDs.
+
+  __analyzer_dump_exploded_nodes (1);
+
+will also dump all of the states within those nodes.
+
+region_model::get_value_by_name can be used when inserting custom
+debugging code (e.g. adding it region_model::validate to detect the point
+at which a named variable acquires an unexpected state).
 
 Re pending_diagnostic's describe_* vfuncs:
      I would have given all these the name "describe" and let overloading
-- 
1.8.5.3

