From b766dc2661056223fb6a3d8a1b499b49f9db20c4 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 5 Jun 2019 06:44:52 -0400
Subject: [PATCH 27/28] FIXME: 2019-06-05: further work on region model

---
 gcc/analyzer/impl-4.cc                             |   7 ++
 gcc/analyzer/region-model.cc                       | 110 ++++++++++++++++-----
 gcc/analyzer/region-model.h                        |   7 ++
 gcc/builtins.def                                   |  10 ++
 gcc/common.opt                                     |   2 +-
 .../NOT_FOR_UPSTREAM-CVE-2005-1689-krb5-recvauth.c |   4 +-
 .../NOT_FOR_UPSTREAM-clang-paper-example-1.c       |   2 -
 .../NOT_FOR_UPSTREAM-clang-paper-example-2.c       |   2 -
 .../NOT_FOR_UPSTREAM-clang-paper-example-3.c       |   2 -
 .../NOT_FOR_UPSTREAM-clang-paper-example-4.c       |   2 -
 gcc/testsuite/gcc.dg/analyzer/abort.c              |   2 -
 gcc/testsuite/gcc.dg/analyzer/conditionals.c       |   2 -
 gcc/testsuite/gcc.dg/analyzer/data-model-1.c       |   2 -
 gcc/testsuite/gcc.dg/analyzer/data-model-2.c       |   2 +-
 gcc/testsuite/gcc.dg/analyzer/data-model-3.c       |   2 +-
 gcc/testsuite/gcc.dg/analyzer/data-model-4.c       |   2 +-
 gcc/testsuite/gcc.dg/analyzer/data-model-5.c       |   2 -
 gcc/testsuite/gcc.dg/analyzer/data-model-5b.c      |  91 +++++++++++++++++
 gcc/testsuite/gcc.dg/analyzer/data-model-5c.c      |  84 ++++++++++++++++
 gcc/testsuite/gcc.dg/analyzer/data-model-5d.c      |  63 ++++++++++++
 gcc/testsuite/gcc.dg/analyzer/data-model-6.c       |  13 +++
 gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c  |   2 -
 gcc/testsuite/gcc.dg/analyzer/equivalence.c        |   2 -
 gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c     |   2 -
 gcc/testsuite/gcc.dg/analyzer/loop-2.c             |   1 -
 gcc/testsuite/gcc.dg/analyzer/loop-3.c             |   8 +-
 gcc/testsuite/gcc.dg/analyzer/loop.c               |   1 -
 gcc/testsuite/gcc.dg/analyzer/malloc-1.c           |   6 +-
 gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c    |   2 -
 .../gcc.dg/analyzer/malloc-many-paths-1.c          |  14 +++
 .../gcc.dg/analyzer/malloc-many-paths-2.c          |  93 +++++++++++++++++
 .../gcc.dg/analyzer/malloc-many-paths-3.c          |  36 +++++++
 gcc/testsuite/gcc.dg/analyzer/malloc-many-paths.c  |  83 ----------------
 gcc/testsuite/gcc.dg/analyzer/operations.c         |   2 -
 gcc/testsuite/gcc.dg/analyzer/params-2.c           |   2 -
 gcc/testsuite/gcc.dg/analyzer/params.c             |   2 -
 gcc/testsuite/gcc.dg/analyzer/switch.c             |   2 -
 37 files changed, 515 insertions(+), 156 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-5b.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-5c.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-5d.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-6.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-3.c
 delete mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-many-paths.c

diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index 624c6af..85de7af 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -2947,6 +2947,13 @@ exploded_node::on_stmt (exploded_graph &eg) const
 	     to stderr.  */
 	  dump ();
 	}
+      else if (is_named_call_p (call, "__analyzer_dump_region_model", 0))
+	{
+	  /* Handle the builtin "__analyzer_dump_region_model" by dumping
+	     the region model's state to stderr (if any).  */
+	  if (new_state.m_region_model)
+	    new_state.m_region_model->dump ();
+	}
       else if (is_named_call_p (call, "__analyzer_eval", 1))
 	{
 	  /* Handle the builtin "__analyzer_eval" by evaluating the input
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index dfd8377..f2fb924 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1258,31 +1258,12 @@ stack_region::pop_frame (region_model *model, purge_stats *stats,
   if (result)
     result_sid = model->get_rvalue (result, ctxt);
 
-  /* Pop the frame.  */
+  /* Pop the frame RID.  */
   m_frame_rids.pop ();
 
-  // FIXME: maybe make the rest be a member of region_model
-
-  /* Find all child and descendent regions.  */
-  region_id_set descendents (model);
-  model->get_descendents (frame_rid, &descendents);
-
-  /* Find any pointers to such regions; convert to poisoned.  */
-  int i;
-  svalue *sval;
-  FOR_EACH_VEC_ELT (model->m_svalues, i, sval)
-    if (region_svalue *ptr_sval = sval->dyn_cast_region_svalue ())
-      {
-	region_id ptr_dst = ptr_sval->get_pointee ();
-	if (!ptr_dst.null_p ())
-	  if (descendents.region_p (ptr_dst))
-	    model->replace_svalue
-	      (svalue_id::from_int (i),
-	       new poisoned_svalue (POISON_KIND_POPPED_STACK));
-      }
-
-  /* Delete all such regions.  */
-  model->purge_regions (descendents, stats);
+  model->delete_region_and_descendents (frame_rid,
+					POISON_KIND_POPPED_STACK,
+					stats);
 
   /* Delete unused svalues, but don't delete the return value.  */
   model->purge_svalues (stats, &result_sid);
@@ -2090,6 +2071,31 @@ region_model::on_call (const gcall *call, region_model_context *ctxt)
 	}
       return;
     }
+  else if (is_named_call_p (call, "free", 1))
+    {
+      tree ptr = gimple_call_arg (call, 0);
+      svalue_id ptr_sid = get_rvalue (ptr, ctxt);
+      svalue *ptr_sval = get_svalue (ptr_sid);
+      if (region_svalue *ptr_to_region_sval
+	  = ptr_sval->dyn_cast_region_svalue ())
+	{
+	  /* If the ptr points to an underlying heap region, delete it,
+	     poisoning pointers.  */
+	  region_id pointee_rid = ptr_to_region_sval->get_pointee ();
+	  region_id heap_rid = get_root_region ()->ensure_heap_region (this);
+	  if (get_region (pointee_rid)->get_parent () == heap_rid)
+	    {
+	      purge_stats stats;
+	      delete_region_and_descendents (pointee_rid,
+					     POISON_KIND_FREED,
+					     &stats);
+	      purge_svalues (&stats, NULL);
+	      validate ();
+	      // TODO: do anything with stats?
+	    }
+	}
+      return;
+    }
   else if (is_named_call_p (call, "__builtin_alloca", 1))
     {
       region_id frame_rid = get_current_frame_id ();
@@ -2127,6 +2133,21 @@ region_model::on_call (const gcall *call, region_model_context *ctxt)
       gcc_unreachable ();
 #endif
     }
+  else if (is_named_call_p (call, "__analyzer_dump_num_heap_regions", 0))
+    {
+      /* Handle the builtin "__analyzer_dump_num_heap_regions" by emitting
+	 a warning (for use in DejaGnu tests).  */
+      int num_heap_regions = 0;
+      region_id heap_rid = get_root_region ()->ensure_heap_region (this);
+      unsigned i;
+      region *region;
+      FOR_EACH_VEC_ELT (m_regions, i, region)
+	if (region->get_parent () == heap_rid)
+	  num_heap_regions++;
+      /* Use quotes to ensure the output isn't truncated.  */
+      warning_at (call->location, 0,
+		  "num heap regions: %qi", num_heap_regions);
+    }
 
   /* Unrecognized call.  */
 
@@ -3219,6 +3240,49 @@ region_model::get_descendents (region_id rid, region_id_set *out) const
     }
 }
 
+/* Delete RID and all descendent regions.
+   Find any pointers to such regions; convert convert them to
+   poisoned values of kind PKIND.
+   Accumulate stats on purged entities into STATS.  */
+
+void
+region_model::delete_region_and_descendents (region_id rid,
+					     enum poison_kind pkind,
+					     purge_stats *stats)
+{
+  /* Find all child and descendent regions.  */
+  region_id_set descendents (this);
+  get_descendents (rid, &descendents);
+
+  /* Find any pointers to such regions; convert to poisoned.  */
+  poison_any_pointers_to_bad_regions (descendents, pkind);
+
+  /* Delete all such regions.  */
+  purge_regions (descendents, stats);
+}
+
+/* Find any pointers to regions within BAD_REGIONS; convert them to
+   poisoned values of kind PKIND.  */
+
+void
+region_model::poison_any_pointers_to_bad_regions (const region_id_set &
+						    bad_regions,
+						  enum poison_kind pkind)
+{
+  int i;
+  svalue *sval;
+  FOR_EACH_VEC_ELT (m_svalues, i, sval)
+    if (region_svalue *ptr_sval = sval->dyn_cast_region_svalue ())
+      {
+	region_id ptr_dst = ptr_sval->get_pointee ();
+	if (!ptr_dst.null_p ())
+	  if (bad_regions.region_p (ptr_dst))
+	    replace_svalue
+	      (svalue_id::from_int (i),
+	       new poisoned_svalue (pkind));
+      }
+}
+
 /* FIXME.  */
 
 void
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 7be2792..e3515ad 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -807,6 +807,10 @@ class region_model
 
   void get_descendents (region_id rid, region_id_set *out) const;
 
+  void delete_region_and_descendents (region_id rid,
+				      enum poison_kind pkind,
+				      purge_stats *stats);
+
  private:
   void update_for_call_superedge (const call_superedge &call_edge,
 				  region_model_context *ctxt);
@@ -819,6 +823,9 @@ class region_model
 				      const gswitch *switch_stmt,
 				      region_model_context *ctxt);
 
+  void poison_any_pointers_to_bad_regions (const region_id_set &bad_regions,
+					   enum poison_kind pkind);
+
  public: // FIXME:
   auto_delete_vec<svalue> m_svalues;
   auto_delete_vec<region> m_regions;
diff --git a/gcc/builtins.def b/gcc/builtins.def
index f1c7687..fef730d 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -1101,6 +1101,16 @@ DEF_GCC_BUILTIN (BUILT_IN_LINE, "LINE", BT_FN_INT, ATTR_NOTHROW_LEAF_LIST)
 DEF_BUILTIN (BUILT_IN_ANALYZER_DUMP, "__analyzer_dump", BUILT_IN_NORMAL, BT_FN_VOID, BT_LAST,
 	     false, false, false, ATTR_NULL, true, true)
 
+DEF_BUILTIN (BUILT_IN_ANALYZER_DUMP_REGION_MODEL,
+	    "__analyzer_dump_region_model",
+	    BUILT_IN_NORMAL, BT_FN_VOID, BT_LAST,
+	    false, false, false, ATTR_NULL, true, true)
+
+DEF_BUILTIN (BUILT_IN_ANALYZER_DUMP_NUM_HEAP_REGIONS,
+	    "__analyzer_dump_num_heap_regions",
+	    BUILT_IN_NORMAL, BT_FN_VOID, BT_LAST,
+	    false, false, false, ATTR_NULL, true, true)
+
 DEF_BUILTIN (BUILT_IN_ANALYZER_EVAL, "__analyzer_eval", BUILT_IN_NORMAL, BT_FN_VOID_INT, BT_LAST,
 	     false, false, false, ATTR_NULL, true, true)
 
diff --git a/gcc/common.opt b/gcc/common.opt
index 68a0285..93be813 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1457,7 +1457,7 @@ Common Var(flag_analyzer_verbose_state_changes) Init(0)
 FIXME
 
 fanalyzer-region-model
-Common Var(flag_analyzer_region_model) Init(0)
+Common Var(flag_analyzer_region_model) Init(1)
 FIXME
 
 fdump-final-insns
diff --git a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-CVE-2005-1689-krb5-recvauth.c b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-CVE-2005-1689-krb5-recvauth.c
index 4c87277..139f200 100644
--- a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-CVE-2005-1689-krb5-recvauth.c
+++ b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-CVE-2005-1689-krb5-recvauth.c
@@ -10670,7 +10670,7 @@ recvauth_common(krb5_context context,
   error.error = 60;
  message = error_message(problem);
  error.text.length = strlen(message) + 1;
- if (!(error.text.data = malloc(error.text.length))) {
+ if (!(error.text.data = malloc(error.text.length))) {  /* { dg-bogus "analysis bailed out early" "" { xfail *-*-* } } */
      retval = 
              12
                    ;
@@ -10722,7 +10722,7 @@ cleanup:;
                                                         );
  }
     }
-    return retval; /* { dg-bogus "analysis bailed out early" "" { xfail *-*-* } } */
+    return retval;
 }
 
 krb5_error_code
diff --git a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-1.c b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-1.c
index 585a06f..77e4965 100644
--- a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-1.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 void test_1 (void)
 {
   int x, y;
diff --git a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-2.c b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-2.c
index 93e7f1a..3622c4b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-2.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 #include <stdlib.h>
 
 void test_2 (void)
diff --git a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-3.c b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-3.c
index 5310c38..1daab33 100644
--- a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-3.c
+++ b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-3.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 #include <stdlib.h>
 
 struct s1
diff --git a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-4.c b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-4.c
index 818166c..12be723 100644
--- a/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-4.c
+++ b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-4.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 #include <stddef.h>
 
 struct s { int data[2]; };
diff --git a/gcc/testsuite/gcc.dg/analyzer/abort.c b/gcc/testsuite/gcc.dg/analyzer/abort.c
index 285a4c7..cfbf74c 100644
--- a/gcc/testsuite/gcc.dg/analyzer/abort.c
+++ b/gcc/testsuite/gcc.dg/analyzer/abort.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals.c b/gcc/testsuite/gcc.dg/analyzer/conditionals.c
index e5da0ed..86b9d45 100644
--- a/gcc/testsuite/gcc.dg/analyzer/conditionals.c
+++ b/gcc/testsuite/gcc.dg/analyzer/conditionals.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 void test (int i, int j)
 {
   if (i > 4)
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
index e68d660..60bab35 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 #include <stdlib.h>
 #include <string.h>
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-2.c b/gcc/testsuite/gcc.dg/analyzer/data-model-2.c
index fe737fe..d5be03b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-2.c
@@ -1,4 +1,4 @@
-/* { dg-additional-options "-fanalyzer-region-model -O2" } */
+/* { dg-additional-options "-O2" } */
 /* TODO:is there a way to automatically run the tests on various
    optimizations levels, and with/without debuginfo, rather than
    hardcoding options?  Adapt from torture .exp, presumably.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-3.c b/gcc/testsuite/gcc.dg/analyzer/data-model-3.c
index 8aeaaf9..3d572eb 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-3.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-3.c
@@ -1,4 +1,4 @@
-/* { dg-additional-options "-fanalyzer-region-model -O2" } */
+/* { dg-additional-options "-O2" } */
 /* TODO:is there a way to automatically run the tests on various
    optimizations levels, and with/without debuginfo, rather than
    hardcoding options?  Adapt from torture .exp, presumably.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-4.c b/gcc/testsuite/gcc.dg/analyzer/data-model-4.c
index 486d4f5..33f9087 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-4.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-4.c
@@ -1,4 +1,4 @@
-/* { dg-additional-options "-fanalyzer-region-model -fexceptions" } */
+/* { dg-additional-options "-fexceptions" } */
 /* TODO:is there a way to automatically run the tests on various
    optimizations levels, and with/without debuginfo, rather than
    hardcoding options?  Adapt from torture .exp, presumably.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
index ed02407..d2ca406 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 /* A toy re-implementation of CPython's object model.  */
 
 #include <stddef.h>
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5b.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5b.c
new file mode 100644
index 0000000..1f910c5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5b.c
@@ -0,0 +1,91 @@
+/* A toy re-implementation of CPython's object model.  */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct base_obj base_obj;
+typedef struct type_obj type_obj;
+typedef struct string_obj string_obj;
+
+struct base_obj
+{
+  struct type_obj *ob_type;
+  int ob_refcnt;
+};
+
+struct type_obj
+{
+  base_obj tp_base;
+  void (*tp_dealloc) (base_obj *);
+};
+
+struct string_obj
+{
+  base_obj str_base;
+  size_t str_len;
+  char str_buf[];
+};
+
+extern void type_del (base_obj *);
+extern void str_del (base_obj *);
+
+type_obj type_type = {
+  { &type_type, 1},
+  type_del
+};
+
+type_obj str_type = {
+  { &str_type, 1},
+  str_del
+};
+
+base_obj *alloc_obj (type_obj *ob_type, size_t sz)
+{
+  base_obj *obj = (base_obj *)malloc (sz);
+  if (!obj)
+    return NULL;
+  obj->ob_type = ob_type;
+  obj->ob_refcnt = 1;
+  return obj;
+}
+
+string_obj *new_string_obj (const char *str)
+{
+  //__analyzer_dump ();
+  size_t len = strlen (str);
+#if 1
+  string_obj *str_obj
+    = (string_obj *)alloc_obj (&str_type, sizeof (string_obj) + len + 1);
+#else
+  string_obj *str_obj = (string_obj *)malloc (sizeof (string_obj) + len + 1);
+  if (!str_obj)
+    return NULL;
+  str_obj->str_base.ob_type = &str_type;
+  str_obj->str_base.ob_refcnt = 1;
+#endif
+  str_obj->str_len = len; /* { dg-warning "use of NULL 'str_obj'" } */
+  memcpy (str_obj->str_buf, str, len);
+  str_obj->str_buf[len] = '\0';
+  return str_obj;
+}
+
+void unref (string_obj *obj)
+{
+  //__analyzer_dump();
+  if (--obj->str_base.ob_refcnt == 0)
+    {
+      //__analyzer_dump();
+      obj->str_base.ob_type->tp_dealloc ((base_obj *)obj); /* { dg-bogus "dereference of uninitialized pointer '<unknown>'" "" { xfail *-*-* } } */
+      // TODO (xfail): not sure what's going on here
+    }
+}
+
+void test_1 (const char *str)
+{
+  string_obj *obj = new_string_obj (str);
+  //__analyzer_dump();
+  if (obj)
+    unref (obj);
+} /* { dg-bogus "leak of 'obj'" "" { xfail *-*-* } } */
+// TODO (xfail): not sure why this is treated as leaking
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5c.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5c.c
new file mode 100644
index 0000000..a522038
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5c.c
@@ -0,0 +1,84 @@
+/* A toy re-implementation of CPython's object model.  */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct base_obj base_obj;
+typedef struct type_obj type_obj;
+typedef struct string_obj string_obj;
+
+struct base_obj
+{
+  struct type_obj *ob_type;
+  int ob_refcnt;
+};
+
+struct type_obj
+{
+  base_obj tp_base;
+};
+
+struct string_obj
+{
+  base_obj str_base;
+  size_t str_len;
+  char str_buf[];
+};
+
+type_obj type_type = {
+  { &type_type, 1},
+};
+
+type_obj str_type = {
+  { &str_type, 1},
+};
+
+base_obj *alloc_obj (type_obj *ob_type, size_t sz)
+{
+  base_obj *obj = (base_obj *)malloc (sz);
+  if (!obj)
+    return NULL;
+  obj->ob_type = ob_type;
+  obj->ob_refcnt = 1;
+  return obj;
+}
+
+string_obj *new_string_obj (const char *str)
+{
+  //__analyzer_dump ();
+  size_t len = strlen (str);
+#if 1
+  string_obj *str_obj
+    = (string_obj *)alloc_obj (&str_type, sizeof (string_obj) + len + 1);
+#else
+  string_obj *str_obj = (string_obj *)malloc (sizeof (string_obj) + len + 1);
+  if (!str_obj)
+    return NULL;
+  str_obj->str_base.ob_type = &str_type;
+  str_obj->str_base.ob_refcnt = 1;
+#endif
+  str_obj->str_len = len; /* { dg-warning "use of NULL 'str_obj'" } */
+  memcpy (str_obj->str_buf, str, len);
+  str_obj->str_buf[len] = '\0';
+  return str_obj;
+}
+
+void unref (string_obj *obj)
+{
+  //__analyzer_dump();
+  if (--obj->str_base.ob_refcnt == 0)
+    {
+      //__analyzer_dump();
+      free (obj);
+    }
+}
+
+void test_1 (const char *str)
+{
+  string_obj *obj = new_string_obj (str);
+  //__analyzer_dump();
+  if (obj)
+    unref (obj);
+} /* { dg-bogus "leak of 'obj'" "" { xfail *-*-* } } */
+// TODO (xfail): not sure why this is treated as leaking
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5d.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5d.c
new file mode 100644
index 0000000..50ab0ac
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5d.c
@@ -0,0 +1,63 @@
+/* A toy re-implementation of CPython's object model.  */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct base_obj base_obj;
+typedef struct type_obj type_obj;
+typedef struct string_obj string_obj;
+
+struct base_obj
+{
+  struct type_obj *ob_type;
+  int ob_refcnt;
+};
+
+struct type_obj
+{
+  base_obj tp_base;
+};
+
+struct string_obj
+{
+  base_obj str_base;
+  size_t str_len;
+  char str_buf[];
+};
+
+type_obj type_type = {
+  { &type_type, 1},
+};
+
+type_obj str_type = {
+  { &str_type, 1},
+};
+
+base_obj *alloc_obj (type_obj *ob_type, size_t sz)
+{
+  base_obj *obj = (base_obj *)malloc (sz);
+  if (!obj)
+    return NULL;
+  obj->ob_type = ob_type;
+  obj->ob_refcnt = 1;
+  return obj;
+}
+
+void unref (base_obj *obj)
+{
+  //__analyzer_dump();
+  if (--obj->ob_refcnt == 0)
+    free (obj);
+}
+
+void test_1 ()
+{
+  base_obj *obj = alloc_obj (&str_type, sizeof (string_obj));
+  if (obj)
+    {
+      __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '1'" } */
+      unref (obj);
+      __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-6.c b/gcc/testsuite/gcc.dg/analyzer/data-model-6.c
new file mode 100644
index 0000000..8b63ab2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-6.c
@@ -0,0 +1,13 @@
+#include <stdlib.h>
+
+/* Verify that we don't accumulate state after a malloc/free pair.  */
+
+void test (void)
+{
+  void *ptr;
+  __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */
+  ptr = malloc (1024);
+  __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '1'" } */
+  free (ptr);
+  __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c
index ec7ef87..10205ae 100644
--- a/gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 #include <stddef.h>
 
 static int *__attribute__((noinline))
diff --git a/gcc/testsuite/gcc.dg/analyzer/equivalence.c b/gcc/testsuite/gcc.dg/analyzer/equivalence.c
index 3986967..fb28660 100644
--- a/gcc/testsuite/gcc.dg/analyzer/equivalence.c
+++ b/gcc/testsuite/gcc.dg/analyzer/equivalence.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 void test (int p, int q, int r)
 {
   if (p == 42)
diff --git a/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c b/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c
index 44263bf..967aa76 100644
--- a/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 #include <stdlib.h>
 
 typedef void (*fn_ptr_t) (void *);
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-2.c b/gcc/testsuite/gcc.dg/analyzer/loop-2.c
index 3636000..444b7c3 100644
--- a/gcc/testsuite/gcc.dg/analyzer/loop-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-2.c
@@ -7,7 +7,6 @@
 
    So we need something smarter to cope with this case.  */
 
-/* { dg-additional-options "-fanalyzer-region-model" } */
 /* { dg-additional-options "-fno-analyzer-state-purge" } */
 
 struct s
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-3.c b/gcc/testsuite/gcc.dg/analyzer/loop-3.c
index 25c0ac0..975afe4 100644
--- a/gcc/testsuite/gcc.dg/analyzer/loop-3.c
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-3.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 #include <stdlib.h>
 
 void test(int c)
@@ -8,13 +6,11 @@ void test(int c)
   char *buffer = (char*)malloc(256);
 
   for (i=0; i<255; i++) {
-    buffer[i] = c; /* BUG: the malloc could have failed
+    buffer[i] = c; /* { dg-warning "dereference of freed pointer 'buffer'" } */
+		   /* BUG: the malloc could have failed
 		      TODO: the checker doesn't yet pick up on this, perhaps
 		      due to the pointer arithmetic not picking up on the
 		      state */
-                   /* TODO: presumably should warn about a use-after-free
-		      on the second time through the loop */
-
     free(buffer); /* { dg-warning "double-free of 'buffer'" } */
   }
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop.c b/gcc/testsuite/gcc.dg/analyzer/loop.c
index 936351b..aeba702 100644
--- a/gcc/testsuite/gcc.dg/analyzer/loop.c
+++ b/gcc/testsuite/gcc.dg/analyzer/loop.c
@@ -1,5 +1,4 @@
 // FIXME:
-/* { dg-additional-options "-fanalyzer-region-model" } */
 /* { dg-additional-options "-fno-analyzer-state-purge" } */
 
 void test(void)
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
index bb774a3..d9fc2e4 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 #include <stdlib.h>
 #include <string.h>
 
@@ -372,8 +370,8 @@ void test_34 (void)
   p->x = 0.0f;
   q = &p->x;
   free (p);
-  *q = 1.0f; /* { dg-warning "use after free of '*q'" "" { xfail *-*-* } } */
-  // TODO(xfail): requires use of region model
+  *q = 1.0f; /* { dg-warning "dereference of freed pointer 'q'" } */
+  // TODO: better wording? it's freed memory, not a freed pointer
 };
 
 int test_35 (void)
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c
index 0121516..7eecadf 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 #include <stdlib.h>
 
 extern void foo (void);
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-1.c
new file mode 100644
index 0000000..82ad547
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-1.c
@@ -0,0 +1,14 @@
+#include <stdlib.h>
+
+/* Ensure that we don't need to laboriously walk every path to get
+   to the end of the function.  */
+
+int test_1 (int n)
+{
+  int i, j, k;
+  k = 0;
+  for (int i = 0; i < n; i++)
+    for (int j = 0; j < 1000; j++)
+      k++;
+  return k;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-2.c b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-2.c
new file mode 100644
index 0000000..4bc487a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-2.c
@@ -0,0 +1,93 @@
+#include <stdlib.h>
+
+/* Ensure that we don't get an exponential growth in paths due to
+   repeated diamonds in the CFG.  */
+
+typedef struct obj {
+  int ob_refcnt;
+} PyObject;
+
+extern void Py_Dealloc (PyObject *op);
+
+#define Py_DECREF(op)                                   \
+    do {                                                \
+      if (--((PyObject*)(op))->ob_refcnt == 0)		\
+	Py_Dealloc((PyObject *)(op));			\
+    } while (0)
+
+int test (PyObject *obj_01, PyObject *obj_02, PyObject *obj_03,
+	  PyObject *obj_04, PyObject *obj_05, PyObject *obj_06,
+	  PyObject *obj_07, PyObject *obj_08, PyObject *obj_09,
+	  PyObject *obj_10, PyObject *obj_11, PyObject *obj_12,
+	  PyObject *obj_13, PyObject *obj_14, PyObject *obj_15
+)
+{
+  Py_DECREF (obj_01); Py_DECREF (obj_02); Py_DECREF (obj_03);
+  Py_DECREF (obj_04); Py_DECREF (obj_05); Py_DECREF (obj_06);
+  Py_DECREF (obj_07); Py_DECREF (obj_08); Py_DECREF (obj_09);
+  Py_DECREF (obj_10); Py_DECREF (obj_11); Py_DECREF (obj_12);
+  Py_DECREF (obj_13); Py_DECREF (obj_14); Py_DECREF (obj_15);
+} /* { dg-bogus "analysis bailed out early" "" { xfail *-*-* } } */
+
+/* FIXME: why the explosion with the region model?
+
+   Why is it not e.g. merging nodes at "before (SN: 3 stmt: 0)"?
+   We have two enodes for that point:
+
+created EN: 17
+     point: callstring: [] before (SN: 3 stmt: 0):  _4 = obj_02_17(D)->ob_refcnt;
+     pruned_state: ps: {ec0: {(‘*(obj_01).ob_refcnt’ @ 0)}, ec1: {(‘<unknown>_10’ @ 0)}  |  }@0
+rmodel: svalues: [sv0: {poisoned: uninit}, sv1: {type: ‘struct PyObject *’, &r8}, sv2: {type: ‘struct PyObject *’, unknown}, sv3: {type: ‘struct PyObject *’, unknown}, sv4: {type: ‘int’, unknown}, sv5: {type: ‘int’, ‘0’}], regions: [r0: {kind: ‘root’, parent: null, sval: null}, r1: {kind: ‘stack’, parent: r0, sval: sv0}, r2: {kind: ‘frame’, parent: r1, sval: null, map: {‘obj_02_17(D)’: r5, ‘obj_03_20(D)’: r7, ‘obj_01’: r3, ‘obj_03’: r6, ‘obj_02’: r4}, function: ‘test’}, r3: {kind: ‘map’, parent: r2, sval: sv1, type: ‘struct PyObject *’, map: {}}, r4: {kind: ‘map’, parent: r2, sval: sv2, type: ‘struct PyObject *’, map: {}}, r5: {kind: ‘map’, parent: r2, sval: sv2, type: ‘struct PyObject *’, map: {}}, r6: {kind: ‘map’, parent: r2, sval: sv3, type: ‘struct PyObject *’, map: {}}, r7: {kind: ‘map’, parent: r2, sval: sv3, type: ‘struct PyObject *’, map: {}}, r8: {kind: ‘symbolic’, parent: r0, sval: null, map: {‘ob_refcnt’: r9}}, r9: {kind: ‘map’, parent: r8, sval: sv4, type: ‘int’, map: {}}], constraints: {ec0: {sv5 == ‘0’}, ec1: {sv4}  |  ec1: {sv4} != ec0: {sv5 == ‘0’}}
+
+created EN: 92
+     point: callstring: [] before (SN: 3 stmt: 0):  _4 = obj_02_17(D)->ob_refcnt;
+     pruned_state: ps: {ec0: {(‘*(obj_01).ob_refcnt’ @ 0)}, ec1: {(‘<unknown>_10’ @ 0)}  |  }@0
+rmodel: svalues: [sv0: {poisoned: uninit}, sv1: {type: ‘struct PyObject *’, &r8}, sv2: {type: ‘struct PyObject *’, unknown}, sv3: {type: ‘struct PyObject *’, unknown}, sv4: {type: ‘int’, unknown}, sv5: {type: ‘int’, ‘0’}], regions: [r0: {kind: ‘root’, parent: null, sval: null}, r1: {kind: ‘stack’, parent: r0, sval: sv0}, r2: {kind: ‘frame’, parent: r1, sval: null, map: {‘obj_02_17(D)’: r5, ‘obj_03_20(D)’: r7, ‘obj_01’: r3, ‘obj_03’: r6, ‘obj_02’: r4}, function: ‘test’}, r3: {kind: ‘map’, parent: r2, sval: sv1, type: ‘struct PyObject *’, map: {}}, r4: {kind: ‘map’, parent: r2, sval: sv2, type: ‘struct PyObject *’, map: {}}, r5: {kind: ‘map’, parent: r2, sval: sv2, type: ‘struct PyObject *’, map: {}}, r6: {kind: ‘map’, parent: r2, sval: sv3, type: ‘struct PyObject *’, map: {}}, r7: {kind: ‘map’, parent: r2, sval: sv3, type: ‘struct PyObject *’, map: {}}, r8: {kind: ‘symbolic’, parent: r0, sval: null, map: {‘ob_refcnt’: r9}}, r9: {kind: ‘map’, parent: r8, sval: sv4, type: ‘int’, map: {}}], constraints: {ec0: {sv4 == sv5 == ‘0’}  |  }
+
+They are identical apart from the constraints:
+-constraints: {ec0: {sv5 == ‘0’}, ec1: {sv4}  |  ec1: {sv4} != ec0: {sv5 == ‘0’}}
++constraints: {ec0: {sv4 == sv5 == ‘0’}  |  }
+
+i.e. in both cases sv5 == 0, but they differ in whether sv4 == 0 or != 0
+
+r0: {kind: ‘root’, parent: null, sval: null}
+|-stack: r1: {kind: ‘stack’, parent: r0, sval: sv0}
+|  |: sval: sv0: {poisoned: uninit}
+|  `-frame for ‘test’: r2: {kind: ‘frame’, parent: r1, sval: null, map: {‘obj_02_17(D)’: r5, ‘obj_03_20(D)’: r7, ‘obj_03’: r6, ‘obj_01’: r3, ‘obj_02’: r4}, function: ‘test’}
+|    |-‘obj_01’: r3: {kind: ‘map’, parent: r2, sval: sv1, type: ‘struct PyObject *’, map: {}}
+|    |  |: sval: sv1: {type: ‘struct PyObject *’, &r8}
+|    |  |: type: ‘struct PyObject *’
+|    |-‘obj_02’: r4: {kind: ‘map’, parent: r2, sval: sv2, type: ‘struct PyObject *’, map: {}}
+|    |  |: sval: sv2: {type: ‘struct PyObject *’, unknown}
+|    |  |: type: ‘struct PyObject *’
+|    |-‘obj_02_17(D)’: r5: {kind: ‘map’, parent: r2, sval: sv2, type: ‘struct PyObject *’, map: {}}
+|    |  |: sval: sv2: {type: ‘struct PyObject *’, unknown}
+|    |  |: type: ‘struct PyObject *’
+|    |-‘obj_03’: r6: {kind: ‘map’, parent: r2, sval: sv3, type: ‘struct PyObject *’, map: {}}
+|    |  |: sval: sv3: {type: ‘struct PyObject *’, unknown}
+|    |  |: type: ‘struct PyObject *’
+|    `-‘obj_03_20(D)’: r7: {kind: ‘map’, parent: r2, sval: sv3, type: ‘struct PyObject *’, map: {}}
+|      |: sval: sv3: {type: ‘struct PyObject *’, unknown}
+|      |: type: ‘struct PyObject *’
+`-r8: {kind: ‘symbolic’, parent: r0, sval: null, map: {‘ob_refcnt’: r9}}
+  `-‘ob_refcnt’: r9: {kind: ‘map’, parent: r8, sval: sv4, type: ‘int’, map: {}}
+    |: sval: sv4: {type: ‘int’, unknown}
+    |: type: ‘int’
+svalues:
+  sv0: {poisoned: uninit}
+  sv1: {type: ‘struct PyObject *’, &r8}
+  sv2: {type: ‘struct PyObject *’, unknown}
+  sv3: {type: ‘struct PyObject *’, unknown}
+  sv4: {type: ‘int’, unknown}
+  sv5: {type: ‘int’, ‘0’}
+constraint manager:
+  equiv classes:
+    ec0: {sv5 == ‘0’}
+    ec1: {sv4}
+  constraints:
+    0: ec1: {sv4} != ec0: {sv5 == ‘0’}
+
+i.e. it's capturing whether obj_01->ob_refcnt is zero or non-zero
+
+Does obj_01 ever get reused, and if not, can we purge all of that state?
+Hmmm: none of these are SSA names, so they don't get purged.   */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-3.c b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-3.c
new file mode 100644
index 0000000..56cd0a6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-3.c
@@ -0,0 +1,36 @@
+#include <stdlib.h>
+
+extern int foo (void);
+
+int successes;
+int failures;
+
+#define ONE_DIAMOND \
+    do {                                                \
+      void *ptr = malloc (128);				\
+      if (foo ())					\
+	successes++;					\
+      else						\
+	failures++;					\
+      free (ptr);					\
+    } while (0)
+
+#define TEN_DIAMONDS \
+  do {								   \
+    ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND;	\
+    ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; \
+ } while (0)
+
+void test_3 (void *ptr)
+{
+  free (ptr);
+#if 1
+  ONE_DIAMOND;
+#else
+  /* TODO: enabling this leads to numerous duplicated reports,
+     all of them detailing all the extraneous info about the malloc/free
+     within the diamonds.  */
+  TEN_DIAMONDS;
+#endif
+  free (ptr); /* { dg-warning "double-free of 'ptr'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths.c b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths.c
deleted file mode 100644
index 062273d..0000000
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths.c
+++ /dev/null
@@ -1,83 +0,0 @@
-#include <stdlib.h>
-
-extern int foo (void);
-
-/* Ensure that we don't need to laboriously walk every path to get
-   to the end of the function.  */
-
-#if 1
-int test_1 (int n)
-{
-  int i, j, k;
-  k = 0;
-  for (int i = 0; i < n; i++)
-    for (int j = 0; j < 1000; j++)
-      k++;
-  return k;
-}
-#endif
-
-#if 1
-/* Ensure that we don't get an exponential growth in paths due to
-   repeated diamonds in the CFG.  */
-
-typedef struct obj {
-  int ob_refcnt;
-} PyObject;
-
-extern void Py_Dealloc (PyObject *op);
-
-#define Py_DECREF(op)                                   \
-    do {                                                \
-      if (--((PyObject*)(op))->ob_refcnt == 0)		\
-	Py_Dealloc((PyObject *)(op));			\
-    } while (0)
-
-int test_2 (PyObject *obj_01, PyObject *obj_02, PyObject *obj_03,
-	    PyObject *obj_04, PyObject *obj_05, PyObject *obj_06,
-	    PyObject *obj_07, PyObject *obj_08, PyObject *obj_09,
-	    PyObject *obj_10, PyObject *obj_11, PyObject *obj_12,
-	    PyObject *obj_13, PyObject *obj_14, PyObject *obj_15)
-{
-    Py_DECREF (obj_01); Py_DECREF (obj_02); Py_DECREF (obj_03);
-    Py_DECREF (obj_04); Py_DECREF (obj_05); Py_DECREF (obj_06);
-    Py_DECREF (obj_07); Py_DECREF (obj_08); Py_DECREF (obj_09);
-    Py_DECREF (obj_10); Py_DECREF (obj_11); Py_DECREF (obj_12);
-    Py_DECREF (obj_13); Py_DECREF (obj_14); Py_DECREF (obj_15);
-}
-#endif
-
-#if 1
-int successes;
-int failures;
-
-#define ONE_DIAMOND \
-    do {                                                \
-      void *ptr = malloc (128);				\
-      if (foo ())					\
-	successes++;					\
-      else						\
-	failures++;					\
-      free (ptr);					\
-    } while (0)
-
-#define TEN_DIAMONDS \
-  do {								   \
-    ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND;	\
-    ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; \
- } while (0)
-
-void test_3 (void *ptr)
-{
-  free (ptr);
-#if 1
-  ONE_DIAMOND;
-#else
-  /* TODO: enabling this leads to numerous duplicated reports,
-     all of them detailing all the extraneous info about the malloc/free
-     within the diamonds.  */
-  TEN_DIAMONDS;
-#endif
-  free (ptr); /* { dg-warning "double-free of 'ptr'" } */
-}
-#endif
diff --git a/gcc/testsuite/gcc.dg/analyzer/operations.c b/gcc/testsuite/gcc.dg/analyzer/operations.c
index a5e0997..9ea2327 100644
--- a/gcc/testsuite/gcc.dg/analyzer/operations.c
+++ b/gcc/testsuite/gcc.dg/analyzer/operations.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 void test (int i, int j)
 {
   int k, m;
diff --git a/gcc/testsuite/gcc.dg/analyzer/params-2.c b/gcc/testsuite/gcc.dg/analyzer/params-2.c
index ede14f1..1584ebd 100644
--- a/gcc/testsuite/gcc.dg/analyzer/params-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/params-2.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 #include <stdlib.h>
 
 static void ensure_equal (int a, int b)
diff --git a/gcc/testsuite/gcc.dg/analyzer/params.c b/gcc/testsuite/gcc.dg/analyzer/params.c
index d9bd530..ff405f2 100644
--- a/gcc/testsuite/gcc.dg/analyzer/params.c
+++ b/gcc/testsuite/gcc.dg/analyzer/params.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 static int called_function(int j)
 {
   int k;
diff --git a/gcc/testsuite/gcc.dg/analyzer/switch.c b/gcc/testsuite/gcc.dg/analyzer/switch.c
index 02e2ec0..a55ba2b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/switch.c
+++ b/gcc/testsuite/gcc.dg/analyzer/switch.c
@@ -1,5 +1,3 @@
-/* { dg-additional-options "-fanalyzer-region-model" } */
-
 void test (int i)
 {
   switch (i)
-- 
1.8.5.3

