From 460cd33ad78b431eac9d2636ec20887f39cdaca8 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 2 Jul 2019 14:32:50 -0400
Subject: [PATCH 59/63] FIXME: WIP on region_model::can_merge_with_p

---
 gcc/analyzer/region-model.cc | 111 ++++++++++++++++++++++++++++++++++++++-----
 gcc/analyzer/region-model.h  |  31 +++++++++++-
 2 files changed, 129 insertions(+), 13 deletions(-)

diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 932b4af..34bbdb9 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1347,6 +1347,35 @@ stack_region::remap_region_ids (const region_id_map &map)
     map.update (&m_frame_rids[i]);
 }
 
+/* FIXME.  */
+
+bool
+stack_region::can_merge_p (const stack_region *stack_region_a,
+			   const stack_region *stack_region_b,
+			   const region_merger &merger)
+{
+  if (stack_region_a->get_num_frames ()
+      != stack_region_b->get_num_frames ())
+    return false;
+
+  for (unsigned i = 0; i < stack_region_a->get_num_frames (); i++)
+    {
+      region_id rid_a = stack_region_a->get_frame_rid (i);
+      frame_region *frame_a
+	= merger.m_model_a->get_region <frame_region> (rid_a);
+
+      region_id rid_b = stack_region_b->get_frame_rid (i);
+      frame_region *frame_b
+	= merger.m_model_b->get_region <frame_region> (rid_b);
+
+      if (frame_a->get_function () != frame_b->get_function ())
+	return false;
+      merger.m_merged_model->push_frame (frame_a->get_function (), NULL);
+    }
+
+  return true;
+}
+
 /* class heap_region : public region.  */
 
 /* FIXME.  */
@@ -1602,6 +1631,14 @@ root_region::ensure_heap_region (region_model *model)
 
 /* FIXME.  */
 
+heap_region *
+root_region::get_heap_region (const region_model *model) const
+{
+  return model->get_region <heap_region> (m_heap_rid);
+}
+
+/* FIXME.  */
+
 void
 root_region::remap_region_ids (const region_id_map &map)
 {
@@ -1612,6 +1649,48 @@ root_region::remap_region_ids (const region_id_map &map)
 
 /* FIXME.  */
 
+bool
+root_region::can_merge_p (const root_region *root_region_a,
+			  const root_region *root_region_b,
+			  root_region *merged_root_region,
+			  const region_merger &merger)
+{
+  /* We can only merge if the stacks are sufficiently similar.  */
+  stack_region *stack_a = root_region_a->get_stack_region (merger.m_model_a);
+  stack_region *stack_b = root_region_b->get_stack_region (merger.m_model_b);
+  if (stack_a && stack_b)
+    {
+      /* If the two models both have a stack, attempt to merge them.  */
+      merged_root_region->ensure_stack_region (merger.m_merged_model);
+      if (!stack_region::can_merge_p (stack_a, stack_b, merger))
+	return false;
+    }
+  else if (stack_a || stack_b)
+    /* Don't attempt to merge if one model has a stack and the other
+       doesn't.  */
+    return false;
+
+  map_region *globals_a = root_region_a->get_globals_region (merger.m_model_a);
+  map_region *globals_b = root_region_b->get_globals_region (merger.m_model_b);
+  if (globals_a && globals_b)
+    {
+      // FIXME: attempt to merge, or fail
+    }
+  /* otherwise, merge as "no globals".  */
+
+  heap_region *heap_a = root_region_a->get_heap_region (merger.m_model_a);
+  heap_region *heap_b = root_region_b->get_heap_region (merger.m_model_b);
+  if (heap_a && heap_b)
+    {
+      // FIXME: attempt to merge, or fail
+    }
+  /* otherwise, merge as "no heap".  */
+
+  return true;
+}
+
+/* FIXME.  */
+
 void
 root_region::add_to_hash (inchash::hash &hstate) const
 {
@@ -3574,12 +3653,25 @@ region_model::poison_any_pointers_to_bad_regions (const region_id_set &
 /* FIXME.  */
 
 bool
-region_model::can_merge_with_p (const region_model &other,
-				region_model *out) const
+region_model::can_merge_with_p (const region_model &other_model,
+				region_model *out_model) const
 {
+  gcc_assert (m_root_rid == other_model.m_root_rid);
+  gcc_assert (m_root_rid.as_int () == 0);
+
   /* TODO: assert they have the same frames.  */
 
-#if 0
+#if 1
+#if 1
+  region_model result;
+  region_merger merger (this, &other_model, &result);
+
+  if (!root_region::can_merge_p (get_root_region (),
+				 other_model.get_root_region (),
+				 result.get_root_region (),
+				 merger))
+    return false;
+#else
   if (m_regions.length () != other.m_regions.length ())
     return false;
 
@@ -3649,8 +3741,8 @@ region_model::can_merge_with_p (const region_model &other,
     }
 
   // TODO: constraint manager
+#endif
 
-  result.validate ();
 #else
   /* FIXME: for now, forget everything.  */
   region_model result;
@@ -3667,8 +3759,10 @@ region_model::can_merge_with_p (const region_model &other,
     }
 #endif
 
-  if (out)
-    *out = result;
+  result.validate ();
+
+  if (out_model)
+    *out_model = result;
 
   return true;
 }
@@ -4161,11 +4255,6 @@ test_state_merging ()
     ASSERT_TRUE (model0.can_merge_with_p (model1, &merged));
     ASSERT_NE (model0, merged);
     ASSERT_NE (model1, merged);
-
-#if 0
-    // FIXME: hack to trigger a failure:
-    ASSERT_FALSE (model0.can_merge_with_p (model1));
-#endif
   }
 
   /* Verify handling of a PARM_DECL.  */
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 061c826..bd5e2ed 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -38,6 +38,7 @@ class svalue;
 class region;
 class region_model;
 class region_model_context;
+struct region_merger;
 class pending_diagnostic;
 
 class constraint_manager;
@@ -619,6 +620,10 @@ public:
   unsigned get_num_frames () const { return m_frame_rids.length (); }
   region_id get_frame_rid (unsigned i) const { return m_frame_rids[i]; }
 
+  static bool can_merge_p (const stack_region *stack_region_a,
+			   const stack_region *stack_region_b,
+			   const region_merger &merger);
+
  private:
   void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
   void print_fields (const region_model &model,
@@ -682,9 +687,15 @@ public:
   map_region *get_globals_region (const region_model *model) const;
 
   region_id ensure_heap_region (region_model *model);
+  heap_region *get_heap_region (const region_model *model) const;
 
   void remap_region_ids (const region_id_map &map) FINAL OVERRIDE;
 
+  static bool can_merge_p (const root_region *root_region_a,
+			   const root_region *root_region_b,
+			   root_region *merged_root_region,
+			   const region_merger &merger);
+
 private:
   void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
   void print_fields (const region_model &model,
@@ -849,8 +860,8 @@ class region_model
 				      purge_stats *stats,
 				      logger *logger);
 
-  bool can_merge_with_p (const region_model &other,
-			 region_model *out) const;
+  bool can_merge_with_p (const region_model &other_model,
+			 region_model *out_model) const;
 
  private:
   void update_for_call_superedge (const call_superedge &call_edge,
@@ -931,6 +942,22 @@ class region_model_context
 
 };
 
+/* FIXME.  */
+
+struct region_merger
+{
+  region_merger (const region_model *model_a,
+		 const region_model *model_b,
+		 region_model *merged_model)
+  : m_model_a (model_a), m_model_b (model_b),
+    m_merged_model (merged_model)
+  {}
+
+  const region_model *m_model_a;
+  const region_model *m_model_b;
+  region_model *m_merged_model;
+};
+
 namespace inchash
 {
   extern void add (svalue_id sid, hash &hstate);
-- 
1.8.5.3

