From 0a45bde628882129e4985cfeffb3ab16d96219b3 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 15 May 2020 18:01:15 -0400
Subject: [PATCH 143/179] FIXME: introduce cast_region2; use it to fix test_51

---
 gcc/analyzer/analyzer.h                      |  1 +
 gcc/analyzer/region-model2.cc                | 98 +++++++++++++++++++-
 gcc/analyzer/region-model2.h                 | 35 +++++++
 gcc/analyzer/store2.cc                       | 32 +++++++
 gcc/testsuite/gcc.dg/analyzer/data-model-1.c |  6 +-
 5 files changed, 165 insertions(+), 7 deletions(-)

diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 6d37f6264b0..905ba42d58a 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -69,6 +69,7 @@ class region2;
   class symbolic_region2;
   class element_region2;
   class offset_region2;
+  class cast_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 6c7afc6457e..9bb12fbd495 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -203,6 +203,19 @@ svalue2::maybe_get_constant () const
     return NULL_TREE;
 }
 
+/* If this svalue2 is a cast (i.e a unaryop NOP_EXPR), return the underlying
+   svalue2.
+   Otherwise return NULL.  */
+
+const svalue2 *
+svalue2::maybe_undo_cast () const
+{
+  if (const unaryop_svalue2 *unaryop_sval = dyn_cast_unaryop_svalue2 ())
+    if (unaryop_sval->get_op () == NOP_EXPR)
+      return unaryop_sval->get_arg ();
+  return NULL;
+}
+
 /* class region_svalue2 : public svalue2.  */
 
 /* FIXME.  */
@@ -863,6 +876,14 @@ region2::operator== (const region2 &other) const
 	  = (const offset_region2 &)other;
 	return this_sub.compare_fields (other_sub);
       }
+    case RK_CAST:
+      {
+	const cast_region2 &this_sub
+	  = (const cast_region2 &)*this;
+	const cast_region2 &other_sub
+	  = (const cast_region2 &)other;
+	return this_sub.compare_fields (other_sub);
+      }
     }
 }
 
@@ -881,6 +902,9 @@ region2::get_base_region () const
 	case RK_OFFSET:
 	  iter = iter->get_parent_region ();
 	  continue;
+	case RK_CAST:
+	  iter = iter->dyn_cast_cast_region2 ()->get_original_region ();
+	  continue;
 	default:
 	  return iter;
 	}
@@ -2510,6 +2534,42 @@ offset_region2::add_to_hash (inchash::hash &hstate) const
   hstate.add_ptr (m_byte_offset);
 }
 
+/* class cast_region2 : public region2.  */
+
+bool
+cast_region2::compare_fields (const cast_region2 &other) const
+{
+  return m_original_region == other.m_original_region;
+}
+
+void
+cast_region2::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  if (simple)
+    {
+      pp_string (pp, "CAST_REG(");
+      print_quoted_type (pp, get_type ());
+      pp_string (pp, ", ");
+      m_original_region->dump_to_pp (pp, simple);
+      pp_string (pp, ")");
+    }
+  else
+    {
+      pp_string (pp, "cast_region2(");
+      m_original_region->dump_to_pp (pp, simple);
+      pp_string (pp, ", ");
+      print_quoted_type (pp, get_type ());
+      pp_printf (pp, ")");
+    }
+}
+
+void
+cast_region2::add_to_hash (inchash::hash &hstate) const
+{
+  region2::add_to_hash (hstate);
+  hstate.add_ptr (m_original_region);
+}
+
 /* class heap_allocated_region2 : public region2.  */
 
 void
@@ -2663,6 +2723,21 @@ region_model2_manager::get_or_create_unknown_svalue2 (tree type)
 const svalue2 *
 region_model2_manager::get_or_create_initial_value (const region2 *reg)
 {
+  /* The initial value of a cast is a cast of the initial value.  */
+  if (const cast_region2 *cast_reg = reg->dyn_cast_cast_region2 ())
+    {
+      const region2 *original_reg = cast_reg->get_original_region ();
+      return get_or_create_cast (cast_reg->get_type (),
+				 get_or_create_initial_value (original_reg));
+    }
+
+  /* We shouldn't create these for SSA names.  */
+  // TODO:
+#if 0
+  if (const decl_region2 *decl_reg = reg->dyn_cast_decl_region2 ())
+    return get_or_create_poisoned_svalue2 (POISON_KIND_UNINIT,
+					   reg->get_type ());
+#endif
   if (initial_svalue2 **slot = m_initial_values_map.get (reg))
     return *slot;
   initial_svalue2 *initial_sval = new initial_svalue2 (reg->get_type (), reg);
@@ -3001,10 +3076,10 @@ region_model2_manager::get_offset_region (const region2 *parent,
 					  const svalue2 *byte_offset,
 					  region_model2_context */*ctxt*/)
 {
-  /* If BYTE_OFFSET is zero and the types match, return PARENT.  */
+  /* If BYTE_OFFSET is zero, return PARENT.  */
   if (tree cst_offset = byte_offset->maybe_get_constant ())
-    if (zerop (cst_offset) && type == parent->get_type ())
-      return parent;
+    if (zerop (cst_offset))
+      return get_cast_region (parent, type);
 
   // FIXME: should only alloc_region_id if consolidation fails
   region2 *offset_reg
@@ -3014,6 +3089,22 @@ region_model2_manager::get_offset_region (const region2 *parent,
 
 /* FIXME.  */
 
+const region2 *
+region_model2_manager::get_cast_region (const region2 *original_region,
+					tree type)
+{
+  /* If types match, return ORIGINAL_REGION.  */
+  if (type == original_region->get_type ())
+    return original_region;
+
+  // FIXME: should only alloc_region_id if consolidation fails
+  region2 *cast_reg
+    = new cast_region2 (alloc_region_id (), original_region, type);
+  return const_cast <region2 *> (m_cast_regions.consolidate (cast_reg));
+}
+
+/* FIXME.  */
+
 const frame_region2 *
 region_model2_manager::get_frame_region (const frame_region2 *calling_frame,
 					 function *fun)
@@ -3173,6 +3264,7 @@ region_model2_manager::log_stats (logger *logger, bool show_objs) const
   log_uniq_manager (logger, show_objs, "field_region2", m_fields);
   log_uniq_manager (logger, show_objs, "element_region2", m_elements);
   log_uniq_manager (logger, show_objs, "offset_region2", m_offset_regions);
+  log_uniq_manager (logger, show_objs, "cast_region2", m_cast_regions);
   log_uniq_manager (logger, show_objs, "frame_region2", m_frames);
   log_uniq_manager (logger, show_objs, "symbolic_region2", m_symbolic_regions);
   log_uniq_map (logger, show_objs, "string_region2", m_string_map);
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index 47943b79da2..e8c83548d87 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -191,6 +191,7 @@ public:
   dyn_cast_unaryop_svalue2 () const { return NULL; }
 
   tree maybe_get_constant () const;
+  const svalue2 *maybe_undo_cast () const;
 
  protected:
   svalue2 (tree type) : m_type (type) {}
@@ -828,6 +829,7 @@ public:
    RK_FIELD,
    RK_ELEMENT,
    RK_OFFSET,
+   RK_CAST,
    RK_HEAP_ALLOCATED,
    RK_ALLOCA,
    RK_STRING,
@@ -856,6 +858,8 @@ public:
   dyn_cast_element_region2 () const { return NULL; }
   virtual const offset_region2 *
   dyn_cast_offset_region2 () const { return NULL; }
+  virtual const cast_region2 *
+  dyn_cast_cast_region2 () const { return NULL; }
   virtual const string_region2 *
   dyn_cast_string_region2 () const { return NULL; }
 
@@ -1445,6 +1449,34 @@ private:
   const svalue2 *m_byte_offset;
 };
 
+/* A region that views another region using a different type.  */
+
+class cast_region2 : public region2
+{
+public:
+  cast_region2 (unsigned id, const region2 *original_region, tree type)
+  : region2 (id, original_region->get_parent_region (), type),
+    m_original_region (original_region)
+  {}
+
+  bool compare_fields (const cast_region2 &other) const;
+
+  enum region2::kind get_kind () const FINAL OVERRIDE { return RK_CAST; }
+  const cast_region2 *
+  dyn_cast_cast_region2 () const FINAL OVERRIDE { return this; }
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
+
+  /* FIXME: should this just be the parent region? */
+  const region2 *get_original_region () const { return m_original_region; }
+
+protected:
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+
+private:
+  const region2 *m_original_region;
+};
+
 /* An untyped region dynamically allocated on the heap via "malloc"
    or similar.  */
 // FIXME: ...but could be NULL
@@ -1588,6 +1620,8 @@ public:
 				    tree type,
 				    const svalue2 *byte_offset,
 				    region_model2_context *ctxt);
+  const region2 *get_cast_region (const region2 *original_region,
+				  tree type);
   const frame_region2 *get_frame_region (const frame_region2 *calling_frame,
 					 function *fun);
   const region2 *get_symbolic_region (const svalue2 *sval);
@@ -1679,6 +1713,7 @@ private:
 #endif
   uniq_manager<region2> m_elements;
   uniq_manager<region2> m_offset_regions;
+  uniq_manager<region2> m_cast_regions;
   uniq_manager<frame_region2> m_frames;
   uniq_manager<region2> m_symbolic_regions;
 
diff --git a/gcc/analyzer/store2.cc b/gcc/analyzer/store2.cc
index a8d3973644f..f1eaced6e7e 100644
--- a/gcc/analyzer/store2.cc
+++ b/gcc/analyzer/store2.cc
@@ -858,12 +858,44 @@ store2::get_any_binding (store2_manager *mgr, const region2 *reg)
   return (*cluster_slot)->get_any_binding (mgr, reg);
 }
 
+/* The store is oblivious to the types of the svalues bound within
+   it: any type can get bound at any location.
+   Simplify any casts before binding.
+
+   For example, if we have:
+     struct big { int ia[1024]; };
+     struct big src, dst;
+     memcpy (&dst, &src, sizeof (struct big));
+   this reaches us in gimple form as:
+     MEM <unsigned char[4096]> [(char * {ref-all})&dst]
+       = MEM <unsigned char[4096]> [(char * {ref-all})&src];
+   Using cast_region when handling the MEM_REF would give us:
+     INIT_VAL(CAST_REG(unsigned char[4096], src))
+   as rhs_sval, but we can fold that into a cast svalue2:
+     CAST(unsigned char[4096], INIT_VAL(src))
+   We can discard that cast from the svalue when binding it in
+   the store for "dst", and simply store:
+     cluster for: dst
+       key:   {kind: direct, start: 0, size: 32768, next: 32768}
+       value: ‘struct big’ {INIT_VAL(src)}
+   FIXME: should we only do this if the sizes are the same?  */
+
+static const svalue2 *
+simplify_for_binding (const svalue2 *sval)
+{
+  if (const svalue2 *cast_sval = sval->maybe_undo_cast ())
+    sval = cast_sval;
+  return sval;
+}
+
 void
 store2::set_value (store2_manager *mgr, const region2 *lhs_reg,
 		   const svalue2 *rhs_sval, enum binding_kind kind)
 {
   remove_overlapping_bindings (mgr, lhs_reg);
 
+  rhs_sval = simplify_for_binding (rhs_sval);
+
   const region2 *base_reg = lhs_reg->get_base_region ();
   binding_cluster2 *cluster = get_or_create_cluster (base_reg);
   cluster->bind (mgr, lhs_reg, rhs_sval, kind);
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
index 939ba2feaa9..2d2371c0074 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
@@ -1039,10 +1039,8 @@ void test_51 (struct coord c)
 {
   struct coord d;
   memcpy (&d, &c, sizeof (struct coord));
-  __analyzer_eval (c.x == d.x); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
-  /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
-  __analyzer_eval (c.y == d.y); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
-  /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
+  __analyzer_eval (c.x == d.x); /* { dg-warning "TRUE" } */
+  __analyzer_eval (c.y == d.y); /* { dg-warning "TRUE" } */
 }
 
 struct big
-- 
2.21.0

