From 5caa6468ed05c48c990c0835b10557ad20ec801b Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 5 May 2020 09:59:18 -0400
Subject: [PATCH 098/293] FIXME: add test_svalue2_folding

---
 gcc/analyzer/region-model2.cc | 180 ++++++++++++++++++++++++++++++++--
 gcc/analyzer/region-model2.h  |   5 +
 2 files changed, 177 insertions(+), 8 deletions(-)

diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index 14e45f62537..d9c4fffa68c 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -2826,14 +2826,22 @@ region_model2_manager::get_ptr_svalue2 (tree ptr_type, region2 *pointee)
 }
 
 svalue2 *
-region_model2_manager::get_or_create_unaryop (tree type, enum tree_code op,
-					      svalue2 *arg)
+region_model2_manager::maybe_fold_unaryop (tree type, enum tree_code op,
+					   svalue2 *arg)
 {
-  /* Folding.  */
+  /* Constants.  */
   if (tree cst = arg->maybe_get_constant ())
     if (tree result = fold_unary (op, type, cst))
       return get_or_create_constant_svalue2 (result);
+  return NULL;
+}
 
+svalue2 *
+region_model2_manager::get_or_create_unaryop (tree type, enum tree_code op,
+					      svalue2 *arg)
+{
+  if (svalue2 *folded = maybe_fold_unaryop  (type, op, arg))
+    return folded;
   unaryop_svalue2::key_t key (type, op, arg);
   if (unaryop_svalue2 **slot = m_unaryop_values_map.get (key))
     return *slot;
@@ -2843,15 +2851,57 @@ region_model2_manager::get_or_create_unaryop (tree type, enum tree_code op,
 }
 
 svalue2 *
-region_model2_manager::get_or_create_binop (tree type, enum tree_code op,
-					    svalue2 *arg0, svalue2 *arg1)
+region_model2_manager::maybe_fold_binop (tree type, enum tree_code op,
+					 svalue2 *arg0, svalue2 *arg1)
 {
-  /* Folding.  */
-  if (tree cst0 = arg0->maybe_get_constant ())
-    if (tree cst1 = arg1->maybe_get_constant ())
+  tree cst0 = arg0->maybe_get_constant ();
+  tree cst1 = arg1->maybe_get_constant ();
+  /* (CST OP CST).  */
+  if (cst0 && cst1)
+    {
       if (tree result = fold_binary (op, type, cst0, cst1))
 	return get_or_create_constant_svalue2 (result);
+    }
 
+  if (FLOAT_TYPE_P (type)
+      || (arg0->get_type () && FLOAT_TYPE_P (arg0->get_type ()))
+      || (arg1->get_type () && FLOAT_TYPE_P (arg1->get_type ())))
+    return NULL;
+
+  switch (op)
+    {
+    default:
+      break;
+    case PLUS_EXPR:
+      /* (VAL + 0) -> VAL.  */
+      if (cst1 && zerop (cst1) && type == arg0->get_type ())
+	return arg0;
+      /* (0 + VAL) -> VAL.  */
+      if (cst0 && zerop (cst0) && type == arg1->get_type ())
+	return arg1;
+      break;
+    case MULT_EXPR:
+      /* (VAL * 0) or (0 * VAL) -> 0.  */
+      if ((cst1 && zerop (cst1)) || (cst0 && zerop (cst0)))
+	// FIXME: check for integer type
+	return get_or_create_constant_svalue2 (build_int_cst (type, 0));
+      /* (VAL * 1) -> VAL.  */
+      if (cst1 && integer_onep (cst1) && type == arg0->get_type ())
+	return arg0;
+      /* (1 * VAL) -> VAL.  */
+      if (cst0 && integer_onep (cst0) && type == arg1->get_type ())
+	return arg1;
+      break;
+    }
+  return NULL;
+}
+
+svalue2 *
+region_model2_manager::get_or_create_binop (tree type, enum tree_code op,
+					    svalue2 *arg0, svalue2 *arg1)
+{
+  if (svalue2 *folded = maybe_fold_binop (type, op, arg0, arg1))
+    return folded;
   binop_svalue2::key_t key (type, op, arg0, arg1);
   if (binop_svalue2 **slot = m_binop_values_map.get (key))
     return *slot;
@@ -7527,7 +7577,118 @@ test_region2_equality ()
 
   // TODO: test coverage for the map within a map_region2
 }
+#endif
+
+/* Verify that binops on constant svalues are folded.  */
+
+static void
+test_svalue2_folding ()
+{
+#define NUM_CSTS 10
+  tree cst_int[NUM_CSTS];
+  region_model2_manager mgr;
+  svalue2 *cst_sval[NUM_CSTS];
+  for (int i = 0; i < NUM_CSTS; i++)
+    {
+      cst_int[i] = build_int_cst (integer_type_node, i);
+      cst_sval[i] = mgr.get_or_create_constant_svalue2 (cst_int[i]);
+      ASSERT_EQ (cst_sval[i]->get_kind (), svalue2::SK_CONSTANT);
+      ASSERT_EQ (cst_sval[i]->maybe_get_constant (), cst_int[i]);
+    }
 
+  for (int i = 0; i < NUM_CSTS; i++)
+    for (int j = 0; j < NUM_CSTS; j++)
+      {
+	if (i != j)
+	  ASSERT_NE (cst_sval[i], cst_sval[j]);
+	if (i + j < NUM_CSTS)
+	  {
+	    svalue2 *sum
+	      = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
+					 cst_sval[i], cst_sval[j]);
+	    ASSERT_EQ (sum, cst_sval[i + j]);
+	  }
+	if (i - j >= 0)
+	  {
+	    svalue2 *difference
+	      = mgr.get_or_create_binop (integer_type_node, MINUS_EXPR,
+					 cst_sval[i], cst_sval[j]);
+	    ASSERT_EQ (difference, cst_sval[i - j]);
+	  }
+	if (i * j < NUM_CSTS)
+	  {
+	    svalue2 *product
+	      = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
+					 cst_sval[i], cst_sval[j]);
+	    ASSERT_EQ (product, cst_sval[i * j]);
+	  }
+	svalue2 *eq = mgr.get_or_create_binop (integer_type_node, EQ_EXPR,
+					       cst_sval[i], cst_sval[j]);
+	ASSERT_EQ (eq, i == j ? cst_sval[1] : cst_sval [0]);
+	svalue2 *neq = mgr.get_or_create_binop (integer_type_node, NE_EXPR,
+						cst_sval[i], cst_sval[j]);
+	ASSERT_EQ (neq, i != j ? cst_sval[1] : cst_sval [0]);
+	// etc
+      }
+
+  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);
+  svalue2 *x_init = model.get_rvalue (x, &ctxt);
+  region2 *x_reg = model.get_lvalue (x, &ctxt);
+  ASSERT_EQ (x_init, mgr.get_or_create_initial_value (x_reg));
+
+  /* PLUS_EXPR folding.  */
+  svalue2 *x_init_plus_zero
+    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
+			       x_init, cst_sval[0]);
+  ASSERT_EQ (x_init_plus_zero, x_init);
+  svalue2 *zero_plus_x_init
+    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
+			       cst_sval[0], x_init);
+  ASSERT_EQ (zero_plus_x_init, x_init);
+
+  /* MULT_EXPR folding.  */
+  svalue2 *x_init_times_zero
+    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
+			       x_init, cst_sval[0]);
+  ASSERT_EQ (x_init_times_zero, cst_sval[0]);
+  svalue2 *zero_times_x_init
+    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
+			       cst_sval[0], x_init);
+  ASSERT_EQ (zero_times_x_init, cst_sval[0]);
+
+  svalue2 *x_init_times_one
+    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
+			       x_init, cst_sval[1]);
+  ASSERT_EQ (x_init_times_one, x_init);
+  svalue2 *one_times_x_init
+    = mgr.get_or_create_binop (integer_type_node, MULT_EXPR,
+			       cst_sval[1], x_init);
+  ASSERT_EQ (one_times_x_init, x_init);
+
+  // etc
+  // TODO: do we want to use the match-and-simplify DSL for this?
+
+  /* TODO: Verify that ((x + 1) + 1) == (x + 2).  */
+  svalue2 *x_init_plus_one
+    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
+			       x_init, cst_sval[1]);
+  svalue2 *x_init_plus_two
+    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
+			       x_init, cst_sval[2]);
+  svalue2 *x_init_plus_one_plus_one
+    = mgr.get_or_create_binop (integer_type_node, PLUS_EXPR,
+			       x_init_plus_one, cst_sval[1]);
+#if 0
+  ASSERT_EQ (x_init_plus_one_plus_one, x_init_plus_two);
+#endif
+
+  /* FIXME: do we want to canonicalize binops to put constants on the RHS ?  */
+}
+#if 0
 /* A subclass of purge_criteria for selftests: purge all svalue2 *instances.  */
 
 class purge_all_svalue2_ids : public purge_criteria
@@ -8878,6 +9039,9 @@ analyzer_region_model2_cc_tests ()
 #if 0
   test_svalue2_equality ();
   test_region2_equality ();
+#endif
+  test_svalue2_folding ();
+#if 0
   test_purging_by_criteria ();
   test_purge_unused_svalue2s ();
 #endif
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index 5d00c7d9bf0..9127c5ab9f3 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -1604,6 +1604,11 @@ public:
   region2 *create_region_for_alloca (frame_region2 *frame);
 
 private:
+  svalue2 *maybe_fold_unaryop (tree type, enum tree_code op,
+			       svalue2 *arg);
+  svalue2 *maybe_fold_binop (tree type, enum tree_code op,
+			     svalue2 *arg0, svalue2 *arg1);
+
   unsigned m_next_region_id;
   root_region2 m_root_region;
   stack_region2 m_stack_region;
-- 
2.26.2

