From bb59c06aecca2001e4a22d697713d042446bffad Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 3 Jul 2019 14:47:39 -0400
Subject: [PATCH 61/85] FIXME: more on region-merging

---
 gcc/analyzer/region-model.cc                    | 92 ++++++++++++++++++++++++-
 gcc/testsuite/gcc.dg/analyzer/malloc-vs-local.c |  3 +-
 gcc/testsuite/gcc.dg/analyzer/paths-4.c         |  4 +-
 gcc/testsuite/gcc.dg/analyzer/paths-5.c         |  4 +-
 4 files changed, 94 insertions(+), 9 deletions(-)

diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index b264dd5..0249b76 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -3937,7 +3937,14 @@ model_merger::can_merge_values_p (svalue_id sid_a,
   if (!(sval_a && sval_b))
     return false;
 
-  gcc_assert (sval_a->get_type () == sval_b->get_type ());
+#if 0
+  gcc_assert (sval_a->get_type () == sval_b->get_type ()
+	      || sval_a->get_type () == NULL_TREE
+	      || sval_b->get_type () == NULL_TREE);
+#endif
+  tree type = sval_a->get_type ();
+  if (type == NULL_TREE)
+    type = sval_b->get_type ();
 
   /* If one of the values is unknown, then we can merge,
      using an unknown value.  */
@@ -3946,13 +3953,91 @@ model_merger::can_merge_values_p (svalue_id sid_a,
     {
       /* FIXME: this loses information about unknown values
 	 that are shared between multiple regions.  */
-      svalue *merged_sval = new unknown_svalue (sval_a->get_type ());
+      svalue *merged_sval = new unknown_svalue (type);
+      *merged_sid = m_merged_model->add_svalue (merged_sval);
+      return true;
+    }
+
+  /* If we have two constants, then we can merge.  */
+  if (sval_a->get_kind () == SK_CONSTANT
+      && sval_b->get_kind () == SK_CONSTANT)
+    {
+      const constant_svalue *cst_sval_a = (const constant_svalue *)sval_a;
+      const constant_svalue *cst_sval_b = (const constant_svalue *)sval_b;
+      tree cst_a = cst_sval_a->get_constant ();
+      tree cst_b = cst_sval_b->get_constant ();
+      svalue *merged_sval;
+      if (cst_a == cst_b) // FIXME: other equality?
+	{
+	  /* If they are the same constant, merge as that constant value.  */
+	  merged_sval = new constant_svalue (cst_a);
+	}
+      else
+	{
+	  /* Otherwise, we have two different constant values.
+	     Merge as an unknown value.
+	     TODO: impose constraints on the value?
+	     (maybe just based on A, to avoid infinite chains)  */
+	  merged_sval = new unknown_svalue (TREE_TYPE (cst_a));
+	}
       *merged_sid = m_merged_model->add_svalue (merged_sval);
       return true;
     }
+#endif
+
+  /* If we have two regions... .  */
+  if (sval_a->get_kind () == SK_REGION
+      && sval_b->get_kind () == SK_REGION)
+    {
+      const region_svalue *region_sval_a = (const region_svalue *)sval_a;
+      const region_svalue *region_sval_b = (const region_svalue *)sval_b;
+
+      if (region_sval_a && region_sval_b)
+	{
+#if 0
+	  /* FIXME: is it OK to just check that the region_id is the same?  */
+	  if (region_sval_a->get_pointee () == region_sval_b->get_pointee ())
+	    {
+	      region_id merged_pointee_rid; // FIXME: what should this be???
+	      svalue *merged_sval
+		= new region_svalue (type, merged_pointee_rid);
+	      *merged_sid = m_merged_model->add_svalue (merged_sval);
+	      return true;
+	    }
+	  else
+#endif
+	    /* FIXME: for now, always just merge to unknown for two non-NULL
+	       pointers, even if they might point to the same thing in
+	       A and B.  */
+	    {
+	      /* Two different non-NULL pointers?  Merge to unknown.  */
+	      // TODO: add a constraint?
+	      svalue *merged_sval = new unknown_svalue (type);
+	      *merged_sid = m_merged_model->add_svalue (merged_sval);
+	      return true;
+	    }
+	}
+      else if (region_sval_a == NULL && region_sval_b == NULL)
+	{
+	  /* Both are NULL.  */
+	  svalue *merged_sval = new region_svalue (type, region_id::null ());
+	  *merged_sid = m_merged_model->add_svalue (merged_sval);
+	  return true;
+	}
+      else
+	{
+	  /* If we have NULL vs non-NULL, then merge to unknown.  */
+	  svalue *merged_sval = new unknown_svalue (type);
+	  *merged_sid = m_merged_model->add_svalue (merged_sval);
+	  return true;
+	}
+    }
 
   // TODO: what about other kinds of value?
 
+  /* See what kinds of pairs we're getting here.  */
+  //gcc_unreachable ();
+
   return false;
 }
 
@@ -4530,6 +4615,9 @@ test_state_merging ()
 
   /* TODO: various things
      - heap regions
+     - value merging:
+       - every combination, but in particular
+          - pairs of regions
    */
 }
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local.c
index 32c7191..176ed46 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local.c
@@ -12,8 +12,7 @@ do_stuff (int *p, int n)
   for (i = 0; i < n; i++)
     p[i] = i;
   for (i = 0; i < n; i++)
-    sum += foo (p[i]); /* { dg-bogus "uninitialized" "" { xfail *-*-* } } */
-  /* FIXME: overzealous state-merging is eliminating p, I think.  */
+    sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
   return sum;
 }
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-4.c b/gcc/testsuite/gcc.dg/analyzer/paths-4.c
index d085f00..aedf963 100644
--- a/gcc/testsuite/gcc.dg/analyzer/paths-4.c
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-4.c
@@ -14,7 +14,7 @@ int test_1 (struct state *s)
   while (1)
     {
       __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
-      __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+      __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
       do_stuff (s, s->mode);
     }
 }
@@ -25,7 +25,7 @@ int test_2 (struct state *s)
   while (1)
     {
       __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
-      __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+      __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
       switch (s->mode)
 	{
 	case 0:
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-5.c b/gcc/testsuite/gcc.dg/analyzer/paths-5.c
index 9b5b5af..bc8b25e 100644
--- a/gcc/testsuite/gcc.dg/analyzer/paths-5.c
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-5.c
@@ -6,9 +6,7 @@ void test (int *p, int n)
   __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
   for (i = 0; i < n; i++)
     {
-      p[i] = i; /* { dg-bogus "uninitialized" "" { xfail *-*-* } } */
-      /* FIXME: overzealous state-merging is eliminating p, I think.  */
-
+      p[i] = i; /* { dg-bogus "uninitialized" } */
       __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
     }
 }
-- 
1.8.5.3

