From baf6c313b435bcdaccd5cd23f4333521c52e8ad4 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 3 Jun 2020 17:29:37 -0400
Subject: [PATCH 196/315] FIXME: re-enable
 selftest::test_get_representative_tree, reworking get_representative_path_var
 to support it

---
 gcc/analyzer/analyzer.h       |   6 +
 gcc/analyzer/engine.cc        |  26 +-
 gcc/analyzer/region-model2.cc | 487 ++++++++++++++++++++--------------
 gcc/analyzer/region-model2.h  | 112 +++++---
 gcc/analyzer/store2.cc        |  19 +-
 gcc/analyzer/store2.h         |   8 +-
 6 files changed, 391 insertions(+), 267 deletions(-)

diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 7b49af636b9..72b1315c5ae 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -76,6 +76,7 @@ class region2;
   class string_region2;
 class region_model2_manager;
 class store2_manager;
+class store2;
 class region_model2;
 class region_model2_context;
   class impl_region_model2_context;
@@ -131,6 +132,11 @@ public:
 	    && m_stack_depth == other.m_stack_depth);
   }
 
+  operator bool () const
+  {
+    return m_tree != NULL_TREE;
+  }
+
   void dump (pretty_printer *pp) const;
 
   tree m_tree;
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 4df661c9e39..6192d49e57e 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -594,14 +594,10 @@ public:
     gcc_assert (m_new_state);
     const svalue2 *sval = m_new_state->m_region_model2->get_rvalue (expr, NULL);
     /* Find trees for all regions storing the value.  */
-    auto_vec<path_var> pvs;
-    m_new_state->m_region_model2->get_path_vars_for_svalue2 (sval, &pvs);
-    if (pvs.length () < 1)
+    if (tree t = m_new_state->m_region_model2->get_representative_tree (sval))
+      return t;
+    else
       return expr;
-    /* Pick the "best" such tree.  */
-    // TODO: should we also consider (and consolidate) equiv classes?
-    pvs.qsort (readability_comparator);
-    return pvs[0].m_tree;
   }
 
   state_machine::state_t get_global_state () const FINAL OVERRIDE
@@ -1069,21 +1065,21 @@ impl_region_model2_context::on_state_leak (const state_machine &sm,
      We can then look that svalue_id up to locate regions and thus tree(s)
      that use it.  */
 
-  auto_vec<path_var> leaked_pvs;
-  m_old_state->m_region_model2->get_path_vars_for_svalue2 (sval, &leaked_pvs);
-
-  if (leaked_pvs.length () < 1)
-    return;
-
   /* Find "best" leaked tree.
+     FIXME:
      Sort the leaks into most human-readable first, through
      to least user-readable.  Given that we only emit one
      leak per EC, this ought to ensure that we pick the most
      user-readable description of each leaking EC.
      This assumes that all vars in the EC have the same state.  */
-  leaked_pvs.qsort (readability_comparator);
+  hash_set <const svalue2 *> visited;
+  path_var leaked_pv
+    = m_old_state->m_region_model2->get_representative_path_var (sval,
+								 &visited);
+  if (!leaked_pv)
+    return;
 
-  tree leaked_tree = leaked_pvs[0].m_tree;
+  tree leaked_tree = leaked_pv.m_tree;
   if (logger)
     logger->log ("best leaked_tree: %qE", leaked_tree);
 
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index e963bbe40be..2e1386d13b1 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -986,6 +986,124 @@ region2::get_bit_size (bit_size_t *out) const
   return true;
 }
 
+/* FIXME.  */
+/* FIXME: support the case where the bit offset is *within* one of the fields;
+   consider nested structs.  */
+
+static tree
+get_field_at_bit_offset (tree record_type, bit_offset_t bit_offset)
+{
+  gcc_assert (TREE_CODE (record_type) == RECORD_TYPE);
+  gcc_assert (bit_offset >= 0);
+  for (tree field = TYPE_FIELDS (record_type); field != NULL_TREE;
+       field = DECL_CHAIN (field))
+    {
+      if (tree next_field = DECL_CHAIN (field))
+	{
+	  int next_field_offset = int_bit_position (next_field);
+	  if (bit_offset < next_field_offset)
+	    return field;
+	}
+      else
+	return field;
+    }
+  return NULL_TREE; /* if no fields.  */
+}
+
+/* FIXME.  */
+/* FIXME:
+   The bit offset is expressed relative to the base region in the cluster.
+   We need some kind of recursive thing that walks the types and components.
+   Or we need to subtract the offset, to get relative offset.
+   See p51 of my notebook.
+   Do we need a way to get the relative offset of a region, relative to
+   its parent?   */
+
+void
+region2::get_subregions_for_binding (region_model2_manager *mgr,
+				     bit_offset_t relative_bit_offset,
+				     bit_size_t size_in_bits,
+				     tree type,
+				     auto_vec <const region2 *> *out) const
+{
+  if (get_type () == NULL_TREE)
+    return;
+#if 1
+  if (relative_bit_offset == 0
+      && get_type () == type) // FIXME: need to strip off casts?  qualifiers?
+    {
+      out->safe_push (this);
+      return;
+    }
+#endif
+  switch (TREE_CODE (get_type ()))
+    {
+    case ARRAY_TYPE:
+      {
+	tree element_type = TREE_TYPE (get_type ());
+	HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (element_type);
+	if (hwi_byte_size > 0)
+	  {
+	    /* FIXME: for some reason this is segfaulting inside
+	       wi::divmod_internal.  */
+#if 0
+	    offset_int bits_per_element = hwi_byte_size << LOG2_BITS_PER_UNIT;
+	    //bit_offset_t*/ element_index = relative_bit_offset / bits_per_element;
+	    bit_offset_t element_index = relative_bit_offset.to_shwi () / bits_per_element.to_shwi ();
+	    tree element_index_cst
+	      = build_int_cst (integer_type_node, element_index.to_shwi ());
+	    bit_offset_t inner_bit_offset
+	      = relative_bit_offset % bits_per_element;
+	    // FIXME: type ^^^^
+#else
+	    HOST_WIDE_INT bits_per_element = hwi_byte_size << LOG2_BITS_PER_UNIT;
+	    //bit_offset_t*/ element_index = relative_bit_offset / bits_per_element;
+	    HOST_WIDE_INT element_index = (relative_bit_offset.to_shwi () / bits_per_element);
+	    tree element_index_cst
+	      = build_int_cst (integer_type_node, element_index);
+	    HOST_WIDE_INT inner_bit_offset
+	      = relative_bit_offset.to_shwi () % bits_per_element;
+	    // FIXME: type ^^^^
+#endif
+	    const region2 *subregion = mgr->get_element_region
+	      (this, element_type,
+	       mgr->get_or_create_constant_svalue2 (element_index_cst));
+	    subregion->get_subregions_for_binding (mgr, inner_bit_offset,
+						   size_in_bits, type, out);
+	  }
+      }
+      break;
+    case RECORD_TYPE:
+      {
+	/* The bit offset might be *within* one of the fields (such as
+	   with nested structs).
+	   So we want to find the enclosing field, adjust the offset,
+	   and repeat.  */
+	// TODO: maybe convert from recursion to iteration?
+	if (tree field = get_field_at_bit_offset (get_type (),
+						  relative_bit_offset))
+	  {
+	    int field_bit_offset = int_bit_position (field);
+	    const region2 *subregion = mgr->get_field_region (this, field);
+	    subregion->get_subregions_for_binding
+	      (mgr, relative_bit_offset - field_bit_offset,
+	       size_in_bits, type, out);
+	  }
+      }
+      break;
+    case UNION_TYPE:
+      {
+	gcc_unreachable (); // TODO
+      }
+      break;
+    default:
+      {
+	gcc_unreachable (); // TODO
+      }
+      break;
+    }
+}
+
 /* FIXME.  */
 
 region_offset
@@ -1941,13 +2059,6 @@ label_region2::dump_to_pp (pretty_printer *pp, bool simple) const
     }
 }
 
-bool
-label_region2::get_representative_path_var (path_var *) const
-{
-  // TODO
-  return false;
-}
-
 /* class stack_region2 : public region2.  */
 
 void
@@ -2420,13 +2531,6 @@ symbolic_region2::dump_to_pp (pretty_printer *pp, bool simple) const
     }
 }
 
-bool
-symbolic_region2::get_representative_path_var (path_var *) const
-{
-  // TODO
-  return false;
-}
-
 /* class decl_region2 : public region2.  */
 
 void
@@ -2459,15 +2563,6 @@ decl_region2::dump_to_pp (pretty_printer *pp, bool simple) const
 
 /* FIXME.  */
 
-bool
-decl_region2::get_representative_path_var (path_var *out) const
-{
-  *out = path_var (m_decl, get_stack_depth ());
-  return true;
-}
-
-/* FIXME.  */
-
 int
 decl_region2::get_stack_depth () const
 {
@@ -2513,20 +2608,6 @@ field_region2::dump_to_pp (pretty_printer *pp, bool simple) const
     }
 }
 
-bool
-field_region2::get_representative_path_var (path_var *out) const
-{
-  path_var parent_pv (NULL_TREE, 0);
-  if (!get_parent_region ()->get_representative_path_var (&parent_pv))
-    return false;
-
-  *out = path_var (build3 (COMPONENT_REF,
-			   get_type (),
-			   parent_pv.m_tree, m_field, NULL_TREE),
-		   parent_pv.m_stack_depth);
-  return true;
-}
-
 /* class element_region2 : public region2.  */
 
 bool
@@ -2559,29 +2640,6 @@ element_region2::dump_to_pp (pretty_printer *pp, bool simple) const
     }
 }
 
-bool
-element_region2::get_representative_path_var (path_var *) const
-{
-  // TODO:
-#if 0
-  path_var parent_pv (NULL_TREE, 0);
-  if (get_parent_region ()->get_representative_path_var (&parent_pv))
-    {
-      path_var index_pv  (NULL_TREE, 0);
-      if (m_index->get_representative_path_var (&index_pv))
-	{
-	  *out = path_var (build4 (ARRAY_REF,
-				   get_type (),
-				   parent_pv.m_tree, index_pv.m_tree,
-				   NULL_TREE, NULL_TREE),
-			   parent_pv.m_stack_depth);
-	  return true;
-	}
-    }
-#endif
-  return false;
-}
-
 void
 element_region2::add_to_hash (inchash::hash &hstate) const
 {
@@ -2620,13 +2678,6 @@ offset_region2::dump_to_pp (pretty_printer *pp, bool simple) const
     }
 }
 
-bool
-offset_region2::get_representative_path_var (path_var *) const
-{
-  // TODO
-  return false;
-}
-
 void
 offset_region2::add_to_hash (inchash::hash &hstate) const
 {
@@ -2663,13 +2714,6 @@ cast_region2::dump_to_pp (pretty_printer *pp, bool simple) const
     }
 }
 
-bool
-cast_region2::get_representative_path_var (path_var *) const
-{
-  // TODO
-  return false;
-}
-
 void
 cast_region2::add_to_hash (inchash::hash &hstate) const
 {
@@ -2689,12 +2733,6 @@ heap_allocated_region2::dump_to_pp (pretty_printer *pp, bool simple) const
     pp_string (pp, "heap_allocated_region2()");
 }
 
-bool
-heap_allocated_region2::get_representative_path_var (path_var *) const
-{
-  return false;
-}
-
 /* class alloca_region2 : public region2.  */
 
 void
@@ -2707,12 +2745,6 @@ alloca_region2::dump_to_pp (pretty_printer *pp, bool simple) const
     pp_string (pp, "alloca_region2()");
 }
 
-bool
-alloca_region2::get_representative_path_var (path_var *) const
-{
-  return false;
-}
-
 /* class string_region2 : public region2.  */
 
 void
@@ -2731,12 +2763,6 @@ string_region2::dump_to_pp (pretty_printer *pp, bool simple) const
     }
 }
 
-bool
-string_region2::get_representative_path_var (path_var *) const
-{
-  return false;
-}
-
 /* class unknown_region2 : public region2.  */
 
 void
@@ -2745,12 +2771,6 @@ unknown_region2::dump_to_pp (pretty_printer *pp, bool /*simple*/) const
   pp_string (pp, "UNKNOWN_REGION");
 }
 
-bool
-unknown_region2::get_representative_path_var (path_var *) const
-{
-  return false;
-}
-
 /* class region_model2_manager.  */
 
 region_model2_manager::region_model2_manager ()
@@ -6212,28 +6232,47 @@ region_model2::add_new_malloc_region2 ()
 }
 #endif
 
-/* Attempt to return a tree that represents SVAL, or return NULL_TREE.  */
+/* Attempt to return a path_var that represents SVAL, or return NULL_TREE.
+   Use VISITED to prevent infinite mutual recursion with the overload for
+   regions.  */
 
-tree
-region_model2::get_representative_tree (const svalue2 *sval) const
+path_var
+region_model2::get_representative_path_var (const svalue2 *sval,
+					    hash_set <const svalue2 *> *visited) const
 {
   if (sval == NULL)
-    return NULL_TREE;
+    return path_var (NULL_TREE, 0);
 
   if (const svalue2 *cast_sval = sval->maybe_undo_cast ())
     sval = cast_sval;
 
+  /* Prevent infinite recursion.  */
+  if (visited->contains (sval))
+    return path_var (NULL_TREE, 0);
+  visited->add (sval);
+
   auto_vec<path_var> pvs;
-  m_store.get_representative_path_vars (sval, &pvs);
+  m_store.get_representative_path_vars (this, visited, sval, &pvs);
 
   if (tree cst = sval->maybe_get_constant ())
     pvs.safe_push (path_var (cst, 0));
 
+  /* Handle string literals and various other pointers.  */
+  if (const region_svalue2 *ptr_sval = sval->dyn_cast_region_svalue2 ())
+    {
+      const region2 *reg = ptr_sval->get_pointee ();
+      if (path_var pv = get_representative_path_var (reg, visited))
+	return path_var (build1 (ADDR_EXPR,
+				 TREE_TYPE (sval->get_type ()),
+				 pv.m_tree),
+			 pv.m_stack_depth);
+    }
+
   if (pvs.length () < 1)
-    return NULL_TREE;
+    return path_var (NULL_TREE, 0);
 
   pvs.qsort (readability_comparator);
-  return pvs[0].m_tree;
+  return pvs[0];
 
 #if 0
   /* Find the first region2 that stores the value (e.g. a local) and
@@ -6248,118 +6287,148 @@ region_model2::get_representative_tree (const svalue2 *sval) const
 	  return pv.m_tree;
       }
 
-  /* Handle string literals and various other pointers.  */
-  const svalue2 *sval = get_svalue2 (sid);
-  if (region_svalue2 *ptr_sval = sval->dyn_cast_region_svalue2 ())
-    {
-      region2 *reg = ptr_sval->get_pointee ();
-      path_var pv = get_representative_path_var (rid);
-      if (pv.m_tree)
-	return build1 (ADDR_EXPR,
-		       TREE_TYPE (sval->get_type ()),
-		       pv.m_tree);
-    }
-
   return maybe_get_constant (sid);
 #endif
 }
 
-/* Attempt to return a path_var that represents the region2, or return
+/* Attempt to return a tree that represents SVAL, or return NULL_TREE.  */
+
+tree
+region_model2::get_representative_tree (const svalue2 *sval) const
+{
+  hash_set <const svalue2 *> visited;
+  return get_representative_path_var (sval, &visited).m_tree;
+}
+
+/* Attempt to return a path_var that represents REG, or return
    the NULL path_var.
    For example, a region2 for a field of a local would be a path_var
-   wrapping a COMPONENT_REF.  */
-#if 0
+   wrapping a COMPONENT_REF.
+   Use VISITED to prevent infinite mutual recursion with the overload for
+   svalue2s.  */
+
 path_var
-region_model2::get_representative_path_var (region2 *reg) const
+region_model2::get_representative_path_var (const region2 *reg,
+					    hash_set <const svalue2 *> *visited) const
 {
-  region2 *reg = get_region2 (rid);
-  region2 *parent_reg = get_region2 (reg->get_parent ());
-  region2 *stack_reg = get_stack_region2 *();
-  if (!stack_reg.null_p ())
-    if (parent_reg && parent_reg->get_parent () == stack_reg)
-      {
-	frame_region2 *parent_frame = (frame_region2 *)parent_reg;
-	tree t = parent_frame->get_tree_for_child_region2 (rid);
-	return path_var (t, parent_frame->get_depth ());
-    }
-  if (reg->get_parent () == get_globals_region2 *())
+  switch (reg->get_kind ())
     {
-      map_region2 *globals = get_root_region2 ()->get_globals_region2 (this);
-      if (globals)
-	return path_var (globals->get_tree_for_child_region2 (rid), -1);
-    }
+    default:
+      gcc_unreachable ();
 
-  /* Handle e.g. fields of a local by recursing.  */
-  region2 *parent_reg = reg->get_parent ();
-  if (parent_reg)
-    {
-      if (reg->is_view_p ())
-	{
-	  path_var parent_pv = get_representative_path_var (parent_reg);
-	  if (parent_pv.m_tree && reg->get_type ())
-	    return path_var (build1 (NOP_EXPR,
-				     reg->get_type (),
-				     parent_pv.m_tree),
-			     parent_pv.m_stack_depth);
-	}
+    case region2::RK_FRAME:
+    case region2::RK_GLOBALS:
+    case region2::RK_CODE:
+    case region2::RK_HEAP:
+    case region2::RK_STACK:
+    case region2::RK_ROOT:
+       /* Regions that represent memory spaces are not expressible as trees.  */
+      return path_var (NULL_TREE, 0);
 
-      if (parent_reg->get_kind () == RK_STRUCT)
-	{
-	  map_region2 *parent_map_region2 = (map_region2 *)parent_reg;
-	  /* This can fail if we have a view, rather than a field.  */
-	  if (tree child_key
-		= parent_map_region2->get_tree_for_child_region2 (rid))
-	    {
-	      path_var parent_pv = get_representative_path_var (parent_reg);
-	      if (parent_pv.m_tree && TREE_CODE (child_key) == FIELD_DECL)
-		return path_var (build3 (COMPONENT_REF,
-					 TREE_TYPE (child_key),
-					 parent_pv.m_tree, child_key,
-					 NULL_TREE),
-				 parent_pv.m_stack_depth);
-	    }
-	}
+    case region2::RK_FUNCTION:
+      {
+	const function_region2 *function_reg
+	  = as_a <const function_region2 *> (reg);
+	return path_var (function_reg->get_fndecl (), 0);
+      }
+    case region2::RK_LABEL:
+      gcc_unreachable (); // TODO
 
-      /* Handle elements within an array.  */
-      if (array_region2 *array_reg = parent_reg->dyn_cast_array_region2 ())
+    case region2::RK_SYMBOLIC:
       {
-	array_region2::key_t key;
-	if (array_reg->get_key_for_child_region2 (rid, &key))
-	  {
-	    path_var parent_pv = get_representative_path_var (parent_reg);
-	    if (parent_pv.m_tree && reg->get_type ())
-	      {
-		tree index = array_reg->constant_from_key (key);
-		return path_var (build4 (ARRAY_REF,
-					 reg->get_type (),
-					 parent_pv.m_tree, index,
-					 NULL_TREE, NULL_TREE),
-				 parent_pv.m_stack_depth);
-	      }
-	  }
+	const symbolic_region2 *symbolic_reg
+	  = as_a <const symbolic_region2 *> (reg);
+	const svalue2 *pointer = symbolic_reg->get_pointer ();
+	path_var pointer_pv = get_representative_path_var (pointer, visited);
+	if (!pointer_pv)
+	  return path_var (NULL_TREE, 0);
+	return path_var (build2 (MEM_REF,
+				 reg->get_type (),
+				 pointer_pv.m_tree,
+				 integer_zero_node),
+			 pointer_pv.m_stack_depth);
+      }
+    case region2::RK_DECL:
+      {
+	const decl_region2 *decl_reg = as_a <const decl_region2 *> (reg);
+	return path_var (decl_reg->get_decl (), decl_reg->get_stack_depth ());
+      }
+    case region2::RK_FIELD:
+      {
+	const field_region2 *field_reg = as_a <const field_region2 *> (reg);
+	path_var parent_pv
+	  = get_representative_path_var (reg->get_parent_region (), visited);
+	if (!parent_pv)
+	  return path_var (NULL_TREE, 0);
+	return path_var (build3 (COMPONENT_REF,
+				 reg->get_type (),
+				 parent_pv.m_tree,
+				 field_reg->get_field (),
+				 NULL_TREE),
+			 parent_pv.m_stack_depth);
+      }
+
+    case region2::RK_ELEMENT:
+      {
+	const element_region2 *element_reg
+	  = as_a <const element_region2 *> (reg);
+	path_var parent_pv
+	  = get_representative_path_var (reg->get_parent_region (), visited);
+	if (!parent_pv)
+	  return path_var (NULL_TREE, 0);
+	path_var index_pv
+	  = get_representative_path_var (element_reg->get_index (), visited);
+	if (!index_pv)
+	  return path_var (NULL_TREE, 0);
+	return path_var (build4 (ARRAY_REF,
+				 reg->get_type (),
+				 parent_pv.m_tree, index_pv.m_tree,
+				 NULL_TREE, NULL_TREE),
+			 parent_pv.m_stack_depth);
       }
-    }
 
-  /* Handle string literals.  */
-  const svalue2 *sval = reg->get_value_direct ();
-  if (const svalue2 *sval = get_svalue2 (sid))
-    if (tree cst = sval->maybe_get_constant ())
-      if (TREE_CODE (cst) == STRING_CST)
-	return path_var (cst, 0);
+    case region2::RK_OFFSET:
+      gcc_unreachable (); // TODO
 
-  return path_var (NULL_TREE, 0);
+    case region2::RK_CAST:
+      {
+	path_var parent_pv
+	  = get_representative_path_var (reg->get_parent_region (), visited);
+	if (!parent_pv)
+	  return path_var (NULL_TREE, 0);
+	return path_var (build1 (NOP_EXPR,
+				 reg->get_type (),
+				 parent_pv.m_tree),
+			 parent_pv.m_stack_depth);
+      }
+
+    case region2::RK_HEAP_ALLOCATED:
+    case region2::RK_ALLOCA:
+      /* No good way to express heap-allocated/alloca regions as trees.  */
+      return path_var (NULL_TREE, 0);
+
+    case region2::RK_STRING:
+      {
+	const string_region2 *string_reg = as_a <const string_region2 *> (reg);
+	return path_var (string_reg->get_string_cst (), 0);
+      }
+
+    case region2::RK_UNKNOWN:
+      return path_var (NULL_TREE, 0);
+    }
 }
-#endif
+
 /* Locate all region2s that directly have value SVAL and append representative
    path_var instances for them into *OUT.  */
 /* FIXME: other ways of getting to the svalue e.g. for initial_svalue2s?
    Also: this might not be the best human-readable description to use.  */
-
+#if 0
 void
 region_model2::get_path_vars_for_svalue2 (const svalue2 *sval,
+					  int i,
 					  auto_vec<path_var> *out) const
 {
-  return m_store.get_representative_path_vars (sval, out);
+  return m_store.get_representative_path_vars (this, sval, out);
 
 #if 0
   unsigned i;
@@ -6373,6 +6442,7 @@ region_model2::get_path_vars_for_svalue2 (const svalue2 *sval,
       }
 #endif
 }
+#endif
 
 /* Set DST_REG value to be a new unknown value of type TYPE.  */
 #if 0
@@ -8047,15 +8117,18 @@ test_dump_3 ()
      "  constraints:\n");
 }
 
+#endif
 /* Verify that region_model2::get_representative_tree works as expected.  */
 
 static void
 test_get_representative_tree ()
 {
+  region_model2_manager mgr;
+
   /* STRING_CST.  */
   {
     tree string_cst = build_string (4, "foo");
-    region_model2 m;
+    region_model2 m (&mgr);
     const svalue2 *str_sval = m.get_rvalue (string_cst, NULL);
     tree rep = m.get_representative_tree (str_sval);
     ASSERT_EQ (rep, string_cst);
@@ -8064,13 +8137,13 @@ test_get_representative_tree ()
   /* String literal.  */
   {
     tree string_cst_ptr = build_string_literal (4, "foo");
-    region_model2 m;
+    region_model2 m (&mgr);
     const svalue2 *str_sval = m.get_rvalue (string_cst_ptr, NULL);
     tree rep = m.get_representative_tree (str_sval);
     ASSERT_DUMP_TREE_EQ (rep, "&\"foo\"[0]");
   }
 }
-#endif
+
 /* Verify that calling region_model2::get_rvalue repeatedly on the same
    tree constant retrieves the same svalue2_id.  */
 
@@ -8803,9 +8876,8 @@ test_stack_frames ()
 #endif
 }
 
-#if 0
 /* Verify that get_representative_path_var works as expected, that
-   we can map from region2 ids to parms and back within a recursive call
+   we can map from region2s to parms and back within a recursive call
    stack.  */
 
 static void
@@ -8845,18 +8917,23 @@ test_get_representative_path_var ()
      at every depth.  */
   for (int depth = 0; depth < 5; depth++)
     {
-      path_var pv (NULL, 0);
-      ASSERT_TRUE (parm_regs[depth]->get_representative_path_var (&pv));
-      ASSERT_EQ (pv, path_var (n, depth + 1));
+      {
+	hash_set <const svalue2 *> visited;
+	ASSERT_EQ (model.get_representative_path_var (parm_regs[depth],
+						      &visited),
+		   path_var (n, depth + 1));
+      }
       /* ...and that we can lookup lvalues for locals for all frames,
 	 not just the top.  */
       ASSERT_EQ (model.get_lvalue (path_var (n, depth), NULL),
 		 parm_regs[depth]);
       /* ...and that we can locate the svalue2s.  */
-      auto_vec<path_var> pvs;
-      model.get_path_vars_for_svalue2 (parm_svals[depth], &pvs);
-      ASSERT_EQ (pvs.length (), 1);
-      ASSERT_EQ (pvs[0], path_var (n, depth + 1));
+      {
+	hash_set <const svalue2 *> visited;
+	ASSERT_EQ (model.get_representative_path_var (parm_svals[depth],
+						      &visited),
+		   path_var (n, depth + 1));
+      }
     }
 }
 
@@ -9780,8 +9857,8 @@ analyzer_region_model2_cc_tests ()
   test_dump_2 ();
 #if 0
   test_dump_3 ();
-  test_get_representative_tree ();
 #endif
+  test_get_representative_tree ();
   test_unique_constants ();
 #if 0
   test_svalue2_equality ();
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index c380a1bf43b..bebbd3a7a90 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -924,7 +924,12 @@ public:
   bool get_byte_size (byte_size_t *out) const;
   bool get_bit_size (bit_size_t *out) const;
 
-  virtual bool get_representative_path_var (path_var *out) const = 0;
+  void
+  get_subregions_for_binding (region_model2_manager *mgr,
+			      bit_offset_t start_bit_offset,
+			      bit_size_t size_in_bits,
+			      tree type,
+			      auto_vec <const region2 *> *out) const;
 
  protected:
   region2 (unsigned id, const region2 *parent, tree type);
@@ -963,13 +968,6 @@ namespace ana {
 
 class space_region2 : public region2
 {
-public:
-  bool get_representative_path_var (path_var *) const FINAL OVERRIDE
-  {
-    /* space_region2s are not expressible as trees.  */
-    return false;
-  }
-
 protected:
   space_region2 (unsigned id, const region2 *parent)
   : region2 (id, parent, NULL_TREE)
@@ -1122,10 +1120,6 @@ public:
   enum kind get_kind () const FINAL OVERRIDE { return RK_FUNCTION; }
   const function_region2 *
   dyn_cast_function_region2 () const FINAL OVERRIDE{ return this; }
-  bool get_representative_path_var (path_var *) const FINAL OVERRIDE
-  {
-    return false;
-  }
 
   tree get_fndecl () const { return m_fndecl; }
 
@@ -1167,7 +1161,6 @@ public:
   /* region2 vfuncs.  */
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
   enum kind get_kind () const FINAL OVERRIDE { return RK_LABEL; }
-  bool get_representative_path_var (path_var *out) const FINAL OVERRIDE;
 
   //bool compare_fields (const function_region2 &other) const;
 
@@ -1204,11 +1197,6 @@ public:
 
   enum kind get_kind () const FINAL OVERRIDE { return RK_STACK; }
 
-  bool get_representative_path_var (path_var *) const FINAL OVERRIDE
-  {
-    return false;
-  }
-
 #if 0
   static bool can_merge_p (const stack_region2 *stack_region2_a,
 			   const stack_region2 *stack_region2_b,
@@ -1260,10 +1248,6 @@ public:
 			   heap_region2 *merged_heap, region2_id merged_heap_rid,
 			   model_merger *merger);
 #endif
-  bool get_representative_path_var (path_var *) const FINAL OVERRIDE
-  {
-    return false;
-  }
 };
 
 } // namespace ana
@@ -1292,11 +1276,6 @@ public:
   enum kind get_kind () const FINAL OVERRIDE { return RK_ROOT; }
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
 
-  bool get_representative_path_var (path_var *) const FINAL OVERRIDE
-  {
-    return false;
-  }
-
 #if 0
   static bool can_merge_p (const root_region2 *root_region2_a,
 			   const root_region2 *root_region2_b,
@@ -1348,7 +1327,6 @@ public:
   enum kind get_kind () const FINAL OVERRIDE { return region2::RK_SYMBOLIC; }
 
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-  bool get_representative_path_var (path_var *) const FINAL OVERRIDE;
 
   const svalue2 *get_pointer () const { return m_sval_ptr; }
 
@@ -1356,6 +1334,18 @@ private:
   const svalue2 *m_sval_ptr;
 };
 
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const symbolic_region2 *>::test (const region2 *reg)
+{
+  return reg->get_kind () == region2::RK_SYMBOLIC;
+}
+
+namespace ana {
+
 /* FIXME.  */
 
 class decl_region2 : public region2
@@ -1372,7 +1362,6 @@ public:
   dyn_cast_decl_region2 () const FINAL OVERRIDE { return this; }
 
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-  bool get_representative_path_var (path_var *out) const FINAL OVERRIDE;
 
   tree get_decl () const { return m_decl; }
   int get_stack_depth () const;
@@ -1384,6 +1373,18 @@ private:
   tree m_decl;
 };
 
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const decl_region2 *>::test (const region2 *reg)
+{
+  return reg->get_kind () == region2::RK_DECL;
+}
+
+namespace ana {
+
 /* FIXME.  */
 
 class field_region2 : public region2
@@ -1400,7 +1401,6 @@ public:
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
   const field_region2 *
   dyn_cast_field_region2 () const FINAL OVERRIDE { return this; }
-  bool get_representative_path_var (path_var *out) const FINAL OVERRIDE;
 
   tree get_field () const { return m_field; }
 
@@ -1411,6 +1411,18 @@ private:
   tree m_field;
 };
 
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const field_region2 *>::test (const region2 *reg)
+{
+  return reg->get_kind () == region2::RK_FIELD;
+}
+
+namespace ana {
+
 /* An element within an array.  */
 
 class element_region2 : public region2
@@ -1426,7 +1438,6 @@ public:
   enum region2::kind get_kind () const FINAL OVERRIDE { return RK_ELEMENT; }
   const element_region2 *
   dyn_cast_element_region2 () const FINAL OVERRIDE { return this; }
-  bool get_representative_path_var (path_var *out) const FINAL OVERRIDE;
 
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
 
@@ -1439,6 +1450,18 @@ private:
   const svalue2 *m_index;
 };
 
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const element_region2 *>::test (const region2 *reg)
+{
+  return reg->get_kind () == region2::RK_ELEMENT;
+}
+
+namespace ana {
+
 /* A byte-offset within another region, for handling pointer arithmetic
    as a region.  */
 
@@ -1455,7 +1478,6 @@ public:
   enum region2::kind get_kind () const FINAL OVERRIDE { return RK_OFFSET; }
   const offset_region2 *
   dyn_cast_offset_region2 () const FINAL OVERRIDE { return this; }
-  bool get_representative_path_var (path_var *out) const FINAL OVERRIDE;
 
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
 
@@ -1483,7 +1505,6 @@ public:
   enum region2::kind get_kind () const FINAL OVERRIDE { return RK_CAST; }
   const cast_region2 *
   dyn_cast_cast_region2 () const FINAL OVERRIDE { return this; }
-  bool get_representative_path_var (path_var *out) const FINAL OVERRIDE;
 
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
 
@@ -1514,7 +1535,6 @@ public:
   enum region2::kind get_kind () const FINAL OVERRIDE { return RK_HEAP_ALLOCATED; }
 
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-  bool get_representative_path_var (path_var *) const FINAL OVERRIDE;
 
 protected:
 #if 0
@@ -1537,7 +1557,6 @@ public:
   enum region2::kind get_kind () const FINAL OVERRIDE { return RK_ALLOCA; }
 
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-  bool get_representative_path_var (path_var *) const FINAL OVERRIDE;
 
 protected:
 #if 0
@@ -1563,7 +1582,6 @@ public:
   enum region2::kind get_kind () const FINAL OVERRIDE { return RK_STRING; }
 
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-  bool get_representative_path_var (path_var *) const FINAL OVERRIDE;
 
   tree get_string_cst () const { return m_string_cst; }
 
@@ -1575,6 +1593,18 @@ private:
   tree m_string_cst;
 };
 
+} // namespace ana
+
+template <>
+template <>
+inline bool
+is_a_helper <const string_region2 *>::test (const region2 *reg)
+{
+  return reg->get_kind () == region2::RK_STRING;
+}
+
+namespace ana {
+
 /* An unknown region, for handling unimplemented tree codes.  */
 
 class unknown_region2 : public region2
@@ -1588,7 +1618,6 @@ public:
   bool compare_fields (const element_region2 &other) const;
 #endif
   enum region2::kind get_kind () const FINAL OVERRIDE { return RK_UNKNOWN; }
-  bool get_representative_path_var (path_var *) const FINAL OVERRIDE;
 
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
 
@@ -1907,10 +1936,15 @@ class region_model2
 
   tree get_representative_tree (const svalue2 *sval) const;
 #if 0
-  path_var get_representative_path_var (region2_id rid) const;
-#endif
   void get_path_vars_for_svalue2 (const svalue2 *sval,
 				  auto_vec<path_var> *out) const;
+#endif
+  path_var
+  get_representative_path_var (const svalue2 *sval,
+			       hash_set <const svalue2 *> *visited) const;
+  path_var
+  get_representative_path_var (const region2 *reg,
+			       hash_set <const svalue2 *> *visited) const;
 
 #if 0
   void purge_unused_svalue2s (purge_stats *out,
diff --git a/gcc/analyzer/store2.cc b/gcc/analyzer/store2.cc
index 9c551b45b75..685993b2de2 100644
--- a/gcc/analyzer/store2.cc
+++ b/gcc/analyzer/store2.cc
@@ -686,7 +686,9 @@ binding_cluster2::redundant_p () const
    appending the results to OUT_PVS.  */
 
 void
-binding_cluster2::get_representative_path_vars (const region2 *base_reg,
+binding_cluster2::get_representative_path_vars (const region_model2 *model,
+						hash_set <const svalue2 *> *visited,
+						const region2 *base_reg,
 						const svalue2 *sval,
 						auto_vec<path_var> *out_pvs)
   const
@@ -707,7 +709,9 @@ binding_cluster2::get_representative_path_vars (const region2 *base_reg,
 	      if (ckey->get_start_bit_offset () == 0)
 		{
 		  path_var pv (NULL_TREE, 0);
-		  if (base_reg->get_representative_path_var (&pv))
+		  if (path_var pv
+		      = model->get_representative_path_var (base_reg,
+							    visited))
 		    out_pvs->safe_push (pv);
 		}
 	      else
@@ -1356,7 +1360,9 @@ store2::escaped_p (const region2 *base_reg) const
 /* FIXME.  */
 
 void
-store2::get_representative_path_vars (const svalue2 *sval,
+store2::get_representative_path_vars (const region_model2 *model,
+				      hash_set <const svalue2 *> *visited,
+				      const svalue2 *sval,
 				      auto_vec<path_var> *out_pvs) const
 {
   gcc_assert (sval);
@@ -1367,14 +1373,15 @@ store2::get_representative_path_vars (const svalue2 *sval,
     {
       const region2 *base_reg = (*iter).first;
       binding_cluster2 *cluster = (*iter).second;
-      cluster->get_representative_path_vars (base_reg, sval, out_pvs);
+      cluster->get_representative_path_vars (model, visited, base_reg, sval,
+					     out_pvs);
     }
 
   if (const initial_svalue2 *init_sval = sval->dyn_cast_initial_svalue2 ())
     {
       const region2 *reg = init_sval->get_region ();
-      path_var pv (NULL_TREE, 0);
-      if (reg->get_representative_path_var (&pv))
+      if (path_var pv = model->get_representative_path_var (reg,
+							    visited))
 	out_pvs->safe_push (pv);
     }
 }
diff --git a/gcc/analyzer/store2.h b/gcc/analyzer/store2.h
index e75b10325fd..eb9d59e323a 100644
--- a/gcc/analyzer/store2.h
+++ b/gcc/analyzer/store2.h
@@ -326,7 +326,9 @@ public:
 
   bool redundant_p () const;
 
-  void get_representative_path_vars (const region2 *base_reg,
+  void get_representative_path_vars (const region_model2 *model,
+				     hash_set <const svalue2 *> *visited,
+				     const region2 *base_reg,
 				     const svalue2 *sval,
 				     auto_vec<path_var> *out_pvs) const;
 
@@ -418,7 +420,9 @@ public:
 
   bool escaped_p (const region2 *reg) const;
 
-  void get_representative_path_vars (const svalue2 *sval,
+  void get_representative_path_vars (const region_model2 *model,
+				     hash_set <const svalue2 *> *visited,
+				     const svalue2 *sval,
 				     auto_vec<path_var> *out_pvs) const;
 
   cluster_map_t::iterator begin () const { return m_cluster_map.begin (); }
-- 
2.26.2

