From 642b4266bd0abb3b727f3e54384fc8415dbde4cc Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 14 Apr 2020 19:16:51 -0400
Subject: [PATCH 037/293] FIXME: implement offsets for fields (needed to use
 bits rather than bytes)

---
 gcc/analyzer/region-model2.cc | 76 +++++++++++++++++++++--------------
 gcc/analyzer/region-model2.h  | 30 +++++++++-----
 2 files changed, 64 insertions(+), 42 deletions(-)

diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index ccc94cc76b3..152800871ce 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -868,7 +868,7 @@ region_offset
 region2::calc_offset () const
 {
   const region2 *iter_region = this;
-  region_offset::byte_offset_t byte_offset = 0;
+  region_offset::bit_offset_t accum_bit_offset = 0;
 
   while (iter_region)
     {
@@ -879,8 +879,17 @@ region2::calc_offset () const
 	    const field_region2 *field_region
 	      = (const field_region2 *)iter_region;
 	    iter_region = iter_region->get_parent_region ();
-	    tree field_offset = DECL_FIELD_OFFSET (field_region->get_field ());
-	    byte_offset += tree_to_shwi (field_offset);
+
+	    /* Compare with e.g. gimple-fold.c's
+	       fold_nonarray_ctor_reference.  */
+	    tree field = field_region->get_field ();
+	    tree byte_offset = DECL_FIELD_OFFSET (field);
+	    tree field_offset = DECL_FIELD_BIT_OFFSET (field);
+	    /* Compute bit offset of the field.  */
+	    offset_int bitoffset
+	      = (wi::to_offset (field_offset)
+		 + (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT));
+	    accum_bit_offset += bitoffset;
 	  }
 	  continue;
 
@@ -906,16 +915,18 @@ region2::calc_offset () const
 		  return offset_sid;
 #endif
 
-		HOST_WIDE_INT hwi_idx_cst = tree_to_shwi (idx_cst);
+		offset_int element_idx = wi::to_offset (idx_cst);
 
 		/* First, use int_size_in_bytes, to reject the case where we
 		   have an incomplete type, or a non-constant value.  */
 		HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (elem_type);
 		if (hwi_byte_size > 0)
 		  {
-		    HOST_WIDE_INT hwi_element_byte_offset
-		      = hwi_idx_cst * hwi_byte_size;
-		    byte_offset += hwi_element_byte_offset;
+		    offset_int element_bit_size
+		      = hwi_byte_size << LOG2_BITS_PER_UNIT;
+		    offset_int element_bit_offset
+		      = element_idx * element_bit_size;
+		    accum_bit_offset += element_bit_offset;
 		  }
 		else
 		  gcc_unreachable ();
@@ -929,9 +940,10 @@ region2::calc_offset () const
 	  continue;
 
 	default:
-	  return region_offset (iter_region, byte_offset);
+	  return region_offset (iter_region, accum_bit_offset);
 	}
     }
+  return region_offset (iter_region, accum_bit_offset);
 }
 
 /* Set this region2's value to RHS_SVAL (or potentially a variant of it,
@@ -2895,7 +2907,7 @@ binding_key::make (region_model2_manager *mgr, region2 *r, enum kind kind)
   region_offset offset = r->get_offset ();
   // FIXME: what if symbolic vs concrete?
   return mgr->get_concrete_binding (offset.get_base_region (), kind,
-				    offset.get_byte_offset ());
+				    offset.get_bit_offset ());
 }
 
 /* FIXME.  */
@@ -2927,8 +2939,9 @@ binding_key::cmp (const binding_key *k1, const binding_key *k2)
     return concrete_cmp;
   if (concrete1)
     {
-      // TODO
-      gcc_unreachable ();
+      const concrete_binding *b1 = (const concrete_binding *)k1;
+      const concrete_binding *b2 = (const concrete_binding *)k2;
+      return wi::cmp (b1->get_bit_offset (), b2->get_bit_offset (), SIGNED);
     }
   else
     {
@@ -3086,7 +3099,7 @@ region_model2_manager::get_element_region (region2 *parent,
 const concrete_binding *
 region_model2_manager::get_concrete_binding (const region2 *region,
 					     enum binding_key::kind kind,
-					     concrete_binding::offset_t offset)
+					     concrete_binding::bit_offset_t offset)
 {
   concrete_binding *b = new concrete_binding (region, kind, offset);
   return m_concrete_binding_key_mgr.consolidate (b);
@@ -7095,7 +7108,7 @@ test_dump_2 ()
     region2 *c_x_reg = model.get_lvalue (&mgr, c_x, NULL);
     region_offset offset = c_x_reg->get_offset ();
     ASSERT_EQ (offset.get_base_region (), model.get_lvalue (&mgr, c, NULL));
-    ASSERT_EQ (offset.get_byte_offset (), 0);
+    ASSERT_EQ (offset.get_bit_offset (), 0);
   }
 
   /* Verify get_offset for "c.y".  */
@@ -7103,29 +7116,20 @@ test_dump_2 ()
     region2 *c_y_reg = model.get_lvalue (&mgr, c_y, NULL);
     region_offset offset = c_y_reg->get_offset ();
     ASSERT_EQ (offset.get_base_region (), model.get_lvalue (&mgr, c, NULL));
-#if 0
-    /* FIXME: this is failing; the DECL_FIELD_OFFSET of field_y is 0
-       for some reason, even after layout_type.  */
-    ASSERT_EQ (offset.get_byte_offset (), 4);
-#endif
-    // FIXME: this assumes sizeof(int) == 4 on the target
+    ASSERT_EQ (offset.get_bit_offset (), INT_TYPE_SIZE);
   }
 
   // FIXME: test of casting to an "int *"
 
   // FIXME: rename this test; it's doing much more that just dumps
 
-  /* FIXME: fails due to lack of sorting!
-     ...but time to implement a[0] = ; a[1] = ; a[i] = ;
-     and a more sophisticated region binding/cluster implementation.  */
-
   /* Simplified dump.  */
 #if 0
   ASSERT_DUMP_EQ (model, true,
 		  "base region: {c} has cluster: {"
-		  "binding key: {region: c.x, kind: direct, offset: 0},"
+		  "binding key: {region: c, kind: direct, offset: 0},"
 		  " value: {17},"
-		  " binding key: {region: c.y, kind: direct, offset: 0},"
+		  " binding key: {region: c, kind: direct, offset: 32},"
 		  " value: {-3}"
 		  "}");
 #endif
@@ -8344,6 +8348,7 @@ test_array ()
 		       arr, i, NULL_TREE, NULL_TREE);
 
   tree int_17 = build_int_cst (integer_type_node, 17);
+  tree int_42 = build_int_cst (integer_type_node, 42);
   tree int_m3 = build_int_cst (integer_type_node, -3);
 
   region_model2_manager mgr;
@@ -8358,12 +8363,17 @@ test_array ()
   ASSERT_EQ (model.get_rvalue (&mgr, arr_1, NULL),
 	     model.get_rvalue (&mgr, int_m3, NULL));
 
+  /* Overwrite a pre-existing binding: "arr[1] = 42;".  */
+  model.set_value (&mgr, arr_1, int_42, NULL);
+  ASSERT_EQ (model.get_rvalue (&mgr, arr_1, NULL),
+	     model.get_rvalue (&mgr, int_42, NULL));
+
   /* Verify get_offset for "arr[0]".  */
   {
     region2 *arr_0_reg = model.get_lvalue (&mgr, arr_0, NULL);
     region_offset offset = arr_0_reg->get_offset ();
     ASSERT_EQ (offset.get_base_region (), model.get_lvalue (&mgr, arr, NULL));
-    ASSERT_EQ (offset.get_byte_offset (), 0);
+    ASSERT_EQ (offset.get_bit_offset (), 0);
   }
 
   /* Verify get_offset for "arr[1]".  */
@@ -8371,18 +8381,22 @@ test_array ()
     region2 *arr_1_reg = model.get_lvalue (&mgr, arr_1, NULL);
     region_offset offset = arr_1_reg->get_offset ();
     ASSERT_EQ (offset.get_base_region (), model.get_lvalue (&mgr, arr, NULL));
-    ASSERT_EQ (offset.get_byte_offset (), 4);
-    // FIXME: this assumes sizeof(int) == 4 on the target
+    ASSERT_EQ (offset.get_bit_offset (), INT_TYPE_SIZE);
   }
 
-  // etc
-
-
 #if 0
   /* "arr[i] = i;" - this should remove the earlier bindings.  */
   model.set_value (&mgr, arr_i, i, NULL);
+  ASSERT_EQ (model.get_rvalue (&mgr, arr_i, NULL),
+	     model.get_rvalue (&mgr, i, NULL));
   // TODO: get_value for other bindings
   // TODO: assert that there is now no binding for the other indices
+
+
+  /* "arr[0] = 17;" - this should remove the arr[i] binding.  */
+  model.set_value (&mgr, arr_0, int_17, NULL);
+  ASSERT_EQ (model.get_rvalue (&mgr, arr_0, NULL),
+	     model.get_rvalue (&mgr, int_17, NULL));
 #endif
 
   /* Simplified dump.  */
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index fc90b423ef5..80d1dac9a8d 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -54,7 +54,12 @@ along with GCC; see the file COPYING3.  If not see
    Other ideas:
    - region subclasses no longer represent internal structure, but instead
      represent how the regions are reached.  So e.g. a global
-     "struct coord c;" is now a decl_region, rather than a struct_region.  */
+     "struct coord c;" is now a decl_region, rather than a struct_region.
+
+   TODO:
+   - some selftests depend on bit sizes of types, hopefully via the TYPE_SIZE
+     macros (see e.g. INT_TYPE_SIZE in defaults.h).  Remember to test on
+     some other targets.  Similarly, big-endian vs little-endian.  */
 
 /* A tree, extended with stack frame information for locals, so that
    we can distinguish between different values of locals within a potentially
@@ -495,18 +500,18 @@ public:
 class region_offset
 {
 public:
-  typedef int byte_offset_t;  // FIXME
+  typedef offset_int bit_offset_t;
 
-  region_offset (const region2 *base_region, byte_offset_t offset)
+  region_offset (const region2 *base_region, bit_offset_t offset)
   : m_base_region (base_region), m_offset (offset)
   {}
 
   const region2 *get_base_region () const { return m_base_region; }
-  byte_offset_t get_byte_offset () const { return m_offset; }
+  bit_offset_t get_bit_offset () const { return m_offset; }
 
 private:
   const region2 *m_base_region;
-  byte_offset_t m_offset;
+  bit_offset_t m_offset;
   // FIXME: also need a "symbolic" value
 };
 
@@ -1304,16 +1309,16 @@ private:
 class concrete_binding : public binding_key
 {
 public:
-  typedef unsigned offset_t;
+  typedef region_offset::bit_offset_t bit_offset_t;
 
-  concrete_binding (const region2 *region, enum kind kind, offset_t offset)
+  concrete_binding (const region2 *region, enum kind kind, bit_offset_t offset)
   : binding_key (region, kind), m_offset (offset)
   {}
   bool concrete_p () const FINAL OVERRIDE { return true; }
 
   hashval_t hash () const
   {
-    return binding_key::impl_hash () ^ m_offset;
+    return binding_key::impl_hash (); // FIXME: ^ m_offset;
   }
   bool operator== (const concrete_binding &other)
   {
@@ -1325,11 +1330,14 @@ public:
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE
   {
     binding_key::dump_to_pp (pp, simple);
-    pp_printf (pp, ", offset: %i", m_offset);
+    pp_string (pp, ", offset: ");
+    pp_wide_int (pp, m_offset, SIGNED);
   }
 
+  bit_offset_t get_bit_offset () const { return m_offset; }
+
 private:
-  offset_t m_offset;
+  bit_offset_t m_offset;
 };
 
 class symbolic_binding : public binding_key
@@ -1424,7 +1432,7 @@ public:
   const concrete_binding *
   get_concrete_binding (const region2 *region,
 			enum binding_key::kind kind,
-			concrete_binding::offset_t offset);
+			concrete_binding::bit_offset_t offset);
 
   const symbolic_binding *
   get_symbolic_binding (const region2 *region,
-- 
2.26.2

