From cf881821def58f0fb8b893cf812bb6672e5491bb Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 31 Oct 2019 16:06:05 -0400
Subject: [PATCH 94/97] FIXME: fix byte offset vs index in array_region; use
 int rather than tree for key, so making it not be a map_region subclass

---
 gcc/analyzer/analyzer.h      |   1 +
 gcc/analyzer/region-model.cc | 346 +++++++++++++++++++++++++++++++++++++------
 gcc/analyzer/region-model.h  | 159 ++++++++++++++------
 3 files changed, 420 insertions(+), 86 deletions(-)

diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 520a180..532adcc 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -39,6 +39,7 @@ class svalue;
   class unknown_svalue;
   class setjmp_svalue;
 class region;
+  class map_region;
 class region_model;
 class region_model_context;
   class impl_region_model_context;
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index bab278f..34c4157 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -2233,12 +2233,15 @@ union_region::compare_fields (const union_region &other) const
   return struct_or_union_region::compare_fields (other);
 }
 
-/* class array_region : public map_region.  */
+// FIXME: array_region here is out of order w.r.t the decls in the header
 
-/* FIXME.  */
+/* class array_region : public region.  */
+
+/* array_region's copy ctor.  */
 
 array_region::array_region (const array_region &other)
-: map_region (other)
+: region (other),
+  m_map (other.m_map)
 {
 }
 
@@ -2254,25 +2257,14 @@ array_region::get_element (region_model *model,
   svalue *index_sval = model->get_svalue (index_sid);
   if (tree cst_index = index_sval->maybe_get_constant ())
     {
+      key_t key = key_from_constant (cst_index);
       region_id element_rid
-	= get_or_create (model, this_rid, cst_index, element_type);
+	= get_or_create (model, this_rid, key, element_type);
       return element_rid;
    }
 
   // FIXME: integrate with offset-based add_view:
   return model->get_or_create_view (this_rid, element_type);
-
-#if 0
-
-	// FIXME: if we have a constant, do this:
-	// FIXME; otherwise, make a view
-	// array_region::get_element (svalue_id index_sid)
-	region_id element_rid
-	  = array_reg->get_or_create (model, this_rid, index, element_type);
-	/* FIXME: ^^^ should be indexed by svalue, rather than by tree?
-	   What about unknown indices?  */
-	return element_rid;
-#endif
 }
 
 /* Implementation of region::clone vfunc for array_region.  */
@@ -2283,22 +2275,234 @@ array_region::clone () const
   return new array_region (*this);
 }
 
-/* FIXME.  */
+/* Compare the fields of this array_region with OTHER, returning true
+   if they are equal.
+   For use by region::operator==.  */
 
 bool
-array_region::valid_key_p (tree key) const
+array_region::compare_fields (const array_region &other) const
+{
+  if (m_map.elements () != other.m_map.elements ())
+    return false;
+
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      int key = (*iter).first;
+      region_id e = (*iter).second;
+      region_id *other_slot = const_cast <map_t &> (other.m_map).get (key);
+      if (other_slot == NULL)
+	return false;
+      if (e != *other_slot)
+	return false;
+    }
+  return true;
+}
+
+/* Implementation of region::print_fields vfunc for array_region.  */
+
+void
+array_region::print_fields (const region_model &model,
+			    region_id this_rid,
+			    pretty_printer *pp) const
 {
-  return TREE_CODE (key) == INTEGER_CST;
+  region::print_fields (model, this_rid, pp);
+  pp_string (pp, ", array: {");
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      if (iter != m_map.begin ())
+	pp_string (pp, ", ");
+      int key = (*iter).first;
+      region_id child_rid = (*iter).second;
+PUSH_IGNORE_WFORMAT
+      pp_printf (pp, "[%i]: ", key);
+POP_IGNORE_WFORMAT
+      child_rid.print (pp);
+    }
+  pp_string (pp, "}");
 }
 
-/* Compare the fields of this array_region with OTHER, returning true
-   if they are equal.
-   For use by region::operator==.  */
+/* Implementation of region::dump_dot_to_pp vfunc for array_region.  */
+
+void
+array_region::dump_dot_to_pp (const region_model &model,
+			      region_id this_rid,
+			      pretty_printer *pp) const
+{
+  region::dump_dot_to_pp (model, this_rid, pp);
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      // TODO: add nodes/edges to label things
+
+      int key = (*iter).first;
+      region_id child_rid = (*iter).second;
+
+      pp_printf (pp, "rid_label_%i [label=\"", child_rid.as_int ());
+      pp_write_text_to_stream (pp);
+PUSH_IGNORE_WFORMAT
+      pp_printf (pp, "%qi", key);
+POP_IGNORE_WFORMAT
+      pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
+      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);
+    }
+}
+
+/* Implementation of region::dump_child_label vfunc for array_region.  */
+
+void
+array_region::dump_child_label (const region_model &model,
+			      region_id this_rid,
+			      region_id child_rid,
+			      pretty_printer *pp) const
+{
+  region::dump_child_label (model, this_rid, child_rid, pp);
+
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      if (child_rid == (*iter).second)
+	{
+	  int key = (*iter).first;
+	  pp_printf (pp, "[%i]: ", key);
+	}
+    }
+}
+
+/* Look for a child region for KEY within this array_region.
+   If it doesn't already exist, create a child array_region, using TYPE for
+   its type.
+   Return the region_id of the child (whether pre-existing, or
+   newly-created).  */
+
+region_id
+array_region::get_or_create (region_model *model,
+			     region_id this_rid,
+			     key_t key,
+			     tree type)
+{
+  region_id *slot = m_map.get (key);
+  if (slot)
+    return *slot;
+  region_id child_rid = model->add_region_for_type (this_rid, type);
+  m_map.put (key, child_rid);
+  return child_rid;
+}
+
+/* Get the region_id for the child region for KEY within this
+   ARRAY_REGION, or NULL if there is no such child region.  */
+
+region_id *
+array_region::get (key_t key)
+{
+  region_id *slot = m_map.get (key);
+  return slot;
+}
+
+/* Implementation of region::add_to_hash vfunc for array_region.  */
+
+void
+array_region::add_to_hash (inchash::hash &hstate) const
+{
+  region::add_to_hash (hstate);
+  // TODO
+}
+
+/* Implementation of region::remap_region_ids vfunc for array_region.  */
+
+void
+array_region::remap_region_ids (const region_id_map &map)
+{
+  region::remap_region_ids (map);
+
+  /* Remap the region ids within the map entries.  */
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end (); ++iter)
+    map.update (&(*iter).second);
+}
+
+/* Look for a child region with id CHILD_RID within this array_region.
+   If one is found, write its key to *OUT and return true,
+   otherwise return false.  */
 
 bool
-array_region::compare_fields (const array_region &other) const
+array_region::get_key_for_child_region (region_id child_rid, key_t *out) const
 {
-  return map_region::compare_fields (other);
+  // FIXME: for now, a linear search through map
+  // FIXME: do we want to store an inverse map?
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      key_t key = (*iter).first;
+      region_id r = (*iter).second;
+      if (r == child_rid)
+	{
+	  *out = key;
+	  return true;
+	}
+    }
+
+  return false;
+}
+
+/* qsort comparator for int.  */
+
+static int
+int_cmp (const void *p1, const void *p2)
+{
+  int i1 = *(const int *)p1;
+  int i2 = *(const int *)p2;
+
+  return i1 - i2;
+}
+
+/* Implementation of region::walk_for_canonicalization vfunc for
+   array_region.  */
+
+void
+array_region::walk_for_canonicalization (canonicalization *c) const
+{
+  auto_vec<int> keys (m_map.elements ());
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      int key_a = (*iter).first;
+      keys.quick_push (key_a);
+    }
+  keys.qsort (int_cmp);
+
+  unsigned i;
+  int key;
+  FOR_EACH_VEC_ELT (keys, i, key)
+    {
+      region_id rid = *const_cast<array_region *>(this)->m_map.get (key);
+      c->walk_rid (rid);
+    }
+}
+
+/* FIXME.  */
+
+array_region::key_t
+array_region::key_from_constant (tree cst)
+{
+  gcc_assert (CONSTANT_CLASS_P (cst));
+  wide_int w = wi::to_wide (cst);
+  key_t result = w.to_shwi (); // FIXME: loss of precision here?
+  return result;
 }
 
 /* class frame_region : public map_region.  */
@@ -4329,7 +4533,7 @@ region_model::on_setjmp (const gcall *call, const exploded_node *enode,
   if (tree lhs = gimple_call_lhs (call))
     {
       tree zero = build_int_cst (TREE_TYPE (lhs), 0);
-      svalue_id new_sid = add_svalue (new constant_svalue (zero));
+      svalue_id new_sid = get_or_create_constant_svalue (zero);
       region_id lhs_rid = get_lvalue (lhs, ctxt);
       set_value (lhs_rid, new_sid);
     }
@@ -6172,6 +6376,35 @@ region_model::get_value_by_name (const char *name) const
   return get_root_region ()->get_value_by_name (identifier, *this);
 }
 
+/* FIXME.  */
+
+svalue_id
+region_model::convert_byte_offset_to_array_index (tree ptr_type,
+						  svalue_id offset_sid)
+{
+  gcc_assert (POINTER_TYPE_P (ptr_type));
+
+  if (tree offset_cst = maybe_get_constant (offset_sid))
+    {
+      tree elem_type = TREE_TYPE (ptr_type);
+
+      /* This might not be a constant.  */
+      tree byte_size = size_in_bytes (elem_type);
+
+      tree index
+	= fold_build2 (TRUNC_DIV_EXPR, integer_type_node,
+		       offset_cst, byte_size);
+
+      if (CONSTANT_CLASS_P (index))
+	return get_or_create_constant_svalue (index);
+    }
+
+  /* Otherwise, we don't know the array index; generate a new unknown value.
+     FIXME: do we need to capture the relationship between two unknown
+     values (the offset and the index)?  */
+  return add_svalue (new unknown_svalue (integer_type_node));
+}
+
 /* Get a region of type TYPE for PTR_SID[OFFSET_SID/sizeof (*PTR_SID)].
 
    If OFFSET_SID is known to be zero, then dereference PTR_SID.
@@ -6184,9 +6417,6 @@ region_model::get_or_create_mem_ref (tree type,
 				     svalue_id offset_sid,
 				     region_model_context *ctxt)
 {
-  // FIXME: lose this assert eventually
-  gcc_assert (!POINTER_TYPE_P (type));
-
   svalue *ptr_sval = get_svalue (ptr_sid);
   tree ptr_type = ptr_sval->get_type ();
   gcc_assert (ptr_type);
@@ -6219,31 +6449,45 @@ region_model::get_or_create_mem_ref (tree type,
 	{
 	  // TODO: check we have the correct parent type
 	  array_region *parent_array = get_region <array_region> (parent_rid);
-	  tree key_for_raw_rid
-	    = parent_array->get_tree_for_child_region (raw_rid);
-	  if (key_for_raw_rid)
+	  array_region::key_t key_for_raw_rid;
+	  if (parent_array->get_key_for_child_region (raw_rid,
+						      &key_for_raw_rid))
 	    {
-	      tree key_relative_to_parent
-		= fold_build2 (PLUS_EXPR, TREE_TYPE (key_for_raw_rid),
-			       key_for_raw_rid, cst_sval->get_constant ());
-
-	      /* Carry on, using the parent region and adjusted offset.  */
-	      raw_rid = parent_rid;
-	      offset_sid
-		= add_svalue (new constant_svalue (key_relative_to_parent));
+	      /* FIXME: the key is an index, not an offset; we need to
+		 convert cst_svals to an index first, and we get
+		 an index.  */
+	      svalue_id index_sid
+		= convert_byte_offset_to_array_index (ptr_type, offset_sid);
+	      if (tree index_cst
+		    = get_svalue (index_sid)->maybe_get_constant ())
+		{
+		  array_region::key_t index_offset
+		    = array_region::key_from_constant (index_cst);
+		  array_region::key_t index_rel_to_parent
+		    = key_for_raw_rid + index_offset;
+		  tree index_rel_to_parent_cst
+		    = wide_int_to_tree (integer_type_node, // FIXME
+					index_rel_to_parent);
+		  svalue_id index_sid
+		    = get_or_create_constant_svalue (index_rel_to_parent_cst);
+
+		  /* Carry on, using the parent region and adjusted index.  */
+		  region_id element_rid
+		    = parent_array->get_element (this, raw_rid, index_sid,
+						 ctxt);
+		  return get_or_create_view (element_rid, type);
+		}
 	    }
 	}
     }
 
-  /* We now need a view as an array region of raw_rid, so we
-     can do the offset.  */
   tree array_type = build_array_type (TREE_TYPE (ptr_type),
 				      integer_type_node); // FIXME
   region_id array_view_rid = get_or_create_view (raw_rid, array_type);
   array_region *array_reg = get_region <array_region> (array_view_rid);
 
-  /* Convert from offset in bytes to an array index.  */
-  svalue_id index_sid = offset_sid; // FIXME
+  svalue_id index_sid
+    = convert_byte_offset_to_array_index (ptr_type, offset_sid);
 
   region_id element_rid
     = array_reg->get_element (this, array_view_rid, index_sid, ctxt);
@@ -6251,6 +6495,22 @@ region_model::get_or_create_mem_ref (tree type,
   return get_or_create_view (element_rid, type);
 }
 
+/* FIXME.  */
+
+region_id
+region_model::get_element_FIXME (tree type,
+				 tree ptr_type,
+				 region_id raw_rid,
+				 svalue_id index_sid,
+				 region_model_context *ctxt)
+{
+  // FIXME
+  /* We now need a view as an array region of raw_rid, so we
+     can do the offset.  */
+
+}
+
+
 /* Get a region of type TYPE for PTR_SID + OFFSET_SID.
 
    If OFFSET_SID is known to be zero, then dereference PTR_SID.
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 535cfd7..6706e97 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -804,11 +804,11 @@ enum region_kind
   RK_PRIMITIVE,
   RK_STRUCT,
   RK_UNION,
-  RK_ARRAY,
   RK_FRAME,
   RK_GLOBALS,
   RK_CODE,
   RK_FUNCTION,
+  RK_ARRAY,
   RK_STACK,
   RK_HEAP,
   RK_ROOT,
@@ -828,12 +828,12 @@ extern const char *region_kind_to_str (enum region_kind);
        struct_or_union_region
          struct_region (RK_STRUCT)
          union_region (RK_UNION)
-       array_region (RK_ARRAY)
        scope_region
          frame_region (RK_FRAME)
          globals_region (RK_GLOBALS)
        code_region (RK_CODE)
        function_region (RK_FUNCTION)
+     array_region (RK_ARRAY)
      stack_region (RK_STACK)
      heap_region (RK_HEAP)
      root_region (RK_ROOT)
@@ -864,6 +864,7 @@ public:
   virtual region *clone () const = 0;
 
   virtual enum region_kind get_kind () const = 0;
+  virtual map_region *dyn_cast_map_region () { return NULL; }
 
   region_id get_parent () const { return m_parent_rid; }
   region *get_parent_region (const region_model &model) const;
@@ -974,6 +975,8 @@ public:
   {}
   map_region (const map_region &other);
 
+  map_region *dyn_cast_map_region () FINAL OVERRIDE { return this; }
+
   void dump_dot_to_pp (const region_model &model,
 		       region_id this_rid,
 		       pretty_printer *pp) const
@@ -1033,13 +1036,11 @@ template <>
 inline bool
 is_a_helper <map_region *>::test (region *reg)
 {
-  return (reg->get_kind () == RK_STRUCT
-	  || reg->get_kind () == RK_ARRAY
-	  || reg->get_kind () == RK_FRAME
-	  || reg->get_kind () == RK_GLOBALS);
+  return (reg->dyn_cast_map_region () != NULL);
 }
 
-/* FIXME.  A region with fields.  */
+/* Abstract subclass representing a region with fields
+   (either a struct or a union).  */
 
 class struct_or_union_region : public map_region
 {
@@ -1117,42 +1118,6 @@ is_a_helper <union_region *>::test (region *reg)
   return reg->get_kind () == RK_UNION;
 }
 
-/* Concrete region subclass.  A map_region representing an array, using
-   INTEGER_CSTs for its keys.  */
-
-class array_region : public map_region
-{
-public:
-  array_region (region_id parent_rid, tree type)
-  : map_region (parent_rid, type)
-  {
-    gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
-  }
-  array_region (const array_region &other);
-
-  /* region vfuncs.  */
-  region *clone () const FINAL OVERRIDE;
-  enum region_kind get_kind () const FINAL OVERRIDE { return RK_ARRAY; }
-
-  /* map_region vfunc.  */
-  bool valid_key_p (tree key) const FINAL OVERRIDE;
-
-  region_id get_element (region_model *model,
-			 region_id this_rid,
-			 svalue_id index_sid,
-			 region_model_context *ctxt);
-
-  bool compare_fields (const array_region &other) const;
-};
-
-template <>
-template <>
-inline bool
-is_a_helper <array_region *>::test (region *reg)
-{
-  return reg->get_kind () == RK_ARRAY;
-}
-
 /* Abstract map_region subclass for accessing decls, used as a base class
    for function frames and for the globals region.  */
 
@@ -1328,6 +1293,106 @@ is_a_helper <function_region *>::test (region *reg)
   return reg->get_kind () == RK_FUNCTION;
 }
 
+/* Concrete region subclass representing an array (or an array-like view
+   of a parent region of memory.
+   This can't be a map_region as we can't use trees as the keys: there's
+   no guarantee about the uniqueness of an INTEGER_CST.  */
+
+class array_region : public region
+{
+public:
+#if 0
+  wide_int m_test;
+
+  typedef wide_int key_t;
+  typedef int_hash <wide_int, -1, -2> hash_t;
+  typedef ordered_hash_map<hash_t, region_id> map_t;
+#else
+  typedef int key_t;
+  typedef int_hash <int, -1, -2> int_hash_t;
+  typedef ordered_hash_map<int_hash_t, region_id> map_t;
+#endif
+  typedef map_t::iterator iterator_t;
+
+  array_region (region_id parent_rid, tree type)
+  : region (parent_rid, svalue_id::null (), type)
+  {
+    gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
+  }
+  array_region (const array_region &other);
+
+  void dump_dot_to_pp (const region_model &model,
+		       region_id this_rid,
+		       pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  void dump_child_label (const region_model &model,
+			 region_id this_rid,
+			 region_id child_rid,
+			 pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  /* region vfuncs.  */
+  region *clone () const FINAL OVERRIDE;
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_ARRAY; }
+
+  region_id get_element (region_model *model,
+			 region_id this_rid,
+			 svalue_id index_sid,
+			 region_model_context *ctxt);
+
+  bool compare_fields (const array_region &other) const;
+
+  static bool can_merge_p (const array_region *array_region_a,
+			   const array_region *array_region_b,
+			   array_region *merged_array_region,
+			   region_id merged_rid,
+			   model_merger *merger);
+
+  void walk_for_canonicalization (canonicalization *c) const FINAL OVERRIDE;
+
+  iterator_t begin () { return m_map.begin (); }
+  iterator_t end () { return m_map.end (); }
+  size_t elements () const { return m_map.elements (); }
+
+  region_id get_or_create (region_model *model,
+			   region_id this_rid,
+			   key_t key, tree type);
+//  void unbind (int expr);
+  region_id *get (key_t key);
+
+  void remap_region_ids (const region_id_map &map) FINAL OVERRIDE;
+
+  bool get_key_for_child_region (region_id child_rid,
+				 key_t *out) const;
+
+#if 0
+  bool get_key_for_child_region (region *child,
+				 const region_model &model,
+				 key_t *out) const;
+#endif
+
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+  void print_fields (const region_model &model,
+		     region_id this_rid,
+		     pretty_printer *pp) const
+    OVERRIDE;
+
+  static key_t key_from_constant (tree cst);
+
+ private:
+  /* Mapping from tree to child region.  */
+  map_t m_map;
+};
+
+template <>
+template <>
+inline bool
+is_a_helper <array_region *>::test (region *reg)
+{
+  return reg->get_kind () == RK_ARRAY;
+}
+
 /* Concrete region subclass representing a stack, containing all stack
    frames, and implicitly providing a POISON_KIND_UNINIT value to all
    child regions by default.  */
@@ -1711,10 +1776,18 @@ class region_model
 
   svalue_id get_value_by_name (const char *name) const;
 
+  svalue_id convert_byte_offset_to_array_index (tree ptr_type,
+						svalue_id offset_sid);
+
   region_id get_or_create_mem_ref (tree type,
 				   svalue_id ptr_sid,
 				   svalue_id offset_sid,
 				   region_model_context *ctxt);
+  region_id get_element_FIXME (tree type,
+			       tree ptr_type,
+			       region_id raw_rid,
+			       svalue_id index_sid,
+			       region_model_context *ctxt);
   region_id get_or_create_pointer_plus_expr (tree type,
 					     svalue_id ptr_sid,
 					     svalue_id offset_sid,
-- 
1.8.5.3

