From da2b791d85491764194d3697b80fa9edf8e73c59 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Sun, 26 Apr 2020 06:42:57 -0400
Subject: [PATCH 074/293] FIXME: implement offset_region2; get msg[1] == 'e'
 working in test_16

---
 gcc/analyzer/analyzer.h                      |   1 +
 gcc/analyzer/region-model2.cc                | 176 +++++++++++++++++--
 gcc/analyzer/region-model2.h                 |  34 ++++
 gcc/testsuite/gcc.dg/analyzer/data-model-1.c |  12 +-
 4 files changed, 204 insertions(+), 19 deletions(-)

diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 3f816f536c3..d3968ed3322 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -66,6 +66,7 @@ class region2;
   class function_region2;
   class symbolic_region2;
   class element_region2;
+  class offset_region2;
   class field_region2;
   class string_region2;
 class region_model2_manager;
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index a3b7bada0f6..70cd8ddbb94 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -944,6 +944,14 @@ region2::operator== (const region2 &other) const
 	  = (const element_region2 &)other;
 	return this_sub.compare_fields (other_sub);
       }
+    case RK_OFFSET:
+      {
+	const offset_region2 &this_sub
+	  = (const offset_region2 &)*this;
+	const offset_region2 &other_sub
+	  = (const offset_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
     }
 }
 
@@ -959,6 +967,7 @@ region2::get_base_region () const
 	{
 	case RK_FIELD:
 	case RK_ELEMENT:
+	case RK_OFFSET:
 	  iter = iter->get_parent_region ();
 	  continue;
 	default:
@@ -1050,6 +1059,65 @@ region2::calc_offset () const
 	  }
 	  continue;
 
+	case RK_OFFSET:
+	  {
+	    const offset_region2 *offset_region
+	      = (const offset_region2 *)iter_region;
+	    iter_region = iter_region->get_parent_region ();
+
+	    if (tree byte_offset_cst
+		  = offset_region->get_byte_offset ()->maybe_get_constant ())
+	      {
+		gcc_assert (TREE_CODE (byte_offset_cst) == INTEGER_CST);
+		offset_int byte_offset = wi::to_offset (byte_offset_cst);
+		offset_int bit_offset = byte_offset << LOG2_BITS_PER_UNIT;
+		accum_bit_offset += bit_offset;
+	      }
+	    else
+	      return region_offset::make_symbolic (iter_region);
+	  }
+	  continue;
+
+#if 0
+	case RK_SYMBOLIC:
+	  {
+	    /* Get the offset of a region identified symbolically from a
+	       pointer svalue.  */
+	    const symbolic_region2 *symbolic_region
+	      = (const symbolic_region2 *)iter_region;
+	    svalue2 *sval = symbolic_region->get_pointer ();
+	    switch (sval->get_kind ())
+	      {
+	      default:
+		gcc_unreachable ();
+	      case svalue2::SK_BINOP:
+		{
+		  binop_svalue2 *binop_sval = as_a <binop_svalue2 *> (sval);
+		  switch (binop_sval->get_op ())
+		    {
+		    default:
+		      gcc_unreachable ();
+		    case POINTER_PLUS_EXPR:
+		      {
+			// FIXME:
+			/* If we have a symbolic value expressing pointer arithmentic,
+			   try to convert it to a suitable region.  */
+			/* FIXME: but should it be an element_region2?  that expresses
+			   things via element indices, rather than via byte offsets.  */
+			/* Perhaps instead we should capture this as an offset relative
+			   to a base region in region_offset?  */
+			svalue2 *ptr = binop_sval->get_arg0 ();
+			svalue2 *offset = binop_sval->get_arg1 ();
+			gcc_unreachable ();
+		      }
+		    }
+		}
+	      }
+	    gcc_unreachable ();
+	  }
+	  break;
+#endif
+
 	default:
 	  return region_offset::make_concrete (iter_region, accum_bit_offset);
 	}
@@ -2907,6 +2975,44 @@ element_region2::add_to_hash (inchash::hash &hstate) const
   hstate.add_ptr (m_index);
 }
 
+/* class offset_region2 : public region2.  */
+
+bool
+offset_region2::compare_fields (const offset_region2 &other) const
+{
+  return m_byte_offset == other.m_byte_offset;
+}
+
+void
+offset_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      //pp_string (pp, "(");
+      get_parent_region ()->dump_to_pp (pp, simple);
+      pp_string (pp, "+");
+      m_byte_offset->dump_to_pp (pp, simple);
+      //pp_string (pp, ")");
+    }
+  else
+    {
+      pp_string (pp, "offset_region2(");
+      get_parent_region ()->dump_to_pp (pp, simple);
+      pp_string (pp, ", ");
+      print_quoted_type (pp, get_type ());
+      pp_string (pp, ", ");
+      m_byte_offset->dump_to_pp (pp, simple);
+      pp_printf (pp, ")");
+    }
+}
+
+void
+offset_region2::add_to_hash (inchash::hash &hstate) const
+{
+  region2::add_to_hash (hstate);
+  hstate.add_ptr (m_byte_offset);
+}
+
 /* class heap_allocated_region2 : public region2.  */
 
 void
@@ -3138,6 +3244,25 @@ region_model2_manager::get_element_region (region2 *parent,
 
 /* FIXME.  */
 
+region2 *
+region_model2_manager::get_offset_region (region2 *parent,
+					  tree type,
+					  svalue2 *byte_offset,
+					  region_model2_context */*ctxt*/)
+{
+  /* If BYTE_OFFSET is zero and the types match, return PARENT.  */
+  if (tree cst_offset = byte_offset->maybe_get_constant ())
+    if (zerop (cst_offset) && type == parent->get_type ())
+      return parent;
+
+  // FIXME: should only alloc_region_id if consolidation fails
+  region2 *offset_reg
+    = new offset_region2 (alloc_region_id (), parent, type, byte_offset);
+  return const_cast <region2 *> (m_offset_regions.consolidate (offset_reg));
+}
+
+/* FIXME.  */
+
 frame_region2 *
 region_model2_manager::get_frame_region (frame_region2 *calling_frame,
 					 function *fun)
@@ -4627,13 +4752,9 @@ region_model2::get_lvalue_1 (path_var pv,
 	svalue2 *ptr_sval = get_rvalue (ptr, ctxt);
 	svalue2 *offset_sval = get_rvalue (offset, ctxt);
 	region2 *star_ptr = deref_rvalue (ptr_sval, ctxt);
-	return m_mgr->get_element_region (star_ptr,
-					  TREE_TYPE (TREE_TYPE (ptr)),
-					  offset_sval, ctxt); // FIXME: offset_sval is in bytes; should this be in elements?
-#if 0
-	return get_or_create_mem_ref (TREE_TYPE (expr), ptr_sval,
-				      offset_sval, ctxt);
-#endif
+	return m_mgr->get_offset_region (star_ptr,
+					 TREE_TYPE (TREE_TYPE (ptr)),
+					 offset_sval, ctxt);
 	/* TODO: a selftest of casting a buffer to both an (int *)
 	   and a (coord *), and doing array accesses with both arrays.  */
       }
@@ -4893,8 +5014,9 @@ region_model2::get_store_value (region2 *reg)
     return sval;
 
   /* Special-case: read at a constant index within a STRING_CST.  */
-  if (element_region2 *element_reg = reg->dyn_cast_element_region2 ())
-    if (tree index_cst = element_reg->get_index ()->maybe_get_constant ())
+  if (offset_region2 *offset_reg = reg->dyn_cast_offset_region2 ())
+    if (tree byte_offset_cst
+	  = offset_reg->get_byte_offset ()->maybe_get_constant ())
       if (string_region2 *str_reg
 	  = reg->get_parent_region ()->dyn_cast_string_region2 ())
 	{
@@ -4903,8 +5025,8 @@ region_model2::get_store_value (region2 *reg)
 
 	  /* Adapted from fold_read_from_constant_string.  */
 	  scalar_int_mode char_mode;
-	  if (TREE_CODE (index_cst) == INTEGER_CST
-	      && compare_tree_int (index_cst,
+	  if (TREE_CODE (byte_offset_cst) == INTEGER_CST
+	      && compare_tree_int (byte_offset_cst,
 				   TREE_STRING_LENGTH (string_cst)) < 0
 	      && is_int_mode (TYPE_MODE (TREE_TYPE (TREE_TYPE (string_cst))),
 			      &char_mode)
@@ -4913,7 +5035,7 @@ region_model2::get_store_value (region2 *reg)
 	      tree char_cst
 		= build_int_cst_type (TREE_TYPE (TREE_TYPE (string_cst)),
 				      (TREE_STRING_POINTER (string_cst)
-				       [TREE_INT_CST_LOW (index_cst)]));
+				       [TREE_INT_CST_LOW (byte_offset_cst)]));
 	      return m_mgr->get_or_create_constant_svalue2 (char_cst);
 	    }
 	}
@@ -5173,10 +5295,35 @@ region_model2::deref_rvalue (svalue2 *ptr_sval, region_model2_context *ctxt)
 	return region2_sval->get_pointee ();
       }
 
+    case svalue2::SK_BINOP:
+#if 1
+      {
+	binop_svalue2 *binop_sval = as_a <binop_svalue2 *> (ptr_sval);
+	switch (binop_sval->get_op ())
+	  {
+	  case POINTER_PLUS_EXPR:
+	    {
+	      /* If we have a symbolic value expressing pointer arithmentic,
+		 try to convert it to a suitable region.  */
+	      /* FIXME: but should it be an element_region2?  that expresses
+		 things via element indices, rather than via byte offsets.  */
+	      /* Perhaps instead we should capture this as an offset relative
+		 to a base region in region_offset?  */
+	      region2 *parent_region
+		= deref_rvalue (binop_sval->get_arg0 (), ctxt);
+	      svalue2 *offset = binop_sval->get_arg1 ();
+	      tree type= TREE_TYPE (ptr_sval->get_type ());
+	      return m_mgr->get_offset_region (parent_region, type, offset,
+					       ctxt);
+	    }
+	  default:
+	    goto create_symbolic_region2;
+	  }
+      }
+#endif
     case svalue2::SK_CONSTANT:
     case svalue2::SK_INITIAL:
     case svalue2::SK_UNARYOP:
-    case svalue2::SK_BINOP:
       goto create_symbolic_region2;
 
 #if 0
@@ -8770,6 +8917,9 @@ test_POINTER_PLUS_EXPR_then_MEM_REF ()
 
   tree int_42 = build_int_cst (integer_type_node, 42);
   m.set_value (mem_ref, int_42, NULL);
+  ASSERT_EQ (m.get_rvalue (mem_ref, NULL)->maybe_get_constant (), int_42);
+  //ASSERT_EQ (m.get_store_value (tmp_5)->maybe_get_constant (), int_42);
+  // TODO: verify value of tmp_2 and tmp_5
 }
 
 /* FIXME.  */
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index 23d52ae63b1..0734f93bdd5 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -754,6 +754,7 @@ namespace ana {
      symbolic_region (RK_SYMBOLIC)
      field_region2 (RK_FIELD)
      element_region2 (RK_ELEMENT)
+     offset_region2 (RK_OFFSET)
      heap_allocated_region2 (RK_HEAP_ALLOCATED)
      alloca_region2 (RK_ALLOCA)
      string_region2 (RK_STRING).  */
@@ -789,6 +790,7 @@ public:
    RK_DECL,
    RK_FIELD,
    RK_ELEMENT,
+   RK_OFFSET,
    RK_HEAP_ALLOCATED,
    RK_ALLOCA,
    RK_STRING
@@ -813,6 +815,7 @@ public:
   virtual symbolic_region2 *dyn_cast_symbolic_region2 () { return NULL; }
   virtual const symbolic_region2 *dyn_cast_symbolic_region2 () const { return NULL; }
   virtual element_region2 *dyn_cast_element_region2 () { return NULL; }
+  virtual offset_region2 *dyn_cast_offset_region2 () { return NULL; }
   virtual string_region2 *dyn_cast_string_region2 () { return NULL; }
 
   region2 *get_parent_region () const { return m_parent; }
@@ -1468,6 +1471,32 @@ private:
   svalue2 *m_index;
 };
 
+/* A byte-offset within another region, for handling pointer arithmetic
+   as a region.  */
+
+class offset_region2 : public region2
+{
+public:
+  offset_region2 (unsigned id, region2 *parent, tree type, svalue2 *byte_offset)
+  : region2 (id, parent, type), m_byte_offset (byte_offset)
+  {}
+
+  bool compare_fields (const offset_region2 &other) const;
+
+  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_OFFSET; }
+  offset_region2 *dyn_cast_offset_region2 () FINAL OVERRIDE { return this; }
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  svalue2 *get_byte_offset () const { return m_byte_offset; }
+
+protected:
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+
+private:
+  svalue2 *m_byte_offset;
+};
+
 /* An untyped region dynamically allocated on the heap via "malloc"
    or similar.  */
 // FIXME: ...but could be NULL
@@ -1573,6 +1602,10 @@ public:
 			       tree element_type,
 			       svalue2 *index,
 			       region_model2_context *ctxt);
+  region2 *get_offset_region (region2 *parent,
+			      tree type,
+			      svalue2 *byte_offset,
+			      region_model2_context *ctxt);
   frame_region2 *get_frame_region (frame_region2 *calling_frame,
 				   function *fun);
   region2 *get_symbolic_region (svalue2 *sval);
@@ -1630,6 +1663,7 @@ private:
   fields_map_t m_fields_map;
 #endif
   uniq_manager<region2> m_elements;
+  uniq_manager<region2> m_offset_regions;
   uniq_manager<frame_region2> m_frames;
   uniq_manager<region2> m_symbolic_regions;
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
index a8e4056cc3c..0613cfde8d3 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
@@ -238,15 +238,15 @@ void test_16 (void)
 
   __analyzer_eval (msg != NULL); /* { dg-warning "TRUE" } */
 
-  __analyzer_eval (msg[0] == 'h'); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
-  /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
-  // TODO(xfail)
+  __analyzer_eval (msg[0] == 'h'); /* { dg-warning "TRUE" } */
 
-  __analyzer_eval (msg[1] == 'e'); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
-  /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
-  // TODO(xfail)
+  __analyzer_eval (msg[1] == 'e'); /* { dg-warning "TRUE" } */
 
   __analyzer_eval (strlen (msg) == 11); /* { dg-warning "TRUE" } */
+
+  /* Out-of-bounds.  */
+  __analyzer_eval (msg[100] == 'e'); /* { dg-warning "UNKNOWN" } */
+  // TODO: some kind of warning for the out-of-bounds access
 }
 
 static const char *__attribute__((noinline))
-- 
2.26.2

