From 2e95b8a40d2f896b7a9bc8c03e4615c4e2c14ddb Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 11 Jun 2020 16:35:45 -0400
Subject: [PATCH 228/237] FIXME: initial impl of
 widening_svalue2::eval_condition_without_cm

---
 gcc/analyzer/region-model2.cc           | 127 ++++++++++++++++++++++++
 gcc/analyzer/region-model2.h            |  14 ++-
 gcc/testsuite/gcc.dg/analyzer/loop-2a.c |  10 +-
 gcc/testsuite/gcc.dg/analyzer/loop.c    |   4 +-
 4 files changed, 144 insertions(+), 11 deletions(-)

diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index 5e3a1ac637a..621fced83b8 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -818,6 +818,92 @@ widening_svalue2::dump_to_pp (pretty_printer *pp, bool simple) const
     }
 }
 
+/* FIXME.  */
+
+enum widening_svalue2::direction_t
+widening_svalue2::get_direction () const
+{
+  tree base_cst = m_base_sval->maybe_get_constant ();
+  if (base_cst == NULL_TREE)
+    return DIR_UNKNOWN;
+  tree iter_cst = m_iter_sval->maybe_get_constant ();
+  if (iter_cst == NULL_TREE)
+    return DIR_UNKNOWN;
+
+  tree iter_gt_base = fold_binary (GT_EXPR, boolean_type_node,
+				   iter_cst, base_cst);
+  if (iter_gt_base == boolean_true_node)
+    return DIR_ASCENDING;
+
+  tree iter_lt_base = fold_binary (LT_EXPR, boolean_type_node,
+				   iter_cst, base_cst);
+  if (iter_lt_base == boolean_true_node)
+    return DIR_DESCENDING;
+
+  return DIR_UNKNOWN;
+}
+
+/* FIXME.  */
+
+tristate
+widening_svalue2::eval_condition_without_cm (enum tree_code op,
+					     tree rhs_cst) const
+{
+  tree base_cst = m_base_sval->maybe_get_constant ();
+  if (base_cst == NULL_TREE)
+    return tristate::TS_UNKNOWN;
+  tree iter_cst = m_iter_sval->maybe_get_constant ();
+  if (iter_cst == NULL_TREE)
+    return tristate::TS_UNKNOWN;
+
+  switch (get_direction ())
+    {
+    default:
+      gcc_unreachable ();
+    case DIR_ASCENDING:
+      /* LHS is in [base_cst, +ve infinity), assuming no overflow.  */
+      switch (op)
+	{
+	case LT_EXPR:
+	  {
+	    /* [BASE, +INF) < RHS:
+	       This is false at +ve ininity.  It can be true for points
+	       X where X < RHS, so we have either "false", or "unknown".  */
+	    tree base_lt_rhs = fold_binary (op, boolean_type_node,
+					    base_cst, rhs_cst);
+	    if (base_lt_rhs == boolean_true_node)
+	      return tristate::TS_UNKNOWN;
+	    else
+	      return tristate::TS_FALSE;
+	  }
+
+	case GE_EXPR:
+	  {
+	    /* [BASE, +INF) >= RHS:
+	       This is true at +ve infinity.  It will be true everywhere
+	       in the range if BASE >= RHS.  */
+	    tree base_ge_rhs = fold_binary (op, boolean_type_node,
+					    base_cst, rhs_cst);
+	    if (base_ge_rhs == boolean_true_node)
+	      return tristate::TS_TRUE;
+	    else
+	      return tristate::TS_UNKNOWN;
+	  }
+
+	default:
+	  return tristate::TS_UNKNOWN;
+	}
+
+    case DIR_DESCENDING:
+      /* LHS is in (-ve infinity, base_cst], assuming no overflow.  */
+      // TODO:
+      return tristate::TS_UNKNOWN;
+
+    case DIR_UNKNOWN:
+      return tristate::TS_UNKNOWN;
+    }
+}
+
 /* class placeholder_svalue2 : public svalue2.  */
 
 void
@@ -6277,6 +6363,42 @@ region_model2::eval_condition_without_cm (const svalue2 *lhs,
 	    }
 	}
 
+  /* Initial values of "external" values (such as params) can't point
+     at locals.  */
+  // FIXME: what about other "external" values vs escaped clusters
+  /* Initial values of pointers can't point at locals.
+     FIXME: unless they escaped?  */
+#if 0
+  if (const initial_svalue2 *init_lhs = lhs->dyn_cast_initial_svalue2 ())
+    if (const region_svalue2 *rhs_ptr = rhs->dyn_cast_region_svalue2 ())
+      {
+	tristate res = compare_initial_and_pointer (init_lhs, rhs_ptr);
+	if (res.is_known ())
+	  return res;
+      }
+  if (const initial_svalue2 *init_rhs = rhs->dyn_cast_initial_svalue2 ())
+    if (const region_svalue2 *lhs_ptr = lhs->dyn_cast_region_svalue2 ())
+      {
+	tristate res = compare_initial_and_pointer (init_rhs, lhs_ptr);
+	if (res.is_known ())
+	  return res;
+      }
+#endif
+
+  if (const widening_svalue2 *widen_lhs = lhs->dyn_cast_widening_svalue2 ())
+    if (tree rhs_cst = rhs->maybe_get_constant ())
+      {
+	tristate res = widen_lhs->eval_condition_without_cm (op, rhs_cst);
+	if (res.is_known ())
+	  return res;
+      }
+#if 0
+  if (const widening_svalue2 *widen_rhs = rhs->dyn_cast_widening_svalue2 ())
+    {
+      gcc_unreachable ();
+    }
+#endif
+
   return tristate::TS_UNKNOWN;
 }
 
@@ -9973,10 +10095,15 @@ test_iteration_1 ()
   ASSERT_TRUE (model1.can_merge_with_p (model0, point, &model2));
   const svalue2 *merged_i = model2.get_rvalue (i, &ctxt);
   ASSERT_EQ (merged_i->get_kind (), svalue2::SK_WIDENING);
+  const widening_svalue2 *w = merged_i->dyn_cast_widening_svalue2 ();
+  ASSERT_EQ (w->get_direction (), widening_svalue2::DIR_ASCENDING);
+
   /* Add constraint: i < 256  */
   model2.add_constraint (i, LT_EXPR, int_256, &ctxt);
   ASSERT_EQ (model2.eval_condition (i, LT_EXPR, int_256, &ctxt),
 	     tristate (tristate::TS_TRUE));
+  ASSERT_EQ (model2.eval_condition (i, GE_EXPR, int_0, &ctxt),
+	     tristate (tristate::TS_TRUE));
 
   /* Try merging with the initial state.  */
   region_model2 model3 (&mgr);
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index 10c7d646db2..6e12eea003e 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -25,7 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "analyzer/store2.h" // FIXME
 #include "analyzer/program-point.h" // FIXME
 
-#define WIDENING 0
+#define WIDENING 1
 
 /* Implementation of the region-based ternary model described in:
      "A Memory Model for Static Analysis of C Programs"
@@ -991,6 +991,13 @@ public:
     const svalue2 *m_base_sval;
     const svalue2 *m_iter_sval;
   };
+
+  enum direction_t
+    {
+     DIR_ASCENDING,
+     DIR_DESCENDING,
+     DIR_UNKNOWN
+    };
   widening_svalue2 (tree type, const program_point &point,
 		    const svalue2 *base_sval, const svalue2 *iter_sval)
   : svalue2 (type), m_point (point.get_function_point ()),
@@ -1012,6 +1019,11 @@ public:
   const svalue2 *get_base_svalue2 () const { return m_base_sval; }
   const svalue2 *get_iter_svalue2 () const { return m_iter_sval; }
 
+  enum direction_t get_direction () const;
+
+  tristate eval_condition_without_cm (enum tree_code op,
+				      tree rhs_cst) const;
+
  private:
 #if 0
   program_point m_point;
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-2a.c b/gcc/testsuite/gcc.dg/analyzer/loop-2a.c
index a392ffc1a63..16b64497cb7 100644
--- a/gcc/testsuite/gcc.dg/analyzer/loop-2a.c
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-2a.c
@@ -14,10 +14,7 @@ void test(void)
 
 
   for (u.i=0; u.i<256; u.i++) {
-    __analyzer_eval (u.i < 256); /* { dg-warning "TRUE" "1st" } */
-    /* { dg-warning "TRUE" "2nd" { xfail *-*-* } .-1 } */
-    /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-2 } */
-    /* (should report TRUE twice). */
+    __analyzer_eval (u.i < 256); /* { dg-warning "TRUE" } */
 
     __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
 
@@ -26,11 +23,10 @@ void test(void)
       /* TODO(xfail^^^): we're only capturing the first iteration, so
 	 we erroneously get i == 0.  */
 
-      //__analyzer_eval (u.i >= 0); /* { d-todo-g-warning "TRUE" } */
+    __analyzer_eval (u.i >= 0); /* { dg-warning "TRUE" } */
   }
 
-  __analyzer_eval (u.i >= 256); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
-  /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
+  __analyzer_eval (u.i >= 256); /* { dg-warning "TRUE" } */
 
   __analyzer_eval (u.i == 256); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
   /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop.c b/gcc/testsuite/gcc.dg/analyzer/loop.c
index 37b757bf8f4..e5f327e2847 100644
--- a/gcc/testsuite/gcc.dg/analyzer/loop.c
+++ b/gcc/testsuite/gcc.dg/analyzer/loop.c
@@ -18,9 +18,7 @@ void test(void)
       /* TODO(xfail^^^): ideally we ought to figure out i > 0 after 1st iteration.  */
 
       __analyzer_eval (i >= 0); /* { dg-warning "TRUE" "1st" } */
-      /* { dg-warning "TRUE" "2nd" { xfail *-*-* } } */
-      /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-2 } */
-      /* TODO(xfail^^^): ideally we ought to figure out i >= 0 for all iterations.  */
+      /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
 
       __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */
   }
-- 
2.26.2

