From d8dfb56a8e917c82531aa678c0383550c8331c3f Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 30 Oct 2019 16:48:42 -0400
Subject: [PATCH 091/119] FIXME: implement pointer arithmetic

---
 gcc/analyzer/region-model.cc                 | 226 ++++++++++++++++++---------
 gcc/analyzer/region-model.h                  |  19 ++-
 gcc/testsuite/gcc.dg/analyzer/data-model-1.c |  55 +++++++
 3 files changed, 220 insertions(+), 80 deletions(-)

diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index dd540c4..bab278f 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -111,16 +111,7 @@ class impl_constraint_manager : public constraint_manager
   tree maybe_get_constant (svalue_id sid) const FINAL OVERRIDE
   {
     svalue *svalue = m_model->get_svalue (sid);
-    if (constant_svalue *cst_svalue = svalue->dyn_cast_constant_svalue ())
-      {
-	tree cst = cst_svalue->get_constant ();
-	// FIXME:
-	/* This could be a constant or a decl; constraint_manager is only
-	   expecting actual constants.  */
-	if (CONSTANT_CLASS_P (cst))
-	  return cst;
-      }
-    return NULL_TREE;
+    return svalue->maybe_get_constant ();
   }
 
   svalue_id get_sid_for_constant (tree cst) const FINAL OVERRIDE
@@ -359,6 +350,18 @@ svalue::get_child_sid (region *parent, region *child,
   return new_child_sid;
 }
 
+/* If this svalue is a constant_svalue, return the underlying tree constant.
+   Otherwise return NULL_TREE.  */
+
+tree
+svalue::maybe_get_constant () const
+{
+  if (const constant_svalue *cst_sval = dyn_cast_constant_svalue ())
+    return cst_sval->get_constant ();
+  else
+    return NULL_TREE;
+}
+
 /* class region_svalue : public svalue.  */
 
 /* Optimized allocator for region_svalue instances.  */
@@ -1066,6 +1069,14 @@ region::operator== (const region &other) const
     }
 }
 
+/* FIXME.  */
+
+region *
+region::get_parent_region (const region_model &model) const
+{
+  return model.get_region (m_parent_rid);
+}
+
 /* Set this region's value to RHS_SID (or potentially a variant of it,
    for some kinds of casts).  */
 
@@ -2241,9 +2252,8 @@ array_region::get_element (region_model *model,
 {
   tree element_type = TREE_TYPE (get_type ());
   svalue *index_sval = model->get_svalue (index_sid);
-  if (constant_svalue *cst_index_sval = index_sval->dyn_cast_constant_svalue ())
+  if (tree cst_index = index_sval->maybe_get_constant ())
     {
-      tree cst_index = cst_index_sval->get_constant ();
       region_id element_rid
 	= get_or_create (model, this_rid, cst_index, element_type);
       return element_rid;
@@ -4002,38 +4012,18 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt)
 	tree ptr = rhs1;
 	tree offset = gimple_assign_rhs2 (assign);
 
-	//region_id buffer_rid = deref_rvalue (ptr, ctxt);
-#if 1
 	svalue_id ptr_sid = get_rvalue (ptr, ctxt);
 	svalue_id offset_sid = get_rvalue (offset, ctxt);
 	/* FIXME: offset is in bytes; we presumably want to
 	   map back to an index.  */
-#endif
-	region_id view_rid = get_or_create_view (TREE_TYPE (lhs),
-						 ptr_sid, offset_sid, ctxt);
-
-	svalue_id element_ptr_sid
-	  = get_or_create_ptr_svalue (TREE_TYPE (ptr), view_rid);
-	set_value (lhs_rid, element_ptr_sid);
-
-#if 0
-	map_region *array_region = get_region<map_region> (buffer_rid);
-	tree element_type = TREE_TYPE (TREE_TYPE (ptr));
-#if 0
-	tree index = offset;
-	/* FIXME: offset is in bytes; we presumably want to
-	   map back to an index.  */
-#endif
 
 	region_id element_rid
-	  = array_region->get_or_create (this, buffer_rid, offset,
-					 element_type);
+	  = get_or_create_pointer_plus_expr (TREE_TYPE (TREE_TYPE (ptr)),
+					     ptr_sid, offset_sid,
+					     ctxt);
 	svalue_id element_ptr_sid
 	  = get_or_create_ptr_svalue (TREE_TYPE (ptr), element_rid);
 	set_value (lhs_rid, element_ptr_sid);
-
-	//gcc_unreachable ();
-#endif
       }
       break;
 
@@ -4457,24 +4447,9 @@ region_model::get_lvalue_1 (path_var pv, region_model_context *ctxt)
 #endif
 
 	region_id array_rid = get_lvalue (array, ctxt);
-#if 1
 	svalue_id index_sid = get_rvalue (index, ctxt);
-#endif
 	array_region *array_reg = get_region<array_region> (array_rid);
-#if 1
 	return array_reg->get_element (this, array_rid, index_sid, ctxt);
-#else
-	tree element_type = TREE_TYPE (TREE_TYPE (array));
-
-	// 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 (this, array_rid, index, element_type);
-	/* FIXME: ^^^ should be indexed by svalue, rather than by tree?
-	   What about unknown indices?  */
-	return element_rid;
-#endif
       }
       break; // FIXME
 
@@ -4482,15 +4457,10 @@ region_model::get_lvalue_1 (path_var pv, region_model_context *ctxt)
       {
 	tree ptr = TREE_OPERAND (expr, 0);
 	tree offset = TREE_OPERAND (expr, 1);
-	// FIXME: what about nonzero offset?
 	svalue_id ptr_sid = get_rvalue (ptr, ctxt);
 	svalue_id offset_sid = get_rvalue (offset, ctxt);
-
-#if 1
-	return get_or_create_view (TREE_TYPE (expr), ptr_sid, offset_sid, ctxt);
-#else
-	return deref_rvalue (ptr, ctxt);
-#endif
+	return get_or_create_mem_ref (TREE_TYPE (expr), ptr_sid,
+				      offset_sid, ctxt);
       }
       break; // FIXME
 
@@ -4680,14 +4650,15 @@ region_model::get_or_create_ptr_svalue (tree ptr_type, region_id rid)
 svalue_id
 region_model::get_or_create_constant_svalue (tree cst_expr)
 {
+  gcc_assert (cst_expr);
+
   /* Reuse one if it already exists.  */
   // FIXME: maybe store a map, rather than do linear search?
   int i;
   svalue *svalue;
   FOR_EACH_VEC_ELT (m_svalues, i, svalue)
-    if (constant_svalue *cst_svalue = svalue->dyn_cast_constant_svalue ())
-      if (cst_svalue->get_constant () == cst_expr)
-	return svalue_id::from_int (i);
+    if (svalue->maybe_get_constant () == cst_expr)
+      return svalue_id::from_int (i);
 
   svalue_id cst_sid = add_svalue (new constant_svalue (cst_expr));
   return cst_sid;
@@ -4824,10 +4795,8 @@ region_model::maybe_cast (tree dst_type, svalue_id sid)
     }
 
   /* Attempt to cast constants.  */
-  if (constant_svalue *cst_sval = sval->dyn_cast_constant_svalue ())
+  if (tree src_cst = sval->maybe_get_constant ())
     {
-      tree src_cst = cst_sval->get_constant ();
-      // FIXME: maybe introduce a helper for this?
 
 #if 1
       //tree dst = fold_convert (dst_type, src_cst);
@@ -5216,9 +5185,7 @@ region_model::maybe_get_constant (svalue_id sid) const
 {
   gcc_assert (!sid.null_p ());
   svalue *sval = get_svalue (sid);
-  if (constant_svalue *cst_sval = sval->dyn_cast_constant_svalue ())
-    return cst_sval->get_constant ();
-  return NULL_TREE;
+  return sval->maybe_get_constant ();
 }
 
 /* Create a new child region of the heap (creating the heap region if
@@ -6205,27 +6172,134 @@ region_model::get_value_by_name (const char *name) const
   return get_root_region ()->get_value_by_name (identifier, *this);
 }
 
-/* FIXME.  */
-/* might add a new region to MODEL, or return the given region.
-   (might be easier if we let regions know their own IDs).  */
+/* 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.
+   Otherwise, impose a view of "typeof(*PTR_SID)[]" on *PTR_SID,
+   and then get a view of type TYPE on the relevant array element.  */
 
 region_id
-region_model::get_or_create_view (tree type,
-				  svalue_id ptr_sid,
-				  svalue_id offset_sid,
-				  region_model_context *ctxt)
+region_model::get_or_create_mem_ref (tree type,
+				     svalue_id ptr_sid,
+				     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);
+
   region_id raw_rid = deref_rvalue (ptr_sid, ctxt);
 
-  // TODO: what about offset_sid
+  svalue *offset_sval = get_svalue (offset_sid);
+  tree offset_type = offset_sval->get_type ();
+  gcc_assert (offset_type);
+
+  if (constant_svalue *cst_sval = offset_sval->dyn_cast_constant_svalue ())
+    {
+      if (zerop (cst_sval->get_constant ()))
+	{
+	  /* Handle the zero offset case.  */
+	  return get_or_create_view (raw_rid, type);
+	}
+
+      /* If we're already within an array of the correct type,
+	 then we want to reuse that array, rather than starting
+	 a new view.
+	 If so, figure out our raw_rid's offset from its parent,
+	 if we can, and use that to offset OFFSET_SID, and create
+	 the element within the parent region.  */
+      region *raw_reg = get_region (raw_rid);
+      region_id parent_rid = raw_reg->get_parent ();
+      tree parent_type = get_region (parent_rid)->get_type ();
+      if (parent_type
+	  && TREE_CODE (parent_type) == ARRAY_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)
+	    {
+	      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));
+	    }
+	}
+    }
+
+  /* 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
 
-  return get_or_create_view (raw_rid, type);
+  region_id element_rid
+    = array_reg->get_element (this, array_view_rid, index_sid, ctxt);
+
+  return get_or_create_view (element_rid, type);
 }
 
-/* FIXME.  */
+/* Get a region of type TYPE for PTR_SID + OFFSET_SID.
+
+   If OFFSET_SID is known to be zero, then dereference PTR_SID.
+   Otherwise, impose a view of "typeof(*PTR_SID)[]" on *PTR_SID,
+   and then get a view of type TYPE on the relevant array element.
+   FIXME: divide by the array size?  */
+
+region_id
+region_model::get_or_create_pointer_plus_expr (tree type,
+					       svalue_id ptr_sid,
+					       svalue_id offset_in_bytes_sid,
+					       region_model_context *ctxt)
+{
+  // FIXME:
+  svalue *ptr_sval = get_svalue (ptr_sid);
+  svalue *offset_in_bytes_sval = get_svalue (offset_in_bytes_sid);
+  svalue_id offset_in_elements_sid;
+#if 0
+  if (tree offset_in_bytes_cst = maybe_get_constant (offset_in_bytes_sid))
+    {
+      //tree offset_in_elements_cst = ;
+      offset_in_elements_sid
+	= add_svalue (new unknown_svalue (offset_in_bytes_sval->get_type ()));
+      gcc_unreachable ();
+    }
+  else
+    {
+      /* FIXME.  */
+      offset_in_elements_sid
+	= add_svalue (new unknown_svalue (offset_in_bytes_sval->get_type ()));
+      gcc_unreachable ();
+    }
+
+  gcc_unreachable ();
+
+  return get_or_create_mem_ref (type,
+				ptr_sid,
+				offset_in_elements_sid,
+				ctxt);
+#else
+  return get_or_create_mem_ref (type,
+				ptr_sid,
+				offset_in_bytes_sid,
+				ctxt);
+#endif
+}
+
+/* Get or create a view of type TYPE of the region with id RAW_ID.
+   Return the id of the view (or RAW_ID if it of the same type).  */
 
 region_id
 region_model::get_or_create_view (region_id raw_rid, tree type)
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index e7b68b9..535cfd7 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -516,6 +516,8 @@ public:
 
   virtual region_svalue *dyn_cast_region_svalue () { return NULL; }
   virtual constant_svalue *dyn_cast_constant_svalue () { return NULL; }
+  virtual const constant_svalue *dyn_cast_constant_svalue () const
+  { return NULL; }
   virtual poisoned_svalue *dyn_cast_poisoned_svalue () { return NULL; }
   virtual unknown_svalue *dyn_cast_unknown_svalue () { return NULL; }
   virtual setjmp_svalue *dyn_cast_setjmp_svalue () { return NULL; }
@@ -528,6 +530,8 @@ public:
 				   region_model &model,
 				   region_model_context *ctxt);
 
+  tree maybe_get_constant () const;
+
  protected:
   svalue (tree type) : m_type (type) {}
 
@@ -629,6 +633,8 @@ public:
   hashval_t hash () const FINAL OVERRIDE;
 
   constant_svalue *dyn_cast_constant_svalue () FINAL OVERRIDE { return this; }
+  const constant_svalue *dyn_cast_constant_svalue () const FINAL OVERRIDE
+  { return this; }
 
   tree get_constant () const { return m_cst_expr; }
 
@@ -860,6 +866,7 @@ public:
   virtual enum region_kind get_kind () const = 0;
 
   region_id get_parent () const { return m_parent_rid; }
+  region *get_parent_region (const region_model &model) const;
 
   void set_value (region_model &model, region_id this_rid, svalue_id rhs_sid);
   svalue_id get_value (region_model &model, bool non_null,
@@ -1704,10 +1711,14 @@ class region_model
 
   svalue_id get_value_by_name (const char *name) const;
 
-  region_id get_or_create_view (tree type,
-				svalue_id ptr_sid,
-				svalue_id offset_sid,
-				region_model_context *ctxt);
+  region_id get_or_create_mem_ref (tree type,
+				   svalue_id ptr_sid,
+				   svalue_id offset_sid,
+				   region_model_context *ctxt);
+  region_id get_or_create_pointer_plus_expr (tree type,
+					     svalue_id ptr_sid,
+					     svalue_id offset_sid,
+					     region_model_context *ctxt);
   region_id get_or_create_view (region_id raw_rid, tree type);
 
  private:
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
index 2618e40..869671e 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
@@ -576,6 +576,61 @@ void test_29 (struct coord *p)
 
   __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */
   __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */
+
+  q -= 2;
+
+  __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+}
+
+void test_29a (struct coord p[])
+{
+  struct coord *q;
+
+  p[0].x = 100024;
+  p[0].y = 100025;
+
+  p[7].x = 107024;
+  p[7].y = 107025;
+
+  p[9].x = 109024;
+  p[9].y = 109025;
+
+  __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */
+
+  q = &p[7];
+
+  __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" } */
+
+  q += 2;
+
+  __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */
+
+  q -= 2;
+
+  __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
 }
 
 void test_30 (void *ptr)
-- 
1.8.5.3

