From 567c7f82416530fe1f4aea7476cdd4f1f1ce327d Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 13 May 2020 17:59:57 -0400
Subject: [PATCH 131/293] FIXME: add POINTER_DIFF_EXPR and BIT_FIELD_REF,
 adding unknown_svalue2 for the latter

---
 gcc/analyzer/region-model2.cc                | 74 +++++++++++++-------
 gcc/analyzer/region-model2.h                 | 13 ++--
 gcc/testsuite/gcc.dg/analyzer/data-model-1.c | 14 ----
 3 files changed, 55 insertions(+), 46 deletions(-)

diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index 3e8b73c3896..3344a800b5c 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -365,6 +365,7 @@ region_svalue2::eval_condition (const region_svalue2 *lhs,
   const region2 *lhs_reg = lhs->get_pointee ();
   const region2 *rhs_reg = rhs->get_pointee ();
   bool ptr_equality = lhs_reg == rhs_reg;
+  // FIXME: what about when we have unknown values?
   /* TODO: what about child region2s where the child is the first child
      (or descendent)?
      Presumably we should look at base regions and compare offsets
@@ -518,19 +519,16 @@ constant_svalue2::get_child_sval (region2 *parent ATTRIBUTE_UNUSED,
 
 /* class unknown_svalue2 : public svalue2.  */
 
-/* Compare the fields of this unknown_svalue2 with OTHER, returning true
-   if they are equal.
-   For use by svalue2::operator==.  */
-#if 0
-bool
-unknown_svalue2::compare_fields (const unknown_svalue2 &) const
+/* FIXME.  */
+
+void
+unknown_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
 {
-  /* I *think* we want to return true here, in that when comparing
-     two region2 models, we want two peer unknown_svalue2 instances
-     to be the "same".  */
-  return true;
+  if (simple)
+    pp_printf (pp, "UNKNOWN");
+  else
+    pp_printf (pp, "unknown_svalue2()");
 }
-#endif
 
 /* class poisoned_svalue2 : public svalue2.  */
 
@@ -2577,6 +2575,9 @@ region_model2_manager::~region_model2_manager ()
   for (constants_map_t::iterator iter = m_constants_map.begin ();
        iter != m_constants_map.end (); ++iter)
     delete (*iter).second;
+  for (unknowns_map_t::iterator iter = m_unknowns_map.begin ();
+       iter != m_unknowns_map.end (); ++iter)
+    delete (*iter).second;
   for (setjmp_values_map_t::iterator iter = m_setjmp_values_map.begin ();
        iter != m_setjmp_values_map.end (); ++iter)
     delete (*iter).second;
@@ -3059,6 +3060,7 @@ region_model2_manager::log_stats (logger *logger, bool show_objs) const
   LOG_SCOPE (logger);
   logger->log ("svalue2 consolidation");
   log_uniq_map (logger, show_objs, "constant_svalue2", m_constants_map);
+  log_uniq_map (logger, show_objs, "unknown_svalue2", m_unknowns_map);
   log_uniq_map (logger, show_objs, "poisoned_svalue2", m_poisoned_values_map);
   log_uniq_map (logger, show_objs, "setjmp_svalue2", m_setjmp_values_map);
   log_uniq_map (logger, show_objs, "initial_svalue2", m_initial_values_map);
@@ -3711,14 +3713,6 @@ region_model2::on_gassign (const supernode */*snode*/,
       }
       break;
 
-#if 0
-    case BIT_FIELD_REF:
-      {
-	// TODO
-      }
-      break;
-#endif
-
     case CONSTRUCTOR:
       {
 	if (TREE_CLOBBER_P (rhs1))
@@ -3764,23 +3758,28 @@ region_model2::on_gassign (const supernode */*snode*/,
       }
       break;
 
-#if 0
     case POINTER_DIFF_EXPR:
       {
 	/* e.g. "_1 = p_2(D) - q_3(D);".  */
+	tree rhs2 = gimple_assign_rhs2 (assign);
+	const svalue2 *rhs1_sval = get_rvalue (rhs1, ctxt);
+	const svalue2 *rhs2_sval = get_rvalue (rhs2, ctxt);
 
-	/* TODO.  */
+	// TODO: perhaps fold to zero if they're known to be equal?
 
-	set_to_new_unknown_value (lhs_reg, TREE_TYPE (lhs), ctxt);
+	const svalue2 *sval_binop
+	  = m_mgr->get_or_create_binop (TREE_TYPE (lhs), op,
+					rhs1_sval, rhs2_sval);
+	set_value (lhs_reg, sval_binop, ctxt);
       }
       break;
-#endif
 
     /* Assignments of the form
 	set_value (lvalue (LHS), rvalue (EXPR))
        for various EXPR.
        We already have the lvalue for the LHS above, as "lhs_reg".  */
     case ADDR_EXPR: /* LHS = &RHS;  */
+    case BIT_FIELD_REF:
     case COMPONENT_REF: /* LHS = op0.op1;  */
     case MEM_REF:
     case REAL_CST:
@@ -4992,6 +4991,28 @@ region_model2::get_rvalue_1 (path_var pv,
       }
       break;
 
+    case BIT_FIELD_REF:
+      {
+#if 0
+	tree expr = pv.m_tree;
+	const region2 *obj = get_lvalue (TREE_OPERAND (expr, 0), ctxt);
+	const svalue2 *num_bits = get_rvalue (TREE_OPERAND (expr, 1), ctxt);
+	const svalue2 *bit_offset = get_rvalue (TREE_OPERAND (expr, 2), ctxt);
+	/* FIXME: attempting to add bitwise access will ultimately require
+	   a new svalue2 type for accessing bits from another svalue2
+	   (e.g. to handle the case where the store has no binding for the
+	   bits), and probably a new region2 type also (or maybe just the
+	   latter, using a sub_svalue2 for the former).
+	   Is it possible to attempting to spot when we're accessing known
+	   values, and have this possibly return NULL?  */
+	return maybe_get_store_bits (TREE_TYPE (expr), obj, num_bits,
+				     bit_offset);
+#else
+	return m_mgr->get_or_create_unknown_svalue2 (TREE_TYPE (pv.m_tree));
+#endif
+      }
+      break;
+
     case SSA_NAME:
     case VAR_DECL:
     case PARM_DECL:
@@ -7841,23 +7862,26 @@ test_region2_equality ()
 }
 #endif
 
-/* Verify that unary ops are folded as expected.  */
+/* Verify that initial_svalue2 are handled as expected.  */
 
 static void
 test_initial_svalue2_folding ()
 {
   region_model2_manager mgr;
   tree x = build_global_decl ("x", integer_type_node);
+  tree y = build_global_decl ("y", integer_type_node);
 
   test_region_model2_context ctxt;
   region_model2 model (&mgr);
   const svalue2 *x_init = model.get_rvalue (x, &ctxt);
+  const svalue2 *y_init = model.get_rvalue (y, &ctxt);
+  ASSERT_NE (x_init, y_init);
   const region2 *x_reg = model.get_lvalue (x, &ctxt);
   ASSERT_EQ (x_init, mgr.get_or_create_initial_value (x_reg));
 
 }
 
-/* Verify unary ops are folded as expected.  */
+/* Verify that unary ops are folded as expected.  */
 
 static void
 test_unaryop_svalue2_folding ()
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index aceb7e9240a..91a2f0cfff0 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -183,8 +183,6 @@ public:
   dyn_cast_constant_svalue2 () const { return NULL; }
   virtual const poisoned_svalue2 *
   dyn_cast_poisoned_svalue2 () const { return NULL; }
-  virtual const unknown_svalue2 *
-  dyn_cast_unknown_svalue2 () const { return NULL; }
   virtual const setjmp_svalue2 *
   dyn_cast_setjmp_svalue2 () const { return NULL; }
   virtual const initial_svalue2 *
@@ -302,6 +300,8 @@ is_a_helper <const constant_svalue2 *>::test (const svalue2 *sval)
 
 namespace ana {
 
+/* FIXME: in the new model, these are unknowable singletons, and comparing
+   them yields "unknown".  */
 /* Concrete subclass of svalue2 representing a unique but unknown value.
    Comparisons of variables that share the same unknown value are known
    to be equal, even if we don't know what the value is.  */
@@ -315,11 +315,6 @@ public:
 
   enum kind get_kind () const FINAL OVERRIDE { return SK_UNKNOWN; }
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
-
-  const unknown_svalue2 *
-  dyn_cast_unknown_svalue2 () const FINAL OVERRIDE { return this; }
-
- private:
 };
 
 #if 0
@@ -1566,6 +1561,7 @@ public:
 
   /* svalue2 consolidation.  */
   const svalue2 *get_or_create_constant_svalue2 (tree cst_expr);
+  const svalue2 *get_or_create_unknown_svalue2 (tree type);
   const svalue2 *get_or_create_setjmp_svalue2 (const setjmp_record &r,
 					       tree type);
   const svalue2 *get_or_create_poisoned_svalue2 (enum poison_kind kind,
@@ -1636,6 +1632,9 @@ private:
   typedef hash_map<tree, constant_svalue2 *> constants_map_t;
   constants_map_t m_constants_map;
 
+  typedef hash_map<tree, unknown_svalue2 *> unknowns_map_t;
+  unknowns_map_t m_unknowns_map;
+
   typedef hash_map<poisoned_svalue2::key_t,
 		   poisoned_svalue2 *> poisoned_values_map_t;
   poisoned_values_map_t m_poisoned_values_map;
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
index 9439b2b553f..47423c3d742 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
@@ -175,7 +175,6 @@ struct coord
   long y;
 };
 
-#if 0
 int test_12d (struct coord c)
 {
   struct coord d;
@@ -194,7 +193,6 @@ int test_12d (struct coord c)
      But d.x and d.y should be different unknown values (although they inherit
      from d's region).  */
 }
-#endif
 
 /* Nested structs.  */
 
@@ -472,7 +470,6 @@ void test_23 (struct foo *f, struct foo *g)
   __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */
 }
 
-#if 0
 void test_24 (struct foo *f)
 {
   struct foo g;
@@ -486,9 +483,7 @@ void test_24 (struct foo *f)
   /* { dg-warning "TRUE" "status quo" { target *-*-* } .-1 } */
   // TODO(xfail)
 }
-#endif
 
-#if 0
 void test_25 (struct foo *f)
 {
   struct foo g;
@@ -505,9 +500,7 @@ void test_25 (struct foo *f)
   /* { dg-warning "FALSE" "status quo" { target *-*-* } .-1 } */
   // TODO(xfail)
 }
-#endif
 
-#if 0
 void test_26 (struct coord *p, struct coord *q)
 {
   p->x = 42;
@@ -531,7 +524,6 @@ void test_26 (struct coord *p, struct coord *q)
   __analyzer_eval (q->x); /* { dg-warning "UNKNOWN" } */
   __analyzer_eval (q->y == 17); /* { dg-warning "TRUE" } */
 }
-#endif
 
 void test_27 (struct coord *p)
 {
@@ -922,7 +914,6 @@ union u
   int *ptr;
 };
 
-#if 0
 void test_41 (void)
 {
   union u u;
@@ -935,7 +926,6 @@ void test_41 (void)
   __analyzer_eval (u.ptr == NULL); /* { dg-warning "TRUE" } */
   __analyzer_eval (u.i == 42); /* { dg-warning "UNKNOWN" } */
 }
-#endif
 
 void test_42 (void)
 {
@@ -988,7 +978,6 @@ struct ubits
 
 /* FIXME: this requires BIT_FIELD_REF to work.  */
 
-#if 0
 void test_45 (void)
 {
   struct ubits bits;
@@ -1002,7 +991,6 @@ void test_45 (void)
   /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
   // TODO(xfail): ^^^^
 };
-#endif
 
 extern const char *char_ptr;
 
@@ -1019,12 +1007,10 @@ char test_47 (void)
   return my_version[0];
 }
 
-#if 0
 unsigned test_48 (unsigned char *p, unsigned char *q)
 {
   return (unsigned int)(p - q);
 }
-#endif
 
 typedef struct {
   const char *filename;
-- 
2.26.2

