From cced63c696cbbfb65c316fa417892fc3220eeccd Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 6 May 2019 12:29:05 -0400
Subject: [PATCH 18/28] FIXME: more work on impl-4

FIXME: make various graph fields const

FIXME: tweaks to comment

FIXME: add base_node/edge/graph idea

FIXME: logging tweaks

FIXME: initial implementation of state purging

FIXME: add path_state::validate

FIXME: add xfails; use warning rather than sorry, for the sake of DejaGnu

FIXME: add per-function stats

FIXME: ensure that we can't have empty ECs

FIXME: fix equiv_class::operator==

FIXME: track the number of pathvars purged

FIXME: purge sm state for unrecognized function calls

NOT_FOR_UPSTREAM-CVE-2005-1689-krb5-recvauth.c

FIXME: fix memory leaks; add timing info

FIXME: rework exploded_graph's hash_map

FIXME: more leak fixes

FIXME: purgable sm state

FIXME: add exploded_graph::dump_stats

FIXME: add #enode:#snode ratio

FIXME: add m_PK_AFTER_SUPERNODE_per_snode

FIXME: add exploded_graph::dump_states_for_supernode

FIXME: add timevars for writing .dot files

FIXME: undo accidental commit to path_state::purge

FIXME: save diagnostics until the end

FIXME: only compute shortest_paths once

FIXME: replace saved_diagnostic::emit with diagnostic_manager::emit_saved_diagnostic

FIXME: move exploded_graph::emit_diagnostic and prune_path to diagnostic_manager

FIXME: more tests

FIXME: deduplication of diagnostics

FIXME: add selftest for auto_delete_vec

FIXME: another test

FIXME: add -fdump-analyzer-supergraph and -fdump-analyzer-exploded-graph

FIXME: WIP on better memory model

FIXME: more on better memory model

FIXME: more on better memory model

FIXME: more work on new data model

FIXME: 2019-05-17 WIP on data model

FIXME: 2019-05-18: WIP on data model

FIXME: add options for each warning

FIXME: rename constraints.h/cc to path-state.h/cc

FIXME: put old constraint impl in a namespace

FIXME: constraint_manager class

FIXME: use constraint_manager class

FIXME: 2019-05-21 work on region model

FIXME: 2019-05-22 work on region model

FIXME: 2019-05-23 work on region model

FIXME: function pointers

FIXME: cleanups

FIXME: 2019-05-24 work on region model

FIXME: further region_model cleanups
---
 gcc/analyzer/Make-lang.in                          |     4 +-
 gcc/analyzer/analyzer-logging.h                    |     8 +
 gcc/analyzer/analyzer-plugin.cc                    |     4 +-
 gcc/analyzer/analyzer.h                            |     4 +-
 gcc/analyzer/constraint-manager.cc                 |  2126 ++++
 gcc/analyzer/constraint-manager.h                  |   243 +
 gcc/analyzer/constraints.cc                        |  1987 ----
 gcc/analyzer/constraints.h                         |   279 -
 gcc/analyzer/digraph.cc                            |     2 +
 gcc/analyzer/digraph.h                             |    73 +
 gcc/analyzer/engine.cc                             |    17 +-
 gcc/analyzer/hardcoded-state-machine-file.cc       |    33 +-
 gcc/analyzer/hardcoded-state-machine-malloc.cc     |   245 +-
 .../hardcoded-state-machine-pattern-test.cc        |    20 +-
 gcc/analyzer/hardcoded-state-machine-sensitive.cc  |    40 +-
 gcc/analyzer/hardcoded-state-machine-taint.cc      |    21 +-
 gcc/analyzer/hardcoded-state-machine.h             |    68 +-
 gcc/analyzer/impl-3.cc                             |    17 +-
 gcc/analyzer/impl-4.cc                             |  2691 ++++-
 gcc/analyzer/path-state.cc                         |  2270 ++++
 gcc/analyzer/path-state.h                          |   315 +
 gcc/analyzer/region-model.cc                       |  3603 +++++++
 gcc/analyzer/region-model.h                        |   820 ++
 gcc/analyzer/supergraph.cc                         |    90 +-
 gcc/analyzer/supergraph.h                          |    53 +-
 gcc/builtins.def                                   |     3 +
 gcc/common.opt                                     |    62 +
 gcc/diagnostic-core.h                              |     7 +-
 gcc/diagnostic.c                                   |     4 +-
 gcc/function-tests.c                               |     4 +-
 gcc/selftest.h                                     |     7 +
 .../malloc-checker/arg_must_not_be_null/input.c    |    30 -
 .../malloc-checker/arg_must_not_be_null/script.py  |    23 -
 .../malloc-checker/arg_must_not_be_null/stderr.txt |     4 -
 .../malloc-checker/arg_must_not_be_null/stdout.txt |     0
 .../checkers/malloc-checker/double_free/input.c    |    29 -
 .../double_free/input.c.cpychecker-log.txt         |     1 -
 .../checkers/malloc-checker/double_free/script.py  |    23 -
 .../checkers/malloc-checker/double_free/stderr.txt |     7 -
 .../checkers/malloc-checker/double_free/stdout.txt |     0
 .../checkers/signal_handler/example/stderr.txt     |     5 -
 .../checkers/signal_handler/example/stdout.txt     |     0
 .../regression/rhs-is-vardecl/stdout.txt           |     0
 .../NOT_FOR_UPSTREAM-CVE-2005-1689-krb5-recvauth.c | 10749 +++++++++++++++++++
 .../NOT_FOR_UPSTREAM-clang-paper-example-1.c       |    12 +
 .../NOT_FOR_UPSTREAM-clang-paper-example-2.c       |    19 +
 .../NOT_FOR_UPSTREAM-clang-paper-example-3.c       |    24 +
 .../NOT_FOR_UPSTREAM-clang-paper-example-4.c       |    31 +
 gcc/testsuite/gcc.dg/analyzer/abort.c              |     2 +
 gcc/testsuite/gcc.dg/analyzer/alloca-leak.c        |     8 +
 gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c  |    57 +
 gcc/testsuite/gcc.dg/analyzer/conditionals.c       |     2 +
 gcc/testsuite/gcc.dg/analyzer/data-model-1.c       |   790 ++
 gcc/testsuite/gcc.dg/analyzer/equivalence.c        |     2 +
 gcc/testsuite/gcc.dg/analyzer/fibonacci.c          |     5 +-
 gcc/testsuite/gcc.dg/analyzer/fields.c             |    41 +
 gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c     |    28 +
 gcc/testsuite/gcc.dg/analyzer/gzio.c               |     3 +-
 gcc/testsuite/gcc.dg/analyzer/loop.c               |     4 +
 gcc/testsuite/gcc.dg/analyzer/malloc-1.c           |    29 +
 gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c    |    48 +
 gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c     |     2 +-
 gcc/testsuite/gcc.dg/analyzer/malloc-vs-local.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/pragma-1.c           |    26 +
 gcc/testsuite/gcc.dg/analyzer/switch.c             |     2 +
 gcc/vec.c                                          |    27 +
 notes.txt                                          |    14 +
 70 files changed, 24272 insertions(+), 2984 deletions(-)
 create mode 100644 gcc/analyzer/constraint-manager.cc
 create mode 100644 gcc/analyzer/constraint-manager.h
 delete mode 100644 gcc/analyzer/constraints.cc
 delete mode 100644 gcc/analyzer/constraints.h
 create mode 100644 gcc/analyzer/path-state.cc
 create mode 100644 gcc/analyzer/path-state.h
 create mode 100644 gcc/analyzer/region-model.cc
 create mode 100644 gcc/analyzer/region-model.h
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/input.c
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/script.py
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/stderr.txt
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/stdout.txt
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/input.c
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/input.c.cpychecker-log.txt
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/script.py
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/stderr.txt
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/stdout.txt
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/stderr.txt
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/stdout.txt
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/regression/rhs-is-vardecl/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-CVE-2005-1689-krb5-recvauth.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-4.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/alloca-leak.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fields.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-vs-local.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/pragma-1.c

diff --git a/gcc/analyzer/Make-lang.in b/gcc/analyzer/Make-lang.in
index f742b9d..73cbf4e 100644
--- a/gcc/analyzer/Make-lang.in
+++ b/gcc/analyzer/Make-lang.in
@@ -57,7 +57,7 @@ ANALYZER_PLUGIN_SO_OBJS = \
 	analyzer/analyzer-pass.o \
 	analyzer/analyzer-plugin.o \
 	analyzer/checker-script.o \
-	analyzer/constraints.o \
+	analyzer/constraint-manager.o \
 	analyzer/digraph.o \
 	analyzer/engine.o \
 	analyzer/graphviz.o \
@@ -69,6 +69,8 @@ ANALYZER_PLUGIN_SO_OBJS = \
 	analyzer/hardcoded-state-machine-taint.o \
 	analyzer/impl-3.o \
 	analyzer/impl-4.o \
+	analyzer/path-state.o \
+	analyzer/region-model.o \
 	analyzer/supergraph.o \
 	analyzer/tristate.o \
 
diff --git a/gcc/analyzer/analyzer-logging.h b/gcc/analyzer/analyzer-logging.h
index e09ed8e8..8f950d9 100644
--- a/gcc/analyzer/analyzer-logging.h
+++ b/gcc/analyzer/analyzer-logging.h
@@ -55,6 +55,8 @@ class logger
     ATTRIBUTE_GCC_DIAG(3, 0);
   void exit_scope (const char *scope_name);
 
+  pretty_printer *get_printer () const { return m_pp; }
+
 private:
   int m_refcount;
   FILE *m_f_out;
@@ -160,6 +162,12 @@ class log_user
   void enter_scope (const char *scope_name);
   void exit_scope (const char *scope_name);
 
+  pretty_printer *get_logger_pp () const
+  {
+    gcc_assert (m_logger);
+    return m_logger->get_printer ();
+  }
+
  private:
   logger *m_logger;
 };
diff --git a/gcc/analyzer/analyzer-plugin.cc b/gcc/analyzer/analyzer-plugin.cc
index 17b69bd..781ae8a 100644
--- a/gcc/analyzer/analyzer-plugin.cc
+++ b/gcc/analyzer/analyzer-plugin.cc
@@ -38,9 +38,11 @@ static void
 analyzer_selftests (void */*gcc_data*/, void */*user_data*/)
 {
   analyzer_checker_script_cc_tests ();
-  analyzer_constraints_cc_tests ();
+  analyzer_constraint_manager_cc_tests ();
   analyzer_engine_cc_tests ();
   analyzer_impl_4_cc_tests ();
+  analyzer_path_state_cc_tests ();
+  analyzer_region_model_cc_tests ();
 }
 
 } /* end of namespace selftest.  */
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 32a6ab9..b34ca99 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -51,9 +51,11 @@ namespace selftest {
 /* Declarations for specific families of tests (by source file), in
    alphabetical order, for the checker plugin.  */
 extern void analyzer_checker_script_cc_tests ();
-extern void analyzer_constraints_cc_tests ();
+extern void analyzer_constraint_manager_cc_tests ();
 extern void analyzer_engine_cc_tests ();
 extern void analyzer_impl_4_cc_tests ();
+extern void analyzer_path_state_cc_tests ();
+extern void analyzer_region_model_cc_tests ();
 
 } /* end of namespace selftest.  */
 
diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc
new file mode 100644
index 0000000..5685c48
--- /dev/null
+++ b/gcc/analyzer/constraint-manager.cc
@@ -0,0 +1,2126 @@
+/* FIXME.  */
+#include "config.h"
+#include "gcc-plugin.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tm.h"
+#include "toplev.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "context.h"
+#include "diagnostic.h"
+#include "bitmap.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-pretty-print.h"
+#include "tree-pretty-print.h"
+#include "analyzer.h"
+#include "graphviz.h"
+#include "cgraph.h"
+#include "diagnostic-path.h"
+#include "gcc-rich-location.h"
+#include "tree-dfa.h"
+#include <string>
+#include "analyzer-logging.h"
+#include "stringpool.h"
+#include "selftest.h"
+#include "fold-const.h"
+#include "tristate.h"
+#include "path-state.h"
+#include "supergraph.h"
+#include "constraint-manager.h"
+#include "tree-diagnostic.h"
+
+/* A subclass of purge_criteria: purge a specific svalue_id.  */
+
+class purge_one_svalue_id : public purge_criteria
+{
+public:
+  purge_one_svalue_id (svalue_id victim) : m_victim (victim) {}
+
+  bool should_purge_p (svalue_id pv) const FINAL OVERRIDE
+  {
+    return pv == m_victim;
+  }
+
+private:
+  svalue_id m_victim;
+};
+
+/* FIXME.  */
+
+equiv_class::equiv_class ()
+: m_constant (NULL_TREE), m_cst_sid (svalue_id::null ()),
+  m_vars (), m_escaped (false)
+{
+}
+
+/* FIXME.  */
+
+equiv_class::equiv_class (const equiv_class &other)
+: m_constant (other.m_constant), m_cst_sid (other.m_cst_sid),
+  m_vars (),
+  m_escaped (other.m_escaped)
+{
+  // FIXME: copy-ctor for auto_vec!
+  int i;
+  svalue_id *sid;
+  FOR_EACH_VEC_ELT (other.m_vars, i, sid)
+    m_vars.safe_push (*sid);
+}
+
+/* FIXME.  */
+
+equiv_class::~equiv_class ()
+{
+}
+
+/* FIXME.  */
+
+std::string
+equiv_class::to_str () const
+{
+  std::string result ("{");
+  int i;
+  svalue_id *sid;
+  FOR_EACH_VEC_ELT (m_vars, i, sid)
+    {
+      if (i > 0)
+	result += " == ";
+      result += sid->to_str ();
+    }
+  if (m_constant)
+    {
+      if (i > 0)
+	result += " == ";
+      result += expr_to_string (m_constant);
+    }
+  if (m_escaped)
+    result += " (escaped)";
+  return result + "}";
+}
+
+/* FIXME.  */
+
+hashval_t
+equiv_class::hash () const
+{
+  inchash::hash hstate;
+  int i;
+  svalue_id *sid;
+
+  inchash::add_expr (m_constant, hstate);
+  FOR_EACH_VEC_ELT (m_vars, i, sid)
+    inchash::add (*sid, hstate);
+  hstate.add_flag (m_escaped);
+  hstate.commit_flag ();
+  return hstate.end ();
+}
+
+/* FIXME.  */
+
+bool
+equiv_class::operator== (const equiv_class &other)
+{
+  if (m_constant != other.m_constant)
+    return false; // TODO: use tree equality here
+
+  // FIXME: m_cst_sid
+
+  if (m_vars.length () != other.m_vars.length ())
+    return false;
+
+  int i;
+  svalue_id *sid;
+  FOR_EACH_VEC_ELT (m_vars, i, sid)
+    if (! (*sid == other.m_vars[i]))
+      return false;
+  if (m_escaped != other.m_escaped)
+    return false;
+
+  return true;
+}
+
+/* FIXME.  */
+
+void
+equiv_class::add (svalue_id sid, const constraint_manager &cm)
+{
+  if (tree cst = cm.maybe_get_constant (sid))
+    {
+      gcc_assert (m_constant == NULL_TREE);
+      m_constant = cst;
+      m_cst_sid = sid;
+    }
+  m_vars.safe_push (sid);
+
+#if 0
+      if (DECL_P (v.m_tree)
+	  && is_global_var (v.m_tree))
+	m_escaped = true;
+#endif
+}
+
+/* Remove SID from this equivalence class.
+   Return true if SID was the last var in the equivalence class (suggesting
+   a possible leak).  */
+
+bool
+equiv_class::del (svalue_id sid)
+{
+  gcc_assert (sid != m_cst_sid);
+
+  int i;
+  svalue_id *iv;
+  FOR_EACH_VEC_ELT (m_vars, i, iv)
+    {
+      if (*iv == sid)
+	{
+	  m_vars[i] = m_vars[m_vars.length () - 1];
+	  m_vars.pop ();
+	  return m_vars.length () == 0;
+	}
+    }
+
+  /* SID must be in the class.  */
+  gcc_unreachable ();
+  return false;
+}
+
+/* Get a representative member of this class, for handling cases
+   where the IDs can change mid-traversal.  */
+
+svalue_id
+equiv_class::get_representative () const
+{
+#if 0
+  if (m_constant)
+    return path_var (m_constant, 0);
+  else
+#endif
+    {
+      gcc_assert (m_vars.length () > 0);
+      return m_vars[0];
+    }
+}
+
+void
+equiv_class::remap_svalue_ids (const svalue_id_map &map)
+{
+  int i;
+  svalue_id *iv;
+  FOR_EACH_VEC_ELT (m_vars, i, iv)
+    *iv = map.get_dst_for_src (*iv);
+  m_cst_sid = map.get_dst_for_src (m_cst_sid);
+}
+
+/* FIXME.  */
+
+const char *
+constraint_op_code (enum constraint_op c_op)
+{
+  switch (c_op)
+    {
+    default:
+      gcc_unreachable ();
+    case CONSTRAINT_NE: return "!=";
+    case CONSTRAINT_LT: return "<";
+    case CONSTRAINT_LE: return "<=";
+    }
+}
+
+/* Given "lhs C_OP rhs", determine "lhs T_OP rhs".
+
+   For example, given "x < y", then "x > y" is false.  */
+
+static tristate
+eval_constraint_op_for_op (enum constraint_op c_op, enum tree_code t_op)
+{
+  switch (c_op)
+    {
+    default:
+      gcc_unreachable ();
+    case CONSTRAINT_NE:
+      if (t_op == EQ_EXPR)
+	return tristate (tristate::TS_FALSE);
+      if (t_op == NE_EXPR)
+	return tristate (tristate::TS_TRUE);
+      break;
+    case CONSTRAINT_LT:
+      if (t_op == LT_EXPR || t_op == LE_EXPR || t_op == NE_EXPR)
+	return tristate (tristate::TS_TRUE);
+      if (t_op == EQ_EXPR || t_op == GT_EXPR || t_op == GE_EXPR)
+	return tristate (tristate::TS_FALSE);
+      break;
+    case CONSTRAINT_LE:
+      if (t_op == LE_EXPR)
+	return tristate (tristate::TS_TRUE);
+      if (t_op == GT_EXPR)
+	return tristate (tristate::TS_FALSE);
+      break;
+    }
+  return tristate (tristate::TS_UNKNOWN);
+}
+
+/* FIXME.  */
+
+std::string
+constraint::to_str (const constraint_manager &cm) const
+{
+  return (m_lhs.to_str () + ": "
+	  + m_lhs.get_obj (cm).to_str ()
+	  + " "
+	  + constraint_op_code (m_op)
+	  + " "
+	  + m_rhs.to_str () + ": "
+	  + m_rhs.get_obj (cm).to_str ());
+}
+
+/* FIXME.  */
+
+hashval_t
+constraint::hash () const
+{
+  inchash::hash hstate;
+  hstate.add_int (m_lhs.m_idx);
+  hstate.add_int (m_op);
+  hstate.add_int (m_rhs.m_idx);
+  return hstate.end ();
+}
+
+/* FIXME.  */
+
+bool
+constraint::operator== (const constraint &other) const
+{
+  if (m_lhs != other.m_lhs)
+    return false;
+  if (m_op != other.m_op)
+    return false;
+  if (m_rhs != other.m_rhs)
+    return false;
+  return true;
+}
+
+/* FIXME.  */
+
+const equiv_class &
+equiv_class_id::get_obj (const constraint_manager &cm) const
+{
+  return cm.get_equiv_class_by_index (m_idx);
+}
+
+/* FIXME.  */
+
+equiv_class &
+equiv_class_id::get_obj (constraint_manager &cm) const
+{
+  return cm.get_equiv_class_by_index (m_idx);
+}
+
+/* FIXME.  */
+
+constraint_manager::constraint_manager (const constraint_manager &other)
+{
+  // FIXME: auto_vec's copy_ctor...
+  int i;
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (other.m_equiv_classes, i, ec)
+    m_equiv_classes.safe_push (new equiv_class (*ec));
+  constraint *c;
+  FOR_EACH_VEC_ELT (other.m_constraints, i, c)
+    m_constraints.safe_push (*c);
+}
+
+/* FIXME.  */
+
+constraint_manager::~constraint_manager ()
+{
+  int i;
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    delete ec;
+}
+
+/* FIXME.  */
+
+constraint_manager&
+constraint_manager::operator= (const constraint_manager &other)
+{
+  gcc_assert (m_equiv_classes.length () == 0);
+  gcc_assert (m_constraints.length () == 0);
+
+  int i;
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (other.m_equiv_classes, i, ec)
+    m_equiv_classes.safe_push (new equiv_class (*ec));
+  constraint *c;
+  FOR_EACH_VEC_ELT (other.m_constraints, i, c)
+    m_constraints.safe_push (*c);
+
+  return *this;
+}
+
+/* FIXME.  */
+
+hashval_t
+constraint_manager::hash () const
+{
+  inchash::hash hstate;
+  int i;
+  equiv_class *ec;
+  constraint *c;
+
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    hstate.merge_hash (ec->hash ());
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    hstate.merge_hash (c->hash ());
+  return hstate.end ();
+}
+
+/* FIXME.  */
+
+bool
+constraint_manager::operator== (const constraint_manager &other) const
+{
+  if (m_equiv_classes.length () != other.m_equiv_classes.length ())
+    return false;
+
+  int i;
+  equiv_class *ec;
+
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    if (!(*ec == *other.m_equiv_classes[i]))
+      return false;
+
+  constraint *c;
+
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    if (!(*c == other.m_constraints[i]))
+      return false;
+
+  return true;
+}
+
+/* FIXME.  */
+
+void
+constraint_manager::print (pretty_printer *pp) const
+{
+  pp_string (pp, "{");
+  int i;
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      pp_printf (pp, "%s: ", equiv_class_id (i).to_str ().c_str ());
+      pp_string (pp, ec->to_str ().c_str ());
+    }
+  pp_string (pp, "  |  ");
+  constraint *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      if (i > 0)
+	pp_string (pp, " && ");
+      pp_string (pp, c->to_str (*this).c_str ());
+    }
+  pp_printf (pp, "}");
+}
+
+/* FIXME.  */
+
+std::string
+constraint_manager::to_str () const
+{
+  pretty_printer pp;
+  print (&pp);
+  return pp_formatted_text (&pp);
+}
+
+/* FIXME.  */
+
+void
+constraint_manager::dump_to_pp (pretty_printer *pp) const
+{
+  // TODO
+  pp_string (pp, "  equiv classes:");
+  pp_newline (pp);
+  int i;
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    {
+      pp_printf (pp, "    %s: ", equiv_class_id (i).to_str ().c_str ());
+      pp_string (pp, ec->to_str ().c_str ());
+      pp_newline (pp);
+    }
+  pp_string (pp, "  constraints:");
+  pp_newline (pp);
+  constraint *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      pp_printf (pp, "    %i: %s", i, c->to_str (*this).c_str ());
+      pp_newline (pp);
+    }
+}
+
+/* FIXME.  */
+
+void
+constraint_manager::dump (FILE *fp) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp.buffer->stream = fp;
+  dump_to_pp (&pp);
+  pp_flush (&pp);
+}
+
+/* FIXME.  */
+
+DEBUG_FUNCTION void
+constraint_manager::dump () const
+{
+  dump (stderr);
+}
+
+/* FIXME.  */
+
+bool
+constraint_manager::add_constraint (svalue_id lhs,
+				    enum tree_code op,
+				    svalue_id rhs)
+{
+  equiv_class_id lhs_ec_id = get_or_add_equiv_class (lhs);
+  equiv_class_id rhs_ec_id = get_or_add_equiv_class (rhs);
+  return add_constraint (lhs_ec_id, op,rhs_ec_id);
+}
+
+/* FIXME.  */
+
+bool
+constraint_manager::add_constraint (equiv_class_id lhs_ec_id,
+				    enum tree_code op,
+				    equiv_class_id rhs_ec_id)
+{
+  tristate t = eval_condition (lhs_ec_id, op, rhs_ec_id);
+
+  /* Discard constraints that are already known.  */
+  if (t.is_true ())
+    return true;
+
+  /* Reject unsatisfiable constraints.  */
+  if (t.is_false ())
+    return false;
+
+  gcc_assert (lhs_ec_id != rhs_ec_id);
+
+  /* For now, simply accumulate constraints, without attempting any further
+     optimization.  */
+  switch (op)
+    {
+    case EQ_EXPR:
+      {
+	/* Merge rhs_ec into lhs_ec.  */
+	equiv_class &lhs_ec_obj = lhs_ec_id.get_obj (*this);
+	const equiv_class &rhs_ec_obj = rhs_ec_id.get_obj (*this);
+
+	int i;
+	svalue_id *sid;
+	FOR_EACH_VEC_ELT (rhs_ec_obj.m_vars, i, sid)
+	  lhs_ec_obj.add (*sid, *this);
+
+	if (rhs_ec_obj.m_constant)
+	  {
+	    //gcc_assert (lhs_ec_obj.m_constant == NULL);
+	    lhs_ec_obj.m_constant = rhs_ec_obj.m_constant;
+	  }
+
+	/* Drop rhs equivalence class, overwriting it with the
+	   final ec (which might be the same one).  */
+	equiv_class_id final_ec_id = m_equiv_classes.length () - 1;
+	equiv_class *old_ec = m_equiv_classes[rhs_ec_id.m_idx];
+	equiv_class *final_ec = m_equiv_classes.pop ();
+	if (final_ec != old_ec)
+	  m_equiv_classes[rhs_ec_id.m_idx] = final_ec;
+	delete old_ec;
+
+	/* Update the constraints.  */
+	constraint *c;
+	FOR_EACH_VEC_ELT (m_constraints, i, c)
+	  {
+	    /* Update references to the rhs_ec so that
+	       they refer to the lhs_ec.  */
+	    if (c->m_lhs == rhs_ec_id)
+	      c->m_lhs = lhs_ec_id;
+	    if (c->m_rhs == rhs_ec_id)
+	      c->m_rhs = lhs_ec_id;
+
+	    /* Renumber all constraints that refer to the final rhs_ec
+	       to the old rhs_ec, where the old final_ec now lives.  */
+	    if (c->m_lhs == final_ec_id)
+	      c->m_lhs = rhs_ec_id;
+	    if (c->m_rhs == final_ec_id)
+	      c->m_rhs = rhs_ec_id;
+	  }
+      }
+      break;
+    case GE_EXPR:
+      add_constraint_internal (rhs_ec_id, CONSTRAINT_LE, lhs_ec_id);
+      break;
+    case LE_EXPR:
+      add_constraint_internal (lhs_ec_id, CONSTRAINT_LE, rhs_ec_id);
+      break;
+    case NE_EXPR:
+      add_constraint_internal (lhs_ec_id, CONSTRAINT_NE, rhs_ec_id);
+      break;
+    case GT_EXPR:
+      add_constraint_internal (rhs_ec_id, CONSTRAINT_LT, lhs_ec_id);
+      break;
+    case LT_EXPR:
+      add_constraint_internal (lhs_ec_id, CONSTRAINT_LT, rhs_ec_id);
+      break;
+    default:
+      /* do nothing.  */
+      break;
+    }
+  validate ();
+  return true;
+}
+
+/* FIXME.  */
+
+void
+bound::ensure_closed (bool is_upper)
+{
+  if (!m_closed)
+    {
+      /* Offset by 1 in the appropriate direction.
+	 For example, convert 3 < x into 4 <= x,
+	 and convert x < 5 into x <= 4.  */
+      gcc_assert (CONSTANT_CLASS_P (m_constant));
+      m_constant = fold_build2 (is_upper ? MINUS_EXPR : PLUS_EXPR,
+				TREE_TYPE (m_constant),
+				m_constant, integer_one_node);
+      gcc_assert (CONSTANT_CLASS_P (m_constant));
+      m_closed = true;
+    }
+}
+
+/* FIXME.  */
+
+std::string
+range::to_str () const
+{
+  std::string result = expr_to_string (m_lower_bound.m_constant);
+  result += " <";
+  if (m_lower_bound.m_closed)
+    result += "=";
+  result += " x <";
+  if (m_upper_bound.m_closed)
+    result += "=";
+  result += " ";
+  result += expr_to_string (m_upper_bound.m_constant);
+  return result;  // TODO
+}
+
+/* FIXME.  */
+
+bool
+range::constrained_to_single_element (tree *out)
+{
+  if (!INTEGRAL_TYPE_P (TREE_TYPE (m_lower_bound.m_constant)))
+    return false;
+  if (!INTEGRAL_TYPE_P (TREE_TYPE (m_upper_bound.m_constant)))
+    return false;
+  // FIXME: also check the endpoint
+
+  /* Convert any open bounds to closed bounds.  */
+  m_lower_bound.ensure_closed (false);
+  m_upper_bound.ensure_closed (true);
+
+  // Are they equal?
+  tree comparison
+    = fold_build2 (EQ_EXPR, boolean_type_node,
+		   m_lower_bound.m_constant,
+		   m_upper_bound.m_constant);
+  if (comparison == boolean_true_node)
+    {
+      *out = m_lower_bound.m_constant;
+      return true;
+    }
+  else
+    return false;
+}
+
+
+/* FIXME.  */
+
+void
+constraint_manager::add_constraint_internal (equiv_class_id lhs_id,
+					     enum constraint_op c_op,
+					     equiv_class_id rhs_id)
+{
+  /* Add the constraint.  */
+  m_constraints.safe_push (constraint (lhs_id, c_op, rhs_id));
+
+  if (c_op != CONSTRAINT_NE)
+    {
+      /* The following can potentially add EQ_EXPR facts, which could lead
+	 to ECs being merged, which would change the meaning of the EC IDs.
+	 Hence we need to do this via path vars.  */
+      svalue_id lhs = lhs_id.get_obj (*this).get_representative ();
+      svalue_id rhs = rhs_id.get_obj (*this).get_representative ();
+
+      /* We have LHS </<= RHS */
+
+      /* Handle transitivity of ordering by adding additional constraints
+	 based on what we already knew.
+
+	 So if we have already have:
+	   (a < b)
+	   (c < d)
+	 Then adding:
+	   (b < c)
+	 will also add:
+	   (a < c)
+	   (b < d)
+	 We need to recurse to ensure we also add:
+	   (a < d).
+	 We call the checked add_constraint to avoid adding constraints
+	 that are already present.  Doing so also ensures termination
+	 in the case of cycles.
+
+	 We also check for single-element ranges, adding EQ_EXPR facts
+	 where we discover them.  For example 3 < x < 5 implies
+	 that x == 4 (if x is an integer).  */
+      for (unsigned i = 0; i < m_constraints.length (); i++)
+	{
+	  const constraint *other = &m_constraints[i];
+	  if (other->is_ordering_p ())
+	    {
+	      /* Refresh the EC IDs, in case any mergers have happened.  */
+	      lhs_id = get_or_add_equiv_class (lhs);
+	      rhs_id = get_or_add_equiv_class (rhs);
+
+	      tree lhs_const = lhs_id.get_obj (*this).m_constant;
+	      tree rhs_const = rhs_id.get_obj (*this).m_constant;
+	      tree other_lhs_const
+		= other->m_lhs.get_obj (*this).m_constant;
+	      tree other_rhs_const
+		= other->m_rhs.get_obj (*this).m_constant;
+
+	      /* We have "LHS </<= RHS" and "other.lhs </<= other.rhs".  */
+
+	      /* If we have LHS </<= RHS and RHS </<= LHS, then we have a
+		 cycle.  */
+	      if (rhs_id == other->m_lhs
+		  && other->m_rhs == lhs_id)
+		{
+		  /* We must have equality for this to be possible.  */
+		  gcc_assert (c_op == CONSTRAINT_LE
+			      && other->m_op == CONSTRAINT_LE);
+		  add_constraint (lhs_id, EQ_EXPR, rhs_id);
+		  /* Adding an equality will merge the two ECs and potentially
+		     reorganize the constraints.  Stop iterating.  */
+		  return;
+		}
+	      /* Otherwise, check for transitivity.  */
+	      if (rhs_id == other->m_lhs)
+		{
+		  /* With RHS == other.lhs, we have:
+		     "LHS </<= (RHS, other.lhs) </<= other.rhs"
+		     and thus this implies "LHS </<= other.rhs".  */
+
+		  /* Do we have a tightly-constrained range?  */
+		  if (lhs_const
+		      && !rhs_const
+		      // FIXME:
+		      //&& INTEGRAL_TYPE_P (TREE_TYPE (rhs.m_tree))
+		      && other_rhs_const)
+		    {
+		      range r (bound (lhs_const, c_op == CONSTRAINT_LE),
+			       bound (other_rhs_const,
+				      other->m_op == CONSTRAINT_LE));
+		      tree constant;
+		      if (r.constrained_to_single_element (&constant))
+			{
+			  svalue_id cst_sid = get_sid_for_constant (constant);
+			  add_constraint
+			    (rhs_id, EQ_EXPR,
+			     get_or_add_equiv_class (cst_sid));
+			  return;
+			}
+		    }
+
+		  /* Otherwise, add the constraint implied by transitivity.  */
+		  enum tree_code new_op
+		    = ((c_op == CONSTRAINT_LE && other->m_op == CONSTRAINT_LE)
+		       ? LE_EXPR : LT_EXPR);
+		  add_constraint (lhs_id, new_op, other->m_rhs);
+		}
+	      else if (other->m_rhs == lhs_id)
+		{
+		  /* With other.rhs == LHS, we have:
+		     "other.lhs </<= (other.rhs, LHS) </<= RHS"
+		     and thus this implies "other.lhs </<= RHS".  */
+
+		  /* Do we have a tightly-constrained range?  */
+		  // TODO:
+		  if (other_lhs_const
+		      && !lhs_const
+		      // FIXME:
+		      //&& INTEGRAL_TYPE_P (TREE_TYPE (lhs.m_tree))
+		      && rhs_const)
+		    {
+		      range r (bound (other_lhs_const,
+				      other->m_op == CONSTRAINT_LE),
+			       bound (rhs_const,
+				      c_op == CONSTRAINT_LE));
+		      tree constant;
+		      if (r.constrained_to_single_element (&constant))
+			{
+			  svalue_id cst_sid = get_sid_for_constant (constant);
+			  add_constraint
+			    (lhs_id, EQ_EXPR,
+			     get_or_add_equiv_class (cst_sid));
+			  return;
+			}
+		    }
+
+		  /* Otherwise, add the constraint implied by transitivity.  */
+		  enum tree_code new_op
+		    = ((c_op == CONSTRAINT_LE && other->m_op == CONSTRAINT_LE)
+		       ? LE_EXPR : LT_EXPR);
+		  add_constraint (other->m_lhs, new_op, rhs_id);
+		}
+	    }
+	}
+    }
+}
+
+/* FIXME.  */
+
+bool
+constraint_manager::get_equiv_class (svalue_id sid, equiv_class_id *out) const
+{
+  if (tree cst = maybe_get_constant (sid))
+    {
+      // FIXME: we want to ensure that constants that are equal are treated as equal here
+      int i;
+      equiv_class *ec;
+      FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+	if (cst == ec->m_constant)
+	  {
+	    *out = equiv_class_id (i);
+	    return true;
+	  }
+    }
+  else
+    {
+      /* FIXME: probably should be a map, rather than these lookups.  */
+      int i;
+      equiv_class *ec;
+      FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+	{
+	  int j;
+	  svalue_id *iv;
+	  FOR_EACH_VEC_ELT (ec->m_vars, j, iv)
+	    if (*iv == sid)
+	      {
+		*out = equiv_class_id (i);
+		return true;
+	      }
+	}
+    }
+  return false;
+}
+
+/* FIXME.  */
+
+equiv_class_id
+constraint_manager::get_or_add_equiv_class (svalue_id sid)
+{
+  equiv_class_id result (-1);
+  if (get_equiv_class (sid, &result))
+    return result;
+
+  /* Not found.  */
+  equiv_class *new_ec = new equiv_class ();
+  new_ec->add (sid, *this);
+  m_equiv_classes.safe_push (new_ec);
+
+  equiv_class_id new_id (m_equiv_classes.length () - 1);
+
+  if (tree cst = maybe_get_constant (sid))
+    {
+      /* If we have a new EC for a constant, add constraints comparing this
+	 to other constants we may have (so that we accumulate the transitive
+	 closure of all constraints on constants as the constants are
+	 added).  */
+      for (equiv_class_id other_id (0); other_id.m_idx < new_id.m_idx;
+	   other_id.m_idx++)
+	{
+	  const equiv_class &other_ec = other_id.get_obj (*this);
+	  if (other_ec.m_constant)
+	    {
+	      /* If we have two ECs, both with constants, the constants must be
+		 non-equal (or they would be in the same EC).
+		 Determine the direction of the inequality, and record that
+		 fact.  */
+	      tree lt
+		= fold_build2 (LT_EXPR, boolean_type_node,
+			       new_ec->m_constant, other_ec.m_constant);
+	      //gcc_assert (lt == boolean_true_node || lt == boolean_false_node);
+	      // not true for int vs float comparisons
+	      if (lt == boolean_true_node)
+		add_constraint_internal (new_id, CONSTRAINT_LT, other_id);
+	      else if (lt == boolean_false_node)
+		add_constraint_internal (other_id, CONSTRAINT_LT, new_id);
+	      /* Refresh new_id, in case ECs were merged.  SID should always
+		 be present by now, so this should never lead to a
+		 recursion.  */
+	      new_id = get_or_add_equiv_class (sid);
+	    }
+	}
+    }
+
+  return new_id;
+}
+
+/* FIXME.  */
+/* For implementing "__analyzer_eval".  */
+
+#if 0
+tristate
+constraint_manager::dump_condition (tree lhs, enum tree_code op, tree rhs)
+{
+  equiv_class_id lhs_ec_id
+    = get_or_add_equiv_class (svalue_id (lhs, m_stack_depth));
+  equiv_class_id rhs_ec_id
+    = get_or_add_equiv_class (svalue_id (rhs, m_stack_depth));
+
+  return eval_condition (lhs_ec_id, op, rhs_ec_id);
+}
+#endif
+
+/* FIXME.  */
+
+tristate
+constraint_manager::eval_condition (equiv_class_id lhs_ec,
+			    enum tree_code op,
+			    equiv_class_id rhs_ec)
+{
+  if (lhs_ec == rhs_ec)
+    {
+      switch (op)
+	{
+	case EQ_EXPR:
+	case GE_EXPR:
+	case LE_EXPR:
+	  return tristate (tristate::TS_TRUE);
+
+	case NE_EXPR:
+	case GT_EXPR:
+	case LT_EXPR:
+	  return tristate (tristate::TS_FALSE);
+	default:
+	  break;
+	}
+    }
+
+  tree lhs_const = lhs_ec.get_obj (*this).get_any_constant ();
+  tree rhs_const = rhs_ec.get_obj (*this).get_any_constant ();
+  if (lhs_const && rhs_const)
+    {
+      tree comparison
+	= fold_build2 (op, boolean_type_node, lhs_const, rhs_const);
+      if (comparison == boolean_true_node)
+	return tristate (tristate::TS_TRUE);
+      if (comparison == boolean_false_node)
+	return tristate (tristate::TS_FALSE);
+    }
+
+  enum tree_code swapped_op = swap_tree_comparison (op);
+
+  int i;
+  constraint *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      if (c->m_lhs == lhs_ec
+	  && c->m_rhs == rhs_ec)
+	{
+	  tristate result_for_constraint
+	    = eval_constraint_op_for_op (c->m_op, op);
+	  if (result_for_constraint.is_known ())
+	    return result_for_constraint;
+	}
+      /* Swapped operands.  */
+      if (c->m_lhs == rhs_ec
+	  && c->m_rhs == lhs_ec)
+	{
+	  tristate result_for_constraint
+	    = eval_constraint_op_for_op (c->m_op, swapped_op);
+	  if (result_for_constraint.is_known ())
+	    return result_for_constraint;
+	}
+    }
+
+  return tristate (tristate::TS_UNKNOWN);
+}
+
+/* FIXME.  */
+
+tristate
+constraint_manager::eval_condition (svalue_id lhs,
+				    enum tree_code op,
+				    svalue_id rhs)
+{
+  return eval_condition (get_or_add_equiv_class (lhs),
+			 op,
+			 get_or_add_equiv_class (rhs));
+}
+
+/* Mark EC_ID as having escaped; walk other ECs that haven't yet been marked
+   as escaped, and determine if they've now escaped, if so, mark them,
+   recursively.
+   e.g. if we know "ptr" escapes, then "ptr->foo" escapes.  */
+
+#if 0
+void
+constraint_manager::recursively_mark_as_escaping (equiv_class_id ec_id)
+{
+  equiv_class &ec = ec_id.get_obj (*this);
+  if (ec.m_escaped)
+    {
+      /* Nothing to do.  */
+      return;
+    }
+
+  ec.m_escaped = true;
+
+  /* Look for other ECs that haven't yet been marked as escaped.  */
+  int other_ec_idx;
+  equiv_class *other_ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, other_ec_idx, other_ec)
+    {
+      if (other_ec->m_escaped)
+	continue;
+
+      int var_idx;
+      svalue_id *v;
+      FOR_EACH_VEC_ELT (other_ec->m_vars, var_idx, v)
+	{
+	  gcc_assert (!CONSTANT_CLASS_P (v->m_tree));
+	  if (calc_escaped_p (*v))
+	    {
+	      recursively_mark_as_escaping (other_ec_idx);
+	      break;
+	    }
+	}
+    }
+
+  // TODO: selftests
+  // TODO: selftest involving a cycle
+}
+#endif
+
+/* Determine if PV (or something that points to it) has been marked as
+   having escaped.  */
+
+#if 0
+bool
+constraint_manager::calc_escaped_p (svalue_id pv)
+{
+  switch (TREE_CODE (pv.m_tree))
+    {
+    case COMPONENT_REF:
+    case MEM_REF:
+      return calc_escaped_p (svalue_id (TREE_OPERAND (pv.m_tree, 0),
+				       pv.m_stack_depth));
+    case SSA_NAME:
+      {
+	equiv_class_id ec_id (-1);
+	if (get_equiv_class (pv, &ec_id))
+	  return ec_id.get_obj (*this).m_escaped;
+      }
+      break;
+    default:
+      return false;
+    }
+
+  return false;
+}
+#endif
+
+/* Delete any information about svalue_id instances identified by P.
+   Such instances are removed from equivalence classes, and any
+   redundant ECs and constraints are also removed.
+   Accumulate stats into STATS.  */
+
+void
+constraint_manager::purge (const purge_criteria &p, purge_stats *stats)
+{
+  /* Delete any svalue_ids identified by P within the various equivalence
+     classes.  */
+  for (unsigned ec_idx = 0; ec_idx < m_equiv_classes.length (); )
+    {
+      equiv_class *ec = m_equiv_classes[ec_idx];
+
+      int i;
+      svalue_id *pv;
+      bool delete_ec = false;
+      FOR_EACH_VEC_ELT (ec->m_vars, i, pv)
+	{
+	  if (*pv == ec->m_cst_sid)
+	    continue;
+	  if (p.should_purge_p (*pv))
+	    {
+	      if (ec->del (*pv))
+		if (!ec->m_constant)
+		  delete_ec = true;
+	    }
+	}
+
+      if (delete_ec)
+	{
+	  delete ec;
+	  m_equiv_classes.ordered_remove (ec_idx);
+	  stats->m_num_equiv_classes++;
+
+	  /* Update the constraints, potentially removing some.  */
+	  for (unsigned con_idx = 0; con_idx < m_constraints.length (); )
+	    {
+	      constraint *c = &m_constraints[con_idx];
+
+	      /* Remove constraints that refer to the deleted EC.  */
+	      if (c->m_lhs == ec_idx
+		  || c->m_rhs == ec_idx)
+		{
+		  m_constraints.ordered_remove (con_idx);
+		  stats->m_num_constraints++;
+		}
+	      else
+		{
+		  /* Renumber constraints that refer to ECs that have
+		     had their idx changed.  */
+		  c->m_lhs.update_for_removal (ec_idx);
+		  c->m_rhs.update_for_removal (ec_idx);
+
+		  con_idx++;
+		}
+	    }
+	}
+      else
+	ec_idx++;
+    }
+
+  /* Now delete any constraints that are purely between constants.  */
+  for (unsigned con_idx = 0; con_idx < m_constraints.length (); )
+    {
+      constraint *c = &m_constraints[con_idx];
+      if (m_equiv_classes[c->m_lhs.m_idx]->m_vars.length () == 0
+	  && m_equiv_classes[c->m_rhs.m_idx]->m_vars.length () == 0)
+	{
+	  m_constraints.ordered_remove (con_idx);
+	  stats->m_num_constraints++;
+	}
+      else
+	{
+	  con_idx++;
+	}
+    }
+
+  /* Finally, delete any ECs that purely contain constants and aren't
+     referenced by any constraints.  */
+  for (unsigned ec_idx = 0; ec_idx < m_equiv_classes.length (); )
+    {
+      equiv_class *ec = m_equiv_classes[ec_idx];
+      if (ec->m_vars.length () == 0)
+	{
+	  bool has_constraint = false;
+	  for (unsigned con_idx = 0; con_idx < m_constraints.length ();
+	       con_idx++)
+	    {
+	      constraint *c = &m_constraints[con_idx];
+	      if (c->m_lhs.m_idx == ec_idx
+		  || c->m_rhs.m_idx == ec_idx)
+		{
+		  has_constraint = true;
+		  break;
+		}
+	    }
+	  if (!has_constraint)
+	    {
+	      delete ec;
+	      m_equiv_classes.ordered_remove (ec_idx);
+	      stats->m_num_equiv_classes++;
+
+	      /* Renumber constraints that refer to ECs that have
+		 had their idx changed.  */
+	      for (unsigned con_idx = 0; con_idx < m_constraints.length ();
+		   con_idx++)
+		{
+		  constraint *c = &m_constraints[con_idx];
+		  c->m_lhs.update_for_removal (ec_idx);
+		  c->m_rhs.update_for_removal (ec_idx);
+		}
+	      continue;
+	    }
+	}
+      ec_idx++;
+    }
+
+  validate ();
+}
+
+/* FIXME.  */
+
+void
+constraint_manager::remap_svalue_ids (const svalue_id_map &map)
+{
+  int i;
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    ec->remap_svalue_ids (map);
+}
+
+/* FIXME.  */
+
+void
+constraint_manager::validate () const
+{
+  // TODO: skip this in a release build
+
+  int i;
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    {
+      gcc_assert (ec);
+
+      int j;
+      svalue_id *sid;
+      FOR_EACH_VEC_ELT (ec->m_vars, j, sid)
+	{
+	  gcc_assert (!sid->null_p ());
+	  gcc_assert (sid->as_int () < get_num_svalues ());
+	}
+      if (ec->m_constant)
+	gcc_assert (CONSTANT_CLASS_P (ec->m_constant));
+#if 0
+      else
+	gcc_assert (ec->m_vars.length () > 0);
+#endif
+    }
+
+  constraint *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      gcc_assert (c->m_lhs.m_idx <= m_equiv_classes.length ());
+      gcc_assert (c->m_rhs.m_idx <= m_equiv_classes.length ());
+    }
+}
+
+// FIXME:
+void
+debug (const constraint_manager &cm)
+{
+  fprintf (stderr, "%s\n", cm.to_str ().c_str ());
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Various constraint_manager selftests.
+   These have to be written in terms of a region_model, since
+   the latter is responsible for managing svalue and svalue_id
+   instances.  */
+
+/* Attempt to add the constraint (LHS OP RHS) to MODEL.
+   Verify that MODEL remains satisfiable.  */
+
+#define ADD_SAT_CONSTRAINT(MODEL, LHS, OP, RHS)	\
+  SELFTEST_BEGIN_STMT					\
+    bool sat = (MODEL).add_constraint (LHS, OP, RHS);	\
+    ASSERT_TRUE (sat);					\
+  SELFTEST_END_STMT
+
+/* Attempt to add the constraint (LHS OP RHS) to MODEL.
+   Verify that the result is not satisfiable.  */
+
+#define ADD_UNSAT_CONSTRAINT(MODEL, LHS, OP, RHS)	\
+  SELFTEST_BEGIN_STMT					\
+    bool sat = (MODEL).add_constraint (LHS, OP, RHS);	\
+    ASSERT_FALSE (sat);				\
+  SELFTEST_END_STMT
+
+static void
+assert_condition (const location &loc,
+		  region_model &model,
+		  tree lhs, tree_code op, tree rhs,
+		  tristate expected)
+{
+  tristate actual = model.eval_condition (lhs, op, rhs);
+  ASSERT_EQ_AT (loc, actual, expected);
+}
+
+#define ASSERT_CONDITION_TRUE(PATH_STATE, LHS, OP, RHS) \
+  SELFTEST_BEGIN_STMT							\
+  assert_condition (SELFTEST_LOCATION, PATH_STATE, LHS, OP, RHS,	\
+		    tristate (tristate::TS_TRUE));		\
+  SELFTEST_END_STMT
+
+#define ASSERT_CONDITION_FALSE(PATH_STATE, LHS, OP, RHS) \
+  SELFTEST_BEGIN_STMT							\
+  assert_condition (SELFTEST_LOCATION, PATH_STATE, LHS, OP, RHS,	\
+		    tristate (tristate::TS_FALSE));		\
+  SELFTEST_END_STMT
+
+#define ASSERT_CONDITION_UNKNOWN(PATH_STATE, LHS, OP, RHS) \
+  SELFTEST_BEGIN_STMT							\
+  assert_condition (SELFTEST_LOCATION, PATH_STATE, LHS, OP, RHS,	\
+		    tristate (tristate::TS_UNKNOWN));		\
+  SELFTEST_END_STMT
+
+/* FIXME.  */
+
+static void
+test_constraint_conditions ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+
+  tree x = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		      get_identifier ("x"),
+		      integer_type_node);
+  tree y = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("y"),
+		       integer_type_node);
+  tree z = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("z"),
+		       integer_type_node);
+
+  /* Self-comparisons.  */
+  {
+    region_model model;
+    ASSERT_CONDITION_TRUE (model, x, EQ_EXPR, x);
+    ASSERT_CONDITION_TRUE (model, x, LE_EXPR, x);
+    ASSERT_CONDITION_TRUE (model, x, GE_EXPR, x);
+    ASSERT_CONDITION_FALSE (model, x, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE (model, x, LT_EXPR, x);
+    ASSERT_CONDITION_FALSE (model, x, GT_EXPR, x);
+  }
+
+  /* x == y.  */
+  {
+    region_model model;
+    ASSERT_CONDITION_UNKNOWN (model, x, EQ_EXPR, y);
+
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
+
+    ASSERT_CONDITION_TRUE (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_TRUE (model, x, LE_EXPR, y);
+    ASSERT_CONDITION_TRUE (model, x, GE_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, GT_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_TRUE (model, y, EQ_EXPR, x);
+    ASSERT_CONDITION_TRUE (model, y, LE_EXPR, x);
+    ASSERT_CONDITION_TRUE (model, y, GE_EXPR, x);
+    ASSERT_CONDITION_FALSE (model, y, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE (model, y, LT_EXPR, x);
+    ASSERT_CONDITION_FALSE (model, y, GT_EXPR, x);
+
+    /* Comparison with other var.  */
+    ASSERT_CONDITION_UNKNOWN (model, x, EQ_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (model, x, LE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (model, x, GE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (model, x, NE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (model, x, LT_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (model, x, GT_EXPR, z);
+  }
+
+  /* x == y, then y == z  */
+  {
+    region_model model;
+    ASSERT_CONDITION_UNKNOWN (model, x, EQ_EXPR, y);
+
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
+    ADD_SAT_CONSTRAINT (model, y, EQ_EXPR, z);
+
+    ASSERT_CONDITION_TRUE (model, x, EQ_EXPR, z);
+    ASSERT_CONDITION_TRUE (model, x, LE_EXPR, z);
+    ASSERT_CONDITION_TRUE (model, x, GE_EXPR, z);
+    ASSERT_CONDITION_FALSE (model, x, NE_EXPR, z);
+    ASSERT_CONDITION_FALSE (model, x, LT_EXPR, z);
+    ASSERT_CONDITION_FALSE (model, x, GT_EXPR, z);
+  }
+
+  /* x != y.  */
+  {
+    region_model model;
+
+    ADD_SAT_CONSTRAINT (model, x, NE_EXPR, y);
+
+    ASSERT_CONDITION_TRUE (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (model, x, LE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (model, x, GE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (model, x, GT_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_TRUE (model, y, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE (model, y, EQ_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (model, y, LE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (model, y, GE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (model, y, LT_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (model, y, GT_EXPR, x);
+
+    /* Comparison with other var.  */
+    ASSERT_CONDITION_UNKNOWN (model, x, EQ_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (model, x, LE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (model, x, GE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (model, x, NE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (model, x, LT_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (model, x, GT_EXPR, z);
+  }
+
+  /* x < y.  */
+  {
+    region_model model;
+
+    ADD_SAT_CONSTRAINT (model, x, LT_EXPR, y);
+
+    ASSERT_CONDITION_TRUE (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_TRUE (model, x, LE_EXPR, y);
+    ASSERT_CONDITION_TRUE (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, GT_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, GE_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_FALSE (model, y, LT_EXPR, x);
+    ASSERT_CONDITION_FALSE (model, y, LE_EXPR, x);
+    ASSERT_CONDITION_TRUE (model, y, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE (model, y, EQ_EXPR, x);
+    ASSERT_CONDITION_TRUE (model, y, GT_EXPR, x);
+    ASSERT_CONDITION_TRUE (model, y, GE_EXPR, x);
+  }
+
+  /* x <= y.  */
+  {
+    region_model model;
+
+    ADD_SAT_CONSTRAINT (model, x, LE_EXPR, y);
+
+    ASSERT_CONDITION_UNKNOWN (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_TRUE (model, x, LE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, GT_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (model, x, GE_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_FALSE (model, y, LT_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (model, y, LE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (model, y, NE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (model, y, EQ_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (model, y, GT_EXPR, x);
+    ASSERT_CONDITION_TRUE (model, y, GE_EXPR, x);
+  }
+
+  /* x > y.  */
+  {
+    region_model model;
+
+    ADD_SAT_CONSTRAINT (model, x, GT_EXPR, y);
+
+    ASSERT_CONDITION_TRUE (model, x, GT_EXPR, y);
+    ASSERT_CONDITION_TRUE (model, x, GE_EXPR, y);
+    ASSERT_CONDITION_TRUE (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, LE_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_FALSE (model, y, GT_EXPR, x);
+    ASSERT_CONDITION_FALSE (model, y, GE_EXPR, x);
+    ASSERT_CONDITION_TRUE (model, y, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE (model, y, EQ_EXPR, x);
+    ASSERT_CONDITION_TRUE (model, y, LT_EXPR, x);
+    ASSERT_CONDITION_TRUE (model, y, LE_EXPR, x);
+  }
+
+  /* x >= y.  */
+  {
+    region_model model;
+
+    ADD_SAT_CONSTRAINT (model, x, GE_EXPR, y);
+
+    ASSERT_CONDITION_UNKNOWN (model, x, GT_EXPR, y);
+    ASSERT_CONDITION_TRUE (model, x, GE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (model, x, LE_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_FALSE (model, y, GT_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (model, y, GE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (model, y, NE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (model, y, EQ_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (model, y, LT_EXPR, x);
+    ASSERT_CONDITION_TRUE (model, y, LE_EXPR, x);
+  }
+
+  // TODO: implied orderings
+
+  /* Constants.  */
+  {
+    region_model model;
+    ASSERT_CONDITION_FALSE (model, int_0, EQ_EXPR, int_42);
+    ASSERT_CONDITION_TRUE (model, int_0, NE_EXPR, int_42);
+    ASSERT_CONDITION_TRUE (model, int_0, LT_EXPR, int_42);
+    ASSERT_CONDITION_TRUE (model, int_0, LE_EXPR, int_42);
+    ASSERT_CONDITION_FALSE (model, int_0, GT_EXPR, int_42);
+    ASSERT_CONDITION_FALSE (model, int_0, GE_EXPR, int_42);
+  }
+
+  /* x == 0, y == 42.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
+    ADD_SAT_CONSTRAINT (model, y, EQ_EXPR, int_42);
+
+    ASSERT_CONDITION_TRUE (model, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_TRUE (model, x, LE_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, GE_EXPR, y);
+    ASSERT_CONDITION_TRUE (model, x, LT_EXPR, y);
+    ASSERT_CONDITION_FALSE (model, x, GT_EXPR, y);
+  }
+
+  /* Unsatisfiable combinations.  */
+
+  /* x == y && x != y.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
+    ADD_UNSAT_CONSTRAINT (model, x, NE_EXPR, y);
+  }
+
+  /* x == 0 then x == 42.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
+    ADD_UNSAT_CONSTRAINT (model, x, EQ_EXPR, int_42);
+  }
+
+  /* x == 0 then x != 0.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
+    ADD_UNSAT_CONSTRAINT (model, x, NE_EXPR, int_0);
+  }
+
+  /* x == 0 then x > 0.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
+    ADD_UNSAT_CONSTRAINT (model, x, GT_EXPR, int_0);
+  }
+
+  /* x != y && x == y.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, x, NE_EXPR, y);
+    ADD_UNSAT_CONSTRAINT (model, x, EQ_EXPR, y);
+  }
+
+  /* x <= y && x > y.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, x, LE_EXPR, y);
+    ADD_UNSAT_CONSTRAINT (model, x, GT_EXPR, y);
+  }
+
+  // etc
+}
+
+/* FIXME.  */
+
+static void
+test_transitivity ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+
+  tree a = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("a"),
+		       integer_type_node);
+  tree b = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("b"),
+		       integer_type_node);
+  tree c = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("c"),
+		       integer_type_node);
+  tree d = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("d"),
+		       integer_type_node);
+
+  /* a == b, then c == d, then c == b.  */
+  {
+    region_model model;
+    ASSERT_CONDITION_UNKNOWN (model, a, EQ_EXPR, b);
+    ASSERT_CONDITION_UNKNOWN (model, b, EQ_EXPR, c);
+    ASSERT_CONDITION_UNKNOWN (model, c, EQ_EXPR, d);
+    ASSERT_CONDITION_UNKNOWN (model, a, EQ_EXPR, d);
+
+    ADD_SAT_CONSTRAINT (model, a, EQ_EXPR, b);
+    ASSERT_CONDITION_TRUE (model, a, EQ_EXPR, b);
+
+    ADD_SAT_CONSTRAINT (model, c, EQ_EXPR, d);
+    ASSERT_CONDITION_TRUE (model, c, EQ_EXPR, d);
+    ASSERT_CONDITION_UNKNOWN (model, a, EQ_EXPR, d);
+
+    ADD_SAT_CONSTRAINT (model, c, EQ_EXPR, b);
+    ASSERT_CONDITION_TRUE (model, c, EQ_EXPR, b);
+    ASSERT_CONDITION_TRUE (model, a, EQ_EXPR, d);
+  }
+
+  /* Transitivity: "a < b", "b < c" should imply "a < c".  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, LT_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, LT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (model, a, LT_EXPR, c);
+    ASSERT_CONDITION_FALSE (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a <= b", "b < c" should imply "a < c".  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, LE_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, LT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (model, a, LT_EXPR, c);
+    ASSERT_CONDITION_FALSE (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a <= b", "b <= c" should imply "a <= c".  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, LE_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, LE_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (model, a, LE_EXPR, c);
+    ASSERT_CONDITION_UNKNOWN (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a > b", "b > c" should imply "a > c".  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, GT_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, GT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (model, a, GT_EXPR, c);
+    ASSERT_CONDITION_FALSE (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a >= b", "b > c" should imply " a > c".  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, GT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (model, a, GT_EXPR, c);
+    ASSERT_CONDITION_FALSE (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a >= b", "b >= c" should imply "a >= c".  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, GE_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (model, a, GE_EXPR, c);
+    ASSERT_CONDITION_UNKNOWN (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "(a < b)", "(c < d)", "(b < c)" should
+     imply the easy cases:
+       (a < c)
+       (b < d)
+     but also that:
+       (a < d).  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, LT_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, c, LT_EXPR, d);
+    ADD_SAT_CONSTRAINT (model, b, LT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (model, a, LT_EXPR, c);
+    ASSERT_CONDITION_TRUE (model, b, LT_EXPR, d);
+    ASSERT_CONDITION_TRUE (model, a, LT_EXPR, d);
+  }
+
+  /* Transitivity: "a >= b", "b >= a" should imply that a == b.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, GE_EXPR, a);
+
+    // TODO:
+    ASSERT_CONDITION_TRUE (model, a, EQ_EXPR, b);
+  }
+
+  /* Transitivity: "a >= b", "b > a" should be impossible.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
+    ADD_UNSAT_CONSTRAINT (model, b, GT_EXPR, a);
+  }
+
+  /* Transitivity: "a >= b", "b >= c", "c >= a" should imply
+     that a == b == c.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, GE_EXPR, c);
+    ADD_SAT_CONSTRAINT (model, c, GE_EXPR, a);
+
+    ASSERT_CONDITION_TRUE (model, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a > b", "b > c", "c > a"
+     should be impossible.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, GT_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, b, GT_EXPR, c);
+    ADD_UNSAT_CONSTRAINT (model, c, GT_EXPR, a);
+  }
+
+}
+
+/* FIXME.  */
+
+static void
+test_constant_comparisons ()
+{
+  tree int_3 = build_int_cst (integer_type_node, 3);
+  tree int_4 = build_int_cst (integer_type_node, 4);
+  tree int_5 = build_int_cst (integer_type_node, 5);
+
+  tree int_1023 = build_int_cst (integer_type_node, 1023);
+  tree int_1024 = build_int_cst (integer_type_node, 1024);
+
+  tree a = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("a"),
+		       integer_type_node);
+  tree b = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("b"),
+		       integer_type_node);
+
+  /* Given a >= 1024, then a <= 1023 should be impossible.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, GE_EXPR, int_1024);
+    ADD_UNSAT_CONSTRAINT (model, a, LE_EXPR, int_1023);
+  }
+
+  /* a > 4.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, GT_EXPR, int_4);
+    ASSERT_CONDITION_TRUE (model, a, GT_EXPR, int_4);
+    ASSERT_CONDITION_TRUE (model, a, NE_EXPR, int_3);
+    ASSERT_CONDITION_UNKNOWN (model, a, NE_EXPR, int_5);
+  }
+
+  /* a <= 4.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, LE_EXPR, int_4);
+    ASSERT_CONDITION_FALSE (model, a, GT_EXPR, int_4);
+    ASSERT_CONDITION_FALSE (model, a, GT_EXPR, int_5);
+    ASSERT_CONDITION_UNKNOWN (model, a, NE_EXPR, int_3);
+  }
+
+  /* If "a > b" and "a == 3", then "b == 4" ought to be unsatisfiable.  */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, a, GT_EXPR, b);
+    ADD_SAT_CONSTRAINT (model, a, EQ_EXPR, int_3);
+    ADD_UNSAT_CONSTRAINT (model, b, EQ_EXPR, int_4);
+  }
+
+  /* Various tests of int ranges where there is only one possible candidate.  */
+  {
+    /* If "a <= 4" && "a > 3", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      region_model model;
+      ADD_SAT_CONSTRAINT (model, a, LE_EXPR, int_4);
+      ADD_SAT_CONSTRAINT (model, a, GT_EXPR, int_3);
+      ASSERT_CONDITION_TRUE (model, a, EQ_EXPR, int_4);
+    }
+
+    /* If "a > 3" && "a <= 4", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      region_model model;
+      ADD_SAT_CONSTRAINT (model, a, GT_EXPR, int_3);
+      ADD_SAT_CONSTRAINT (model, a, LE_EXPR, int_4);
+      ASSERT_CONDITION_TRUE (model, a, EQ_EXPR, int_4);
+    }
+    /* If "a > 3" && "a < 5", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      region_model model;
+      ADD_SAT_CONSTRAINT (model, a, GT_EXPR, int_3);
+      ADD_SAT_CONSTRAINT (model, a, LT_EXPR, int_5);
+      ASSERT_CONDITION_TRUE (model, a, EQ_EXPR, int_4);
+    }
+    /* If "a >= 4" && "a < 5", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      region_model model;
+      ADD_SAT_CONSTRAINT (model, a, GE_EXPR, int_4);
+      ADD_SAT_CONSTRAINT (model, a, LT_EXPR, int_5);
+      ASSERT_CONDITION_TRUE (model, a, EQ_EXPR, int_4);
+    }
+    /* If "a >= 4" && "a <= 4", then "a == 4".  */
+    {
+      region_model model;
+      ADD_SAT_CONSTRAINT (model, a, GE_EXPR, int_4);
+      ADD_SAT_CONSTRAINT (model, a, LE_EXPR, int_4);
+      ASSERT_CONDITION_TRUE (model, a, EQ_EXPR, int_4);
+    }
+  }
+
+#if 0
+  /* As above, but for float.   */
+  {
+    region_model model;
+    ADD_SAT_CONSTRAINT (model, f, GT_EXPR, float_3);
+    ADD_SAT_CONSTRAINT (model, f, LE_EXPR, float_4);
+    ASSERT_CONDITION_UNKNOWN (model, f, EQ_EXPR, int_4);
+  }
+#endif
+}
+
+/* FIXME.  */
+
+static void
+test_constraint_impl ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+
+  tree x = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("x"),
+		       integer_type_node);
+  tree y = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("y"),
+		       integer_type_node);
+  tree z = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("z"),
+		       integer_type_node);
+
+  /* x == y.  */
+  {
+    region_model model;
+
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
+
+    /* Assert various things about the insides of model.  */
+    constraint_manager *cm = model.get_constraints ();
+    ASSERT_EQ (cm->m_constraints.length (), 0);
+    ASSERT_EQ (cm->m_equiv_classes.length (), 1);
+  }
+
+  /* y <= z; x == y.  */
+  {
+    region_model model;
+    ASSERT_CONDITION_UNKNOWN (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (model, x, GE_EXPR, z);
+
+    ADD_SAT_CONSTRAINT (model, y, GE_EXPR, z);
+    ASSERT_CONDITION_TRUE (model, y, GE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (model, x, GE_EXPR, z);
+
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, y);
+
+    /* Assert various things about the insides of model.  */
+    constraint_manager *cm = model.get_constraints ();
+    ASSERT_EQ (cm->m_constraints.length (), 1);
+    ASSERT_EQ (cm->m_equiv_classes.length (), 2);
+
+    /* Ensure that we merged the constraints.  */
+    ASSERT_CONDITION_TRUE (model, x, GE_EXPR, z);
+  }
+
+  /* y <= z; y == x.  */
+  {
+    region_model model;
+    ASSERT_CONDITION_UNKNOWN (model, x, EQ_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (model, x, GE_EXPR, z);
+
+    ADD_SAT_CONSTRAINT (model, y, GE_EXPR, z);
+    ASSERT_CONDITION_TRUE (model, y, GE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (model, x, GE_EXPR, z);
+
+    ADD_SAT_CONSTRAINT (model, y, EQ_EXPR, x);
+
+    /* Assert various things about the insides of model.  */
+    constraint_manager *cm = model.get_constraints ();
+    ASSERT_EQ (cm->m_constraints.length (), 1);
+    ASSERT_EQ (cm->m_equiv_classes.length (), 2);
+
+    /* Ensure that we merged the constraints.  */
+    ASSERT_CONDITION_TRUE (model, x, GE_EXPR, z);
+  }
+
+  /* x == 0, then x != 42.  */
+  {
+    region_model model;
+
+    ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
+    ADD_SAT_CONSTRAINT (model, x, NE_EXPR, int_42);
+
+    /* Assert various things about the insides of model.  */
+    constraint_manager *cm = model.get_constraints ();
+    ASSERT_EQ (cm->m_constraints.length (), 1);
+    ASSERT_EQ (cm->m_equiv_classes.length (), 2);
+    ASSERT_EQ (cm->m_constraints[0].m_lhs,
+	       cm->get_or_add_equiv_class (model.get_rvalue (int_0)));
+    ASSERT_EQ (cm->m_constraints[0].m_rhs,
+	       cm->get_or_add_equiv_class (model.get_rvalue (int_42)));
+    ASSERT_EQ (cm->m_constraints[0].m_op, CONSTRAINT_LT);
+  }
+
+  // TODO: selftest for merging ecs "in the middle"
+  // where a non-final one gets overwritten
+
+  // TODO: selftest where there are pre-existing constraints
+}
+
+#if 0
+/* FIXME.  */
+
+static void
+test_assignment ()
+{
+  svalue_id int_0 (build_int_cst (integer_type_node, 0), 0);
+
+  svalue_id x (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("x"),
+			  integer_type_node), 0);
+  svalue_id y (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("y"),
+			  integer_type_node), 0);
+  /* "x == 0", then use of y, then "y = 0".  */
+  constraint_manager cm;
+  ADD_SAT_CONSTRAINT (cm, x, EQ_EXPR, int_0);
+  ASSERT_CONDITION_UNKNOWN (cm, y, EQ_EXPR, int_0);
+  cm.on_assignment (y, int_0);
+  ASSERT_CONDITION_TRUE (cm, y, EQ_EXPR, int_0);
+  ASSERT_CONDITION_TRUE (cm, y, EQ_EXPR, x);
+}
+#endif
+
+/* Check that operator== and hashing works as expected for the
+   various types.  */
+
+static void
+test_equality ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+
+  tree x = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("x"),
+		       integer_type_node);
+  tree y = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("y"),
+		       integer_type_node);
+  tree z = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("z"),
+		       integer_type_node);
+
+  {
+    region_model model0;
+    region_model model1;
+
+    constraint_manager *cm0 = model0.get_constraints ();
+    constraint_manager *cm1 = model1.get_constraints ();
+
+    ASSERT_EQ (cm0->hash (), cm1->hash ());
+    ASSERT_EQ (*cm0, *cm1);
+
+    ASSERT_EQ (model0.hash (), model1.hash ());
+    ASSERT_EQ (model0, model1);
+
+    ADD_SAT_CONSTRAINT (model1, x, EQ_EXPR, y);
+    ASSERT_NE (cm0->hash (), cm1->hash ());
+    ASSERT_NE (*cm0, *cm1);
+
+    ASSERT_NE (model0.hash (), model1.hash ());
+    ASSERT_NE (model0, model1);
+
+    region_model model2;
+    constraint_manager *cm2 = model2.get_constraints ();
+    /* Make the same change to cm2.  */
+    ADD_SAT_CONSTRAINT (model2, x, EQ_EXPR, y);
+    ASSERT_EQ (cm1->hash (), cm2->hash ());
+    ASSERT_EQ (*cm1, *cm2);
+
+    ASSERT_EQ (model1.hash (), model2.hash ());
+    ASSERT_EQ (model1, model2);
+  }
+}
+
+#if 0
+
+/* A subclass of purge_criteria for selftests: purge all svalue_id instances.  */
+
+class purge_all_svalue_ids : public purge_criteria
+{
+public:
+  bool should_purge_p (svalue_id) const FINAL OVERRIDE
+  {
+    return true;
+  }
+};
+
+/* Check that constraint_manager objects can "forget".  */
+
+static void
+test_purging ()
+{
+  svalue_id int_42 (build_int_cst (integer_type_node, 42), 0);
+  svalue_id int_0 (build_int_cst (integer_type_node, 0), 0);
+
+  svalue_id x (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("x"),
+			  integer_type_node), 0);
+  svalue_id y (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("y"),
+			  integer_type_node), 0);
+
+  {
+    constraint_manager cm0;
+    constraint_manager cm1;
+
+    ADD_SAT_CONSTRAINT (cm1, x, EQ_EXPR, y);
+    ASSERT_NE (cm0, cm1);
+
+    purge_one_svalue_id px (x);
+    cm1.purge (px);
+
+    purge_one_svalue_id py (y);
+    cm1.purge (py);
+
+    ASSERT_EQ (cm0, cm1);
+  }
+
+  {
+    constraint_manager cm0;
+    constraint_manager cm1;
+
+    ADD_SAT_CONSTRAINT (cm1, x, EQ_EXPR, int_42);
+    ASSERT_NE (cm0, cm1);
+
+    purge_one_svalue_id px (x);
+    cm1.purge (px);
+
+    ASSERT_EQ (cm0, cm1);
+  }
+
+  {
+    constraint_manager cm0;
+    constraint_manager cm1;
+
+    ADD_SAT_CONSTRAINT (cm1, x, GE_EXPR, int_0);
+    ADD_SAT_CONSTRAINT (cm1, x, LE_EXPR, int_42);
+    ASSERT_NE (cm0, cm1);
+
+    purge_one_svalue_id px (x);
+    cm1.purge (px);
+
+    ASSERT_EQ (cm0, cm1);
+  }
+
+  {
+    constraint_manager cm0;
+    constraint_manager cm1;
+
+    ADD_SAT_CONSTRAINT (cm1, x, NE_EXPR, int_42);
+    ADD_SAT_CONSTRAINT (cm1, y, NE_EXPR, int_0);
+    ASSERT_NE (cm0, cm1);
+
+    purge_one_svalue_id px (x);
+    cm1.purge (px);
+    ASSERT_NE (cm0, cm1);
+
+    ASSERT_CONDITION_TRUE (cm1, y, NE_EXPR, int_0);
+  }
+
+  {
+    constraint_manager cm0;
+    constraint_manager cm1;
+
+    ADD_SAT_CONSTRAINT (cm1, x, NE_EXPR, int_42);
+    ADD_SAT_CONSTRAINT (cm1, y, NE_EXPR, int_0);
+    ASSERT_NE (cm0, cm1);
+
+    purge_all_svalue_ids p;
+    cm1.purge (p);
+    ASSERT_EQ (cm0, cm1);
+  }
+
+}
+#endif
+
+
+/* Run all of the selftests within this file.  */
+
+void
+analyzer_constraint_manager_cc_tests ()
+{
+  test_constraint_conditions ();
+  test_transitivity ();
+  test_constant_comparisons ();
+  test_constraint_impl ();
+#if 0
+  test_assignment ();
+  test_stack_frames ();
+#endif
+  test_equality ();
+#if 0
+  test_purging ();
+#endif
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/analyzer/constraint-manager.h b/gcc/analyzer/constraint-manager.h
new file mode 100644
index 0000000..5f0d3ca
--- /dev/null
+++ b/gcc/analyzer/constraint-manager.h
@@ -0,0 +1,243 @@
+/* Tracking equivalence classes and constraints at a point on an execution path.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_ANALYZER_CONSTRAINT_MANAGER_H
+#define GCC_ANALYZER_CONSTRAINT_MANAGER_H
+
+#include "region-model.h" // for svalue_id
+
+class superedge;
+class call_superedge;
+class return_superedge;
+
+class constraint_manager;
+
+/* One of the end-points of a range.  */
+
+struct bound
+{
+  bound () : m_constant (NULL_TREE), m_closed (false) {}
+  bound (tree constant, bool closed)
+  : m_constant (constant), m_closed (closed) {}
+
+  void ensure_closed (bool is_upper);
+
+  tree m_constant;
+  bool m_closed;
+};
+
+/* FIXME.  */
+
+struct range
+{
+  range () : m_lower_bound (), m_upper_bound () {}
+  range (const bound &lower, const bound &upper)
+  : m_lower_bound (lower), m_upper_bound (upper) {}
+
+  std::string to_str () const;
+
+  bool constrained_to_single_element (tree *out);
+
+  bound m_lower_bound;
+  bound m_upper_bound;
+};
+
+/* Abstract base class for specifying how state should be purged.  */
+
+class purge_criteria
+{
+public:
+  virtual ~purge_criteria () {}
+  virtual bool should_purge_p (svalue_id sid) const = 0;
+};
+
+/* FIXME.  */
+
+class equiv_class
+{
+public:
+  equiv_class ();
+  equiv_class (const equiv_class &other);
+  ~equiv_class ();
+
+  std::string to_str () const;
+  hashval_t hash () const;
+  bool operator== (const equiv_class &other);
+
+  void add (svalue_id sid, const constraint_manager &cm);
+  bool del (svalue_id sid);
+
+  tree get_any_constant () const { return m_constant; }
+
+  svalue_id get_representative () const;
+
+  void remap_svalue_ids (const svalue_id_map &map);
+
+  /* An equivalence class can contain at most one constant.  */
+  tree m_constant;
+  svalue_id m_cst_sid;
+
+  // FIXME: this is a bad representation, probably should be a set:
+  auto_vec<svalue_id> m_vars;
+
+  /* Has this value escaped from this function?  */
+  bool m_escaped;
+};
+
+enum constraint_op
+{
+  CONSTRAINT_NE,
+  CONSTRAINT_LT,
+  CONSTRAINT_LE
+};
+
+const char *constraint_op_code (enum constraint_op c_op);
+
+class equiv_class_id
+{
+public:
+  equiv_class_id (unsigned idx) : m_idx (idx) {}
+  const equiv_class &get_obj (const constraint_manager &cm) const;
+  equiv_class &get_obj (constraint_manager &cm) const;
+
+  bool operator== (const equiv_class_id &other) const
+  {
+    return m_idx == other.m_idx;
+  }
+  bool operator!= (const equiv_class_id &other) const
+  {
+    return m_idx != other.m_idx;
+  }
+
+  std::string to_str () const
+  {
+    return std::string ("ec") + std::to_string (m_idx);
+  }
+
+  void update_for_removal (equiv_class_id other)
+  {
+    if (m_idx > other.m_idx)
+      m_idx--;
+  }
+
+  unsigned m_idx;
+};
+
+class constraint {
+ public:
+  constraint (equiv_class_id lhs, enum constraint_op c_op, equiv_class_id rhs)
+    : m_lhs (lhs), m_op (c_op), m_rhs (rhs) {}
+
+  std::string to_str (const constraint_manager &cm) const;
+  hashval_t hash () const;
+  bool operator== (const constraint &other) const;
+
+  /* Is this an ordering, rather than a "!=".  */
+  bool is_ordering_p () const
+  {
+    return m_op != CONSTRAINT_NE;
+  }
+
+  equiv_class_id m_lhs;
+  enum constraint_op m_op;
+  equiv_class_id m_rhs;
+};
+
+/* FIXME.  */
+
+class constraint_manager
+{
+public:
+  constraint_manager () {}
+  constraint_manager (const constraint_manager &other);
+  virtual ~constraint_manager ();
+
+  virtual constraint_manager *clone (region_model *) const = 0;
+  virtual tree maybe_get_constant (svalue_id sid) const = 0;
+  virtual svalue_id get_sid_for_constant (tree cst) const = 0;
+  virtual int get_num_svalues () const = 0;
+
+  constraint_manager& operator= (const constraint_manager &other);
+
+  hashval_t hash () const;
+  bool operator== (const constraint_manager &other) const;
+  bool operator!= (const constraint_manager &other) const
+  {
+    return !(*this == other);
+  }
+
+  std::string to_str () const;
+  void print (pretty_printer *pp) const;
+  void dump_to_pp (pretty_printer *pp) const;
+  void dump (FILE *fp) const;
+  void dump () const;
+
+  const equiv_class &get_equiv_class_by_index (unsigned idx) const
+  {
+    return *m_equiv_classes[idx];
+  }
+  equiv_class &get_equiv_class_by_index (unsigned idx)
+  {
+    return *m_equiv_classes[idx];
+  }
+
+  equiv_class &get_equiv_class (svalue_id sid)
+  {
+    equiv_class_id ec_id = get_or_add_equiv_class (sid);
+    return ec_id.get_obj (*this);
+  }
+
+  bool add_constraint (svalue_id lhs, enum tree_code op, svalue_id rhs);
+
+  bool add_constraint (equiv_class_id lhs_ec_id,
+		       enum tree_code op,
+		       equiv_class_id rhs_ec_id);
+
+  bool get_equiv_class (svalue_id sid, equiv_class_id *out) const;
+  equiv_class_id get_or_add_equiv_class (svalue_id sid);
+  tristate eval_condition (equiv_class_id lhs,
+			   enum tree_code op,
+			   equiv_class_id rhs);
+  tristate eval_condition (svalue_id lhs,
+			   enum tree_code op,
+			   svalue_id rhs);
+
+  /* For implementing "__analyzer_eval".  */
+  tristate dump_condition (tree lhs, enum tree_code op, tree rhs);
+
+  void recursively_mark_as_escaping (equiv_class_id ec_id);
+  bool calc_escaped_p (svalue_id sid);
+
+  void purge (const purge_criteria &p, purge_stats *stats);
+
+  void remap_svalue_ids (const svalue_id_map &map);
+
+  void validate () const;
+
+  auto_vec<equiv_class *> m_equiv_classes;
+  auto_vec<constraint> m_constraints;
+
+ private:
+  void add_constraint_internal (equiv_class_id lhs_id,
+				enum constraint_op c_op,
+				equiv_class_id rhs_id);
+};
+
+#endif /* GCC_ANALYZER_CONSTRAINT_MANAGER_H */
diff --git a/gcc/analyzer/constraints.cc b/gcc/analyzer/constraints.cc
deleted file mode 100644
index 249e9de..0000000
--- a/gcc/analyzer/constraints.cc
+++ /dev/null
@@ -1,1987 +0,0 @@
-/* FIXME.  */
-#include "config.h"
-#include "gcc-plugin.h"
-#include "system.h"
-#include "coretypes.h"
-#include "tree.h"
-#include "tm.h"
-#include "toplev.h"
-#include "hash-table.h"
-#include "vec.h"
-#include "ggc.h"
-#include "basic-block.h"
-#include "tree-ssa-alias.h"
-#include "internal-fn.h"
-#include "gimple-fold.h"
-#include "tree-eh.h"
-#include "gimple-expr.h"
-#include "is-a.h"
-#include "gimple.h"
-#include "tree-pass.h"
-#include "intl.h"
-#include "context.h"
-#include "diagnostic.h"
-#include "bitmap.h"
-#include "gimple.h"
-#include "gimple-iterator.h"
-#include "gimple-pretty-print.h"
-#include "tree-pretty-print.h"
-#include "analyzer.h"
-#include "graphviz.h"
-#include "cgraph.h"
-#include "diagnostic-path.h"
-#include "gcc-rich-location.h"
-#include "tree-dfa.h"
-#include <string>
-#include "analyzer-logging.h"
-#include "stringpool.h"
-#include "selftest.h"
-#include "fold-const.h"
-#include "tristate.h"
-#include "constraints.h"
-#include "supergraph.h"
-
-/* FIXME.  */
-
-std::string
-path_var::to_str () const
-{
-  if (m_tree == NULL_TREE)
-    return "NULL";
-  if (CONSTANT_CLASS_P (m_tree))
-    return expr_to_string (m_tree);
-  else
-    return (std::string ("(")
-	    + expr_to_string (m_tree)
-	    + " @ "
-	    + std::to_string (m_stack_depth)
-	    + ")");
-}
-
-/* FIXME.  */
-
-equiv_class::equiv_class ()
-: m_constant (NULL_TREE), m_vars (), m_escaped (false), m_extra_state (NULL)
-{
-}
-
-/* FIXME.  */
-
-equiv_class::equiv_class (const equiv_class &other)
-: m_constant (other.m_constant),
-  m_vars (),
-  m_escaped (other.m_escaped),
-  m_extra_state (other.m_extra_state ? other.m_extra_state->clone () : NULL)
-{
-  // FIXME: copy-ctor for auto_vec!
-  int i;
-  path_var *v;
-  FOR_EACH_VEC_ELT (other.m_vars, i, v)
-    m_vars.safe_push (*v);
-}
-
-/* FIXME.  */
-
-equiv_class::~equiv_class ()
-{
-  delete m_extra_state;
-}
-
-/* FIXME.  */
-
-std::string
-equiv_class::to_str () const
-{
-  std::string result ("{");
-  int i;
-  path_var *v;
-  FOR_EACH_VEC_ELT (m_vars, i, v)
-    {
-      gcc_assert (!CONSTANT_CLASS_P (v->m_tree));
-      if (i > 0)
-	result += " == ";
-      result += v->to_str ();
-    }
-  if (m_constant)
-    {
-      if (i > 0)
-	result += " == ";
-      result += expr_to_string (m_constant);
-    }
-  if (m_escaped)
-    result += " (escaped)";
-  if (m_extra_state)
-    result += m_extra_state->to_str ();
-  return result + "}";
-}
-
-/* FIXME.  */
-
-hashval_t
-equiv_class::hash () const
-{
-  inchash::hash hstate;
-  int i;
-  path_var *pv;
-
-  inchash::add_expr (m_constant, hstate);
-  FOR_EACH_VEC_ELT (m_vars, i, pv)
-    inchash::add_path_var (*pv, hstate);
-  hstate.add_flag (m_escaped);
-  hstate.commit_flag ();
-  if (m_extra_state)
-    hstate.merge_hash (m_extra_state->hash ());
-  return hstate.end ();
-}
-
-/* FIXME.  */
-
-bool
-equiv_class::operator== (const equiv_class &other)
-{
-  if (m_constant != other.m_constant)
-    return false; // TODO: use tree equality here
-
-  int i;
-  path_var *pv;
-  FOR_EACH_VEC_ELT (m_vars, i, pv)
-    if (! (*pv == other.m_vars[i]))
-      return false;
-  if (m_escaped != other.m_escaped)
-    return false;
-
-  if ((m_extra_state != NULL) != (other.m_extra_state != NULL))
-    return false;
-
-  if (m_extra_state != NULL)
-    if (!(*m_extra_state == *other.m_extra_state))
-      return false;
-
-  return true;
-}
-
-/* FIXME.  */
-
-void
-equiv_class::add (path_var v)
-{
-  if (CONSTANT_CLASS_P (v.m_tree))
-    {
-      gcc_assert (m_constant == NULL_TREE);
-      m_constant = v.m_tree;
-    }
-  else
-    {
-      m_vars.safe_push (v);
-
-      if (DECL_P (v.m_tree)
-	  && is_global_var (v.m_tree))
-	m_escaped = true;
-    }
-}
-
-/* Remove V from this equivalence class.
-   Return true if V was the last var in the equivalence class (suggesting
-   a possible leak).  */
-
-bool
-equiv_class::del (path_var v)
-{
-  gcc_assert (!CONSTANT_CLASS_P (v.m_tree));
-
-  int i;
-  path_var *iv;
-  FOR_EACH_VEC_ELT (m_vars, i, iv)
-    {
-      if (*iv == v)
-	{
-	  m_vars[i] = m_vars[m_vars.length () - 1];
-	  m_vars.pop ();
-	  return m_vars.length () == 0;
-	}
-    }
-
-  /* V must be in the class.  */
-  gcc_unreachable ();
-  return false;
-}
-
-/* FIXME.  */
-
-int
-equiv_class::get_min_stack_depth () const
-{
-  int result = INT_MAX;
-
-  int i;
-  path_var *iv;
-  FOR_EACH_VEC_ELT (m_vars, i, iv)
-    result = std::min (result, iv->m_stack_depth);
-  return result;
-}
-
-/* FIXME.  */
-
-void
-equiv_class::purge_stack_frame (int callee_stack_depth)
-{
-  unsigned ix, ix2;
-  path_var *iv;
-  VEC_ORDERED_REMOVE_IF (m_vars, ix, ix2, iv,
-			 iv->m_stack_depth >= callee_stack_depth);
-  // FIXME: doesn't need to preserve order
-}
-
-/* Get a representative member of this class, for handling cases
-   where the IDs can change mid-traversal.  */
-
-path_var
-equiv_class::get_representative () const
-{
-  if (m_constant)
-    return path_var (m_constant, 0);
-  else
-    {
-      gcc_assert (m_vars.length () > 0);
-      return m_vars[0];
-    }
-}
-
-/* FIXME.  */
-
-const char *
-constraint_op_code (enum constraint_op c_op)
-{
-  switch (c_op)
-    {
-    default:
-      gcc_unreachable ();
-    case CONSTRAINT_NE: return "!=";
-    case CONSTRAINT_LT: return "<";
-    case CONSTRAINT_LE: return "<=";
-    }
-}
-
-/* Given "lhs C_OP rhs", determine "lhs T_OP rhs".
-
-   For example, given "x < y", then "x > y" is false.  */
-
-static tristate
-eval_constraint_op_for_op (enum constraint_op c_op, enum tree_code t_op)
-{
-  switch (c_op)
-    {
-    default:
-      gcc_unreachable ();
-    case CONSTRAINT_NE:
-      if (t_op == EQ_EXPR)
-	return tristate (tristate::TS_FALSE);
-      if (t_op == NE_EXPR)
-	return tristate (tristate::TS_TRUE);
-      break;
-    case CONSTRAINT_LT:
-      if (t_op == LT_EXPR || t_op == LE_EXPR || t_op == NE_EXPR)
-	return tristate (tristate::TS_TRUE);
-      if (t_op == EQ_EXPR || t_op == GT_EXPR || t_op == GE_EXPR)
-	return tristate (tristate::TS_FALSE);
-      break;
-    case CONSTRAINT_LE:
-      if (t_op == LE_EXPR)
-	return tristate (tristate::TS_TRUE);
-      if (t_op == GT_EXPR)
-	return tristate (tristate::TS_FALSE);
-      break;
-    }
-  return tristate (tristate::TS_UNKNOWN);
-}
-
-/* FIXME.  */
-
-std::string
-constraint::to_str (const path_state &ps) const
-{
-  return (m_lhs.to_str () + ": "
-	  + m_lhs.get_obj (ps).to_str ()
-	  + " "
-	  + constraint_op_code (m_op)
-	  + " "
-	  + m_rhs.to_str () + ": "
-	  + m_rhs.get_obj (ps).to_str ());
-}
-
-/* FIXME.  */
-
-hashval_t
-constraint::hash () const
-{
-  inchash::hash hstate;
-  hstate.add_int (m_lhs.m_idx);
-  hstate.add_int (m_op);
-  hstate.add_int (m_rhs.m_idx);
-  return hstate.end ();
-}
-
-/* FIXME.  */
-
-bool
-constraint::operator== (const constraint &other) const
-{
-  if (m_lhs != other.m_lhs)
-    return false;
-  if (m_op != other.m_op)
-    return false;
-  if (m_rhs != other.m_rhs)
-    return false;
-  return true;
-}
-
-/* FIXME.  */
-
-const equiv_class &
-equiv_class_id::get_obj (const path_state &ps) const
-{
-  return ps.get_equiv_class_by_index (m_idx);
-}
-
-/* FIXME.  */
-
-equiv_class &
-equiv_class_id::get_obj (path_state &ps) const
-{
-  return ps.get_equiv_class_by_index (m_idx);
-}
-
-/* FIXME.  */
-
-path_state::path_state (const path_state &other)
-: m_stack_depth (other.m_stack_depth)
-{
-  // FIXME: auto_vec's copy_ctor...
-  int i;
-  equiv_class *ec;
-  FOR_EACH_VEC_ELT (other.m_equiv_classes, i, ec)
-    m_equiv_classes.safe_push (new equiv_class (*ec));
-  constraint *c;
-  FOR_EACH_VEC_ELT (other.m_constraints, i, c)
-    m_constraints.safe_push (*c);
-}
-
-/* FIXME.  */
-
-path_state::~path_state ()
-{
-  int i;
-  equiv_class *ec;
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    delete ec;
-}
-
-/* FIXME.  */
-
-path_state&
-path_state::operator= (const path_state &other)
-{
-  m_stack_depth = other.m_stack_depth;
-
-  gcc_assert (m_equiv_classes.length () == 0);
-  gcc_assert (m_constraints.length () == 0);
-
-  int i;
-  equiv_class *ec;
-  FOR_EACH_VEC_ELT (other.m_equiv_classes, i, ec)
-    m_equiv_classes.safe_push (new equiv_class (*ec));
-  constraint *c;
-  FOR_EACH_VEC_ELT (other.m_constraints, i, c)
-    m_constraints.safe_push (*c);
-
-  return *this;
-}
-
-/* FIXME.  */
-
-hashval_t
-path_state::hash () const
-{
-  inchash::hash hstate;
-  int i;
-  equiv_class *ec;
-  constraint *c;
-
-  hstate.add_int (m_stack_depth);
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    hstate.merge_hash (ec->hash ());
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    hstate.merge_hash (c->hash ());
-  return hstate.end ();
-}
-
-/* FIXME.  */
-
-bool
-path_state::operator== (const path_state &other) const
-{
-  if (m_stack_depth != other.m_stack_depth)
-    return false;
-  if (m_equiv_classes.length () != other.m_equiv_classes.length ())
-    return false;
-
-  int i;
-  equiv_class *ec;
-
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    if (!(*ec == *other.m_equiv_classes[i]))
-      return false;
-
-  constraint *c;
-
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    if (!(*c == other.m_constraints[i]))
-      return false;
-
-  return true;
-}
-
-/* FIXME.  */
-
-std::string
-path_state::to_str () const
-{
-  std::string result ("{");
-  int i;
-  equiv_class *ec;
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    {
-      if (i > 0)
-	result += ", ";
-      result += equiv_class_id (i).to_str () + ": ";
-      result += ec->to_str ();
-    }
-  result += "  |  ";
-  constraint *c;
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    {
-      if (i > 0)
-	result += " && ";
-      result += c->to_str (*this);
-    }
-  result += "}@";
-  result += std::to_string (m_stack_depth);
-  return result;
-}
-
-/* FIXME.  */
-
-void
-path_state::on_assignment (path_var lhs, path_var rhs)
-{
-  /* Clear LHS.  */
-  {
-    equiv_class_id lhs_ec_id (-1);
-    if (get_equiv_class (lhs, &lhs_ec_id))
-      {
-	lhs_ec_id.get_obj (*this).del (lhs); // TODO
-      }
-  }
-
-  add_constraint (lhs, EQ_EXPR, rhs);
-}
-
-/* FIXME.
-   Attempt to extend this path's state with the constraints required by
-   EDGE (where LAST_STMT, if non-NULL, was the stmt guarding the edge).
-   Return true if the edge can be followed, false if it can't be.  */
-
-bool
-path_state::apply_constraints_for_edge (const superedge &edge,
-					const gimple *last_stmt)
-{
-  if (edge.m_kind == SUPEREDGE_CALL)
-    // FIXME: use as_a<>
-    return apply_constraints_for_call (*edge.dyn_cast_call_superedge ());
-
-  if (edge.m_kind == SUPEREDGE_RETURN)
-    // FIXME: use as_a<>
-    return apply_constraints_for_return (*edge.dyn_cast_return_superedge ());
-
-  if (last_stmt == NULL)
-    return true;
-
-  if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
-    return apply_constraints_for_gcond (edge, cond_stmt);
-
-  // FIXME: some linker issues with is_a_helper const
-  if (const gswitch *switch_stmt = dyn_cast <gswitch *> (const_cast <gimple *> (last_stmt)))
-    return apply_constraints_for_gswitch (edge, switch_stmt);
-
-  return true;
-}
-
-/* FIXME.  */
-
-bool
-path_state::apply_constraints_for_call (const call_superedge& edge)
-{
-  /* Map args to parms at next stack_depth, incrementing stack depth.  */
-  int caller_stack_depth = m_stack_depth;
-  int callee_stack_depth = ++m_stack_depth;
-
-  tree callee = edge.get_callee_decl ();
-  gcall *call_stmt = edge.get_call_stmt ();
-
-  int i = 0;
-  for (tree parm = DECL_ARGUMENTS (callee); parm;
-       parm = DECL_CHAIN (parm), ++i)
-    {
-      tree parm_default_ssa = ssa_default_def (edge.m_dest->m_fun, parm);
-      if (parm_default_ssa)
-	add_constraint (path_var (parm_default_ssa, callee_stack_depth),
-			EQ_EXPR,
-			path_var (gimple_call_arg (call_stmt, i),
-				  caller_stack_depth));
-    }
-  return true;
-}
-
-/* FIXME.  */
-
-bool
-path_state::apply_constraints_for_return (const return_superedge&)
-{
-  pop_stack_frame ();
-  return true;
-}
-
-/* Decrement the stack depth, purging vars at the old stack depth (and beyond).
-   This is broken out as a subroutine so it can be unit-tested.  */
-
-void
-path_state::pop_stack_frame ()
-{
-  int callee_stack_depth = m_stack_depth--;
-
-  int i;
-  equiv_class *ec;
-  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-    {
-      ec->purge_stack_frame (callee_stack_depth);
-      /* This can lead to empty equiv classes (possibly without
-	 constants).  Perhaps we ought to purge them?  */
-    }
-}
-
-
-/* FIXME.  */
-
-bool
-path_state::apply_constraints_for_gcond (const superedge &edge,
-					 const gcond *cond_stmt)
-{
-  ::edge cfg_edge = edge.get_any_cfg_edge ();
-  gcc_assert (cfg_edge != NULL);
-  gcc_assert (cfg_edge->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE));
-
-  enum tree_code op = gimple_cond_code (cond_stmt);
-  tree lhs = gimple_cond_lhs (cond_stmt);
-  tree rhs = gimple_cond_rhs (cond_stmt);
-  if (cfg_edge->flags & EDGE_FALSE_VALUE)
-    op = invert_tree_comparison (op, false /* honor_nans */);
-  return add_constraint (path_var (lhs, m_stack_depth),
-			 op,
-			 path_var (rhs, m_stack_depth));
-}
-
-/* FIXME.  */
-
-bool
-path_state::apply_constraints_for_gswitch (const superedge &edge,
-					   const gswitch *switch_stmt)
-{
-  // FIXME: use an as_a for this cast:
-  const switch_cfg_superedge &switch_edge = (const switch_cfg_superedge &)edge;
-  path_var index (gimple_switch_index (switch_stmt), m_stack_depth);
-  tree case_label = switch_edge.get_case_label ();
-  gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
-  path_var lower_bound (CASE_LOW (case_label), m_stack_depth);
-  path_var upper_bound (CASE_HIGH (case_label), m_stack_depth);
-  if (lower_bound.m_tree)
-    {
-      if (upper_bound.m_tree)
-	{
-	  /* Range.  */
-	  if (!add_constraint (index, GE_EXPR, lower_bound))
-	    return false;
-	  return add_constraint (index, LE_EXPR, upper_bound);
-	}
-      else
-	/* Single-value.  */
-	return add_constraint (index, EQ_EXPR, lower_bound);
-    }
-  else
-    {
-      /* The default case.
-	 Add exclusions based on the other cases.  */
-      for (unsigned other_idx = 1;
-	   other_idx < gimple_switch_num_labels (switch_stmt);
-	   other_idx++)
-	{
-	  tree other_label = gimple_switch_label (switch_stmt,
-						  other_idx);
-	  path_var other_lower_bound (CASE_LOW (other_label), m_stack_depth);
-	  path_var other_upper_bound (CASE_HIGH (other_label), m_stack_depth);
-	  gcc_assert (other_lower_bound.m_tree);
-	  if (other_upper_bound.m_tree)
-	    {
-	      /* Exclude this range-valued case.
-		 For now, we just exclude the boundary values.
-		 TODO: exclude the values within the region.  */
-	      if (!add_constraint (index, NE_EXPR, other_lower_bound))
-		return false;
-	      if (!add_constraint (index, NE_EXPR, other_upper_bound))
-		return false;
-	    }
-	  else
-	    /* Exclude this single-valued case.  */
-	    if (!add_constraint (index, NE_EXPR, other_lower_bound))
-	      return false;
-	}
-      return true;
-    }
-}
-
-/* FIXME.  */
-
-bool
-path_state::add_constraint (path_var lhs, enum tree_code op, path_var rhs)
-{
-  equiv_class_id lhs_ec_id = get_or_add_equiv_class (lhs);
-  equiv_class_id rhs_ec_id = get_or_add_equiv_class (rhs);
-  return add_constraint (lhs_ec_id, op,rhs_ec_id);
-}
-
-/* FIXME.  */
-
-bool
-path_state::add_constraint (equiv_class_id lhs_ec_id,
-			    enum tree_code op,
-			    equiv_class_id rhs_ec_id)
-{
-  tristate t = eval_condition (lhs_ec_id, op, rhs_ec_id);
-
-  /* Discard constraints that are already known.  */
-  if (t.is_true ())
-    return true;
-
-  /* Reject unsatisfiable constraints.  */
-  if (t.is_false ())
-    return false;
-
-  gcc_assert (lhs_ec_id != rhs_ec_id);
-
-  /* For now, simply accumulate constraints, without attempting any further
-     optimization.  */
-  switch (op)
-    {
-    case EQ_EXPR:
-      {
-	/* Merge rhs_ec into lhs_ec.  */
-	equiv_class &lhs_ec_obj = lhs_ec_id.get_obj (*this);
-	const equiv_class &rhs_ec_obj = rhs_ec_id.get_obj (*this);
-
-	int i;
-	path_var *v;
-	FOR_EACH_VEC_ELT (rhs_ec_obj.m_vars, i, v)
-	  lhs_ec_obj.add (*v);
-
-	if (rhs_ec_obj.m_constant)
-	  {
-	    gcc_assert (lhs_ec_obj.m_constant == NULL);
-	    lhs_ec_obj.m_constant = rhs_ec_obj.m_constant;
-	  }
-
-	/* Merge any extra state.  */
-	extra_ec_state *lhs_extra_state = lhs_ec_obj.m_extra_state;
-	extra_ec_state *rhs_extra_state = rhs_ec_obj.m_extra_state;
-
-	/* How to merge two state objects?  */
-	gcc_assert (!(lhs_extra_state && rhs_extra_state));
-
-	if (lhs_extra_state == NULL && rhs_extra_state)
-	  lhs_ec_obj.m_extra_state = rhs_extra_state->clone ();
-
-	/* Drop rhs equivalence class, overwriting it with the
-	   final ec (which might be the same one).  */
-	equiv_class_id final_ec_id = m_equiv_classes.length () - 1;
-	equiv_class *old_ec = m_equiv_classes[rhs_ec_id.m_idx];
-	equiv_class *final_ec = m_equiv_classes.pop ();
-	if (final_ec != old_ec)
-	  m_equiv_classes[rhs_ec_id.m_idx] = final_ec;
-	delete old_ec;
-
-	/* Update the constraints.  */
-	constraint *c;
-	FOR_EACH_VEC_ELT (m_constraints, i, c)
-	  {
-	    /* Update references to the rhs_ec so that
-	       they refer to the lhs_ec.  */
-	    if (c->m_lhs == rhs_ec_id)
-	      c->m_lhs = lhs_ec_id;
-	    if (c->m_rhs == rhs_ec_id)
-	      c->m_rhs = lhs_ec_id;
-
-	    /* Renumber all constraints that refer to the final rhs_ec
-	       to the old rhs_ec, where the old final_ec now lives.  */
-	    if (c->m_lhs == final_ec_id)
-	      c->m_lhs = rhs_ec_id;
-	    if (c->m_rhs == final_ec_id)
-	      c->m_rhs = rhs_ec_id;
-	  }
-      }
-      break;
-    case GE_EXPR:
-      add_constraint_internal (rhs_ec_id, CONSTRAINT_LE, lhs_ec_id);
-      break;
-    case LE_EXPR:
-      add_constraint_internal (lhs_ec_id, CONSTRAINT_LE, rhs_ec_id);
-      break;
-    case NE_EXPR:
-      add_constraint_internal (lhs_ec_id, CONSTRAINT_NE, rhs_ec_id);
-      break;
-    case GT_EXPR:
-      add_constraint_internal (rhs_ec_id, CONSTRAINT_LT, lhs_ec_id);
-      break;
-    case LT_EXPR:
-      add_constraint_internal (lhs_ec_id, CONSTRAINT_LT, rhs_ec_id);
-      break;
-    default:
-      /* do nothing.  */
-      break;
-    }
-  return true;
-}
-
-/* FIXME.  */
-
-void
-bound::ensure_closed (bool is_upper)
-{
-  if (!m_closed)
-    {
-      /* Offset by 1 in the appropriate direction.
-	 For example, convert 3 < x into 4 <= x,
-	 and convert x < 5 into x <= 4.  */
-      gcc_assert (CONSTANT_CLASS_P (m_constant));
-      m_constant = fold_build2 (is_upper ? MINUS_EXPR : PLUS_EXPR,
-				TREE_TYPE (m_constant),
-				m_constant, integer_one_node);
-      gcc_assert (CONSTANT_CLASS_P (m_constant));
-      m_closed = true;
-    }
-}
-
-/* FIXME.  */
-
-std::string
-range::to_str () const
-{
-  std::string result = expr_to_string (m_lower_bound.m_constant);
-  result += " <";
-  if (m_lower_bound.m_closed)
-    result += "=";
-  result += " x <";
-  if (m_upper_bound.m_closed)
-    result += "=";
-  result += " ";
-  result += expr_to_string (m_upper_bound.m_constant);
-  return result;  // TODO
-}
-
-/* FIXME.  */
-
-bool
-range::constrained_to_single_element (tree *out)
-{
-  if (!INTEGRAL_TYPE_P (TREE_TYPE (m_lower_bound.m_constant)))
-    return false;
-  if (!INTEGRAL_TYPE_P (TREE_TYPE (m_upper_bound.m_constant)))
-    return false;
-  // FIXME: also check the endpoint
-
-  /* Convert any open bounds to closed bounds.  */
-  m_lower_bound.ensure_closed (false);
-  m_upper_bound.ensure_closed (true);
-
-  // Are they equal?
-  tree comparison
-    = fold_build2 (EQ_EXPR, boolean_type_node,
-		   m_lower_bound.m_constant,
-		   m_upper_bound.m_constant);
-  if (comparison == boolean_true_node)
-    {
-      *out = m_lower_bound.m_constant;
-      return true;
-    }
-  else
-    return false;
-}
-
-
-/* FIXME.  */
-
-void
-path_state::add_constraint_internal (equiv_class_id lhs_id,
-				     enum constraint_op c_op,
-				     equiv_class_id rhs_id)
-{
-  /* Add the constraint.  */
-  m_constraints.safe_push (constraint (lhs_id, c_op, rhs_id));
-
-  if (c_op != CONSTRAINT_NE)
-    {
-      /* The following can potentially add EQ_EXPR facts, which could lead
-	 to ECs being merged, which would change the meaning of the EC IDs.
-	 Hence we need to do this via path vars.  */
-      path_var lhs = lhs_id.get_obj (*this).get_representative ();
-      path_var rhs = rhs_id.get_obj (*this).get_representative ();
-
-      /* We have LHS </<= RHS */
-
-      /* Handle transitivity of ordering by adding additional constraints
-	 based on what we already knew.
-
-	 So if we have already have:
-	   (a < b)
-	   (c < d)
-	 Then adding:
-	   (b < c)
-	 will also add:
-	   (a < c)
-	   (b < d)
-	 We need to recurse to ensure we also add:
-	   (a < d).
-	 We call the checked add_constraint to avoid adding constraints
-	 that are already present.  Doing so also ensures termination
-	 in the case of cycles.
-
-	 We also check for single-element ranges, adding EQ_EXPR facts
-	 where we discover them.  For example 3 < x < 5 implies
-	 that x == 4 (if x is an integer).  */
-      for (unsigned i = 0; i < m_constraints.length (); i++)
-	{
-	  const constraint *other = &m_constraints[i];
-	  if (other->is_ordering_p ())
-	    {
-	      /* Refresh the EC IDs, in case any mergers have happened.  */
-	      lhs_id = get_or_add_equiv_class (lhs);
-	      rhs_id = get_or_add_equiv_class (rhs);
-
-	      tree lhs_const = lhs_id.get_obj (*this).m_constant;
-	      tree rhs_const = rhs_id.get_obj (*this).m_constant;
-	      tree other_lhs_const
-		= other->m_lhs.get_obj (*this).m_constant;
-	      tree other_rhs_const
-		= other->m_rhs.get_obj (*this).m_constant;
-
-	      /* We have "LHS </<= RHS" and "other.lhs </<= other.rhs".  */
-
-	      /* If we have LHS </<= RHS and RHS </<= LHS, then we have a
-		 cycle.  */
-	      if (rhs_id == other->m_lhs
-		  && other->m_rhs == lhs_id)
-		{
-		  /* We must have equality for this to be possible.  */
-		  gcc_assert (c_op == CONSTRAINT_LE
-			      && other->m_op == CONSTRAINT_LE);
-		  add_constraint (lhs_id, EQ_EXPR, rhs_id);
-		  /* Adding an equality will merge the two ECs and potentially
-		     reorganize the constraints.  Stop iterating.  */
-		  return;
-		}
-	      /* Otherwise, check for transitivity.  */
-	      if (rhs_id == other->m_lhs)
-		{
-		  /* With RHS == other.lhs, we have:
-		     "LHS </<= (RHS, other.lhs) </<= other.rhs"
-		     and thus this implies "LHS </<= other.rhs".  */
-
-		  /* Do we have a tightly-constrained range?  */
-		  if (lhs_const
-		      && !rhs_const
-		      && INTEGRAL_TYPE_P (TREE_TYPE (rhs.m_tree))
-		      && other_rhs_const)
-		    {
-		      range r (bound (lhs_const, c_op == CONSTRAINT_LE),
-			       bound (other_rhs_const,
-				      other->m_op == CONSTRAINT_LE));
-		      tree constant;
-		      if (r.constrained_to_single_element (&constant))
-			{
-			  add_constraint
-			    (rhs_id, EQ_EXPR,
-			     get_or_add_equiv_class (path_var (constant, 0)));
-			  return;
-			}
-		    }
-		  /* Otherwise, add the constraint implied by transitivity.  */
-		  enum tree_code new_op
-		    = ((c_op == CONSTRAINT_LE && other->m_op == CONSTRAINT_LE)
-		       ? LE_EXPR : LT_EXPR);
-		  add_constraint (lhs_id, new_op, other->m_rhs);
-		}
-	      else if (other->m_rhs == lhs_id)
-		{
-		  /* With other.rhs == LHS, we have:
-		     "other.lhs </<= (other.rhs, LHS) </<= RHS"
-		     and thus this implies "other.lhs </<= RHS".  */
-
-		  /* Do we have a tightly-constrained range?  */
-		  if (other_lhs_const
-		      && !lhs_const
-		      && INTEGRAL_TYPE_P (TREE_TYPE (lhs.m_tree))
-		      && rhs_const)
-		    {
-		      range r (bound (other_lhs_const,
-				      other->m_op == CONSTRAINT_LE),
-			       bound (rhs_const,
-				      c_op == CONSTRAINT_LE));
-		      tree constant;
-		      if (r.constrained_to_single_element (&constant))
-			{
-			  add_constraint
-			    (lhs_id, EQ_EXPR,
-			     get_or_add_equiv_class (path_var (constant, 0)));
-			  return;
-			}
-		    }
-		  /* Otherwise, add the constraint implied by transitivity.  */
-		  enum tree_code new_op
-		    = ((c_op == CONSTRAINT_LE && other->m_op == CONSTRAINT_LE)
-		       ? LE_EXPR : LT_EXPR);
-		  add_constraint (other->m_lhs, new_op, rhs_id);
-		}
-	    }
-	}
-    }
-}
-
-/* FIXME.  */
-
-bool
-path_state::get_equiv_class (path_var pv, equiv_class_id *out) const
-{
-  gcc_assert (pv.m_tree);
-
-  if (CONSTANT_CLASS_P (pv.m_tree))
-    {
-      // FIXME: we want to ensure that constants that are equal are treated as equal here
-      int i;
-      equiv_class *ec;
-      FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-	if (pv.m_tree == ec->m_constant)
-	  {
-	    *out = equiv_class_id (i);
-	    return true;
-	  }
-    }
-  else
-    {
-      /* FIXME: probably should be a map, rather than these lookups.  */
-      int i;
-      equiv_class *ec;
-      FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
-	{
-	  int j;
-	  path_var *v;
-	  FOR_EACH_VEC_ELT (ec->m_vars, j, v)
-	    if (*v == pv)
-	      {
-		*out = equiv_class_id (i);
-		return true;
-	      }
-	}
-    }
-  return false;
-}
-
-/* FIXME.  */
-
-equiv_class_id
-path_state::get_or_add_equiv_class (path_var pv)
-{
-  gcc_assert (pv.m_tree);
-
-  equiv_class_id result (-1);
-  if (get_equiv_class (pv, &result))
-    return result;
-
-  /* Not found.  */
-  equiv_class *new_ec = new equiv_class ();
-  new_ec->add (pv);
-  m_equiv_classes.safe_push (new_ec);
-
-  equiv_class_id new_id (m_equiv_classes.length () - 1);
-
-  if (CONSTANT_CLASS_P (pv.m_tree))
-    {
-      /* If we have a new EC for a constant, add constraints comparing this
-	 to other constants we may have (so that we accumulate the transitive
-	 closure of all constraints on constants as the constants are
-	 added).  */
-      for (equiv_class_id other_id (0); other_id.m_idx < new_id.m_idx;
-	   other_id.m_idx++)
-	{
-	  const equiv_class &other_ec = other_id.get_obj (*this);
-	  if (other_ec.m_constant)
-	    {
-	      /* If we have two ECs, both with constants, the constants must be
-		 non-equal (or they would be in the same EC).
-		 Determine the direction of the inequality, and record that
-		 fact.  */
-	      tree lt
-		= fold_build2 (LT_EXPR, boolean_type_node,
-			       new_ec->m_constant, other_ec.m_constant);
-	      //gcc_assert (lt == boolean_true_node || lt == boolean_false_node);
-	      // not true for int vs float comparisons
-	      if (lt == boolean_true_node)
-		add_constraint_internal (new_id, CONSTRAINT_LT, other_id);
-	      else if (lt == boolean_false_node)
-		add_constraint_internal (other_id, CONSTRAINT_LT, new_id);
-	      /* Refresh new_id, in case ECs were merged.  PV should always
-		 be present by now, so this should never lead to a
-		 recursion.  */
-	      new_id = get_or_add_equiv_class (pv);
-	    }
-	}
-    }
-
-  return new_id;
-}
-
-/* FIXME.  */
-/* For implementing "__analyzer_eval".  */
-
-tristate
-path_state::dump_condition (tree lhs, enum tree_code op, tree rhs)
-{
-  equiv_class_id lhs_ec_id
-    = get_or_add_equiv_class (path_var (lhs, m_stack_depth));
-  equiv_class_id rhs_ec_id
-    = get_or_add_equiv_class (path_var (rhs, m_stack_depth));
-
-  return eval_condition (lhs_ec_id, op, rhs_ec_id);
-}
-
-/* FIXME.  */
-
-tristate
-path_state::eval_condition (equiv_class_id lhs_ec,
-			    enum tree_code op,
-			    equiv_class_id rhs_ec)
-{
-  if (lhs_ec == rhs_ec)
-    {
-      switch (op)
-	{
-	case EQ_EXPR:
-	case GE_EXPR:
-	case LE_EXPR:
-	  return tristate (tristate::TS_TRUE);
-
-	case NE_EXPR:
-	case GT_EXPR:
-	case LT_EXPR:
-	  return tristate (tristate::TS_FALSE);
-	default:
-	  break;
-	}
-    }
-
-  tree lhs_const = lhs_ec.get_obj (*this).get_any_constant ();
-  tree rhs_const = rhs_ec.get_obj (*this).get_any_constant ();
-  if (lhs_const && rhs_const)
-    {
-      tree comparison
-	= fold_build2 (op, boolean_type_node, lhs_const, rhs_const);
-      if (comparison == boolean_true_node)
-	return tristate (tristate::TS_TRUE);
-      if (comparison == boolean_false_node)
-	return tristate (tristate::TS_FALSE);
-    }
-
-  enum tree_code swapped_op = swap_tree_comparison (op);
-
-  int i;
-  constraint *c;
-  FOR_EACH_VEC_ELT (m_constraints, i, c)
-    {
-      if (c->m_lhs == lhs_ec
-	  && c->m_rhs == rhs_ec)
-	{
-	  tristate result_for_constraint
-	    = eval_constraint_op_for_op (c->m_op, op);
-	  if (result_for_constraint.is_known ())
-	    return result_for_constraint;
-	}
-      /* Swapped operands.  */
-      if (c->m_lhs == rhs_ec
-	  && c->m_rhs == lhs_ec)
-	{
-	  tristate result_for_constraint
-	    = eval_constraint_op_for_op (c->m_op, swapped_op);
-	  if (result_for_constraint.is_known ())
-	    return result_for_constraint;
-	}
-    }
-
-  return tristate (tristate::TS_UNKNOWN);
-}
-
-/* Mark EC_ID as having escaped; walk other ECs that haven't yet been marked
-   as escaped, and determine if they've now escaped, if so, mark them,
-   recursively.
-   e.g. if we know "ptr" escapes, then "ptr->foo" escapes.  */
-
-void
-path_state::recursively_mark_as_escaping (equiv_class_id ec_id)
-{
-  equiv_class &ec = ec_id.get_obj (*this);
-  if (ec.m_escaped)
-    {
-      /* Nothing to do.  */
-      return;
-    }
-
-  ec.m_escaped = true;
-
-  /* Look for other ECs that haven't yet been marked as escaped.  */
-  int other_ec_idx;
-  equiv_class *other_ec;
-  FOR_EACH_VEC_ELT (m_equiv_classes, other_ec_idx, other_ec)
-    {
-      if (other_ec->m_escaped)
-	continue;
-
-      int var_idx;
-      path_var *v;
-      FOR_EACH_VEC_ELT (other_ec->m_vars, var_idx, v)
-	{
-	  gcc_assert (!CONSTANT_CLASS_P (v->m_tree));
-	  if (calc_escaped_p (*v))
-	    {
-	      recursively_mark_as_escaping (other_ec_idx);
-	      break;
-	    }
-	}
-    }
-
-  // TODO: selftests
-  // TODO: selftest involving a cycle
-}
-
-/* Determine if PV (or something that points to it) has been marked as
-   having escaped.  */
-
-bool
-path_state::calc_escaped_p (path_var pv)
-{
-  switch (TREE_CODE (pv.m_tree))
-    {
-    case COMPONENT_REF:
-    case MEM_REF:
-      return calc_escaped_p (path_var (TREE_OPERAND (pv.m_tree, 0),
-				       pv.m_stack_depth));
-    case SSA_NAME:
-      {
-	equiv_class_id ec_id (-1);
-	if (get_equiv_class (pv, &ec_id))
-	  return ec_id.get_obj (*this).m_escaped;
-      }
-      break;
-    default:
-      return false;
-    }
-
-  return false;
-}
-
-// FIXME:
-void
-debug (const path_state &ps)
-{
-  fprintf (stderr, "%s\n", ps.to_str ().c_str ());
-}
-
-void
-inchash::add_path_var (path_var pv, hash &hstate)
-{
-  inchash::add_expr (pv.m_tree, hstate);
-  hstate.add_int (pv.m_stack_depth);
-}
-
-#if CHECKING_P
-
-namespace selftest {
-
-/* Attempt to add the constraint (LHS OP RHS) to PS.
-   Verify that PS remains satisfiable.  */
-
-#define ADD_SAT_CONSTRAINT(PS, LHS, OP, RHS)				\
-  SELFTEST_BEGIN_STMT							\
-    bool sat = (PS).add_constraint (LHS, OP, RHS);			\
-    ASSERT_TRUE (sat);							\
-  SELFTEST_END_STMT
-
-/* Attempt to add the constraint (LHS OP RHS) to PS.
-   Verify that the result is not satisfiable.  */
-
-#define ADD_UNSAT_CONSTRAINT(PS, LHS, OP, RHS)				\
-  SELFTEST_BEGIN_STMT							\
-    bool sat = (PS).add_constraint (LHS, OP, RHS);			\
-    ASSERT_FALSE (sat);							\
-  SELFTEST_END_STMT
-
-static void
-assert_condition (const location &loc,
-		  path_state &ps,
-		  path_var lhs, tree_code op, path_var rhs,
-		  tristate expected)
-{
-  equiv_class_id lhs_ec = ps.get_or_add_equiv_class (lhs);
-  equiv_class_id rhs_ec = ps.get_or_add_equiv_class (rhs);
-  tristate actual = ps.eval_condition (lhs_ec, op, rhs_ec);
-  ASSERT_EQ_AT (loc, actual, expected);
-}
-
-#define ASSERT_CONDITION_TRUE(PATH_STATE, LHS, OP, RHS) \
-  SELFTEST_BEGIN_STMT							\
-  assert_condition (SELFTEST_LOCATION, PATH_STATE, LHS, OP, RHS,	\
-		    tristate (tristate::TS_TRUE));		\
-  SELFTEST_END_STMT
-
-#define ASSERT_CONDITION_FALSE(PATH_STATE, LHS, OP, RHS) \
-  SELFTEST_BEGIN_STMT							\
-  assert_condition (SELFTEST_LOCATION, PATH_STATE, LHS, OP, RHS,	\
-		    tristate (tristate::TS_FALSE));		\
-  SELFTEST_END_STMT
-
-#define ASSERT_CONDITION_UNKNOWN(PATH_STATE, LHS, OP, RHS) \
-  SELFTEST_BEGIN_STMT							\
-  assert_condition (SELFTEST_LOCATION, PATH_STATE, LHS, OP, RHS,	\
-		    tristate (tristate::TS_UNKNOWN));		\
-  SELFTEST_END_STMT
-
-/* FIXME.  */
-
-static void
-test_constraint_conditions ()
-{
-  path_var int_42 (build_int_cst (integer_type_node, 42), 0);
-  path_var int_0 (build_int_cst (integer_type_node, 0), 0);
-
-  path_var x (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("x"),
-			  integer_type_node), 0);
-  path_var y (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("y"),
-			  integer_type_node), 0);
-  path_var z (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("z"),
-			  integer_type_node), 0);
-
-  /* Self-comparisons.  */
-  {
-    path_state ps;
-    ASSERT_CONDITION_TRUE (ps, x, EQ_EXPR, x);
-    ASSERT_CONDITION_TRUE (ps, x, LE_EXPR, x);
-    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, x);
-    ASSERT_CONDITION_FALSE (ps, x, NE_EXPR, x);
-    ASSERT_CONDITION_FALSE (ps, x, LT_EXPR, x);
-    ASSERT_CONDITION_FALSE (ps, x, GT_EXPR, x);
-  }
-
-  /* x == y.  */
-  {
-    path_state ps;
-    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, y);
-
-    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, y);
-
-    ASSERT_CONDITION_TRUE (ps, x, EQ_EXPR, y);
-    ASSERT_CONDITION_TRUE (ps, x, LE_EXPR, y);
-    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, NE_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, LT_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, GT_EXPR, y);
-
-    /* Swapped operands.  */
-    ASSERT_CONDITION_TRUE (ps, y, EQ_EXPR, x);
-    ASSERT_CONDITION_TRUE (ps, y, LE_EXPR, x);
-    ASSERT_CONDITION_TRUE (ps, y, GE_EXPR, x);
-    ASSERT_CONDITION_FALSE (ps, y, NE_EXPR, x);
-    ASSERT_CONDITION_FALSE (ps, y, LT_EXPR, x);
-    ASSERT_CONDITION_FALSE (ps, y, GT_EXPR, x);
-
-    /* Comparison with other var.  */
-    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN (ps, x, LE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN (ps, x, NE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN (ps, x, LT_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN (ps, x, GT_EXPR, z);
-  }
-
-  /* x == y, then y == z  */
-  {
-    path_state ps;
-    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, y);
-
-    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, y);
-    ADD_SAT_CONSTRAINT (ps, y, EQ_EXPR, z);
-
-    ASSERT_CONDITION_TRUE (ps, x, EQ_EXPR, z);
-    ASSERT_CONDITION_TRUE (ps, x, LE_EXPR, z);
-    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, z);
-    ASSERT_CONDITION_FALSE (ps, x, NE_EXPR, z);
-    ASSERT_CONDITION_FALSE (ps, x, LT_EXPR, z);
-    ASSERT_CONDITION_FALSE (ps, x, GT_EXPR, z);
-  }
-
-  /* x != y.  */
-  {
-    path_state ps;
-
-    ADD_SAT_CONSTRAINT (ps, x, NE_EXPR, y);
-
-    ASSERT_CONDITION_TRUE (ps, x, NE_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, EQ_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN (ps, x, LE_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN (ps, x, LT_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN (ps, x, GT_EXPR, y);
-
-    /* Swapped operands.  */
-    ASSERT_CONDITION_TRUE (ps, y, NE_EXPR, x);
-    ASSERT_CONDITION_FALSE (ps, y, EQ_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN (ps, y, LE_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN (ps, y, GE_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN (ps, y, LT_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN (ps, y, GT_EXPR, x);
-
-    /* Comparison with other var.  */
-    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN (ps, x, LE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN (ps, x, NE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN (ps, x, LT_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN (ps, x, GT_EXPR, z);
-  }
-
-  /* x < y.  */
-  {
-    path_state ps;
-
-    ADD_SAT_CONSTRAINT (ps, x, LT_EXPR, y);
-
-    ASSERT_CONDITION_TRUE (ps, x, LT_EXPR, y);
-    ASSERT_CONDITION_TRUE (ps, x, LE_EXPR, y);
-    ASSERT_CONDITION_TRUE (ps, x, NE_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, EQ_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, GT_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, GE_EXPR, y);
-
-    /* Swapped operands.  */
-    ASSERT_CONDITION_FALSE (ps, y, LT_EXPR, x);
-    ASSERT_CONDITION_FALSE (ps, y, LE_EXPR, x);
-    ASSERT_CONDITION_TRUE (ps, y, NE_EXPR, x);
-    ASSERT_CONDITION_FALSE (ps, y, EQ_EXPR, x);
-    ASSERT_CONDITION_TRUE (ps, y, GT_EXPR, x);
-    ASSERT_CONDITION_TRUE (ps, y, GE_EXPR, x);
-  }
-
-  /* x <= y.  */
-  {
-    path_state ps;
-
-    ADD_SAT_CONSTRAINT (ps, x, LE_EXPR, y);
-
-    ASSERT_CONDITION_UNKNOWN (ps, x, LT_EXPR, y);
-    ASSERT_CONDITION_TRUE (ps, x, LE_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN (ps, x, NE_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, GT_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, y);
-
-    /* Swapped operands.  */
-    ASSERT_CONDITION_FALSE (ps, y, LT_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN (ps, y, LE_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN (ps, y, NE_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN (ps, y, EQ_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN (ps, y, GT_EXPR, x);
-    ASSERT_CONDITION_TRUE (ps, y, GE_EXPR, x);
-  }
-
-  /* x > y.  */
-  {
-    path_state ps;
-
-    ADD_SAT_CONSTRAINT (ps, x, GT_EXPR, y);
-
-    ASSERT_CONDITION_TRUE (ps, x, GT_EXPR, y);
-    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, y);
-    ASSERT_CONDITION_TRUE (ps, x, NE_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, EQ_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, LT_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, LE_EXPR, y);
-
-    /* Swapped operands.  */
-    ASSERT_CONDITION_FALSE (ps, y, GT_EXPR, x);
-    ASSERT_CONDITION_FALSE (ps, y, GE_EXPR, x);
-    ASSERT_CONDITION_TRUE (ps, y, NE_EXPR, x);
-    ASSERT_CONDITION_FALSE (ps, y, EQ_EXPR, x);
-    ASSERT_CONDITION_TRUE (ps, y, LT_EXPR, x);
-    ASSERT_CONDITION_TRUE (ps, y, LE_EXPR, x);
-  }
-
-  /* x >= y.  */
-  {
-    path_state ps;
-
-    ADD_SAT_CONSTRAINT (ps, x, GE_EXPR, y);
-
-    ASSERT_CONDITION_UNKNOWN (ps, x, GT_EXPR, y);
-    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN (ps, x, NE_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, LT_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN (ps, x, LE_EXPR, y);
-
-    /* Swapped operands.  */
-    ASSERT_CONDITION_FALSE (ps, y, GT_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN (ps, y, GE_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN (ps, y, NE_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN (ps, y, EQ_EXPR, x);
-    ASSERT_CONDITION_UNKNOWN (ps, y, LT_EXPR, x);
-    ASSERT_CONDITION_TRUE (ps, y, LE_EXPR, x);
-  }
-
-  // TODO: implied orderings
-
-  /* Constants.  */
-  {
-    path_state ps;
-    ASSERT_CONDITION_FALSE (ps, int_0, EQ_EXPR, int_42);
-    ASSERT_CONDITION_TRUE (ps, int_0, NE_EXPR, int_42);
-    ASSERT_CONDITION_TRUE (ps, int_0, LT_EXPR, int_42);
-    ASSERT_CONDITION_TRUE (ps, int_0, LE_EXPR, int_42);
-    ASSERT_CONDITION_FALSE (ps, int_0, GT_EXPR, int_42);
-    ASSERT_CONDITION_FALSE (ps, int_0, GE_EXPR, int_42);
-  }
-
-  /* x == 0, y == 42.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, int_0);
-    ADD_SAT_CONSTRAINT (ps, y, EQ_EXPR, int_42);
-
-    ASSERT_CONDITION_TRUE (ps, x, NE_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, EQ_EXPR, y);
-    ASSERT_CONDITION_TRUE (ps, x, LE_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, GE_EXPR, y);
-    ASSERT_CONDITION_TRUE (ps, x, LT_EXPR, y);
-    ASSERT_CONDITION_FALSE (ps, x, GT_EXPR, y);
-  }
-
-  /* Unsatisfiable combinations.  */
-
-  /* x == y && x != y.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, y);
-    ADD_UNSAT_CONSTRAINT (ps, x, NE_EXPR, y);
-  }
-
-  /* x == 0 then x == 42.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, int_0);
-    ADD_UNSAT_CONSTRAINT (ps, x, EQ_EXPR, int_42);
-  }
-
-  /* x == 0 then x != 0.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, int_0);
-    ADD_UNSAT_CONSTRAINT (ps, x, NE_EXPR, int_0);
-  }
-
-  /* x == 0 then x > 0.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, int_0);
-    ADD_UNSAT_CONSTRAINT (ps, x, GT_EXPR, int_0);
-  }
-
-  /* x != y && x == y.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, x, NE_EXPR, y);
-    ADD_UNSAT_CONSTRAINT (ps, x, EQ_EXPR, y);
-  }
-
-  /* x <= y && x > y.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, x, LE_EXPR, y);
-    ADD_UNSAT_CONSTRAINT (ps, x, GT_EXPR, y);
-  }
-
-  // etc
-}
-
-/* FIXME.  */
-
-static void
-test_transitivity ()
-{
-  path_var int_42 (build_int_cst (integer_type_node, 42), 0);
-  path_var int_0 (build_int_cst (integer_type_node, 0), 0);
-
-  path_var a (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("a"),
-			  integer_type_node), 0);
-  path_var b (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("b"),
-			  integer_type_node), 0);
-  path_var c (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("c"),
-			  integer_type_node), 0);
-  path_var d (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("d"),
-			  integer_type_node), 0);
-
-  /* a == b, then c == d, then c == b.  */
-  {
-    path_state ps;
-    ASSERT_CONDITION_UNKNOWN (ps, a, EQ_EXPR, b);
-    ASSERT_CONDITION_UNKNOWN (ps, b, EQ_EXPR, c);
-    ASSERT_CONDITION_UNKNOWN (ps, c, EQ_EXPR, d);
-    ASSERT_CONDITION_UNKNOWN (ps, a, EQ_EXPR, d);
-
-    ADD_SAT_CONSTRAINT (ps, a, EQ_EXPR, b);
-    ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, b);
-
-    ADD_SAT_CONSTRAINT (ps, c, EQ_EXPR, d);
-    ASSERT_CONDITION_TRUE (ps, c, EQ_EXPR, d);
-    ASSERT_CONDITION_UNKNOWN (ps, a, EQ_EXPR, d);
-
-    ADD_SAT_CONSTRAINT (ps, c, EQ_EXPR, b);
-    ASSERT_CONDITION_TRUE (ps, c, EQ_EXPR, b);
-    ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, d);
-  }
-
-  /* Transitivity: "a < b", "b < c" should imply "a < c".  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, LT_EXPR, b);
-    ADD_SAT_CONSTRAINT (ps, b, LT_EXPR, c);
-
-    ASSERT_CONDITION_TRUE (ps, a, LT_EXPR, c);
-    ASSERT_CONDITION_FALSE (ps, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "a <= b", "b < c" should imply "a < c".  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, b);
-    ADD_SAT_CONSTRAINT (ps, b, LT_EXPR, c);
-
-    ASSERT_CONDITION_TRUE (ps, a, LT_EXPR, c);
-    ASSERT_CONDITION_FALSE (ps, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "a <= b", "b <= c" should imply "a <= c".  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, b);
-    ADD_SAT_CONSTRAINT (ps, b, LE_EXPR, c);
-
-    ASSERT_CONDITION_TRUE (ps, a, LE_EXPR, c);
-    ASSERT_CONDITION_UNKNOWN (ps, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "a > b", "b > c" should imply "a > c".  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, b);
-    ADD_SAT_CONSTRAINT (ps, b, GT_EXPR, c);
-
-    ASSERT_CONDITION_TRUE (ps, a, GT_EXPR, c);
-    ASSERT_CONDITION_FALSE (ps, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "a >= b", "b > c" should imply " a > c".  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, b);
-    ADD_SAT_CONSTRAINT (ps, b, GT_EXPR, c);
-
-    ASSERT_CONDITION_TRUE (ps, a, GT_EXPR, c);
-    ASSERT_CONDITION_FALSE (ps, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "a >= b", "b >= c" should imply "a >= c".  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, b);
-    ADD_SAT_CONSTRAINT (ps, b, GE_EXPR, c);
-
-    ASSERT_CONDITION_TRUE (ps, a, GE_EXPR, c);
-    ASSERT_CONDITION_UNKNOWN (ps, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "(a < b)", "(c < d)", "(b < c)" should
-     imply the easy cases:
-       (a < c)
-       (b < d)
-     but also that:
-       (a < d).  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, LT_EXPR, b);
-    ADD_SAT_CONSTRAINT (ps, c, LT_EXPR, d);
-    ADD_SAT_CONSTRAINT (ps, b, LT_EXPR, c);
-
-    ASSERT_CONDITION_TRUE (ps, a, LT_EXPR, c);
-    ASSERT_CONDITION_TRUE (ps, b, LT_EXPR, d);
-    ASSERT_CONDITION_TRUE (ps, a, LT_EXPR, d);
-  }
-
-  /* Transitivity: "a >= b", "b >= a" should imply that a == b.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, b);
-    ADD_SAT_CONSTRAINT (ps, b, GE_EXPR, a);
-
-    // TODO:
-    ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, b);
-  }
-
-  /* Transitivity: "a >= b", "b > a" should be impossible.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, b);
-    ADD_UNSAT_CONSTRAINT (ps, b, GT_EXPR, a);
-  }
-
-  /* Transitivity: "a >= b", "b >= c", "c >= a" should imply
-     that a == b == c.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, b);
-    ADD_SAT_CONSTRAINT (ps, b, GE_EXPR, c);
-    ADD_SAT_CONSTRAINT (ps, c, GE_EXPR, a);
-
-    ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, c);
-  }
-
-  /* Transitivity: "a > b", "b > c", "c > a"
-     should be impossible.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, b);
-    ADD_SAT_CONSTRAINT (ps, b, GT_EXPR, c);
-    ADD_UNSAT_CONSTRAINT (ps, c, GT_EXPR, a);
-  }
-
-}
-
-/* FIXME.  */
-
-static void
-test_constant_comparisons ()
-{
-  path_var int_3 (build_int_cst (integer_type_node, 3), 0);
-  path_var int_4 (build_int_cst (integer_type_node, 4), 0);
-  path_var int_5 (build_int_cst (integer_type_node, 5), 0);
-
-  path_var int_1023 (build_int_cst (integer_type_node, 1023), 0);
-  path_var int_1024 (build_int_cst (integer_type_node, 1024), 0);
-
-  path_var a (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("a"),
-			  integer_type_node), 0);
-  path_var b (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("b"),
-			  integer_type_node), 0);
-
-  /* Given a >= 1024, then a <= 1023 should be impossible.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, int_1024);
-    ADD_UNSAT_CONSTRAINT (ps, a, LE_EXPR, int_1023);
-  }
-
-  /* a > 4.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, int_4);
-    ASSERT_CONDITION_TRUE (ps, a, GT_EXPR, int_4);
-    ASSERT_CONDITION_TRUE (ps, a, NE_EXPR, int_3);
-    ASSERT_CONDITION_UNKNOWN (ps, a, NE_EXPR, int_5);
-  }
-
-  /* a <= 4.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, int_4);
-    ASSERT_CONDITION_FALSE (ps, a, GT_EXPR, int_4);
-    ASSERT_CONDITION_FALSE (ps, a, GT_EXPR, int_5);
-    ASSERT_CONDITION_UNKNOWN (ps, a, NE_EXPR, int_3);
-  }
-
-  /* If "a > b" and "a == 3", then "b == 4" ought to be unsatisfiable.  */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, b);
-    ADD_SAT_CONSTRAINT (ps, a, EQ_EXPR, int_3);
-    ADD_UNSAT_CONSTRAINT (ps, b, EQ_EXPR, int_4);
-  }
-
-  /* Various tests of int ranges where there is only one possible candidate.  */
-  {
-    /* If "a <= 4" && "a > 3", then "a == 4",
-       assuming a is of integral type.  */
-    {
-      path_state ps;
-      ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, int_4);
-      ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, int_3);
-      ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
-    }
-
-    /* If "a > 3" && "a <= 4", then "a == 4",
-       assuming a is of integral type.  */
-    {
-      path_state ps;
-      ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, int_3);
-      ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, int_4);
-      ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
-    }
-    /* If "a > 3" && "a < 5", then "a == 4",
-       assuming a is of integral type.  */
-    {
-      path_state ps;
-      ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, int_3);
-      ADD_SAT_CONSTRAINT (ps, a, LT_EXPR, int_5);
-      ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
-    }
-    /* If "a >= 4" && "a < 5", then "a == 4",
-       assuming a is of integral type.  */
-    {
-      path_state ps;
-      ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, int_4);
-      ADD_SAT_CONSTRAINT (ps, a, LT_EXPR, int_5);
-      ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
-    }
-    /* If "a >= 4" && "a <= 4", then "a == 4".  */
-    {
-      path_state ps;
-      ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, int_4);
-      ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, int_4);
-      ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
-    }
-  }
-
-#if 0
-  /* As above, but for float.   */
-  {
-    path_state ps;
-    ADD_SAT_CONSTRAINT (ps, f, GT_EXPR, float_3);
-    ADD_SAT_CONSTRAINT (ps, f, LE_EXPR, float_4);
-    ASSERT_CONDITION_UNKNOWN (ps, f, EQ_EXPR, int_4);
-  }
-#endif
-}
-
-/* FIXME.  */
-
-static void
-test_constraint_impl ()
-{
-  path_var int_42 (build_int_cst (integer_type_node, 42), 0);
-  path_var int_0 (build_int_cst (integer_type_node, 0), 0);
-
-  path_var x (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("x"),
-			  integer_type_node), 0);
-  path_var y (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("y"),
-			  integer_type_node), 0);
-  path_var z (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("z"),
-			  integer_type_node), 0);
-
-  /* x == y.  */
-  {
-    path_state ps;
-
-    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, y);
-
-    /* Assert various things about the insides of ps.  */
-    ASSERT_EQ (ps.m_constraints.length (), 0);
-    ASSERT_EQ (ps.m_equiv_classes.length (), 1);
-  }
-
-  /* y <= z; x == y.  */
-  {
-    path_state ps;
-    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, z);
-
-    ADD_SAT_CONSTRAINT (ps, y, GE_EXPR, z);
-    ASSERT_CONDITION_TRUE (ps, y, GE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, z);
-
-    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, y);
-
-    /* Assert various things about the insides of ps.  */
-    ASSERT_EQ (ps.m_constraints.length (), 1);
-    ASSERT_EQ (ps.m_equiv_classes.length (), 2);
-
-    /* Ensure that we merged the constraints.  */
-    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, z);
-  }
-
-  /* y <= z; y == x.  */
-  {
-    path_state ps;
-    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, y);
-    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, z);
-
-    ADD_SAT_CONSTRAINT (ps, y, GE_EXPR, z);
-    ASSERT_CONDITION_TRUE (ps, y, GE_EXPR, z);
-    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, z);
-
-    ADD_SAT_CONSTRAINT (ps, y, EQ_EXPR, x);
-
-    /* Assert various things about the insides of ps.  */
-    ASSERT_EQ (ps.m_constraints.length (), 1);
-    ASSERT_EQ (ps.m_equiv_classes.length (), 2);
-
-    /* Ensure that we merged the constraints.  */
-    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, z);
-  }
-
-  /* x == 0, then x != 42.  */
-  {
-    path_state ps;
-
-    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, int_0);
-    ADD_SAT_CONSTRAINT (ps, x, NE_EXPR, int_42);
-
-    /* Assert various things about the insides of ps.  */
-    ASSERT_EQ (ps.m_constraints.length (), 1);
-    ASSERT_EQ (ps.m_equiv_classes.length (), 2);
-    ASSERT_EQ (ps.m_constraints[0].m_lhs, ps.get_or_add_equiv_class (int_0));
-    ASSERT_EQ (ps.m_constraints[0].m_rhs, ps.get_or_add_equiv_class (int_42));
-    ASSERT_EQ (ps.m_constraints[0].m_op, CONSTRAINT_LT);
-  }
-
-  // TODO: selftest for merging ecs "in the middle"
-  // where a non-final one gets overwritten
-
-  // TODO: selftest where there are pre-existing constraints
-}
-
-/* FIXME.  */
-
-static void
-test_assignment ()
-{
-  // TODO
-}
-
-/* FIXME.  */
-
-static void
-test_stack_frames ()
-{
-  path_var int_42 (build_int_cst (integer_type_node, 42), 0);
-  path_var int_0 (build_int_cst (integer_type_node, 0), 0);
-
-  /* "a" in the parent frame.  */
-  path_var a (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("a"),
-			  integer_type_node), 0);
-  /* "x" in a child frame.  */
-  path_var x (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("x"),
-			  integer_type_node), 1);
-
-  {
-    path_state ps;
-    ps.m_stack_depth = 1;
-
-    /* x == a.  */
-    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, a);
-    ASSERT_CONDITION_TRUE (ps, x, EQ_EXPR, a);
-
-    /* Pop the stack.  */
-    ps.pop_stack_frame ();
-    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, a);
-  }
-}
-
-/* Check that operator== and hashing works as expected for the
-   various types.  */
-
-static void
-test_equality ()
-{
-  path_var int_42 (build_int_cst (integer_type_node, 42), 0);
-  path_var int_0 (build_int_cst (integer_type_node, 0), 0);
-
-  path_var x (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("x"),
-			  integer_type_node), 0);
-  path_var y (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("y"),
-			  integer_type_node), 0);
-  path_var z (build_decl (UNKNOWN_LOCATION, PARM_DECL,
-			  get_identifier ("z"),
-			  integer_type_node), 0);
-
-  {
-    path_state ps0;
-    path_state ps1;
-
-    ASSERT_EQ (ps0.hash (), ps1.hash ());
-    ASSERT_EQ (ps0, ps1);
-
-    ADD_SAT_CONSTRAINT (ps1, x, EQ_EXPR, y);
-    ASSERT_NE (ps0.hash (), ps1.hash ());
-    ASSERT_NE (ps0, ps1);
-
-    path_state ps2;
-    /* Make the same change to ps2.  */
-    ADD_SAT_CONSTRAINT (ps2, x, EQ_EXPR, y);
-    ASSERT_EQ (ps1.hash (), ps2.hash ());
-    ASSERT_EQ (ps1, ps2);
-  }
-}
-
-/* Run all of the selftests within this file.  */
-
-void
-analyzer_constraints_cc_tests ()
-{
-  test_constraint_conditions ();
-  test_transitivity ();
-  test_constant_comparisons ();
-  test_constraint_impl ();
-  test_assignment ();
-  test_stack_frames ();
-  test_equality ();
-}
-
-} // namespace selftest
-
-#endif /* CHECKING_P */
diff --git a/gcc/analyzer/constraints.h b/gcc/analyzer/constraints.h
deleted file mode 100644
index 40243d0..0000000
--- a/gcc/analyzer/constraints.h
+++ /dev/null
@@ -1,279 +0,0 @@
-/* Tracking equivalence classes and constraints at a point on an execution path.
-   Copyright (C) 2019 Free Software Foundation, Inc.
-   Contributed by David Malcolm <dmalcolm@redhat.com>.
-
-This file is part of GCC.
-
-GCC is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3, or (at your option)
-any later version.
-
-GCC is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING3.  If not see
-<http://www.gnu.org/licenses/>.  */
-
-#ifndef GCC_ANALYZER_CONSTRAINTS_H
-#define GCC_ANALYZER_CONSTRAINTS_H
-
-class superedge;
-class call_superedge;
-class return_superedge;
-
-/* One of the end-points of a range.  */
-
-struct bound
-{
-  bound () : m_constant (NULL_TREE), m_closed (false) {}
-  bound (tree constant, bool closed)
-  : m_constant (constant), m_closed (closed) {}
-
-  void ensure_closed (bool is_upper);
-
-  tree m_constant;
-  bool m_closed;
-};
-
-/* FIXME.  */
-
-struct range
-{
-  range () : m_lower_bound (), m_upper_bound () {}
-  range (const bound &lower, const bound &upper)
-  : m_lower_bound (lower), m_upper_bound (upper) {}
-
-  std::string to_str () const;
-
-  bool constrained_to_single_element (tree *out);
-
-  bound m_lower_bound;
-  bound m_upper_bound;
-};
-
-/* A tree, extended with stack frame information for locals, so that
-   we can distinguish between different values of locals within a potentially
-   recursive callstack.  */
-
-class path_var
-{
-public:
-  path_var (tree t, int stack_depth)
-  : m_tree (t), m_stack_depth (stack_depth)
-  {
-    // TODO: ignore stack depth for globals and constants
-  }
-
-  bool operator== (const path_var &other) const
-  {
-    return (m_tree == other.m_tree
-	    && m_stack_depth == other.m_stack_depth);
-  }
-
-  std::string to_str () const;
-
-  tree m_tree;
-  int m_stack_depth; // or -1 for globals?
-};
-
-/* Optional per-engine extra state (to avoid making path_state a
-   template).  */
-
-class extra_ec_state
-{
- public:
-  virtual ~extra_ec_state () {}
-  virtual extra_ec_state *clone () const = 0;
-  virtual std::string to_str () const = 0;
-  virtual hashval_t hash () const = 0;
-  virtual bool operator== (const extra_ec_state &other) const = 0;
-};
-
-/* FIXME.  */
-
-class equiv_class
-{
-public:
-  equiv_class ();
-  equiv_class (const equiv_class &other);
-  ~equiv_class ();
-
-  std::string to_str () const;
-  hashval_t hash () const;
-  bool operator== (const equiv_class &other);
-
-  void add (path_var v);
-  bool del (path_var v);
-
-  tree get_any_constant () const { return m_constant; }
-
-  int get_min_stack_depth () const;
-
-  void purge_stack_frame (int callee_stack_depth);
-
-  path_var get_representative () const;
-
-  /* An equivalence class can contain at most one constant.  */
-  tree m_constant;
-
-  // FIXME: this is a bad representation, probably should be a set:
-  auto_vec<path_var> m_vars;
-
-  /* Has this value escaped from this function?  */
-  bool m_escaped;
-
-  /* Optional per-engine extra state (to avoid making path_state a
-     template).  */
-  extra_ec_state *m_extra_state;
-};
-
-enum constraint_op
-{
-  CONSTRAINT_NE,
-  CONSTRAINT_LT,
-  CONSTRAINT_LE
-};
-
-const char *constraint_op_code (enum constraint_op c_op);
-
-class path_state;
-
-class equiv_class_id
-{
-public:
-  equiv_class_id (unsigned idx) : m_idx (idx) {}
-  const equiv_class &get_obj (const path_state &ps) const;
-  equiv_class &get_obj (path_state &ps) const;
-
-  bool operator== (const equiv_class_id &other) const
-  {
-    return m_idx == other.m_idx;
-  }
-  bool operator!= (const equiv_class_id &other) const
-  {
-    return m_idx != other.m_idx;
-  }
-
-  std::string to_str () const
-  {
-    return std::string ("ec") + std::to_string (m_idx);
-  }
-
-  unsigned m_idx;
-};
-
-class constraint {
- public:
-  constraint (equiv_class_id lhs, enum constraint_op c_op, equiv_class_id rhs)
-    : m_lhs (lhs), m_op (c_op), m_rhs (rhs) {}
-
-  std::string to_str (const path_state &ps) const;
-  hashval_t hash () const;
-  bool operator== (const constraint &other) const;
-
-  /* Is this an ordering, rather than a "!=".  */
-  bool is_ordering_p () const
-  {
-    return m_op != CONSTRAINT_NE;
-  }
-
-  equiv_class_id m_lhs;
-  enum constraint_op m_op;
-  equiv_class_id m_rhs;
-};
-
-/* The state of a particular point at one point in the traversal (not including
-   state-machine information).  */
-
-class path_state
-{
-public:
-  path_state () : m_stack_depth (0) {}
-  path_state (const path_state &other);
-  ~path_state ();
-
-  path_state& operator= (const path_state &other);
-
-  hashval_t hash () const;
-  bool operator== (const path_state &other) const;
-  bool operator!= (const path_state &other) const
-  {
-    return !(*this == other);
-  }
-
-  std::string to_str () const;
-
-  const equiv_class &get_equiv_class_by_index (unsigned idx) const
-  {
-    return *m_equiv_classes[idx];
-  }
-  equiv_class &get_equiv_class_by_index (unsigned idx)
-  {
-    return *m_equiv_classes[idx];
-  }
-
-  equiv_class &get_equiv_class (path_var pv)
-  {
-    equiv_class_id ec_id = get_or_add_equiv_class (pv);
-    return ec_id.get_obj (*this);
-  }
-
-  path_var make_path_var (tree expr) const
-  {
-    return path_var (expr, m_stack_depth);
-  }
-
-  void on_assignment (path_var lhs, path_var rhs);
-
-  bool apply_constraints_for_edge (const superedge &edge,
-				   const gimple *last_stmt);
-
-  bool add_constraint (path_var lhs, enum tree_code op, path_var rhs);
-
-  bool add_constraint (equiv_class_id lhs_ec_id,
-		       enum tree_code op,
-		       equiv_class_id rhs_ec_id);
-
-  //private:
-  bool apply_constraints_for_call (const call_superedge& edge);
-
-  bool apply_constraints_for_return (const return_superedge& edge);
-
-  void pop_stack_frame ();
-
-  bool apply_constraints_for_gcond (const superedge &edge,
-				    const gcond *cond_stmt);
-  bool apply_constraints_for_gswitch (const superedge &edge,
-				      const gswitch *switch_stmt);
-  bool get_equiv_class (path_var pv, equiv_class_id *out) const;
-  equiv_class_id get_or_add_equiv_class (path_var vr);
-  tristate eval_condition (equiv_class_id lhs,
-			   enum tree_code op,
-			   equiv_class_id rhs);
-
-  /* For implementing "__analyzer_eval".  */
-  tristate dump_condition (tree lhs, enum tree_code op, tree rhs);
-
-  void recursively_mark_as_escaping (equiv_class_id ec_id);
-  bool calc_escaped_p (path_var pv);
-
-  auto_vec<equiv_class *> m_equiv_classes;
-  auto_vec<constraint> m_constraints;
-  int m_stack_depth;
-
- private:
-  void add_constraint_internal (equiv_class_id lhs_id,
-				enum constraint_op c_op,
-				equiv_class_id rhs_id);
-};
-
-namespace inchash
-{
-  extern void add_path_var (path_var pv, hash &hstate);
-} // namespace inchash
-
-#endif /* GCC_ANALYZER_CONSTRAINTS_H */
diff --git a/gcc/analyzer/digraph.cc b/gcc/analyzer/digraph.cc
index d03fd6b..f7c1ce1 100644
--- a/gcc/analyzer/digraph.cc
+++ b/gcc/analyzer/digraph.cc
@@ -49,6 +49,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "graphviz.h"
 #include "cgraph.h"
 #include "digraph.h"
+#include "tree-diagnostic.h"
 
 void
 digraph::dump_dot_to_pp (pretty_printer *pp, cluster *root_cluster) const
@@ -106,6 +107,7 @@ void
 digraph::dump_dot_to_file (FILE *fp, cluster *root_cluster) const
 {
   pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
   pp.buffer->stream = fp;
   dump_dot_to_pp (&pp, root_cluster);
   pp_flush (&pp);
diff --git a/gcc/analyzer/digraph.h b/gcc/analyzer/digraph.h
index 54275d3..6f923ee 100644
--- a/gcc/analyzer/digraph.h
+++ b/gcc/analyzer/digraph.h
@@ -94,4 +94,77 @@ class cluster
   virtual void dump_dot (graphviz_out *gv) const = 0;
 };
 
+#if 0
+////////////////////////////////////////////////////////////////////////////
+
+template <typename GraphTraits>
+class base_node
+{
+ public:
+  typedef typename GraphTraits::edge_t edge_t;
+
+  void dump_dot (graphviz_out *gv) const;
+
+ protected:
+  auto_vec<edge_t *> m_succs;
+};
+
+template <typename GraphTraits>
+class base_edge
+{
+ public:
+  typedef typename GraphTraits::node_t node_t;
+
+  base_edge (node_t *src, node_t *dest)
+  : m_src (src), m_dest (dest) {}
+
+  void dump_dot (graphviz_out *gv) const;
+
+ protected:
+  node_t *m_src;
+  node_t *m_dest;
+};
+
+template <typename GraphTraits>
+class base_graph
+{
+ public:
+  typedef typename GraphTraits::node_t node_t;
+  typedef typename GraphTraits::edge_t edge_t;
+
+  void dump_dot (graphviz_out *gv) const;
+
+ protected:
+  auto_vec<node_t *> m_nodes;
+  auto_vec<edge_t *> m_edge;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+/* Curiously-recurring template pattern, for node/edge/graph.  */
+
+struct test_node;
+struct test_edge;
+struct test_graph;
+
+struct test_traits
+{
+  typedef test_node node_t;
+  typedef test_edge edge_t;
+  typedef test_graph graph_t;
+};
+
+struct test_node : public base_node<test_traits>
+{
+};
+
+struct test_edge : public base_edge<test_traits>
+{
+};
+
+struct test_graph : public base_graph<test_traits>
+{
+};
+#endif
+
 #endif /* GCC_ANALYZER_DIGRAPH_H */
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 5281c16..873c990 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -1075,7 +1075,7 @@ public:
   auto_vec <link> m_links; // FIXME: copy-ctor etc
 };
 
-#include "constraints.h" // FIXME
+#include "path-state.h" // FIXME
 
 class candidate_diagnostic
 {
@@ -1412,8 +1412,8 @@ public:
 
     // FIXME: do it for everything in the equivalence class
     // FIXME: stack depth:
-    equiv_class_id ec_id = m_ps->get_or_add_equiv_class (path_var (var, 0));
-    equiv_class &ec = ec_id.get_obj (*m_ps);
+    psn::equiv_class_id ec_id = m_ps->get_or_add_equiv_class (path_var (var, 0));
+    psn::equiv_class &ec = ec_id.get_obj (*m_ps);
 
     if (m_engine->get_logger ())
       m_engine->log ("considering equivalence class: %s",
@@ -1865,8 +1865,8 @@ engine::on_stmt (const supernode *node, const gimple *stmt,
 	       this path, and record the result, if known.  */
 	    tree rhs2 = gimple_assign_rhs2 (assign);
 	    // FIXME: stack depths:
-	    equiv_class_id rhs1_ec_id = ps->get_or_add_equiv_class (path_var (rhs1, 0));
-	    equiv_class_id rhs2_ec_id = ps->get_or_add_equiv_class (path_var (rhs2, 0));
+	    psn::equiv_class_id rhs1_ec_id = ps->get_or_add_equiv_class (path_var (rhs1, 0));
+	    psn::equiv_class_id rhs2_ec_id = ps->get_or_add_equiv_class (path_var (rhs2, 0));
 	    tristate t = ps->eval_condition (rhs1_ec_id, op, rhs2_ec_id);
 	    if (get_logger ())
 	      log ("eval condition: %s", t.as_string ());
@@ -2604,7 +2604,7 @@ engine::detect_leaks (const supernode *node, const sm_instance &sm_inst,
       {
 	log ("new_ps before escapes: %s", new_ps.to_str ().c_str ());
 	// FIXME: stack depth:
-	equiv_class_id ec_id
+	psn::equiv_class_id ec_id
 	  = new_ps.get_or_add_equiv_class (path_var (result, 0));
 	new_ps.recursively_mark_as_escaping (ec_id);
 	log ("new_ps after escapes: %s", new_ps.to_str ().c_str ());
@@ -2641,9 +2641,9 @@ engine::detect_leaks (const supernode *node, const sm_instance &sm_inst,
        /* Did this var's equivalence class escape?  If so, don't complain
 	 about it leaking.  */
       // FIXME: stack depth:
-      equiv_class_id ec_id
+      psn::equiv_class_id ec_id
 	= new_ps.get_or_add_equiv_class (path_var (vs->m_var, 0));
-      equiv_class &ec = ec_id.get_obj (new_ps);
+      psn::equiv_class &ec = ec_id.get_obj (new_ps);
       if (get_logger ())
 	log ("within %s", ec.to_str ().c_str ());
       if (ec.m_escaped)
@@ -3813,7 +3813,6 @@ run_checkers_impl_2 ()
       }
 
 #if 0
-    // TODO: selftest for auto_delete_vec
     auto_delete_vec <state_machine> checkers;
     make_checkers (checkers, the_logger.get_logger ());
 
diff --git a/gcc/analyzer/hardcoded-state-machine-file.cc b/gcc/analyzer/hardcoded-state-machine-file.cc
index 8d74298..0c4f821 100644
--- a/gcc/analyzer/hardcoded-state-machine-file.cc
+++ b/gcc/analyzer/hardcoded-state-machine-file.cc
@@ -40,14 +40,20 @@ static const bool DEBUG = false;
 
 ////////////////////////////////////////////////////////////////////////////
 
-class double_fclose : public pending_diagnostic
+class double_fclose : public pending_diagnostic_subclass<double_fclose>
 {
 public:
   double_fclose (tree arg) : m_arg (arg) {}
 
+  const char *get_kind () const FINAL OVERRIDE { return "double_fclose"; }
+  bool operator== (const double_fclose &other) const
+  {
+    return m_arg == other.m_arg;
+  }
+
   void emit (rich_location *rich_loc) FINAL OVERRIDE
   {
-    warning_at (rich_loc, 0,
+    warning_at (rich_loc, OPT_Wanalyzer_double_fclose,
 		"double %<fclose%> of FILE %qE",
 		m_arg);
   }
@@ -56,16 +62,24 @@ private:
   tree m_arg;
 };
 
-class file_leak : public pending_diagnostic
+class file_leak : public pending_diagnostic_subclass<file_leak>
 {
 public:
   file_leak (tree arg) : m_arg (arg) {}
 
+  const char *get_kind () const FINAL OVERRIDE { return "file_leak"; }
+  bool operator== (const file_leak &other) const
+  {
+    return m_arg == other.m_arg;
+  }
+
   void emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     metadata m;
+    /* CWE-775: "Missing Release of File Descriptor or Handle after
+       Effective Lifetime". */
     m.add_cwe (775);
-    warning_at (rich_loc, 0,
+    warning_at (rich_loc, m, OPT_Wanalyzer_file_leak,
 		"leak of FILE %qE",
 		m_arg);
   }
@@ -88,7 +102,7 @@ fileptr_state_machine::fileptr_state_machine (logger *logger)
   m_stop = add_state ("stop");
 }
 
-void
+bool
 fileptr_state_machine::on_stmt (sm_context *sm_ctxt,
 				const supernode *node,
 				const gimple *stmt) const
@@ -109,6 +123,7 @@ fileptr_state_machine::on_stmt (sm_context *sm_ctxt,
 	    {
 	      /* TODO: report leak.  */
 	    }
+	  return true;
 	}
 
       if (is_named_call_p (call, "fclose", 1))
@@ -127,11 +142,13 @@ fileptr_state_machine::on_stmt (sm_context *sm_ctxt,
 	  sm_ctxt->warn_for_state (node, stmt, arg, m_closed,
 				   new double_fclose (arg));
 	  sm_ctxt->on_transition (node, stmt, arg, m_closed, m_stop);
+	  return true;
 	}
 
       // TODO: operations on closed file
       // etc
     }
+  return false;
 }
 
 /* FIXME.  */
@@ -190,3 +207,9 @@ fileptr_state_machine::on_leak (sm_context *sm_ctxt,
   sm_ctxt->warn_for_state (node, stmt, var, m_nonnull,
 			   new file_leak (var));
 }
+
+bool
+fileptr_state_machine::can_purge_p (state_t s) const
+{
+  return s != m_unchecked && s != m_nonnull;
+}
diff --git a/gcc/analyzer/hardcoded-state-machine-malloc.cc b/gcc/analyzer/hardcoded-state-machine-malloc.cc
index cf61b0d..9704bae 100644
--- a/gcc/analyzer/hardcoded-state-machine-malloc.cc
+++ b/gcc/analyzer/hardcoded-state-machine-malloc.cc
@@ -47,6 +47,11 @@ public:
   : m_sm (sm), m_arg (arg)
   {}
 
+  bool subclass_equal_p (const pending_diagnostic &base_other) const OVERRIDE
+  {
+    return m_arg == ((const malloc_diagnostic &)base_other).m_arg;
+  }
+
 protected:
   const malloc_state_machine &m_sm;
   tree m_arg;
@@ -59,12 +64,15 @@ public:
   : malloc_diagnostic (sm, arg)
   {}
 
+  const char *get_kind () const FINAL OVERRIDE { return "double_free"; }
+
   void emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     auto_diagnostic_group d;
     metadata m;
     m.add_cwe (415); /* CWE-415: Double Free.  */
-    warning_at (rich_loc, m, "double-free of %qE", m_arg);
+    warning_at (rich_loc, m, OPT_Wanalyzer_double_free,
+		"double-free of %qE", m_arg);
 
 #if 0
     if (!rich_loc->get_path ())
@@ -120,19 +128,15 @@ private:
   //event_id_t m_second_free_event;
 };
 
-class possible_null_deref : public malloc_diagnostic
+/* Abstract subclass for describing possible bad uses of NULL.
+   Responsible for describing the call that could return NULL.  */
+
+class possible_null : public malloc_diagnostic
 {
 public:
-  possible_null_deref (const malloc_state_machine &sm, tree arg)
-  : malloc_diagnostic (sm, arg) {}
-
-  void emit (rich_location *rich_loc) FINAL OVERRIDE
-  {
-    /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
-    metadata m;
-    m.add_cwe (690);
-    warning_at (rich_loc, m, "use of possibly-NULL %qE", m_arg);
-  }
+  possible_null (const malloc_state_machine &sm, tree arg)
+  : malloc_diagnostic (sm, arg)
+  {}
 
   label_text describe_critical_state_change (tree expr,
 					     state_machine::state_t old_state,
@@ -144,6 +148,28 @@ public:
       return label_text::borrow ("this call could return NULL");
     return label_text ();
   }
+};
+
+/* Concrete subclass for describing dereference of a possible NULL
+   value.  */
+
+class possible_null_deref : public possible_null
+{
+public:
+  possible_null_deref (const malloc_state_machine &sm, tree arg)
+  : possible_null (sm, arg)
+  {}
+
+  const char *get_kind () const FINAL OVERRIDE { return "possible_null_deref"; }
+
+  void emit (rich_location *rich_loc) FINAL OVERRIDE
+  {
+    /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
+    metadata m;
+    m.add_cwe (690);
+    warning_at (rich_loc, m, OPT_Wanalyzer_possible_null_dereference,
+		"use of possibly-NULL %qE", m_arg);
+  }
 
   /* FIXME.  */
   label_text describe_final_event
@@ -156,21 +182,146 @@ public:
 
 };
 
+/* Subroutine for use by possible_null_arg::emit and null_arg::emit.
+   Issue a note informing that the pertinent argument must be non-NULL.  */
+
+static void
+inform_nonnull_attribute (tree fndecl, int arg_idx)
+{
+  inform (DECL_SOURCE_LOCATION (fndecl),
+	  "argument %u of %qD must be non-null",
+	  arg_idx + 1, fndecl);
+  /* FIXME: ideally, use the location of the parm
+     and underline the attribute also (but we probably don't have the
+     location_t...)  */
+
+  /* C and C++ FEs have get_fndecl_argument_location.  */
+}
+
+/* Concrete subclass for describing passing a possibly-NULL value to a
+   function marked with __attribute__((nonnull)).  */
+
+class possible_null_arg : public possible_null
+{
+public:
+  possible_null_arg (const malloc_state_machine &sm, tree arg,
+		     tree fndecl, int arg_idx)
+  : possible_null (sm, arg),
+    m_fndecl (fndecl), m_arg_idx (arg_idx)
+  {}
+
+  const char *get_kind () const FINAL OVERRIDE { return "possible_null_arg"; }
+
+  bool subclass_equal_p (const pending_diagnostic &base_other) const
+  {
+    const possible_null_arg &sub_other
+      = (const possible_null_arg &)base_other;
+    return (m_arg == sub_other.m_arg
+	    && m_fndecl == sub_other.m_fndecl
+	    && m_arg_idx == sub_other.m_arg_idx);
+  }
+
+
+  void emit (rich_location *rich_loc) FINAL OVERRIDE
+  {
+    /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
+    auto_diagnostic_group d;
+    metadata m;
+    m.add_cwe (690);
+    warning_at (rich_loc, m, OPT_Wanalyzer_possible_null_argument,
+		"use of possibly-NULL %qE where non-null expected",
+		m_arg);
+    inform_nonnull_attribute (m_fndecl, m_arg_idx);
+  }
+
+  /* FIXME.  */
+  label_text describe_final_event
+    (/*diagnostic_context *ctxt, */
+     tree expr,
+     state_machine::state_t state ATTRIBUTE_UNUSED)
+  {
+    return formatted_print ("argument %u (%qE) could be NULL"
+			    " where non-null expected",
+			    m_arg_idx + 1, expr);
+  }
+
+  // TODO
+
+private:
+  tree m_fndecl;
+  int m_arg_idx;
+};
+
+/* Concrete subclass for describing a dereference of a NULL value.  */
+
 class null_deref : public malloc_diagnostic
 {
 public:
   null_deref (const malloc_state_machine &sm, tree arg)
   : malloc_diagnostic (sm, arg) {}
 
+  const char *get_kind () const FINAL OVERRIDE { return "null_deref"; }
+
   void emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
     metadata m;
     m.add_cwe (690);
-    warning_at (rich_loc, m, "use of NULL %qE", m_arg);
+    warning_at (rich_loc, m, OPT_Wanalyzer_null_dereference,
+		"use of NULL %qE", m_arg);
   }
 };
 
+/* Concrete subclass for describing passing a NULL value to a
+   function marked with __attribute__((nonnull)).  */
+
+class null_arg : public malloc_diagnostic
+{
+public:
+  null_arg (const malloc_state_machine &sm, tree arg,
+	    tree fndecl, int arg_idx)
+  : malloc_diagnostic (sm, arg),
+    m_fndecl (fndecl), m_arg_idx (arg_idx)
+  {}
+
+  const char *get_kind () const FINAL OVERRIDE { return "null_arg"; }
+
+  bool subclass_equal_p (const pending_diagnostic &base_other) const
+  {
+    const null_arg &sub_other
+      = (const null_arg &)base_other;
+    return (m_arg == sub_other.m_arg
+	    && m_fndecl == sub_other.m_fndecl
+	    && m_arg_idx == sub_other.m_arg_idx);
+  }
+
+  void emit (rich_location *rich_loc) FINAL OVERRIDE
+  {
+    /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
+    auto_diagnostic_group d;
+    metadata m;
+    m.add_cwe (690);
+    warning_at (rich_loc, m, OPT_Wanalyzer_null_argument,
+		"use of NULL %qE where non-null expected", m_arg);
+    inform_nonnull_attribute (m_fndecl, m_arg_idx);
+  }
+
+  /* FIXME.  */
+  label_text describe_final_event
+    (/*diagnostic_context *ctxt, */
+     tree expr,
+     state_machine::state_t state ATTRIBUTE_UNUSED)
+  {
+    return formatted_print ("argument %u (%qE) NULL"
+			    " where non-null expected",
+			    m_arg_idx + 1, expr);
+  }
+
+private:
+  tree m_fndecl;
+  int m_arg_idx;
+};
+
 class use_after_free : public malloc_diagnostic
 {
 public:
@@ -178,12 +329,15 @@ public:
   : malloc_diagnostic (sm, arg)
   {}
 
+  const char *get_kind () const FINAL OVERRIDE { return "use_after_free"; }
+
   void emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     /* CWE-416: Use After Free.  */
     metadata m;
     m.add_cwe (416);
-    warning_at (rich_loc, m, "use after free of %qE", m_arg);
+    warning_at (rich_loc, m, OPT_Wanalyzer_use_after_free,
+		"use after free of %qE", m_arg);
   }
 
   label_text describe_critical_state_change (tree expr,
@@ -204,11 +358,14 @@ public:
   malloc_leak (const malloc_state_machine &sm, tree arg)
   : malloc_diagnostic (sm, arg) {}
 
+  const char *get_kind () const FINAL OVERRIDE { return "malloc_leak"; }
+
   void emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     metadata m;
     m.add_cwe (401);
-    warning_at (rich_loc, m, "leak of %qE", m_arg);
+    warning_at (rich_loc, m, OPT_Wanalyzer_malloc_leak,
+		"leak of %qE", m_arg);
   }
 
   label_text describe_critical_state_change (tree expr,
@@ -241,6 +398,15 @@ public:
   {
   }
 
+  const char *get_kind () const FINAL OVERRIDE { return "free_of_non_heap"; }
+
+  bool subclass_equal_p (const pending_diagnostic &base_other) const
+    FINAL OVERRIDE
+  {
+    const free_of_non_heap &other = (const free_of_non_heap &)base_other;
+    return (m_arg == other.m_arg && m_kind == other.m_kind);
+  }
+
   void emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     auto_diagnostic_group d;
@@ -251,12 +417,12 @@ public:
       default:
 	gcc_unreachable ();
       case KIND_UNKNOWN:
-	warning_at (rich_loc, m,
+	warning_at (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
 		    "free of %qE which points to memory not on the heap",
 		    m_arg);
 	break;
       case KIND_ALLOCA:
-	warning_at (rich_loc, m,
+	warning_at (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
 		    "free of memory allocated on the stack by %qs (%qE)"
 		    " will corrupt the heap",
 		    "alloca", m_arg);
@@ -323,7 +489,7 @@ malloc_state_machine::malloc_state_machine (logger *logger)
   m_stop = add_state ("stop");
 }
 
-void
+bool
 malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 			       const supernode *node,
 			       const gimple *stmt) const
@@ -345,6 +511,7 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 	    {
 	      /* TODO: report leak.  */
 	    }
+	  return true;
 	}
 
       if (is_named_call_p (call, "alloca", 1)
@@ -353,6 +520,7 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 	  tree lhs = gimple_call_lhs (call);
 	  if (lhs)
 	    sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
+	  return true;
 	}
 
       if (is_named_call_p (call, "free", 1))
@@ -378,6 +546,40 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 	  sm_ctxt->warn_for_state (node, stmt, arg, m_non_heap,
 				   new free_of_non_heap (*this, arg));
 	  sm_ctxt->on_transition (node, stmt, arg, m_non_heap, m_stop);
+	  return true;
+	}
+
+      /* TODO: handle "__attribute__((nonnull))".   */
+      if (tree callee_fndecl = gimple_call_fndecl (stmt))
+	{
+	  tree fntype = TREE_TYPE (callee_fndecl);
+	  bitmap nonnull_args = get_nonnull_args (fntype);
+	  if (nonnull_args)
+	    {
+	      for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
+		{
+		  tree arg = gimple_call_arg (stmt, i);
+		  if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
+		    continue;
+		  /* If we have a nonnull-args, and either all pointers, or just
+		     the specified pointers.  */
+		  if (bitmap_empty_p (nonnull_args)
+		      || bitmap_bit_p (nonnull_args, i))
+		    {
+		      sm_ctxt->warn_for_state
+			(node, stmt, arg, m_unchecked,
+			 new possible_null_arg (*this, arg, callee_fndecl, i));
+		      sm_ctxt->on_transition (node, stmt, arg, m_unchecked,
+					      m_nonnull);
+
+		      sm_ctxt->warn_for_state
+			(node, stmt, arg, m_null,
+			 new null_arg (*this, arg, callee_fndecl, i));
+		      sm_ctxt->on_transition (node, stmt, arg, m_null, m_stop);
+		    }
+		}
+	      BITMAP_FREE (nonnull_args);
+	    }
 	}
     }
 
@@ -437,6 +639,7 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 	  sm_ctxt->on_transition (node, stmt, arg, m_free, m_stop);
 	}
     }
+  return false;
 }
 
 /* FIXME.  */
@@ -494,3 +697,9 @@ malloc_state_machine::on_leak (sm_context *sm_ctxt,
   sm_ctxt->warn_for_state (node, stmt, var, m_nonnull,
 			   new malloc_leak (*this, var));
 }
+
+bool
+malloc_state_machine::can_purge_p (state_t s) const
+{
+  return s != m_unchecked && s != m_nonnull;
+}
diff --git a/gcc/analyzer/hardcoded-state-machine-pattern-test.cc b/gcc/analyzer/hardcoded-state-machine-pattern-test.cc
index 7f1460c..bef3bcb 100644
--- a/gcc/analyzer/hardcoded-state-machine-pattern-test.cc
+++ b/gcc/analyzer/hardcoded-state-machine-pattern-test.cc
@@ -40,11 +40,20 @@ static const bool DEBUG = false;
 
 ////////////////////////////////////////////////////////////////////////////
 
-class pattern_match : public pending_diagnostic
+class pattern_match : public pending_diagnostic_subclass<pattern_match>
 {
 public:
   pattern_match (tree arg, const char *msg) : m_arg (arg), m_msg (msg) {}
 
+  const char *get_kind () const FINAL OVERRIDE { return "pattern_match"; }
+
+  bool operator== (const pattern_match &other) const
+  {
+    return (m_arg == other.m_arg
+	    // FIXME: should this be string equality?
+	    && m_msg == other.m_msg);
+  }
+
   void emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     // TODO:
@@ -65,7 +74,7 @@ pattern_test_state_machine::pattern_test_state_machine (logger *logger)
   m_start = add_state ("start");
 }
 
-void
+bool
 pattern_test_state_machine::on_stmt (sm_context *sm_ctxt,
 				     const supernode *node,
 				     const gimple *stmt) const
@@ -76,6 +85,7 @@ pattern_test_state_machine::on_stmt (sm_context *sm_ctxt,
   if (const gcall *call = dyn_cast <const gcall *> (stmt))
     {
     }
+  return false;
 }
 
 /* FIXME.  */
@@ -129,3 +139,9 @@ pattern_test_state_machine::on_leak (sm_context *sm_ctxt,
 {
   /* Empty.  */
 }
+
+bool
+pattern_test_state_machine::can_purge_p (state_t s) const
+{
+  return true;
+}
diff --git a/gcc/analyzer/hardcoded-state-machine-sensitive.cc b/gcc/analyzer/hardcoded-state-machine-sensitive.cc
index 59e5cb7..5c52431 100644
--- a/gcc/analyzer/hardcoded-state-machine-sensitive.cc
+++ b/gcc/analyzer/hardcoded-state-machine-sensitive.cc
@@ -40,17 +40,25 @@ static const bool DEBUG = false;
 
 ////////////////////////////////////////////////////////////////////////////
 
-class leakage_through_output_file : public pending_diagnostic
+class exposure_through_output_file
+  : public pending_diagnostic_subclass<exposure_through_output_file>
 {
 public:
-  leakage_through_output_file (tree arg) : m_arg (arg) {}
+  exposure_through_output_file (tree arg) : m_arg (arg) {}
+
+  const char *get_kind () const FINAL OVERRIDE { return "exposure_through_output_file"; }
+
+  bool operator== (const exposure_through_output_file &other) const
+  {
+    return m_arg == other.m_arg;
+  }
 
   void emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     metadata m;
     /* CWE-532: Information Exposure Through Log Files */
     m.add_cwe (532);
-    warning_at (rich_loc, m,
+    warning_at (rich_loc, m, OPT_Wanalyzer_exposure_through_output_file,
 		"sensitive value %qE written to output file",
 		m_arg);
   }
@@ -71,16 +79,16 @@ sensitive_state_machine::sensitive_state_machine (logger *logger)
 }
 
 void
-sensitive_state_machine::warn_for_any_leakage (sm_context *sm_ctxt,
-					       const supernode *node,
-					       const gimple *stmt,
-					       tree arg) const
+sensitive_state_machine::warn_for_any_exposure (sm_context *sm_ctxt,
+						const supernode *node,
+						const gimple *stmt,
+						tree arg) const
 {
   sm_ctxt->warn_for_state (node, stmt, arg, m_sensitive,
-			   new leakage_through_output_file (arg));
+			   new exposure_through_output_file (arg));
 }
 
-void
+bool
 sensitive_state_machine::on_stmt (sm_context *sm_ctxt,
 				  const supernode *node,
 				  const gimple *stmt) const
@@ -95,6 +103,7 @@ sensitive_state_machine::on_stmt (sm_context *sm_ctxt,
 	  tree lhs = gimple_call_lhs (call);
 	  if (lhs)
 	    sm_ctxt->on_transition (node, stmt, lhs, m_start, m_sensitive);
+	  return true;
 	}
       else if (is_named_call_p (call, "fprintf")
 	       || is_named_call_p (call, "printf"))
@@ -103,15 +112,18 @@ sensitive_state_machine::on_stmt (sm_context *sm_ctxt,
 	  for (int idx = 1; idx < gimple_call_num_args (call); idx++)
 	    {
 	      tree arg = gimple_call_arg (call, idx);
-	      warn_for_any_leakage (sm_ctxt, node, stmt, arg);
+	      warn_for_any_exposure (sm_ctxt, node, stmt, arg);
 	    }
+	  return true;
 	}
       else if (is_named_call_p (call, "fwrite", 4))
 	{
 	  tree arg = gimple_call_arg (call, 0);
-	  warn_for_any_leakage (sm_ctxt, node, stmt, arg);
+	  warn_for_any_exposure (sm_ctxt, node, stmt, arg);
+	  return true;
 	}
     }
+  return false;
 }
 
 /* FIXME.  */
@@ -133,3 +145,9 @@ sensitive_state_machine::on_leak (sm_context *sm_ctxt,
 {
   /* Empty.  */
 }
+
+bool
+sensitive_state_machine::can_purge_p (state_t s) const
+{
+  return true;
+}
diff --git a/gcc/analyzer/hardcoded-state-machine-taint.cc b/gcc/analyzer/hardcoded-state-machine-taint.cc
index 682726c..d6c2d6c 100644
--- a/gcc/analyzer/hardcoded-state-machine-taint.cc
+++ b/gcc/analyzer/hardcoded-state-machine-taint.cc
@@ -40,16 +40,23 @@ static const bool DEBUG = false;
 
 ////////////////////////////////////////////////////////////////////////////
 
-class tainted_array_index : public pending_diagnostic
+class tainted_array_index : public pending_diagnostic_subclass<tainted_array_index>
 {
 public:
   tainted_array_index (tree arg) : m_arg (arg) {}
 
+  const char *get_kind () const FINAL OVERRIDE { return "tainted_array_index"; }
+
+  bool operator== (const tainted_array_index &other) const
+  {
+    return m_arg == other.m_arg;
+  }
+
   void emit (rich_location *rich_loc) FINAL OVERRIDE
   {
     metadata m;
     m.add_cwe (129);
-    warning_at (rich_loc, m,
+    warning_at (rich_loc, m, OPT_Wanalyzer_tainted_array_index,
 		"use of tainted value %qE in array lookup without bounds checking",
 		m_arg);
   }
@@ -71,7 +78,7 @@ taint_state_machine::taint_state_machine (logger *logger)
   m_stop = add_state ("stop");
 }
 
-void
+bool
 taint_state_machine::on_stmt (sm_context *sm_ctxt,
 			       const supernode *node,
 			       const gimple *stmt) const
@@ -93,6 +100,7 @@ taint_state_machine::on_stmt (sm_context *sm_ctxt,
 	  if (TREE_CODE (arg) == ADDR_EXPR)
 	    sm_ctxt->on_transition (node, stmt, TREE_OPERAND (arg, 0),
 				    m_start, m_tainted);
+	  return true;
 	}
     }
 
@@ -155,6 +163,7 @@ taint_state_machine::on_stmt (sm_context *sm_ctxt,
 	}
     }
 #endif
+  return false;
 }
 
 /* FIXME.  */
@@ -214,3 +223,9 @@ taint_state_machine::on_leak (sm_context *sm_ctxt,
 {
   /* Empty.  */
 }
+
+bool
+taint_state_machine::can_purge_p (state_t s) const
+{
+  return true;
+}
diff --git a/gcc/analyzer/hardcoded-state-machine.h b/gcc/analyzer/hardcoded-state-machine.h
index 274c968..ff9065f 100644
--- a/gcc/analyzer/hardcoded-state-machine.h
+++ b/gcc/analyzer/hardcoded-state-machine.h
@@ -54,7 +54,8 @@ public:
 
   const char *get_state_name (state_t s) const;
 
-  virtual void on_stmt (sm_context *sm_ctxt,
+  /* Return true if STMT is a function call recognized by this sm.  */
+  virtual bool on_stmt (sm_context *sm_ctxt,
 			const supernode *node,
 			const gimple *stmt) const = 0;
 
@@ -68,6 +69,11 @@ public:
 			tree var,
 			state_machine::state_t state) const = 0;
 
+  /* Return true if it safe to discard the given state (to help
+     when simplifying state objects).
+     States that need leak detection should return false.  */
+  virtual bool can_purge_p (state_t s) const = 0;
+
 protected:
   state_t add_state (const char *name);
 
@@ -126,8 +132,28 @@ class pending_diagnostic
 {
  public:
   virtual ~pending_diagnostic () {}
+
+  /* Hand-coded RTTI: get an ID for the subclass.  */
+  virtual const char *get_kind () const = 0;
+
   virtual void emit (rich_location *) = 0;
 
+  /* Compare for equality with OTHER, which might be of a different
+     subclass.  */
+
+  bool equal_p (const pending_diagnostic &other)
+  {
+    /* Check for pointer equality on the IDs from get_kind.  */
+    if (get_kind () != other.get_kind ())
+      return false;
+    /* Call vfunc now we know they have the same ID: */
+    return subclass_equal_p (other);
+  }
+  /* A vfunc for testing for equality, where we've already
+     checked they have the same ID.  See pending_diagnostic_subclass
+     below for a convenience subclass for implementing this.  */
+  virtual bool subclass_equal_p (const pending_diagnostic &other) const = 0;
+
   /* For greatest precision-of-wording, give the pending diagnostic
      a chance to describe critical state changes in terms of the
      diagnostic.
@@ -170,6 +196,23 @@ class pending_diagnostic
     ATTRIBUTE_GCC_DIAG(1,2);
 };
 
+/* Use of the curiously-recurring template pattern, to implement
+   subclass_equal_p by casting and calling the operator==
+   This assumes that BASE_OTHER has already been checked to have
+   been of the same subclass.  */
+
+template <class Subclass>
+class pending_diagnostic_subclass : public pending_diagnostic
+{
+ public:
+  bool subclass_equal_p (const pending_diagnostic &base_other) const
+    FINAL OVERRIDE
+  {
+    const Subclass &other = (const Subclass &)base_other;
+    return *(const Subclass*)this == other;
+  }
+};
+
 #if 0
 /* A thing for state machines to pass to the engine to create
    pending_diagnostic instances (so that we can filter without having 
@@ -226,7 +269,7 @@ class malloc_state_machine : public state_machine
 public:
   malloc_state_machine (logger *logger);
 
-  void on_stmt (sm_context *sm_ctxt,
+  bool on_stmt (sm_context *sm_ctxt,
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
@@ -239,6 +282,7 @@ public:
 		const gimple *stmt,
 		tree var,
 		state_machine::state_t state) const FINAL OVERRIDE;
+  bool can_purge_p (state_t s) const FINAL OVERRIDE;
 
   state_t m_start;
   state_t m_unchecked;
@@ -260,7 +304,7 @@ class fileptr_state_machine : public state_machine
 public:
   fileptr_state_machine (logger *logger);
 
-  void on_stmt (sm_context *sm_ctxt,
+  bool on_stmt (sm_context *sm_ctxt,
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
@@ -273,6 +317,7 @@ public:
 		const gimple *stmt,
 		tree var,
 		state_machine::state_t state) const FINAL OVERRIDE;
+  bool can_purge_p (state_t s) const FINAL OVERRIDE;
 
 private:
   state_t m_start;
@@ -293,7 +338,7 @@ class taint_state_machine : public state_machine
 public:
   taint_state_machine (logger *logger);
 
-  void on_stmt (sm_context *sm_ctxt,
+  bool on_stmt (sm_context *sm_ctxt,
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
@@ -306,6 +351,7 @@ public:
 		const gimple *stmt,
 		tree var,
 		state_machine::state_t state) const FINAL OVERRIDE;
+  bool can_purge_p (state_t s) const FINAL OVERRIDE;
 
 private:
   state_t m_start;
@@ -325,7 +371,7 @@ class sensitive_state_machine : public state_machine
 public:
   sensitive_state_machine (logger *logger);
 
-  void on_stmt (sm_context *sm_ctxt,
+  bool on_stmt (sm_context *sm_ctxt,
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
@@ -338,12 +384,13 @@ public:
 		const gimple *stmt,
 		tree var,
 		state_machine::state_t state) const FINAL OVERRIDE;
+  bool can_purge_p (state_t s) const FINAL OVERRIDE;
 
 private:
-  void warn_for_any_leakage (sm_context *sm_ctxt,
-			     const supernode *node,
-			     const gimple *stmt,
-			     tree arg) const;
+  void warn_for_any_exposure (sm_context *sm_ctxt,
+			      const supernode *node,
+			      const gimple *stmt,
+			      tree arg) const;
 
   state_t m_start;
   state_t m_sensitive;
@@ -360,7 +407,7 @@ class pattern_test_state_machine : public state_machine
 public:
   pattern_test_state_machine (logger *logger);
 
-  void on_stmt (sm_context *sm_ctxt,
+  bool on_stmt (sm_context *sm_ctxt,
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
@@ -373,6 +420,7 @@ public:
 		const gimple *stmt,
 		tree var,
 		state_machine::state_t state) const FINAL OVERRIDE;
+  bool can_purge_p (state_t s) const FINAL OVERRIDE;
 
 private:
   state_t m_start;
diff --git a/gcc/analyzer/impl-3.cc b/gcc/analyzer/impl-3.cc
index 30b3240..1bfaca4 100644
--- a/gcc/analyzer/impl-3.cc
+++ b/gcc/analyzer/impl-3.cc
@@ -41,7 +41,9 @@
 #include "fold-const.h"
 #include "tristate.h"
 #include "selftest.h"
-#include "constraints.h"
+#include "path-state.h"
+
+using namespace psn;
 
 namespace impl_3 {
 
@@ -87,7 +89,7 @@ class engine;
 
 /* FIXME.  */
 
-class ec3_state : public extra_ec_state
+class ec3_state : public psn::extra_ec_state
 {
  public:
   ec3_state (const state_machine &sm,
@@ -645,10 +647,10 @@ public:
 		  state_machine::state_t baseline_state,
 		  state_machine::state_t new_state);
 
-  equiv_class &get_ec (tree expr)
+  psn::equiv_class &get_ec (tree expr)
   {
     path_var pv (expr, get_stack_depth ());
-    equiv_class_id ec_id = m_ps.get_or_add_equiv_class (pv);
+    psn::equiv_class_id ec_id = m_ps.get_or_add_equiv_class (pv);
     return ec_id.get_obj (m_ps);
   }
 
@@ -781,7 +783,7 @@ public:
   void reset_baseline_states ()
   {
     int i;
-    equiv_class *ec;
+    psn::equiv_class *ec;
     FOR_EACH_VEC_ELT (m_path->m_ps.m_equiv_classes, i, ec)
       {
 	ec3_state *extra_state = (ec3_state *)ec->m_extra_state;
@@ -1079,7 +1081,7 @@ checker3_path::add_link (const superedge *edge, const supernode *node)
 state_machine::state_t
 checker3_path::get_baseline_state (tree expr)
 {
-  equiv_class &ec = get_ec (expr);
+  psn::equiv_class &ec = get_ec (expr);
   ec3_state *extra_state = (ec3_state *)ec.m_extra_state;
   if (extra_state)
     return extra_state->m_baseline_state;
@@ -1092,7 +1094,7 @@ checker3_path::set_state (tree expr,
 			  state_machine::state_t baseline_state,
 			  state_machine::state_t new_state)
 {
-  equiv_class &ec = get_ec (expr);
+  psn::equiv_class &ec = get_ec (expr);
   delete ec.m_extra_state;
   ec.m_extra_state = new ec3_state (get_sm (), baseline_state, new_state);
   if (m_engine->get_logger ())
@@ -1592,7 +1594,6 @@ run_checkers_impl_3 ()
       }
 
 #if 0
-    // TODO: selftest for auto_delete_vec
     auto_delete_vec <state_machine> checkers;
     make_checkers (checkers, the_logger.get_logger ());
 
diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index b12102c..5089c16 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -42,8 +42,15 @@
 #include "tristate.h"
 #include "selftest.h"
 #include "digraph.h"
-#include "constraints.h"
-//#include "tree-ssa-operands.h"
+#include "path-state.h"
+#include "tree-ssa-operands.h"
+#include "tree-vrp.h"
+#include "gimple-ssa.h"
+#include "tree-ssanames.h"
+#include "tree-phinodes.h"
+#include "ssa-iterators.h"
+#include "region-model.h"
+#include "tree-diagnostic.h"
 
 /* Overview:
 
@@ -100,8 +107,11 @@ namespace impl_4 {
 class exploded_graph;
 class exploded_node;
 class exploded_edge;
+class exploded_path;
 class checker4_path;
 class stmt_finder;
+class state_purge_per_ssa_name;
+class shortest_paths;
 
 ////////////////////////////////////////////////////////////////////////////
 
@@ -157,6 +167,24 @@ struct pod_hash_traits : typed_noop_remove<Type>
 
 ////////////////////////////////////////////////////////////////////////////
 
+class format
+{
+public:
+  format (bool newlines) : m_newlines (newlines) {}
+
+  void spacer (pretty_printer *pp) const
+  {
+    if (m_newlines)
+      pp_newline (pp);
+    else
+      pp_space (pp);
+  }
+
+  bool m_newlines;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
 /* A string of return_superedge pointers, representing a call stack
    at a program point.
 
@@ -265,9 +293,9 @@ public:
   {
   }
 
-  void print (pretty_printer *pp) const;
+  void print (pretty_printer *pp, const format &f) const;
   void print_source_line (pretty_printer *pp) const;
-  std::string to_str () const;
+  std::string to_str (const format &f) const;
 
   hashval_t hash () const;
   bool operator== (const program_point &other) const
@@ -347,7 +375,7 @@ public:
     return program_point (NULL, NULL, 0, PK_DELETED, call_string ());
   }
 
-  bool on_edge (const exploded_graph &eg,
+  bool on_edge (exploded_graph &eg,
 		const superedge *succ);
 
 //private:
@@ -358,6 +386,127 @@ public:
   call_string m_call_string;
 };
 
+} // namespace impl_4
+
+using impl_4::pod_hash_traits;
+using impl_4::program_point;
+
+template <> struct default_hash_traits<program_point>
+: public pod_hash_traits<program_point>
+{
+};
+
+template <>
+inline hashval_t
+pod_hash_traits<program_point>::hash (value_type v)
+{
+  return v.hash ();
+}
+
+template <>
+inline bool
+pod_hash_traits<program_point>::equal (const value_type &existing,
+                                 const value_type &candidate)
+{
+  return existing == candidate;
+}
+template <>
+inline void
+pod_hash_traits<program_point>::mark_deleted (value_type &v)
+{
+  v = program_point::deleted ();
+}
+template <>
+inline void
+pod_hash_traits<program_point>::mark_empty (value_type &v)
+{
+  v = program_point::empty ();
+}
+template <>
+inline bool
+pod_hash_traits<program_point>::is_deleted (value_type v)
+{
+  return v.m_kind == PK_DELETED;
+}
+template <>
+inline bool
+pod_hash_traits<program_point>::is_empty (value_type v)
+{
+  return v.m_kind == PK_EMPTY;
+}
+
+namespace impl_4 {
+
+////////////////////////////////////////////////////////////////////////////
+
+/* The result of analyzing which SSA names can be purged from state at
+   different points in the program, so that we can simplify program_state
+   objects, in the hope of reducing state-blowup.  */
+
+class state_purge_map : public log_user
+{
+public:
+  typedef hash_map<tree, state_purge_per_ssa_name *> map_t;
+  typedef map_t::iterator iterator;
+
+  state_purge_map (const supergraph &sg, logger *logger);
+  ~state_purge_map ();
+
+  const state_purge_per_ssa_name &get_data_for_ssa_name (tree var) const
+  {
+    gcc_assert (TREE_CODE (var) == SSA_NAME);
+    state_purge_per_ssa_name **slot
+      = const_cast <map_t&> (m_map).get (var);
+    return **slot;
+  }
+
+  iterator begin () const { return m_map.begin (); }
+  iterator end () const { return m_map.end (); }
+
+  //private:
+  const supergraph &m_sg;
+  map_t m_map;
+};
+
+/* The part of a state_purge_map relating to a specific SSA name.
+
+   The result of analyzing a given SSA name, recording which
+   program_points need to retain state information about it to handle
+   their successor states, so that we can simplify program_state objects,
+   in the hope of reducing state-blowup.  */
+
+class state_purge_per_ssa_name
+{
+public:
+  state_purge_per_ssa_name (const state_purge_map &map,
+			    tree var,
+			    function *fun);
+
+  bool needed_at_point_p (const program_point &point) const;
+
+  /* FIXME: this stores points, but ignores interprocedural issues;
+     everything will be at depth 0.  */
+  /* Should we build a program_point graph first???  */
+
+private:
+  static program_point before_use_stmt (const state_purge_map &map,
+					const gimple *use_stmt);
+
+  void add_to_worklist (const program_point &point,
+			auto_vec<program_point *> *worklist,
+			logger *logger);
+
+  void process_point (const program_point &point,
+		      auto_vec<program_point *> *worklist,
+		      logger *logger);
+
+  typedef hash_set<program_point> point_set_t;
+  point_set_t m_points_needing_var;
+  tree m_var;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
 /* Data shared by all program_state instances.  */
 
 class extrinsic_state
@@ -435,13 +584,17 @@ namespace impl_4 {
 /* Map from var to state machine state, also capturing the origin of
    each state.  */
 
+template<typename Expr_T>
 class sm_state_map
 {
 public:
+  typedef Expr_T expr_t;
+
+  /* An entry in a hash_map.  */
   struct entry_t
   {
     entry_t (state_machine::state_t state,
-	     path_var origin)
+	     expr_t origin)
     : m_state (state), m_origin (origin)
     {}
 
@@ -456,10 +609,10 @@ public:
     }
 
     state_machine::state_t m_state;
-    path_var m_origin;
+    expr_t m_origin;
   };
-  typedef hash_map <path_var, entry_t> map_t;
-  typedef map_t::iterator iterator_t;
+  typedef hash_map <expr_t, entry_t> map_t;
+  typedef typename map_t::iterator iterator_t;
 
   sm_state_map *clone () const;
 
@@ -476,20 +629,28 @@ public:
     return !(*this == other);
   }
 
-  state_machine::state_t get_state (path_var pv) const;
-  path_var get_origin (path_var pv) const;
+  state_machine::state_t get_state (expr_t pv) const;
+  expr_t get_origin (expr_t pv) const;
 
-  void set_state (path_state *ps, path_var pv, state_machine::state_t state,
-		  path_var origin);
-  void set_state (const equiv_class &ec,
+  void set_state (path_state *ps, expr_t pv, state_machine::state_t state,
+		  expr_t origin);
+  void set_state (const psn::equiv_class &ec,
 		  state_machine::state_t state,
-		  path_var origin);
-  void impl_set_state (path_var pv,
+		  expr_t origin);
+  void impl_set_state (expr_t pv,
 		       state_machine::state_t state,
-		       path_var origin);
+		       expr_t origin);
 
   void purge_stack_frame (int callee_stack_depth);
 
+  void purge_for_unknown_fncall (const exploded_graph &eg,
+				 const state_machine &sm,
+				 const gcall *call, tree fndecl,
+				 path_state *new_ps);
+
+  int purge (const state_machine &sm,
+	     const psn::purge_criteria &p);
+
   iterator_t begin () const { return m_map.begin (); }
   iterator_t end () const { return m_map.end (); }
 
@@ -503,13 +664,13 @@ private:
    Currently this is a combination of:
    - a path_state giving a set of equivalence classes of path_vars and
      inequalities between them
+   - an optional region_model, for when -fanalyzer-region-model is enabled
    - sm_state_maps per state machine, giving a sparse mapping of
      path_vars to states.  */
 
 class program_state
 {
 public:
-  program_state (int num_checkers);
   program_state (const extrinsic_state &ext_state);
   program_state (const program_state &other);
   program_state& operator= (const program_state &other);
@@ -521,23 +682,30 @@ public:
 	      pretty_printer *pp) const;
   std::string to_str (const extrinsic_state &ext_state) const;
 
-  equiv_class &get_ec (path_var pv)
+  void dump (const extrinsic_state &ext_state,
+	     pretty_printer *pp) const;
+
+  psn::equiv_class &get_ec (path_var pv)
   {
-    equiv_class_id ec_id = m_path_state.get_or_add_equiv_class (pv);
+    psn::equiv_class_id ec_id = m_path_state.get_or_add_equiv_class (pv);
     return ec_id.get_obj (m_path_state);
   }
 
-  program_state prune_for_point (const exploded_graph &eg,
+  program_state prune_for_point (exploded_graph &eg,
 				 const program_point &point) const;
 
-  bool on_edge (const exploded_graph &eg,
+  bool on_edge (exploded_graph &eg,
 		const exploded_node &enode,
 		const superedge *succ);
 
+  void validate () const;
+
   //private:
   path_state m_path_state;
 
-  auto_vec<sm_state_map *> m_checker_states;
+  region_model *m_region_model;
+
+  auto_delete_vec<sm_state_map<path_var> > m_checker_states;
 };
 
 /* A <program_point, program_state> pair, used internally by
@@ -557,63 +725,15 @@ struct point_and_state
   {
     return m_point.hash () ^ m_state.hash ();
   }
+  bool operator== (const point_and_state &other) const
+  {
+    return m_point == other.m_point && m_state == other.m_state;
+  }
 
   program_point m_point;
   program_state m_state;
 };
 
-} // namespace impl_4
-
-using impl_4::point_and_state;
-using impl_4::pod_hash_traits;
-
-template <> struct default_hash_traits<point_and_state>
-: public pod_hash_traits<point_and_state>
-{
-};
-
-template <>
-inline hashval_t
-pod_hash_traits<point_and_state>::hash (value_type v)
-{
-  return v.hash ();
-}
-
-template <>
-inline bool
-pod_hash_traits<point_and_state>::equal (const value_type &existing,
-					 const value_type &candidate)
-{
-  return (existing.m_point == candidate.m_point
-	  && existing.m_state == candidate.m_state);
-}
-template <>
-inline void
-pod_hash_traits<point_and_state>::mark_deleted (value_type &v)
-{
-  v = point_and_state (program_point::deleted (), program_state (0));
-}
-template <>
-inline void
-pod_hash_traits<point_and_state>::mark_empty (value_type &v)
-{
-  v = point_and_state (program_point::empty (), program_state (0));
-}
-template <>
-inline bool
-pod_hash_traits<point_and_state>::is_deleted (value_type v)
-{
-  return (v.m_point.m_kind == PK_DELETED);
-}
-template <>
-inline bool
-pod_hash_traits<point_and_state>::is_empty (value_type v)
-{
-  return (v.m_point.m_kind == PK_EMPTY);
-}
-
-namespace impl_4 {
-
 /* An exploded_node is a unique, immutable <point, state> pair within the
    exploded_graph.
    Each exploded_node has a unique index within the graph
@@ -629,11 +749,17 @@ class exploded_node : public dnode
   {
   }
 
+  hashval_t hash () const { return m_ps.hash (); }
+
   void dump_dot (graphviz_out *gv) const FINAL OVERRIDE;
   void dump_dot_id (pretty_printer *pp) const;
 
-  program_state on_stmt (const exploded_graph &eg) const;
-  bool on_edge (const exploded_graph &eg,
+  void dump_to_pp (pretty_printer *pp) const;
+  void dump (FILE *fp) const;
+  void dump () const;
+
+  program_state on_stmt (exploded_graph &eg) const;
+  bool on_edge (exploded_graph &eg,
 		const superedge *succ,
 		program_point *next_point,
 		program_state *next_state) const;
@@ -644,13 +770,26 @@ class exploded_node : public dnode
 		      const program_state &old_state,
 		      program_state *new_state) const;
 
+  void on_assignment_for_region_model (const exploded_graph &eg,
+				       const gassign *stmt,
+				       const program_state &old_state,
+				       program_state *new_state) const;
+  void on_call_for_region_model (const exploded_graph &eg,
+				 const gcall *stmt,
+				 const program_state &old_state,
+				 program_state *new_state) const;
+  void on_return_for_region_model (const exploded_graph &eg,
+				   const greturn *return_,
+				   const program_state &old_state,
+				   program_state *new_state) const;
+
   void copy_sm_state (const exploded_graph &eg,
 		      path_var lhs,
 		      path_var rhs,
 		      const program_state &old_state,
 		      program_state *new_state) const;
 
-  void detect_leaks (const exploded_graph &eg) const;
+  void detect_leaks (exploded_graph &eg) const;
 
   const program_point &get_point () const { return m_ps.m_point; }
   const supernode *get_supernode () const
@@ -668,6 +807,8 @@ class exploded_node : public dnode
 
   const program_state &get_state () const { return m_ps.m_state; }
 
+  const point_and_state *get_key () const { return &m_ps; }
+
 private:
   /* The <program_point, program_state> pair.  This is const, as it
      is immutable once the exploded_node has been created.  */
@@ -696,9 +837,139 @@ class exploded_edge : public dedge
   void dump_dot (graphviz_out *gv) const FINAL OVERRIDE;
 
   //private:
-  exploded_node *m_src;
-  exploded_node *m_dest;
-  const superedge *m_sedge;
+  exploded_node *const m_src;
+  exploded_node *const m_dest;
+  const superedge *const m_sedge;
+};
+
+/* Statistics about aspects of an exploded_graph.  */
+
+struct stats
+{
+  stats (int num_supernodes);
+  void log (logger *logger) const;
+  void dump (FILE *out) const;
+
+  int m_num_nodes[NUM_POINT_KINDS];
+  int m_node_reuse_count;
+  int m_path_vars_purged;
+  int m_num_supernodes;
+};
+
+/* FIXME.  */
+
+struct eg_hash_map_traits
+{
+  typedef const point_and_state *key_type;
+  typedef exploded_node *value_type;
+  typedef exploded_node *compare_type;
+
+  static inline hashval_t hash (const key_type &v)
+  {
+    return v->hash ();
+  }
+  static inline bool equal_keys (const key_type &k1, const key_type &k2)
+  {
+    return *k1 == *k2;
+  }
+  template <typename T>
+  static inline void remove (T &)
+  {
+    /* empty; the nodes are handled elsewhere.  */
+  }
+  template <typename T>
+  static inline void mark_deleted (T &entry)
+  {
+    entry.m_key = reinterpret_cast<key_type> (1);
+  }
+  template <typename T>
+  static inline void mark_empty (T &entry)
+  {
+    entry.m_key = NULL;
+  }
+  template <typename T>
+  static inline bool is_deleted (const T &entry)
+  {
+    return entry.m_key == reinterpret_cast<key_type> (1);
+  }
+  template <typename T>
+  static inline bool is_empty (const T &entry)
+  {
+    return entry.m_key == NULL;
+  }
+};
+
+/* A to-be-emitted diagnostic stored within diagnostic_manager.  */
+
+class saved_diagnostic
+{
+public:
+  saved_diagnostic (const state_machine &sm,
+		    const exploded_node *enode,
+		    const supernode *node, const gimple *stmt,
+		    stmt_finder *stmt_finder,
+		    tree var, state_machine::state_t state,
+		    pending_diagnostic *d);
+  ~saved_diagnostic ();
+
+  bool operator== (const saved_diagnostic &other) const
+  {
+    return (&m_sm == &other.m_sm
+	    /* We don't compare m_enode.  */
+	    && m_node == other.m_node
+	    && m_stmt == other.m_stmt
+	    /* We don't compare m_stmt_finder.  */
+	    && m_var == other.m_var
+	    && m_state == other.m_state
+	    && m_d->equal_p (*other.m_d));
+  }
+
+  //private:
+  const state_machine &m_sm;
+  const exploded_node *m_enode;
+  const supernode *m_node;
+  const gimple *m_stmt;
+  stmt_finder *m_stmt_finder;
+  tree m_var;
+  state_machine::state_t m_state;
+  pending_diagnostic *m_d;
+};
+
+/* A class with responsibility for saving pending diagnostics, so that
+   they can be emitted after the exploded_graph is complete.
+   This lets us de-duplicate diagnostics, and find the shortest path
+   for each similar diagnostic, potentially using edges that might
+   not have been found when each diagnostic was first saved.
+
+   This also lets us compute shortest_paths once, rather than
+   per-diagnostic.  */
+
+class diagnostic_manager : public log_user
+{
+public:
+  diagnostic_manager (logger *logger, int verbosity);
+
+  void add_diagnostic (const state_machine &sm,
+		       const exploded_node *enode,
+		       const supernode *node, const gimple *stmt,
+		       stmt_finder *finder,
+		       tree var, state_machine::state_t state,
+		       pending_diagnostic *d);
+
+  void emit_saved_diagnostics (const exploded_graph &eg);
+  void emit_saved_diagnostic (const exploded_graph &eg,
+			      const saved_diagnostic &sd,
+			      const exploded_path &epath,
+			      const gimple *stmt,
+			      int num_dupes);
+
+  void prune_path (checker4_path *path,
+		   const state_machine &sm,
+		   tree var, state_machine::state_t state) const;
+
+private:
+  auto_delete_vec<saved_diagnostic> m_saved_diagnostics;
+  const int m_verbosity;
 };
 
 /* An exploded_graph is a directed graph of unique <point, state> pairs.
@@ -710,10 +981,13 @@ class exploded_graph : public digraph, public log_user
 public:
   exploded_graph (const supergraph &sg, logger *logger,
 		  const extrinsic_state &ext_state,
+		  const state_purge_map *purge_map,
 		  int verbosity);
+  ~exploded_graph ();
 
   const supergraph &get_supergraph () const { return m_sg; }
   const extrinsic_state &get_ext_state () const { return m_ext_state; }
+  const state_purge_map *get_purge_map () const { return m_purge_map; }
 
   exploded_node *get_origin () const { return m_origin; }
 
@@ -727,23 +1001,33 @@ public:
   void add_edge (exploded_node *src, exploded_node *dest,
 		 const superedge *sedge);
 
-  void emit_diagnostic (const state_machine &sm,
+  void save_diagnostic (const state_machine &sm,
 			const exploded_node *enode,
 			const supernode *node, const gimple *stmt,
 			stmt_finder *finder,
 			tree var, state_machine::state_t state,
-			pending_diagnostic *d) const;
-  void prune_path (checker4_path *path,
-		   const state_machine &sm,
-		   tree var, state_machine::state_t state) const;
+			pending_diagnostic *d);
+
+  diagnostic_manager &get_diagnostic_manager ()
+  {
+    return m_diagnostic_manager;
+  }
 
+  stats *get_global_stats () { return &m_global_stats; }
+  stats *get_or_create_function_stats (function *fn);
   void log_stats () const;
+  void dump_stats (FILE *) const;
+  void dump_states_for_supernode (FILE *, const supernode *snode) const;
 
 private:
   const supergraph &m_sg;
 
-  /* Map from point/state to exploded node.  */
-  typedef hash_map <point_and_state, exploded_node *> map_t;
+  /* Map from point/state to exploded node.
+     To avoid duplication we store point_and_state
+     *pointers* as keys, rather than point_and_state, using the
+     instance from within the exploded_node, with a custom hasher.  */
+  typedef hash_map <const point_and_state *, exploded_node *,
+		    eg_hash_map_traits> map_t;
   map_t m_point_and_state_to_node;
 
   auto_vec<exploded_node *> m_worklist;
@@ -752,11 +1036,15 @@ private:
 
   const extrinsic_state &m_ext_state;
 
-  int m_verbosity;
+  const state_purge_map *const m_purge_map;
+
+  diagnostic_manager m_diagnostic_manager;
 
   /* Stats.  */
-  int m_num_nodes[NUM_POINT_KINDS];
-  int m_node_reuse_count;
+  stats m_global_stats;
+  typedef hash_map<function *, stats *> function_stat_map_t;
+  function_stat_map_t m_per_function_stats;
+  auto_vec<int> m_PK_AFTER_SUPERNODE_per_snode;
 };
 
 /* A path within an exploded_graph: a sequence of edges.  */
@@ -768,6 +1056,8 @@ public:
   exploded_path (const exploded_path &other);
   exploded_path & operator= (const exploded_path &other);
 
+  unsigned length () const { return m_edges.length (); }
+
   bool find_stmt_backwards (const gimple *search_stmt,
 			    int *out_idx) const;
 
@@ -806,6 +1096,8 @@ private:
 class stmt_finder
 {
 public:
+  virtual ~stmt_finder () {}
+  virtual stmt_finder *clone () const = 0;
   virtual const gimple *find_stmt (const exploded_path &epath) = 0;
 };
 
@@ -1322,6 +1614,7 @@ public:
 			   enode->get_stack_depth (),
 			   msg);
     add_event (end_of_path);
+    free (msg);
   }
 
   void prepare_for_emission (pending_diagnostic *pd)
@@ -1462,11 +1755,11 @@ call_string::calc_recursion_depth () const
 /* FIXME.  */
 
 void
-program_point::print (pretty_printer *pp) const
+program_point::print (pretty_printer *pp, const format &f) const
 {
   pp_string (pp, "callstring: ");
   m_call_string.print (pp);
-  pp_newline (pp);
+  f.spacer (pp);
 
   switch (m_kind)
     {
@@ -1475,7 +1768,6 @@ program_point::print (pretty_printer *pp) const
 
     case PK_ORIGIN:
       pp_printf (pp, "origin");
-      pp_newline (pp);
       break;
 
     case PK_BEFORE_SUPERNODE:
@@ -1486,7 +1778,7 @@ program_point::print (pretty_printer *pp) const
 	else
 	  pp_printf (pp, "before SN: %i (NULL from-edge)",
 		     m_supernode->m_index);
-	pp_newline (pp);
+	f.spacer (pp);
 	for (gphi_iterator gpi
 	       = const_cast<supernode *>(get_supernode ())->start_phis ();
 	     !gsi_end_p (gpi); gsi_next (&gpi))
@@ -1494,7 +1786,6 @@ program_point::print (pretty_printer *pp) const
 	    gphi *phi = gpi.phi ();
 	    pp_gimple_stmt_1 (pp, const_cast <gphi *> (phi),
 			      0, (dump_flags_t)0);
-	    pp_newline (pp);
 	  }
       }
       break;
@@ -1502,26 +1793,25 @@ program_point::print (pretty_printer *pp) const
     case PK_BEFORE_STMT:
       pp_printf (pp, "before (SN: %i stmt: %i): ", m_supernode->m_index,
 		 m_stmt_idx);
-      pp_newline (pp);
+      f.spacer (pp);
       pp_gimple_stmt_1 (pp, const_cast <gimple *> (get_stmt ()),
 			0, (dump_flags_t)0);
-      pp_newline (pp);
-      print_source_line (pp);
+      if (f.m_newlines)
+	print_source_line (pp);
       break;
 
     case PK_AFTER_STMT:
       pp_printf (pp, "after (SN: %i stmt: %i): ", m_supernode->m_index,
 		 m_stmt_idx);
-      pp_newline (pp);
+      f.spacer (pp);
       pp_gimple_stmt_1 (pp, const_cast <gimple *> (get_stmt ()),
 			0, (dump_flags_t)0);
-      pp_newline (pp);
-      print_source_line (pp);
+      if (f.m_newlines)
+	print_source_line (pp);
       break;
 
     case PK_AFTER_SUPERNODE:
       pp_printf (pp, "after SN: %i", m_supernode->m_index);
-      pp_newline (pp);
       break;
     }
 }
@@ -1538,6 +1828,10 @@ public:
     show_line_numbers_p = true;
     show_caret = true;
   }
+  ~debug_diagnostic_context ()
+  {
+    diagnostic_finish (this);
+  }
 };
 
 /* FIXME.  */
@@ -1558,10 +1852,10 @@ program_point::print_source_line (pretty_printer *pp) const
 /* FIXME.  */
 
 std::string
-program_point::to_str () const
+program_point::to_str (const format &f) const
 {
   pretty_printer pp;
-  print (&pp);
+  print (&pp, f);
   return std::string (pp_formatted_text (&pp));
 }
 
@@ -1595,19 +1889,21 @@ program_point::get_stmt () const
 
 /* FIXME.  */
 
-sm_state_map *
-sm_state_map::clone () const
+template <typename Expr_T>
+sm_state_map<Expr_T> *
+sm_state_map<Expr_T>::clone () const
 {
   return new sm_state_map (*this);
 }
 
 /* FIXME.  */
 
+template <typename Expr_T>
 void
-sm_state_map::print (const state_machine &sm, pretty_printer *pp) const
+sm_state_map<Expr_T>::print (const state_machine &sm, pretty_printer *pp) const
 {
   pp_string (pp, "{");
-  for (map_t::iterator iter = m_map.begin ();
+  for (typename map_t::iterator iter = m_map.begin ();
        iter != m_map.end ();
        ++iter)
     {
@@ -1626,8 +1922,9 @@ sm_state_map::print (const state_machine &sm, pretty_printer *pp) const
 
 /* FIXME.  */
 
+template <typename Expr_T>
 std::string
-sm_state_map::to_str (const state_machine &sm) const
+sm_state_map<Expr_T>::to_str (const state_machine &sm) const
 {
   pretty_printer pp;
   print (sm, &pp);
@@ -1636,19 +1933,21 @@ sm_state_map::to_str (const state_machine &sm) const
 
 /* FIXME.  */
 
+template <typename Expr_T>
 bool
-sm_state_map::is_empty_p () const
+sm_state_map<Expr_T>::is_empty_p () const
 {
   return m_map.elements () == 0;
 }
 
 /* FIXME.  */
 
+template <typename Expr_T>
 hashval_t
-sm_state_map::hash () const
+sm_state_map<Expr_T>::hash () const
 {
   inchash::hash hstate;
-  for (map_t::iterator iter = m_map.begin ();
+  for (typename map_t::iterator iter = m_map.begin ();
        iter != m_map.end ();
        ++iter)
     {
@@ -1663,13 +1962,14 @@ sm_state_map::hash () const
 
 /* FIXME.  */
 
+template <typename Expr_T>
 bool
-sm_state_map::operator== (const sm_state_map &other) const
+sm_state_map<Expr_T>::operator== (const sm_state_map &other) const
 {
   if (m_map.elements () != other.m_map.elements ())
     return false;
 
-  for (map_t::iterator iter = m_map.begin ();
+  for (typename map_t::iterator iter = m_map.begin ();
        iter != m_map.end ();
        ++iter)
     {
@@ -1686,8 +1986,9 @@ sm_state_map::operator== (const sm_state_map &other) const
 
 /* FIXME.  */
 
+template <typename Expr_T>
 state_machine::state_t
-sm_state_map::get_state (path_var pv) const
+sm_state_map<Expr_T>::get_state (Expr_T pv) const
 {
   if (entry_t *slot
       = const_cast <map_t &> (m_map).get (pv))
@@ -1698,8 +1999,9 @@ sm_state_map::get_state (path_var pv) const
 
 /* FIXME.  */
 
-path_var
-sm_state_map::get_origin (path_var pv) const
+template <typename Expr_T>
+Expr_T
+sm_state_map<Expr_T>::get_origin (Expr_T pv) const
 {
   entry_t *slot
     = const_cast <map_t &> (m_map).get (pv);
@@ -1709,33 +2011,37 @@ sm_state_map::get_origin (path_var pv) const
 
 /* FIXME.  */
 
+template <typename Expr_T>
 void
-sm_state_map::set_state (path_state *ps,
-			 path_var pv,
+sm_state_map<Expr_T>::set_state (path_state *ps,
+			 Expr_T pv,
 			 state_machine::state_t state,
-			 path_var origin)
+			 Expr_T origin)
 {
-  equiv_class &ec = ps->get_equiv_class (pv);
+  psn::equiv_class &ec = ps->get_equiv_class (pv);
   set_state (ec, state, origin);
 }
 
 /* FIXME.  */
 
+template <typename Expr_T>
 void
-sm_state_map::set_state (const equiv_class &ec, state_machine::state_t state,
-			 path_var origin)
+sm_state_map<Expr_T>::set_state (const psn::equiv_class &ec,
+			 state_machine::state_t state,
+			 Expr_T origin)
 {
   int i;
-  path_var *pv;
+  Expr_T *pv;
   FOR_EACH_VEC_ELT (ec.m_vars, i, pv)
     impl_set_state (*pv, state, origin);
 }
 
 /* Set state of PV to STATE, bypassing equivalence classes.  */
 
+template <typename Expr_T>
 void
-sm_state_map::impl_set_state (path_var pv, state_machine::state_t state,
-			      path_var origin)
+sm_state_map<Expr_T>::impl_set_state (Expr_T pv, state_machine::state_t state,
+				      Expr_T origin)
 {
   /* Special-case state 0 as the default value.  */
   if (state == 0)
@@ -1749,24 +2055,99 @@ sm_state_map::impl_set_state (path_var pv, state_machine::state_t state,
 
 /* FIXME.  */
 
+template <typename Expr_T>
 void
-sm_state_map::purge_stack_frame (int callee_stack_depth)
+sm_state_map<Expr_T>::purge_stack_frame (int callee_stack_depth)
 {
   /* FIXME: not sure how to safely remove during a traversal, so for now
      do it in two stages.  */
-  auto_vec <path_var> keys;
-  for (map_t::iterator iter = m_map.begin ();
+  auto_vec <Expr_T> keys;
+  for (typename map_t::iterator iter = m_map.begin ();
        iter != m_map.end ();
        ++iter)
     keys.safe_push ((*iter).first);
 
   int i;
-  path_var *pv;
+  Expr_T *pv;
   FOR_EACH_VEC_ELT (keys, i, pv)
     if (pv->m_stack_depth >= callee_stack_depth)
       m_map.remove (*pv);
 }
 
+/* FIXME.  */
+
+template <>
+void
+sm_state_map<path_var>::purge_for_unknown_fncall (const exploded_graph &eg,
+						  const state_machine &sm,
+						  const gcall *call,
+						  tree fndecl,
+						  path_state *new_ps)
+{
+  if (fndecl)
+    eg.log ("function %qE is unknown to checker %qs",
+	    fndecl, sm.get_name ());
+  else
+    eg.log ("unknown function pointer for checker %qs",
+	    sm.get_name ());
+
+  /* Unknown function body, which could do anything to the
+     states passed to it.
+     Clear any state for the params and any LHS.  */
+  tree iter_param_types = NULL_TREE;
+  if (fndecl)
+    iter_param_types = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+  for (int arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++)
+    {
+      /* Track expected param type, where available.  */
+      if (iter_param_types)
+	{
+	  tree param_type = TREE_VALUE (iter_param_types);
+	  gcc_assert (param_type);
+	  iter_param_types = TREE_CHAIN (iter_param_types);
+
+	  /* Don't purge state if it was passed as a const pointer
+	     e.g. for things like strlen (PTR).  */
+	  if (TREE_CODE (param_type) == POINTER_TYPE)
+	    if (TYPE_READONLY (TREE_TYPE (param_type)))
+	      continue;
+	}
+
+      set_state (new_ps,
+		 new_ps->make_path_var (gimple_call_arg (call, arg_idx)),
+		 0, path_var (NULL_TREE, 0));
+    }
+
+  if (gimple_call_lhs (call))
+    set_state (new_ps,
+	       new_ps->make_path_var (gimple_call_lhs (call)),
+	       0, path_var (NULL_TREE, 0));
+}
+
+template <>
+int
+sm_state_map<path_var>::purge (const state_machine &sm,
+			       const psn::purge_criteria &p)
+{
+  /* FIXME: not sure how to safely remove during a traversal, so for now
+     do it in two stages.  */
+  auto_vec<path_var> to_remove;
+  for (typename map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      if (p.should_purge_p ((*iter).first)
+	  && sm.can_purge_p ((*iter).second.m_state))
+	to_remove.safe_push ((*iter).first);
+    }
+
+  int i;
+  path_var *pv;
+  FOR_EACH_VEC_ELT (to_remove, i, pv)
+    m_map.remove (*pv);
+
+  return to_remove.length ();
+}
 
 ////////////////////////////////////////////////////////////////////////////
 
@@ -1775,7 +2156,7 @@ sm_state_map::purge_stack_frame (int callee_stack_depth)
 class impl_sm_context : public sm_context, public log_user
 {
 public:
-  impl_sm_context (const exploded_graph &eg,
+  impl_sm_context (exploded_graph &eg,
 		   const state_machine &sm,
 		   const exploded_node *node,
 
@@ -1783,8 +2164,8 @@ public:
 		      old state, rather than the new?  */
 		   program_state *new_state,
 
-		   const sm_state_map &old_smap,
-		   sm_state_map *new_smap,
+		   const sm_state_map<path_var> &old_smap,
+		   sm_state_map<path_var> *new_smap,
 
 		   stmt_finder *stmt_finder = NULL)
   : log_user (eg.get_logger ()), m_eg (eg), m_sm (sm), m_node (node),
@@ -1829,18 +2210,20 @@ public:
     state_machine::state_t current = m_old_smap.get_state (pv);
     if (state == current)
       {
-	m_eg.emit_diagnostic (m_sm, m_node, node, stmt, m_stmt_finder,
-			      var, state, d);
+	m_eg.get_diagnostic_manager ().add_diagnostic
+	  (m_sm, m_node, node, stmt, m_stmt_finder,
+	   var, state, d);
       }
-    delete d;
+    else
+      delete d;
   }
 
-  const exploded_graph &m_eg;
+  exploded_graph &m_eg;
   const state_machine &m_sm;
   const exploded_node *m_node;
   program_state *m_new_state;
-  const sm_state_map &m_old_smap;
-  sm_state_map *m_new_smap;
+  const sm_state_map<path_var> &m_old_smap;
+  sm_state_map<path_var> *m_new_smap;
   impl_4::stmt_finder *m_stmt_finder;
 };
 
@@ -1849,29 +2232,27 @@ public:
 /* FIXME.  */
 
 program_state::program_state (const extrinsic_state &ext_state)
-: m_path_state ()
+: m_path_state (),
+  m_region_model (NULL)
 {
-  int num_states = ext_state.m_checkers.length ();
-  for (int i = 0; i < num_states; i++)
-    m_checker_states.safe_push (new sm_state_map ());
-}
-
-/* FIXME.  */
+  if (flag_analyzer_region_model)
+    m_region_model = new region_model ();
 
-program_state::program_state (int num_states)
-: m_path_state ()
-{
+  int num_states = ext_state.m_checkers.length ();
   for (int i = 0; i < num_states; i++)
-    m_checker_states.safe_push (new sm_state_map ());
+    m_checker_states.safe_push (new sm_state_map<path_var> ());
 }
 
 /* FIXME.  */
 
 program_state::program_state (const program_state &other)
-: m_path_state (other.m_path_state)
+: m_path_state (other.m_path_state),
+  m_region_model (other.m_region_model
+		  ? new region_model (*other.m_region_model)
+		  : NULL)
 {
   int i;
-  sm_state_map *smap;
+  sm_state_map<path_var> *smap;
   FOR_EACH_VEC_ELT (other.m_checker_states, i, smap)
     m_checker_states.safe_push (smap->clone ());
 }
@@ -1883,11 +2264,17 @@ program_state::operator= (const program_state &other)
 {
   m_path_state = other.m_path_state;
 
+  delete m_region_model;
+  if (other.m_region_model)
+    m_region_model = new region_model (*other.m_region_model);
+  else
+    m_region_model = NULL;
+
   // TODO: eliminate existing things
   gcc_assert (m_checker_states.length () == 0);
 
   int i;
-  sm_state_map *smap;
+  sm_state_map<path_var> *smap;
   FOR_EACH_VEC_ELT (other.m_checker_states, i, smap)
     m_checker_states.safe_push (smap->clone ());
 
@@ -1900,8 +2287,12 @@ hashval_t
 program_state::hash () const
 {
   hashval_t result = m_path_state.hash ();
+
+  if (m_region_model)
+    result ^= m_region_model->hash ();
+
   int i;
-  sm_state_map *smap;
+  sm_state_map<path_var> *smap;
   FOR_EACH_VEC_ELT (m_checker_states, i, smap)
     result ^= smap->hash ();
   return result;
@@ -1914,15 +2305,26 @@ program_state::operator== (const program_state &other) const
 {
   if (!(m_path_state == other.m_path_state))
     return false;
-  int i;
-  sm_state_map *smap;
-  FOR_EACH_VEC_ELT (m_checker_states, i, smap)
-    if (!(*smap == *other.m_checker_states[i]))
+
+  if (m_region_model)
+    {
+      if (other.m_region_model == NULL)
+	return false;
+      if (!(*m_region_model == *other.m_region_model))
+	return false;
+    }
+  else if (other.m_region_model)
+    return false;
+
+  int i;
+  sm_state_map<path_var> *smap;
+  FOR_EACH_VEC_ELT (m_checker_states, i, smap)
+    if (!(*smap == *other.m_checker_states[i]))
       return false;
   return true;
 }
 
-/* FIXME.  */
+/* Print a compact representation of this state to PP.  */
 
 void
 program_state::print (const extrinsic_state &ext_state,
@@ -1931,8 +2333,15 @@ program_state::print (const extrinsic_state &ext_state,
   pp_printf (pp, "ps: %s", m_path_state.to_str ().c_str ());
   pp_newline (pp);
 
+  if (m_region_model)
+    {
+      pp_printf (pp, "rmodel: ");
+      m_region_model->print (pp);
+      pp_newline (pp);
+    }
+
   int i;
-  sm_state_map *smap;
+  sm_state_map<path_var> *smap;
   FOR_EACH_VEC_ELT (m_checker_states, i, smap)
     {
       if (!smap->is_empty_p ())
@@ -1950,10 +2359,100 @@ std::string
 program_state::to_str (const extrinsic_state &ext_state) const
 {
   pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
   print (ext_state, &pp);
   return std::string (pp_formatted_text (&pp));
 }
 
+/* Dump a multiline representation of this state to PP.  */
+
+void
+program_state::dump (const extrinsic_state &ext_state,
+		     pretty_printer *pp) const
+{
+  // FIXME: multiline format?
+  pp_printf (pp, "ps: %s", m_path_state.to_str ().c_str ());
+  pp_newline (pp);
+
+  if (m_region_model)
+    {
+      pp_printf (pp, "rmodel: ");
+      m_region_model->dump_to_pp (pp);
+    }
+
+  int i;
+  sm_state_map<path_var> *smap;
+  FOR_EACH_VEC_ELT (m_checker_states, i, smap)
+    {
+      if (!smap->is_empty_p ())
+	{
+	  pp_printf (pp, "%s: ", ext_state.get_name (i));
+	  smap->print (ext_state.get_sm (i), pp);
+	  pp_newline (pp);
+	}
+    }
+}
+
+/* Implementation of purge_criteria for using state_purge_map.
+
+   Purge state within a path_state using PURGE_MAP for POINT.
+   Use the sm-states within OLD_STATE to avoid purging path_vars
+   that have any sm-state.  */
+
+class purge_at_point_criteria : public psn::purge_criteria
+{
+public:
+  purge_at_point_criteria (const program_point &point,
+			   const state_purge_map *purge_map,
+			   const extrinsic_state &ext_state,
+			   const program_state &old_state)
+  : m_point (point),
+    m_flat_point (point),
+    m_purge_map (purge_map),
+    m_ext_state (ext_state),
+    m_old_state (old_state)
+  {
+    m_flat_point.m_call_string = call_string ();
+  }
+
+  bool should_purge_p (path_var pv) const FINAL OVERRIDE
+  {
+    if (TREE_CODE (pv.m_tree) != SSA_NAME)
+      return false;
+    if (pv.m_stack_depth != m_point.get_stack_depth ())
+      return false;
+
+    if (!m_purge_map)
+      return false;
+
+    const state_purge_per_ssa_name &per_var_data
+      = m_purge_map->get_data_for_ssa_name (pv.m_tree);
+    if (!per_var_data.needed_at_point_p (m_flat_point))
+      {
+	/* Don't purge vars that have sm state, to avoid
+	   losing leak complaints.  */
+	int i;
+	sm_state_map<path_var> *smap;
+	FOR_EACH_VEC_ELT (m_old_state.m_checker_states, i, smap)
+	  {
+	    const state_machine &sm = m_ext_state.get_sm (i);
+	    if (!sm.can_purge_p (smap->get_state (pv)))
+	      return false;
+	  }
+
+	return true;
+      }
+    return false;
+  }
+
+private:
+  const program_point &m_point;
+  program_point m_flat_point;
+  const state_purge_map *m_purge_map;
+  const extrinsic_state &m_ext_state;
+  const program_state &m_old_state;
+};
+
 /* FIXME.
    Generate a simpler version of THIS, discarding state that's no longer
    relevant at POINT.
@@ -1962,36 +2461,118 @@ program_state::to_str (const extrinsic_state &ext_state) const
    irrelevant state (e.g. at the end of functions).  */
 
 program_state
-program_state::prune_for_point (const exploded_graph &eg,
+program_state::prune_for_point (exploded_graph &eg,
 				const program_point &point) const
 {
-  /* FIXME: for now a hack: purge all states at exit nodes (which won't work
-     interprocedurally).  */
-#if 0
-  if (const supernode *supernode = point.get_supernode ())
-    if (supernode->return_p ())
-      return program_state (eg.m_ext_state);
-#endif
+  function *fun = point.get_function ();
+  if (!fun)
+    return *this;
+
+  /* The points in the state map don't have call strings, so flatten
+     accordingly.  */
+  program_point flat_point (point);
+  flat_point.m_call_string = call_string ();
+
+  program_state new_state (*this);
+
+  purge_at_point_criteria criteria (point, eg.get_purge_map (),
+				    eg.get_ext_state (),
+				    *this);
+  int num_purged = new_state.m_path_state.purge (criteria);
+  eg.log ("path_vars purged: %i", num_purged);
 
-  // FIXME: what about leak detection, though?
+  eg.get_global_stats ()->m_path_vars_purged++;
+  stats *per_fn_stats = eg.get_or_create_function_stats (point.get_function ());
+  per_fn_stats->m_path_vars_purged++;
+
+  // FIXME: state maps also
+  int i;
+  sm_state_map<path_var> *smap;
+  int num_sm_purged = 0;
+  FOR_EACH_VEC_ELT (new_state.m_checker_states, i, smap)
+    {
+      const state_machine &sm = eg.get_ext_state ().get_sm (i);
+      num_sm_purged += smap->purge (sm, criteria);
+    }
+  eg.log ("num_sm_purged: %i", num_sm_purged);
 
+  if (new_state.m_region_model)
+    {
 #if 0
-  {
-    function *fun = point.get_function ();
-    if (fun)
-      {
-	auto_cfun sentinel (fun);
-	debug_immediate_uses ();
-	/* FIXME: maybe do the pruning here: we only know what state will be
-	   important when in conjunction with a program_point.  */
-      }
-  }
+      if (eg.get_logger ())
+	new_state.m_region_model->dump_to_pp (eg.get_logger_pp ());
 #endif
+      purge_stats stats;
+
+      const state_purge_map *pm = eg.get_purge_map ();
+      if (pm)
+	{
+	  region_id_set purgeable_ssa_regions (new_state.m_region_model);
+	  region_id frame_rid
+	    = new_state.m_region_model->get_current_frame_id ();
+	  frame_region *frame
+	    = new_state.m_region_model->get_region <frame_region>(frame_rid);
+
+	  /* FIXME: move to a member of region_model, once the state_purge_map
+	     etc decls are visible there.  */
+
+	  auto_vec<tree> ssa_names_to_purge;
+	  for (frame_region::map_t::iterator iter = frame->m_map.begin ();
+	       iter != frame->m_map.end ();
+	       ++iter)
+	    {
+	      tree var = (*iter).first;
+	      region_id rid = (*iter).second;
+	      if (TREE_CODE (var) == SSA_NAME)
+		{
+		  const state_purge_per_ssa_name &per_ssa
+		    = pm->get_data_for_ssa_name (var);
+		  if (!per_ssa.needed_at_point_p (flat_point))
+		    {
+		      purgeable_ssa_regions.add_region (rid);
+		      ssa_names_to_purge.safe_push (var);
+		      eg.log ("purging RID: %i for %qE", rid.as_int (), var);
+		      // FIXME: we also need to remove the region from the map
+		      // note that we're mid-traversal: this is done in unbind
+		      // below
+		    }
+		}
+	    }
 
+	  /* Unbind the regions from the frame's map of vars-to-regions.  */
+	  tree var;
+	  FOR_EACH_VEC_ELT (ssa_names_to_purge, i, var)
+	    frame->unbind (var);
 
-  return *this;
+	  /* Purge the regions.  Nothing should point to them, and they
+	     should have no children, as they are for SSA names.  */
+	  new_state.m_region_model->purge_regions (purgeable_ssa_regions,
+						   &stats);
+	}
+
+      /* Purge unused svalues.  */
+      new_state.m_region_model->purge_svalues (&stats);
+
+      eg.log ("num svalues purged: %i", stats.m_num_svalues);
+      eg.log ("num regions purged: %i", stats.m_num_regions);
+      eg.log ("num equiv_classes purged: %i", stats.m_num_equiv_classes);
+      eg.log ("num constraints purged: %i", stats.m_num_constraints);
+    }
+
+  return new_state;
+}
+
+/* FIXME.  */
+
+void
+program_state::validate () const
+{
+  m_path_state.validate ();
+  if (m_region_model)
+    m_region_model->validate ();
 }
 
+
 ////////////////////////////////////////////////////////////////////////////
 
 /* FIXME.  */
@@ -2010,7 +2591,8 @@ exploded_node::dump_dot (graphviz_out *gv) const
   pp_printf (pp, "EN: %i", m_index);
   pp_newline (pp);
 
-  m_ps.m_point.print (pp);
+  format f (true);
+  m_ps.m_point.print (pp, f);
   pp_newline (pp);
 
   m_ps.m_state.print (m_ext_state, pp);
@@ -2121,10 +2703,63 @@ exploded_node::dump_dot_id (pretty_printer *pp) const
   pp_printf (pp, "exploded_node_%i", m_index);
 }
 
+/* Dump a multiline representation of this node to PP.  */
+
+void
+exploded_node::dump_to_pp (pretty_printer *pp) const
+{
+  pp_printf (pp, "EN: %i", m_index);
+  pp_newline (pp);
+
+  format f (true);
+  m_ps.m_point.print (pp, f);
+  pp_newline (pp);
+
+  m_ps.m_state.dump (m_ext_state, pp);
+  pp_newline (pp);
+}
+
+/* Dump a multiline representation of this node to FILE.  */
+
+void
+exploded_node::dump (FILE *fp) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp.buffer->stream = fp;
+  dump_to_pp (&pp);
+  pp_flush (&pp);
+}
+
+/* Dump a multiline representation of this node to stderr.  */
+
+DEBUG_FUNCTION void
+exploded_node::dump () const
+{
+  dump (stderr);
+}
+
+/* FIXME.  */
+// is there a pre-canned way to do this?
+
+static bool
+fndecl_has_gimple_body_p (tree fndecl)
+{
+  if (fndecl == NULL_TREE)
+    return false;
+
+  cgraph_node *n = cgraph_node::get (fndecl);
+  if (!n)
+    return false;
+
+  return n->has_gimple_body_p ();
+}
+
 /* FIXME.  */
 
 program_state
-exploded_node::on_stmt (const exploded_graph &eg) const
+exploded_node::on_stmt (exploded_graph &eg) const
 {
   const program_point &point = m_ps.m_point;
   const program_state &old_state = m_ps.m_state;
@@ -2136,16 +2771,36 @@ exploded_node::on_stmt (const exploded_graph &eg) const
   const gimple *stmt = point.get_stmt ();
   int stack_depth = point.get_stack_depth ();
 
-  int i;
-  sm_state_map *smap;
-  FOR_EACH_VEC_ELT (old_state.m_checker_states, i, smap)
+  /* Update cfun and input_location in case of ICE: make it easier to
+     track down which source construct we're failing to handle.  */
+  auto_cfun sentinel (point.get_function ());
+  input_location = stmt->location;
+
+  int sm_idx;
+  sm_state_map<path_var> *smap;
+  FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap)
     {
-      const state_machine &sm = m_ext_state.get_sm (i);
+      const state_machine &sm = m_ext_state.get_sm (sm_idx);
+      const sm_state_map<path_var> &old_smap
+	= *old_state.m_checker_states[sm_idx];
+      sm_state_map<path_var> *new_smap = new_state.m_checker_states[sm_idx];
       impl_sm_context ctxt (eg, sm, this,
 			    &new_state,
-			    *old_state.m_checker_states[i],
-			    new_state.m_checker_states[i]);
-      sm.on_stmt (&ctxt, point.m_supernode, stmt);
+			    old_smap, new_smap);
+      /* Allow the state_machine to handle the stmt.  */
+      if (!sm.on_stmt (&ctxt, point.m_supernode, stmt))
+	{
+	  /* For those stmts that were not handled by the state machine.  */
+	  if (const gcall *call = dyn_cast <const gcall *> (stmt))
+	    {
+	      tree callee_fndecl = gimple_call_fndecl (call);
+	      // TODO: maybe we can be smarter about handling function pointers?
+
+	      if (!fndecl_has_gimple_body_p (callee_fndecl))
+		new_smap->purge_for_unknown_fncall (eg, sm, call, callee_fndecl,
+						    new_ps);
+	    }
+	}
     }
 
   if (const gassign *assign = dyn_cast <const gassign *> (stmt))
@@ -2181,9 +2836,9 @@ exploded_node::on_stmt (const exploded_graph &eg) const
 	    /* If we see a comparison, evaluate it based on what we know along
 	       this path, and record the result, if known.  */
 	    tree rhs2 = gimple_assign_rhs2 (assign);
-	    equiv_class_id rhs1_ec_id
+	    psn::equiv_class_id rhs1_ec_id
 	      = new_ps->get_or_add_equiv_class (path_var (rhs1, stack_depth));
-	    equiv_class_id rhs2_ec_id
+	    psn::equiv_class_id rhs2_ec_id
 	      = new_ps->get_or_add_equiv_class (path_var (rhs2, stack_depth));
 	    tristate t = new_ps->eval_condition (rhs1_ec_id, op, rhs2_ec_id);
 	    if (eg.get_logger ())
@@ -2208,6 +2863,9 @@ exploded_node::on_stmt (const exploded_graph &eg) const
 	  }
 	  break;
 	}
+
+      /* TODO: working on having region model handle all assignments.  */
+      on_assignment_for_region_model (eg, assign, old_state, &new_state);
     }
 
   if (const greturn *return_ = dyn_cast <const greturn *> (stmt))
@@ -2221,29 +2879,46 @@ exploded_node::on_stmt (const exploded_graph &eg) const
 		       new_ps->make_path_var (rhs),
 		       old_state,
 		       &new_state);
+      on_return_for_region_model (eg, return_, old_state, &new_state);
     }
 
   if (const gcall *call = dyn_cast <const gcall *> (stmt))
     {
       /* Debugging/test support.  */
       if (is_named_call_p (call, "__dump_constraints", 0))
-	inform (stmt->location, "__dump_constraints: %s",
-		new_ps->to_str ().c_str ());
-
-      /* Handle the builtin "__analyzer_eval" by evaluating the input
-	 and dumping as a dummy warning, so that test cases can use dg-warning
-	 to validate the result (and so unexpected warnings will lead to DejaGnu
-	 failures.  */
-      if (is_named_call_p (call, "__analyzer_eval", 1))
 	{
+	  inform (stmt->location, "__dump_constraints: %s",
+		  new_ps->to_str ().c_str ());
+	}
+      else if (is_named_call_p (call, "__analyzer_dump", 0))
+	{
+	  /* Handle the builtin "__analyzer_dump" by dumping state
+	     to stderr.  */
+	  dump ();
+	}
+      else if (is_named_call_p (call, "__analyzer_eval", 1))
+	{
+	  /* Handle the builtin "__analyzer_eval" by evaluating the input
+	     and dumping as a dummy warning, so that test cases can use dg-warning
+	     to validate the result (and so unexpected warnings will lead to DejaGnu
+	     failures.  */
 	  tree t_arg = gimple_call_arg (call, 0);
 	  tristate t
-	    = new_ps->dump_condition (t_arg, NE_EXPR, integer_zero_node);
+	    = (new_state.m_region_model
+	       ? new_state.m_region_model->eval_condition (t_arg,
+							   NE_EXPR,
+							   integer_zero_node)
+	       : new_ps->dump_condition (t_arg, NE_EXPR, integer_zero_node));
 	  warning_at (call->location, 0, "%s", t.as_string ());
 	}
+      else if (is_named_call_p (call, "__analyzer_break", 0))
+	{
+	  raise (SIGINT); // FIXME: cross-platform way to do this?
+	  //gcc_unreachable (); // FIXME: compare to a hard breakpoint
+	}
+      on_call_for_region_model (eg, call, old_state, &new_state);
     }
 
-
   return new_state;
 }
 
@@ -2261,11 +2936,54 @@ exploded_node::on_assignment (const exploded_graph &eg,
       eg.log ("rhs: %s", rhs.to_str ().c_str ());
     }
 
+#if 0
+  if (TREE_CODE (lhs.m_tree) != SSA_NAME
+      && TREE_CODE (lhs.m_tree) != RESULT_DECL
+      && TREE_CODE (lhs.m_tree) != VAR_DECL)
+    return;
+#endif
+
   new_state->m_path_state.on_assignment (lhs, rhs);
 
   copy_sm_state (eg, lhs, rhs, old_state, new_state);
 }
 
+/* FIXME.  */
+
+void
+exploded_node::on_assignment_for_region_model (const exploded_graph &eg,
+					       const gassign *stmt,
+					       const program_state &old_state,
+					       program_state *new_state) const
+{
+  if (new_state->m_region_model)
+    new_state->m_region_model->on_assignment (stmt);
+}
+
+/* FIXME.  */
+
+void
+exploded_node::on_call_for_region_model (const exploded_graph &eg,
+					 const gcall *stmt,
+					 const program_state &old_state,
+					 program_state *new_state) const
+{
+  if (new_state->m_region_model)
+    new_state->m_region_model->on_call (stmt);
+}
+
+/* FIXME.  */
+
+void
+exploded_node::on_return_for_region_model (const exploded_graph &eg,
+					   const greturn *stmt,
+					   const program_state &old_state,
+					   program_state *new_state) const
+{
+  if (new_state->m_region_model)
+    new_state->m_region_model->on_return (stmt);
+}
+
 /* Copy SM state (if any) from RHS to LHS.  */
 
 void
@@ -2289,8 +3007,8 @@ exploded_node::copy_sm_state (const exploded_graph &eg,
   for (unsigned i = 0; i < ext_state.m_checkers.length (); i++)
     {
       const state_machine &sm = ext_state.get_sm (i);
-      const sm_state_map *src_smap = old_state.m_checker_states[i];
-      sm_state_map *dst_smap = new_state->m_checker_states[i];
+      const sm_state_map<path_var> *src_smap = old_state.m_checker_states[i];
+      sm_state_map<path_var> *dst_smap = new_state->m_checker_states[i];
       state_machine::state_t rhs_state = src_smap->get_state (rhs);
       dst_smap->set_state (&new_state->m_path_state, lhs, rhs_state, rhs);
       if (eg.get_logger ())
@@ -2319,7 +3037,7 @@ exploded_node::copy_sm_state (const exploded_graph &eg,
    Update NEXT_POINT accordingly (updating the call string).  */
 
 bool
-exploded_node::on_edge (const exploded_graph &eg,
+exploded_node::on_edge (exploded_graph &eg,
 			const superedge *succ,
 			program_point *next_point,
 			program_state *next_state) const
@@ -2392,6 +3110,11 @@ public:
   leak_stmt_finder (const exploded_graph &eg, path_var pv)
   : m_eg (eg), m_pv (pv) {}
 
+  stmt_finder *clone () const FINAL OVERRIDE
+  {
+    return new leak_stmt_finder (m_eg, m_pv);
+  }
+
   const gimple *find_stmt (const exploded_path &epath)
     FINAL OVERRIDE
   {
@@ -2475,7 +3198,7 @@ private:
    for top-level functions.  */
 
 void
-exploded_node::detect_leaks (const exploded_graph &eg) const
+exploded_node::detect_leaks (exploded_graph &eg) const
 {
   JIT_LOG_FUNC_1 (eg.get_logger (), "EN: %i", m_index);
 
@@ -2495,7 +3218,7 @@ exploded_node::detect_leaks (const exploded_graph &eg) const
       {
 	if (eg.get_logger ())
 	  eg.log ("new_ps before escapes: %s", new_ps->to_str ().c_str ());
-	equiv_class_id ec_id
+	psn::equiv_class_id ec_id
 	  = new_ps->get_or_add_equiv_class (path_var (result, stack_depth));
 	new_ps->recursively_mark_as_escaping (ec_id);
 	if (eg.get_logger ())
@@ -2504,7 +3227,7 @@ exploded_node::detect_leaks (const exploded_graph &eg) const
   }
 
   int i;
-  equiv_class *ec;
+  psn::equiv_class *ec;
   FOR_EACH_VEC_ELT (new_ps->m_equiv_classes, i, ec)
     {
       if (eg.get_logger ())
@@ -2543,7 +3266,7 @@ exploded_node::detect_leaks (const exploded_graph &eg) const
       leak_stmt_finder stmt_finder (eg, best_pv);
 
       int i;
-      sm_state_map *smap;
+      sm_state_map<path_var> *smap;
       FOR_EACH_VEC_ELT (old_state.m_checker_states, i, smap)
 	{
 	  const state_machine &sm = m_ext_state.get_sm (i);
@@ -2564,7 +3287,7 @@ exploded_node::detect_leaks (const exploded_graph &eg) const
    in the exploded graph.  */
 
 bool
-program_point::on_edge (const exploded_graph &eg,
+program_point::on_edge (exploded_graph &eg,
 			const superedge *succ)
 {
   JIT_LOG_FUNC (eg.get_logger ());
@@ -2626,7 +3349,7 @@ program_point::on_edge (const exploded_graph &eg,
 /* FIXME.  */
 
 bool
-program_state::on_edge (const exploded_graph &eg,
+program_state::on_edge (exploded_graph &eg,
 			const exploded_node &enode,
 			const superedge *succ)
 {
@@ -2648,6 +3371,15 @@ program_state::on_edge (const exploded_graph &eg,
       return false;
     }
 
+  if (m_region_model)
+    if (!m_region_model->apply_constraints_for_edge (*succ,
+						     last_stmt))
+      {
+	eg.log ("edge to SN: %i is impossible due to region_model constraints",
+		succ->m_dest->m_index);
+	return false;
+      }
+
   /* FIXME: the above is setting ECs for args/params on calls, but not
      updating states.  should we call on_assignment instead.
      Also, where should it happen for return values at returns?
@@ -2726,7 +3458,7 @@ program_state::on_edge (const exploded_graph &eg,
   /* Pass the edge to the various state machines to see if any transitions
      or warnings occur.  */
   int i;
-  sm_state_map *old_smap;
+  sm_state_map<path_var> *old_smap;
   FOR_EACH_VEC_ELT (old_state.m_checker_states, i, old_smap)
     {
       const state_machine &sm = eg.get_ext_state ().get_sm (i);
@@ -2817,282 +3549,380 @@ exploded_edge::dump_dot (graphviz_out *gv) const
   pp_printf (pp, "\"];\n");
 }
 
-
 ////////////////////////////////////////////////////////////////////////////
 
-/* exploded_graph's ctor.  */
+/* struct stats.  */
 
-exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
-				const extrinsic_state &ext_state,
-				int verbosity)
-: log_user (logger), m_sg (sg), m_ext_state (ext_state),
-  m_verbosity (verbosity),
-  m_node_reuse_count (0)
+/* FIXME.  */
+
+stats::stats (int num_supernodes)
+: m_node_reuse_count (0),
+  m_path_vars_purged (0),
+  m_num_supernodes (num_supernodes)
 {
   for (int i = 0; i < NUM_POINT_KINDS; i++)
     m_num_nodes[i] = 0;
-
-  m_origin = get_or_create_node (program_point (NULL, NULL, 0, PK_ORIGIN,
-						call_string ()),
-				 program_state (ext_state));
 }
 
-/* Ensure that there is an exploded_node representing an external call to
-   FUN, adding it to the worklist if creating it.
-
-   Add an edge from the origin exploded_node to the function entrypoint
-   exploded_node.  */
+/* FIXME.  */
 
 void
-exploded_graph::add_function_entry (function *fun)
+stats::log (logger *logger) const
 {
-  program_point point = program_point::from_function_entry (m_sg, fun);
-  program_state state (m_ext_state);
-
-  add_edge (m_origin, get_or_create_node (point, state), NULL);
+  gcc_assert (logger);
+  for (int i = 0; i < NUM_POINT_KINDS; i++)
+    logger->log ("m_num_nodes[%s]: %i",
+		 point_kind_to_string (static_cast <enum point_kind> (i)),
+		 m_num_nodes[i]);
+  logger->log ("m_node_reuse_count: %i", m_node_reuse_count);
+  logger->log ("m_path_vars_purged: %i", m_path_vars_purged);
 }
 
-/* Get or create an exploded_node for (POINT, STATE).
-   If a new node is created, it is added to the worklist.  */
+/* FIXME.  */
 
-exploded_node *
-exploded_graph::get_or_create_node (const program_point &point,
-				    const program_state &state)
+void
+stats::dump (FILE *out) const
 {
-  JIT_LOG_FUNC (get_logger ());
+  for (int i = 0; i < NUM_POINT_KINDS; i++)
+    fprintf (out, "m_num_nodes[%s]: %i\n",
+	     point_kind_to_string (static_cast <enum point_kind> (i)),
+	     m_num_nodes[i]);
+  fprintf (out, "m_node_reuse_count: %i\n", m_node_reuse_count);
+  fprintf (out, "m_path_vars_purged: %i\n", m_path_vars_purged);
+
+  if (m_num_supernodes > 0)
+    fprintf (out, "PK_AFTER_SUPERNODE nodes per supernode: %.2f\n",
+	     (float)m_num_nodes[PK_AFTER_SUPERNODE] / (float)m_num_supernodes);
+}
 
-  /* Prune state to try to improve the chances of a cache hit,
-     avoiding generating redundant nodes.  */
-  program_state pruned_state = state.prune_for_point (*this, point);
+////////////////////////////////////////////////////////////////////////////
 
-  point_and_state ps (point, pruned_state);
-  if (exploded_node **slot = m_point_and_state_to_node.get (ps))
-    {
-      /* An exploded_node for PS already exists.  */
-      log ("reused EN: %i", (*slot)->m_index);
-      m_node_reuse_count++;
-      return *slot;
-    }
+/* class saved_diagnostic.  */
 
-  /* An exploded_node for PS doesn't already exist; create one.  */
-  exploded_node *node = new exploded_node (ps, m_ext_state, m_nodes.length ());
-  add_node (node);
-  m_point_and_state_to_node.put (ps, node);
-  ++m_num_nodes[node->get_point ().m_kind];
+/* FIXME.  */
 
-  if (get_logger ())
-    {
-      log ("created EN: %i", node->m_index);
-      log ("point: %s",
-	   point.to_str ().c_str ());
-      log ("pruned_state: %s",
-	   pruned_state.to_str (m_ext_state).c_str ());
-    }
+saved_diagnostic::saved_diagnostic (const state_machine &sm,
+				    const exploded_node *enode,
+				    const supernode *node, const gimple *stmt,
+				    stmt_finder *stmt_finder,
+				    tree var, state_machine::state_t state,
+				    pending_diagnostic *d)
+: m_sm (sm), m_enode (enode), m_node (node), m_stmt (stmt),
+ /* stmt_finder could be on-stack; we want our own copy that can
+    outlive that.  */
+  m_stmt_finder (stmt_finder ? stmt_finder->clone () : NULL),
+  m_var (var), m_state (state),
+  m_d (d)
+{
+}
 
-  /* Add the new node to the worlist.  */
-  m_worklist.safe_push (node);
-  return node;
+/* FIXME.  */
+
+saved_diagnostic::~saved_diagnostic ()
+{
+  delete m_stmt_finder;
+  delete m_d;
 }
 
-/* Add an exploded_edge from SRC to DEST, recording its association
-   with SEDGE (which may be NULL).  */
+////////////////////////////////////////////////////////////////////////////
 
-void
-exploded_graph::add_edge (exploded_node *src, exploded_node *dest,
-			  const superedge *sedge)
+/* class diagnostic_manager.  */
+
+/* FIXME.  */
+
+diagnostic_manager::diagnostic_manager (logger *logger, int verbosity)
+: log_user (logger), m_verbosity (verbosity)
 {
-  exploded_edge *e = new exploded_edge (src, dest, sedge);
-  digraph::add_edge (e);
-  src->m_succs.safe_push (e);
 }
 
-/* The main loop of the analysis.
-   Pop freshly-created exploded_nodes from the worklist, calling
-   process_node on them to explore the <point, state> graph.
-   Add edges to their successors, potentially creating new successors
-   (which are also added to the worklist).  */
+/* FIXME.  */
 
 void
-exploded_graph::process_worklist ()
+diagnostic_manager::add_diagnostic (const state_machine &sm,
+				    const exploded_node *enode,
+				    const supernode *node, const gimple *stmt,
+				    stmt_finder *finder,
+				    tree var, state_machine::state_t state,
+				    pending_diagnostic *d)
 {
-  while (m_worklist.length () > 0)
-    {
-      exploded_node *node = m_worklist.pop ();
-      process_node (node);
-
-      /* FIXME: make this less ad-hoc?  e.g. a param */
-      /* we use exit rather than entry since there can be multiple
-	 entry ENs, one per phi; the number of PK_AFTER_SUPERNODE ought
-	 to be equivalent to the number of supernodes multiplied by the
-	 number of states.  */
-      if (m_num_nodes[PK_AFTER_SUPERNODE]> m_sg.num_nodes () * 5)
-	{
-	  log ("bailing out; too many nodes");
-	  sorry ("analysis bailed out early");
-	  return;
-	}
-    }
+  // TODO: potentially do them directly, vs saved
+  saved_diagnostic *sd
+    = new saved_diagnostic (sm, enode, node, stmt, finder, var, state, d);
+  m_saved_diagnostics.safe_push (sd);
+  if (get_logger ())
+    log ("adding saved diagnostic %i at SN %i: %qs",
+	 m_saved_diagnostics.length () - 1,
+	 node->m_index, d->get_kind ());
 }
 
-/* The core of exploded_graph::process_worklist (the main analysis loop),
-   handling one node in the worklist.
+/* FIXME.  */
+/* we want to find the simplest dedupe_candidate amongst those that share a
+   dedupe_key.  */
 
-   Get successor <point, state> pairs for NODE, calling get_or_create on
-   them, and adding an exploded_edge to each successors.
+class dedupe_key
+{
+public:
+  dedupe_key (const saved_diagnostic &sd,
+	      const exploded_path &epath)
+  : m_sd (sd), m_stmt (sd.m_stmt)
+  {
+    /* Support deferring the choice of stmt until after an emission path has
+     been built, using an optional stmt_finder.  */
+    if (m_stmt == NULL)
+      {
+	gcc_assert (sd.m_stmt_finder);
+	m_stmt = sd.m_stmt_finder->find_stmt (epath);
+      }
+    gcc_assert (m_stmt);
+  }
 
-   Freshly-created nodes will be added to the worklist.  */
+  hashval_t hash () const
+  {
+    inchash::hash hstate;
+    hstate.add_ptr (m_stmt);
+    // TODO: m_sd
+    return hstate.end ();
+  }
+  bool operator== (const dedupe_key &other) const
+  {
+    return (m_sd == other.m_sd
+	    && m_stmt == other.m_stmt);
+  }
 
-void
-exploded_graph::process_node (exploded_node *node)
+  location_t get_location () const
+  {
+    return m_stmt->location;
+  }
+
+  /* A qsort comparator for use by dedupe_winners::emit_best
+     to sort them into location_t order.  */
+
+  static int
+  comparator (const void *p1, const void *p2)
+  {
+    const dedupe_key *pk1 = *(const dedupe_key * const *)p1;
+    const dedupe_key *pk2 = *(const dedupe_key * const *)p2;
+
+    location_t loc1 = pk1->get_location ();
+    location_t loc2 = pk2->get_location ();
+
+    return linemap_compare_locations (line_table, loc2, loc1);
+  }
+
+  // the pending_diagnostic
+  // the (stmt (non-NULL), var, state)
+  const saved_diagnostic &m_sd;
+  const gimple *m_stmt;
+};
+
+/* FIXME.  */
+class dedupe_candidate
 {
-  JIT_LOG_FUNC_1 (get_logger (), "EN: %i", node->m_index);
+public:
+  // has the exploded_path
+  dedupe_candidate  (const shortest_paths &sp,
+		     const saved_diagnostic &sd)
+  : m_epath (sp.get_shortest_path (sd.m_enode)),
+    m_num_dupes (0)
+  {
+  }
 
-  const program_point &point = node->get_point ();
-  const program_state &state = node->get_state ();
-  if (get_logger ())
-    {
-      log ("point: %s, state: %s",
-	   point.to_str ().c_str (),
-	   state.to_str (m_ext_state).c_str ());
-    }
+  unsigned length () const { return m_epath.length (); }
+  const exploded_path &get_path () const { return m_epath; }
 
-  switch (point.m_kind)
-    {
-    default:
-      gcc_unreachable ();
-    case PK_ORIGIN:
-      /* This node exists to simplify finding the shortest path
-	 to an exploded_node.  */
-      break;
+private:
+  exploded_path m_epath;
+public:
+  int m_num_dupes;
+};
 
-    case PK_BEFORE_SUPERNODE:
-      {
-	program_state next_state (state);
+/* FIXME.  */
 
-	for (gphi_iterator gpi
-	       = const_cast<supernode *>(node->get_supernode ())->start_phis ();
-	     !gsi_end_p (gpi); gsi_next (&gpi))
-	  {
-	    gphi *phi = gpi.phi ();
+class dedupe_hash_map_traits
+{
+public:
+  typedef const dedupe_key *key_type;
+  typedef dedupe_candidate *value_type;
+  typedef dedupe_candidate *compare_type;
 
-	    const cfg_superedge *last_cfg_superedge
-	      = point.m_from_edge->dyn_cast_cfg_superedge ();
-	    gcc_assert (last_cfg_superedge);
-	    // FIXME: this ought to be an as_a<>
+  static inline hashval_t hash (const key_type &v)
+  {
+    return v->hash ();
+  }
+  static inline bool equal_keys (const key_type &k1, const key_type &k2)
+  {
+    return *k1 == *k2;
+  }
+  template <typename T>
+  static inline void remove (T &)
+  {
+    // TODO
+  }
+  template <typename T>
+  static inline void mark_deleted (T &entry)
+  {
+    entry.m_key = reinterpret_cast<key_type> (1);
+  }
+  template <typename T>
+  static inline void mark_empty (T &entry)
+  {
+    entry.m_key = NULL;
+  }
+  template <typename T>
+  static inline bool is_deleted (const T &entry)
+  {
+    return entry.m_key == reinterpret_cast<key_type> (1);
+  }
+  template <typename T>
+  static inline bool is_empty (const T &entry)
+  {
+    return entry.m_key == NULL;
+  }
 
-	    tree src = last_cfg_superedge->get_phi_arg (phi);
-	    tree lhs = gimple_phi_result (phi);
+};
 
-	    /* Update next_state based on phi.  */
-	    node->on_assignment (*this,
-				 point.make_path_var (lhs),
-				 point.make_path_var (src),
-				 state,
-				 &next_state);
-	  }
+/* A map from dedupe_key to best current dedupe_candidate.  */
 
-	if (point.m_supernode->m_stmts.length () > 0)
-	  {
-	    program_point next_point
-	      = program_point::before_stmt (point.m_supernode, 0,
-					    point.m_call_string);
-	    exploded_node *next
-	      = get_or_create_node (next_point, next_state);
-	    add_edge (node, next, NULL);
-	  }
-	else
-	  {
-	    program_point next_point
-	      = program_point::after_supernode (point.m_supernode,
-						point.m_call_string);
-	    exploded_node *next = get_or_create_node (next_point, next_state);
-	    add_edge (node, next, NULL);
-	  }
-      }
-      break;
-    case PK_BEFORE_STMT:
+class dedupe_winners
+{
+public:
+  ~dedupe_winners ()
+  {
+    /* Delete all keys and candidates.  */
+    for (map_t::iterator iter = m_map.begin ();
+	 iter != m_map.end ();
+	 ++iter)
       {
-	// TODO: update state based on stmt!
-	program_point next_point
-	  = program_point::after_stmt (point.m_supernode, point.m_stmt_idx,
-				       point.m_call_string);
-	program_state next_state = node->on_stmt (*this);
-	exploded_node *next = get_or_create_node (next_point, next_state);
-	add_edge (node, next, NULL);
+	delete (*iter).first;
+	delete (*iter).second;
       }
-      break;
-    case PK_AFTER_STMT:
+  }
+
+  void add (const shortest_paths &sp,
+	    const saved_diagnostic &sd)
+  {
+    dedupe_candidate *dc = new dedupe_candidate (sp, sd);
+    dedupe_key *key = new dedupe_key (sd, dc->get_path ());
+    if (dedupe_candidate **slot = m_map.get (key))
       {
-	unsigned next_idx = point.m_stmt_idx + 1;
-	if (next_idx < point.m_supernode->m_stmts.length ())
+	(*slot)->m_num_dupes++;
+
+	if (dc->length () < (*slot)->length ())
 	  {
-	    program_point next_point
-	      = program_point::before_stmt (point.m_supernode, next_idx,
-					    point.m_call_string);
-	    exploded_node *next
-	      = get_or_create_node (next_point, state);
-	    add_edge (node, next, NULL);
+	    /* We've got a shorter path for the key; replace
+	       the current candidate.  */
+	    dc->m_num_dupes = (*slot)->m_num_dupes;
+	    delete *slot;
+	    *slot = dc;
 	  }
 	else
-	  {
-	    program_point next_point
-	      = program_point::after_supernode (point.m_supernode,
-						point.m_call_string);
-	    exploded_node *next = get_or_create_node (next_point, state);
-	    add_edge (node, next, NULL);
-	  }
+	  /* We haven't beaten the current best candidate;
+	     drop the new candidate.  */
+	  delete dc;
+	delete key;
       }
-      break;
-    case PK_AFTER_SUPERNODE:
-      {
-	/* If this is an EXIT BB, detect leaks.  */
-	if (point.m_supernode->return_p ())
-	  node->detect_leaks (*this);
+    else
+      /* This is the first candidate for this key.  */
+      m_map.put (key, dc);
+  }
 
-	/* Traverse into successors of the supernode.  */
-	int i;
-	superedge *succ;
-	FOR_EACH_VEC_ELT (point.m_supernode->m_succs, i, succ)
-	  {
-	    log ("considering SN: %i -> SN: %i",
-		 succ->m_src->m_index, succ->m_dest->m_index);
+ /* Emit the simplest diagnostic within each set.  */
 
-	    program_point next_point
-	      = program_point::before_supernode (succ->m_dest, succ,
-						 point.m_call_string);
-	    program_state next_state (state);
+  void emit_best (diagnostic_manager *dm,
+		  const exploded_graph &eg,
+		  const shortest_paths &sp)
+  {
+    JIT_LOG_SCOPE (dm->get_logger ());
 
-	    if (!node->on_edge (*this, succ, &next_point, &next_state))
-	      {
-		log ("skipping impossible edge to SN: %i",
-		     succ->m_dest->m_index);
-		continue;
-	      }
+    /* Get keys into a vec for sorting.  */
+    auto_vec<const dedupe_key *> keys;
+    for (map_t::iterator iter = m_map.begin ();
+	 iter != m_map.end ();
+	 ++iter)
+      keys.safe_push ((*iter).first);
 
-	    exploded_node *next = get_or_create_node (next_point, next_state);
-	    add_edge (node, next, succ);
-	  }
+    dm->log ("# keys after de-duplication: %i", keys.length ());
+
+    /* Sort into a good emission order.  */
+    keys.qsort (dedupe_key::comparator);
+
+    /* Emit the best candidate for each key.  */
+    int i;
+    const dedupe_key *key;
+    FOR_EACH_VEC_ELT (keys, i, key)
+      {
+	dedupe_candidate **slot = m_map.get (key);
+	gcc_assert (*slot);
+	const dedupe_candidate &dc = **slot;
+
+	dm->emit_saved_diagnostic (eg, key->m_sd,
+				   dc.get_path (), key->m_stmt,
+				   dc.m_num_dupes);
       }
-      break;
-    }
+  }
+
+private:
+
+  typedef hash_map<const dedupe_key *, dedupe_candidate *,
+		   dedupe_hash_map_traits> map_t;
+  map_t m_map;
+};
+
+/* FIXME.  */
+
+void
+diagnostic_manager::emit_saved_diagnostics (const exploded_graph &eg)
+{
+  JIT_LOG_SCOPE (get_logger ());
+  auto_client_timevar tv ("emit saved diagnostics");
+  log ("# saved diagnostics: %i", m_saved_diagnostics.length ());
+
+  if (m_saved_diagnostics.length () == 0)
+    return;
+
+  /* Compute the shortest_paths once, sharing it between all diagnostics.  */
+  shortest_paths sp (eg);
+
+  // FIXME: implement a way to disable de-duplication
+  /* FIXME: add stats on de-duplication, and a way to show the
+     number of duplicates (e.g. via an option that adds a message.
+     Add a DejaGnu test for this  */
+
+  // FIXME: this isn't true:
+  /* De-duplicate the diagnostics by generating a collection of diagnostic_set,
+     picking the diagnostic with the shortest path within each set.  */
+#if 1
+  dedupe_winners best_candidates;
+
+  int i;
+  saved_diagnostic *sd;
+  FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
+    best_candidates.add (sp, *sd);
+
+  best_candidates.emit_best (this, eg, sp);
+
+#else
+  // TODO: for now, just emit them all
+
+  int i;
+  saved_diagnostic *sd;
+  FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
+    emit_saved_diagnostic (eg, sp, *sd);
+#endif
 }
 
 /* FIXME.  */
 
 void
-exploded_graph::emit_diagnostic (const state_machine &sm,
-				 const exploded_node *enode,
-				 const supernode *node, const gimple *stmt,
-				 stmt_finder *stmt_finder,
-				 tree var, state_machine::state_t state,
-				 pending_diagnostic *d) const
-{
-  /* TODO: generate a path.  */
-  /* TODO: cache the diagnostics, and flush them all at the
-     end, with one shortest_paths instance, and so finding
-     the shortest path, rather than just the first path.  */
-  shortest_paths sp (*this);
-  exploded_path epath (sp.get_shortest_path (enode));
+diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
+					   const saved_diagnostic &sd,
+					   const exploded_path &epath,
+					   const gimple *stmt,
+					   int num_dupes)
+{
+  JIT_LOG_SCOPE (get_logger ());
+  log ("num dupes: %i", num_dupes);
+
   pretty_printer *pp = global_dc->printer->clone ();
 
   checker4_path emission_path;
@@ -3179,7 +4009,7 @@ exploded_graph::emit_diagnostic (const state_machine &sm,
       /* Add state change events for the states that have changed.  */
       const program_state &src_state = src_node->get_state ();
       const program_state &dst_state = dst_node->get_state ();
-      const extrinsic_state &ext_state = m_ext_state;
+      const extrinsic_state &ext_state = eg.get_ext_state ();
       gcc_assert (src_state.m_checker_states.length ()
 		  == ext_state.m_checkers.length ());
       gcc_assert (dst_state.m_checker_states.length ()
@@ -3187,9 +4017,11 @@ exploded_graph::emit_diagnostic (const state_machine &sm,
       for (unsigned i = 0; i < ext_state.m_checkers.length (); i++)
 	{
 	  const state_machine &sm = ext_state.get_sm (i);
-	  const sm_state_map &src_smap = *src_state.m_checker_states[i];
-	  const sm_state_map &dst_smap = *dst_state.m_checker_states[i];
-	  for (sm_state_map::iterator_t iter = dst_smap.begin ();
+	  const sm_state_map<path_var> &src_smap
+	    = *src_state.m_checker_states[i];
+	  const sm_state_map<path_var> &dst_smap
+	    = *dst_state.m_checker_states[i];
+	  for (sm_state_map<path_var>::iterator_t iter = dst_smap.begin ();
 	       iter != dst_smap.end ();
 	       ++iter)
 	    {
@@ -3229,34 +4061,32 @@ exploded_graph::emit_diagnostic (const state_machine &sm,
 	}
     }
 
-  /* Support deferring the choice of stmt until after an emission path has
-     been built, using an optional stmt_finder.  */
-  if (stmt == NULL)
-    {
-      gcc_assert (stmt_finder);
-      stmt = stmt_finder->find_stmt (epath);
-      gcc_assert (stmt);
-    }
-
-  prune_path (&emission_path, sm, var, state);
-  emission_path.add_final_event (sm, enode, stmt, var, state, d);
-  emission_path.prepare_for_emission (d);
+  prune_path (&emission_path, sd.m_sm, sd.m_var, sd.m_state);
+  emission_path.add_final_event (sd.m_sm, sd.m_enode, stmt,
+				 sd.m_var, sd.m_state, sd.m_d);
+  emission_path.prepare_for_emission (sd.m_d);
 
   gcc_rich_location rich_loc (stmt->location);
   rich_loc.set_path (&emission_path);
 
-  auto_cfun sentinel (node->m_fun);
-  d->emit (&rich_loc);
+  auto_diagnostic_group d;
+  auto_cfun sentinel (sd.m_node->m_fun);
+  sd.m_d->emit (&rich_loc);
+  if (num_dupes > 0)
+    inform_n (stmt->location, num_dupes,
+	      "%i duplicate", "%i duplicates",
+	      num_dupes);
   delete pp;
 }
 
 /* FIXME.  */
 
 void
-exploded_graph::prune_path (checker4_path *path,
-			    const state_machine &sm,
-			    tree var,
-			    state_machine::state_t state ATTRIBUTE_UNUSED) const
+diagnostic_manager::prune_path (checker4_path *path,
+				const state_machine &sm,
+				tree var,
+				state_machine::state_t state
+				  ATTRIBUTE_UNUSED) const
 {
   JIT_LOG_FUNC (get_logger ());
   path->maybe_log (get_logger (), "path");
@@ -3407,17 +4237,18 @@ exploded_graph::prune_path (checker4_path *path,
 
   /* Second pass to remove redundant interprocedural information.  For
      example, given:
-     - calling "f2" from "f1"
-     --- entry to "f2"
-     --- calling "f3" from "f2"
-     ----- entry to "f3"
-     --- returning to "f2" to "f3"
-     - returning to "f1" to "f2"
+     (1)- calling "f2" from "f1"
+     (2)--- entry to "f2"
+     (3)--- calling "f3" from "f2"
+     (4)----- entry to "f3"
+     (5)--- returning to "f2" to "f3"
+     (6)- returning to "f1" to "f2"
      with no other intervening events, then none of these events are
      likely to be interesting to the user.
 
      Prune [..., call, function-entry, return, ...] triples repeatedly
-     until nothing has changed.  */
+     until nothing has changed.  For the example above, this would
+     remove events (3, 4, 5), and then remove events (1, 2, 6).  */
   bool changed = false;
   do
     {
@@ -3446,25 +4277,363 @@ exploded_graph::prune_path (checker4_path *path,
 	}
 
     }
-  while (changed);
+  while (changed);
+
+  /* Third pass.  If all we're left with is in one function, then
+     filter function entry events.  */
+  if (!path->interprocedural_p ())
+    {
+      // FIXME
+      int idx = path->m_events.length () - 1;
+      while (idx >= 0 && idx < path->m_events.length ())
+	{
+	  checker4_event *base_event = path->m_events[idx];
+	  if (base_event->m_kind == EK_FUNCTION_ENTRY)
+	    {
+	      log ("filtering event %i:"
+		   " function entry for purely intraprocedural path", idx);
+	      path->delete_event (idx);
+	    }
+	  idx--;
+	}
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+/* exploded_graph's ctor.  */
+
+exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
+				const extrinsic_state &ext_state,
+				const state_purge_map *purge_map,
+				int verbosity)
+: log_user (logger), m_sg (sg), m_ext_state (ext_state),
+  m_purge_map (purge_map),
+  m_diagnostic_manager (logger, verbosity),
+  m_global_stats (m_sg.num_nodes ()),
+  m_PK_AFTER_SUPERNODE_per_snode (m_sg.num_nodes ())
+{
+  m_origin = get_or_create_node (program_point (NULL, NULL, 0, PK_ORIGIN,
+						call_string ()),
+				 program_state (ext_state));
+  for (int i = 0; i < m_sg.num_nodes (); i++)
+    m_PK_AFTER_SUPERNODE_per_snode.safe_push (i);
+}
+
+/* exploded_graph's dtor.  */
+
+exploded_graph::~exploded_graph ()
+{
+  for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
+       iter != m_per_function_stats.end ();
+       ++iter)
+    delete (*iter).second;
+}
+
+/* Ensure that there is an exploded_node representing an external call to
+   FUN, adding it to the worklist if creating it.
+
+   Add an edge from the origin exploded_node to the function entrypoint
+   exploded_node.  */
+
+void
+exploded_graph::add_function_entry (function *fun)
+{
+  program_point point = program_point::from_function_entry (m_sg, fun);
+  program_state state (m_ext_state);
+  if (state.m_region_model)
+    state.m_region_model->push_frame (fun, NULL);
+
+  add_edge (m_origin, get_or_create_node (point, state), NULL);
+}
+
+/* Get or create an exploded_node for (POINT, STATE).
+   If a new node is created, it is added to the worklist.  */
+
+exploded_node *
+exploded_graph::get_or_create_node (const program_point &point,
+				    const program_state &state)
+{
+  JIT_LOG_FUNC (get_logger ());
+  if (get_logger ())
+    {
+      format f (false);
+      log ("point: %s", point.to_str (f).c_str ());
+      log ("state: %s", state.to_str (m_ext_state).c_str ());
+    }
+
+  auto_cfun sentinel (point.get_function ());
+
+  state.validate ();
+
+  /* Prune state to try to improve the chances of a cache hit,
+     avoiding generating redundant nodes.  */
+  program_state pruned_state = state.prune_for_point (*this, point);
+
+  pruned_state.validate ();
+
+  if (get_logger ())
+    {
+      log ("pruned_state: %s", pruned_state.to_str (m_ext_state).c_str ());
+      if (pruned_state.m_region_model)
+	pruned_state.m_region_model->dump_to_pp (get_logger_pp ());
+    }
+
+  stats *per_fn_stats = get_or_create_function_stats (point.get_function ());
+
+  point_and_state ps (point, pruned_state);
+  if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
+    {
+      /* An exploded_node for PS already exists.  */
+      log ("reused EN: %i", (*slot)->m_index);
+      m_global_stats.m_node_reuse_count++;
+      per_fn_stats->m_node_reuse_count++;
+      return *slot;
+    }
+
+  /* An exploded_node for PS doesn't already exist; create one.  */
+  exploded_node *node = new exploded_node (ps, m_ext_state, m_nodes.length ());
+  add_node (node);
+  m_point_and_state_to_node.put (node->get_key (), node);
+  m_global_stats.m_num_nodes[node->get_point ().m_kind]++;
+  per_fn_stats->m_num_nodes[node->get_point ().m_kind]++;
+
+  if (node->get_point ().m_kind == PK_AFTER_SUPERNODE)
+    m_PK_AFTER_SUPERNODE_per_snode[point.get_supernode ()->m_index]++;
+
+  if (get_logger ())
+    {
+      format f (false);
+      log ("created EN: %i", node->m_index);
+      log ("point: %s", point.to_str (f).c_str ());
+      log ("pruned_state: %s", pruned_state.to_str (m_ext_state).c_str ());
+    }
+
+  if (pruned_state.m_region_model)
+    {
+      pretty_printer pp;
+      pp_printf (&pp, "%s.EN-%i.rmodel.dot",
+		 dump_base_name, node->m_index);
+      pruned_state.m_region_model->dump_dot (pp_formatted_text (&pp));
+    }
+
+  /* Add the new node to the worlist.  */
+  m_worklist.safe_push (node);
+  return node;
+}
+
+/* Add an exploded_edge from SRC to DEST, recording its association
+   with SEDGE (which may be NULL).  */
+
+void
+exploded_graph::add_edge (exploded_node *src, exploded_node *dest,
+			  const superedge *sedge)
+{
+  exploded_edge *e = new exploded_edge (src, dest, sedge);
+  digraph::add_edge (e);
+  src->m_succs.safe_push (e);
+}
+
+/* The main loop of the analysis.
+   Pop freshly-created exploded_nodes from the worklist, calling
+   process_node on them to explore the <point, state> graph.
+   Add edges to their successors, potentially creating new successors
+   (which are also added to the worklist).  */
+
+void
+exploded_graph::process_worklist ()
+{
+  JIT_LOG_SCOPE (get_logger ());
+  auto_client_timevar tv ("exploded_graph::process_worklist");
+
+  while (m_worklist.length () > 0)
+    {
+      exploded_node *node = m_worklist.pop ();
+      process_node (node);
+
+      /* FIXME: make this less ad-hoc?  e.g. a param */
+      /* we use exit rather than entry since there can be multiple
+	 entry ENs, one per phi; the number of PK_AFTER_SUPERNODE ought
+	 to be equivalent to the number of supernodes multiplied by the
+	 number of states.  */
+      // FIXME: do per function
+      if (m_global_stats.m_num_nodes[PK_AFTER_SUPERNODE]> m_sg.num_nodes () * 5)
+	{
+	  log ("bailing out; too many nodes");
+	  warning (0, "analysis bailed out early (%i nodes)",
+		   m_global_stats.m_num_nodes[PK_AFTER_SUPERNODE]);
+	  // TODO: better location??
+	  return;
+	}
+    }
+}
+
+/* The core of exploded_graph::process_worklist (the main analysis loop),
+   handling one node in the worklist.
+
+   Get successor <point, state> pairs for NODE, calling get_or_create on
+   them, and adding an exploded_edge to each successors.
+
+   Freshly-created nodes will be added to the worklist.  */
+
+void
+exploded_graph::process_node (exploded_node *node)
+{
+  JIT_LOG_FUNC_1 (get_logger (), "EN: %i", node->m_index);
+
+  auto_cfun sentinel (node->get_function ());
+
+  const program_point &point = node->get_point ();
+  const program_state &state = node->get_state ();
+  if (get_logger ())
+    {
+      format f (false);
+      log ("point: %s, state: %s",
+	   point.to_str (f).c_str (),
+	   state.to_str (m_ext_state).c_str ());
+    }
+
+  switch (point.m_kind)
+    {
+    default:
+      gcc_unreachable ();
+    case PK_ORIGIN:
+      /* This node exists to simplify finding the shortest path
+	 to an exploded_node.  */
+      break;
+
+    case PK_BEFORE_SUPERNODE:
+      {
+	program_state next_state (state);
+
+	for (gphi_iterator gpi
+	       = const_cast<supernode *>(node->get_supernode ())->start_phis ();
+	     !gsi_end_p (gpi); gsi_next (&gpi))
+	  {
+	    gphi *phi = gpi.phi ();
+
+	    const cfg_superedge *last_cfg_superedge
+	      = point.m_from_edge->dyn_cast_cfg_superedge ();
+	    gcc_assert (last_cfg_superedge);
+	    // FIXME: this ought to be an as_a<>
+
+	    tree src = last_cfg_superedge->get_phi_arg (phi);
+	    tree lhs = gimple_phi_result (phi);
+
+	    /* Update next_state based on phi.  */
+	    node->on_assignment (*this,
+				 point.make_path_var (lhs),
+				 point.make_path_var (src),
+				 state,
+				 &next_state);
+
+	    if (next_state.m_region_model)
+	      {
+		bool is_back_edge = last_cfg_superedge->back_edge_p ();
+		next_state.m_region_model->handle_phi (lhs, src, is_back_edge);
+	      }
+	  }
+
+	if (point.m_supernode->m_stmts.length () > 0)
+	  {
+	    program_point next_point
+	      = program_point::before_stmt (point.m_supernode, 0,
+					    point.m_call_string);
+	    exploded_node *next
+	      = get_or_create_node (next_point, next_state);
+	    add_edge (node, next, NULL);
+	  }
+	else
+	  {
+	    program_point next_point
+	      = program_point::after_supernode (point.m_supernode,
+						point.m_call_string);
+	    exploded_node *next = get_or_create_node (next_point, next_state);
+	    add_edge (node, next, NULL);
+	  }
+      }
+      break;
+    case PK_BEFORE_STMT:
+      {
+	// TODO: update state based on stmt!
+	program_point next_point
+	  = program_point::after_stmt (point.m_supernode, point.m_stmt_idx,
+				       point.m_call_string);
+	program_state next_state = node->on_stmt (*this);
+	exploded_node *next = get_or_create_node (next_point, next_state);
+	add_edge (node, next, NULL);
+      }
+      break;
+    case PK_AFTER_STMT:
+      {
+	unsigned next_idx = point.m_stmt_idx + 1;
+	if (next_idx < point.m_supernode->m_stmts.length ())
+	  {
+	    program_point next_point
+	      = program_point::before_stmt (point.m_supernode, next_idx,
+					    point.m_call_string);
+	    exploded_node *next
+	      = get_or_create_node (next_point, state);
+	    add_edge (node, next, NULL);
+	  }
+	else
+	  {
+	    program_point next_point
+	      = program_point::after_supernode (point.m_supernode,
+						point.m_call_string);
+	    exploded_node *next = get_or_create_node (next_point, state);
+	    add_edge (node, next, NULL);
+	  }
+      }
+      break;
+    case PK_AFTER_SUPERNODE:
+      {
+	/* If this is an EXIT BB, detect leaks.  */
+	if (point.m_supernode->return_p ())
+	  node->detect_leaks (*this);
+
+	/* Traverse into successors of the supernode.  */
+	int i;
+	superedge *succ;
+	FOR_EACH_VEC_ELT (point.m_supernode->m_succs, i, succ)
+	  {
+	    log ("considering SN: %i -> SN: %i",
+		 succ->m_src->m_index, succ->m_dest->m_index);
+
+	    program_point next_point
+	      = program_point::before_supernode (succ->m_dest, succ,
+						 point.m_call_string);
+	    program_state next_state (state);
+
+	    if (!node->on_edge (*this, succ, &next_point, &next_state))
+	      {
+		log ("skipping impossible edge to SN: %i",
+		     succ->m_dest->m_index);
+		continue;
+	      }
+
+	    exploded_node *next = get_or_create_node (next_point, next_state);
+	    add_edge (node, next, succ);
+	  }
+      }
+      break;
+    }
+}
 
-  /* Third pass.  If all we're left with is in one function, then
-     filter function entry events.  */
-  if (!path->interprocedural_p ())
+/* FIXME.  */
+
+stats *
+exploded_graph::get_or_create_function_stats (function *fn)
+{
+  if (stats **slot = m_per_function_stats.get (fn))
+    return *slot;
+  else
     {
-      // FIXME
-      int idx = path->m_events.length () - 1;
-      while (idx >= 0 && idx < path->m_events.length ())
-	{
-	  checker4_event *base_event = path->m_events[idx];
-	  if (base_event->m_kind == EK_FUNCTION_ENTRY)
-	    {
-	      log ("filtering event %i:"
-		   " function entry for purely intraprocedural path", idx);
-	      path->delete_event (idx);
-	    }
-	  idx--;
-	}
+      int num_supernodes = fn ? n_basic_blocks_for_fn (fn) : 0;
+      // FIXME: ^^^ not quite the num supernodes, but nearly
+      stats *new_stats = new stats (num_supernodes);
+      m_per_function_stats.put (fn, new_stats);
+      return new_stats;
     }
 }
 
@@ -3476,13 +4645,69 @@ exploded_graph::log_stats () const
   if (!get_logger ())
     return;
 
+  JIT_LOG_SCOPE (get_logger ());
+
   log ("m_sg.num_nodes (): %i", m_sg.num_nodes ());
   log ("m_nodes.length (): %i", m_nodes.length ());
-  for (int i = 0; i < NUM_POINT_KINDS; i++)
-    log ("m_num_nodes[%s]: %i",
-	 point_kind_to_string (static_cast <enum point_kind> (i)),
-	 m_num_nodes[i]);
-  log ("m_node_reuse_count: %i", m_node_reuse_count);
+  log ("m_edges.length (): %i", m_edges.length ());
+
+  log ("global stats:");
+  m_global_stats.log (get_logger ());
+
+  for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
+       iter != m_per_function_stats.end ();
+       ++iter)
+    {
+      function *fn = (*iter).first;
+      log_scope s (get_logger (), function_name (fn));
+      (*iter).second->log (get_logger ());
+    }
+}
+
+void
+exploded_graph::dump_stats (FILE *out) const
+{
+  fprintf (out, "m_sg.num_nodes (): %i\n", m_sg.num_nodes ());
+  fprintf (out, "m_nodes.length (): %i\n", m_nodes.length ());
+  fprintf (out, "m_edges.length (): %i\n", m_edges.length ());
+
+  fprintf (out, "global stats:\n");
+  m_global_stats.dump (out);
+
+  for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
+       iter != m_per_function_stats.end ();
+       ++iter)
+    {
+      function *fn = (*iter).first;
+      fprintf (out, "function: %s\n", function_name (fn));
+      (*iter).second->dump (out);
+    }
+
+  fprintf (out, "PK_AFTER_SUPERNODE per supernode:\n");
+  for (int i = 0; i < m_PK_AFTER_SUPERNODE_per_snode.length (); i++)
+    fprintf (out, "  SN %i: %3i\n", i, m_PK_AFTER_SUPERNODE_per_snode[i]);
+}
+
+void
+exploded_graph::dump_states_for_supernode (FILE *out,
+					   const supernode *snode) const
+{
+  fprintf (out, "PK_AFTER_SUPERNODE nodes for SN: %i\n", snode->m_index);
+  int i;
+  dnode *node;
+  int state_idx = 0;
+  FOR_EACH_VEC_ELT (m_nodes, i, node)
+    {
+      exploded_node *enode = (exploded_node *)node;
+      const supernode *iter_snode = enode->get_supernode ();
+      if (enode->get_point ().m_kind == PK_AFTER_SUPERNODE
+	  && iter_snode == snode)
+	fprintf (out, "state %i: EN: %i\n  %s\n",
+		 state_idx++, enode->m_index,
+		 enode->get_state ().to_str (m_ext_state).c_str ());
+    }
+  fprintf (out, "#exploded_node for PK_AFTER_SUPERNODE for SN: %i = %i\n",
+	   snode->m_index, state_idx);
 }
 
 ////////////////////////////////////////////////////////////////////////////
@@ -3525,6 +4750,8 @@ shortest_paths::shortest_paths (const exploded_graph &eg)
   m_dist (eg.m_nodes.length ()),
   m_prev (eg.m_nodes.length ())
 {
+  auto_client_timevar tv ("shortest_paths");
+
   auto_vec<int> queue (eg.m_nodes.length ());
 
   for (unsigned i = 0; i < eg.m_nodes.length (); i++)
@@ -3809,7 +5036,13 @@ public:
   function_call_string_cluster (function *fun, call_string cs)
   : m_fun (fun), m_cs (cs) {}
 
-  // TODO: dtor
+  ~function_call_string_cluster ()
+  {
+    for (map_t::iterator iter = m_map.begin ();
+	 iter != m_map.end ();
+	 ++iter)
+      delete (*iter).second;
+  }
 
   void dump_dot (graphviz_out *gv) const FINAL OVERRIDE
   {
@@ -3925,7 +5158,10 @@ class root_cluster : public en_cluster
 public:
   ~root_cluster ()
   {
-    // TODO: delete the function_cluster instances in the map
+    for (map_t::iterator iter = m_map.begin ();
+	 iter != m_map.end ();
+	 ++iter)
+      delete (*iter).second;
   }
 
   void dump_dot (graphviz_out *gv) const FINAL OVERRIDE
@@ -3979,6 +5215,456 @@ private:
 
 ////////////////////////////////////////////////////////////////////////////
 
+/* FIXME.  */
+
+state_purge_map::state_purge_map (const supergraph &sg,
+				  logger *logger)
+: log_user (logger), m_sg (sg)
+{
+  JIT_LOG_FUNC (logger);
+
+  auto_client_timevar tv ("state_purge_map ctor");
+
+  cgraph_node *node;
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+  {
+    function *fun = node->get_fun ();
+    if (logger)
+      log ("function: %s", function_name (fun));
+    tree var;
+    unsigned int i;;
+    FOR_EACH_SSA_NAME (i, var, fun)
+      m_map.put (var, new state_purge_per_ssa_name (*this, var, fun));
+  }
+}
+
+/* FIXME.  */
+
+state_purge_map::~state_purge_map ()
+{
+  for (iterator iter = m_map.begin (); iter != m_map.end (); ++iter)
+    delete (*iter).second;
+}
+
+/* state_purge_per_ssa_name's ctor.
+
+   Locate all uses of VAR within FUN.
+   Walk backwards from each use, marking program points, until
+   we reach the def stmt, populating m_points_needing_var.
+
+   We have to track program points rather than
+   just stmts since there could be empty basic blocks on the way.  */
+
+state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map &map,
+						    tree var,
+						    function *fun)
+: m_var (var)
+{
+  JIT_LOG_FUNC (map.get_logger ());
+
+  map.log ("SSA name: %qE", var);
+  if (map.get_logger ())
+    {
+      /* Show def stmt.  */
+      gimple *def_stmt = SSA_NAME_DEF_STMT (var);
+      pretty_printer pp;
+      pp_gimple_stmt_1 (&pp, def_stmt, 0, (dump_flags_t)0);
+      map.log ("def stmt: %s", pp_formatted_text (&pp));
+    }
+
+  /* The worklist for the traversal.
+     FIXME: worklist can't be a vec<program_point>; we can't have a
+     vec<program_point> because vec<T> require T to be a POD.
+     Hence (annoyingly) we dynamically allocate.  */
+  auto_vec<program_point *> worklist;
+
+  /* Add all immediate uses of var to the worklist.
+     Compare with debug_immediate_uses.  */
+  imm_use_iterator iter;
+  use_operand_p use_p;
+  FOR_EACH_IMM_USE_FAST (use_p, iter, var)
+    {
+      if (USE_STMT (use_p))
+	{
+	  gimple *use_stmt = USE_STMT (use_p);
+	  if (map.get_logger ())
+	    {
+	      pretty_printer pp;
+	      pp_gimple_stmt_1 (&pp, use_stmt, 0, (dump_flags_t)0);
+	      map.log ("used by stmt: %s", pp_formatted_text (&pp));
+	    }
+
+	  const supernode *snode
+	    = map.m_sg.get_node_for_block (use_stmt->bb);
+
+	  /* If it's a use within a phi node, then we care about
+	     which in-edge we came from.  */
+	  if (use_stmt->code == GIMPLE_PHI)
+	    {
+	      for (gphi_iterator gpi
+		     = const_cast<supernode *> (snode)->start_phis ();
+		   !gsi_end_p (gpi); gsi_next (&gpi))
+		{
+		  gphi *phi = gpi.phi ();
+		  if (phi == use_stmt)
+		    {
+		      /* Find arguments (and thus in-edges) which use VAR.  */
+		      for (unsigned arg_idx = 0;
+			   arg_idx < gimple_phi_num_args (phi);
+			   ++arg_idx)
+			{
+			  if (var == gimple_phi_arg (phi, arg_idx)->def)
+			    {
+			      edge in_edge = gimple_phi_arg_edge (phi, arg_idx);
+			      const superedge *in_sedge
+				= map.m_sg.get_edge_for_cfg_edge (in_edge);
+			      program_point point
+				= program_point::before_supernode
+				(snode, in_sedge, call_string ());
+			      add_to_worklist (point, &worklist,
+					       map.get_logger ());
+			      m_points_needing_var.add (point);
+			    }
+			}
+		    }
+		}
+	    }
+	  else
+	    {
+	      program_point point = before_use_stmt (map, use_stmt);
+	      add_to_worklist (point, &worklist, map.get_logger ());
+	      m_points_needing_var.add (point);
+
+	      /* We also need to add uses for conditionals and switches,
+		 where the stmt "happens" at the after_supernode, for filtering
+		 the out-edges.  */
+	      if (use_stmt == snode->get_last_stmt ())
+		{
+		  program_point point
+		    = program_point::after_supernode (snode, call_string ());
+		  add_to_worklist (point, &worklist, map.get_logger ());
+		  m_points_needing_var.add (point);
+		}
+	    }
+	}
+    }
+
+  /* Process worklist by walking backwards until we reach the def stmt.  */
+  {
+    log_scope s (map.get_logger (), "processing worklist");
+    while (worklist.length () > 0)
+      {
+	program_point *point_ptr = worklist.pop ();
+	program_point point (*point_ptr);
+	process_point (*point_ptr, &worklist, map.get_logger ());
+	delete point_ptr;
+    }
+  }
+
+  if (map.get_logger ())
+    {
+      map.log ("%qE is needed to process:", var);
+      for (point_set_t::iterator iter = m_points_needing_var.begin ();
+	   iter != m_points_needing_var.end ();
+	   ++iter)
+	{
+	  format f (false);
+	  map.log ("  point: %s", (*iter).to_str (f).c_str ());
+	}
+    }
+}
+
+/* FIXME.  */
+
+bool
+state_purge_per_ssa_name::needed_at_point_p (const program_point &point) const
+{
+  return const_cast <point_set_t &> (m_points_needing_var).contains (point);
+}
+
+/* FIXME.  */
+
+program_point
+state_purge_per_ssa_name::before_use_stmt (const state_purge_map &map,
+					   const gimple *use_stmt)
+{
+  gcc_assert (use_stmt->code != GIMPLE_PHI); // FIXME: for now
+#if 0
+  if (stmt->code == GIMPLE_PHI)
+    {
+      const supernode *supernode = sg.get_node_for_block (stmt->bb);
+      return before_supernode (supernode, from_edge, call_string);
+    }
+  else
+#endif
+    {
+      const supernode *supernode = map.m_sg.get_supernode_for_stmt (use_stmt);
+      unsigned int stmt_idx = supernode->get_stmt_index (use_stmt);
+      return program_point::before_stmt (supernode, stmt_idx, call_string ());
+    }
+}
+
+/* FIXME.  */
+
+void
+state_purge_per_ssa_name::add_to_worklist (const program_point &point,
+					   auto_vec<program_point *> *worklist,
+					   logger *logger)
+{
+  JIT_LOG_FUNC (logger);
+  if (logger)
+    {
+      format f (false);
+      logger->log ("point: %s", point.to_str (f).c_str ());
+    }
+
+  if (m_points_needing_var.contains (point))
+    {
+      if (logger)
+	logger->log ("already seen");
+    }
+  else
+    {
+      if (logger)
+	logger->log ("not seen; adding to worklist");
+      worklist->safe_push (new program_point (point));
+    }
+}
+
+/* Process POINT, popped from WORKLIST.
+   Iterate over predecessors of POINT, adding to WORKLIST.  */
+
+void
+state_purge_per_ssa_name::process_point (const program_point &point,
+					 auto_vec<program_point *> *worklist,
+					 logger *logger)
+{
+  JIT_LOG_FUNC (logger);
+  if (logger)
+    {
+      format f (false);
+      logger->log ("considering point: %s", point.to_str (f).c_str ());
+    }
+
+  gimple *def_stmt = SSA_NAME_DEF_STMT (m_var);
+
+  m_points_needing_var.add (point);
+
+  const supernode *snode = point.get_supernode ();
+
+  switch (point.m_kind)
+    {
+    default:
+      gcc_unreachable ();
+
+    case PK_ORIGIN:
+      break;
+
+    case PK_BEFORE_SUPERNODE:
+      {
+	for (gphi_iterator gpi
+	       = const_cast<supernode *> (snode)->start_phis ();
+	     !gsi_end_p (gpi); gsi_next (&gpi))
+	  {
+	    gphi *phi = gpi.phi ();
+	    if (phi == def_stmt)
+	      {
+		if (logger)
+		  logger->log ("def stmt within phis; terminating");
+		return;
+	      }
+	  }
+
+	/* Add given pred to worklist.  */
+	if (point.m_from_edge)
+	  {
+	    if (point.m_from_edge->m_src)
+	      add_to_worklist
+		(program_point::after_supernode (point.m_from_edge->m_src,
+						 call_string ()),
+		 worklist, logger);
+	  }
+	else
+	  gcc_assert (snode->entry_p ());
+      }
+      break;
+
+    case PK_BEFORE_STMT:
+      {
+	if (def_stmt == point.get_stmt ())
+	  {
+	    if (logger)
+	      logger->log ("def stmt; terminating");
+	    return;
+	  }
+	if (point.m_stmt_idx > 0)
+	  add_to_worklist (program_point::after_stmt (snode,
+						      point.m_stmt_idx - 1,
+						      call_string ()),
+			   worklist, logger);
+	else
+	{
+	  /* Add before_supernode to worklist.  This captures the in-edge,
+	     so we have to do it once per in-edge.  */
+	  unsigned i;
+	  superedge *pred;
+	  FOR_EACH_VEC_ELT (snode->m_preds, i, pred)
+	    add_to_worklist (program_point::before_supernode (snode,
+							      pred,
+							      call_string ()),
+			     worklist, logger);
+	}
+      }
+      break;
+
+    case PK_AFTER_STMT:
+      add_to_worklist (program_point::before_stmt (snode,
+						   point.m_stmt_idx,
+						   call_string ()),
+		       worklist, logger);
+      break;
+
+    case PK_AFTER_SUPERNODE:
+      {
+	if (snode->m_stmts.length ())
+	  add_to_worklist
+	    (program_point::after_stmt (snode,
+					snode->m_stmts.length () - 1,
+					call_string ()),
+	     worklist, logger);
+	else
+	  {
+	    /* Add before_supernode to worklist.  This captures the in-edge,
+	       so we have to do it once per in-edge.  */
+	    unsigned i;
+	    superedge *pred;
+	    FOR_EACH_VEC_ELT (snode->m_preds, i, pred)
+	      add_to_worklist (program_point::before_supernode (snode,
+								pred,
+								call_string ()),
+			       worklist, logger);
+	    /* If no preds and it's the initial BB, add it, to ensure that we
+	       have "before supernode" for the initial ENTRY block, and don't
+	       erroneously purge SSA names for initial values of parameters.  */
+	    if (snode->entry_p ()
+		&& snode->m_preds.length () == 0)
+	      {
+		add_to_worklist
+		  (program_point::before_supernode (snode,
+						    NULL,
+						    call_string ()),
+		   worklist, logger);
+	      }
+	  }
+      }
+      break;
+    }
+}
+
+/* FIXME.  */
+
+class state_purge_annotator  : public dot_annotator
+{
+public:
+  state_purge_annotator (const state_purge_map *map) : m_map (map) {}
+
+  void add_node_annotations (pretty_printer *pp ATTRIBUTE_UNUSED,
+			     const supernode &n ATTRIBUTE_UNUSED)
+    const FINAL OVERRIDE
+  {
+    if (m_map == NULL)
+      return;
+
+#if 0
+    //pp_printf (pp, "FIXME: node annotation");
+    pp_printf (pp, "annotation_for_node_%i", n.m_index);
+    pp_printf (pp, " [shape=%s,style=filled,fillcolor=%s,label=\"",
+	       "record", "lightblue");
+    pp_left_brace (pp);
+    pp_write_text_to_stream (pp);
+
+    // TODO: which in-edge?
+    program_point before_supernode
+      (program_point::before_supernode (&n, call_string ()));
+
+    for (state_purge_map::iterator iter = m_map.begin ();
+	 iter != m_map.end ();
+	 ++iter)
+      {
+	tree var = (*iter).first;
+	state_purge_per_ssa_name *per_var_data = (*iter).second;
+	if (per_var_data->needed_at_point_p (before_supernode))
+	  pp_printf (pp, "%qE needed here", var);
+	else
+	  pp_printf (pp, "%qE not needed here", var);
+	pp_newline (pp);
+      }
+
+    pp_right_brace (pp);
+
+    pp_string (pp, "\"];\n\n");
+    pp_flush (pp);
+#endif
+  }
+
+  void add_stmt_annotations (pretty_printer *pp ATTRIBUTE_UNUSED,
+			     const gimple *stmt ATTRIBUTE_UNUSED)
+    const FINAL OVERRIDE
+  {
+    if (m_map == NULL)
+      return;
+
+    if (stmt->code == GIMPLE_PHI)
+      return;
+
+    pp_newline (pp);
+
+    const supernode *supernode = m_map->m_sg.get_supernode_for_stmt (stmt);
+    unsigned int stmt_idx = supernode->get_stmt_index (stmt);
+    program_point before_stmt
+      (program_point::before_stmt (supernode, stmt_idx, call_string ()));
+
+    auto_vec<tree> needed;
+    auto_vec<tree> not_needed;
+    for (state_purge_map::iterator iter = m_map->begin ();
+	 iter != m_map->end ();
+	 ++iter)
+      {
+	tree var = (*iter).first;
+	state_purge_per_ssa_name *per_var_data = (*iter).second;
+	if (per_var_data->needed_at_point_p (before_stmt))
+	  needed.safe_push (var);
+	else
+	  not_needed.safe_push (var);
+      }
+
+    tree var;
+    unsigned i;
+    pp_printf (pp, "needed here: {");
+    FOR_EACH_VEC_ELT (needed, i, var)
+      {
+	if (i > 0)
+	  pp_string (pp, ", ");
+	pp_printf (pp, "%qE", var);
+      }
+    pp_printf (pp, "}");
+    pp_newline (pp);
+    pp_printf (pp, "not needed here: {");
+    FOR_EACH_VEC_ELT (not_needed, i, var)
+      {
+	if (i > 0)
+	  pp_string (pp, ", ");
+	pp_printf (pp, "%qE", var);
+      }
+    pp_newline (pp);
+  }
+
+private:
+  const state_purge_map *m_map;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
 #if 0
 /* FIXME.  */
 
@@ -4005,14 +5691,25 @@ impl_run_checkers (logger *logger)
   /* Create the supergraph.  */
   supergraph sg (logger);
 
-  if (1)
+  state_purge_map *purge_map = NULL;
+
+  if (flag_analyzer_state_purge)
+      purge_map = new state_purge_map (sg, logger);
+
+  if (flag_dump_analyzer_supergraph)
     {
+      auto_client_timevar tv ("writing .supergraph.dot");
+      state_purge_annotator a (purge_map);
       char *filename = concat (dump_base_name, ".supergraph.dot", NULL);
-      sg.dump_dot (filename, (enum supergraph_dot_flags)0);
+      sg.dump_dot (filename, (enum supergraph_dot_flags)0, &a);
       free (filename);
     }
 
-  // TODO: selftest for auto_delete_vec
+#if 0
+  // FIXME: early exit (while debugging state_purge_map)
+  return;
+#endif
+
   auto_delete_vec <state_machine> checkers;
   make_checkers (checkers, logger);
 
@@ -4023,11 +5720,12 @@ impl_run_checkers (logger *logger)
       FOR_EACH_VEC_ELT (checkers, i, sm)
 	logger->log ("checkers[%i]: %s", i, sm->get_name ());
     }
-  //engine e (sg, sm, logger, analyzer_verbosity);
 
-  // TODO: add initial nodes
-  impl_4::exploded_graph eg (sg, logger, checkers, analyzer_verbosity);
+  /* The exploded graph.  */
+  impl_4::exploded_graph eg (sg, logger, checkers, purge_map,
+			     analyzer_verbosity);
 
+  /* Add entrypoints to the graph for externally-callable functions.  */
   cgraph_node *node;
   FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
   {
@@ -4041,10 +5739,12 @@ impl_run_checkers (logger *logger)
     eg.add_function_entry (fun);
   }
 
+  /* Now process the worklist, exploring the <point, state> graph.  */
   eg.process_worklist ();
 
-  if (1)
+  if (flag_dump_analyzer_exploded_graph)
     {
+      auto_client_timevar tv ("writing .eg.dot");
       char *filename
 	= concat (dump_base_name, ".eg.dot", NULL);
       root_cluster c;
@@ -4052,7 +5752,12 @@ impl_run_checkers (logger *logger)
       free (filename);
     }
 
+  /* Now emit any saved diagnostics.  */
+  eg.get_diagnostic_manager ().emit_saved_diagnostics (eg);
+
   eg.log_stats ();
+
+  delete purge_map;
 }
 
 } // namespace impl_4
@@ -4107,7 +5812,7 @@ test_sm_state_map ()
 
   /* Test setting states on path_var instances directly.  */
   {
-    impl_4::sm_state_map map;
+    impl_4::sm_state_map<path_var> map;
     ASSERT_TRUE (map.is_empty_p ());
     ASSERT_EQ (map.get_state (x0), 0);
 
@@ -4127,7 +5832,7 @@ test_sm_state_map ()
 
   /* Test setting states via equivalence classes.  */
   {
-    impl_4::sm_state_map map;
+    impl_4::sm_state_map<path_var> map;
     ASSERT_TRUE (map.is_empty_p ());
     ASSERT_EQ (map.get_state (x0), 0);
     ASSERT_EQ (map.get_state (y0), 0);
@@ -4146,9 +5851,9 @@ test_sm_state_map ()
 
   /* Test equality and hashing.  */
   {
-    impl_4::sm_state_map map0;
-    impl_4::sm_state_map map1;
-    impl_4::sm_state_map map2;
+    impl_4::sm_state_map<path_var> map0;
+    impl_4::sm_state_map<path_var> map1;
+    impl_4::sm_state_map<path_var> map2;
 
     ASSERT_EQ (map0.hash (), map1.hash ());
     ASSERT_EQ (map0, map1);
diff --git a/gcc/analyzer/path-state.cc b/gcc/analyzer/path-state.cc
new file mode 100644
index 0000000..3dcb237
--- /dev/null
+++ b/gcc/analyzer/path-state.cc
@@ -0,0 +1,2270 @@
+/* FIXME.  */
+#include "config.h"
+#include "gcc-plugin.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tm.h"
+#include "toplev.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "context.h"
+#include "diagnostic.h"
+#include "bitmap.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-pretty-print.h"
+#include "tree-pretty-print.h"
+#include "analyzer.h"
+#include "graphviz.h"
+#include "cgraph.h"
+#include "diagnostic-path.h"
+#include "gcc-rich-location.h"
+#include "tree-dfa.h"
+#include <string>
+#include "analyzer-logging.h"
+#include "stringpool.h"
+#include "selftest.h"
+#include "fold-const.h"
+#include "tristate.h"
+#include "path-state.h"
+#include "supergraph.h"
+
+using namespace psn;
+
+/* A subclass of purge_criteria: purge a specific path_var.  */
+
+class purge_one_path_var : public purge_criteria
+{
+public:
+  purge_one_path_var (path_var victim) : m_victim (victim) {}
+
+  bool should_purge_p (path_var pv) const FINAL OVERRIDE
+  {
+    return pv == m_victim;
+  }
+
+private:
+  path_var m_victim;
+};
+
+/* FIXME.  */
+
+std::string
+path_var::to_str () const
+{
+  if (m_tree == NULL_TREE)
+    return "NULL";
+  if (CONSTANT_CLASS_P (m_tree))
+    return expr_to_string (m_tree);
+  else
+    return (std::string ("(")
+	    + expr_to_string (m_tree)
+	    + " @ "
+	    + std::to_string (m_stack_depth)
+	    + ")");
+}
+
+/* FIXME.  */
+
+equiv_class::equiv_class ()
+: m_constant (NULL_TREE), m_vars (), m_escaped (false), m_extra_state (NULL)
+{
+}
+
+/* FIXME.  */
+
+equiv_class::equiv_class (const equiv_class &other)
+: m_constant (other.m_constant),
+  m_vars (),
+  m_escaped (other.m_escaped),
+  m_extra_state (other.m_extra_state ? other.m_extra_state->clone () : NULL)
+{
+  // FIXME: copy-ctor for auto_vec!
+  int i;
+  path_var *v;
+  FOR_EACH_VEC_ELT (other.m_vars, i, v)
+    m_vars.safe_push (*v);
+}
+
+/* FIXME.  */
+
+equiv_class::~equiv_class ()
+{
+  delete m_extra_state;
+}
+
+/* FIXME.  */
+
+std::string
+equiv_class::to_str () const
+{
+  std::string result ("{");
+  int i;
+  path_var *v;
+  FOR_EACH_VEC_ELT (m_vars, i, v)
+    {
+      gcc_assert (!CONSTANT_CLASS_P (v->m_tree));
+      if (i > 0)
+	result += " == ";
+      result += v->to_str ();
+    }
+  if (m_constant)
+    {
+      if (i > 0)
+	result += " == ";
+      result += expr_to_string (m_constant);
+    }
+  if (m_escaped)
+    result += " (escaped)";
+  if (m_extra_state)
+    result += m_extra_state->to_str ();
+  return result + "}";
+}
+
+/* FIXME.  */
+
+hashval_t
+equiv_class::hash () const
+{
+  inchash::hash hstate;
+  int i;
+  path_var *pv;
+
+  inchash::add_expr (m_constant, hstate);
+  FOR_EACH_VEC_ELT (m_vars, i, pv)
+    inchash::add_path_var (*pv, hstate);
+  hstate.add_flag (m_escaped);
+  hstate.commit_flag ();
+  if (m_extra_state)
+    hstate.merge_hash (m_extra_state->hash ());
+  return hstate.end ();
+}
+
+/* FIXME.  */
+
+bool
+equiv_class::operator== (const equiv_class &other)
+{
+  if (m_constant != other.m_constant)
+    return false; // TODO: use tree equality here
+
+  if (m_vars.length () != other.m_vars.length ())
+    return false;
+
+  int i;
+  path_var *pv;
+  FOR_EACH_VEC_ELT (m_vars, i, pv)
+    if (! (*pv == other.m_vars[i]))
+      return false;
+  if (m_escaped != other.m_escaped)
+    return false;
+
+  if ((m_extra_state != NULL) != (other.m_extra_state != NULL))
+    return false;
+
+  if (m_extra_state != NULL)
+    if (!(*m_extra_state == *other.m_extra_state))
+      return false;
+
+  return true;
+}
+
+/* FIXME.  */
+
+void
+equiv_class::add (path_var v)
+{
+  if (CONSTANT_CLASS_P (v.m_tree))
+    {
+      gcc_assert (m_constant == NULL_TREE);
+      m_constant = v.m_tree;
+    }
+  else
+    {
+      m_vars.safe_push (v);
+
+      if (DECL_P (v.m_tree)
+	  && is_global_var (v.m_tree))
+	m_escaped = true;
+    }
+}
+
+/* Remove V from this equivalence class.
+   Return true if V was the last var in the equivalence class (suggesting
+   a possible leak).  */
+
+bool
+equiv_class::del (path_var v)
+{
+  gcc_assert (!CONSTANT_CLASS_P (v.m_tree));
+
+  int i;
+  path_var *iv;
+  FOR_EACH_VEC_ELT (m_vars, i, iv)
+    {
+      if (*iv == v)
+	{
+	  m_vars[i] = m_vars[m_vars.length () - 1];
+	  m_vars.pop ();
+	  return m_vars.length () == 0;
+	}
+    }
+
+  /* V must be in the class.  */
+  gcc_unreachable ();
+  return false;
+}
+
+/* FIXME.  */
+
+int
+equiv_class::get_min_stack_depth () const
+{
+  int result = INT_MAX;
+
+  int i;
+  path_var *iv;
+  FOR_EACH_VEC_ELT (m_vars, i, iv)
+    result = std::min (result, iv->m_stack_depth);
+  return result;
+}
+
+/* Get a representative member of this class, for handling cases
+   where the IDs can change mid-traversal.  */
+
+path_var
+equiv_class::get_representative () const
+{
+  if (m_constant)
+    return path_var (m_constant, 0);
+  else
+    {
+      gcc_assert (m_vars.length () > 0);
+      return m_vars[0];
+    }
+}
+
+/* FIXME.  */
+
+const char *
+psn::constraint_op_code (enum constraint_op c_op)
+{
+  switch (c_op)
+    {
+    default:
+      gcc_unreachable ();
+    case CONSTRAINT_NE: return "!=";
+    case CONSTRAINT_LT: return "<";
+    case CONSTRAINT_LE: return "<=";
+    }
+}
+
+/* Given "lhs C_OP rhs", determine "lhs T_OP rhs".
+
+   For example, given "x < y", then "x > y" is false.  */
+
+static tristate
+eval_constraint_op_for_op (enum constraint_op c_op, enum tree_code t_op)
+{
+  switch (c_op)
+    {
+    default:
+      gcc_unreachable ();
+    case CONSTRAINT_NE:
+      if (t_op == EQ_EXPR)
+	return tristate (tristate::TS_FALSE);
+      if (t_op == NE_EXPR)
+	return tristate (tristate::TS_TRUE);
+      break;
+    case CONSTRAINT_LT:
+      if (t_op == LT_EXPR || t_op == LE_EXPR || t_op == NE_EXPR)
+	return tristate (tristate::TS_TRUE);
+      if (t_op == EQ_EXPR || t_op == GT_EXPR || t_op == GE_EXPR)
+	return tristate (tristate::TS_FALSE);
+      break;
+    case CONSTRAINT_LE:
+      if (t_op == LE_EXPR)
+	return tristate (tristate::TS_TRUE);
+      if (t_op == GT_EXPR)
+	return tristate (tristate::TS_FALSE);
+      break;
+    }
+  return tristate (tristate::TS_UNKNOWN);
+}
+
+/* FIXME.  */
+
+std::string
+constraint::to_str (const path_state &ps) const
+{
+  return (m_lhs.to_str () + ": "
+	  + m_lhs.get_obj (ps).to_str ()
+	  + " "
+	  + constraint_op_code (m_op)
+	  + " "
+	  + m_rhs.to_str () + ": "
+	  + m_rhs.get_obj (ps).to_str ());
+}
+
+/* FIXME.  */
+
+hashval_t
+constraint::hash () const
+{
+  inchash::hash hstate;
+  hstate.add_int (m_lhs.m_idx);
+  hstate.add_int (m_op);
+  hstate.add_int (m_rhs.m_idx);
+  return hstate.end ();
+}
+
+/* FIXME.  */
+
+bool
+constraint::operator== (const constraint &other) const
+{
+  if (m_lhs != other.m_lhs)
+    return false;
+  if (m_op != other.m_op)
+    return false;
+  if (m_rhs != other.m_rhs)
+    return false;
+  return true;
+}
+
+/* FIXME.  */
+
+const equiv_class &
+equiv_class_id::get_obj (const path_state &ps) const
+{
+  return ps.get_equiv_class_by_index (m_idx);
+}
+
+/* FIXME.  */
+
+equiv_class &
+equiv_class_id::get_obj (path_state &ps) const
+{
+  return ps.get_equiv_class_by_index (m_idx);
+}
+
+/* FIXME.  */
+
+path_state::path_state (const path_state &other)
+: m_stack_depth (other.m_stack_depth)
+{
+  // FIXME: auto_vec's copy_ctor...
+  int i;
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (other.m_equiv_classes, i, ec)
+    m_equiv_classes.safe_push (new equiv_class (*ec));
+  constraint *c;
+  FOR_EACH_VEC_ELT (other.m_constraints, i, c)
+    m_constraints.safe_push (*c);
+}
+
+/* FIXME.  */
+
+path_state::~path_state ()
+{
+  int i;
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    delete ec;
+}
+
+/* FIXME.  */
+
+path_state&
+path_state::operator= (const path_state &other)
+{
+  m_stack_depth = other.m_stack_depth;
+
+  gcc_assert (m_equiv_classes.length () == 0);
+  gcc_assert (m_constraints.length () == 0);
+
+  int i;
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (other.m_equiv_classes, i, ec)
+    m_equiv_classes.safe_push (new equiv_class (*ec));
+  constraint *c;
+  FOR_EACH_VEC_ELT (other.m_constraints, i, c)
+    m_constraints.safe_push (*c);
+
+  return *this;
+}
+
+/* FIXME.  */
+
+hashval_t
+path_state::hash () const
+{
+  inchash::hash hstate;
+  int i;
+  equiv_class *ec;
+  constraint *c;
+
+  hstate.add_int (m_stack_depth);
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    hstate.merge_hash (ec->hash ());
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    hstate.merge_hash (c->hash ());
+  return hstate.end ();
+}
+
+/* FIXME.  */
+
+bool
+path_state::operator== (const path_state &other) const
+{
+  if (m_stack_depth != other.m_stack_depth)
+    return false;
+  if (m_equiv_classes.length () != other.m_equiv_classes.length ())
+    return false;
+
+  int i;
+  equiv_class *ec;
+
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    if (!(*ec == *other.m_equiv_classes[i]))
+      return false;
+
+  constraint *c;
+
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    if (!(*c == other.m_constraints[i]))
+      return false;
+
+  return true;
+}
+
+/* FIXME.  */
+
+std::string
+path_state::to_str () const
+{
+  std::string result ("{");
+  int i;
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    {
+      if (i > 0)
+	result += ", ";
+      result += equiv_class_id (i).to_str () + ": ";
+      result += ec->to_str ();
+    }
+  result += "  |  ";
+  constraint *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      if (i > 0)
+	result += " && ";
+      result += c->to_str (*this);
+    }
+  result += "}@";
+  result += std::to_string (m_stack_depth);
+  return result;
+}
+
+/* FIXME.  */
+
+void
+path_state::on_assignment (path_var lhs, path_var rhs)
+{
+  /* Clear LHS.  */
+  purge_one_path_var p (lhs);
+  purge (p);
+
+  add_constraint (lhs, EQ_EXPR, rhs);
+}
+
+/* FIXME.
+   Attempt to extend this path's state with the constraints required by
+   EDGE (where LAST_STMT, if non-NULL, was the stmt guarding the edge).
+   Return true if the edge can be followed, false if it can't be.  */
+
+bool
+path_state::apply_constraints_for_edge (const superedge &edge,
+					const gimple *last_stmt)
+{
+  if (edge.m_kind == SUPEREDGE_CALL)
+    // FIXME: use as_a<>
+    return apply_constraints_for_call (*edge.dyn_cast_call_superedge ());
+
+  if (edge.m_kind == SUPEREDGE_RETURN)
+    // FIXME: use as_a<>
+    return apply_constraints_for_return (*edge.dyn_cast_return_superedge ());
+
+  if (last_stmt == NULL)
+    return true;
+
+  if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
+    return apply_constraints_for_gcond (edge, cond_stmt);
+
+  // FIXME: some linker issues with is_a_helper const
+  if (const gswitch *switch_stmt = dyn_cast <gswitch *> (const_cast <gimple *> (last_stmt)))
+    return apply_constraints_for_gswitch (edge, switch_stmt);
+
+  return true;
+}
+
+/* FIXME.  */
+
+bool
+path_state::apply_constraints_for_call (const call_superedge& edge)
+{
+  /* Map args to parms at next stack_depth, incrementing stack depth.  */
+  int caller_stack_depth = m_stack_depth;
+  int callee_stack_depth = ++m_stack_depth;
+
+  tree callee = edge.get_callee_decl ();
+  gcall *call_stmt = edge.get_call_stmt ();
+
+  int i = 0;
+  for (tree parm = DECL_ARGUMENTS (callee); parm;
+       parm = DECL_CHAIN (parm), ++i)
+    {
+      tree parm_default_ssa = ssa_default_def (edge.m_dest->m_fun, parm);
+      if (parm_default_ssa)
+	add_constraint (path_var (parm_default_ssa, callee_stack_depth),
+			EQ_EXPR,
+			path_var (gimple_call_arg (call_stmt, i),
+				  caller_stack_depth));
+    }
+  return true;
+}
+
+/* FIXME.  */
+
+bool
+path_state::apply_constraints_for_return (const return_superedge&)
+{
+  pop_stack_frame ();
+  return true;
+}
+
+/* A subclass of purge_criteria: purge anything above a given stack depth.  */
+
+class purge_above_stack_frame : public purge_criteria
+{
+public:
+  purge_above_stack_frame (int max_stack_depth)
+  : m_max_stack_depth (max_stack_depth) {}
+
+  bool should_purge_p (path_var pv) const FINAL OVERRIDE
+  {
+    return pv.m_stack_depth > m_max_stack_depth;
+  }
+
+private:
+  int m_max_stack_depth;
+};
+
+
+/* Decrement the stack depth, purging vars at the old stack depth (and beyond).
+   This is broken out as a subroutine so it can be unit-tested.  */
+
+void
+path_state::pop_stack_frame ()
+{
+  int caller_stack_depth = --m_stack_depth;
+
+  purge_above_stack_frame p (caller_stack_depth);
+  purge (p);
+}
+
+/* FIXME.  */
+
+bool
+path_state::apply_constraints_for_gcond (const superedge &edge,
+					 const gcond *cond_stmt)
+{
+  ::edge cfg_edge = edge.get_any_cfg_edge ();
+  gcc_assert (cfg_edge != NULL);
+  gcc_assert (cfg_edge->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE));
+
+  enum tree_code op = gimple_cond_code (cond_stmt);
+  tree lhs = gimple_cond_lhs (cond_stmt);
+  tree rhs = gimple_cond_rhs (cond_stmt);
+  if (cfg_edge->flags & EDGE_FALSE_VALUE)
+    op = invert_tree_comparison (op, false /* honor_nans */);
+  return add_constraint (path_var (lhs, m_stack_depth),
+			 op,
+			 path_var (rhs, m_stack_depth));
+}
+
+/* FIXME.  */
+
+bool
+path_state::apply_constraints_for_gswitch (const superedge &edge,
+					   const gswitch *switch_stmt)
+{
+  // FIXME: use an as_a for this cast:
+  const switch_cfg_superedge &switch_edge = (const switch_cfg_superedge &)edge;
+  path_var index (gimple_switch_index (switch_stmt), m_stack_depth);
+  tree case_label = switch_edge.get_case_label ();
+  gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
+  path_var lower_bound (CASE_LOW (case_label), m_stack_depth);
+  path_var upper_bound (CASE_HIGH (case_label), m_stack_depth);
+  if (lower_bound.m_tree)
+    {
+      if (upper_bound.m_tree)
+	{
+	  /* Range.  */
+	  if (!add_constraint (index, GE_EXPR, lower_bound))
+	    return false;
+	  return add_constraint (index, LE_EXPR, upper_bound);
+	}
+      else
+	/* Single-value.  */
+	return add_constraint (index, EQ_EXPR, lower_bound);
+    }
+  else
+    {
+      /* The default case.
+	 Add exclusions based on the other cases.  */
+      for (unsigned other_idx = 1;
+	   other_idx < gimple_switch_num_labels (switch_stmt);
+	   other_idx++)
+	{
+	  tree other_label = gimple_switch_label (switch_stmt,
+						  other_idx);
+	  path_var other_lower_bound (CASE_LOW (other_label), m_stack_depth);
+	  path_var other_upper_bound (CASE_HIGH (other_label), m_stack_depth);
+	  gcc_assert (other_lower_bound.m_tree);
+	  if (other_upper_bound.m_tree)
+	    {
+	      /* Exclude this range-valued case.
+		 For now, we just exclude the boundary values.
+		 TODO: exclude the values within the region.  */
+	      if (!add_constraint (index, NE_EXPR, other_lower_bound))
+		return false;
+	      if (!add_constraint (index, NE_EXPR, other_upper_bound))
+		return false;
+	    }
+	  else
+	    /* Exclude this single-valued case.  */
+	    if (!add_constraint (index, NE_EXPR, other_lower_bound))
+	      return false;
+	}
+      return true;
+    }
+}
+
+/* FIXME.  */
+
+bool
+path_state::add_constraint (path_var lhs, enum tree_code op, path_var rhs)
+{
+  equiv_class_id lhs_ec_id = get_or_add_equiv_class (lhs);
+  equiv_class_id rhs_ec_id = get_or_add_equiv_class (rhs);
+  return add_constraint (lhs_ec_id, op,rhs_ec_id);
+}
+
+/* FIXME.  */
+
+bool
+path_state::add_constraint (equiv_class_id lhs_ec_id,
+			    enum tree_code op,
+			    equiv_class_id rhs_ec_id)
+{
+  tristate t = eval_condition (lhs_ec_id, op, rhs_ec_id);
+
+  /* Discard constraints that are already known.  */
+  if (t.is_true ())
+    return true;
+
+  /* Reject unsatisfiable constraints.  */
+  if (t.is_false ())
+    return false;
+
+  gcc_assert (lhs_ec_id != rhs_ec_id);
+
+  /* For now, simply accumulate constraints, without attempting any further
+     optimization.  */
+  switch (op)
+    {
+    case EQ_EXPR:
+      {
+	/* Merge rhs_ec into lhs_ec.  */
+	equiv_class &lhs_ec_obj = lhs_ec_id.get_obj (*this);
+	const equiv_class &rhs_ec_obj = rhs_ec_id.get_obj (*this);
+
+	int i;
+	path_var *v;
+	FOR_EACH_VEC_ELT (rhs_ec_obj.m_vars, i, v)
+	  lhs_ec_obj.add (*v);
+
+	if (rhs_ec_obj.m_constant)
+	  {
+	    gcc_assert (lhs_ec_obj.m_constant == NULL);
+	    lhs_ec_obj.m_constant = rhs_ec_obj.m_constant;
+	  }
+
+	/* Merge any extra state.  */
+	extra_ec_state *lhs_extra_state = lhs_ec_obj.m_extra_state;
+	extra_ec_state *rhs_extra_state = rhs_ec_obj.m_extra_state;
+
+	/* How to merge two state objects?  */
+	gcc_assert (!(lhs_extra_state && rhs_extra_state));
+
+	if (lhs_extra_state == NULL && rhs_extra_state)
+	  lhs_ec_obj.m_extra_state = rhs_extra_state->clone ();
+
+	/* Drop rhs equivalence class, overwriting it with the
+	   final ec (which might be the same one).  */
+	equiv_class_id final_ec_id = m_equiv_classes.length () - 1;
+	equiv_class *old_ec = m_equiv_classes[rhs_ec_id.m_idx];
+	equiv_class *final_ec = m_equiv_classes.pop ();
+	if (final_ec != old_ec)
+	  m_equiv_classes[rhs_ec_id.m_idx] = final_ec;
+	delete old_ec;
+
+	/* Update the constraints.  */
+	constraint *c;
+	FOR_EACH_VEC_ELT (m_constraints, i, c)
+	  {
+	    /* Update references to the rhs_ec so that
+	       they refer to the lhs_ec.  */
+	    if (c->m_lhs == rhs_ec_id)
+	      c->m_lhs = lhs_ec_id;
+	    if (c->m_rhs == rhs_ec_id)
+	      c->m_rhs = lhs_ec_id;
+
+	    /* Renumber all constraints that refer to the final rhs_ec
+	       to the old rhs_ec, where the old final_ec now lives.  */
+	    if (c->m_lhs == final_ec_id)
+	      c->m_lhs = rhs_ec_id;
+	    if (c->m_rhs == final_ec_id)
+	      c->m_rhs = rhs_ec_id;
+	  }
+      }
+      break;
+    case GE_EXPR:
+      add_constraint_internal (rhs_ec_id, CONSTRAINT_LE, lhs_ec_id);
+      break;
+    case LE_EXPR:
+      add_constraint_internal (lhs_ec_id, CONSTRAINT_LE, rhs_ec_id);
+      break;
+    case NE_EXPR:
+      add_constraint_internal (lhs_ec_id, CONSTRAINT_NE, rhs_ec_id);
+      break;
+    case GT_EXPR:
+      add_constraint_internal (rhs_ec_id, CONSTRAINT_LT, lhs_ec_id);
+      break;
+    case LT_EXPR:
+      add_constraint_internal (lhs_ec_id, CONSTRAINT_LT, rhs_ec_id);
+      break;
+    default:
+      /* do nothing.  */
+      break;
+    }
+  validate ();
+  return true;
+}
+
+/* FIXME.  */
+
+void
+bound::ensure_closed (bool is_upper)
+{
+  if (!m_closed)
+    {
+      /* Offset by 1 in the appropriate direction.
+	 For example, convert 3 < x into 4 <= x,
+	 and convert x < 5 into x <= 4.  */
+      gcc_assert (CONSTANT_CLASS_P (m_constant));
+      m_constant = fold_build2 (is_upper ? MINUS_EXPR : PLUS_EXPR,
+				TREE_TYPE (m_constant),
+				m_constant, integer_one_node);
+      gcc_assert (CONSTANT_CLASS_P (m_constant));
+      m_closed = true;
+    }
+}
+
+/* FIXME.  */
+
+std::string
+range::to_str () const
+{
+  std::string result = expr_to_string (m_lower_bound.m_constant);
+  result += " <";
+  if (m_lower_bound.m_closed)
+    result += "=";
+  result += " x <";
+  if (m_upper_bound.m_closed)
+    result += "=";
+  result += " ";
+  result += expr_to_string (m_upper_bound.m_constant);
+  return result;  // TODO
+}
+
+/* FIXME.  */
+
+bool
+range::constrained_to_single_element (tree *out)
+{
+  if (!INTEGRAL_TYPE_P (TREE_TYPE (m_lower_bound.m_constant)))
+    return false;
+  if (!INTEGRAL_TYPE_P (TREE_TYPE (m_upper_bound.m_constant)))
+    return false;
+  // FIXME: also check the endpoint
+
+  /* Convert any open bounds to closed bounds.  */
+  m_lower_bound.ensure_closed (false);
+  m_upper_bound.ensure_closed (true);
+
+  // Are they equal?
+  tree comparison
+    = fold_build2 (EQ_EXPR, boolean_type_node,
+		   m_lower_bound.m_constant,
+		   m_upper_bound.m_constant);
+  if (comparison == boolean_true_node)
+    {
+      *out = m_lower_bound.m_constant;
+      return true;
+    }
+  else
+    return false;
+}
+
+
+/* FIXME.  */
+
+void
+path_state::add_constraint_internal (equiv_class_id lhs_id,
+				     enum constraint_op c_op,
+				     equiv_class_id rhs_id)
+{
+  /* Add the constraint.  */
+  m_constraints.safe_push (constraint (lhs_id, c_op, rhs_id));
+
+  if (c_op != CONSTRAINT_NE)
+    {
+      /* The following can potentially add EQ_EXPR facts, which could lead
+	 to ECs being merged, which would change the meaning of the EC IDs.
+	 Hence we need to do this via path vars.  */
+      path_var lhs = lhs_id.get_obj (*this).get_representative ();
+      path_var rhs = rhs_id.get_obj (*this).get_representative ();
+
+      /* We have LHS </<= RHS */
+
+      /* Handle transitivity of ordering by adding additional constraints
+	 based on what we already knew.
+
+	 So if we have already have:
+	   (a < b)
+	   (c < d)
+	 Then adding:
+	   (b < c)
+	 will also add:
+	   (a < c)
+	   (b < d)
+	 We need to recurse to ensure we also add:
+	   (a < d).
+	 We call the checked add_constraint to avoid adding constraints
+	 that are already present.  Doing so also ensures termination
+	 in the case of cycles.
+
+	 We also check for single-element ranges, adding EQ_EXPR facts
+	 where we discover them.  For example 3 < x < 5 implies
+	 that x == 4 (if x is an integer).  */
+      for (unsigned i = 0; i < m_constraints.length (); i++)
+	{
+	  const constraint *other = &m_constraints[i];
+	  if (other->is_ordering_p ())
+	    {
+	      /* Refresh the EC IDs, in case any mergers have happened.  */
+	      lhs_id = get_or_add_equiv_class (lhs);
+	      rhs_id = get_or_add_equiv_class (rhs);
+
+	      tree lhs_const = lhs_id.get_obj (*this).m_constant;
+	      tree rhs_const = rhs_id.get_obj (*this).m_constant;
+	      tree other_lhs_const
+		= other->m_lhs.get_obj (*this).m_constant;
+	      tree other_rhs_const
+		= other->m_rhs.get_obj (*this).m_constant;
+
+	      /* We have "LHS </<= RHS" and "other.lhs </<= other.rhs".  */
+
+	      /* If we have LHS </<= RHS and RHS </<= LHS, then we have a
+		 cycle.  */
+	      if (rhs_id == other->m_lhs
+		  && other->m_rhs == lhs_id)
+		{
+		  /* We must have equality for this to be possible.  */
+		  gcc_assert (c_op == CONSTRAINT_LE
+			      && other->m_op == CONSTRAINT_LE);
+		  add_constraint (lhs_id, EQ_EXPR, rhs_id);
+		  /* Adding an equality will merge the two ECs and potentially
+		     reorganize the constraints.  Stop iterating.  */
+		  return;
+		}
+	      /* Otherwise, check for transitivity.  */
+	      if (rhs_id == other->m_lhs)
+		{
+		  /* With RHS == other.lhs, we have:
+		     "LHS </<= (RHS, other.lhs) </<= other.rhs"
+		     and thus this implies "LHS </<= other.rhs".  */
+
+		  /* Do we have a tightly-constrained range?  */
+		  if (lhs_const
+		      && !rhs_const
+		      && INTEGRAL_TYPE_P (TREE_TYPE (rhs.m_tree))
+		      && other_rhs_const)
+		    {
+		      range r (bound (lhs_const, c_op == CONSTRAINT_LE),
+			       bound (other_rhs_const,
+				      other->m_op == CONSTRAINT_LE));
+		      tree constant;
+		      if (r.constrained_to_single_element (&constant))
+			{
+			  add_constraint
+			    (rhs_id, EQ_EXPR,
+			     get_or_add_equiv_class (path_var (constant, 0)));
+			  return;
+			}
+		    }
+		  /* Otherwise, add the constraint implied by transitivity.  */
+		  enum tree_code new_op
+		    = ((c_op == CONSTRAINT_LE && other->m_op == CONSTRAINT_LE)
+		       ? LE_EXPR : LT_EXPR);
+		  add_constraint (lhs_id, new_op, other->m_rhs);
+		}
+	      else if (other->m_rhs == lhs_id)
+		{
+		  /* With other.rhs == LHS, we have:
+		     "other.lhs </<= (other.rhs, LHS) </<= RHS"
+		     and thus this implies "other.lhs </<= RHS".  */
+
+		  /* Do we have a tightly-constrained range?  */
+		  if (other_lhs_const
+		      && !lhs_const
+		      && INTEGRAL_TYPE_P (TREE_TYPE (lhs.m_tree))
+		      && rhs_const)
+		    {
+		      range r (bound (other_lhs_const,
+				      other->m_op == CONSTRAINT_LE),
+			       bound (rhs_const,
+				      c_op == CONSTRAINT_LE));
+		      tree constant;
+		      if (r.constrained_to_single_element (&constant))
+			{
+			  add_constraint
+			    (lhs_id, EQ_EXPR,
+			     get_or_add_equiv_class (path_var (constant, 0)));
+			  return;
+			}
+		    }
+		  /* Otherwise, add the constraint implied by transitivity.  */
+		  enum tree_code new_op
+		    = ((c_op == CONSTRAINT_LE && other->m_op == CONSTRAINT_LE)
+		       ? LE_EXPR : LT_EXPR);
+		  add_constraint (other->m_lhs, new_op, rhs_id);
+		}
+	    }
+	}
+    }
+}
+
+/* FIXME.  */
+
+bool
+path_state::get_equiv_class (path_var pv, equiv_class_id *out) const
+{
+  gcc_assert (pv.m_tree);
+
+  if (CONSTANT_CLASS_P (pv.m_tree))
+    {
+      // FIXME: we want to ensure that constants that are equal are treated as equal here
+      int i;
+      equiv_class *ec;
+      FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+	if (pv.m_tree == ec->m_constant)
+	  {
+	    *out = equiv_class_id (i);
+	    return true;
+	  }
+    }
+  else
+    {
+      /* FIXME: probably should be a map, rather than these lookups.  */
+      int i;
+      equiv_class *ec;
+      FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+	{
+	  int j;
+	  path_var *v;
+	  FOR_EACH_VEC_ELT (ec->m_vars, j, v)
+	    if (*v == pv)
+	      {
+		*out = equiv_class_id (i);
+		return true;
+	      }
+	}
+    }
+  return false;
+}
+
+/* FIXME.  */
+
+equiv_class_id
+path_state::get_or_add_equiv_class (path_var pv)
+{
+  gcc_assert (pv.m_tree);
+
+  equiv_class_id result (-1);
+  if (get_equiv_class (pv, &result))
+    return result;
+
+  /* Not found.  */
+  equiv_class *new_ec = new equiv_class ();
+  new_ec->add (pv);
+  m_equiv_classes.safe_push (new_ec);
+
+  equiv_class_id new_id (m_equiv_classes.length () - 1);
+
+  if (CONSTANT_CLASS_P (pv.m_tree))
+    {
+      /* If we have a new EC for a constant, add constraints comparing this
+	 to other constants we may have (so that we accumulate the transitive
+	 closure of all constraints on constants as the constants are
+	 added).  */
+      for (equiv_class_id other_id (0); other_id.m_idx < new_id.m_idx;
+	   other_id.m_idx++)
+	{
+	  const equiv_class &other_ec = other_id.get_obj (*this);
+	  if (other_ec.m_constant)
+	    {
+	      /* If we have two ECs, both with constants, the constants must be
+		 non-equal (or they would be in the same EC).
+		 Determine the direction of the inequality, and record that
+		 fact.  */
+	      tree lt
+		= fold_build2 (LT_EXPR, boolean_type_node,
+			       new_ec->m_constant, other_ec.m_constant);
+	      //gcc_assert (lt == boolean_true_node || lt == boolean_false_node);
+	      // not true for int vs float comparisons
+	      if (lt == boolean_true_node)
+		add_constraint_internal (new_id, CONSTRAINT_LT, other_id);
+	      else if (lt == boolean_false_node)
+		add_constraint_internal (other_id, CONSTRAINT_LT, new_id);
+	      /* Refresh new_id, in case ECs were merged.  PV should always
+		 be present by now, so this should never lead to a
+		 recursion.  */
+	      new_id = get_or_add_equiv_class (pv);
+	    }
+	}
+    }
+
+  return new_id;
+}
+
+/* FIXME.  */
+/* For implementing "__analyzer_eval".  */
+
+tristate
+path_state::dump_condition (tree lhs, enum tree_code op, tree rhs)
+{
+  equiv_class_id lhs_ec_id
+    = get_or_add_equiv_class (path_var (lhs, m_stack_depth));
+  equiv_class_id rhs_ec_id
+    = get_or_add_equiv_class (path_var (rhs, m_stack_depth));
+
+  return eval_condition (lhs_ec_id, op, rhs_ec_id);
+}
+
+/* FIXME.  */
+
+tristate
+path_state::eval_condition (equiv_class_id lhs_ec,
+			    enum tree_code op,
+			    equiv_class_id rhs_ec)
+{
+  if (lhs_ec == rhs_ec)
+    {
+      switch (op)
+	{
+	case EQ_EXPR:
+	case GE_EXPR:
+	case LE_EXPR:
+	  return tristate (tristate::TS_TRUE);
+
+	case NE_EXPR:
+	case GT_EXPR:
+	case LT_EXPR:
+	  return tristate (tristate::TS_FALSE);
+	default:
+	  break;
+	}
+    }
+
+  tree lhs_const = lhs_ec.get_obj (*this).get_any_constant ();
+  tree rhs_const = rhs_ec.get_obj (*this).get_any_constant ();
+  if (lhs_const && rhs_const)
+    {
+      tree comparison
+	= fold_build2 (op, boolean_type_node, lhs_const, rhs_const);
+      if (comparison == boolean_true_node)
+	return tristate (tristate::TS_TRUE);
+      if (comparison == boolean_false_node)
+	return tristate (tristate::TS_FALSE);
+    }
+
+  enum tree_code swapped_op = swap_tree_comparison (op);
+
+  int i;
+  constraint *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      if (c->m_lhs == lhs_ec
+	  && c->m_rhs == rhs_ec)
+	{
+	  tristate result_for_constraint
+	    = eval_constraint_op_for_op (c->m_op, op);
+	  if (result_for_constraint.is_known ())
+	    return result_for_constraint;
+	}
+      /* Swapped operands.  */
+      if (c->m_lhs == rhs_ec
+	  && c->m_rhs == lhs_ec)
+	{
+	  tristate result_for_constraint
+	    = eval_constraint_op_for_op (c->m_op, swapped_op);
+	  if (result_for_constraint.is_known ())
+	    return result_for_constraint;
+	}
+    }
+
+  return tristate (tristate::TS_UNKNOWN);
+}
+
+/* Mark EC_ID as having escaped; walk other ECs that haven't yet been marked
+   as escaped, and determine if they've now escaped, if so, mark them,
+   recursively.
+   e.g. if we know "ptr" escapes, then "ptr->foo" escapes.  */
+
+void
+path_state::recursively_mark_as_escaping (equiv_class_id ec_id)
+{
+  equiv_class &ec = ec_id.get_obj (*this);
+  if (ec.m_escaped)
+    {
+      /* Nothing to do.  */
+      return;
+    }
+
+  ec.m_escaped = true;
+
+  /* Look for other ECs that haven't yet been marked as escaped.  */
+  int other_ec_idx;
+  equiv_class *other_ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, other_ec_idx, other_ec)
+    {
+      if (other_ec->m_escaped)
+	continue;
+
+      int var_idx;
+      path_var *v;
+      FOR_EACH_VEC_ELT (other_ec->m_vars, var_idx, v)
+	{
+	  gcc_assert (!CONSTANT_CLASS_P (v->m_tree));
+	  if (calc_escaped_p (*v))
+	    {
+	      recursively_mark_as_escaping (other_ec_idx);
+	      break;
+	    }
+	}
+    }
+
+  // TODO: selftests
+  // TODO: selftest involving a cycle
+}
+
+/* Determine if PV (or something that points to it) has been marked as
+   having escaped.  */
+
+bool
+path_state::calc_escaped_p (path_var pv)
+{
+  switch (TREE_CODE (pv.m_tree))
+    {
+    case COMPONENT_REF:
+    case MEM_REF:
+      return calc_escaped_p (path_var (TREE_OPERAND (pv.m_tree, 0),
+				       pv.m_stack_depth));
+    case SSA_NAME:
+      {
+	equiv_class_id ec_id (-1);
+	if (get_equiv_class (pv, &ec_id))
+	  return ec_id.get_obj (*this).m_escaped;
+      }
+      break;
+    default:
+      return false;
+    }
+
+  return false;
+}
+
+/* Delete any information about path_var instances identified by P.
+   Such instances are removed from equivalence classes, and any
+   redundant ECs and constraints are also removed.
+   Return the number of path_vars that are purged.  */
+
+int
+path_state::purge (const purge_criteria &p)
+{
+  int num_purged = 0;
+
+  /* Delete any path_vars identified by P within the various equivalence
+     classes.  */
+  for (unsigned ec_idx = 0; ec_idx < m_equiv_classes.length (); )
+    {
+      equiv_class *ec = m_equiv_classes[ec_idx];
+
+      int i;
+      path_var *pv;
+      bool delete_ec = false;
+      FOR_EACH_VEC_ELT (ec->m_vars, i, pv)
+	{
+	  if (p.should_purge_p (*pv))
+	    {
+	      num_purged++;
+	      if (ec->del (*pv))
+		if (!ec->m_constant)
+		  delete_ec = true;
+	    }
+	}
+
+      if (delete_ec)
+	{
+	  delete ec;
+	  m_equiv_classes.ordered_remove (ec_idx);
+
+	  /* Update the constraints, potentially removing some.  */
+	  for (unsigned con_idx = 0; con_idx < m_constraints.length (); )
+	    {
+	      constraint *c = &m_constraints[con_idx];
+
+	      /* Remove constraints that refer to the deleted EC.  */
+	      if (c->m_lhs == ec_idx
+		  || c->m_rhs == ec_idx)
+		{
+		  m_constraints.ordered_remove (con_idx);
+		}
+	      else
+		{
+		  /* Renumber constraints that refer to ECs that have
+		     had their idx changed.  */
+		  c->m_lhs.update_for_removal (ec_idx);
+		  c->m_rhs.update_for_removal (ec_idx);
+
+		  con_idx++;
+		}
+	    }
+	}
+      else
+	ec_idx++;
+    }
+
+  /* Now delete any constraints that are purely between constants.  */
+  for (unsigned con_idx = 0; con_idx < m_constraints.length (); )
+    {
+      constraint *c = &m_constraints[con_idx];
+      if (m_equiv_classes[c->m_lhs.m_idx]->m_vars.length () == 0
+	  && m_equiv_classes[c->m_rhs.m_idx]->m_vars.length () == 0)
+	m_constraints.ordered_remove (con_idx);
+      else
+	{
+	  con_idx++;
+	}
+    }
+
+  /* Finally, delete any ECs that purely contain constants and aren't
+     referenced by any constraints.  */
+  for (unsigned ec_idx = 0; ec_idx < m_equiv_classes.length (); )
+    {
+      equiv_class *ec = m_equiv_classes[ec_idx];
+      if (ec->m_vars.length () == 0)
+	{
+	  bool has_constraint = false;
+	  for (unsigned con_idx = 0; con_idx < m_constraints.length ();
+	       con_idx++)
+	    {
+	      constraint *c = &m_constraints[con_idx];
+	      if (c->m_lhs.m_idx == ec_idx
+		  || c->m_rhs.m_idx == ec_idx)
+		{
+		  has_constraint = true;
+		  break;
+		}
+	    }
+	  if (!has_constraint)
+	    {
+	      delete ec;
+	      m_equiv_classes.ordered_remove (ec_idx);
+
+	      /* Renumber constraints that refer to ECs that have
+		 had their idx changed.  */
+	      for (unsigned con_idx = 0; con_idx < m_constraints.length ();
+		   con_idx++)
+		{
+		  constraint *c = &m_constraints[con_idx];
+		  c->m_lhs.update_for_removal (ec_idx);
+		  c->m_rhs.update_for_removal (ec_idx);
+		}
+	      continue;
+	    }
+	}
+      ec_idx++;
+    }
+
+  validate ();
+
+  return num_purged;
+}
+
+/* FIXME.  */
+
+void
+path_state::validate () const
+{
+  // TODO: skip this in a release build
+
+  int i;
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    {
+      gcc_assert (ec);
+
+      int j;
+      path_var *pv;
+      FOR_EACH_VEC_ELT (ec->m_vars, j, pv)
+	{
+	  gcc_assert (!CONSTANT_CLASS_P (pv->m_tree));
+	  //gcc_assert (pv->m_stack_depth <= m_stack_depth);
+	}
+      if (ec->m_constant)
+	gcc_assert (CONSTANT_CLASS_P (ec->m_constant));
+      else
+	gcc_assert (ec->m_vars.length () > 0);
+    }
+
+  constraint *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      gcc_assert (c->m_lhs.m_idx <= m_equiv_classes.length ());
+      gcc_assert (c->m_rhs.m_idx <= m_equiv_classes.length ());
+    }
+}
+
+// FIXME:
+void
+debug (const path_state &ps)
+{
+  fprintf (stderr, "%s\n", ps.to_str ().c_str ());
+}
+
+void
+inchash::add_path_var (path_var pv, hash &hstate)
+{
+  inchash::add_expr (pv.m_tree, hstate);
+  hstate.add_int (pv.m_stack_depth);
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Attempt to add the constraint (LHS OP RHS) to PS.
+   Verify that PS remains satisfiable.  */
+
+#define ADD_SAT_CONSTRAINT(PS, LHS, OP, RHS)				\
+  SELFTEST_BEGIN_STMT							\
+    bool sat = (PS).add_constraint (LHS, OP, RHS);			\
+    ASSERT_TRUE (sat);							\
+  SELFTEST_END_STMT
+
+/* Attempt to add the constraint (LHS OP RHS) to PS.
+   Verify that the result is not satisfiable.  */
+
+#define ADD_UNSAT_CONSTRAINT(PS, LHS, OP, RHS)				\
+  SELFTEST_BEGIN_STMT							\
+    bool sat = (PS).add_constraint (LHS, OP, RHS);			\
+    ASSERT_FALSE (sat);							\
+  SELFTEST_END_STMT
+
+static void
+assert_condition (const location &loc,
+		  path_state &ps,
+		  path_var lhs, tree_code op, path_var rhs,
+		  tristate expected)
+{
+  equiv_class_id lhs_ec = ps.get_or_add_equiv_class (lhs);
+  equiv_class_id rhs_ec = ps.get_or_add_equiv_class (rhs);
+  tristate actual = ps.eval_condition (lhs_ec, op, rhs_ec);
+  ASSERT_EQ_AT (loc, actual, expected);
+}
+
+#define ASSERT_CONDITION_TRUE(PATH_STATE, LHS, OP, RHS) \
+  SELFTEST_BEGIN_STMT							\
+  assert_condition (SELFTEST_LOCATION, PATH_STATE, LHS, OP, RHS,	\
+		    tristate (tristate::TS_TRUE));		\
+  SELFTEST_END_STMT
+
+#define ASSERT_CONDITION_FALSE(PATH_STATE, LHS, OP, RHS) \
+  SELFTEST_BEGIN_STMT							\
+  assert_condition (SELFTEST_LOCATION, PATH_STATE, LHS, OP, RHS,	\
+		    tristate (tristate::TS_FALSE));		\
+  SELFTEST_END_STMT
+
+#define ASSERT_CONDITION_UNKNOWN(PATH_STATE, LHS, OP, RHS) \
+  SELFTEST_BEGIN_STMT							\
+  assert_condition (SELFTEST_LOCATION, PATH_STATE, LHS, OP, RHS,	\
+		    tristate (tristate::TS_UNKNOWN));		\
+  SELFTEST_END_STMT
+
+/* FIXME.  */
+
+static void
+test_constraint_conditions ()
+{
+  path_var int_42 (build_int_cst (integer_type_node, 42), 0);
+  path_var int_0 (build_int_cst (integer_type_node, 0), 0);
+
+  path_var x (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("x"),
+			  integer_type_node), 0);
+  path_var y (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("y"),
+			  integer_type_node), 0);
+  path_var z (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("z"),
+			  integer_type_node), 0);
+
+  /* Self-comparisons.  */
+  {
+    path_state ps;
+    ASSERT_CONDITION_TRUE (ps, x, EQ_EXPR, x);
+    ASSERT_CONDITION_TRUE (ps, x, LE_EXPR, x);
+    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, x);
+    ASSERT_CONDITION_FALSE (ps, x, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE (ps, x, LT_EXPR, x);
+    ASSERT_CONDITION_FALSE (ps, x, GT_EXPR, x);
+  }
+
+  /* x == y.  */
+  {
+    path_state ps;
+    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, y);
+
+    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, y);
+
+    ASSERT_CONDITION_TRUE (ps, x, EQ_EXPR, y);
+    ASSERT_CONDITION_TRUE (ps, x, LE_EXPR, y);
+    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, LT_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, GT_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_TRUE (ps, y, EQ_EXPR, x);
+    ASSERT_CONDITION_TRUE (ps, y, LE_EXPR, x);
+    ASSERT_CONDITION_TRUE (ps, y, GE_EXPR, x);
+    ASSERT_CONDITION_FALSE (ps, y, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE (ps, y, LT_EXPR, x);
+    ASSERT_CONDITION_FALSE (ps, y, GT_EXPR, x);
+
+    /* Comparison with other var.  */
+    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (ps, x, LE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (ps, x, NE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (ps, x, LT_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (ps, x, GT_EXPR, z);
+  }
+
+  /* x == y, then y == z  */
+  {
+    path_state ps;
+    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, y);
+
+    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, y);
+    ADD_SAT_CONSTRAINT (ps, y, EQ_EXPR, z);
+
+    ASSERT_CONDITION_TRUE (ps, x, EQ_EXPR, z);
+    ASSERT_CONDITION_TRUE (ps, x, LE_EXPR, z);
+    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, z);
+    ASSERT_CONDITION_FALSE (ps, x, NE_EXPR, z);
+    ASSERT_CONDITION_FALSE (ps, x, LT_EXPR, z);
+    ASSERT_CONDITION_FALSE (ps, x, GT_EXPR, z);
+  }
+
+  /* x != y.  */
+  {
+    path_state ps;
+
+    ADD_SAT_CONSTRAINT (ps, x, NE_EXPR, y);
+
+    ASSERT_CONDITION_TRUE (ps, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, EQ_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (ps, x, LE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (ps, x, LT_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (ps, x, GT_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_TRUE (ps, y, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE (ps, y, EQ_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (ps, y, LE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (ps, y, GE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (ps, y, LT_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (ps, y, GT_EXPR, x);
+
+    /* Comparison with other var.  */
+    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (ps, x, LE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (ps, x, NE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (ps, x, LT_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (ps, x, GT_EXPR, z);
+  }
+
+  /* x < y.  */
+  {
+    path_state ps;
+
+    ADD_SAT_CONSTRAINT (ps, x, LT_EXPR, y);
+
+    ASSERT_CONDITION_TRUE (ps, x, LT_EXPR, y);
+    ASSERT_CONDITION_TRUE (ps, x, LE_EXPR, y);
+    ASSERT_CONDITION_TRUE (ps, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, EQ_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, GT_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, GE_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_FALSE (ps, y, LT_EXPR, x);
+    ASSERT_CONDITION_FALSE (ps, y, LE_EXPR, x);
+    ASSERT_CONDITION_TRUE (ps, y, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE (ps, y, EQ_EXPR, x);
+    ASSERT_CONDITION_TRUE (ps, y, GT_EXPR, x);
+    ASSERT_CONDITION_TRUE (ps, y, GE_EXPR, x);
+  }
+
+  /* x <= y.  */
+  {
+    path_state ps;
+
+    ADD_SAT_CONSTRAINT (ps, x, LE_EXPR, y);
+
+    ASSERT_CONDITION_UNKNOWN (ps, x, LT_EXPR, y);
+    ASSERT_CONDITION_TRUE (ps, x, LE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (ps, x, NE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, GT_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_FALSE (ps, y, LT_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (ps, y, LE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (ps, y, NE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (ps, y, EQ_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (ps, y, GT_EXPR, x);
+    ASSERT_CONDITION_TRUE (ps, y, GE_EXPR, x);
+  }
+
+  /* x > y.  */
+  {
+    path_state ps;
+
+    ADD_SAT_CONSTRAINT (ps, x, GT_EXPR, y);
+
+    ASSERT_CONDITION_TRUE (ps, x, GT_EXPR, y);
+    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, y);
+    ASSERT_CONDITION_TRUE (ps, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, EQ_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, LT_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, LE_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_FALSE (ps, y, GT_EXPR, x);
+    ASSERT_CONDITION_FALSE (ps, y, GE_EXPR, x);
+    ASSERT_CONDITION_TRUE (ps, y, NE_EXPR, x);
+    ASSERT_CONDITION_FALSE (ps, y, EQ_EXPR, x);
+    ASSERT_CONDITION_TRUE (ps, y, LT_EXPR, x);
+    ASSERT_CONDITION_TRUE (ps, y, LE_EXPR, x);
+  }
+
+  /* x >= y.  */
+  {
+    path_state ps;
+
+    ADD_SAT_CONSTRAINT (ps, x, GE_EXPR, y);
+
+    ASSERT_CONDITION_UNKNOWN (ps, x, GT_EXPR, y);
+    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (ps, x, NE_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, LT_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (ps, x, LE_EXPR, y);
+
+    /* Swapped operands.  */
+    ASSERT_CONDITION_FALSE (ps, y, GT_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (ps, y, GE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (ps, y, NE_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (ps, y, EQ_EXPR, x);
+    ASSERT_CONDITION_UNKNOWN (ps, y, LT_EXPR, x);
+    ASSERT_CONDITION_TRUE (ps, y, LE_EXPR, x);
+  }
+
+  // TODO: implied orderings
+
+  /* Constants.  */
+  {
+    path_state ps;
+    ASSERT_CONDITION_FALSE (ps, int_0, EQ_EXPR, int_42);
+    ASSERT_CONDITION_TRUE (ps, int_0, NE_EXPR, int_42);
+    ASSERT_CONDITION_TRUE (ps, int_0, LT_EXPR, int_42);
+    ASSERT_CONDITION_TRUE (ps, int_0, LE_EXPR, int_42);
+    ASSERT_CONDITION_FALSE (ps, int_0, GT_EXPR, int_42);
+    ASSERT_CONDITION_FALSE (ps, int_0, GE_EXPR, int_42);
+  }
+
+  /* x == 0, y == 42.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, int_0);
+    ADD_SAT_CONSTRAINT (ps, y, EQ_EXPR, int_42);
+
+    ASSERT_CONDITION_TRUE (ps, x, NE_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, EQ_EXPR, y);
+    ASSERT_CONDITION_TRUE (ps, x, LE_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, GE_EXPR, y);
+    ASSERT_CONDITION_TRUE (ps, x, LT_EXPR, y);
+    ASSERT_CONDITION_FALSE (ps, x, GT_EXPR, y);
+  }
+
+  /* Unsatisfiable combinations.  */
+
+  /* x == y && x != y.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, y);
+    ADD_UNSAT_CONSTRAINT (ps, x, NE_EXPR, y);
+  }
+
+  /* x == 0 then x == 42.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, int_0);
+    ADD_UNSAT_CONSTRAINT (ps, x, EQ_EXPR, int_42);
+  }
+
+  /* x == 0 then x != 0.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, int_0);
+    ADD_UNSAT_CONSTRAINT (ps, x, NE_EXPR, int_0);
+  }
+
+  /* x == 0 then x > 0.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, int_0);
+    ADD_UNSAT_CONSTRAINT (ps, x, GT_EXPR, int_0);
+  }
+
+  /* x != y && x == y.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, x, NE_EXPR, y);
+    ADD_UNSAT_CONSTRAINT (ps, x, EQ_EXPR, y);
+  }
+
+  /* x <= y && x > y.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, x, LE_EXPR, y);
+    ADD_UNSAT_CONSTRAINT (ps, x, GT_EXPR, y);
+  }
+
+  // etc
+}
+
+/* FIXME.  */
+
+static void
+test_transitivity ()
+{
+  path_var int_42 (build_int_cst (integer_type_node, 42), 0);
+  path_var int_0 (build_int_cst (integer_type_node, 0), 0);
+
+  path_var a (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("a"),
+			  integer_type_node), 0);
+  path_var b (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("b"),
+			  integer_type_node), 0);
+  path_var c (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("c"),
+			  integer_type_node), 0);
+  path_var d (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("d"),
+			  integer_type_node), 0);
+
+  /* a == b, then c == d, then c == b.  */
+  {
+    path_state ps;
+    ASSERT_CONDITION_UNKNOWN (ps, a, EQ_EXPR, b);
+    ASSERT_CONDITION_UNKNOWN (ps, b, EQ_EXPR, c);
+    ASSERT_CONDITION_UNKNOWN (ps, c, EQ_EXPR, d);
+    ASSERT_CONDITION_UNKNOWN (ps, a, EQ_EXPR, d);
+
+    ADD_SAT_CONSTRAINT (ps, a, EQ_EXPR, b);
+    ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, b);
+
+    ADD_SAT_CONSTRAINT (ps, c, EQ_EXPR, d);
+    ASSERT_CONDITION_TRUE (ps, c, EQ_EXPR, d);
+    ASSERT_CONDITION_UNKNOWN (ps, a, EQ_EXPR, d);
+
+    ADD_SAT_CONSTRAINT (ps, c, EQ_EXPR, b);
+    ASSERT_CONDITION_TRUE (ps, c, EQ_EXPR, b);
+    ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, d);
+  }
+
+  /* Transitivity: "a < b", "b < c" should imply "a < c".  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, LT_EXPR, b);
+    ADD_SAT_CONSTRAINT (ps, b, LT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (ps, a, LT_EXPR, c);
+    ASSERT_CONDITION_FALSE (ps, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a <= b", "b < c" should imply "a < c".  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, b);
+    ADD_SAT_CONSTRAINT (ps, b, LT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (ps, a, LT_EXPR, c);
+    ASSERT_CONDITION_FALSE (ps, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a <= b", "b <= c" should imply "a <= c".  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, b);
+    ADD_SAT_CONSTRAINT (ps, b, LE_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (ps, a, LE_EXPR, c);
+    ASSERT_CONDITION_UNKNOWN (ps, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a > b", "b > c" should imply "a > c".  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, b);
+    ADD_SAT_CONSTRAINT (ps, b, GT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (ps, a, GT_EXPR, c);
+    ASSERT_CONDITION_FALSE (ps, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a >= b", "b > c" should imply " a > c".  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, b);
+    ADD_SAT_CONSTRAINT (ps, b, GT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (ps, a, GT_EXPR, c);
+    ASSERT_CONDITION_FALSE (ps, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a >= b", "b >= c" should imply "a >= c".  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, b);
+    ADD_SAT_CONSTRAINT (ps, b, GE_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (ps, a, GE_EXPR, c);
+    ASSERT_CONDITION_UNKNOWN (ps, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "(a < b)", "(c < d)", "(b < c)" should
+     imply the easy cases:
+       (a < c)
+       (b < d)
+     but also that:
+       (a < d).  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, LT_EXPR, b);
+    ADD_SAT_CONSTRAINT (ps, c, LT_EXPR, d);
+    ADD_SAT_CONSTRAINT (ps, b, LT_EXPR, c);
+
+    ASSERT_CONDITION_TRUE (ps, a, LT_EXPR, c);
+    ASSERT_CONDITION_TRUE (ps, b, LT_EXPR, d);
+    ASSERT_CONDITION_TRUE (ps, a, LT_EXPR, d);
+  }
+
+  /* Transitivity: "a >= b", "b >= a" should imply that a == b.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, b);
+    ADD_SAT_CONSTRAINT (ps, b, GE_EXPR, a);
+
+    // TODO:
+    ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, b);
+  }
+
+  /* Transitivity: "a >= b", "b > a" should be impossible.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, b);
+    ADD_UNSAT_CONSTRAINT (ps, b, GT_EXPR, a);
+  }
+
+  /* Transitivity: "a >= b", "b >= c", "c >= a" should imply
+     that a == b == c.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, b);
+    ADD_SAT_CONSTRAINT (ps, b, GE_EXPR, c);
+    ADD_SAT_CONSTRAINT (ps, c, GE_EXPR, a);
+
+    ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, c);
+  }
+
+  /* Transitivity: "a > b", "b > c", "c > a"
+     should be impossible.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, b);
+    ADD_SAT_CONSTRAINT (ps, b, GT_EXPR, c);
+    ADD_UNSAT_CONSTRAINT (ps, c, GT_EXPR, a);
+  }
+
+}
+
+/* FIXME.  */
+
+static void
+test_constant_comparisons ()
+{
+  path_var int_3 (build_int_cst (integer_type_node, 3), 0);
+  path_var int_4 (build_int_cst (integer_type_node, 4), 0);
+  path_var int_5 (build_int_cst (integer_type_node, 5), 0);
+
+  path_var int_1023 (build_int_cst (integer_type_node, 1023), 0);
+  path_var int_1024 (build_int_cst (integer_type_node, 1024), 0);
+
+  path_var a (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("a"),
+			  integer_type_node), 0);
+  path_var b (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("b"),
+			  integer_type_node), 0);
+
+  /* Given a >= 1024, then a <= 1023 should be impossible.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, int_1024);
+    ADD_UNSAT_CONSTRAINT (ps, a, LE_EXPR, int_1023);
+  }
+
+  /* a > 4.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, int_4);
+    ASSERT_CONDITION_TRUE (ps, a, GT_EXPR, int_4);
+    ASSERT_CONDITION_TRUE (ps, a, NE_EXPR, int_3);
+    ASSERT_CONDITION_UNKNOWN (ps, a, NE_EXPR, int_5);
+  }
+
+  /* a <= 4.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, int_4);
+    ASSERT_CONDITION_FALSE (ps, a, GT_EXPR, int_4);
+    ASSERT_CONDITION_FALSE (ps, a, GT_EXPR, int_5);
+    ASSERT_CONDITION_UNKNOWN (ps, a, NE_EXPR, int_3);
+  }
+
+  /* If "a > b" and "a == 3", then "b == 4" ought to be unsatisfiable.  */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, b);
+    ADD_SAT_CONSTRAINT (ps, a, EQ_EXPR, int_3);
+    ADD_UNSAT_CONSTRAINT (ps, b, EQ_EXPR, int_4);
+  }
+
+  /* Various tests of int ranges where there is only one possible candidate.  */
+  {
+    /* If "a <= 4" && "a > 3", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      path_state ps;
+      ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, int_4);
+      ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, int_3);
+      ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
+    }
+
+    /* If "a > 3" && "a <= 4", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      path_state ps;
+      ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, int_3);
+      ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, int_4);
+      ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
+    }
+    /* If "a > 3" && "a < 5", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      path_state ps;
+      ADD_SAT_CONSTRAINT (ps, a, GT_EXPR, int_3);
+      ADD_SAT_CONSTRAINT (ps, a, LT_EXPR, int_5);
+      ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
+    }
+    /* If "a >= 4" && "a < 5", then "a == 4",
+       assuming a is of integral type.  */
+    {
+      path_state ps;
+      ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, int_4);
+      ADD_SAT_CONSTRAINT (ps, a, LT_EXPR, int_5);
+      ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
+    }
+    /* If "a >= 4" && "a <= 4", then "a == 4".  */
+    {
+      path_state ps;
+      ADD_SAT_CONSTRAINT (ps, a, GE_EXPR, int_4);
+      ADD_SAT_CONSTRAINT (ps, a, LE_EXPR, int_4);
+      ASSERT_CONDITION_TRUE (ps, a, EQ_EXPR, int_4);
+    }
+  }
+
+#if 0
+  /* As above, but for float.   */
+  {
+    path_state ps;
+    ADD_SAT_CONSTRAINT (ps, f, GT_EXPR, float_3);
+    ADD_SAT_CONSTRAINT (ps, f, LE_EXPR, float_4);
+    ASSERT_CONDITION_UNKNOWN (ps, f, EQ_EXPR, int_4);
+  }
+#endif
+}
+
+/* FIXME.  */
+
+static void
+test_constraint_impl ()
+{
+  path_var int_42 (build_int_cst (integer_type_node, 42), 0);
+  path_var int_0 (build_int_cst (integer_type_node, 0), 0);
+
+  path_var x (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("x"),
+			  integer_type_node), 0);
+  path_var y (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("y"),
+			  integer_type_node), 0);
+  path_var z (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("z"),
+			  integer_type_node), 0);
+
+  /* x == y.  */
+  {
+    path_state ps;
+
+    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, y);
+
+    /* Assert various things about the insides of ps.  */
+    ASSERT_EQ (ps.m_constraints.length (), 0);
+    ASSERT_EQ (ps.m_equiv_classes.length (), 1);
+  }
+
+  /* y <= z; x == y.  */
+  {
+    path_state ps;
+    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, z);
+
+    ADD_SAT_CONSTRAINT (ps, y, GE_EXPR, z);
+    ASSERT_CONDITION_TRUE (ps, y, GE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, z);
+
+    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, y);
+
+    /* Assert various things about the insides of ps.  */
+    ASSERT_EQ (ps.m_constraints.length (), 1);
+    ASSERT_EQ (ps.m_equiv_classes.length (), 2);
+
+    /* Ensure that we merged the constraints.  */
+    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, z);
+  }
+
+  /* y <= z; y == x.  */
+  {
+    path_state ps;
+    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, y);
+    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, z);
+
+    ADD_SAT_CONSTRAINT (ps, y, GE_EXPR, z);
+    ASSERT_CONDITION_TRUE (ps, y, GE_EXPR, z);
+    ASSERT_CONDITION_UNKNOWN (ps, x, GE_EXPR, z);
+
+    ADD_SAT_CONSTRAINT (ps, y, EQ_EXPR, x);
+
+    /* Assert various things about the insides of ps.  */
+    ASSERT_EQ (ps.m_constraints.length (), 1);
+    ASSERT_EQ (ps.m_equiv_classes.length (), 2);
+
+    /* Ensure that we merged the constraints.  */
+    ASSERT_CONDITION_TRUE (ps, x, GE_EXPR, z);
+  }
+
+  /* x == 0, then x != 42.  */
+  {
+    path_state ps;
+
+    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, int_0);
+    ADD_SAT_CONSTRAINT (ps, x, NE_EXPR, int_42);
+
+    /* Assert various things about the insides of ps.  */
+    ASSERT_EQ (ps.m_constraints.length (), 1);
+    ASSERT_EQ (ps.m_equiv_classes.length (), 2);
+    ASSERT_EQ (ps.m_constraints[0].m_lhs, ps.get_or_add_equiv_class (int_0));
+    ASSERT_EQ (ps.m_constraints[0].m_rhs, ps.get_or_add_equiv_class (int_42));
+    ASSERT_EQ (ps.m_constraints[0].m_op, CONSTRAINT_LT);
+  }
+
+  // TODO: selftest for merging ecs "in the middle"
+  // where a non-final one gets overwritten
+
+  // TODO: selftest where there are pre-existing constraints
+}
+
+/* FIXME.  */
+
+static void
+test_assignment ()
+{
+  path_var int_0 (build_int_cst (integer_type_node, 0), 0);
+
+  path_var x (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("x"),
+			  integer_type_node), 0);
+  path_var y (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("y"),
+			  integer_type_node), 0);
+  /* "x == 0", then use of y, then "y = 0".  */
+  path_state ps;
+  ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, int_0);
+  ASSERT_CONDITION_UNKNOWN (ps, y, EQ_EXPR, int_0);
+  ps.on_assignment (y, int_0);
+  ASSERT_CONDITION_TRUE (ps, y, EQ_EXPR, int_0);
+  ASSERT_CONDITION_TRUE (ps, y, EQ_EXPR, x);
+}
+
+/* FIXME.  */
+
+static void
+test_stack_frames ()
+{
+  path_var int_42 (build_int_cst (integer_type_node, 42), 0);
+  path_var int_0 (build_int_cst (integer_type_node, 0), 0);
+
+  /* "a" in the parent frame.  */
+  path_var a (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("a"),
+			  integer_type_node), 0);
+  /* "x" in a child frame.  */
+  path_var x (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("x"),
+			  integer_type_node), 1);
+
+  {
+    path_state ps;
+    ps.m_stack_depth = 1;
+
+    /* x == a.  */
+    ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, a);
+    ASSERT_CONDITION_TRUE (ps, x, EQ_EXPR, a);
+
+    /* Pop the stack.  */
+    ps.pop_stack_frame ();
+    ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, a);
+  }
+}
+
+/* Check that operator== and hashing works as expected for the
+   various types.  */
+
+static void
+test_equality ()
+{
+  path_var int_42 (build_int_cst (integer_type_node, 42), 0);
+  path_var int_0 (build_int_cst (integer_type_node, 0), 0);
+
+  path_var x (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("x"),
+			  integer_type_node), 0);
+  path_var y (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("y"),
+			  integer_type_node), 0);
+  path_var z (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("z"),
+			  integer_type_node), 0);
+
+  {
+    path_state ps0;
+    path_state ps1;
+
+    ASSERT_EQ (ps0.hash (), ps1.hash ());
+    ASSERT_EQ (ps0, ps1);
+
+    ADD_SAT_CONSTRAINT (ps1, x, EQ_EXPR, y);
+    ASSERT_NE (ps0.hash (), ps1.hash ());
+    ASSERT_NE (ps0, ps1);
+
+    path_state ps2;
+    /* Make the same change to ps2.  */
+    ADD_SAT_CONSTRAINT (ps2, x, EQ_EXPR, y);
+    ASSERT_EQ (ps1.hash (), ps2.hash ());
+    ASSERT_EQ (ps1, ps2);
+  }
+}
+
+/* A subclass of purge_criteria for selftests: purge all path_var instances.  */
+
+class purge_all_path_vars : public purge_criteria
+{
+public:
+  bool should_purge_p (path_var) const FINAL OVERRIDE
+  {
+    return true;
+  }
+};
+
+/* Check that path_state objects can "forget".  */
+
+static void
+test_purging ()
+{
+  path_var int_42 (build_int_cst (integer_type_node, 42), 0);
+  path_var int_0 (build_int_cst (integer_type_node, 0), 0);
+
+  path_var x (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("x"),
+			  integer_type_node), 0);
+  path_var y (build_decl (UNKNOWN_LOCATION, PARM_DECL,
+			  get_identifier ("y"),
+			  integer_type_node), 0);
+
+  {
+    path_state ps0;
+    path_state ps1;
+
+    ADD_SAT_CONSTRAINT (ps1, x, EQ_EXPR, y);
+    ASSERT_NE (ps0, ps1);
+
+    purge_one_path_var px (x);
+    ps1.purge (px);
+
+    purge_one_path_var py (y);
+    ps1.purge (py);
+
+    ASSERT_EQ (ps0, ps1);
+  }
+
+  {
+    path_state ps0;
+    path_state ps1;
+
+    ADD_SAT_CONSTRAINT (ps1, x, EQ_EXPR, int_42);
+    ASSERT_NE (ps0, ps1);
+
+    purge_one_path_var px (x);
+    ps1.purge (px);
+
+    ASSERT_EQ (ps0, ps1);
+  }
+
+  {
+    path_state ps0;
+    path_state ps1;
+
+    ADD_SAT_CONSTRAINT (ps1, x, GE_EXPR, int_0);
+    ADD_SAT_CONSTRAINT (ps1, x, LE_EXPR, int_42);
+    ASSERT_NE (ps0, ps1);
+
+    purge_one_path_var px (x);
+    ps1.purge (px);
+
+    ASSERT_EQ (ps0, ps1);
+  }
+
+  {
+    path_state ps0;
+    path_state ps1;
+
+    ADD_SAT_CONSTRAINT (ps1, x, NE_EXPR, int_42);
+    ADD_SAT_CONSTRAINT (ps1, y, NE_EXPR, int_0);
+    ASSERT_NE (ps0, ps1);
+
+    purge_one_path_var px (x);
+    ps1.purge (px);
+    ASSERT_NE (ps0, ps1);
+
+    ASSERT_CONDITION_TRUE (ps1, y, NE_EXPR, int_0);
+  }
+
+  {
+    path_state ps0;
+    path_state ps1;
+
+    ADD_SAT_CONSTRAINT (ps1, x, NE_EXPR, int_42);
+    ADD_SAT_CONSTRAINT (ps1, y, NE_EXPR, int_0);
+    ASSERT_NE (ps0, ps1);
+
+    purge_all_path_vars p;
+    ps1.purge (p);
+    ASSERT_EQ (ps0, ps1);
+  }
+
+}
+
+
+/* Run all of the selftests within this file.  */
+
+void
+analyzer_path_state_cc_tests ()
+{
+  test_constraint_conditions ();
+  test_transitivity ();
+  test_constant_comparisons ();
+  test_constraint_impl ();
+  test_assignment ();
+  test_stack_frames ();
+  test_equality ();
+  test_purging ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/analyzer/path-state.h b/gcc/analyzer/path-state.h
new file mode 100644
index 0000000..68edf80
--- /dev/null
+++ b/gcc/analyzer/path-state.h
@@ -0,0 +1,315 @@
+/* Tracking equivalence classes and constraints at a point on an execution path.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_ANALYZER_PATH_STATE_H
+#define GCC_ANALYZER_PATH_STATE_H
+
+class superedge;
+class call_superedge;
+class return_superedge;
+
+/* FIXME: put everything to do with path_state in its own namespace
+   while building up the alternative impl.  */
+
+namespace psn {
+
+/* One of the end-points of a range.  */
+
+struct bound
+{
+  bound () : m_constant (NULL_TREE), m_closed (false) {}
+  bound (tree constant, bool closed)
+  : m_constant (constant), m_closed (closed) {}
+
+  void ensure_closed (bool is_upper);
+
+  tree m_constant;
+  bool m_closed;
+};
+
+/* FIXME.  */
+
+struct range
+{
+  range () : m_lower_bound (), m_upper_bound () {}
+  range (const bound &lower, const bound &upper)
+  : m_lower_bound (lower), m_upper_bound (upper) {}
+
+  std::string to_str () const;
+
+  bool constrained_to_single_element (tree *out);
+
+  bound m_lower_bound;
+  bound m_upper_bound;
+};
+
+} // namespace psn
+
+/* A tree, extended with stack frame information for locals, so that
+   we can distinguish between different values of locals within a potentially
+   recursive callstack.  */
+
+class path_var
+{
+public:
+  path_var (tree t, int stack_depth)
+  : m_tree (t), m_stack_depth (stack_depth)
+  {
+    // TODO: ignore stack depth for globals and constants
+  }
+
+  bool operator== (const path_var &other) const
+  {
+    return (m_tree == other.m_tree
+	    && m_stack_depth == other.m_stack_depth);
+  }
+
+  std::string to_str () const;
+
+  tree m_tree;
+  int m_stack_depth; // or -1 for globals?
+};
+
+/* FIXME: put everything to do with path_state in its own namespace
+   while building up the alternative impl.  */
+
+namespace psn {
+
+/* Abstract base class for specifying how state should be purged.  */
+
+class purge_criteria
+{
+public:
+  virtual ~purge_criteria () {}
+  virtual bool should_purge_p (path_var pv) const = 0;
+};
+
+} // namespace psn
+
+
+class path_state;
+
+namespace psn {
+
+/* Optional per-engine extra state (to avoid making path_state a
+   template).  */
+
+class extra_ec_state
+{
+ public:
+  virtual ~extra_ec_state () {}
+  virtual extra_ec_state *clone () const = 0;
+  virtual std::string to_str () const = 0;
+  virtual hashval_t hash () const = 0;
+  virtual bool operator== (const extra_ec_state &other) const = 0;
+};
+
+/* FIXME.  */
+
+class equiv_class
+{
+public:
+  equiv_class ();
+  equiv_class (const equiv_class &other);
+  ~equiv_class ();
+
+  std::string to_str () const;
+  hashval_t hash () const;
+  bool operator== (const equiv_class &other);
+
+  void add (path_var v);
+  bool del (path_var v);
+
+  tree get_any_constant () const { return m_constant; }
+
+  int get_min_stack_depth () const;
+
+  path_var get_representative () const;
+
+  /* An equivalence class can contain at most one constant.  */
+  tree m_constant;
+
+  // FIXME: this is a bad representation, probably should be a set:
+  auto_vec<path_var> m_vars;
+
+  /* Has this value escaped from this function?  */
+  bool m_escaped;
+
+  /* Optional per-engine extra state (to avoid making path_state a
+     template).  */
+  extra_ec_state *m_extra_state;
+};
+
+enum constraint_op
+{
+  CONSTRAINT_NE,
+  CONSTRAINT_LT,
+  CONSTRAINT_LE
+};
+
+const char *constraint_op_code (enum constraint_op c_op);
+
+class equiv_class_id
+{
+public:
+  equiv_class_id (unsigned idx) : m_idx (idx) {}
+  const equiv_class &get_obj (const path_state &ps) const;
+  equiv_class &get_obj (path_state &ps) const;
+
+  bool operator== (const equiv_class_id &other) const
+  {
+    return m_idx == other.m_idx;
+  }
+  bool operator!= (const equiv_class_id &other) const
+  {
+    return m_idx != other.m_idx;
+  }
+
+  std::string to_str () const
+  {
+    return std::string ("ec") + std::to_string (m_idx);
+  }
+
+  void update_for_removal (equiv_class_id other)
+  {
+    if (m_idx > other.m_idx)
+      m_idx--;
+  }
+
+  unsigned m_idx;
+};
+
+class constraint {
+ public:
+  constraint (equiv_class_id lhs, enum constraint_op c_op, equiv_class_id rhs)
+    : m_lhs (lhs), m_op (c_op), m_rhs (rhs) {}
+
+  std::string to_str (const path_state &ps) const;
+  hashval_t hash () const;
+  bool operator== (const constraint &other) const;
+
+  /* Is this an ordering, rather than a "!=".  */
+  bool is_ordering_p () const
+  {
+    return m_op != CONSTRAINT_NE;
+  }
+
+  equiv_class_id m_lhs;
+  enum constraint_op m_op;
+  equiv_class_id m_rhs;
+};
+
+} // namespace psn
+
+/* The state of a particular point at one point in the traversal (not including
+   state-machine information).  */
+
+class path_state
+{
+public:
+  path_state () : m_stack_depth (0) {}
+  path_state (const path_state &other);
+  ~path_state ();
+
+  path_state& operator= (const path_state &other);
+
+  hashval_t hash () const;
+  bool operator== (const path_state &other) const;
+  bool operator!= (const path_state &other) const
+  {
+    return !(*this == other);
+  }
+
+  std::string to_str () const;
+
+  const psn::equiv_class &get_equiv_class_by_index (unsigned idx) const
+  {
+    return *m_equiv_classes[idx];
+  }
+  psn::equiv_class &get_equiv_class_by_index (unsigned idx)
+  {
+    return *m_equiv_classes[idx];
+  }
+
+  psn::equiv_class &get_equiv_class (path_var pv)
+  {
+    psn::equiv_class_id ec_id = get_or_add_equiv_class (pv);
+    return ec_id.get_obj (*this);
+  }
+
+  path_var make_path_var (tree expr) const
+  {
+    return path_var (expr, m_stack_depth);
+  }
+
+  void on_assignment (path_var lhs, path_var rhs);
+
+  bool apply_constraints_for_edge (const superedge &edge,
+				   const gimple *last_stmt);
+
+  bool add_constraint (path_var lhs, enum tree_code op, path_var rhs);
+
+  bool add_constraint (psn::equiv_class_id lhs_ec_id,
+		       enum tree_code op,
+		       psn::equiv_class_id rhs_ec_id);
+
+  //private:
+  bool apply_constraints_for_call (const call_superedge& edge);
+
+  bool apply_constraints_for_return (const return_superedge& edge);
+
+  void pop_stack_frame ();
+
+  bool apply_constraints_for_gcond (const superedge &edge,
+				    const gcond *cond_stmt);
+  bool apply_constraints_for_gswitch (const superedge &edge,
+				      const gswitch *switch_stmt);
+  bool get_equiv_class (path_var pv, psn::equiv_class_id *out) const;
+  psn::equiv_class_id get_or_add_equiv_class (path_var vr);
+  tristate eval_condition (psn::equiv_class_id lhs,
+			   enum tree_code op,
+			   psn::equiv_class_id rhs);
+
+  /* For implementing "__analyzer_eval".  */
+  tristate dump_condition (tree lhs, enum tree_code op, tree rhs);
+
+  void recursively_mark_as_escaping (psn::equiv_class_id ec_id);
+  bool calc_escaped_p (path_var pv);
+
+  int purge (const psn::purge_criteria &p);
+
+  void validate () const;
+
+  auto_vec<psn::equiv_class *> m_equiv_classes;
+  auto_vec<psn::constraint> m_constraints;
+  int m_stack_depth;
+
+ private:
+  void add_constraint_internal (psn::equiv_class_id lhs_id,
+				enum psn::constraint_op c_op,
+				psn::equiv_class_id rhs_id);
+};
+
+namespace inchash
+{
+  extern void add_path_var (path_var pv, hash &hstate);
+} // namespace inchash
+
+#endif /* GCC_ANALYZER_PATH_STATE_H */
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
new file mode 100644
index 0000000..57ce0c4
--- /dev/null
+++ b/gcc/analyzer/region-model.cc
@@ -0,0 +1,3603 @@
+/* FIXME.  */
+#include "config.h"
+#include "gcc-plugin.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tm.h"
+#include "toplev.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "context.h"
+#include "diagnostic.h"
+#include "bitmap.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-pretty-print.h"
+#include "tree-pretty-print.h"
+#include "analyzer.h"
+#include "graphviz.h"
+#include "cgraph.h"
+#include "diagnostic-path.h"
+#include "gcc-rich-location.h"
+#include "tree-dfa.h"
+#include <string>
+#include "analyzer-logging.h"
+#include "stringpool.h"
+#include "selftest.h"
+#include "fold-const.h"
+#include "tristate.h"
+#include "path-state.h"
+#include "supergraph.h"
+#include "region-model.h"
+#include "tree-diagnostic.h"
+#include "diagnostic-color.h"
+#include "constraint-manager.h"
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+class impl_constraint_manager : public constraint_manager
+{
+ public:
+  impl_constraint_manager (region_model *model)
+  : constraint_manager (),
+    m_model (model)
+  {}
+
+  impl_constraint_manager (const impl_constraint_manager &other,
+			   region_model *model)
+  : constraint_manager (other),
+    m_model (model)
+  {}
+
+  constraint_manager *clone (region_model *model) const
+  {
+    return new impl_constraint_manager (*this, model);
+  }
+
+  tree maybe_get_constant (svalue_id sid) const FINAL OVERRIDE
+  {
+    svalue *svalue = m_model->get_svalue (sid);
+    if (constant_svalue *cst_svalue = svalue->dyn_cast_constant_svalue ())
+      {
+	return cst_svalue->get_constant ();
+      }
+    return NULL_TREE;
+  }
+
+  svalue_id get_sid_for_constant (tree cst) const FINAL OVERRIDE
+  {
+    gcc_assert (CONSTANT_CLASS_P (cst));
+    return m_model->get_rvalue (cst);
+  }
+
+  int get_num_svalues () const FINAL OVERRIDE
+  {
+    return m_model->get_num_svalues ();
+  }
+
+ private:
+  region_model *m_model;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+void
+svalue_id::print (pretty_printer *pp) const
+{
+  if (null_p ())
+    pp_printf (pp, "null");
+  else
+    pp_printf (pp, "sv%i", m_idx);
+}
+
+/* FIXME.  */
+
+void
+svalue_id::dump_node_name_to_pp (pretty_printer *pp) const
+{
+  gcc_assert (!null_p ());
+  pp_printf (pp, "svalue_%i", m_idx);
+}
+
+/* FIXME.  */
+
+std::string
+svalue_id::to_str () const
+{
+  pretty_printer pp;
+  print (&pp);
+  return pp_formatted_text (&pp);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+void
+region_id::print (pretty_printer *pp) const
+{
+  if (null_p ())
+    pp_printf (pp, "null");
+  else
+    pp_printf (pp, "r%i", m_idx);
+}
+
+/* FIXME.  */
+
+void
+region_id::dump_node_name_to_pp (pretty_printer *pp) const
+{
+  gcc_assert (!null_p ());
+  pp_printf (pp, "region_%i", m_idx);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+/* class id_map.  */
+
+/* FIXME.  */
+
+template <typename T>
+id_map<T>::id_map (int num_svalues)
+: m_src_to_dst (num_svalues),
+  m_dst_to_src (num_svalues)
+{
+  for (int i = 0; i < num_svalues; i++)
+    {
+      m_src_to_dst.quick_push (T::null ());
+      m_dst_to_src.quick_push (T::null ());
+    }
+}
+
+/* FIXME.  */
+
+template <typename T>
+void
+id_map<T>::put (T src, T dst)
+{
+  m_src_to_dst[src.as_int ()] = dst;
+  m_dst_to_src[dst.as_int ()] = src;
+}
+
+/* FIXME.  */
+
+template <typename T>
+T
+id_map<T>::get_dst_for_src (T src) const
+{
+  if (src.null_p ())
+    return src;
+  return m_src_to_dst[src.as_int ()];
+}
+
+/* FIXME.  */
+
+template <typename T>
+T
+id_map<T>::get_src_for_dst (T dst) const
+{
+  if (dst.null_p ())
+    return dst;
+  return m_dst_to_src[dst.as_int ()];
+}
+
+/* FIXME.  */
+
+template <typename T>
+void
+id_map<T>::dump_to_pp (pretty_printer *pp) const
+{
+  pp_string (pp, "src to dst: {");
+  unsigned i;
+  T *dst;
+  FOR_EACH_VEC_ELT (m_src_to_dst, i, dst)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      T src (T::from_int (i));
+      src.print (pp);
+      pp_string (pp, " -> ");
+      dst->print (pp);
+    }
+  pp_string (pp, "}");
+  pp_newline (pp);
+
+  pp_string (pp, "dst to src: {");
+  T *src;
+  FOR_EACH_VEC_ELT (m_dst_to_src, i, src)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      T dst (T::from_int (i));
+      dst.print (pp);
+      pp_string (pp, " <- ");
+      src->print (pp);
+    }
+  pp_string (pp, "}");
+  pp_newline (pp);
+}
+
+/* FIXME.  */
+
+template <typename T>
+DEBUG_FUNCTION void
+id_map<T>::dump () const
+{
+  pretty_printer pp;
+  pp.buffer->stream = stderr;
+  dump_to_pp (&pp);
+  pp_flush (&pp);
+}
+
+/* FIXME.  */
+
+template <typename T>
+void
+id_map<T>::update (T &id) const
+{
+  id = get_dst_for_src (id);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+/* class id_set.  */
+
+template<>
+id_set<region_id>::id_set (const region_model *model)
+: m_bitmap (model->m_regions.length ())
+{
+  bitmap_clear (m_bitmap);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+
+#define PUSH_IGNORE_WFORMAT \
+  _Pragma("GCC diagnostic push") \
+  _Pragma("GCC diagnostic ignored \"-Wformat\"") \
+  _Pragma("GCC diagnostic ignored \"-Wformat-extra-args\"")
+
+#define POP_IGNORE_WFORMAT \
+  _Pragma("GCC diagnostic pop")
+
+/* class svalue.  */
+
+/* FIXME.  */
+
+bool
+svalue::operator== (const svalue &other) const
+{
+  enum svalue_kind this_kind = get_kind ();
+  enum svalue_kind other_kind = other.get_kind ();
+  if (this_kind != other_kind)
+    return false;
+
+  if (m_type != other.m_type)
+    return false;
+
+  switch (this_kind)
+    {
+    default:
+      gcc_unreachable ();
+    case SK_REGION:
+      {
+	const region_svalue &this_sub
+	  = (const region_svalue &)*this;
+	const region_svalue &other_sub
+	  = (const region_svalue &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+      break;
+    case SK_CONSTANT:
+      {
+	const constant_svalue &this_sub
+	  = (const constant_svalue &)*this;
+	const constant_svalue &other_sub
+	  = (const constant_svalue &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+      break;
+    case SK_POISONED:
+      {
+	const poisoned_svalue &this_sub
+	  = (const poisoned_svalue &)*this;
+	const poisoned_svalue &other_sub
+	  = (const poisoned_svalue &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+      break;
+    case SK_UNKNOWN:
+      {
+	const unknown_svalue &this_sub
+	  = (const unknown_svalue &)*this;
+	const unknown_svalue &other_sub
+	  = (const unknown_svalue &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+      break;
+    }
+}
+
+/* FIXME.  */
+
+void
+svalue::print (const region_model &model,
+	       svalue_id this_sid,
+	       pretty_printer *pp) const
+{
+  this_sid.print (pp);
+  pp_string (pp, ": {");
+
+PUSH_IGNORE_WFORMAT
+  if (m_type)
+    pp_printf (pp, "type: %qT, ", m_type);
+POP_IGNORE_WFORMAT
+
+  /* vfunc.  */
+  print_details (model, this_sid, pp);
+
+  pp_string (pp, "}");
+}
+
+/* FIXME.  */
+
+void
+svalue::dump_dot_to_pp (const region_model &model,
+			svalue_id this_sid,
+			pretty_printer *pp) const
+{
+  this_sid.dump_node_name_to_pp (pp);
+  pp_printf (pp, " [label=\"");
+  pp_write_text_to_stream (pp);
+  this_sid.print (pp);
+  pp_string (pp, ": {");
+  print (model, this_sid, pp);
+  pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
+  pp_string (pp, "}\"];");
+  pp_newline (pp);
+}
+
+#if 0
+/* FIXME.  */
+
+std::string
+svalue::to_str () const
+{
+  pretty_printer pp;
+  print (&pp);
+  return pp_formatted_text (&pp);
+}
+#endif
+
+/* FIXME.  */
+
+void
+svalue::remap_region_ids (const region_id_map &map)
+{
+  /* Empty.  */
+}
+
+/* class region_svalue : public svalue.  */
+
+/* FIXME.  */
+
+bool
+region_svalue::compare_fields (const region_svalue &other) const
+{
+  return m_rid == other.m_rid;
+}
+
+/* FIXME.  */
+
+hashval_t
+region_svalue::hash () const
+{
+  return m_rid.as_int ();
+}
+
+/* FIXME.  */
+
+void
+region_svalue::print_details (const region_model &model ATTRIBUTE_UNUSED,
+			      svalue_id this_sid ATTRIBUTE_UNUSED,
+			      pretty_printer *pp) const
+{
+  if (m_rid.null_p ())
+    pp_string (pp, "NULL");
+  else
+    {
+      pp_string (pp, "&");
+      m_rid.print (pp);
+    }
+}
+
+/* FIXME.  */
+
+void
+region_svalue::dump_dot_to_pp (const region_model &model,
+			       svalue_id this_sid,
+			       pretty_printer *pp) const
+{
+  svalue::dump_dot_to_pp (model, this_sid, pp);
+
+  /* If non-NULL, add an edge to the pointed-to region.  */
+  if (!m_rid.null_p ())
+    {
+      this_sid.dump_node_name_to_pp (pp);
+      pp_string (pp, " -> ");
+      m_rid.dump_node_name_to_pp (pp);
+      pp_string (pp, ";");
+      pp_newline (pp);
+  }
+}
+
+/* FIXME.  */
+
+void
+region_svalue::remap_region_ids (const region_id_map &map)
+{
+  m_rid = map.get_dst_for_src (m_rid);
+}
+
+/* class constant_svalue : public svalue.  */
+
+/* FIXME.  */
+
+bool
+constant_svalue::compare_fields (const constant_svalue &other) const
+{
+  // FIXME: should we do tree equality here?
+  return m_cst_expr == other.m_cst_expr;
+}
+
+/* FIXME.  */
+
+hashval_t
+constant_svalue::hash () const
+{
+  inchash::hash hstate;
+  // FIXME: what if it's a fndecl?
+  inchash::add_expr (m_cst_expr, hstate);
+  return hstate.end ();
+}
+
+/* FIXME.  */
+
+void
+constant_svalue::print_details (const region_model &model ATTRIBUTE_UNUSED,
+				svalue_id this_sid ATTRIBUTE_UNUSED,
+				pretty_printer *pp) const
+{
+PUSH_IGNORE_WFORMAT
+  if (TREE_CODE (m_cst_expr) == FUNCTION_DECL)
+    pp_printf (pp, "%qD", m_cst_expr);
+  else
+    pp_printf (pp, "%qE", m_cst_expr);
+POP_IGNORE_WFORMAT
+}
+
+/* class unknown_svalue : public svalue.  */
+
+/* FIXME.  */
+
+bool
+unknown_svalue::compare_fields (const unknown_svalue &other) const
+{
+  /* I *think* we want to return true here, in that when comparing
+     two region models, we want two peer unknown_svalue instances
+     to be the "same".  */
+  return true;
+}
+
+/* FIXME.  */
+
+hashval_t
+unknown_svalue::hash () const
+{
+  return 0;
+}
+
+/* FIXME.  */
+
+void
+unknown_svalue::print_details (const region_model &model ATTRIBUTE_UNUSED,
+			       svalue_id this_sid ATTRIBUTE_UNUSED,
+			       pretty_printer *pp) const
+{
+  pp_string (pp, "unknown");
+}
+
+/* FIXME.  */
+
+const char *
+poison_kind_to_str (enum poison_kind kind)
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case POISON_KIND_UNINIT:
+      return "uninit";
+    case POISON_KIND_FREED:
+      return "freed";
+    case POISON_KIND_POPPED_STACK:
+      return "popped stack";
+    }
+}
+
+/* class poisoned_svalue : public svalue.  */
+
+/* FIXME.  */
+
+bool
+poisoned_svalue::compare_fields (const poisoned_svalue &other) const
+{
+  return m_kind == other.m_kind;
+}
+
+/* FIXME.  */
+
+hashval_t
+poisoned_svalue::hash () const
+{
+  return m_kind;
+}
+
+/* FIXME.  */
+
+void
+poisoned_svalue::print_details (const region_model &model ATTRIBUTE_UNUSED,
+				svalue_id this_sid ATTRIBUTE_UNUSED,
+				pretty_printer *pp) const
+{
+  pp_printf (pp, "poisoned: %s", poison_kind_to_str (m_kind));
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+const char *
+region_kind_to_str (enum region_kind kind)
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case RK_MAP:
+      return "map";
+    case RK_FRAME:
+      return "frame";
+    case RK_STACK:
+      return "stack";
+    case RK_ROOT:
+      return "root";
+    case RK_SYMBOLIC:
+      return "symbolic";
+    case RK_HEAP:
+      return "heap";
+    }
+}
+
+/* FIXME.  */
+
+bool
+region::operator== (const region &other) const
+{
+  if (m_parent_rid != other.m_parent_rid)
+    return false;
+  if (m_sval_id != other.m_sval_id)
+    return false;
+  if (m_type != other.m_type)
+    return false;
+
+  enum region_kind this_kind = get_kind ();
+  enum region_kind other_kind = other.get_kind ();
+  if (this_kind != other_kind)
+    return false;
+
+  switch (this_kind)
+    {
+    default:
+      gcc_unreachable ();
+    case RK_MAP:
+      {
+	const map_region &this_sub
+	  = (const map_region &)*this;
+	const map_region &other_sub
+	  = (const map_region &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_FRAME:
+      {
+	const frame_region &this_sub
+	  = (const frame_region &)*this;
+	const frame_region &other_sub
+	  = (const frame_region &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_STACK:
+      {
+	const stack_region &this_sub
+	  = (const stack_region &)*this;
+	const stack_region &other_sub
+	  = (const stack_region &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_ROOT:
+      {
+	const root_region &this_sub
+	  = (const root_region &)*this;
+	const root_region &other_sub
+	  = (const root_region &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_SYMBOLIC:
+      {
+	const symbolic_region &this_sub
+	  = (const symbolic_region &)*this;
+	const symbolic_region &other_sub
+	  = (const symbolic_region &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    case RK_HEAP:
+      {
+	const heap_region &this_sub
+	  = (const heap_region &)*this;
+	const heap_region &other_sub
+	  = (const heap_region &)other;
+	return this_sub.compare_fields (other_sub);
+      }
+    }
+}
+
+/* class region.  */
+
+void
+region::set_value (const region_model &model ATTRIBUTE_UNUSED,
+		   svalue_id rhs_sid)
+{
+#if 0
+  svalue *sval = model.get_svalue (rhs_sid);
+
+  // FIXME: we can't enable this until we have working casts
+  /* Type-check where we can.  */
+  if (m_type && sval->get_type ())
+    gcc_assert (m_type == sval->get_type ());
+#endif
+
+  m_sval_id = rhs_sid;
+}
+
+/* FIXME.  */
+
+svalue_id
+region::get_value (region_model &model, bool non_null)
+{
+  /* If this region has a value, use it. */
+  if (!m_sval_id.null_p ())
+    return m_sval_id;
+
+  /* Otherwise, "inherit" value from first ancestor with a
+     non-null value. */
+  for (region *ancestor = model.get_region (m_parent_rid);
+       ancestor;
+       ancestor = model.get_region (ancestor->m_parent_rid))
+    {
+      if (!ancestor->m_sval_id.null_p ())
+	return ancestor->m_sval_id;
+    }
+
+  /* If a non-null value has been requested, then generate
+     a new unknown value.  Store it, so that repeated reads from this
+     region will yield the same unknown value.  */
+  if (non_null)
+    {
+      svalue_id unknown_sid = model.add_svalue (new unknown_svalue (m_type));
+      m_sval_id = unknown_sid;
+      return unknown_sid;
+    }
+
+  gcc_unreachable ();
+
+  return svalue_id::null ();
+}
+
+/* FIXME.  */
+
+hashval_t
+region::hash () const
+{
+  inchash::hash hstate;
+  add_to_hash (hstate);
+  return hstate.end ();
+}
+
+/* FIXME.  */
+
+void
+region::print (const region_model &model,
+		   region_id this_rid,
+		   pretty_printer *pp) const
+{
+  this_rid.print (pp);
+  pp_string (pp, ": {");
+
+  /* vfunc.  */
+  print_fields (model, this_rid, pp);
+
+  pp_string (pp, "}");
+}
+
+/* FIXME.  */
+
+void
+region::dump_dot_to_pp (const region_model &model,
+			region_id this_rid,
+			pretty_printer *pp) const
+{
+  this_rid.dump_node_name_to_pp (pp);
+  pp_printf (pp, " [shape=%s,style=filled,fillcolor=%s,label=\"",
+	     "record", "lightgrey");
+  pp_write_text_to_stream (pp);
+  print (model, this_rid, pp);
+  pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+  pp_string (pp, "\"];");
+  pp_newline (pp);
+
+  /* Add edge to svalue.  */
+  if (!m_sval_id.null_p ())
+    {
+      this_rid.dump_node_name_to_pp (pp);
+      pp_string (pp, " -> ");
+      m_sval_id.dump_node_name_to_pp (pp);
+      pp_string (pp, ";");
+      pp_newline (pp);
+    }
+
+  /* Add edge to parent.  */
+  if (!m_parent_rid.null_p ())
+    {
+      this_rid.dump_node_name_to_pp (pp);
+      pp_string (pp, " -> ");
+      m_parent_rid.dump_node_name_to_pp (pp);
+      pp_string (pp, ";");
+      pp_newline (pp);
+    }
+}
+
+/* FIXME.  */
+
+void
+region::dump_to_pp (const region_model &model,
+		    region_id this_rid,
+		    pretty_printer *pp,
+		    const char *prefix,
+		    bool is_last_child) const
+{
+  print (model, this_rid, pp);
+  pp_newline (pp);
+
+  const char *new_prefix;
+  if (!m_parent_rid.null_p ())
+    new_prefix = ACONCAT ((prefix, is_last_child ? "  " : "|  ", NULL));
+  else
+    new_prefix = prefix;
+
+  const char *begin_color = colorize_start (pp_show_color (pp), "note");
+  const char *end_color = colorize_stop (pp_show_color (pp));
+  char *field_prefix
+    = ACONCAT ((begin_color, new_prefix, "|:", end_color, NULL));
+
+  if (!m_sval_id.null_p ())
+    {
+      pp_printf (pp, "%s sval: ", field_prefix);
+      model.get_svalue (m_sval_id)->print (model, m_sval_id, pp);
+      pp_newline (pp);
+    }
+  if (m_type)
+    {
+PUSH_IGNORE_WFORMAT
+      pp_printf (pp, "%s type: %qT", field_prefix, m_type);
+POP_IGNORE_WFORMAT
+      pp_newline (pp);
+    }
+
+  /* Find, then iterate over children.  */
+  /* TODO: we'll also want to label them
+     (potentially with tree labels, or with sval labels).  */
+
+  auto_vec<region_id> child_rids;
+  unsigned i;
+  region *child;
+  FOR_EACH_VEC_ELT (model.m_regions, i, child)
+    if (child->m_parent_rid == this_rid)
+      child_rids.safe_push (region_id::from_int (i));
+
+  region_id *child_rid;
+  FOR_EACH_VEC_ELT (child_rids, i, child_rid)
+    {
+      is_last_child = (i == child_rids.length () - 1);
+      if (!this_rid.null_p ())
+	{
+	  // FIXME: colorize hierachical ASCII art
+	  const char *tail = is_last_child ? "`-" : "|-";
+	  pp_printf (pp, "%r%s%s%R", "note", new_prefix, tail);
+	  // FIXME: dedicated color code?
+	}
+      dump_child_label (model, this_rid, *child_rid, pp);
+      model.get_region (*child_rid)->dump_to_pp (model, *child_rid, pp,
+						 new_prefix,
+						 is_last_child);
+    }
+}
+
+/* FIXME.  */
+
+void
+region::dump_child_label (const region_model &model ATTRIBUTE_UNUSED,
+			  region_id this_rid ATTRIBUTE_UNUSED,
+			  region_id child_rid ATTRIBUTE_UNUSED,
+			  pretty_printer *pp ATTRIBUTE_UNUSED) const
+{
+  /* Empty.  */
+}
+
+#if 0
+/* FIXME.  */
+
+std::string
+region::to_str () const
+{
+  pretty_printer pp;
+  print (&pp);
+  return pp_formatted_text (&pp);
+}
+#endif
+
+/* FIXME.  */
+
+void
+region::remap_svalue_ids (const svalue_id_map &map)
+{
+  m_sval_id = map.get_dst_for_src (m_sval_id);
+}
+
+/* FIXME.  */
+
+void
+region::remap_region_ids (const region_id_map &map)
+{
+  map.update (m_parent_rid);
+}
+
+/* FIXME.  */
+
+void
+region::add_to_hash (inchash::hash &hstate) const
+{
+  inchash::add (m_parent_rid, hstate);
+  inchash::add (m_sval_id, hstate);
+  hstate.add_ptr (m_type);
+}
+
+/* FIXME.  */
+
+void
+region::print_fields (const region_model &model ATTRIBUTE_UNUSED,
+		      region_id this_rid ATTRIBUTE_UNUSED,
+		      pretty_printer *pp) const
+{
+  pp_printf (pp, "kind: %qs", region_kind_to_str (get_kind ()));
+
+  pp_string (pp, ", parent: ");
+  m_parent_rid.print (pp);
+
+  pp_printf (pp, ", sval: ");
+  m_sval_id.print (pp);
+
+PUSH_IGNORE_WFORMAT
+  if (m_type)
+    pp_printf (pp, ", type: %qT", m_type);
+POP_IGNORE_WFORMAT
+}
+
+/* class map_region : public region.  */
+
+/* FIXME.  */
+
+map_region::map_region (const map_region &other)
+: region (other),
+  m_map (other.m_map)
+{
+}
+
+/* FIXME.  */
+
+bool
+map_region::compare_fields (const map_region &other) const
+{
+  if (m_map.elements () != other.m_map.elements ())
+    return false;
+
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      tree key = (*iter).first;
+      region_id e = (*iter).second;
+      region_id *other_slot = const_cast <map_t &> (other.m_map).get (key);
+      if (other_slot == NULL)
+	return false;
+      if (e != *other_slot)
+	return false;
+    }
+  return true;
+}
+
+/* FIXME.  */
+
+region *
+map_region::clone () const
+{
+  return new map_region (*this);
+}
+
+/* FIXME.  */
+
+void
+map_region::print_fields (const region_model &model,
+			  region_id this_rid,
+			  pretty_printer *pp) const
+{
+  region::print_fields (model, this_rid, pp);
+  pp_string (pp, ", map: {");
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      if (iter != m_map.begin ())
+	pp_string (pp, ", ");
+      tree expr = (*iter).first;
+      region_id child_rid = (*iter).second;
+PUSH_IGNORE_WFORMAT
+      pp_printf (pp, "%qE: ", expr);
+POP_IGNORE_WFORMAT
+      child_rid.print (pp);
+    }
+  pp_string (pp, "}");
+}
+
+/* FIXME.  */
+
+void
+map_region::dump_dot_to_pp (const region_model &model,
+			    region_id this_rid,
+			    pretty_printer *pp) const
+{
+  region::dump_dot_to_pp (model, this_rid, pp);
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      // TODO: add nodes/edges to label things
+
+      tree expr = (*iter).first;
+      region_id child_rid = (*iter).second;
+
+      pp_printf (pp, "rid_label_%i [label=\"", child_rid.as_int ());
+      pp_write_text_to_stream (pp);
+PUSH_IGNORE_WFORMAT
+      pp_printf (pp, "%qE", expr);
+POP_IGNORE_WFORMAT
+      pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
+      pp_string (pp, "\"];");
+      pp_newline (pp);
+
+      pp_printf (pp, "rid_label_%i", child_rid.as_int ());
+      pp_string (pp, " -> ");
+      child_rid.dump_node_name_to_pp (pp);
+      pp_string (pp, ";");
+      pp_newline (pp);
+    }
+}
+
+/* FIXME.  */
+
+void
+map_region::dump_child_label (const region_model &model ATTRIBUTE_UNUSED,
+			      region_id this_rid ATTRIBUTE_UNUSED,
+			      region_id child_rid,
+			      pretty_printer *pp) const
+{
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      if (child_rid == (*iter).second)
+	{
+	  tree expr = (*iter).first;
+PUSH_IGNORE_WFORMAT
+	  pp_printf (pp, "%qE: ", expr);
+POP_IGNORE_WFORMAT
+	}
+    }
+}
+
+/* FIXME.  */
+
+region_id
+map_region::get_or_create (region_model *model,
+			   region_id this_rid,
+			   tree expr,
+			   tree type)
+{
+  // TODO
+  region_id *slot = m_map.get (expr);
+  if (slot)
+    return *slot;
+  // FIXME: what kind of region?
+  region_id child_rid = model->add_region (new map_region (this_rid, type));
+  m_map.put (expr, child_rid);
+  return child_rid;
+}
+
+/* FIXME.  */
+
+void
+map_region::add_to_hash (inchash::hash &hstate) const
+{
+  region::add_to_hash (hstate);
+  // TODO
+}
+
+/* FIXME.  */
+
+void
+map_region::remap_region_ids (const region_id_map &map)
+{
+  region::remap_region_ids (map);
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      tree expr = (*iter).first;
+      region_id child_rid = (*iter).second;
+      m_map.put (expr, map.get_dst_for_src (child_rid));
+    }
+}
+
+/* Remove the binding of EXPR to its child region (but not the
+   child region itself).
+   For use when purging unneeded SSA names.  */
+
+void
+map_region::unbind (tree expr)
+{
+  m_map.remove (expr);
+}
+
+/* FIXME.  */
+
+bool
+frame_region::compare_fields (const frame_region &other) const
+{
+  if (!map_region::compare_fields (other))
+    return false;
+  if (m_fun != other.m_fun)
+    return false;
+  return true;
+}
+
+/* FIXME.  */
+
+region *
+frame_region::clone () const
+{
+  return new frame_region (*this);
+}
+
+/* FIXME.  */
+
+void
+frame_region::print_fields (const region_model &model,
+			    region_id this_rid,
+			    pretty_printer *pp) const
+{
+  map_region::print_fields (model, this_rid, pp);
+  pp_printf (pp, ", function: %qs", function_name (m_fun));
+}
+
+/* FIXME.  */
+
+void
+frame_region::add_to_hash (inchash::hash &hstate) const
+{
+  map_region::add_to_hash (hstate);
+  hstate.add_ptr (m_fun);
+}
+
+/* class stack_region : public region.  */
+
+/* FIXME.  */
+
+stack_region::stack_region (const stack_region &other)
+: region (other),
+  m_frame_rids ()
+{
+  int i;
+  region_id *frame_rid;
+  FOR_EACH_VEC_ELT (other.m_frame_rids, i, frame_rid)
+    m_frame_rids.safe_push (*frame_rid);
+}
+
+/* FIXME.  */
+
+bool
+stack_region::compare_fields (const stack_region &other) const
+{
+  if (m_frame_rids.length () != other.m_frame_rids.length ())
+    return false;
+
+  int i;
+  region_id *frame_rid;
+  FOR_EACH_VEC_ELT (m_frame_rids, i, frame_rid)
+    if (m_frame_rids[i] != other.m_frame_rids[i])
+      return false;
+
+  return true;
+}
+
+/* FIXME.  */
+
+region *
+stack_region::clone () const
+{
+  return new stack_region (*this);
+}
+
+/* FIXME.  */
+
+void
+stack_region::print_fields (const region_model &model,
+			    region_id this_rid,
+			    pretty_printer *pp) const
+{
+  region::print_fields (model, this_rid, pp);
+  // TODO
+}
+
+/* FIXME.  */
+
+void
+stack_region::dump_child_label (const region_model &model,
+				region_id this_rid ATTRIBUTE_UNUSED,
+				region_id child_rid,
+				pretty_printer *pp) const
+{
+  function *fun = model.get_region<frame_region> (child_rid)->get_function ();
+  pp_printf (pp, "frame for %qs: ", function_name (fun));
+}
+
+/* FIXME.  */
+
+void
+stack_region::push_frame (region_id frame_rid)
+{
+  m_frame_rids.safe_push (frame_rid);
+}
+
+/* FIXME.  */
+
+region_id
+stack_region::get_current_frame_id () const
+{
+  if (m_frame_rids.length () > 0)
+    return m_frame_rids[m_frame_rids.length () - 1];
+  else
+    return region_id::null ();
+}
+
+/* Pop the topmost frame_region from this stack.
+
+   Purge the frame region and all its descendent regions.
+   Convert any pointers that point into such regions into
+   POISON_KIND_POPPED_STACK svalues.
+
+   Return the ID of any return value from the frame.
+
+   Purge all unused svalues, with the exception of any
+   return value for the frame, which is temporarily
+   preserved in case no regions reference it, so it can
+   be written into a region in the caller.
+
+   Accumulate stats on purged entities into STATS.  */
+
+svalue_id
+stack_region::pop_frame (region_model *model, purge_stats *stats)
+{
+  gcc_assert (m_frame_rids.length () > 0);
+
+  region_id frame_rid = get_current_frame_id ();
+  frame_region *frame = model->get_region<frame_region> (frame_rid);
+
+  /* Evaluate the result, within the callee frame.  */
+  svalue_id result_sid;
+  tree fndecl = frame->get_function ()->decl;
+  tree result = DECL_RESULT (fndecl);
+  if (result)
+    result_sid = model->get_rvalue (result);
+
+  /* Pop the frame.  */
+  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);
+
+  /* Delete unused svalues, but don't delete the return value.  */
+  model->purge_svalues (stats, &result_sid);
+
+  model->validate ();
+
+  return result_sid;
+}
+
+/* FIXME.  */
+
+void
+stack_region::add_to_hash (inchash::hash &hstate) const
+{
+  region::add_to_hash (hstate);
+
+  int i;
+  region_id *frame_rid;
+  FOR_EACH_VEC_ELT (m_frame_rids, i, frame_rid)
+    inchash::add (*frame_rid, hstate);
+}
+
+/* FIXME.  */
+
+void
+stack_region::remap_region_ids (const region_id_map &map)
+{
+  region::remap_region_ids (map);
+  int i;
+  region_id *frame_rid;
+  FOR_EACH_VEC_ELT (m_frame_rids, i, frame_rid)
+    map.update (m_frame_rids[i]);
+}
+
+/* class heap_region : public region.  */
+
+/* FIXME.  */
+
+heap_region::heap_region (const heap_region &other)
+: region (other)
+{
+}
+
+/* FIXME.  */
+
+bool
+heap_region::compare_fields (const heap_region &other) const
+{
+  /* Empty.  */
+  return true;
+}
+
+/* FIXME.  */
+
+region *
+heap_region::clone () const
+{
+  return new heap_region (*this);
+}
+
+/* class root_region : public region.  */
+
+/* FIXME.  */
+
+root_region::root_region ()
+: region (region_id::null (),
+	  svalue_id::null (),
+	  NULL_TREE)
+{
+}
+
+/* FIXME.  */
+
+root_region::root_region (const root_region &other)
+: region (other),
+  m_stack_rid (other.m_stack_rid),
+  m_globals_rid (other.m_globals_rid),
+  m_heap_rid (other.m_heap_rid)
+{
+}
+
+/* FIXME.  */
+
+bool
+root_region::compare_fields (const root_region &other) const
+{
+  if (m_stack_rid != other.m_stack_rid)
+    return false;
+  if (m_globals_rid != other.m_globals_rid)
+    return false;
+  if (m_heap_rid != other.m_heap_rid)
+    return false;
+  return true;
+}
+
+/* FIXME.  */
+
+region *
+root_region::clone () const
+{
+  return new root_region (*this);
+}
+/* FIXME.  */
+
+void
+root_region::print_fields (const region_model &model,
+			   region_id this_rid,
+			   pretty_printer *pp) const
+{
+  region::print_fields (model, this_rid, pp);
+  // TODO
+}
+
+/* FIXME.  */
+
+void
+root_region::dump_child_label (const region_model &model ATTRIBUTE_UNUSED,
+			       region_id this_rid ATTRIBUTE_UNUSED,
+			       region_id child_rid,
+			       pretty_printer *pp) const
+{
+  if (child_rid == m_stack_rid)
+    pp_printf (pp, "stack: ");
+  else if (child_rid == m_globals_rid)
+    pp_printf (pp, "globals: ");
+  else if (child_rid == m_heap_rid)
+    pp_printf (pp, "heap: ");
+}
+
+/* FIXME.  */
+
+region_id
+root_region::push_frame (region_model *model, function *fun,
+			 vec<svalue_id> *arg_sids)
+{
+  gcc_assert (fun);
+  /* arg_sids can be NULL.  */
+
+  ensure_stack_region (model);
+  frame_region *region = new frame_region (m_stack_rid, fun);
+  region_id frame_rid = model->add_region (region);
+
+  // TODO: unify these cases by building a vec of unknown?
+
+  if (arg_sids)
+    {
+      /* Arguments supplied from a caller frame.  */
+
+      tree fndecl = fun->decl;
+      int idx = 0;
+      for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
+	   iter_parm = DECL_CHAIN (iter_parm), ++idx)
+	{
+	  svalue_id arg_sid = (*arg_sids)[idx];
+	  region_id parm_rid
+	    = region->get_or_create (model, frame_rid, iter_parm,
+				     TREE_TYPE (iter_parm));
+	  model->set_value (parm_rid, arg_sid);
+
+	  /* Also do it for default SSA name (sharing the same unknown
+	     value).  */
+	  tree parm_default_ssa = ssa_default_def (fun, iter_parm);
+	  if (parm_default_ssa)
+	    {
+	      region_id defssa_rid
+		= region->get_or_create (model, frame_rid, parm_default_ssa,
+					 TREE_TYPE (iter_parm));
+	      model->set_value (defssa_rid, arg_sid);
+	    }
+	}
+    }
+  else
+    {
+      /* No known arguments (a top-level call within the analysis).  */
+
+      /* Params have a defined, unknown value; they should not inherit
+	 from the poisoned uninit value.  */
+      tree fndecl = fun->decl;
+      for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
+	   iter_parm = DECL_CHAIN (iter_parm))
+	{
+	  region_id parm_rid
+	    = region->get_or_create (model, frame_rid, iter_parm,
+				     TREE_TYPE (iter_parm));
+	  svalue_id parm_sid
+	    = model->set_to_new_unknown_value (parm_rid, TREE_TYPE (iter_parm));
+
+	  /* Also do it for default SSA name (sharing the same unknown
+	     value).  */
+	  tree parm_default_ssa = ssa_default_def (fun, iter_parm);
+	  if (parm_default_ssa)
+	    {
+	      region_id defssa_rid
+		= region->get_or_create (model, frame_rid, parm_default_ssa,
+					 TREE_TYPE (iter_parm));
+	      model->get_region (defssa_rid)->set_value (*model, parm_sid);
+	    }
+	}
+    }
+
+  stack_region *stack = model->get_region <stack_region> (m_stack_rid);
+  stack->push_frame (frame_rid);
+
+  return frame_rid;
+}
+
+/* FIXME.  */
+
+region_id
+root_region::get_current_frame_id (const region_model &model) const
+{
+  stack_region *stack = model.get_region <stack_region> (m_stack_rid);
+  if (stack)
+    return stack->get_current_frame_id ();
+  else
+    return region_id::null ();
+}
+
+/* FIXME.  */
+
+svalue_id
+root_region::pop_frame (region_model *model, purge_stats *out)
+{
+  stack_region *stack = model->get_region <stack_region> (m_stack_rid);
+  return stack->pop_frame (model, out);
+}
+
+/* FIXME.  */
+
+region_id
+root_region::ensure_stack_region (region_model *model)
+{
+  if (m_stack_rid.null_p ())
+    {
+      svalue_id uninit_sid
+	= model->add_svalue (new poisoned_svalue (POISON_KIND_UNINIT));
+      m_stack_rid
+	= model->add_region (new stack_region (model->m_root_rid,
+					       uninit_sid));
+    }
+  return m_stack_rid;
+}
+
+/* FIXME.  */
+
+region_id
+root_region::ensure_globals_region (region_model *model)
+{
+  if (m_globals_rid.null_p ())
+    m_globals_rid
+      = model->add_region (new map_region (model->m_root_rid, NULL_TREE));
+  return m_globals_rid;
+}
+
+/* FIXME.  */
+
+region_id
+root_region::ensure_heap_region (region_model *model)
+{
+  if (m_heap_rid.null_p ())
+    {
+      svalue_id uninit_sid
+	= model->add_svalue (new poisoned_svalue (POISON_KIND_UNINIT));
+      // FIXME: maybe share uninit values?
+      m_heap_rid
+	= model->add_region (new heap_region (model->m_root_rid, uninit_sid));
+    }
+  return m_heap_rid;
+}
+
+/* FIXME.  */
+
+void
+root_region::remap_region_ids (const region_id_map &map)
+{
+  map.update (m_stack_rid);
+  map.update (m_globals_rid);
+  map.update (m_heap_rid);
+}
+
+/* FIXME.  */
+
+void
+root_region::add_to_hash (inchash::hash &hstate) const
+{
+  region::add_to_hash (hstate);
+  inchash::add (m_stack_rid, hstate);
+  inchash::add (m_globals_rid, hstate);
+  inchash::add (m_heap_rid, hstate);
+}
+
+/* class symbolic_region : public map_region.  */
+
+/* FIXME.  */
+
+symbolic_region::symbolic_region (const symbolic_region &other)
+: map_region (other)
+{
+}
+
+/* FIXME.  */
+
+bool
+symbolic_region::compare_fields (const symbolic_region &other) const
+{
+  /* Empty.  */
+  return true;
+}
+
+/* FIXME.  */
+
+region *
+symbolic_region::clone () const
+{
+  return new symbolic_region (*this);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+/* class region_model.  */
+
+/* FIXME.  */
+
+region_model::region_model ()
+{
+  m_root_rid = add_region (new root_region ());
+  m_constraints = new impl_constraint_manager (this);
+  // TODO
+}
+
+/* FIXME.  */
+
+region_model::region_model (const region_model &other)
+: m_root_rid (other.m_root_rid)
+{
+  /* Clone the svalues and regions.  */
+  int i;
+
+  svalue *svalue;
+  FOR_EACH_VEC_ELT (other.m_svalues, i, svalue)
+    m_svalues.safe_push (svalue->clone ());
+
+  region *region;
+  FOR_EACH_VEC_ELT (other.m_regions, i, region)
+    m_regions.safe_push (region->clone ());
+
+  m_constraints = other.m_constraints->clone (this);
+}
+
+/* FIXME.  */
+
+bool
+region_model::operator== (const region_model &other) const
+{
+  /* FIXME: for now, simple equality checking, rather than an attempt
+     to do it independent of ordering.  */
+
+  if (m_root_rid != other.m_root_rid)
+    return false;
+
+  if (m_svalues.length () != other.m_svalues.length ())
+    return false;
+
+  if (*m_constraints != *other.m_constraints)
+    return false;
+
+  unsigned i;
+  svalue *svalue;
+  FOR_EACH_VEC_ELT (other.m_svalues, i, svalue)
+    if (!(*m_svalues[i] == *other.m_svalues[i]))
+      return false;
+
+  region *region;
+  FOR_EACH_VEC_ELT (other.m_regions, i, region)
+    if (!(*m_regions[i] == *other.m_regions[i]))
+      return false;
+
+  return true;
+}
+
+/* FIXME.  */
+
+hashval_t
+region_model::hash () const
+{
+  hashval_t result = 0;
+  int i;
+
+  svalue *svalue;
+  FOR_EACH_VEC_ELT (m_svalues, i, svalue)
+    result ^= svalue->hash ();
+
+  region *region;
+  FOR_EACH_VEC_ELT (m_regions, i, region)
+    result ^= region->hash ();
+
+  // FIXME: constraints
+
+  return result;
+}
+
+/* FIXME.  */
+
+void
+region_model::print (pretty_printer *pp) const
+{
+  // TODO
+
+  int i;
+
+  pp_string (pp, "svalues: [");
+  svalue *svalue;
+  FOR_EACH_VEC_ELT (m_svalues, i, svalue)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      print_svalue (svalue_id::from_int (i), pp);
+    }
+
+  pp_string (pp, "], regions: [");
+
+  region *region;
+  FOR_EACH_VEC_ELT (m_regions, i, region)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      region->print (*this, region_id::from_int (i), pp);
+    }
+
+  pp_string (pp, "], constraints: ");
+
+  m_constraints->print (pp);
+}
+
+/* FIXME.  */
+
+std::string
+region_model::to_str () const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  print (&pp);
+  return pp_formatted_text (&pp);
+}
+
+/* FIXME.  */
+
+void
+region_model::print_svalue (svalue_id sid, pretty_printer *pp) const
+{
+  get_svalue (sid)->print (*this, sid, pp);
+}
+
+/* FIXME.  */
+
+void
+region_model::dump_dot_to_pp (pretty_printer *pp) const
+{
+  graphviz_out gv (pp);
+
+  pp_string (pp, "digraph \"");
+  pp_write_text_to_stream (pp);
+  pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
+  pp_string (pp, "\" {\n");
+
+  gv.indent ();
+
+  pp_string (pp, "overlap=false;\n");
+  pp_string (pp, "compound=true;\n");
+
+  int i;
+
+  svalue *svalue;
+  FOR_EACH_VEC_ELT (m_svalues, i, svalue)
+    svalue->dump_dot_to_pp (*this, svalue_id::from_int (i), pp);
+
+  region *region;
+  FOR_EACH_VEC_ELT (m_regions, i, region)
+    region->dump_dot_to_pp (*this, region_id::from_int (i), pp);
+
+  // FIXME: constraints
+
+  /* Terminate "digraph" */
+  gv.outdent ();
+  pp_string (pp, "}");
+  pp_newline (pp);
+}
+
+/* FIXME.  */
+
+void
+region_model::dump_dot_to_file (FILE *fp) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  pp.buffer->stream = fp;
+  dump_dot_to_pp (&pp);
+  pp_flush (&pp);
+}
+
+/* FIXME.  */
+
+void
+region_model::dump_dot (const char *path) const
+{
+  FILE *fp = fopen (path, "w");
+  dump_dot_to_file (fp);
+  fclose (fp);
+}
+
+/* Dump a multiline representation of this model to PP, showing the
+   region hierarchy, the svalues, and any contraints.  */
+
+void
+region_model::dump_to_pp (pretty_printer *pp) const
+{
+  get_region (m_root_rid)->dump_to_pp (*this, m_root_rid, pp, "", true);
+
+  pp_string (pp, "svalues:");
+  pp_newline (pp);
+  int i;
+  svalue *svalue;
+  FOR_EACH_VEC_ELT (m_svalues, i, svalue)
+    {
+      pp_string (pp, "  ");
+      svalue_id sid = svalue_id::from_int (i);
+      print_svalue (sid, pp);
+      pp_newline (pp);
+    }
+
+  pp_string (pp, "constraint manager:");
+  pp_newline (pp);
+  m_constraints->dump_to_pp (pp);
+}
+
+/* Dump a multiline representation of this model to FILE.  */
+
+void
+region_model::dump (FILE *fp) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp.buffer->stream = fp;
+  dump_to_pp (&pp);
+  pp_flush (&pp);
+}
+
+/* Dump a multiline representation of this model to stderr.  */
+
+DEBUG_FUNCTION void
+region_model::dump () const
+{
+  dump (stderr);
+}
+
+/* FIXME.  */
+
+void
+region_model::validate () const
+{
+  // TODO: skip this in a release build
+
+  m_constraints->validate ();
+
+  unsigned i;
+  region *r;
+  FOR_EACH_VEC_ELT (m_regions, i, r)
+    {
+      svalue_id sid = r->get_value_direct ();
+      if (!sid.null_p ())
+	gcc_assert (sid.as_int () < m_svalues.length ());
+    }
+
+  // TODO: anything else?
+}
+
+/* FIXME.  */
+
+void
+region_model::on_assignment (const gassign *assign)
+{
+  tree lhs = gimple_assign_lhs (assign);
+  tree rhs1 = gimple_assign_rhs1 (assign);
+
+  region_id lhs_rid = get_lvalue (lhs);
+
+  if (lhs_rid.null_p ())
+    return;
+  // TODO: issue a warning for this case
+
+  enum tree_code op = gimple_assign_rhs_code (assign);
+  switch (op)
+    {
+    default:
+      gcc_unreachable ();
+      break;
+
+    case CONSTRUCTOR:
+      {
+	/* e.g. "x ={v} {CLOBBER};"  */
+	// TODO
+      }
+      break;
+
+    case POINTER_PLUS_EXPR:
+      {
+	/* e.g. "_1 = a_10(D) + 12;" */
+	tree ptr = rhs1;
+	tree offset = gimple_assign_rhs2 (assign);
+
+	region_id buffer_rid = deref_rvalue (ptr);
+	svalue_id offset_sid = get_rvalue (offset);
+	/* FIXME: offset is in bytes; we presumably want to
+	   map back to an index.  */
+
+	map_region *array_region = get_region<map_region> (buffer_rid);
+	tree element_type = TREE_TYPE (TREE_TYPE (ptr));
+	tree index = offset;
+	/* FIXME: offset is in bytes; we presumably want to
+	   map back to an index.  */
+
+	region_id element_rid
+	  = array_region->get_or_create (this, buffer_rid, offset,
+					 element_type);
+	svalue_id element_ptr_sid = get_svalue_for_region (element_rid);
+	set_value (lhs_rid, element_ptr_sid);
+      }
+      break;
+
+    case ADDR_EXPR:
+      {
+	/* LHS = &RHS;  */
+	svalue_id ptr_sid = get_rvalue (rhs1);
+	set_value (lhs_rid, ptr_sid);
+      }
+      break;
+
+    case MEM_REF:
+      {
+	region_id rhs_rid = get_lvalue (rhs1);
+	svalue_id rhs_sid
+	  = get_region (rhs_rid)->get_value (*this, true);
+	set_value (lhs_rid, rhs_sid);
+      }
+      break;
+
+    case REAL_CST:
+    case INTEGER_CST:
+    case ARRAY_REF:
+      {
+	/* LHS = RHS;  */
+	svalue_id cst_sid = get_rvalue (rhs1);
+	set_value (lhs_rid, cst_sid);
+      }
+      break;
+
+    case NOP_EXPR:
+      // cast: TODO
+      // fall though for now
+    case SSA_NAME:
+    case VAR_DECL:
+    case PARM_DECL:
+      {
+	/* LHS = VAR;  */
+	region_id rhs_rid = get_lvalue (rhs1);
+	svalue_id var_sid = get_region (rhs_rid)->get_value (*this, true);
+	set_value (lhs_rid, var_sid);
+      }
+      break;
+
+    case EQ_EXPR:
+    case GE_EXPR:
+    case LE_EXPR:
+    case NE_EXPR:
+    case GT_EXPR:
+    case LT_EXPR:
+      {
+	tree rhs2 = gimple_assign_rhs2 (assign);
+
+	// TODO: constraints between svalues
+	svalue_id rhs1_sid = get_rvalue (rhs1);
+	svalue_id rhs2_sid = get_rvalue (rhs2);
+
+	tristate t = eval_condition (rhs1_sid, op, rhs2_sid);
+	if (t.is_known ())
+	  set_value (lhs_rid,
+		     get_rvalue (t.is_true ()
+				 ? boolean_true_node
+				 : boolean_false_node));
+      }
+      break;
+
+    case NEGATE_EXPR:
+    case BIT_NOT_EXPR:
+      {
+	// TODO: unary ops
+
+	// TODO: constant?
+
+	set_to_new_unknown_value (lhs_rid, TREE_TYPE (lhs));
+      }
+      break;
+
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+    case MULT_EXPR:
+    case TRUNC_DIV_EXPR:
+    case TRUNC_MOD_EXPR:
+    case LSHIFT_EXPR:
+    case RSHIFT_EXPR:
+    case BIT_IOR_EXPR:
+    case BIT_XOR_EXPR:
+    case BIT_AND_EXPR:
+    case MIN_EXPR:
+    case MAX_EXPR:
+      {
+	/* Binary ops.  */
+	tree rhs2 = gimple_assign_rhs2 (assign);
+
+	svalue_id rhs1_sid = get_rvalue (rhs1);
+	svalue_id rhs2_sid = get_rvalue (rhs2);
+
+	if (tree rhs1_cst = maybe_get_constant (rhs1_sid))
+	  if (tree rhs2_cst = maybe_get_constant (rhs2_sid))
+	    {
+	      tree result = fold_build2 (op, TREE_TYPE (lhs),
+					 rhs1_cst, rhs2_cst);
+	      if (CONSTANT_CLASS_P (result))
+		{
+		  svalue_id result_sid
+		    = get_svalue_for_constant (result);
+		  set_value (lhs_rid, result_sid);
+		  return;
+		}
+	    }
+	set_to_new_unknown_value (lhs_rid, TREE_TYPE (lhs));
+      }
+      break;
+
+    case COMPONENT_REF:
+      {
+	/* LHS = op0.op1;  */
+	region_id child_rid = get_lvalue (rhs1);
+	svalue_id child_sid = get_region (child_rid)->get_value (*this, true);
+	set_value (lhs_rid, child_sid);
+      }
+      break;
+    }
+}
+
+/* FIXME.  */
+
+void
+region_model::on_call (const gcall *call)
+{
+  region_id lhs_rid;
+  tree lhs_type = NULL_TREE;
+  if (tree lhs = gimple_call_lhs (call))
+    {
+      lhs_rid = get_lvalue (lhs);
+      lhs_type = TREE_TYPE (lhs);
+    }
+
+  if (is_named_call_p (call, "malloc", 1))
+    {
+      // FIXME: we should split state into two outcomes: success and failure
+      // TODO:
+      // FIXME: do we want a new subclass for malloc regions?
+      // FIXME: possibly capturing size as a svalue?
+
+      region_id heap_rid
+	= get_root_region ()->ensure_heap_region (this);
+      region_id new_rid = add_region (new symbolic_region (heap_rid));
+
+      if (tree lhs = gimple_call_lhs (call))
+	{
+	  svalue_id ptr_sid
+	    = add_svalue (new region_svalue (lhs_type, new_rid));
+	  set_value (lhs_rid, ptr_sid);
+	}
+      return;
+    }
+  else if (is_named_call_p (call, "__builtin_alloca", 1))
+    {
+      region_id frame_rid = get_current_frame_id ();
+      region_id new_rid = add_region (new symbolic_region (frame_rid));
+      if (tree lhs = gimple_call_lhs (call))
+	{
+	  svalue_id ptr_sid
+	    = add_svalue (new region_svalue (lhs_type, new_rid));
+	  set_value (lhs_rid, ptr_sid);
+	}
+      return;
+    }
+  else if (is_named_call_p (call, "strlen", 1))
+    {
+      region_id buf_rid = deref_rvalue (gimple_call_arg (call, 0));
+      svalue_id buf_sid = get_region (buf_rid)->get_value (*this, true);
+      if (tree cst_expr = maybe_get_constant (buf_sid))
+	{
+	  if (TREE_CODE (cst_expr) == STRING_CST
+	      && !lhs_rid.null_p ())
+	    {
+	      /* TREE_STRING_LENGTH is sizeof, not strlen.  */
+	      int sizeof_cst = TREE_STRING_LENGTH (cst_expr);
+	      int strlen_cst = sizeof_cst - 1;
+	      tree t_cst = build_int_cst (lhs_type, strlen_cst);
+	      svalue_id result_sid
+		= get_svalue_for_constant (t_cst);
+	      set_value (lhs_rid, result_sid);
+	      return;
+	    }
+	}
+
+      // FIXME: otherwise an unknown value
+#if 0
+      gcc_unreachable ();
+#endif
+    }
+
+  /* Unrecognized call.  */
+
+  /* Unknown return value.  */
+  if (!lhs_rid.null_p ())
+    set_to_new_unknown_value (lhs_rid, lhs_type);
+
+  /* TODO: also, any pointer arguments might have been written through,
+     or the things they point to (implying a graph traversal, which
+     presumably we need to do before overwriting the old value).  */
+}
+
+/* FIXME.  */
+
+void
+region_model::on_return (const greturn *return_stmt)
+{
+  tree callee = get_current_function ()->decl;
+  tree lhs = DECL_RESULT (callee);
+  tree rhs = gimple_return_retval (return_stmt);
+
+  if (lhs && rhs)
+    set_value (get_lvalue (lhs), get_rvalue (rhs));
+}
+
+/* FIXME.  */
+
+void
+region_model::handle_phi (tree lhs, tree rhs, bool is_back_edge)
+{
+  /* For now, don't bother tracking the .MEM SSA names.  */
+  if (tree var = SSA_NAME_VAR (lhs))
+    if (TREE_CODE (var) == VAR_DECL)
+      if (VAR_DECL_IS_VIRTUAL_OPERAND (var))
+	return;
+
+  svalue_id rhs_sid = get_rvalue (rhs);
+
+  if (is_back_edge && get_svalue (rhs_sid)->get_kind () != SK_UNKNOWN)
+    {
+      /* If we have a back edge, we probably have a loop.
+	 Use an unknown value, to avoid effectively unrolling the
+	 loop.
+	 To terminate, we need to avoid generating a series of
+	 models with an unbounded monotonically increasing number of
+	 redundant unknown values; hence we need to purge svalues
+	 before inserting the state into the exploded graph, to
+	 collect unused svalues.  */
+      set_to_new_unknown_value (get_lvalue (lhs), TREE_TYPE (lhs));
+    }
+  else
+    set_value (get_lvalue (lhs), rhs_sid);
+}
+
+/* FIXME.  */
+
+region_id
+region_model::get_lvalue (tree expr)
+{
+  switch (TREE_CODE (expr))
+    {
+    default:
+      gcc_unreachable ();
+
+    case ARRAY_REF:
+      {
+	tree array = TREE_OPERAND (expr, 0);
+	tree index = TREE_OPERAND (expr, 1);
+	// TODO: operands 2 and 3, if present:
+	gcc_assert (TREE_OPERAND (expr, 2) == NULL_TREE);
+	gcc_assert (TREE_OPERAND (expr, 3) == NULL_TREE);
+	region_id array_rid = get_lvalue (array);
+	svalue_id index_sid = get_rvalue (index);
+	map_region *array_region = get_region<map_region> (array_rid);
+	tree element_type = TREE_TYPE (TREE_TYPE (array));
+	region_id element_rid
+	  = array_region->get_or_create (this, array_rid, index, element_type);
+	/* FIXME: ^^^ should be indexed by svalue, rather than by tree?
+	   What about unknown indices?  */
+	return element_rid;
+      }
+      break; // FIXME
+
+    case MEM_REF:
+      {
+	tree ptr = TREE_OPERAND (expr, 0);
+	tree offset = TREE_OPERAND (expr, 1);
+	// FIXME: what about nonzero offset?
+
+	return deref_rvalue (ptr);
+      }
+      break; // FIXME
+
+    case VAR_DECL:
+      /* Handle globals.  */
+      if (DECL_FILE_SCOPE_P (expr))
+	{
+	  region_id globals_rid
+	    = get_root_region ()->ensure_globals_region (this);
+	  map_region *globals = get_region<map_region> (globals_rid);
+	  region_id var_rid = globals->get_or_create (this, globals_rid, expr,
+						      TREE_TYPE (expr));
+	  return var_rid;
+	}
+
+      /* Fall through.  */
+
+    case SSA_NAME:
+    case PARM_DECL:
+    case RESULT_DECL:
+      {
+	// FIXME:
+	gcc_assert (TREE_CODE (expr) == SSA_NAME
+		    || TREE_CODE (expr) == PARM_DECL
+		    || TREE_CODE (expr) == VAR_DECL
+		    || TREE_CODE (expr) == RESULT_DECL);
+
+	region_id frame_rid = get_current_frame_id ();
+	frame_region *frame = get_region <frame_region> (frame_rid);
+	gcc_assert (frame);
+	region_id child_rid = frame->get_or_create (this, frame_rid, expr,
+						    TREE_TYPE (expr));
+	return child_rid;
+      }
+
+    case COMPONENT_REF:
+      {
+	/* obj.field  */
+	tree obj = TREE_OPERAND (expr, 0);
+	tree field = TREE_OPERAND (expr, 1);
+	region_id obj_rid = get_lvalue (obj);
+	map_region *obj_region = get_region<map_region> (obj_rid);
+	region_id child_rid = obj_region->get_or_create (this, obj_rid, field,
+							 TREE_TYPE (field));
+	return child_rid;
+      }
+      break;
+
+    case STRING_CST:
+      {
+	// FIXME: should map to a unique, custom region, containing the constant
+	tree cst_type = TREE_TYPE (expr);
+	map_region *cst_region = new map_region (m_root_rid, cst_type);
+	svalue_id cst_sid = get_svalue_for_constant (expr);
+	cst_region->set_value (*this, cst_sid);
+	region_id cst_rid = add_region (cst_region);
+	return cst_rid;
+      }
+      break;
+    }
+}
+
+/* FIXME.  */
+
+svalue_id
+region_model::get_rvalue (tree expr)
+{
+  switch (TREE_CODE (expr))
+    {
+    default:
+      gcc_unreachable ();
+
+    case ADDR_EXPR:
+      {
+	/* "&EXPR".  */
+	if (TREE_CODE (TREE_OPERAND (expr, 0)) == FUNCTION_DECL)
+	  return get_svalue_for_fndecl (TREE_OPERAND (expr, 0));
+	region_id expr_rid = get_lvalue (TREE_OPERAND (expr, 0));
+	return get_svalue_for_region (expr_rid);
+      }
+      break;
+
+    case ARRAY_REF:
+      {
+	region_id element_rid = get_lvalue (expr);
+	return get_region (element_rid)->get_value (*this, true);
+      }
+
+    case INTEGER_CST:
+    case REAL_CST:
+    case STRING_CST:
+      return get_svalue_for_constant (expr);
+
+    case SSA_NAME:
+    case VAR_DECL:
+    case PARM_DECL:
+    case RESULT_DECL:
+      {
+	region_id var_rid = get_lvalue (expr);
+	return get_region (var_rid)->get_value (*this, true);
+      }
+    }
+}
+
+/* FIXME.  */
+
+svalue_id
+region_model::get_svalue_for_region (region_id rid)
+{
+  // TODO: maybe reuse existing ones? (maybe keep a record in the region?)
+  tree type = get_region (rid)->get_type ();
+  // FIXME: ^^^ should this be a pointer type to type?
+  return add_svalue (new region_svalue (type, rid));
+}
+
+/* FIXME.  */
+
+svalue_id
+region_model::get_svalue_for_constant (tree cst_expr)
+{
+  /* Reuse one if it already exists.  */
+  // FIXME: maybe store a map, rather than do linear search?
+  int i;
+  svalue *svalue;
+  FOR_EACH_VEC_ELT (m_svalues, i, svalue)
+    if (constant_svalue *cst_svalue = svalue->dyn_cast_constant_svalue ())
+      if (cst_svalue->get_constant () == cst_expr)
+	return svalue_id::from_int (i);
+
+  svalue_id cst_sid = add_svalue (new constant_svalue (cst_expr));
+  return cst_sid;
+}
+
+/* FIXME.  */
+
+svalue_id
+region_model::get_svalue_for_fndecl (tree fndecl)
+{
+  return get_svalue_for_constant (fndecl);
+}
+
+/* FIXME.  */
+
+region_id
+region_model::deref_rvalue (tree ptr)
+{
+  svalue_id ptr_sid = get_rvalue (ptr);
+  gcc_assert (!ptr_sid.null_p ());
+  svalue *ptr_svalue = get_svalue (ptr_sid);
+  gcc_assert (ptr_svalue);
+
+  switch (ptr_svalue->get_kind ())
+    {
+    case SK_REGION:
+      {
+	region_svalue *region_sval = ptr_svalue->dyn_cast_region_svalue ();
+	// FIXME: ^^ as_a
+	return region_sval->get_pointee ();
+      }
+
+    case SK_CONSTANT:
+      {
+	constant_svalue *constant_sval
+	  = ptr_svalue->dyn_cast_constant_svalue ();
+	// FIXME: ^^ as_a
+
+	/* TODO: issue a warning (presumably using some kind of context
+	   within the region_model?).  */
+	// FIXME: the malloc state machine already complains about this
+#if 0
+	tree tree_cst = constant_sval->get_constant ();
+	if (zerop (tree_cst))
+	  warning (0, "null pointer deref FIXME");
+#endif
+
+	goto create_symbolic_region;
+	// map of regions per constant?
+      }
+
+    case SK_POISONED:
+      {
+	poisoned_svalue *poisoned_sval
+	  = ptr_svalue->dyn_cast_poisoned_svalue ();
+	// FIXME: ^^ as_a
+	switch (poisoned_sval->get_poison_kind ())
+	  {
+	  default:
+	    gcc_unreachable ();
+	  case POISON_KIND_UNINIT:
+	    warning (0, "deref of uninitialized pointer");
+	    break;
+	  case POISON_KIND_FREED:
+	    warning (0, "use-after-free");
+	    break;
+	  case POISON_KIND_POPPED_STACK:
+	    warning (0, "use of memory within popped stack");
+	    break;
+	  }
+	goto create_symbolic_region;
+      }
+
+    case SK_UNKNOWN:
+      {
+      create_symbolic_region:
+	/* We need a symbolic_region to represent this unknown region.
+	   We don't know if it on the heap, stack, or a global,
+	   so use the root region as parent.  */
+	region_id new_rid = add_region (new symbolic_region (m_root_rid));
+
+	/* We need to write the region back into the pointer,
+	   or we'll get a new, different region each time.
+	   We do this by changing the meaning of ptr_sid, replacing
+	   the unknown value with the ptr to the new region.
+	   We replace the meaning of the ID rather than simply writing
+	   to PTR's lvalue since there could be several places sharing
+	   the same unknown ptr value.  */
+	svalue *ptr_val = new region_svalue (TREE_TYPE (ptr), new_rid);
+	replace_svalue (ptr_sid, ptr_val);
+
+	return new_rid;
+      }
+    }
+
+  gcc_unreachable ();
+
+  // FIXME: how to hit this path?
+  return region_id::null ();
+}
+
+/* FIXME.  */
+
+void
+region_model::set_value (region_id lhs_rid, svalue_id rhs_sid)
+{
+  gcc_assert (!lhs_rid.null_p ());
+  gcc_assert (!rhs_sid.null_p ());
+  get_region (lhs_rid)->set_value (*this, rhs_sid);
+}
+
+/* FIXME.  */
+
+tristate
+region_model::eval_condition (svalue_id lhs_sid,
+			      enum tree_code op,
+			      svalue_id rhs_sid) const
+{
+  svalue *lhs = get_svalue (lhs_sid);
+  svalue *rhs = get_svalue (rhs_sid);
+  gcc_assert (lhs);
+  gcc_assert (rhs);
+
+  if (lhs && rhs)
+    {
+      if (lhs == rhs)
+	{
+	  /* If we have the same svalue, then we have equality.  */
+	  // FIXME: but what about poisoned values?
+	  switch (op)
+	    {
+	    default:
+	      gcc_unreachable ();
+
+	    case EQ_EXPR:
+	    case GE_EXPR:
+	    case LE_EXPR:
+	      return tristate::TS_TRUE;
+
+	    case NE_EXPR:
+	    case GT_EXPR:
+	    case LT_EXPR:
+	      return tristate::TS_FALSE;
+	    }
+	}
+
+      /* If we have a pair of region_svalues, see if they point to the same
+	 region.  */
+      if (region_svalue *lhs_ptr = lhs->dyn_cast_region_svalue ())
+	if (region_svalue *rhs_ptr = rhs->dyn_cast_region_svalue ())
+	  {
+	    switch (op)
+	      {
+	      default:
+		gcc_unreachable ();
+
+	      case EQ_EXPR:
+		if (lhs_ptr->get_pointee () == rhs_ptr->get_pointee ())
+		  return tristate::TS_TRUE;
+		else
+		  return tristate::TS_FALSE;
+		break;
+
+	      case NE_EXPR:
+		if (lhs_ptr->get_pointee () != rhs_ptr->get_pointee ())
+		  return tristate::TS_TRUE;
+		else
+		  return tristate::TS_FALSE;
+		break;
+
+	      case GE_EXPR:
+	      case LE_EXPR:
+		if (lhs_ptr->get_pointee () == rhs_ptr->get_pointee ())
+		  return tristate::TS_TRUE;
+		break;
+
+	      case GT_EXPR:
+	      case LT_EXPR:
+		if (lhs_ptr->get_pointee () == rhs_ptr->get_pointee ())
+		  return tristate::TS_FALSE;
+		break;
+	      }
+	    /* Otherwise, try constraints.  */
+	  }
+
+      /* If we have a pair of constants, compare them.  */
+      if (constant_svalue *cst_lhs = lhs->dyn_cast_constant_svalue ())
+	if (constant_svalue *cst_rhs = rhs->dyn_cast_constant_svalue ())
+	  {
+	    tree lhs_const = cst_lhs->get_constant ();
+	    tree rhs_const = cst_rhs->get_constant ();
+
+	    /* If one or both of the "constants" are function pointers, then
+	       we have equality iff the trees are equal.  */
+	    if (TREE_CODE (lhs_const) == FUNCTION_DECL
+		|| TREE_CODE (rhs_const) == FUNCTION_DECL)
+	      {
+		bool eq = (lhs_const == rhs_const);
+		switch (op)
+		  {
+		  default:
+		    gcc_unreachable ();
+
+		  case EQ_EXPR:
+		    return tristate (eq);
+
+		  case GE_EXPR:
+		  case LE_EXPR:
+		    if (eq)
+		      return tristate (true);
+		    else
+		      return tristate (tristate::TS_UNKNOWN);
+
+		  case NE_EXPR:
+		    return tristate (lhs_const != rhs_const);
+
+		  case GT_EXPR:
+		  case LT_EXPR:
+		    if (eq)
+		      return tristate (false);
+		    return tristate::TS_UNKNOWN;
+		  }
+	      }
+
+	    /* Otherwise, we have a pair of constants; compare them.  */
+	    gcc_assert (CONSTANT_CLASS_P (lhs_const));
+	    gcc_assert (CONSTANT_CLASS_P (rhs_const));
+
+	    tree comparison
+	      = fold_build2 (op, boolean_type_node, lhs_const, rhs_const);
+	    if (comparison == boolean_true_node)
+	      return tristate (tristate::TS_TRUE);
+	    if (comparison == boolean_false_node)
+	      return tristate (tristate::TS_FALSE);
+	    return tristate::TS_UNKNOWN;
+	  }
+    }
+
+  /* Otherwise, try constraints.  */
+  return m_constraints->eval_condition (lhs_sid, op, rhs_sid);
+}
+
+/* FIXME.  */
+
+bool
+region_model::add_constraint (tree lhs, enum tree_code op, tree rhs)
+{
+  svalue_id lhs_sid = get_rvalue (lhs);
+  svalue_id rhs_sid = get_rvalue (rhs);
+
+  tristate t_cond = eval_condition (lhs_sid, op, rhs_sid);
+
+  /* If we already have the condition, do nothing.  */
+  if (t_cond.is_true ())
+    return true;
+
+  /* Reject constraint that contradicts existing knowledge as unsatisfiable.  */
+  if (t_cond.is_false ())
+    return false;
+
+  /* Store the constraint.  */
+  m_constraints->add_constraint (lhs_sid, op, rhs_sid);
+
+  return true;
+}
+
+/* FIXME.  */
+
+tristate
+region_model::eval_condition (tree lhs,
+			      enum tree_code op,
+			      tree rhs)
+{
+  return eval_condition (get_rvalue (lhs), op, get_rvalue (rhs));
+}
+
+/* If SID is a constant, return the underlying tree value.
+   Otherwise, return NULL_TREE.  */
+
+tree
+region_model::maybe_get_constant (svalue_id sid)
+{
+  gcc_assert (!sid.null_p ());
+  svalue *sval = get_svalue (sid);
+  if (constant_svalue *cst_sval = sval->dyn_cast_constant_svalue ())
+    return cst_sval->get_constant ();
+  return NULL_TREE;
+}
+
+/* FIXME.  */
+
+svalue_id
+region_model::set_to_new_unknown_value (region_id dst_rid, tree type)
+{
+  gcc_assert (!dst_rid.null_p ());
+  svalue_id new_sid = add_svalue (new unknown_svalue (type));
+  set_value (dst_rid, new_sid);
+
+  // TODO: presumably purge all child regions too (but do this in set_value?)
+
+  return new_sid;
+}
+
+/* FIXME.  */
+// FIXME: this also updates frame information
+
+bool
+region_model::apply_constraints_for_edge (const superedge &edge,
+					  const gimple *last_stmt)
+{
+  /* Handle frame updates for interprocedural edges.  */
+  switch (edge.m_kind)
+    {
+    default:
+      break;
+
+    case SUPEREDGE_CALL:
+      {
+	const call_superedge *call_edge
+	  = edge.dyn_cast_call_superedge ();
+	// FIXME: should be an as_a
+	update_for_call_superedge (*call_edge);
+      }
+      break;
+
+    case SUPEREDGE_RETURN:
+      {
+	const return_superedge *return_edge
+	  = edge.dyn_cast_return_superedge ();
+	// FIXME: should be an as_a
+	update_for_return_superedge (*return_edge);
+      }
+      break;
+    }
+
+  // TODO: update constraints
+
+  if (last_stmt == NULL)
+    return true;
+
+  if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
+    return apply_constraints_for_gcond (edge, cond_stmt);
+
+  // FIXME: some linker issues with is_a_helper const
+  if (const gswitch *switch_stmt = dyn_cast <gswitch *> (const_cast <gimple *> (last_stmt)))
+    return apply_constraints_for_gswitch (edge, switch_stmt);
+
+  return true;
+}
+
+/* FIXME.  */
+
+void
+region_model::update_for_call_superedge (const call_superedge &call_edge)
+{
+  /* Build a vec of argument svalue_id, using the current top
+     frame for resolving tree expressions.  */
+  auto_vec<svalue_id> arg_sids;
+
+  tree callee = call_edge.get_callee_decl ();
+  gcall *call_stmt = call_edge.get_call_stmt ();
+
+  int i = 0;
+  for (tree parm = DECL_ARGUMENTS (callee); parm;
+       parm = DECL_CHAIN (parm), ++i)
+    {
+      tree arg = gimple_call_arg (call_stmt, i);
+      arg_sids.safe_push (get_rvalue (arg));
+    }
+
+  push_frame (call_edge.get_callee_function (), &arg_sids);
+}
+
+/* FIXME.  */
+
+void
+region_model::update_for_return_superedge (const return_superedge &return_edge)
+{
+  purge_stats stats;
+  svalue_id result_sid = pop_frame (&stats);
+  // TODO: do something with the stats?
+
+  // FIXME: result_sid could change meaning within pop_frame;
+  // maybe we can return the new sid for the result (if any)
+
+  /* Set the result of the call, within the caller frame.  */
+  gcall *call_stmt = return_edge.get_call_stmt ();
+  tree lhs = gimple_call_lhs (call_stmt);
+  if (lhs)
+    set_value (get_lvalue (lhs), result_sid);
+}
+
+/* FIXME.  */
+
+bool
+region_model::apply_constraints_for_gcond (const superedge &edge,
+					   const gcond *cond_stmt)
+{
+  ::edge cfg_edge = edge.get_any_cfg_edge ();
+  gcc_assert (cfg_edge != NULL);
+  gcc_assert (cfg_edge->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE));
+
+  enum tree_code op = gimple_cond_code (cond_stmt);
+  tree lhs = gimple_cond_lhs (cond_stmt);
+  tree rhs = gimple_cond_rhs (cond_stmt);
+  if (cfg_edge->flags & EDGE_FALSE_VALUE)
+    op = invert_tree_comparison (op, false /* honor_nans */);
+  return add_constraint (lhs, op, rhs);
+}
+
+/* FIXME.  */
+
+bool
+region_model::apply_constraints_for_gswitch (const superedge &edge,
+					     const gswitch *switch_stmt)
+{
+  // FIXME: use an as_a for this cast:
+  const switch_cfg_superedge &switch_edge = (const switch_cfg_superedge &)edge;
+  tree index  = gimple_switch_index (switch_stmt);
+  tree case_label = switch_edge.get_case_label ();
+  gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
+  tree lower_bound = CASE_LOW (case_label);
+  tree upper_bound = CASE_HIGH (case_label);
+  if (lower_bound)
+    {
+      if (upper_bound)
+	{
+	  /* Range.  */
+	  if (!add_constraint (index, GE_EXPR, lower_bound))
+	    return false;
+	  return add_constraint (index, LE_EXPR, upper_bound);
+	}
+      else
+	/* Single-value.  */
+	return add_constraint (index, EQ_EXPR, lower_bound);
+    }
+  else
+    {
+      /* The default case.
+	 Add exclusions based on the other cases.  */
+      for (unsigned other_idx = 1;
+	   other_idx < gimple_switch_num_labels (switch_stmt);
+	   other_idx++)
+	{
+	  tree other_label = gimple_switch_label (switch_stmt,
+						  other_idx);
+	  tree other_lower_bound = CASE_LOW (other_label);
+	  tree other_upper_bound = CASE_HIGH (other_label);
+	  gcc_assert (other_lower_bound);
+	  if (other_upper_bound)
+	    {
+	      /* Exclude this range-valued case.
+		 For now, we just exclude the boundary values.
+		 TODO: exclude the values within the region.  */
+	      if (!add_constraint (index, NE_EXPR, other_lower_bound))
+		return false;
+	      if (!add_constraint (index, NE_EXPR, other_upper_bound))
+		return false;
+	    }
+	  else
+	    /* Exclude this single-valued case.  */
+	    if (!add_constraint (index, NE_EXPR, other_lower_bound))
+	      return false;
+	}
+      return true;
+    }
+}
+
+/* FIXME.  */
+
+root_region *
+region_model::get_root_region () const
+{
+  return get_region<root_region> (m_root_rid);
+}
+
+/* FIXME.
+   Return the region_id of the new frame_region.  */
+
+region_id
+region_model::push_frame (function *fun, vec<svalue_id> *arg_sids)
+{
+  return get_root_region ()->push_frame (this, fun, arg_sids);
+}
+
+/* FIXME.  */
+
+region_id
+region_model::get_current_frame_id () const
+{
+  return get_root_region ()->get_current_frame_id (*this);
+}
+
+/* FIXME.  */
+
+function *
+region_model::get_current_function () const
+{
+  region_id frame_id = get_current_frame_id ();
+  frame_region *frame = get_region<frame_region> (frame_id);
+  return frame->get_function ();
+}
+
+/* FIXME.  */
+
+svalue_id
+region_model::pop_frame (purge_stats *out)
+{
+  return get_root_region ()->pop_frame (this, out);
+}
+
+/* FIXME.  */
+
+svalue_id
+region_model::add_svalue (svalue *sval)
+{
+  gcc_assert (sval);
+  m_svalues.safe_push (sval);
+  return svalue_id::from_int (m_svalues.length () - 1);
+}
+
+/* Change the meaning of SID to be NEW_SVAL
+   (e.g. when deferencing an unknown pointer, the pointer
+   becomes a pointer to a symbolic region, so that all users
+   of the former unknown pointer are now effectively pointing
+   at the same region).  */
+
+void
+region_model::replace_svalue (svalue_id sid, svalue *new_sval)
+{
+  gcc_assert (!sid.null_p ());
+  int idx = sid.as_int ();
+
+  gcc_assert (m_svalues[idx]);
+  delete m_svalues[idx];
+
+  m_svalues[idx] = new_sval;
+}
+
+/* FIXME.  */
+
+region_id
+region_model::add_region (region *r)
+{
+  gcc_assert (r);
+  m_regions.safe_push (r);
+  return region_id::from_int (m_regions.length () - 1);
+}
+
+/* FIXME.  */
+
+svalue *
+region_model::get_svalue (svalue_id sval_id) const
+{
+  if (sval_id.null_p ())
+    return NULL;
+  return m_svalues[sval_id.as_int ()];
+}
+
+/* FIXME.  */
+
+region *
+region_model::get_region (region_id rid) const
+{
+  if (rid.null_p ())
+    return NULL;
+  return m_regions[rid.as_int ()];
+}
+
+/* Helper class for region_model::purge_svalues.  */
+
+class restrict_to_used_svalues : public purge_criteria
+{
+public:
+  restrict_to_used_svalues (const sbitmap &used) : m_used (used) {}
+
+  bool should_purge_p (svalue_id sid) const FINAL OVERRIDE
+  {
+    gcc_assert (!sid.null_p ());
+    return !bitmap_bit_p (m_used, sid.as_int ());
+  }
+
+private:
+  const sbitmap &m_used;
+};
+
+/* Remove unused svalues from this model, accumulating stats into STATS.
+
+   Treat KNOWN_USED as used (this is for handling values being returned
+   from functions as their frame is popped).
+
+   This is important for ensuring that the analysis of loops terminates.
+   Otherwise, we could generate a succession of models with unreferenced
+   "unknown" values, where the number of redundant unknown values could
+   grow without bounds, and each such model would be treated as distinct.  */
+
+void
+region_model::purge_svalues (purge_stats *stats, svalue_id *known_used_sid)
+{
+  //svalue_id_set used (*this);
+  auto_sbitmap used (m_svalues.length ());
+  bitmap_clear (used); // FIXME: is this needed?
+
+  if (known_used_sid)
+    if (!known_used_sid->null_p ())
+      bitmap_set_bit (used, known_used_sid->as_int ());
+
+  /* Walk the regions, marking sids that are used.  */
+  unsigned i;
+  region *r;
+  FOR_EACH_VEC_ELT (m_regions, i, r)
+    {
+      svalue_id sid = r->get_value_direct ();
+      if (!sid.null_p ())
+	bitmap_set_bit (used, sid.as_int ());
+    }
+
+  /* Now purge any constraints involving svalues we don't care about.  */
+  restrict_to_used_svalues criterion (used);
+  m_constraints->purge (criterion, stats);
+
+  /* Mark any sids that are in constraints that survived.  */
+  {
+    equiv_class *ec;
+    FOR_EACH_VEC_ELT (m_constraints->m_equiv_classes, i, ec)
+      {
+	int j;
+	svalue_id *sid;
+	FOR_EACH_VEC_ELT (ec->m_vars, j, sid)
+	  {
+	    gcc_assert (!sid->null_p ());
+	    bitmap_set_bit (used, sid->as_int ());
+	  }
+	// FIXME: should we just do this for constants?
+	// is everything else purged?
+      }
+  }
+
+  /* Build a mapping from old-sid to new-sid so that we can preserve
+     order of the used IDs and move all redundant ones to the end.
+     Iterate though svalue IDs, adding used ones to the front of
+     the new list, and unused ones to the back.  */
+  svalue_id_map map (m_svalues.length ());
+  int next_used_new_sid = 0;
+  int after_next_unused_new_sid = m_svalues.length ();
+  for (unsigned i = 0; i < m_svalues.length (); i++)
+    {
+      svalue_id src (svalue_id::from_int (i));
+      if (bitmap_bit_p (used, i))
+	map.put (src, svalue_id::from_int (next_used_new_sid++));
+      else
+	map.put (src, svalue_id::from_int (--after_next_unused_new_sid));
+    }
+  /* The two insertion points should have met.  */
+  gcc_assert (next_used_new_sid == after_next_unused_new_sid);
+
+  /* Now walk the regions and the constraints, remapping sids,
+     so that all the redundant svalues are at the end.  */
+  remap_svalue_ids (map);
+
+  /* Drop the redundant svalues from the end of the vector.  */
+  while (m_svalues.length () > next_used_new_sid)
+    {
+      delete m_svalues.pop ();
+      stats->m_num_svalues++;
+    }
+
+  if (known_used_sid)
+    map.update (*known_used_sid);
+
+  validate ();
+}
+
+/* Renumber the svalues within this model according to MAP.  */
+
+void
+region_model::remap_svalue_ids (const svalue_id_map &map)
+{
+  /* Update IDs within regions.  */
+  unsigned i;
+  region *r;
+  FOR_EACH_VEC_ELT (m_regions, i, r)
+    r->remap_svalue_ids (map);
+
+  /* Update IDs within ECs within constraints.  */
+  m_constraints->remap_svalue_ids (map);
+
+  /* Build a reordered svalues vector.  */
+  auto_vec<svalue *> new_svalues;
+  for (int i = 0; i < m_svalues.length (); i++)
+    {
+      svalue_id dst (svalue_id::from_int (i));
+      svalue_id src = map.get_src_for_dst (dst);
+      new_svalues.safe_push (get_svalue (src));
+    }
+
+  /* Copy over the reordered vec to m_svalues.  */
+  m_svalues.truncate (0);
+  svalue *sval;
+  FOR_EACH_VEC_ELT (new_svalues, i, sval)
+    m_svalues.safe_push (sval);
+}
+
+/* Renumber the regions within this model according to MAP.  */
+
+void
+region_model::remap_region_ids (const region_id_map &map)
+{
+  /* Update IDs within regions.  */
+  unsigned i;
+  region *r;
+  FOR_EACH_VEC_ELT (m_regions, i, r)
+    r->remap_region_ids (map);
+
+  /* Update IDs within svalues.  */
+  svalue *sval;
+  FOR_EACH_VEC_ELT (m_svalues, i, sval)
+    sval->remap_region_ids (map);
+
+  /* Build a reordered regions vector.  */
+  auto_vec<region *> new_regions;
+  for (int i = 0; i < m_regions.length (); i++)
+    {
+      region_id dst (region_id::from_int (i));
+      region_id src = map.get_src_for_dst (dst);
+      new_regions.safe_push (get_region (src));
+    }
+
+  /* Copy over the reordered vec to m_regions.  */
+  m_regions.truncate (0);
+  FOR_EACH_VEC_ELT (new_regions, i, r)
+    m_regions.safe_push (r);
+}
+
+/* Delete all regions within SET_TO_PURGE, remapping region IDs for
+   other regions.  It's required that there are no uses of the
+   regions within the set (or the region IDs will become invalid).
+
+   Accumulate stats to STATS.  */
+
+void
+region_model::purge_regions (const region_id_set &set_to_purge,
+			     purge_stats *stats)
+{
+  /* Build a mapping from old-rid to new-rid so that we can preserve
+     order of the used IDs and move all redundant ones to the end.
+     Iterate though region IDs, adding used ones to the front of
+     the new list, and unused ones to the back.  */
+  region_id_map map (m_regions.length ());
+  int next_used_new_rid = 0;
+  int after_next_unused_new_rid = m_regions.length ();
+  for (unsigned i = 0; i < m_regions.length (); i++)
+    {
+      region_id src (region_id::from_int (i));
+      if (set_to_purge.region_p (src))
+	map.put (src, region_id::from_int (--after_next_unused_new_rid));
+      else
+	map.put (src, region_id::from_int (next_used_new_rid++));
+    }
+  /* The two insertion points should have met.  */
+  gcc_assert (next_used_new_rid == after_next_unused_new_rid);
+
+  /* Now walk the regions and svalues, remapping rids,
+     so that all the redundant regions are at the end.  */
+  remap_region_ids (map);
+
+  /* Drop the redundant regions from the end of the vector.  */
+  while (m_regions.length () > next_used_new_rid)
+    {
+      delete m_regions.pop ();
+      stats->m_num_regions++;
+    }
+}
+
+/* Populate *OUT with RID and all of its descendents.  */
+
+void
+region_model::get_descendents (region_id rid, region_id_set *out) const
+{
+  out->add_region (rid);
+
+  bool changed = true;
+  while (changed)
+    {
+      changed = false;
+      unsigned i;
+      region *r;
+      FOR_EACH_VEC_ELT (m_regions, i, r)
+	{
+	  region_id iter_rid = region_id::from_int (i);
+	  if (!out->region_p (iter_rid))
+	    {
+	      region_id parent_rid = r->get_parent ();
+	      if (!parent_rid.null_p ())
+		if (out->region_p (parent_rid))
+		  {
+		    out->add_region (iter_rid);
+		    changed = true;
+		  }
+	    }
+	}
+    }
+}
+
+/* FIXME.  */
+
+void
+inchash::add (svalue_id sid, inchash::hash &hstate)
+{
+  hstate.add_int (sid.as_int ());
+}
+
+/* FIXME.  */
+
+void
+inchash::add (region_id rid, inchash::hash &hstate)
+{
+  hstate.add_int (rid.as_int ());
+}
+
+// FIXME:
+void
+debug (const region_model &rmodel)
+{
+  fprintf (stderr, "%s\n", rmodel.to_str ().c_str ());
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+static void
+test_dump ()
+{
+  region_model model;
+  model.get_root_region ()->ensure_stack_region (&model);
+  model.get_root_region ()->ensure_globals_region (&model);
+  model.get_root_region ()->ensure_heap_region (&model);
+
+  auto_fix_quotes sentinel;
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  model.dump_to_pp (&pp);
+  ASSERT_STREQ (pp_formatted_text (&pp),
+		"r0: {kind: `root', parent: null, sval: null}\n"
+		"|-stack: r1: {kind: `stack', parent: r0, sval: sv0}\n"
+		"|  |: sval: sv0: {poisoned: uninit}\n"
+		"|-globals: r2: {kind: `map', parent: r0, sval: null, map: {}}\n"
+		"`-heap: r3: {kind: `heap', parent: r0, sval: sv1}\n"
+		"  |: sval: sv1: {poisoned: uninit}\n"
+		"svalues:\n"
+		"  sv0: {poisoned: uninit}\n"
+		"  sv1: {poisoned: uninit}\n"
+		"constraint manager:\n"
+		"  equiv classes:\n"
+		"  constraints:\n");
+}
+
+static void
+test_unique_constants ()
+{
+  tree int_0 = build_int_cst (integer_type_node, 0);
+  tree int_42 = build_int_cst (integer_type_node, 42);
+
+  region_model model;
+  ASSERT_EQ (model.get_rvalue (int_0), model.get_rvalue (int_0));
+  ASSERT_NE (model.get_rvalue (int_0), model.get_rvalue (int_42));
+}
+
+/* Check that operator== and hashing works as expected for the
+   various svalue subclasses.  */
+
+static void
+test_svalue_equality ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+
+  /* Create pairs instances of the various subclasses of svalue,
+     testing for hash and equality between (this, this) and
+     (this, other of same subclass). */
+  svalue *ptr_to_r0
+    = new region_svalue (ptr_type_node, region_id::from_int (0));
+  svalue *ptr_to_r1
+    = new region_svalue (ptr_type_node, region_id::from_int (1));
+
+  ASSERT_EQ (ptr_to_r0->hash (), ptr_to_r0->hash ());
+  ASSERT_EQ (*ptr_to_r0, *ptr_to_r0);
+
+  ASSERT_NE (ptr_to_r0->hash (), ptr_to_r1->hash ());
+  ASSERT_NE (*ptr_to_r0, *ptr_to_r1);
+
+  svalue *cst_int_42 = new constant_svalue (int_42);
+  svalue *cst_int_0 = new constant_svalue (int_0);
+
+  ASSERT_EQ (cst_int_42->hash (), cst_int_42->hash ());
+  ASSERT_EQ (*cst_int_42, *cst_int_42);
+
+  ASSERT_NE (cst_int_42->hash (), cst_int_0->hash ());
+  ASSERT_NE (*cst_int_42, *cst_int_0);
+
+  svalue *uninit = new poisoned_svalue (POISON_KIND_UNINIT);
+  svalue *freed = new poisoned_svalue (POISON_KIND_FREED);
+
+  ASSERT_EQ (uninit->hash (), uninit->hash ());
+  ASSERT_EQ (*uninit, *uninit);
+
+  ASSERT_NE (uninit->hash (), freed->hash ());
+  ASSERT_NE (*uninit, *freed);
+
+  svalue *unknown_0 = new unknown_svalue (ptr_type_node);
+  svalue *unknown_1 = new unknown_svalue (ptr_type_node);
+  ASSERT_EQ (unknown_0->hash (), unknown_0->hash ());
+  ASSERT_EQ (*unknown_0, *unknown_0);
+  ASSERT_EQ (*unknown_1, *unknown_1);
+
+  /* Comparisons between different kinds of svalue.  */
+  ASSERT_NE (*ptr_to_r0, *cst_int_42);
+  ASSERT_NE (*ptr_to_r0, *uninit);
+  ASSERT_NE (*ptr_to_r0, *unknown_0);
+  ASSERT_NE (*cst_int_42, *ptr_to_r0);
+  ASSERT_NE (*cst_int_42, *uninit);
+  ASSERT_NE (*cst_int_42, *unknown_0);
+  ASSERT_NE (*uninit, *ptr_to_r0);
+  ASSERT_NE (*uninit, *cst_int_42);
+  ASSERT_NE (*uninit, *unknown_0);
+  ASSERT_NE (*unknown_0, *ptr_to_r0);
+  ASSERT_NE (*unknown_0, *cst_int_42);
+  ASSERT_NE (*unknown_0, *uninit);
+
+  delete ptr_to_r0;
+  delete ptr_to_r1;
+  delete cst_int_42;
+  delete cst_int_0;
+  delete uninit;
+  delete freed;
+  delete unknown_0;
+  delete unknown_1;
+}
+
+/* Check that operator== and hashing works as expected for the
+   various region subclasses.  */
+
+static void
+test_region_equality ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+
+  tree x = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("x"),
+		       integer_type_node);
+  tree y = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("y"),
+		       integer_type_node);
+  tree z = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("z"),
+		       integer_type_node);
+  // TODO
+
+  region *r0 = new map_region (region_id::from_int (3), integer_type_node);
+  region *r1 = new map_region (region_id::from_int (4), integer_type_node);
+
+  ASSERT_EQ (*r0, *r0);
+  ASSERT_EQ (r0->hash (), r0->hash ());
+  ASSERT_NE (*r0, *r1);
+  ASSERT_NE (r0->hash (), r1->hash ());
+
+  // TODO: test coverage for the map within a map_region
+
+  delete r0;
+  delete r1;
+}
+
+/* Test that region_model::purge_svalues works as expected.  */
+
+static void
+test_purge_svalues ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+  tree x = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("x"),
+		       integer_type_node);
+  tree y = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("y"),
+		       integer_type_node);
+
+  region_model model;
+  model.set_to_new_unknown_value (model.get_lvalue (x), TREE_TYPE (x));
+  model.set_to_new_unknown_value (model.get_lvalue (x), TREE_TYPE (x));
+  model.set_to_new_unknown_value (model.get_lvalue (x), TREE_TYPE (x));
+  model.add_constraint (x, NE_EXPR, int_42);
+
+  model.set_value (model.get_lvalue (x), model.get_rvalue (int_42));
+  model.add_constraint (y, GT_EXPR, int_0);
+
+  /* The redundant unknown values should have been purged.  */
+  purge_stats purged;
+  model.purge_svalues (&purged);
+  ASSERT_EQ (purged.m_num_svalues, 3);
+
+  /* and the redundant constraint on an old, unknown value for x should
+     have been purged.  */
+  ASSERT_EQ (purged.m_num_equiv_classes, 1);
+  ASSERT_EQ (purged.m_num_constraints, 1);
+  ASSERT_EQ (model.get_constraints ()->m_constraints.length (), 2);
+
+  /* ...but we should still have x == 42.  */
+  ASSERT_EQ (model.eval_condition (x, EQ_EXPR, int_42), tristate::TS_TRUE);
+
+  /* ...and we should still have the constraint on y.  */
+  ASSERT_EQ (model.eval_condition (y, GT_EXPR, int_0), tristate::TS_TRUE);
+}
+
+#if 0
+
+/* FIXME.  */
+
+static void
+test_assignment ()
+{
+  tree int_0 (build_int_cst (integer_type_node, 0), 0);
+
+  tree x (build_decl (UNKNOWN_LOCATION, VAR_DECL,
+			  get_identifier ("x"),
+			  integer_type_node);
+  tree y (build_decl (UNKNOWN_LOCATION, VAR_DECL,
+			  get_identifier ("y"),
+			  integer_type_node);
+  /* "x == 0", then use of y, then "y = 0".  */
+  region_model model;
+  ADD_SAT_CONSTRAINT (model, x, EQ_EXPR, int_0);
+  ASSERT_CONDITION_UNKNOWN (model, y, EQ_EXPR, int_0);
+  model.on_assignment (y, int_0);
+  ASSERT_CONDITION_TRUE (model, y, EQ_EXPR, int_0);
+  ASSERT_CONDITION_TRUE (model, y, EQ_EXPR, x);
+}
+
+#endif
+
+/* FIXME.  */
+
+static void
+test_stack_frames ()
+{
+  tree int_42 = build_int_cst (integer_type_node, 42);
+  tree int_10 = build_int_cst (integer_type_node, 10);
+  tree int_5 = build_int_cst (integer_type_node, 5);
+  tree int_0 = build_int_cst (integer_type_node, 0);
+
+  auto_vec <tree> param_types;
+  tree parent_fndecl = make_fndecl (integer_type_node,
+				    "parent_fn",
+				    param_types);
+  allocate_struct_function (parent_fndecl, true);
+
+  tree child_fndecl = make_fndecl (integer_type_node,
+				   "child_fn",
+				   param_types);
+  allocate_struct_function (child_fndecl, true);
+
+  /* "a" and "b" in the parent frame.  */
+  tree a = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		       get_identifier ("a"),
+		       integer_type_node);
+  tree b = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		       get_identifier ("b"),
+		       integer_type_node);
+  /* "x" and "y" in a child frame.  */
+  tree x = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		       get_identifier ("x"),
+		       integer_type_node);
+  tree y = build_decl (UNKNOWN_LOCATION, PARM_DECL,
+		       get_identifier ("y"),
+		       integer_type_node);
+
+  /* "p" global.  */
+  tree p = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("p"),
+		       ptr_type_node);
+
+  /* "q" global.  */
+  tree q = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		       get_identifier ("q"),
+		       ptr_type_node);
+
+  region_model model;
+
+  /* Push stack frame for "parent_fn".  */
+  region_id parent_frame_rid
+    = model.push_frame (DECL_STRUCT_FUNCTION (parent_fndecl), NULL);
+  ASSERT_EQ (model.get_current_frame_id (), parent_frame_rid);
+  region_id a_in_parent_rid = model.get_lvalue (a);
+  model.set_value (a_in_parent_rid, model.get_rvalue (int_42));
+  model.set_to_new_unknown_value (model.get_lvalue (b), integer_type_node);
+  model.add_constraint (b, LT_EXPR, int_10);
+  ASSERT_EQ (model.eval_condition (b, LT_EXPR, int_10),
+	     tristate (tristate::TS_TRUE));
+
+  /* Push stack frame for "child_fn".  */
+  region_id child_frame_rid
+    = model.push_frame (DECL_STRUCT_FUNCTION (child_fndecl), NULL);
+  ASSERT_EQ (model.get_current_frame_id (), child_frame_rid);
+  region_id x_in_child_rid = model.get_lvalue (x);
+  model.set_value (x_in_child_rid, model.get_rvalue (int_0));
+  model.set_to_new_unknown_value (model.get_lvalue (y), integer_type_node);
+  model.add_constraint (y, NE_EXPR, int_5);
+  ASSERT_EQ (model.eval_condition (y, NE_EXPR, int_5),
+	     tristate (tristate::TS_TRUE));
+
+  /* Point a global pointer at a local in the child frame:  p = &x.  */
+  region_id p_in_globals_rid = model.get_lvalue (p);
+  model.set_value (p_in_globals_rid,
+		   model.get_svalue_for_region (x_in_child_rid));
+
+  /* Point another global pointer at p: q = &p.  */
+  region_id q_in_globals_rid = model.get_lvalue (q);
+  model.set_value (q_in_globals_rid,
+		   model.get_svalue_for_region (p_in_globals_rid));
+
+  /* Test get_descendents.  */
+  region_id_set descendents (&model);
+  model.get_descendents (child_frame_rid, &descendents);
+  ASSERT_TRUE (descendents.region_p (child_frame_rid));
+  ASSERT_TRUE (descendents.region_p (x_in_child_rid));
+  ASSERT_FALSE (descendents.region_p (a_in_parent_rid));
+  ASSERT_EQ (descendents.num_regions (), 3);
+
+  /* Pop the "child_fn" frame from the stack.  */
+  purge_stats purged;
+  model.pop_frame (&purged);
+
+  /* We should have purged the unknown values for x and y. */
+  ASSERT_EQ (purged.m_num_svalues, 2);
+
+  /* We should have purged the frame region and the regions for x and y. */
+  ASSERT_EQ (purged.m_num_regions, 3);
+
+  /* We should have purged the constraint on y.  */
+  ASSERT_EQ (purged.m_num_equiv_classes, 1);
+  ASSERT_EQ (purged.m_num_constraints, 1);
+
+  /* Verify that p has been poisoned.  */
+  svalue *new_p_sval = model.get_svalue (model.get_rvalue (p));
+  ASSERT_EQ (new_p_sval->get_kind (), SK_POISONED);
+  ASSERT_EQ (new_p_sval->dyn_cast_poisoned_svalue ()->get_poison_kind (),
+	     POISON_KIND_POPPED_STACK);
+
+  /* Verify that q still points to p, in spite of the region
+     renumbering.  */
+  svalue *new_q_sval = model.get_svalue (model.get_rvalue (q));
+  ASSERT_EQ (new_q_sval->get_kind (), SK_REGION);
+  ASSERT_EQ (new_q_sval->dyn_cast_region_svalue ()->get_pointee (),
+	     model.get_lvalue (p));
+
+  /* Verify that top of stack has been updated.  */
+  ASSERT_EQ (model.get_current_frame_id (), parent_frame_rid);
+
+  /* Verify locals in parent frame.  */
+  /* Verify "a" still has its value.  */
+  svalue *new_a_sval = model.get_svalue (model.get_rvalue (a));
+  ASSERT_EQ (new_a_sval->get_kind (), SK_CONSTANT);
+  ASSERT_EQ (new_a_sval->dyn_cast_constant_svalue ()->get_constant (),
+	     int_42);
+  /* Verify "b" still has its constraint.  */
+  ASSERT_EQ (model.eval_condition (b, LT_EXPR, int_10),
+	     tristate (tristate::TS_TRUE));
+}
+
+#if 0
+/* A subclass of purge_criteria for selftests: purge all path_var instances.  */
+
+class purge_all_path_vars : public purge_criteria
+{
+public:
+  bool should_purge_p (path_var) const FINAL OVERRIDE
+  {
+    return true;
+  }
+};
+
+/* Check that path_state objects can "forget".  */
+
+static void
+test_purging ()
+{
+  path_var int_42 (build_int_cst (integer_type_node, 42), 0);
+  path_var int_0 (build_int_cst (integer_type_node, 0), 0);
+
+  path_var x (build_decl (UNKNOWN_LOCATION, VAR_DECL,
+			  get_identifier ("x"),
+			  integer_type_node);
+  path_var y (build_decl (UNKNOWN_LOCATION, VAR_DECL,
+			  get_identifier ("y"),
+			  integer_type_node);
+
+  {
+    region_model model0;
+    region_model model1;
+
+    ADD_SAT_CONSTRAINT (model1, x, EQ_EXPR, y);
+    ASSERT_NE (model0, model1);
+
+    purge_one_path_var px (x);
+    model1.purge (px);
+
+    purge_one_path_var py (y);
+    model1.purge (py);
+
+    ASSERT_EQ (model0, model1);
+  }
+
+  {
+    region_model model0;
+    region_model model1;
+
+    ADD_SAT_CONSTRAINT (model1, x, EQ_EXPR, int_42);
+    ASSERT_NE (model0, model1);
+
+    purge_one_path_var px (x);
+    model1.purge (px);
+
+    ASSERT_EQ (model0, model1);
+  }
+
+  {
+    region_model model0;
+    region_model model1;
+
+    ADD_SAT_CONSTRAINT (model1, x, GE_EXPR, int_0);
+    ADD_SAT_CONSTRAINT (model1, x, LE_EXPR, int_42);
+    ASSERT_NE (model0, model1);
+
+    purge_one_path_var px (x);
+    model1.purge (px);
+
+    ASSERT_EQ (model0, model1);
+  }
+
+  {
+    region_model model0;
+    region_model model1;
+
+    ADD_SAT_CONSTRAINT (model1, x, NE_EXPR, int_42);
+    ADD_SAT_CONSTRAINT (model1, y, NE_EXPR, int_0);
+    ASSERT_NE (model0, model1);
+
+    purge_one_path_var px (x);
+    model1.purge (px);
+    ASSERT_NE (model0, model1);
+
+    ASSERT_CONDITION_TRUE (model1, y, NE_EXPR, int_0);
+  }
+
+  {
+    region_model model0;
+    region_model model1;
+
+    ADD_SAT_CONSTRAINT (model1, x, NE_EXPR, int_42);
+    ADD_SAT_CONSTRAINT (model1, y, NE_EXPR, int_0);
+    ASSERT_NE (model0, model1);
+
+    purge_all_path_vars p;
+    model1.purge (p);
+    ASSERT_EQ (model0, model1);
+  }
+
+}
+#endif
+
+
+/* Run all of the selftests within this file.  */
+
+void
+analyzer_region_model_cc_tests ()
+{
+  test_dump ();
+  test_unique_constants ();
+  test_svalue_equality ();
+  test_region_equality ();
+  test_purge_svalues ();
+#if 0
+  test_assignment ();
+#endif
+  test_stack_frames ();
+#if 0
+  test_purging ();
+#endif
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
new file mode 100644
index 0000000..8957532
--- /dev/null
+++ b/gcc/analyzer/region-model.h
@@ -0,0 +1,820 @@
+/* FIXME.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_ANALYZER_REGION_MODEL_H
+#define GCC_ANALYZER_REGION_MODEL_H
+
+/* Implemention of the region-based ternary model described in:
+     "A Memory Model for Static Analysis of C Programs"
+      (Zhongxing Xu, Ted Kremenek, and Jian Zhang)
+     http://lcs.ios.ac.cn/~xuzb/canalyze/memmodel.pdf  */
+
+class superedge;
+
+class svalue;
+  class region_svalue;
+  class constant_svalue;
+  class poisoned_svalue;
+  class unknown_svalue;
+class region;
+class region_model;
+
+class constraint_manager;
+
+/* A region_model is effectively a graph of regions and symbolic values.
+   We store per-model IDs rather than pointers to make it easier to clone
+   and to compare graphs.  */
+
+/* An ID for an svalue within a region_model.  Internally this is an index
+   into a vector.  */
+
+class svalue_id
+{
+public:
+  static svalue_id null () { return svalue_id (-1); }
+
+  svalue_id () : m_idx (-1) {}
+
+  bool operator== (const svalue_id &other) const
+  {
+    return m_idx == other.m_idx;
+  }
+
+  bool operator!= (const svalue_id &other) const
+  {
+    return m_idx != other.m_idx;
+  }
+
+  bool null_p () const { return m_idx == -1; }
+
+  static svalue_id from_int (int idx) { return svalue_id (idx); }
+  int as_int () const { return m_idx; }
+
+  void print (pretty_printer *pp) const;
+  void dump_node_name_to_pp (pretty_printer *pp) const;
+  std::string to_str () const;
+
+private:
+  svalue_id (int idx) : m_idx (idx) {}
+
+  int m_idx;
+};
+
+/* An ID for a region within a region_model.  Internally this is an index
+   into a vector.  */
+
+class region_id
+{
+public:
+  static region_id null () { return region_id (-1); }
+
+  region_id () : m_idx (-1) {}
+
+  bool operator== (const region_id &other) const
+  {
+    return m_idx == other.m_idx;
+  }
+
+  bool operator!= (const region_id &other) const
+  {
+    return m_idx != other.m_idx;
+  }
+
+  bool null_p () const { return m_idx == -1; }
+
+  static region_id from_int (int idx) { return region_id (idx); }
+  int as_int () const { return m_idx; }
+
+  void print (pretty_printer *pp) const;
+  void dump_node_name_to_pp (pretty_printer *pp) const;
+
+private:
+  region_id (int idx) : m_idx (idx) {}
+
+  int m_idx;
+};
+
+/* A mapping of IDs to IDs (e.g. when removing one or more elements,
+   thus needing to renumber).  */
+
+template <typename T>
+class id_map
+{
+ public:
+  id_map (int num_ids);
+  void put (T src, T dst);
+  T get_dst_for_src (T src) const;
+  T get_src_for_dst (T dst) const;
+  void dump_to_pp (pretty_printer *pp) const;
+  void dump () const;
+  void update (T&) const;
+
+ private:
+  auto_vec<T> m_src_to_dst;
+  auto_vec<T> m_dst_to_src;
+};
+
+typedef id_map<svalue_id> svalue_id_map;
+typedef id_map<region_id> region_id_map;
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+template <typename T>
+class id_set
+{
+public:
+  id_set (const region_model *model);
+
+  void add_region (T id)
+  {
+    if (!id.null_p ())
+      bitmap_set_bit (m_bitmap, id.as_int ());
+  }
+
+  bool region_p (T id) const
+  {
+    gcc_assert (!id.null_p ());
+    return bitmap_bit_p (const_cast <auto_sbitmap &> (m_bitmap),
+			 id.as_int ());
+  }
+
+  unsigned int num_regions ()
+  {
+    return bitmap_count_bits (m_bitmap);
+  }
+
+private:
+  auto_sbitmap m_bitmap;
+};
+
+typedef id_set<region_id> region_id_set;
+
+////////////////////////////////////////////////////////////////////////////
+
+/* Stats on how much was purged, for various operations that delete
+   information.  */
+
+struct purge_stats
+{
+  purge_stats ()
+  : m_num_svalues (0),
+    m_num_regions (0),
+    m_num_equiv_classes (0),
+    m_num_constraints (0)
+  {}
+
+  int m_num_svalues;
+  int m_num_regions;
+  int m_num_equiv_classes;
+  int m_num_constraints;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+enum svalue_kind
+{
+  SK_REGION,
+  SK_CONSTANT,
+  SK_POISONED,
+  SK_UNKNOWN
+};
+
+/* FIXME.  */
+
+class svalue
+{
+public:
+  virtual ~svalue () {}
+
+  bool operator== (const svalue &other) const;
+  bool operator!= (const svalue &other) const { return !(*this == other); }
+
+  virtual svalue *clone () const = 0;
+
+  tree get_type () const { return m_type; }
+
+  virtual enum svalue_kind get_kind () const = 0;
+
+  virtual hashval_t hash () const = 0;
+
+  void print (const region_model &model,
+	      svalue_id this_sid,
+	      pretty_printer *pp) const;
+
+  virtual void dump_dot_to_pp (const region_model &model,
+			       svalue_id this_sid,
+			       pretty_printer *pp) const;
+
+  std::string to_str () const;
+
+  virtual region_svalue *dyn_cast_region_svalue () { return NULL; }
+  virtual constant_svalue *dyn_cast_constant_svalue () { return NULL; }
+  virtual poisoned_svalue *dyn_cast_poisoned_svalue () { return NULL; }
+  virtual unknown_svalue *dyn_cast_unknown_svalue () { return NULL; }
+
+  virtual void remap_region_ids (const region_id_map &map);
+
+ protected:
+  svalue (tree type) : m_type (type) {}
+
+ private:
+  virtual void print_details (const region_model &model,
+			      svalue_id this_sid,
+			      pretty_printer *pp) const = 0;
+
+  // FIXME: hash this:
+  tree m_type;
+};
+
+/* Concrete subclass of svalue representing a pointer value that points to
+   a known region  */
+
+class region_svalue : public svalue
+{
+public:
+  region_svalue (tree type, region_id rid) : svalue (type), m_rid (rid) {}
+
+  bool compare_fields (const region_svalue &other) const;
+
+  svalue *clone () const FINAL OVERRIDE
+  { return new region_svalue (get_type (), m_rid); }
+
+  enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_REGION; }
+
+  hashval_t hash () const FINAL OVERRIDE;
+
+  void dump_dot_to_pp (const region_model &model,
+		       svalue_id this_sid,
+		       pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  region_svalue *dyn_cast_region_svalue () FINAL OVERRIDE { return this; }
+
+  region_id get_pointee () const { return m_rid; }
+
+  void remap_region_ids (const region_id_map &map) FINAL OVERRIDE;
+
+ private:
+  void print_details (const region_model &model,
+		      svalue_id this_sid,
+		      pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  region_id m_rid;
+};
+
+/* Concrete subclass of svalue representing a specific constant value
+   (or a FUNCTION_DECL).  */
+
+class constant_svalue : public svalue
+{
+public:
+  constant_svalue (tree cst_expr)
+  : svalue (TREE_TYPE (cst_expr)), m_cst_expr (cst_expr)
+  {}
+
+  bool compare_fields (const constant_svalue &other) const;
+
+  svalue *clone () const FINAL OVERRIDE
+  { return new constant_svalue (m_cst_expr); }
+
+  enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_CONSTANT; }
+
+  hashval_t hash () const FINAL OVERRIDE;
+
+  constant_svalue *dyn_cast_constant_svalue () FINAL OVERRIDE { return this; }
+
+  tree get_constant () const { return m_cst_expr; }
+
+ private:
+  void print_details (const region_model &model,
+		      svalue_id this_sid,
+		      pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  tree m_cst_expr;
+};
+
+/* Concrete subclass of svalue representing a unique but unknown value.
+   Comparisons of variables that share the same unknown value are known
+   to be equal, even if we don't know what the value is.  */
+
+class unknown_svalue : public svalue
+{
+public:
+  unknown_svalue (tree type)
+  : svalue (type)
+  {}
+
+  bool compare_fields (const unknown_svalue &other) const;
+
+  svalue *clone () const FINAL OVERRIDE
+  { return new unknown_svalue (get_type ()); }
+
+  enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_UNKNOWN; }
+
+  hashval_t hash () const FINAL OVERRIDE;
+
+  unknown_svalue *dyn_cast_unknown_svalue () FINAL OVERRIDE { return this; }
+
+ private:
+  void print_details (const region_model &model,
+		      svalue_id this_sid,
+		      pretty_printer *pp) const
+    FINAL OVERRIDE;
+};
+
+/* FIXME.  */
+
+enum poison_kind
+{
+  POISON_KIND_UNINIT,
+  POISON_KIND_FREED,
+
+  /* For use on pointers to regions within popped stack frames.  */
+  POISON_KIND_POPPED_STACK
+};
+
+extern const char *poison_kind_to_str (enum poison_kind);
+
+/* Concrete subclass of svalue representing a value that should not
+   be used (e.g. uninitialized memory, freed memory).  */
+
+class poisoned_svalue : public svalue
+{
+public:
+  poisoned_svalue (enum poison_kind kind) : svalue (NULL), m_kind (kind) {}
+
+  bool compare_fields (const poisoned_svalue &other) const;
+
+  svalue *clone () const FINAL OVERRIDE
+  { return new poisoned_svalue (m_kind); }
+
+  enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_POISONED; }
+
+  hashval_t hash () const FINAL OVERRIDE;
+
+  poisoned_svalue *dyn_cast_poisoned_svalue () FINAL OVERRIDE { return this; }
+
+  enum poison_kind get_poison_kind () const { return m_kind; }
+
+ private:
+  void print_details (const region_model &model,
+		      svalue_id this_sid,
+		      pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  enum poison_kind m_kind;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+/* FIXME.  */
+
+enum region_kind
+{
+  RK_MAP,
+  RK_FRAME,
+  RK_STACK,
+  RK_HEAP,
+  RK_ROOT,
+  RK_SYMBOLIC
+};
+
+extern const char *region_kind_to_str (enum region_kind);
+
+/* Abstract base class representing a chunk of memory.
+
+   Regions form a tree-like hierarchy, with a root region at the base,
+   with memory space regions within it, representing the stack and
+   globals, with frames within the stack, and regions for variables
+   within the frames and the "globals" region.  Regions for structs
+   can have subregions for fields.
+
+   A region can optionally have a value, or inherit its value from
+   the first ancestor with a value.  For example, the stack region
+   has a "uninitialized" poison value which is inherited by all
+   descendent regions that don't themselves have a value.  */
+
+class region
+{
+public:
+  virtual ~region () {}
+
+  bool operator== (const region &other) const;
+  bool operator!= (const region &other) const { return !(*this == other); }
+
+  virtual region *clone () const = 0;
+
+  virtual enum region_kind get_kind () const = 0;
+
+  region_id get_parent () const { return m_parent_rid; }
+
+  void set_value (const region_model &model, svalue_id rhs_sid);
+  svalue_id get_value (region_model &model, bool non_null);
+  svalue_id get_value_direct () const { return m_sval_id; }
+
+  tree get_type () const { return m_type; }
+
+  hashval_t hash () const;
+
+  void print (const region_model &model,
+	      region_id this_rid,
+	      pretty_printer *pp) const;
+
+  virtual void dump_dot_to_pp (const region_model &model,
+			       region_id this_rid,
+			       pretty_printer *pp) const;
+
+  void dump_to_pp (const region_model &model,
+		   region_id this_rid,
+		   pretty_printer *pp,
+		   const char *prefix,
+		   bool is_last_child) const;
+  virtual void dump_child_label (const region_model &model,
+				 region_id this_rid,
+				 region_id child_rid,
+				 pretty_printer *pp) const;
+
+  std::string to_str () const;
+
+  void remap_svalue_ids (const svalue_id_map &map);
+  virtual void remap_region_ids (const region_id_map &map);
+
+ protected:
+  region (region_id parent_rid, svalue_id sval_id, tree type)
+  : m_parent_rid (parent_rid), m_sval_id (sval_id), m_type (type) {}
+
+  region (const region &other)
+  : m_parent_rid (other.m_parent_rid), m_sval_id (other.m_sval_id),
+    m_type (other.m_type)
+  {
+  }
+
+  virtual void add_to_hash (inchash::hash &hstate) const;
+  virtual void print_fields (const region_model &model,
+			     region_id this_rid,
+			     pretty_printer *pp) const;
+
+ private:
+  region_id m_parent_rid;
+  svalue_id m_sval_id;
+  tree m_type;
+};
+
+/* A region that has children identified by tree keys.
+   For example a stack frame has subregions per local, and a region
+   for a struct has subregions per field.  */
+
+class map_region : public region
+{
+public:
+  map_region (region_id parent_rid, tree type)
+  : region (parent_rid, svalue_id::null (), type)
+  {}
+  map_region (const map_region &other);
+
+  bool compare_fields (const map_region &other) const;
+
+  region *clone () const OVERRIDE;
+
+  enum region_kind get_kind () const OVERRIDE { return RK_MAP; }
+
+  void dump_dot_to_pp (const region_model &model,
+		       region_id this_rid,
+		       pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  void dump_child_label (const region_model &model,
+			 region_id this_rid,
+			 region_id child_rid,
+			 pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  region_id get_or_create (region_model *model,
+			   region_id this_rid,
+			   tree expr, tree type);
+  void unbind (tree expr);
+
+  void remap_region_ids (const region_id_map &map) FINAL OVERRIDE;
+
+ protected:
+  void add_to_hash (inchash::hash &hstate) const OVERRIDE;
+  void print_fields (const region_model &model,
+		     region_id this_rid,
+		     pretty_printer *pp) const
+    OVERRIDE;
+
+ private:
+ public: // FIXME
+  // FIXME: mapping from tree to child region
+  typedef hash_map<tree, region_id> map_t;
+  map_t m_map;
+};
+
+/* A region representing a function frame on the stack, to contain
+   the locals.  */
+
+class frame_region : public map_region
+{
+public:
+  frame_region (region_id parent_rid, function *fun)
+  : map_region (parent_rid, NULL_TREE), m_fun (fun)
+  {}
+
+  frame_region (const frame_region &other)
+  : map_region (other), m_fun (other.m_fun)
+  {
+  }
+
+  bool compare_fields (const frame_region &other) const;
+
+  region *clone () const FINAL OVERRIDE;
+
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_FRAME; }
+
+  function *get_function () const { return m_fun; }
+
+ private:
+  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
+  void print_fields (const region_model &model,
+		     region_id this_rid,
+		     pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  function *m_fun;
+};
+
+/* A region containing all stack frames, and implicitly providing
+   a POISON_KIND_UNINIT value to all child regions by default.  */
+
+class stack_region : public region
+{
+public:
+  stack_region (region_id parent_rid, svalue_id sval_id)
+  : region (parent_rid, sval_id, NULL_TREE)
+  {}
+
+  stack_region (const stack_region &other);
+
+  bool compare_fields (const stack_region &other) const;
+
+  region *clone () const FINAL OVERRIDE;
+
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_STACK; }
+
+  void dump_child_label (const region_model &model,
+			 region_id this_rid,
+			 region_id child_rid,
+			 pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  void push_frame (region_id frame_rid);
+  region_id get_current_frame_id () const;
+  svalue_id pop_frame (region_model *model, purge_stats *stats);
+
+  void remap_region_ids (const region_id_map &map) FINAL OVERRIDE;
+
+ private:
+  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
+  void print_fields (const region_model &model,
+		     region_id this_rid,
+		     pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  auto_vec<region_id> m_frame_rids;
+};
+
+/* FIXME.  */
+
+class heap_region : public region
+{
+public:
+  heap_region (region_id parent_rid, svalue_id sval_id)
+  : region (parent_rid, sval_id, NULL_TREE)
+  {}
+  heap_region (const heap_region &other);
+
+  bool compare_fields (const heap_region &other) const;
+
+  region *clone () const OVERRIDE;
+
+  enum region_kind get_kind () const OVERRIDE { return RK_HEAP; }
+};
+
+/* The root region, containing all regions (either directly, or as
+   descendents).
+   Unique within a region_model.  */
+
+class root_region : public region
+{
+public:
+  root_region ();
+  root_region (const root_region &other);
+
+  bool compare_fields (const root_region &other) const;
+
+  region *clone () const FINAL OVERRIDE;
+
+  enum region_kind get_kind () const FINAL OVERRIDE { return RK_ROOT; }
+
+  void dump_child_label (const region_model &model,
+			 region_id this_rid,
+			 region_id child_rid,
+			 pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  region_id push_frame (region_model *model, function *fun,
+			vec<svalue_id> *arg_sids);
+  region_id get_current_frame_id (const region_model &model) const;
+  svalue_id pop_frame (region_model *model, purge_stats *stats);
+
+  region_id ensure_stack_region (region_model *model);
+  region_id ensure_globals_region (region_model *model);
+  region_id ensure_heap_region (region_model *model);
+
+  void remap_region_ids (const region_id_map &map) FINAL OVERRIDE;
+
+private:
+  void add_to_hash (inchash::hash &hstate) const FINAL OVERRIDE;
+  void print_fields (const region_model &model,
+		     region_id this_rid,
+		     pretty_printer *pp) const
+    FINAL OVERRIDE;
+
+  region_id m_stack_rid;
+  region_id m_globals_rid;
+  region_id m_heap_rid;
+};
+
+/* FIXME.  */
+
+class symbolic_region : public map_region
+{
+public:
+  symbolic_region (region_id parent_rid)
+  : map_region (parent_rid, NULL_TREE)
+  {}
+  symbolic_region (const symbolic_region &other);
+
+  bool compare_fields (const symbolic_region &other) const;
+
+  region *clone () const OVERRIDE;
+
+  enum region_kind get_kind () const OVERRIDE { return RK_SYMBOLIC; }
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+/* A region_model encapsulates a representation of the state of memory, with
+   a tree of regions, along with their associated values.
+   The representation is graph-like because values can be pointers to
+   regions.  */
+
+class region_model
+{
+ public:
+  region_model ();
+  region_model (const region_model &other);
+
+  bool operator== (const region_model &other) const;
+  bool operator!= (const region_model &other) const
+  {
+    return !(*this == other);
+  }
+
+  hashval_t hash () const;
+
+  void print (pretty_printer *pp) const;
+  std::string to_str () const;
+
+  void print_svalue (svalue_id sid, pretty_printer *pp) const;
+
+  void dump_dot_to_pp (pretty_printer *pp) const;
+  void dump_dot_to_file (FILE *fp) const;
+  void dump_dot (const char *path) const;
+
+  void dump_to_pp (pretty_printer *pp) const;
+  void dump (FILE *fp) const;
+  void dump () const;
+
+  void validate () const;
+
+  void on_assignment (const gassign *stmt);
+  void on_call (const gcall *stmt);
+  void on_return (const greturn *stmt);
+  void handle_phi (tree lhs, tree rhs, bool is_back_edge);
+
+  bool apply_constraints_for_edge (const superedge &edge,
+				   const gimple *last_stmt);
+
+  root_region *get_root_region () const;
+
+  region_id push_frame (function *fun, vec<svalue_id> *arg_sids);
+  region_id get_current_frame_id () const;
+  function * get_current_function () const;
+  svalue_id pop_frame (purge_stats *stats);
+
+  svalue_id add_svalue (svalue *sval);
+  void replace_svalue (svalue_id sid, svalue *new_sval);
+
+  region_id add_region (region *r);
+
+  svalue *get_svalue (svalue_id sval_id) const;
+  region *get_region (region_id rid) const;
+
+  template <typename Subclass>
+  Subclass *get_region (region_id rid) const
+  {
+    return (Subclass *)get_region (rid); // TODO: checking
+  }
+
+#if 0
+  void assign_to_lvalue (lvalue *lval,
+			 rvalue *rval);
+#endif
+  region_id get_lvalue (tree expr);
+  svalue_id get_rvalue (tree expr);
+
+  svalue_id get_svalue_for_region (region_id id);
+  svalue_id get_svalue_for_constant (tree cst_expr);
+  svalue_id get_svalue_for_fndecl (tree fndecl);
+
+  region_id deref_rvalue (tree ptr);
+
+  void set_value (region_id lhs_rid, svalue_id rhs_sid);
+  svalue_id set_to_new_unknown_value (region_id dst_rid, tree type);
+
+  tristate eval_condition (svalue_id lhs,
+			   enum tree_code op,
+			   svalue_id rhs) const;
+  tristate eval_condition (tree lhs,
+			   enum tree_code op,
+			   tree rhs);
+  bool add_constraint (tree lhs, enum tree_code op, tree rhs);
+
+  tree maybe_get_constant (svalue_id sid);
+
+  void purge_svalues (purge_stats *out,
+		      svalue_id *known_used_sid = NULL);
+  void remap_svalue_ids (const svalue_id_map &map);
+  void remap_region_ids (const region_id_map &map);
+
+  void purge_regions (const region_id_set &set, purge_stats *stats);
+
+  int get_num_svalues () const { return m_svalues.length (); }
+
+  /* For selftests.  */
+  constraint_manager *get_constraints ()
+  {
+    return m_constraints;
+  }
+
+  void get_descendents (region_id rid, region_id_set *out) const;
+
+ private:
+  void update_for_call_superedge (const call_superedge &call_edge);
+  void update_for_return_superedge (const return_superedge &return_edge);
+  bool apply_constraints_for_gcond (const superedge &edge,
+				    const gcond *cond_stmt);
+  bool apply_constraints_for_gswitch (const superedge &edge,
+				      const gswitch *switch_stmt);
+
+ public: // FIXME:
+  auto_delete_vec<svalue> m_svalues;
+  auto_delete_vec<region> m_regions;
+  region_id m_root_rid;
+ private:
+  constraint_manager *m_constraints; // FIXME: embed, rather than dynalloc?
+};
+
+namespace inchash
+{
+  extern void add (svalue_id sid, hash &hstate);
+  extern void add (region_id rid, hash &hstate);
+} // namespace inchash
+
+extern void debug (const region_model &rmodel);
+
+#endif /* GCC_ANALYZER_REGION_MODEL_H */
diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc
index 31609e2..bf20f74 100644
--- a/gcc/analyzer/supergraph.cc
+++ b/gcc/analyzer/supergraph.cc
@@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  If not see
 #include <string>
 #include "supergraph.h"
 #include "analyzer-logging.h"
+#include "tree-diagnostic.h"
 
 /* Get the cgraph_edge, but only if there's an underlying function body.  */
 
@@ -88,6 +89,11 @@ supergraph::supergraph (logger *logger)
     {
       function *fun = node->get_fun ();
 
+      /* Ensure that EDGE_DFS_BACK is correct for every CFG edge in
+	 the supergraph (by doing it per-function).  */
+      auto_cfun sentinel (fun);
+      mark_dfs_back_edges ();
+
       const int start_idx = m_nodes.length ();
 
       basic_block bb;
@@ -105,6 +111,7 @@ supergraph::supergraph (logger *logger)
 	    {
 	      gimple *stmt = gsi_stmt (gsi);
 	      node_for_stmts->m_stmts.safe_push (stmt);
+	      m_stmt_to_node_t.put (stmt, node_for_stmts);
 	      if (cgraph_edge *edge = supergraph_call_edge (fun, stmt))
 		{
 		  m_cgraph_edge_to_caller_prev_node.put(edge, node_for_stmts);
@@ -143,7 +150,9 @@ supergraph::supergraph (logger *logger)
 	      basic_block dest_cfg_block = cfg_edge->dest;
 	      supernode *dest_supernode
 		= *m_bb_to_initial_node.get (dest_cfg_block);
-	      add_cfg_edge (src_supernode, dest_supernode, cfg_edge, idx);
+	      cfg_superedge *cfg_sedge
+		= add_cfg_edge (src_supernode, dest_supernode, cfg_edge, idx);
+	      m_cfg_edge_to_cfg_superedge.put (cfg_edge, cfg_sedge);
 	    }
       }
 
@@ -316,10 +325,16 @@ void
 supergraph::dump_dot_to_file (FILE *fp, enum supergraph_dot_flags flags,
 			      const dot_annotator *a) const
 {
-  pretty_printer pp;
-  pp.buffer->stream = fp;
-  dump_dot_to_pp (&pp, flags, a);
-  pp_flush (&pp);
+  pretty_printer *pp = global_dc->printer->clone ();
+  pp_show_color (pp) = 0;
+  /* %qE in logs for SSA_NAMEs should show the ssa names, rather than
+     trying to prettify things by showing the underlying var.  */
+  pp_format_decoder (pp) = default_tree_printer;
+
+  pp->buffer->stream = fp;
+  dump_dot_to_pp (pp, flags, a);
+  pp_flush (pp);
+  delete pp;
 }
 
 void
@@ -341,16 +356,19 @@ supergraph::add_node (function *fun, basic_block bb, gcall *returning_call,
   return n;
 }
 
-superedge *
+cfg_superedge *
 supergraph::add_cfg_edge (supernode *src, supernode *dest, ::edge e, int idx)
 {
   /* Special-case switch edges.  */
   // FIXME: see ana::at_stmt::add_unreached_successors_to_worklist
   gimple *stmt = src->get_last_stmt ();
+  cfg_superedge *new_edge;
   if (stmt && stmt->code == GIMPLE_SWITCH)
-    return add_edge (new switch_cfg_superedge (src, dest, e, idx));
+    new_edge = new switch_cfg_superedge (src, dest, e, idx);
   else
-    return add_edge (new cfg_superedge (src, dest, e));
+    new_edge = new cfg_superedge (src, dest, e);
+  add_edge (new_edge);
+  return new_edge;
 }
 
 call_superedge *
@@ -375,6 +393,7 @@ superedge *
 supergraph::add_edge (superedge *e)
 {
   m_edges.safe_push (e);
+  e->m_dest->m_preds.safe_push (e);
   e->m_src->m_succs.safe_push (e);
   return e;
 }
@@ -440,6 +459,8 @@ supernode::dump_dot (graphviz_out *gv,
     {
       gimple *stmt = gsi_stmt (gpi);
       pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0);
+      if (a)
+	a->add_stmt_annotations (pp, stmt);
       pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
       pp_newline (pp);
     }
@@ -561,6 +582,17 @@ supernode::get_end_location () const
   return UNKNOWN_LOCATION;
 }
 
+unsigned int
+supernode::get_stmt_index (const gimple *stmt) const
+{
+  unsigned i;
+  gimple *iter_stmt;
+  FOR_EACH_VEC_ELT (m_stmts, i, iter_stmt)
+    if (iter_stmt == stmt)
+      return i;
+  gcc_unreachable ();
+}
+
 void
 superedge::dump_dot (graphviz_out *gv,
 		     enum supergraph_dot_flags flags ATTRIBUTE_UNUSED) const
@@ -713,6 +745,28 @@ cfg_superedge::dump_label_to_pp (pretty_printer *pp) const
     pp_printf (pp, "true");
   else if (false_value_p ())
     pp_printf (pp, "false");
+
+  /* Express edge flags as a string with " | " separator.
+     e.g. (flags "FALLTHRU | DFS_BACK").  */
+  if (get_flags ())
+    {
+      pp_string (pp, " (flags ");
+      bool seen_flag = false;
+#define DEF_EDGE_FLAG(NAME,IDX)			\
+  do {						\
+    if (get_flags () & EDGE_##NAME)			\
+      {						\
+	if (seen_flag)				\
+	  pp_string (pp, " | ");			\
+	pp_printf (pp, "%s", (#NAME));		\
+	seen_flag = true;			\
+      }						\
+  } while (0);
+#include "cfg-flags.def"
+#undef DEF_EDGE_FLAG
+      pp_string (pp, ")");
+    }
+
   /* Otherwise, no label.  */
 }
 
@@ -796,10 +850,26 @@ callgraph_superedge::dump_label_to_pp (pretty_printer *pp) const
 
 /* FIXME.  */
 
+function *
+callgraph_superedge::get_callee_function () const
+{
+  return m_cedge->callee->get_fun ();
+}
+
+/* FIXME.  */
+
+function *
+callgraph_superedge::get_caller_function () const
+{
+  return m_cedge->caller->get_fun ();
+}
+
+/* FIXME.  */
+
 tree
 callgraph_superedge::get_callee_decl () const
 {
-  return m_cedge->callee->get_fun ()->decl;
+  return get_callee_function ()->decl;
 }
 
 /* FIXME.  */
@@ -807,7 +877,7 @@ callgraph_superedge::get_callee_decl () const
 tree
 callgraph_superedge::get_caller_decl () const
 {
-  return m_cedge->caller->get_fun ()->decl;
+  return get_caller_function ()->decl;
 }
 
 /* FIXME.  */
diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h
index f887998..3b7bf70 100644
--- a/gcc/analyzer/supergraph.h
+++ b/gcc/analyzer/supergraph.h
@@ -114,6 +114,18 @@ public:
 	      (m_cgraph_edge_to_return_superedge).get (edge));
   }
 
+  cfg_superedge *get_edge_for_cfg_edge (edge e) const
+  {
+    return (*const_cast <cfg_edge_to_cfg_superedge_t &>
+	      (m_cfg_edge_to_cfg_superedge).get (e));
+  }
+
+  supernode *get_supernode_for_stmt (const gimple *stmt) const
+  {
+    return (*const_cast <stmt_to_node_t &>(m_stmt_to_node_t).get
+	    (const_cast <gimple *> (stmt)));
+  }
+
   void dump_dot_to_pp (pretty_printer *pp, enum supergraph_dot_flags flags,
 		       const dot_annotator *a = NULL) const;
   void dump_dot_to_file (FILE *fp, enum supergraph_dot_flags flags,
@@ -132,7 +144,7 @@ public:
 private:
   supernode *add_node (function *fun, basic_block bb, gcall *returning_call,
 		       gimple_seq phi_nodes);
-  superedge *add_cfg_edge (supernode *src, supernode *dest, ::edge e, int idx);
+  cfg_superedge *add_cfg_edge (supernode *src, supernode *dest, ::edge e, int idx);
   call_superedge *add_call_superedge (supernode *src, supernode *dest,
 				      cgraph_edge *cedge);
   return_superedge *add_return_superedge (supernode *src, supernode *dest,
@@ -141,8 +153,8 @@ private:
 
   /* Data.  */
 
-  auto_vec<supernode *> m_nodes;
-  auto_vec<superedge *> m_edges;
+  auto_delete_vec<supernode> m_nodes;
+  auto_delete_vec<superedge> m_edges;
 
   typedef hash_map<basic_block, supernode *> bb_to_node_t;
   bb_to_node_t m_bb_to_initial_node;
@@ -152,6 +164,10 @@ private:
   cgraph_edge_to_node_t m_cgraph_edge_to_caller_prev_node;
   cgraph_edge_to_node_t m_cgraph_edge_to_caller_next_node;
 
+  typedef hash_map<::edge, cfg_superedge *>
+    cfg_edge_to_cfg_superedge_t;
+  cfg_edge_to_cfg_superedge_t m_cfg_edge_to_cfg_superedge;
+
   typedef hash_map<cgraph_edge *, call_superedge *>
     cgraph_edge_to_call_superedge_t;
   cgraph_edge_to_call_superedge_t m_cgraph_edge_to_call_superedge;
@@ -159,6 +175,9 @@ private:
   typedef hash_map<cgraph_edge *, return_superedge *>
     cgraph_edge_to_return_superedge_t;
   cgraph_edge_to_return_superedge_t m_cgraph_edge_to_return_superedge;
+
+  typedef hash_map<gimple *, supernode *> stmt_to_node_t;
+  stmt_to_node_t m_stmt_to_node_t;
 };
 
 /* FIXME.  */
@@ -222,13 +241,16 @@ class supernode
     return dyn_cast<gcall *> (stmt);
   }
 
-  function *m_fun; // alternatively could be stored as runs of indices within the supergraph
-  basic_block m_bb;
-  gcall *m_returning_call; // for handling the result of a returned call
+  unsigned int get_stmt_index (const gimple *stmt) const;
+
+  function * const m_fun; // alternatively could be stored as runs of indices within the supergraph
+  const basic_block m_bb;
+  gcall * const m_returning_call; // for handling the result of a returned call
   gimple_seq m_phi_nodes; // ptr to that of the underlying BB, for the first supernode for the BB
   auto_vec<gimple *> m_stmts;
+  auto_vec<superedge *> m_preds;
   auto_vec<superedge *> m_succs;
-  int m_index; /* unique within the supergraph as a whole.  */
+  const int m_index; /* unique within the supergraph as a whole.  */
 };
 
 /* FIXME.  */
@@ -236,6 +258,8 @@ class supernode
 class superedge
 {
  public:
+  virtual ~superedge () {}
+
   void dump_dot (graphviz_out *gv, enum supergraph_dot_flags flags) const;
 
   virtual void dump_label_to_pp (pretty_printer *pp) const = 0;
@@ -262,9 +286,9 @@ class superedge
   {}
 
  public:
-  supernode *m_src;
-  supernode *m_dest;
-  enum edge_kind m_kind;
+  supernode *const m_src;
+  supernode *const m_dest;
+  const enum edge_kind m_kind;
 };
 
 /* A subclass of superedge corresponding to a callgraph edge.  */
@@ -280,6 +304,8 @@ class callgraph_superedge : public superedge
 
   void dump_label_to_pp (pretty_printer *pp) const OVERRIDE;
 
+  function *get_callee_function () const;
+  function *get_caller_function () const;
   tree get_callee_decl () const;
   tree get_caller_decl () const;
   gcall *get_call_stmt () const { return m_cedge->call_stmt; }
@@ -288,7 +314,7 @@ class callgraph_superedge : public superedge
   tree map_expr_from_caller_to_callee (tree caller_expr) const;
   tree map_expr_from_callee_to_caller (tree callee_expr) const;
 
-  cgraph_edge *m_cedge;
+  cgraph_edge *const m_cedge;
 };
 
 class call_superedge : public callgraph_superedge
@@ -367,10 +393,11 @@ class cfg_superedge : public superedge
   int get_flags () const { return m_cfg_edge->flags; }
   int true_value_p () const { return get_flags () & EDGE_TRUE_VALUE; }
   int false_value_p () const { return get_flags () & EDGE_FALSE_VALUE; }
+  int back_edge_p () const { return get_flags () & EDGE_DFS_BACK; }
 
   tree get_phi_arg (const gphi *phi) const;
 
-  ::edge m_cfg_edge;
+  const ::edge m_cfg_edge;
 };
 
 /* A subclass for edges from switch statements, retaining enough
@@ -394,7 +421,7 @@ class switch_cfg_superedge : public cfg_superedge {
   tree get_case_label () const;
 
  private:
-  int m_idx;
+  const int m_idx;
 };
 
 /* Base class for adding additional content to the .dot output
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 1def29d..f1c7687 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -1104,4 +1104,7 @@ DEF_BUILTIN (BUILT_IN_ANALYZER_DUMP, "__analyzer_dump", BUILT_IN_NORMAL, BT_FN_V
 DEF_BUILTIN (BUILT_IN_ANALYZER_EVAL, "__analyzer_eval", BUILT_IN_NORMAL, BT_FN_VOID_INT, BT_LAST,
 	     false, false, false, ATTR_NULL, true, true)
 
+DEF_BUILTIN (BUILT_IN_ANALYZER_BREAK, "__analyzer_break", BUILT_IN_NORMAL, BT_FN_VOID, BT_LAST,
+	     false, false, false, ATTR_NULL, true, true)
+
 #undef DEF_BUILTIN
diff --git a/gcc/common.opt b/gcc/common.opt
index c59063d..19f54d7 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -541,6 +541,54 @@ Waggressive-loop-optimizations
 Common Var(warn_aggressive_loop_optimizations) Init(1) Warning
 Warn if a loop with constant number of iterations triggers undefined behavior.
 
+Wanalyzer-double-fclose
+Common Init(1) Warning
+FIXME
+
+Wanalyzer-double-free
+Common Init(1) Warning
+FIXME
+
+Wanalyzer-exposure-through-output-file
+Common Init(1) Warning
+FIXME
+
+Wanalyzer-file-leak
+Common Init(1) Warning
+FIXME
+
+Wanalyzer-free-of-non-heap
+Common Init(1) Warning
+FIXME
+
+Wanalyzer-malloc-leak
+Common Init(1) Warning
+FIXME
+
+Wanalyzer-possible-null-argument
+Common Init(1) Warning
+FIXME
+
+Wanalyzer-possible-null-dereference
+Common Init(1) Warning
+FIXME
+
+Wanalyzer-null-argument
+Common Init(1) Warning
+FIXME
+
+Wanalyzer-null-dereference
+Common Init(1) Warning
+FIXME
+
+Wanalyzer-tainted-array-index
+Common Init(1) Warning
+FIXME
+
+Wanalyzer-use-after-free
+Common Init(1) Warning
+FIXME
+
 Warray-bounds
 Common Var(warn_array_bounds) Warning
 Warn if an array is accessed out of bounds.
@@ -1000,6 +1048,10 @@ fanalyzer-impl=
 Common Joined RejectNegative UInteger Var(flag_analyzer_impl)
 FIXME
 
+fanalyzer-state-purge
+Common Var(flag_analyzer_state_purge) Init(1)
+FIXME
+
 fargument-alias
 Common Ignore
 Does nothing. Preserved for backward compatibility.
@@ -1378,6 +1430,12 @@ Common RejectNegative Var(flag_dump_analyzer)
 fdump-analyzer-summaries
 Common RejectNegative Var(flag_dump_analyzer_summaries)
 
+fdump-analyzer-supergraph
+Common RejectNegative Var(flag_dump_analyzer_supergraph)
+
+fdump-analyzer-exploded-graph
+Common RejectNegative Var(flag_dump_analyzer_exploded_graph)
+
 fanalyzer-verbosity=
 Common Joined UInteger Var(analyzer_verbosity) Init(1)
 FIXME
@@ -1386,6 +1444,10 @@ fanalyzer-verbose-state-changes
 Common Var(flag_analyzer_verbose_state_changes) Init(0)
 FIXME
 
+fanalyzer-region-model
+Common Var(flag_analyzer_region_model) Init(0)
+FIXME
+
 fdump-final-insns
 Driver RejectNegative
 
diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
index 0770ac7..2e7f120 100644
--- a/gcc/diagnostic-core.h
+++ b/gcc/diagnostic-core.h
@@ -81,8 +81,9 @@ extern bool warning_at (location_t, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
 extern bool warning_at (rich_location *, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
-extern bool warning_at (rich_location *, const diagnostic_metadata &, const char *, ...)
-    ATTRIBUTE_GCC_DIAG(3,4);
+extern bool warning_at (rich_location *, const diagnostic_metadata &, int,
+			const char *, ...)
+    ATTRIBUTE_GCC_DIAG(4,5);
 extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
 extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *,
 		     const char *, ...)
@@ -114,7 +115,7 @@ extern bool emit_diagnostic (diagnostic_t, rich_location *, int,
 			     const char *, ...) ATTRIBUTE_GCC_DIAG(4,5);
 extern bool emit_diagnostic_valist (diagnostic_t, location_t, int, const char *,
 				    va_list *) ATTRIBUTE_GCC_DIAG (4,0);
-extern bool emit_diagnostic_valist (diagnostic_t, rich_location *, 
+extern bool emit_diagnostic_valist (diagnostic_t, rich_location *,
 				    const diagnostic_metadata *metadata,
 				    int, const char *, va_list *)
   ATTRIBUTE_GCC_DIAG (5,0);
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index 4a5f1b44..0468325 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -1438,7 +1438,7 @@ warning_at (rich_location *richloc, int opt, const char *gmsgid, ...)
 
 bool
 warning_at (rich_location *richloc, const diagnostic_metadata &metadata,
-	    const char *gmsgid, ...)
+	    int opt, const char *gmsgid, ...)
 {
   gcc_assert (richloc);
 
@@ -1446,7 +1446,7 @@ warning_at (rich_location *richloc, const diagnostic_metadata &metadata,
   va_list ap;
   va_start (ap, gmsgid);
   bool ret
-    = diagnostic_impl (richloc, &metadata, 0, gmsgid, &ap,
+    = diagnostic_impl (richloc, &metadata, opt, gmsgid, &ap,
 		       DK_WARNING);
   va_end (ap);
   return ret;
diff --git a/gcc/function-tests.c b/gcc/function-tests.c
index f1e29e4..394a5ef 100644
--- a/gcc/function-tests.c
+++ b/gcc/function-tests.c
@@ -82,11 +82,11 @@ namespace selftest {
 
 /* Helper function for selftests of function-creation.  */
 
-static tree
+tree
 make_fndecl (tree return_type,
 	     const char *name,
 	     vec <tree> &param_types,
-	     bool is_variadic = false)
+	     bool is_variadic)
 {
   tree fn_type;
   if (is_variadic)
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 88dc951..8d08843 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -170,6 +170,13 @@ class line_table_test
   ~line_table_test ();
 };
 
+/* FIXME.  */
+
+extern tree make_fndecl (tree return_type,
+			 const char *name,
+			 vec <tree> &param_types,
+			 bool is_variadic = false);
+
 /* Run TESTCASE multiple times, once for each case in our test matrix.  */
 
 extern void
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/input.c
deleted file mode 100644
index 141e55c..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/input.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-   Copyright 2013 David Malcolm <dmalcolm@redhat.com>
-   Copyright 2013 Red Hat, Inc.
-
-   This is free software: you can redistribute it and/or modify it
-   under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see
-   <http://www.gnu.org/licenses/>.
-*/
-
-#include <stdlib.h>
-
-extern void foo(void *ptrA, void *ptrB, void *ptrC)
-  __attribute__((nonnull (1, 3)));
-
-void test(void *p, void *q, void *r)
-{
-  p = NULL;
-
-  foo(p, q, r);
-}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/script.py
deleted file mode 100644
index d6390c5..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/script.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-#   Copyright 2012 David Malcolm <dmalcolm@redhat.com>
-#   Copyright 2012 Red Hat, Inc.
-#
-#   This is free software: you can redistribute it and/or modify it
-#   under the terms of the GNU General Public License as published by
-#   the Free Software Foundation, either version 3 of the License, or
-#   (at your option) any later version.
-#
-#   This program is distributed in the hope that it will be useful, but
-#   WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#   General Public License for more details.
-#
-#   You should have received a copy of the GNU General Public License
-#   along with this program.  If not, see
-#   <http://www.gnu.org/licenses/>.
-
-from sm import main
-from sm.parser import parse_file
-
-checker = parse_file('sm/checkers/malloc_checker.sm')
-main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/stderr.txt
deleted file mode 100644
index cc5466f..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-tests/sm/checkers/malloc-checker/arg_must_not_be_null/input.c: In function 'test':
-tests/sm/checkers/malloc-checker/arg_must_not_be_null/input.c:29:nn: error: NULL pointer p passed as argument 1 to foo [CWE-690]
-tests/sm/checkers/malloc-checker/arg_must_not_be_null/input.c:27:nn: note: p assigned to 0
-tests/sm/checkers/malloc-checker/arg_must_not_be_null/input.c:29:nn: note: NULL pointer p passed as argument 1 to foo
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/arg_must_not_be_null/stdout.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/input.c
deleted file mode 100644
index 56385c3..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/input.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-   Copyright 2012 David Malcolm <dmalcolm@redhat.com>
-   Copyright 2012 Red Hat, Inc.
-
-   This is free software: you can redistribute it and/or modify it
-   under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see
-   <http://www.gnu.org/licenses/>.
-*/
-
-#include <stdlib.h>
-#include <string.h>
-
-void test(void *ptr)
-{
-  free(ptr);
-
-  /* BUG: double-free: */
-  free(ptr);
-}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/input.c.cpychecker-log.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/input.c.cpychecker-log.txt
deleted file mode 100644
index 7499d84..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/input.c.cpychecker-log.txt
+++ /dev/null
@@ -1 +0,0 @@
-gcc.Function('test')
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/script.py
deleted file mode 100644
index d6390c5..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/script.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-#   Copyright 2012 David Malcolm <dmalcolm@redhat.com>
-#   Copyright 2012 Red Hat, Inc.
-#
-#   This is free software: you can redistribute it and/or modify it
-#   under the terms of the GNU General Public License as published by
-#   the Free Software Foundation, either version 3 of the License, or
-#   (at your option) any later version.
-#
-#   This program is distributed in the hope that it will be useful, but
-#   WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#   General Public License for more details.
-#
-#   You should have received a copy of the GNU General Public License
-#   along with this program.  If not, see
-#   <http://www.gnu.org/licenses/>.
-
-from sm import main
-from sm.parser import parse_file
-
-checker = parse_file('sm/checkers/malloc_checker.sm')
-main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/stderr.txt
deleted file mode 100644
index 6bdd7a2..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/stderr.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-tests/sm/checkers/malloc-checker/double_free/input.c: In function 'test':
-tests/sm/checkers/malloc-checker/double_free/input.c:28:nn: error: double-free of ptr [CWE-415]
-tests/sm/checkers/malloc-checker/double_free/input.c:25:nn: note: ptr passed to free()
-tests/sm/checkers/malloc-checker/double_free/input.c:28:nn: note: double-free of ptr
-tests/sm/checkers/malloc-checker/double_free/input.c:28:nn: error: use-after-free of ptr [CWE-416]
-tests/sm/checkers/malloc-checker/double_free/input.c:25:nn: note: ptr passed to free()
-tests/sm/checkers/malloc-checker/double_free/input.c:28:nn: note: use-after-free of ptr
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/double_free/stdout.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/stderr.txt
deleted file mode 100644
index 55b4ce0..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/stderr.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-tests/sm/checkers/points_to_stack/return_of_ptr_to_local/input.c: In function 'test':
-tests/sm/checkers/points_to_stack/return_of_ptr_to_local/input.c:41:nn: error: return of "result" returns address within stack of local variable "buffer" [CWE-562]
-tests/sm/checkers/points_to_stack/return_of_ptr_to_local/input.c:38:nn: note: assignment of result to &buffer
-tests/sm/checkers/points_to_stack/return_of_ptr_to_local/input.c:41:nn: note: state of result ("State('ptr.points_to_stack', 'var_pointed_to'=gcc.VarDecl('buffer'))") propagated to result
-tests/sm/checkers/points_to_stack/return_of_ptr_to_local/input.c:41:nn: note: return of "result" returns address within stack of local variable "buffer"
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/stdout.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/regression/rhs-is-vardecl/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/regression/rhs-is-vardecl/stdout.txt
deleted file mode 100644
index e69de29..0000000
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
new file mode 100644
index 0000000..4c87277
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-CVE-2005-1689-krb5-recvauth.c
@@ -0,0 +1,10749 @@
+
+
+
+
+
+
+typedef unsigned char __u_char;
+typedef unsigned short int __u_short;
+typedef unsigned int __u_int;
+typedef unsigned long int __u_long;
+
+
+typedef signed char __int8_t;
+typedef unsigned char __uint8_t;
+typedef signed short int __int16_t;
+typedef unsigned short int __uint16_t;
+typedef signed int __int32_t;
+typedef unsigned int __uint32_t;
+
+typedef signed long int __int64_t;
+typedef unsigned long int __uint64_t;
+
+
+
+
+
+
+
+typedef long int __quad_t;
+typedef unsigned long int __u_quad_t;
+
+
+typedef unsigned long int __dev_t;
+typedef unsigned int __uid_t;
+typedef unsigned int __gid_t;
+typedef unsigned long int __ino_t;
+typedef unsigned long int __ino64_t;
+typedef unsigned int __mode_t;
+typedef unsigned long int __nlink_t;
+typedef long int __off_t;
+typedef long int __off64_t;
+typedef int __pid_t;
+typedef struct { int __val[2]; } __fsid_t;
+typedef long int __clock_t;
+typedef unsigned long int __rlim_t;
+typedef unsigned long int __rlim64_t;
+typedef unsigned int __id_t;
+typedef long int __time_t;
+typedef unsigned int __useconds_t;
+typedef long int __suseconds_t;
+
+typedef int __daddr_t;
+typedef int __key_t;
+
+
+typedef int __clockid_t;
+
+
+typedef void * __timer_t;
+
+
+typedef long int __blksize_t;
+
+
+
+
+typedef long int __blkcnt_t;
+typedef long int __blkcnt64_t;
+
+
+typedef unsigned long int __fsblkcnt_t;
+typedef unsigned long int __fsblkcnt64_t;
+
+
+typedef unsigned long int __fsfilcnt_t;
+typedef unsigned long int __fsfilcnt64_t;
+
+
+typedef long int __fsword_t;
+
+typedef long int __ssize_t;
+
+
+typedef long int __syscall_slong_t;
+
+typedef unsigned long int __syscall_ulong_t;
+
+
+
+typedef __off64_t __loff_t;
+typedef __quad_t *__qaddr_t;
+typedef char *__caddr_t;
+
+
+typedef long int __intptr_t;
+
+
+typedef unsigned int __socklen_t;
+
+
+
+typedef __u_char u_char;
+typedef __u_short u_short;
+typedef __u_int u_int;
+typedef __u_long u_long;
+typedef __quad_t quad_t;
+typedef __u_quad_t u_quad_t;
+typedef __fsid_t fsid_t;
+
+
+
+
+typedef __loff_t loff_t;
+
+
+
+typedef __ino_t ino_t;
+typedef __dev_t dev_t;
+
+
+
+
+typedef __gid_t gid_t;
+
+
+
+
+typedef __mode_t mode_t;
+
+
+
+
+typedef __nlink_t nlink_t;
+
+
+
+
+typedef __uid_t uid_t;
+
+
+
+
+
+typedef __off_t off_t;
+typedef __pid_t pid_t;
+
+
+
+
+
+typedef __id_t id_t;
+
+
+
+
+typedef __ssize_t ssize_t;
+
+
+
+
+
+typedef __daddr_t daddr_t;
+typedef __caddr_t caddr_t;
+
+
+
+
+
+typedef __key_t key_t;
+
+
+typedef __clock_t clock_t;
+
+
+
+
+
+typedef __time_t time_t;
+
+
+
+typedef __clockid_t clockid_t;
+typedef __timer_t timer_t;
+typedef long unsigned int size_t;
+
+
+
+typedef unsigned long int ulong;
+typedef unsigned short int ushort;
+typedef unsigned int uint;
+typedef int int8_t __attribute__ ((__mode__ (__QI__)));
+typedef int int16_t __attribute__ ((__mode__ (__HI__)));
+typedef int int32_t __attribute__ ((__mode__ (__SI__)));
+typedef int int64_t __attribute__ ((__mode__ (__DI__)));
+
+
+typedef unsigned int u_int8_t __attribute__ ((__mode__ (__QI__)));
+typedef unsigned int u_int16_t __attribute__ ((__mode__ (__HI__)));
+typedef unsigned int u_int32_t __attribute__ ((__mode__ (__SI__)));
+typedef unsigned int u_int64_t __attribute__ ((__mode__ (__DI__)));
+
+typedef int register_t __attribute__ ((__mode__ (__word__)));
+
+
+
+
+
+
+static __inline unsigned int
+__bswap_32 (unsigned int __bsx)
+{
+  return __builtin_bswap32 (__bsx);
+}
+static __inline __uint64_t
+__bswap_64 (__uint64_t __bsx)
+{
+  return __builtin_bswap64 (__bsx);
+}
+
+
+
+
+typedef int __sig_atomic_t;
+
+
+
+
+typedef struct
+  {
+    unsigned long int __val[(1024 / (8 * sizeof (unsigned long int)))];
+  } __sigset_t;
+
+
+
+typedef __sigset_t sigset_t;
+
+
+
+
+
+struct timespec
+  {
+    __time_t tv_sec;
+    __syscall_slong_t tv_nsec;
+  };
+
+struct timeval
+  {
+    __time_t tv_sec;
+    __suseconds_t tv_usec;
+  };
+
+
+typedef __suseconds_t suseconds_t;
+
+
+
+
+
+typedef long int __fd_mask;
+typedef struct
+  {
+
+
+
+
+
+
+    __fd_mask __fds_bits[1024 / (8 * (int) sizeof (__fd_mask))];
+
+
+  } fd_set;
+
+
+
+
+
+
+typedef __fd_mask fd_mask;
+
+extern int select (int __nfds, fd_set *__restrict __readfds,
+     fd_set *__restrict __writefds,
+     fd_set *__restrict __exceptfds,
+     struct timeval *__restrict __timeout);
+extern int pselect (int __nfds, fd_set *__restrict __readfds,
+      fd_set *__restrict __writefds,
+      fd_set *__restrict __exceptfds,
+      const struct timespec *__restrict __timeout,
+      const __sigset_t *__restrict __sigmask);
+
+
+
+
+
+__extension__
+extern unsigned int gnu_dev_major (unsigned long long int __dev)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+__extension__
+extern unsigned int gnu_dev_minor (unsigned long long int __dev)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+__extension__
+extern unsigned long long int gnu_dev_makedev (unsigned int __major,
+            unsigned int __minor)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+
+
+__extension__ extern __inline __attribute__ ((__gnu_inline__)) __attribute__ ((__const__)) unsigned int
+__attribute__ ((__nothrow__ , __leaf__)) gnu_dev_major (unsigned long long int __dev)
+{
+  return ((__dev >> 8) & 0xfff) | ((unsigned int) (__dev >> 32) & ~0xfff);
+}
+
+__extension__ extern __inline __attribute__ ((__gnu_inline__)) __attribute__ ((__const__)) unsigned int
+__attribute__ ((__nothrow__ , __leaf__)) gnu_dev_minor (unsigned long long int __dev)
+{
+  return (__dev & 0xff) | ((unsigned int) (__dev >> 12) & ~0xff);
+}
+
+__extension__ extern __inline __attribute__ ((__gnu_inline__)) __attribute__ ((__const__)) unsigned long long int
+__attribute__ ((__nothrow__ , __leaf__)) gnu_dev_makedev (unsigned int __major, unsigned int __minor)
+{
+  return ((__minor & 0xff) | ((__major & 0xfff) << 8)
+   | (((unsigned long long int) (__minor & ~0xff)) << 12)
+   | (((unsigned long long int) (__major & ~0xfff)) << 32));
+}
+
+
+
+
+
+
+
+typedef __blksize_t blksize_t;
+
+
+
+
+
+
+typedef __blkcnt_t blkcnt_t;
+
+
+
+typedef __fsblkcnt_t fsblkcnt_t;
+
+
+
+typedef __fsfilcnt_t fsfilcnt_t;
+typedef unsigned long int pthread_t;
+
+
+union pthread_attr_t
+{
+  char __size[56];
+  long int __align;
+};
+
+typedef union pthread_attr_t pthread_attr_t;
+
+
+
+
+
+typedef struct __pthread_internal_list
+{
+  struct __pthread_internal_list *__prev;
+  struct __pthread_internal_list *__next;
+} __pthread_list_t;
+typedef union
+{
+  struct __pthread_mutex_s
+  {
+    int __lock;
+    unsigned int __count;
+    int __owner;
+
+    unsigned int __nusers;
+
+
+
+    int __kind;
+
+    short __spins;
+    short __elision;
+    __pthread_list_t __list;
+  } __data;
+  char __size[40];
+  long int __align;
+} pthread_mutex_t;
+
+typedef union
+{
+  char __size[4];
+  int __align;
+} pthread_mutexattr_t;
+
+
+
+
+typedef union
+{
+  struct
+  {
+    int __lock;
+    unsigned int __futex;
+    __extension__ unsigned long long int __total_seq;
+    __extension__ unsigned long long int __wakeup_seq;
+    __extension__ unsigned long long int __woken_seq;
+    void *__mutex;
+    unsigned int __nwaiters;
+    unsigned int __broadcast_seq;
+  } __data;
+  char __size[48];
+  __extension__ long long int __align;
+} pthread_cond_t;
+
+typedef union
+{
+  char __size[4];
+  int __align;
+} pthread_condattr_t;
+
+
+
+typedef unsigned int pthread_key_t;
+
+
+
+typedef int pthread_once_t;
+
+
+
+
+
+typedef union
+{
+
+  struct
+  {
+    int __lock;
+    unsigned int __nr_readers;
+    unsigned int __readers_wakeup;
+    unsigned int __writer_wakeup;
+    unsigned int __nr_readers_queued;
+    unsigned int __nr_writers_queued;
+    int __writer;
+    int __shared;
+    unsigned long int __pad1;
+    unsigned long int __pad2;
+
+
+    unsigned int __flags;
+
+  } __data;
+  char __size[56];
+  long int __align;
+} pthread_rwlock_t;
+
+typedef union
+{
+  char __size[8];
+  long int __align;
+} pthread_rwlockattr_t;
+
+
+
+
+
+typedef volatile int pthread_spinlock_t;
+
+
+
+
+typedef union
+{
+  char __size[32];
+  long int __align;
+} pthread_barrier_t;
+
+typedef union
+{
+  char __size[4];
+  int __align;
+} pthread_barrierattr_t;
+
+
+
+
+
+
+extern void __assert_fail (const char *__assertion, const char *__file,
+      unsigned int __line, const char *__function)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__));
+
+
+extern void __assert_perror_fail (int __errnum, const char *__file,
+      unsigned int __line, const char *__function)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__));
+
+
+
+
+extern void __assert (const char *__assertion, const char *__file, int __line)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__));
+
+
+
+
+
+
+
+typedef struct {
+    const char *filename;
+    short lineno;
+} k5_debug_loc;
+typedef char k5_debug_mutex_stats;
+enum k5_mutex_init_states {
+    K5_MUTEX_DEBUG_PARTLY_INITIALIZED = 0x12,
+    K5_MUTEX_DEBUG_INITIALIZED,
+    K5_MUTEX_DEBUG_DESTROYED
+};
+enum k5_mutex_flag_states {
+    K5_MUTEX_DEBUG_UNLOCKED = 0x23,
+    K5_MUTEX_DEBUG_LOCKED
+};
+
+typedef struct {
+    enum k5_mutex_init_states initialized;
+    enum k5_mutex_flag_states locked;
+} k5_os_nothread_mutex;
+typedef unsigned char k5_os_nothread_once_t;
+
+
+
+
+struct sched_param
+  {
+    int __sched_priority;
+  };
+
+
+
+
+
+
+
+
+
+
+struct __sched_param
+  {
+    int __sched_priority;
+  };
+typedef unsigned long int __cpu_mask;
+
+
+
+
+
+
+typedef struct
+{
+  __cpu_mask __bits[1024 / (8 * sizeof (__cpu_mask))];
+} cpu_set_t;
+
+
+extern int __sched_cpucount (size_t __setsize, const cpu_set_t *__setp)
+  __attribute__ ((__nothrow__ , __leaf__));
+extern cpu_set_t *__sched_cpualloc (size_t __count) __attribute__ ((__nothrow__ , __leaf__)) ;
+extern void __sched_cpufree (cpu_set_t *__set) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+
+
+extern int sched_setparam (__pid_t __pid, const struct sched_param *__param)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int sched_getparam (__pid_t __pid, struct sched_param *__param) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int sched_setscheduler (__pid_t __pid, int __policy,
+          const struct sched_param *__param) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int sched_getscheduler (__pid_t __pid) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int sched_yield (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int sched_get_priority_max (int __algorithm) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int sched_get_priority_min (int __algorithm) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int sched_rr_get_interval (__pid_t __pid, struct timespec *__t) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+struct tm
+{
+  int tm_sec;
+  int tm_min;
+  int tm_hour;
+  int tm_mday;
+  int tm_mon;
+  int tm_year;
+  int tm_wday;
+  int tm_yday;
+  int tm_isdst;
+
+
+  long int tm_gmtoff;
+  const char *tm_zone;
+
+
+
+
+};
+
+
+
+
+
+
+
+
+struct itimerspec
+  {
+    struct timespec it_interval;
+    struct timespec it_value;
+  };
+
+
+struct sigevent;
+
+
+
+extern clock_t clock (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern time_t time (time_t *__timer) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern double difftime (time_t __time1, time_t __time0)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+
+
+extern time_t mktime (struct tm *__tp) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern size_t strftime (char *__restrict __s, size_t __maxsize,
+   const char *__restrict __format,
+   const struct tm *__restrict __tp) __attribute__ ((__nothrow__ , __leaf__));
+
+typedef struct __locale_struct
+{
+
+  struct __locale_data *__locales[13];
+
+
+  const unsigned short int *__ctype_b;
+  const int *__ctype_tolower;
+  const int *__ctype_toupper;
+
+
+  const char *__names[13];
+} *__locale_t;
+
+
+typedef __locale_t locale_t;
+
+extern size_t strftime_l (char *__restrict __s, size_t __maxsize,
+     const char *__restrict __format,
+     const struct tm *__restrict __tp,
+     __locale_t __loc) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern struct tm *gmtime (const time_t *__timer) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern struct tm *localtime (const time_t *__timer) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern struct tm *gmtime_r (const time_t *__restrict __timer,
+       struct tm *__restrict __tp) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern struct tm *localtime_r (const time_t *__restrict __timer,
+          struct tm *__restrict __tp) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern char *asctime (const struct tm *__tp) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern char *ctime (const time_t *__timer) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+extern char *asctime_r (const struct tm *__restrict __tp,
+   char *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern char *ctime_r (const time_t *__restrict __timer,
+        char *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern char *__tzname[2];
+extern int __daylight;
+extern long int __timezone;
+
+
+
+
+extern char *tzname[2];
+
+
+
+extern void tzset (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern int daylight;
+extern long int timezone;
+
+
+
+
+
+extern int stime (const time_t *__when) __attribute__ ((__nothrow__ , __leaf__));
+extern time_t timegm (struct tm *__tp) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern time_t timelocal (struct tm *__tp) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int dysize (int __year) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+extern int nanosleep (const struct timespec *__requested_time,
+        struct timespec *__remaining);
+
+
+
+extern int clock_getres (clockid_t __clock_id, struct timespec *__res) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int clock_gettime (clockid_t __clock_id, struct timespec *__tp) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int clock_settime (clockid_t __clock_id, const struct timespec *__tp)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+extern int clock_nanosleep (clockid_t __clock_id, int __flags,
+       const struct timespec *__req,
+       struct timespec *__rem);
+
+
+extern int clock_getcpuclockid (pid_t __pid, clockid_t *__clock_id) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern int timer_create (clockid_t __clock_id,
+    struct sigevent *__restrict __evp,
+    timer_t *__restrict __timerid) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int timer_delete (timer_t __timerid) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int timer_settime (timer_t __timerid, int __flags,
+     const struct itimerspec *__restrict __value,
+     struct itimerspec *__restrict __ovalue) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int timer_gettime (timer_t __timerid, struct itimerspec *__value)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int timer_getoverrun (timer_t __timerid) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern int timespec_get (struct timespec *__ts, int __base)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+
+typedef long int __jmp_buf[8];
+
+
+
+enum
+{
+  PTHREAD_CREATE_JOINABLE,
+
+  PTHREAD_CREATE_DETACHED
+
+};
+
+
+
+enum
+{
+  PTHREAD_MUTEX_TIMED_NP,
+  PTHREAD_MUTEX_RECURSIVE_NP,
+  PTHREAD_MUTEX_ERRORCHECK_NP,
+  PTHREAD_MUTEX_ADAPTIVE_NP
+
+  ,
+  PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_TIMED_NP,
+  PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,
+  PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,
+  PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL
+
+
+
+
+
+};
+
+
+
+
+enum
+{
+  PTHREAD_MUTEX_STALLED,
+  PTHREAD_MUTEX_STALLED_NP = PTHREAD_MUTEX_STALLED,
+  PTHREAD_MUTEX_ROBUST,
+  PTHREAD_MUTEX_ROBUST_NP = PTHREAD_MUTEX_ROBUST
+};
+
+
+
+
+
+enum
+{
+  PTHREAD_PRIO_NONE,
+  PTHREAD_PRIO_INHERIT,
+  PTHREAD_PRIO_PROTECT
+};
+enum
+{
+  PTHREAD_RWLOCK_PREFER_READER_NP,
+  PTHREAD_RWLOCK_PREFER_WRITER_NP,
+  PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP,
+  PTHREAD_RWLOCK_DEFAULT_NP = PTHREAD_RWLOCK_PREFER_READER_NP
+};
+enum
+{
+  PTHREAD_INHERIT_SCHED,
+
+  PTHREAD_EXPLICIT_SCHED
+
+};
+
+
+
+enum
+{
+  PTHREAD_SCOPE_SYSTEM,
+
+  PTHREAD_SCOPE_PROCESS
+
+};
+
+
+
+enum
+{
+  PTHREAD_PROCESS_PRIVATE,
+
+  PTHREAD_PROCESS_SHARED
+
+};
+struct _pthread_cleanup_buffer
+{
+  void (*__routine) (void *);
+  void *__arg;
+  int __canceltype;
+  struct _pthread_cleanup_buffer *__prev;
+};
+
+
+enum
+{
+  PTHREAD_CANCEL_ENABLE,
+
+  PTHREAD_CANCEL_DISABLE
+
+};
+enum
+{
+  PTHREAD_CANCEL_DEFERRED,
+
+  PTHREAD_CANCEL_ASYNCHRONOUS
+
+};
+
+
+
+
+
+extern int pthread_create (pthread_t *__restrict __newthread,
+      const pthread_attr_t *__restrict __attr,
+      void *(*__start_routine) (void *),
+      void *__restrict __arg) __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1, 3)));
+
+
+
+
+
+extern void pthread_exit (void *__retval) __attribute__ ((__noreturn__));
+
+
+
+
+
+
+
+extern int pthread_join (pthread_t __th, void **__thread_return);
+extern int pthread_detach (pthread_t __th) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern pthread_t pthread_self (void) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+
+
+extern int pthread_equal (pthread_t __thread1, pthread_t __thread2)
+  __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+
+
+
+
+
+
+
+extern int pthread_attr_init (pthread_attr_t *__attr) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_attr_destroy (pthread_attr_t *__attr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_attr_getdetachstate (const pthread_attr_t *__attr,
+     int *__detachstate)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_attr_setdetachstate (pthread_attr_t *__attr,
+     int __detachstate)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int pthread_attr_getguardsize (const pthread_attr_t *__attr,
+          size_t *__guardsize)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_attr_setguardsize (pthread_attr_t *__attr,
+          size_t __guardsize)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int pthread_attr_getschedparam (const pthread_attr_t *__restrict __attr,
+           struct sched_param *__restrict __param)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_attr_setschedparam (pthread_attr_t *__restrict __attr,
+           const struct sched_param *__restrict
+           __param) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_attr_getschedpolicy (const pthread_attr_t *__restrict
+     __attr, int *__restrict __policy)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_attr_setschedpolicy (pthread_attr_t *__attr, int __policy)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_attr_getinheritsched (const pthread_attr_t *__restrict
+      __attr, int *__restrict __inherit)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_attr_setinheritsched (pthread_attr_t *__attr,
+      int __inherit)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int pthread_attr_getscope (const pthread_attr_t *__restrict __attr,
+      int *__restrict __scope)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_attr_setscope (pthread_attr_t *__attr, int __scope)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_attr_getstackaddr (const pthread_attr_t *__restrict
+          __attr, void **__restrict __stackaddr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))) __attribute__ ((__deprecated__));
+
+
+
+
+
+extern int pthread_attr_setstackaddr (pthread_attr_t *__attr,
+          void *__stackaddr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) __attribute__ ((__deprecated__));
+
+
+extern int pthread_attr_getstacksize (const pthread_attr_t *__restrict
+          __attr, size_t *__restrict __stacksize)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+
+extern int pthread_attr_setstacksize (pthread_attr_t *__attr,
+          size_t __stacksize)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int pthread_attr_getstack (const pthread_attr_t *__restrict __attr,
+      void **__restrict __stackaddr,
+      size_t *__restrict __stacksize)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2, 3)));
+
+
+
+
+extern int pthread_attr_setstack (pthread_attr_t *__attr, void *__stackaddr,
+      size_t __stacksize) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+extern int pthread_setschedparam (pthread_t __target_thread, int __policy,
+      const struct sched_param *__param)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3)));
+
+
+extern int pthread_getschedparam (pthread_t __target_thread,
+      int *__restrict __policy,
+      struct sched_param *__restrict __param)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2, 3)));
+
+
+extern int pthread_setschedprio (pthread_t __target_thread, int __prio)
+     __attribute__ ((__nothrow__ , __leaf__));
+extern int pthread_once (pthread_once_t *__once_control,
+    void (*__init_routine) (void)) __attribute__ ((__nonnull__ (1, 2)));
+extern int pthread_setcancelstate (int __state, int *__oldstate);
+
+
+
+extern int pthread_setcanceltype (int __type, int *__oldtype);
+
+
+extern int pthread_cancel (pthread_t __th);
+
+
+
+
+extern void pthread_testcancel (void);
+
+
+
+
+typedef struct
+{
+  struct
+  {
+    __jmp_buf __cancel_jmp_buf;
+    int __mask_was_saved;
+  } __cancel_jmp_buf[1];
+  void *__pad[4];
+} __pthread_unwind_buf_t __attribute__ ((__aligned__));
+struct __pthread_cleanup_frame
+{
+  void (*__cancel_routine) (void *);
+  void *__cancel_arg;
+  int __do_it;
+  int __cancel_type;
+};
+extern void __pthread_register_cancel (__pthread_unwind_buf_t *__buf)
+     ;
+extern void __pthread_unregister_cancel (__pthread_unwind_buf_t *__buf)
+  ;
+extern void __pthread_unwind_next (__pthread_unwind_buf_t *__buf)
+     __attribute__ ((__noreturn__))
+
+
+
+     ;
+
+
+
+struct __jmp_buf_tag;
+extern int __sigsetjmp (struct __jmp_buf_tag *__env, int __savemask) __attribute__ ((__nothrow__));
+
+
+
+
+
+extern int pthread_mutex_init (pthread_mutex_t *__mutex,
+          const pthread_mutexattr_t *__mutexattr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_mutex_destroy (pthread_mutex_t *__mutex)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_mutex_trylock (pthread_mutex_t *__mutex)
+     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_mutex_lock (pthread_mutex_t *__mutex)
+     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int pthread_mutex_timedlock (pthread_mutex_t *__restrict __mutex,
+        const struct timespec *__restrict
+        __abstime) __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+extern int pthread_mutex_unlock (pthread_mutex_t *__mutex)
+     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int pthread_mutex_getprioceiling (const pthread_mutex_t *
+      __restrict __mutex,
+      int *__restrict __prioceiling)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+extern int pthread_mutex_setprioceiling (pthread_mutex_t *__restrict __mutex,
+      int __prioceiling,
+      int *__restrict __old_ceiling)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 3)));
+
+
+
+
+extern int pthread_mutex_consistent (pthread_mutex_t *__mutex)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+extern int pthread_mutexattr_init (pthread_mutexattr_t *__attr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_mutexattr_destroy (pthread_mutexattr_t *__attr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_mutexattr_getpshared (const pthread_mutexattr_t *
+      __restrict __attr,
+      int *__restrict __pshared)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_mutexattr_setpshared (pthread_mutexattr_t *__attr,
+      int __pshared)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int pthread_mutexattr_gettype (const pthread_mutexattr_t *__restrict
+          __attr, int *__restrict __kind)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+
+extern int pthread_mutexattr_settype (pthread_mutexattr_t *__attr, int __kind)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int pthread_mutexattr_getprotocol (const pthread_mutexattr_t *
+       __restrict __attr,
+       int *__restrict __protocol)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+extern int pthread_mutexattr_setprotocol (pthread_mutexattr_t *__attr,
+       int __protocol)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_mutexattr_getprioceiling (const pthread_mutexattr_t *
+          __restrict __attr,
+          int *__restrict __prioceiling)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_mutexattr_setprioceiling (pthread_mutexattr_t *__attr,
+          int __prioceiling)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int pthread_mutexattr_getrobust (const pthread_mutexattr_t *__attr,
+     int *__robustness)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+
+
+
+
+extern int pthread_mutexattr_setrobust (pthread_mutexattr_t *__attr,
+     int __robustness)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+extern int pthread_rwlock_init (pthread_rwlock_t *__restrict __rwlock,
+    const pthread_rwlockattr_t *__restrict
+    __attr) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_rwlock_destroy (pthread_rwlock_t *__rwlock)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_rwlock_rdlock (pthread_rwlock_t *__rwlock)
+     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_rwlock_tryrdlock (pthread_rwlock_t *__rwlock)
+  __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int pthread_rwlock_timedrdlock (pthread_rwlock_t *__restrict __rwlock,
+           const struct timespec *__restrict
+           __abstime) __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+extern int pthread_rwlock_wrlock (pthread_rwlock_t *__rwlock)
+     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_rwlock_trywrlock (pthread_rwlock_t *__rwlock)
+     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int pthread_rwlock_timedwrlock (pthread_rwlock_t *__restrict __rwlock,
+           const struct timespec *__restrict
+           __abstime) __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+extern int pthread_rwlock_unlock (pthread_rwlock_t *__rwlock)
+     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+extern int pthread_rwlockattr_init (pthread_rwlockattr_t *__attr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_rwlockattr_destroy (pthread_rwlockattr_t *__attr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *
+       __restrict __attr,
+       int *__restrict __pshared)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_rwlockattr_setpshared (pthread_rwlockattr_t *__attr,
+       int __pshared)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_rwlockattr_getkind_np (const pthread_rwlockattr_t *
+       __restrict __attr,
+       int *__restrict __pref)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_rwlockattr_setkind_np (pthread_rwlockattr_t *__attr,
+       int __pref) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+
+extern int pthread_cond_init (pthread_cond_t *__restrict __cond,
+         const pthread_condattr_t *__restrict __cond_attr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_cond_destroy (pthread_cond_t *__cond)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_cond_signal (pthread_cond_t *__cond)
+     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_cond_broadcast (pthread_cond_t *__cond)
+     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+extern int pthread_cond_wait (pthread_cond_t *__restrict __cond,
+         pthread_mutex_t *__restrict __mutex)
+     __attribute__ ((__nonnull__ (1, 2)));
+extern int pthread_cond_timedwait (pthread_cond_t *__restrict __cond,
+       pthread_mutex_t *__restrict __mutex,
+       const struct timespec *__restrict __abstime)
+     __attribute__ ((__nonnull__ (1, 2, 3)));
+
+
+
+
+extern int pthread_condattr_init (pthread_condattr_t *__attr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_condattr_destroy (pthread_condattr_t *__attr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_condattr_getpshared (const pthread_condattr_t *
+     __restrict __attr,
+     int *__restrict __pshared)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_condattr_setpshared (pthread_condattr_t *__attr,
+     int __pshared) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int pthread_condattr_getclock (const pthread_condattr_t *
+          __restrict __attr,
+          __clockid_t *__restrict __clock_id)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_condattr_setclock (pthread_condattr_t *__attr,
+          __clockid_t __clock_id)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+extern int pthread_spin_init (pthread_spinlock_t *__lock, int __pshared)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_spin_destroy (pthread_spinlock_t *__lock)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_spin_lock (pthread_spinlock_t *__lock)
+     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_spin_trylock (pthread_spinlock_t *__lock)
+     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_spin_unlock (pthread_spinlock_t *__lock)
+     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+extern int pthread_barrier_init (pthread_barrier_t *__restrict __barrier,
+     const pthread_barrierattr_t *__restrict
+     __attr, unsigned int __count)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_barrier_destroy (pthread_barrier_t *__barrier)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_barrier_wait (pthread_barrier_t *__barrier)
+     __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int pthread_barrierattr_init (pthread_barrierattr_t *__attr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_barrierattr_destroy (pthread_barrierattr_t *__attr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_barrierattr_getpshared (const pthread_barrierattr_t *
+        __restrict __attr,
+        int *__restrict __pshared)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int pthread_barrierattr_setpshared (pthread_barrierattr_t *__attr,
+        int __pshared)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+extern int pthread_key_create (pthread_key_t *__key,
+          void (*__destr_function) (void *))
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int pthread_key_delete (pthread_key_t __key) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern void *pthread_getspecific (pthread_key_t __key) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int pthread_setspecific (pthread_key_t __key,
+    const void *__pointer) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+extern int pthread_getcpuclockid (pthread_t __thread_id,
+      __clockid_t *__clock_id)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+extern int pthread_atfork (void (*__prepare) (void),
+      void (*__parent) (void),
+      void (*__child) (void)) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+__attribute__ ((__nothrow__ , __leaf__)) pthread_equal (pthread_t __thread1, pthread_t __thread2)
+{
+  return __thread1 == __thread2;
+}
+
+
+
+
+
+typedef struct {
+    pthread_once_t o;
+    k5_os_nothread_once_t n;
+} k5_once_t;
+typedef struct {
+    pthread_mutex_t p;
+
+    pthread_t owner;
+
+
+
+
+} k5_os_mutex;
+static __inline__ void k5_pthread_assert_unlocked(pthread_mutex_t *m) { }
+static __inline__ int k5_os_mutex_finish_init(k5_os_mutex *m) { return 0; }
+typedef struct {
+    k5_debug_loc loc_last, loc_created;
+    k5_os_mutex os;
+    k5_debug_mutex_stats stats;
+} k5_mutex_t;
+
+
+
+static __inline__ int k5_mutex_init_1(k5_mutex_t *m, k5_debug_loc l)
+{
+    int err = pthread_mutex_init(&(&m->os)->p, 0);
+    if (err) return err;
+    m->loc_created = m->loc_last = l;
+    err = (*(&m->stats) = 's', 0);
+    
+   ((
+   err == 0
+   ) ? (void) (0) : __assert_fail (
+   "err == 0"
+   , "../../../include/k5-thread.h", 659, __PRETTY_FUNCTION__))
+                   ;
+    return 0;
+}
+
+static __inline__ int k5_mutex_finish_init_1(k5_mutex_t *m, k5_debug_loc l)
+{
+    int err = k5_os_mutex_finish_init(&m->os);
+    if (err) return err;
+    m->loc_created = m->loc_last = l;
+    err = (0);
+    
+   ((
+   err == 0
+   ) ? (void) (0) : __assert_fail (
+   "err == 0"
+   , "../../../include/k5-thread.h", 669, __PRETTY_FUNCTION__))
+                   ;
+    return 0;
+}
+typedef enum {
+    K5_KEY_COM_ERR,
+    K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME,
+    K5_KEY_GSS_KRB5_CCACHE_NAME,
+    K5_KEY_MAX
+} k5_key_t;
+
+
+
+
+
+extern int krb5int_key_register(k5_key_t, void (*)(void *));
+extern void *krb5int_getspecific(k5_key_t);
+extern int krb5int_setspecific(k5_key_t, void *);
+extern int krb5int_key_delete(k5_key_t);
+typedef struct { k5_once_t once; int error, did_run; void (*fn)(void); } k5_init_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short int uint16_t;
+
+typedef unsigned int uint32_t;
+
+
+
+typedef unsigned long int uint64_t;
+typedef signed char int_least8_t;
+typedef short int int_least16_t;
+typedef int int_least32_t;
+
+typedef long int int_least64_t;
+
+
+
+
+
+
+typedef unsigned char uint_least8_t;
+typedef unsigned short int uint_least16_t;
+typedef unsigned int uint_least32_t;
+
+typedef unsigned long int uint_least64_t;
+typedef signed char int_fast8_t;
+
+typedef long int int_fast16_t;
+typedef long int int_fast32_t;
+typedef long int int_fast64_t;
+typedef unsigned char uint_fast8_t;
+
+typedef unsigned long int uint_fast16_t;
+typedef unsigned long int uint_fast32_t;
+typedef unsigned long int uint_fast64_t;
+typedef long int intptr_t;
+
+
+typedef unsigned long int uintptr_t;
+typedef long int intmax_t;
+typedef unsigned long int uintmax_t;
+
+
+typedef int __gwchar_t;
+
+
+
+
+
+typedef struct
+  {
+    long int quot;
+    long int rem;
+  } imaxdiv_t;
+extern intmax_t imaxabs (intmax_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+
+
+extern imaxdiv_t imaxdiv (intmax_t __numer, intmax_t __denom)
+      __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+
+
+extern intmax_t strtoimax (const char *__restrict __nptr,
+      char **__restrict __endptr, int __base) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern uintmax_t strtoumax (const char *__restrict __nptr,
+       char ** __restrict __endptr, int __base) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern intmax_t wcstoimax (const __gwchar_t *__restrict __nptr,
+      __gwchar_t **__restrict __endptr, int __base)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern uintmax_t wcstoumax (const __gwchar_t *__restrict __nptr,
+       __gwchar_t ** __restrict __endptr, int __base)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern long int __strtol_internal (const char *__restrict __nptr,
+       char **__restrict __endptr,
+       int __base, int __group)
+  __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+extern __inline __attribute__ ((__gnu_inline__)) intmax_t
+__attribute__ ((__nothrow__ , __leaf__)) strtoimax (const char *__restrict nptr, char **__restrict endptr, int base)
+
+{
+  return __strtol_internal (nptr, endptr, base, 0);
+}
+
+extern unsigned long int __strtoul_internal (const char *__restrict __nptr,
+          char ** __restrict __endptr,
+          int __base, int __group)
+  __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+extern __inline __attribute__ ((__gnu_inline__)) uintmax_t
+__attribute__ ((__nothrow__ , __leaf__)) strtoumax (const char *__restrict nptr, char **__restrict endptr, int base)
+
+{
+  return __strtoul_internal (nptr, endptr, base, 0);
+}
+
+extern long int __wcstol_internal (const __gwchar_t * __restrict __nptr,
+       __gwchar_t **__restrict __endptr,
+       int __base, int __group)
+  __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+extern __inline __attribute__ ((__gnu_inline__)) intmax_t
+__attribute__ ((__nothrow__ , __leaf__)) wcstoimax (const __gwchar_t *__restrict nptr, __gwchar_t **__restrict endptr, int base)
+
+{
+  return __wcstol_internal (nptr, endptr, base, 0);
+}
+
+extern unsigned long int __wcstoul_internal (const __gwchar_t *
+          __restrict __nptr,
+          __gwchar_t **
+          __restrict __endptr,
+          int __base, int __group)
+  __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+extern __inline __attribute__ ((__gnu_inline__)) uintmax_t
+__attribute__ ((__nothrow__ , __leaf__)) wcstoumax (const __gwchar_t *__restrict nptr, __gwchar_t **__restrict endptr, int base)
+
+{
+  return __wcstoul_internal (nptr, endptr, base, 0);
+}
+
+
+
+
+
+
+
+
+
+
+
+static __inline__ void
+store_16_be (unsigned int val, unsigned char *p)
+{
+    p[0] = (val >> 8) & 0xff;
+    p[1] = (val ) & 0xff;
+}
+static __inline__ void
+store_16_le (unsigned int val, unsigned char *p)
+{
+    p[1] = (val >> 8) & 0xff;
+    p[0] = (val ) & 0xff;
+}
+static __inline__ void
+store_32_be (unsigned int val, unsigned char *p)
+{
+    p[0] = (val >> 24) & 0xff;
+    p[1] = (val >> 16) & 0xff;
+    p[2] = (val >> 8) & 0xff;
+    p[3] = (val ) & 0xff;
+}
+static __inline__ void
+store_32_le (unsigned int val, unsigned char *p)
+{
+    p[3] = (val >> 24) & 0xff;
+    p[2] = (val >> 16) & 0xff;
+    p[1] = (val >> 8) & 0xff;
+    p[0] = (val ) & 0xff;
+}
+static __inline__ void
+store_64_be (uint64_t val, unsigned char *p)
+{
+    p[0] = (unsigned char)((val >> 56) & 0xff);
+    p[1] = (unsigned char)((val >> 48) & 0xff);
+    p[2] = (unsigned char)((val >> 40) & 0xff);
+    p[3] = (unsigned char)((val >> 32) & 0xff);
+    p[4] = (unsigned char)((val >> 24) & 0xff);
+    p[5] = (unsigned char)((val >> 16) & 0xff);
+    p[6] = (unsigned char)((val >> 8) & 0xff);
+    p[7] = (unsigned char)((val ) & 0xff);
+}
+static __inline__ void
+store_64_le (uint64_t val, unsigned char *p)
+{
+    p[7] = (unsigned char)((val >> 56) & 0xff);
+    p[6] = (unsigned char)((val >> 48) & 0xff);
+    p[5] = (unsigned char)((val >> 40) & 0xff);
+    p[4] = (unsigned char)((val >> 32) & 0xff);
+    p[3] = (unsigned char)((val >> 24) & 0xff);
+    p[2] = (unsigned char)((val >> 16) & 0xff);
+    p[1] = (unsigned char)((val >> 8) & 0xff);
+    p[0] = (unsigned char)((val ) & 0xff);
+}
+static __inline__ unsigned short
+load_16_be (unsigned char *p)
+{
+    return (p[1] | (p[0] << 8));
+}
+static __inline__ unsigned short
+load_16_le (unsigned char *p)
+{
+    return (p[0] | (p[1] << 8));
+}
+static __inline__ unsigned int
+load_32_be (unsigned char *p)
+{
+    return (p[3] | (p[2] << 8) | (p[1] << 16) | (p[0] << 24));
+}
+static __inline__ unsigned int
+load_32_le (unsigned char *p)
+{
+    return (p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
+}
+static __inline__ uint64_t
+load_64_be (unsigned char *p)
+{
+    return ((uint64_t)load_32_be(p) << 32) | load_32_be(p+4);
+}
+static __inline__ uint64_t
+load_64_le (unsigned char *p)
+{
+    return ((uint64_t)load_32_le(p+4) << 32) | load_32_le(p);
+}
+
+typedef uint64_t krb5_ui_8;
+typedef int64_t krb5_int64;
+
+
+
+
+
+
+
+
+extern int *__errno_location (void) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+
+typedef int wchar_t;
+
+
+
+
+
+
+
+
+union wait
+  {
+    int w_status;
+    struct
+      {
+
+ unsigned int __w_termsig:7;
+ unsigned int __w_coredump:1;
+ unsigned int __w_retcode:8;
+ unsigned int:16;
+
+
+
+
+
+
+
+      } __wait_terminated;
+    struct
+      {
+
+ unsigned int __w_stopval:8;
+ unsigned int __w_stopsig:8;
+ unsigned int:16;
+
+
+
+
+
+
+      } __wait_stopped;
+  };
+typedef union
+  {
+    union wait *__uptr;
+    int *__iptr;
+  } __WAIT_STATUS __attribute__ ((__transparent_union__));
+
+
+typedef struct
+  {
+    int quot;
+    int rem;
+  } div_t;
+
+
+
+typedef struct
+  {
+    long int quot;
+    long int rem;
+  } ldiv_t;
+
+
+
+
+
+
+
+__extension__ typedef struct
+  {
+    long long int quot;
+    long long int rem;
+  } lldiv_t;
+
+
+extern size_t __ctype_get_mb_cur_max (void) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+extern double atof (const char *__nptr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1))) ;
+
+extern int atoi (const char *__nptr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1))) ;
+
+extern long int atol (const char *__nptr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1))) ;
+
+
+
+
+
+__extension__ extern long long int atoll (const char *__nptr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1))) ;
+
+
+
+
+
+extern double strtod (const char *__restrict __nptr,
+        char **__restrict __endptr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+extern float strtof (const char *__restrict __nptr,
+       char **__restrict __endptr) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+extern long double strtold (const char *__restrict __nptr,
+       char **__restrict __endptr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+extern long int strtol (const char *__restrict __nptr,
+   char **__restrict __endptr, int __base)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+extern unsigned long int strtoul (const char *__restrict __nptr,
+      char **__restrict __endptr, int __base)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+__extension__
+extern long long int strtoq (const char *__restrict __nptr,
+        char **__restrict __endptr, int __base)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+__extension__
+extern unsigned long long int strtouq (const char *__restrict __nptr,
+           char **__restrict __endptr, int __base)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+__extension__
+extern long long int strtoll (const char *__restrict __nptr,
+         char **__restrict __endptr, int __base)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+__extension__
+extern unsigned long long int strtoull (const char *__restrict __nptr,
+     char **__restrict __endptr, int __base)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+__attribute__ ((__nothrow__ , __leaf__)) atoi (const char *__nptr)
+{
+  return (int) strtol (__nptr, (char **) ((void *)0), 10);
+}
+extern __inline __attribute__ ((__gnu_inline__)) long int
+__attribute__ ((__nothrow__ , __leaf__)) atol (const char *__nptr)
+{
+  return strtol (__nptr, (char **) ((void *)0), 10);
+}
+
+
+
+
+__extension__ extern __inline __attribute__ ((__gnu_inline__)) long long int
+__attribute__ ((__nothrow__ , __leaf__)) atoll (const char *__nptr)
+{
+  return strtoll (__nptr, (char **) ((void *)0), 10);
+}
+
+extern char *l64a (long int __n) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+extern long int a64l (const char *__s)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1))) ;
+extern long int random (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern void srandom (unsigned int __seed) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern char *initstate (unsigned int __seed, char *__statebuf,
+   size_t __statelen) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+
+
+
+extern char *setstate (char *__statebuf) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+
+struct random_data
+  {
+    int32_t *fptr;
+    int32_t *rptr;
+    int32_t *state;
+    int rand_type;
+    int rand_deg;
+    int rand_sep;
+    int32_t *end_ptr;
+  };
+
+extern int random_r (struct random_data *__restrict __buf,
+       int32_t *__restrict __result) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+extern int srandom_r (unsigned int __seed, struct random_data *__buf)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+
+extern int initstate_r (unsigned int __seed, char *__restrict __statebuf,
+   size_t __statelen,
+   struct random_data *__restrict __buf)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2, 4)));
+
+extern int setstate_r (char *__restrict __statebuf,
+         struct random_data *__restrict __buf)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+
+
+
+extern int rand (void) __attribute__ ((__nothrow__ , __leaf__));
+
+extern void srand (unsigned int __seed) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern int rand_r (unsigned int *__seed) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+extern double drand48 (void) __attribute__ ((__nothrow__ , __leaf__));
+extern double erand48 (unsigned short int __xsubi[3]) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern long int lrand48 (void) __attribute__ ((__nothrow__ , __leaf__));
+extern long int nrand48 (unsigned short int __xsubi[3])
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern long int mrand48 (void) __attribute__ ((__nothrow__ , __leaf__));
+extern long int jrand48 (unsigned short int __xsubi[3])
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern void srand48 (long int __seedval) __attribute__ ((__nothrow__ , __leaf__));
+extern unsigned short int *seed48 (unsigned short int __seed16v[3])
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+extern void lcong48 (unsigned short int __param[7]) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+struct drand48_data
+  {
+    unsigned short int __x[3];
+    unsigned short int __old_x[3];
+    unsigned short int __c;
+    unsigned short int __init;
+    __extension__ unsigned long long int __a;
+
+  };
+
+
+extern int drand48_r (struct drand48_data *__restrict __buffer,
+        double *__restrict __result) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+extern int erand48_r (unsigned short int __xsubi[3],
+        struct drand48_data *__restrict __buffer,
+        double *__restrict __result) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int lrand48_r (struct drand48_data *__restrict __buffer,
+        long int *__restrict __result)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+extern int nrand48_r (unsigned short int __xsubi[3],
+        struct drand48_data *__restrict __buffer,
+        long int *__restrict __result)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int mrand48_r (struct drand48_data *__restrict __buffer,
+        long int *__restrict __result)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+extern int jrand48_r (unsigned short int __xsubi[3],
+        struct drand48_data *__restrict __buffer,
+        long int *__restrict __result)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int srand48_r (long int __seedval, struct drand48_data *__buffer)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+
+extern int seed48_r (unsigned short int __seed16v[3],
+       struct drand48_data *__buffer) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+extern int lcong48_r (unsigned short int __param[7],
+        struct drand48_data *__buffer)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+
+
+
+
+
+
+extern void *malloc (size_t __size) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) ;
+
+extern void *calloc (size_t __nmemb, size_t __size)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) ;
+
+
+
+
+
+
+
+
+
+
+extern void *realloc (void *__ptr, size_t __size)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__warn_unused_result__));
+
+extern void free (void *__ptr) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern void cfree (void *__ptr) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+
+
+
+extern void *alloca (size_t __size) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+
+
+
+
+extern void *valloc (size_t __size) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) ;
+
+
+
+
+extern int posix_memalign (void **__memptr, size_t __alignment, size_t __size)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+
+
+
+extern void *aligned_alloc (size_t __alignment, size_t __size)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) __attribute__ ((__alloc_size__ (2))) ;
+
+
+
+
+extern void abort (void) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__));
+
+
+
+extern int atexit (void (*__func) (void)) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+
+extern int at_quick_exit (void (*__func) (void)) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+
+extern int on_exit (void (*__func) (int __status, void *__arg), void *__arg)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+extern void exit (int __status) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__));
+
+
+
+
+
+extern void quick_exit (int __status) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__));
+
+
+
+
+
+
+
+extern void _Exit (int __status) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__));
+
+
+
+
+
+
+extern char *getenv (const char *__name) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+extern int putenv (char *__string) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+extern int setenv (const char *__name, const char *__value, int __replace)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+
+
+extern int unsetenv (const char *__name) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+extern int clearenv (void) __attribute__ ((__nothrow__ , __leaf__));
+extern char *mktemp (char *__template) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+extern int mkstemp (char *__template) __attribute__ ((__nonnull__ (1))) ;
+extern int mkstemps (char *__template, int __suffixlen) __attribute__ ((__nonnull__ (1))) ;
+extern char *mkdtemp (char *__template) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+
+
+
+
+extern int system (const char *__command) ;
+
+extern char *realpath (const char *__restrict __name,
+         char *__restrict __resolved) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+
+
+typedef int (*__compar_fn_t) (const void *, const void *);
+
+
+
+extern void *bsearch (const void *__key, const void *__base,
+        size_t __nmemb, size_t __size, __compar_fn_t __compar)
+     __attribute__ ((__nonnull__ (1, 2, 5))) ;
+
+
+extern __inline __attribute__ ((__gnu_inline__)) void *
+bsearch (const void *__key, const void *__base, size_t __nmemb, size_t __size,
+  __compar_fn_t __compar)
+{
+  size_t __l, __u, __idx;
+  const void *__p;
+  int __comparison;
+
+  __l = 0;
+  __u = __nmemb;
+  while (__l < __u)
+    {
+      __idx = (__l + __u) / 2;
+      __p = (void *) (((const char *) __base) + (__idx * __size));
+      __comparison = (*__compar) (__key, __p);
+      if (__comparison < 0)
+ __u = __idx;
+      else if (__comparison > 0)
+ __l = __idx + 1;
+      else
+ return (void *) __p;
+    }
+
+  return ((void *)0);
+}
+
+
+
+
+extern void qsort (void *__base, size_t __nmemb, size_t __size,
+     __compar_fn_t __compar) __attribute__ ((__nonnull__ (1, 4)));
+extern int abs (int __x) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__)) ;
+extern long int labs (long int __x) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__)) ;
+
+
+
+__extension__ extern long long int llabs (long long int __x)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__)) ;
+
+
+
+
+
+
+
+extern div_t div (int __numer, int __denom)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__)) ;
+extern ldiv_t ldiv (long int __numer, long int __denom)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__)) ;
+
+
+
+
+__extension__ extern lldiv_t lldiv (long long int __numer,
+        long long int __denom)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__)) ;
+
+extern char *ecvt (double __value, int __ndigit, int *__restrict __decpt,
+     int *__restrict __sign) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3, 4))) ;
+
+
+
+
+extern char *fcvt (double __value, int __ndigit, int *__restrict __decpt,
+     int *__restrict __sign) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3, 4))) ;
+
+
+
+
+extern char *gcvt (double __value, int __ndigit, char *__buf)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3))) ;
+
+
+
+
+extern char *qecvt (long double __value, int __ndigit,
+      int *__restrict __decpt, int *__restrict __sign)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3, 4))) ;
+extern char *qfcvt (long double __value, int __ndigit,
+      int *__restrict __decpt, int *__restrict __sign)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3, 4))) ;
+extern char *qgcvt (long double __value, int __ndigit, char *__buf)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3))) ;
+
+
+
+
+extern int ecvt_r (double __value, int __ndigit, int *__restrict __decpt,
+     int *__restrict __sign, char *__restrict __buf,
+     size_t __len) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3, 4, 5)));
+extern int fcvt_r (double __value, int __ndigit, int *__restrict __decpt,
+     int *__restrict __sign, char *__restrict __buf,
+     size_t __len) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3, 4, 5)));
+
+extern int qecvt_r (long double __value, int __ndigit,
+      int *__restrict __decpt, int *__restrict __sign,
+      char *__restrict __buf, size_t __len)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3, 4, 5)));
+extern int qfcvt_r (long double __value, int __ndigit,
+      int *__restrict __decpt, int *__restrict __sign,
+      char *__restrict __buf, size_t __len)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3, 4, 5)));
+
+
+
+
+
+
+extern int mblen (const char *__s, size_t __n) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int mbtowc (wchar_t *__restrict __pwc,
+     const char *__restrict __s, size_t __n) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int wctomb (char *__s, wchar_t __wchar) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern size_t mbstowcs (wchar_t *__restrict __pwcs,
+   const char *__restrict __s, size_t __n) __attribute__ ((__nothrow__ , __leaf__));
+
+extern size_t wcstombs (char *__restrict __s,
+   const wchar_t *__restrict __pwcs, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+
+extern int rpmatch (const char *__response) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+extern int getsubopt (char **__restrict __optionp,
+        char *const *__restrict __tokens,
+        char **__restrict __valuep)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2, 3))) ;
+extern int getloadavg (double __loadavg[], int __nelem)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern __inline __attribute__ ((__gnu_inline__)) double
+__attribute__ ((__nothrow__ , __leaf__)) atof (const char *__nptr)
+{
+  return strtod (__nptr, (char **) ((void *)0));
+}
+
+
+
+
+
+
+
+
+
+
+struct _profile_t;
+typedef unsigned char krb5_octet;
+
+
+
+
+
+typedef short krb5_int16;
+typedef unsigned short krb5_ui_2;
+
+
+
+
+
+typedef int krb5_int32;
+typedef unsigned int krb5_ui_4;
+typedef unsigned int krb5_boolean;
+typedef unsigned int krb5_msgtype;
+typedef unsigned int krb5_kvno;
+
+typedef krb5_int32 krb5_addrtype;
+typedef krb5_int32 krb5_enctype;
+typedef krb5_int32 krb5_cksumtype;
+typedef krb5_int32 krb5_authdatatype;
+typedef krb5_int32 krb5_keyusage;
+
+typedef krb5_int32 krb5_preauthtype;
+typedef krb5_int32 krb5_flags;
+typedef krb5_int32 krb5_timestamp;
+typedef krb5_int32 krb5_error_code;
+typedef krb5_int32 krb5_deltat;
+
+typedef krb5_error_code krb5_magic;
+
+typedef struct _krb5_data {
+ krb5_magic magic;
+ unsigned int length;
+ char *data;
+} krb5_data;
+typedef void * krb5_pointer;
+typedef void const * krb5_const_pointer;
+
+typedef struct krb5_principal_data {
+    krb5_magic magic;
+    krb5_data realm;
+    krb5_data *data;
+    krb5_int32 length;
+    krb5_int32 type;
+} krb5_principal_data;
+
+typedef krb5_principal_data * krb5_principal;
+typedef const krb5_principal_data *krb5_const_principal;
+typedef struct _krb5_address {
+    krb5_magic magic;
+    krb5_addrtype addrtype;
+    unsigned int length;
+    krb5_octet *contents;
+} krb5_address;
+struct _krb5_context;
+typedef struct _krb5_context * krb5_context;
+
+struct _krb5_auth_context;
+typedef struct _krb5_auth_context * krb5_auth_context;
+
+struct _krb5_cryptosystem_entry;
+
+
+
+
+
+typedef struct _krb5_keyblock {
+    krb5_magic magic;
+    krb5_enctype enctype;
+    unsigned int length;
+    krb5_octet *contents;
+} krb5_keyblock;
+
+
+typedef struct _krb5_encrypt_block {
+    krb5_magic magic;
+    krb5_enctype crypto_entry;
+
+
+    krb5_keyblock *key;
+} krb5_encrypt_block;
+
+
+typedef struct _krb5_checksum {
+    krb5_magic magic;
+    krb5_cksumtype checksum_type;
+    unsigned int length;
+    krb5_octet *contents;
+} krb5_checksum;
+
+typedef struct _krb5_enc_data {
+    krb5_magic magic;
+    krb5_enctype enctype;
+    krb5_kvno kvno;
+    krb5_data ciphertext;
+} krb5_enc_data;
+enum {
+  KRB5_C_RANDSOURCE_OLDAPI = 0,
+  KRB5_C_RANDSOURCE_OSRAND = 1,
+  KRB5_C_RANDSOURCE_TRUSTEDPARTY = 2,
+
+
+
+
+  KRB5_C_RANDSOURCE_TIMING = 3,
+  KRB5_C_RANDSOURCE_EXTERNAL_PROTOCOL = 4,
+  KRB5_C_RANDSOURCE_MAX = 5
+};
+krb5_error_code
+    krb5_c_encrypt
+    (krb5_context context, const krb5_keyblock *key,
+      krb5_keyusage usage, const krb5_data *cipher_state,
+      const krb5_data *input, krb5_enc_data *output);
+
+krb5_error_code
+    krb5_c_decrypt
+    (krb5_context context, const krb5_keyblock *key,
+      krb5_keyusage usage, const krb5_data *cipher_state,
+      const krb5_enc_data *input, krb5_data *output);
+
+krb5_error_code
+    krb5_c_encrypt_length
+    (krb5_context context, krb5_enctype enctype,
+      size_t inputlen, size_t *length);
+
+krb5_error_code
+    krb5_c_block_size
+    (krb5_context context, krb5_enctype enctype,
+      size_t *blocksize);
+
+krb5_error_code
+ krb5_c_init_state
+(krb5_context context,
+const krb5_keyblock *key, krb5_keyusage usage,
+krb5_data *new_state);
+
+krb5_error_code
+ krb5_c_free_state
+(krb5_context context, const krb5_keyblock *key, krb5_data *state);
+
+krb5_error_code
+    krb5_c_make_random_key
+    (krb5_context context, krb5_enctype enctype,
+      krb5_keyblock *k5_random_key);
+
+
+
+
+
+krb5_error_code
+ krb5_c_random_add_entropy
+(krb5_context context, unsigned int randsource_id, const krb5_data *data);
+
+
+krb5_error_code
+    krb5_c_random_make_octets
+    (krb5_context context, krb5_data *data);
+krb5_error_code
+krb5_c_random_os_entropy
+(krb5_context context, int strong, int *success);
+
+               krb5_error_code
+    krb5_c_random_seed
+    (krb5_context context, krb5_data *data);
+
+krb5_error_code
+    krb5_c_string_to_key
+    (krb5_context context, krb5_enctype enctype,
+      const krb5_data *string, const krb5_data *salt,
+      krb5_keyblock *key);
+krb5_error_code
+krb5_c_string_to_key_with_params(krb5_context context,
+     krb5_enctype enctype,
+     const krb5_data *string,
+     const krb5_data *salt,
+     const krb5_data *params,
+     krb5_keyblock *key);
+
+krb5_error_code
+    krb5_c_enctype_compare
+    (krb5_context context, krb5_enctype e1, krb5_enctype e2,
+      krb5_boolean *similar);
+
+krb5_error_code
+    krb5_c_make_checksum
+    (krb5_context context, krb5_cksumtype cksumtype,
+      const krb5_keyblock *key, krb5_keyusage usage,
+      const krb5_data *input, krb5_checksum *cksum);
+
+krb5_error_code
+    krb5_c_verify_checksum
+    (krb5_context context,
+      const krb5_keyblock *key, krb5_keyusage usage,
+      const krb5_data *data,
+      const krb5_checksum *cksum,
+      krb5_boolean *valid);
+
+krb5_error_code
+    krb5_c_checksum_length
+    (krb5_context context, krb5_cksumtype cksumtype,
+      size_t *length);
+
+krb5_error_code
+    krb5_c_keyed_checksum_types
+    (krb5_context context, krb5_enctype enctype,
+      unsigned int *count, krb5_cksumtype **cksumtypes);
+krb5_boolean krb5_c_valid_enctype
+ (krb5_enctype ktype);
+krb5_boolean krb5_c_valid_cksumtype
+ (krb5_cksumtype ctype);
+krb5_boolean krb5_c_is_coll_proof_cksum
+ (krb5_cksumtype ctype);
+krb5_boolean krb5_c_is_keyed_cksum
+ (krb5_cksumtype ctype);
+
+
+
+krb5_boolean valid_enctype
+ (krb5_enctype ktype);
+krb5_boolean valid_cksumtype
+ (krb5_cksumtype ctype);
+krb5_boolean is_coll_proof_cksum
+ (krb5_cksumtype ctype);
+krb5_boolean is_keyed_cksum
+ (krb5_cksumtype ctype);
+
+
+
+
+
+
+
+krb5_error_code krb5_encrypt
+ (krb5_context context,
+  krb5_const_pointer inptr,
+  krb5_pointer outptr,
+  size_t size,
+  krb5_encrypt_block * eblock,
+  krb5_pointer ivec);
+krb5_error_code krb5_decrypt
+ (krb5_context context,
+  krb5_const_pointer inptr,
+  krb5_pointer outptr,
+  size_t size,
+  krb5_encrypt_block * eblock,
+  krb5_pointer ivec);
+krb5_error_code krb5_process_key
+ (krb5_context context,
+  krb5_encrypt_block * eblock,
+  const krb5_keyblock * key);
+krb5_error_code krb5_finish_key
+ (krb5_context context,
+  krb5_encrypt_block * eblock);
+krb5_error_code krb5_string_to_key
+ (krb5_context context,
+  const krb5_encrypt_block * eblock,
+  krb5_keyblock * keyblock,
+  const krb5_data * data,
+  const krb5_data * salt);
+krb5_error_code krb5_init_random_key
+ (krb5_context context,
+  const krb5_encrypt_block * eblock,
+  const krb5_keyblock * keyblock,
+  krb5_pointer * ptr);
+krb5_error_code krb5_finish_random_key
+ (krb5_context context,
+  const krb5_encrypt_block * eblock,
+  krb5_pointer * ptr);
+krb5_error_code krb5_random_key
+ (krb5_context context,
+  const krb5_encrypt_block * eblock,
+  krb5_pointer ptr,
+  krb5_keyblock ** keyblock);
+krb5_enctype krb5_eblock_enctype
+ (krb5_context context,
+  const krb5_encrypt_block * eblock);
+krb5_error_code krb5_use_enctype
+ (krb5_context context,
+  krb5_encrypt_block * eblock,
+  krb5_enctype enctype);
+size_t krb5_encrypt_size
+ (size_t length,
+  krb5_enctype crypto);
+size_t krb5_checksum_size
+ (krb5_context context,
+  krb5_cksumtype ctype);
+krb5_error_code krb5_calculate_checksum
+ (krb5_context context,
+  krb5_cksumtype ctype,
+  krb5_const_pointer in, size_t in_length,
+  krb5_const_pointer seed, size_t seed_length,
+  krb5_checksum * outcksum);
+krb5_error_code krb5_verify_checksum
+ (krb5_context context,
+  krb5_cksumtype ctype,
+  const krb5_checksum * cksum,
+  krb5_const_pointer in, size_t in_length,
+  krb5_const_pointer seed, size_t seed_length);
+
+
+krb5_error_code krb5_random_confounder
+ (size_t, krb5_pointer);
+
+krb5_error_code krb5_encrypt_data
+ (krb5_context context, krb5_keyblock *key,
+  krb5_pointer ivec, krb5_data *data,
+  krb5_enc_data *enc_data);
+
+krb5_error_code krb5_decrypt_data
+ (krb5_context context, krb5_keyblock *key,
+  krb5_pointer ivec, krb5_enc_data *data,
+  krb5_data *enc_data);
+typedef struct _krb5_ticket_times {
+    krb5_timestamp authtime;
+
+    krb5_timestamp starttime;
+
+    krb5_timestamp endtime;
+    krb5_timestamp renew_till;
+} krb5_ticket_times;
+
+
+typedef struct _krb5_authdata {
+    krb5_magic magic;
+    krb5_authdatatype ad_type;
+    unsigned int length;
+    krb5_octet *contents;
+} krb5_authdata;
+
+
+typedef struct _krb5_transited {
+    krb5_magic magic;
+    krb5_octet tr_type;
+    krb5_data tr_contents;
+} krb5_transited;
+
+typedef struct _krb5_enc_tkt_part {
+    krb5_magic magic;
+
+    krb5_flags flags;
+    krb5_keyblock *session;
+    krb5_principal client;
+    krb5_transited transited;
+    krb5_ticket_times times;
+    krb5_address **caddrs;
+    krb5_authdata **authorization_data;
+} krb5_enc_tkt_part;
+
+typedef struct _krb5_ticket {
+    krb5_magic magic;
+
+    krb5_principal server;
+    krb5_enc_data enc_part;
+
+    krb5_enc_tkt_part *enc_part2;
+
+} krb5_ticket;
+
+
+typedef struct _krb5_authenticator {
+    krb5_magic magic;
+    krb5_principal client;
+    krb5_checksum *checksum;
+    krb5_int32 cusec;
+    krb5_timestamp ctime;
+    krb5_keyblock *subkey;
+    krb5_ui_4 seq_number;
+    krb5_authdata **authorization_data;
+} krb5_authenticator;
+
+typedef struct _krb5_tkt_authent {
+    krb5_magic magic;
+    krb5_ticket *ticket;
+    krb5_authenticator *authenticator;
+    krb5_flags ap_options;
+} krb5_tkt_authent;
+
+
+typedef struct _krb5_creds {
+    krb5_magic magic;
+    krb5_principal client;
+    krb5_principal server;
+    krb5_keyblock keyblock;
+    krb5_ticket_times times;
+    krb5_boolean is_skey;
+
+    krb5_flags ticket_flags;
+    krb5_address **addresses;
+    krb5_data ticket;
+    krb5_data second_ticket;
+
+
+    krb5_authdata **authdata;
+} krb5_creds;
+
+
+typedef struct _krb5_last_req_entry {
+    krb5_magic magic;
+    krb5_int32 lr_type;
+    krb5_timestamp value;
+} krb5_last_req_entry;
+
+
+typedef struct _krb5_pa_data {
+    krb5_magic magic;
+    krb5_preauthtype pa_type;
+    unsigned int length;
+    krb5_octet *contents;
+} krb5_pa_data;
+
+typedef struct _krb5_kdc_req {
+    krb5_magic magic;
+    krb5_msgtype msg_type;
+    krb5_pa_data **padata;
+
+    krb5_flags kdc_options;
+    krb5_principal client;
+    krb5_principal server;
+
+    krb5_timestamp from;
+    krb5_timestamp till;
+    krb5_timestamp rtime;
+    krb5_int32 nonce;
+    int nktypes;
+    krb5_enctype *ktype;
+    krb5_address **addresses;
+    krb5_enc_data authorization_data;
+    krb5_authdata **unenc_authdata;
+
+    krb5_ticket **second_ticket;
+} krb5_kdc_req;
+
+typedef struct _krb5_enc_kdc_rep_part {
+    krb5_magic magic;
+
+    krb5_msgtype msg_type;
+    krb5_keyblock *session;
+    krb5_last_req_entry **last_req;
+    krb5_int32 nonce;
+    krb5_timestamp key_exp;
+    krb5_flags flags;
+    krb5_ticket_times times;
+    krb5_principal server;
+    krb5_address **caddrs;
+
+} krb5_enc_kdc_rep_part;
+
+typedef struct _krb5_kdc_rep {
+    krb5_magic magic;
+
+    krb5_msgtype msg_type;
+    krb5_pa_data **padata;
+    krb5_principal client;
+    krb5_ticket *ticket;
+    krb5_enc_data enc_part;
+
+    krb5_enc_kdc_rep_part *enc_part2;
+} krb5_kdc_rep;
+
+
+typedef struct _krb5_error {
+    krb5_magic magic;
+
+    krb5_timestamp ctime;
+    krb5_int32 cusec;
+    krb5_int32 susec;
+    krb5_timestamp stime;
+    krb5_ui_4 error;
+    krb5_principal client;
+
+    krb5_principal server;
+    krb5_data text;
+    krb5_data e_data;
+} krb5_error;
+
+typedef struct _krb5_ap_req {
+    krb5_magic magic;
+    krb5_flags ap_options;
+    krb5_ticket *ticket;
+    krb5_enc_data authenticator;
+} krb5_ap_req;
+
+typedef struct _krb5_ap_rep {
+    krb5_magic magic;
+    krb5_enc_data enc_part;
+} krb5_ap_rep;
+
+typedef struct _krb5_ap_rep_enc_part {
+    krb5_magic magic;
+    krb5_timestamp ctime;
+    krb5_int32 cusec;
+    krb5_keyblock *subkey;
+    krb5_ui_4 seq_number;
+} krb5_ap_rep_enc_part;
+
+typedef struct _krb5_response {
+    krb5_magic magic;
+    krb5_octet message_type;
+    krb5_data response;
+    krb5_int32 expected_nonce;
+    krb5_timestamp request_time;
+} krb5_response;
+
+typedef struct _krb5_cred_info {
+    krb5_magic magic;
+    krb5_keyblock *session;
+
+    krb5_principal client;
+    krb5_principal server;
+    krb5_flags flags;
+    krb5_ticket_times times;
+
+    krb5_address **caddrs;
+} krb5_cred_info;
+
+typedef struct _krb5_cred_enc_part {
+    krb5_magic magic;
+    krb5_int32 nonce;
+    krb5_timestamp timestamp;
+    krb5_int32 usec;
+    krb5_address *s_address;
+    krb5_address *r_address;
+    krb5_cred_info **ticket_info;
+} krb5_cred_enc_part;
+
+typedef struct _krb5_cred {
+    krb5_magic magic;
+    krb5_ticket **tickets;
+    krb5_enc_data enc_part;
+    krb5_cred_enc_part *enc_part2;
+} krb5_cred;
+
+
+typedef struct _passwd_phrase_element {
+    krb5_magic magic;
+    krb5_data *passwd;
+    krb5_data *phrase;
+} passwd_phrase_element;
+
+typedef struct _krb5_pwd_data {
+    krb5_magic magic;
+    int sequence_count;
+    passwd_phrase_element **element;
+} krb5_pwd_data;
+typedef struct krb5_replay_data {
+    krb5_timestamp timestamp;
+    krb5_int32 usec;
+    krb5_ui_4 seq;
+} krb5_replay_data;
+typedef krb5_error_code
+( * krb5_mk_req_checksum_func) (krb5_context, krb5_auth_context , void *,
+          krb5_data **);
+typedef krb5_pointer krb5_cc_cursor;
+
+struct _krb5_ccache;
+typedef struct _krb5_ccache *krb5_ccache;
+struct _krb5_cc_ops;
+typedef struct _krb5_cc_ops krb5_cc_ops;
+const char *
+krb5_cc_get_name (krb5_context context, krb5_ccache cache);
+
+krb5_error_code
+krb5_cc_gen_new (krb5_context context, krb5_ccache *cache);
+
+krb5_error_code
+krb5_cc_initialize(krb5_context context, krb5_ccache cache,
+     krb5_principal principal);
+
+krb5_error_code
+krb5_cc_destroy (krb5_context context, krb5_ccache cache);
+
+krb5_error_code
+krb5_cc_close (krb5_context context, krb5_ccache cache);
+
+krb5_error_code
+krb5_cc_store_cred (krb5_context context, krb5_ccache cache,
+      krb5_creds *creds);
+
+krb5_error_code
+krb5_cc_retrieve_cred (krb5_context context, krb5_ccache cache,
+         krb5_flags flags, krb5_creds *mcreds,
+         krb5_creds *creds);
+
+krb5_error_code
+krb5_cc_get_principal (krb5_context context, krb5_ccache cache,
+         krb5_principal *principal);
+
+krb5_error_code
+krb5_cc_start_seq_get (krb5_context context, krb5_ccache cache,
+         krb5_cc_cursor *cursor);
+
+krb5_error_code
+krb5_cc_next_cred (krb5_context context, krb5_ccache cache,
+     krb5_cc_cursor *cursor, krb5_creds *creds);
+
+krb5_error_code
+krb5_cc_end_seq_get (krb5_context context, krb5_ccache cache,
+       krb5_cc_cursor *cursor);
+
+krb5_error_code
+krb5_cc_remove_cred (krb5_context context, krb5_ccache cache, krb5_flags flags,
+       krb5_creds *creds);
+
+krb5_error_code
+krb5_cc_set_flags (krb5_context context, krb5_ccache cache, krb5_flags flags);
+
+const char *
+krb5_cc_get_type (krb5_context context, krb5_ccache cache);
+struct krb5_rc_st;
+typedef struct krb5_rc_st *krb5_rcache;
+typedef krb5_pointer krb5_kt_cursor;
+
+typedef struct krb5_keytab_entry_st {
+    krb5_magic magic;
+    krb5_principal principal;
+    krb5_timestamp timestamp;
+    krb5_kvno vno;
+    krb5_keyblock key;
+} krb5_keytab_entry;
+
+
+struct _krb5_kt_ops;
+typedef struct _krb5_kt {
+    krb5_magic magic;
+    const struct _krb5_kt_ops *ops;
+    krb5_pointer data;
+} *krb5_keytab;
+
+
+
+
+
+char *
+krb5_kt_get_type (krb5_context, krb5_keytab keytab);
+krb5_error_code
+krb5_kt_get_name(krb5_context context, krb5_keytab keytab, char *name,
+   unsigned int namelen);
+krb5_error_code
+krb5_kt_close(krb5_context context, krb5_keytab keytab);
+krb5_error_code
+krb5_kt_get_entry(krb5_context context, krb5_keytab keytab,
+    krb5_const_principal principal, krb5_kvno vno,
+    krb5_enctype enctype, krb5_keytab_entry *entry);
+krb5_error_code
+krb5_kt_start_seq_get(krb5_context context, krb5_keytab keytab,
+        krb5_kt_cursor *cursor);
+krb5_error_code
+krb5_kt_next_entry(krb5_context context, krb5_keytab keytab,
+     krb5_keytab_entry *entry, krb5_kt_cursor *cursor);
+krb5_error_code
+krb5_kt_end_seq_get(krb5_context context, krb5_keytab keytab,
+      krb5_kt_cursor *cursor);
+krb5_error_code krb5_init_context
+ (krb5_context *);
+krb5_error_code krb5_init_secure_context
+ (krb5_context *);
+void krb5_free_context
+ (krb5_context);
+
+
+krb5_error_code krb5_set_default_in_tkt_ktypes
+ (krb5_context,
+  const krb5_enctype *);
+krb5_error_code krb5_get_default_in_tkt_ktypes
+ (krb5_context,
+  krb5_enctype **);
+
+krb5_error_code krb5_set_default_tgs_ktypes
+ (krb5_context,
+  const krb5_enctype *);
+
+
+krb5_error_code
+krb5_set_default_tgs_enctypes
+ (krb5_context,
+  const krb5_enctype *);
+
+krb5_error_code krb5_get_tgs_ktypes
+ (krb5_context,
+  krb5_const_principal,
+  krb5_enctype **);
+
+
+krb5_error_code krb5_get_permitted_enctypes
+ (krb5_context, krb5_enctype **);
+
+
+void krb5_free_ktypes
+ (krb5_context, krb5_enctype *);
+
+krb5_boolean krb5_is_permitted_enctype
+ (krb5_context, krb5_enctype);
+
+
+krb5_boolean krb5_is_thread_safe(void);
+
+
+
+krb5_error_code krb5_kdc_rep_decrypt_proc
+ (krb5_context,
+  const krb5_keyblock *,
+  krb5_const_pointer,
+  krb5_kdc_rep * );
+krb5_error_code krb5_decrypt_tkt_part
+ (krb5_context,
+  const krb5_keyblock *,
+  krb5_ticket * );
+krb5_error_code krb5_get_cred_from_kdc
+ (krb5_context,
+  krb5_ccache,
+
+  krb5_creds *,
+  krb5_creds **,
+  krb5_creds *** );
+krb5_error_code krb5_get_cred_from_kdc_validate
+ (krb5_context,
+  krb5_ccache,
+
+  krb5_creds *,
+  krb5_creds **,
+  krb5_creds *** );
+krb5_error_code krb5_get_cred_from_kdc_renew
+ (krb5_context,
+  krb5_ccache,
+
+  krb5_creds *,
+  krb5_creds **,
+  krb5_creds *** );
+
+
+void krb5_free_tgt_creds
+ (krb5_context,
+  krb5_creds **);
+
+
+
+
+krb5_error_code krb5_get_credentials
+ (krb5_context,
+  krb5_flags,
+  krb5_ccache,
+  krb5_creds *,
+  krb5_creds **);
+krb5_error_code krb5_get_credentials_validate
+ (krb5_context,
+  krb5_flags,
+  krb5_ccache,
+  krb5_creds *,
+  krb5_creds **);
+krb5_error_code krb5_get_credentials_renew
+ (krb5_context,
+  krb5_flags,
+  krb5_ccache,
+  krb5_creds *,
+  krb5_creds **);
+
+krb5_error_code krb5_get_cred_via_tkt
+ (krb5_context,
+     krb5_creds *,
+     krb5_flags,
+     krb5_address * const *,
+     krb5_creds *,
+     krb5_creds **);
+
+krb5_error_code krb5_mk_req
+ (krb5_context,
+  krb5_auth_context *,
+  krb5_flags,
+  char *,
+  char *,
+  krb5_data *,
+  krb5_ccache,
+  krb5_data * );
+krb5_error_code krb5_mk_req_extended
+ (krb5_context,
+  krb5_auth_context *,
+  krb5_flags,
+  krb5_data *,
+  krb5_creds *,
+  krb5_data * );
+krb5_error_code krb5_mk_rep
+ (krb5_context,
+  krb5_auth_context,
+  krb5_data *);
+krb5_error_code krb5_rd_rep
+ (krb5_context,
+  krb5_auth_context,
+  const krb5_data *,
+  krb5_ap_rep_enc_part **);
+krb5_error_code krb5_mk_error
+ (krb5_context,
+  const krb5_error *,
+  krb5_data * );
+krb5_error_code krb5_rd_error
+ (krb5_context,
+  const krb5_data *,
+  krb5_error ** );
+krb5_error_code krb5_rd_safe
+ (krb5_context,
+  krb5_auth_context,
+  const krb5_data *,
+  krb5_data *,
+  krb5_replay_data *);
+krb5_error_code krb5_rd_priv
+ (krb5_context,
+  krb5_auth_context,
+  const krb5_data *,
+  krb5_data *,
+  krb5_replay_data *);
+krb5_error_code krb5_parse_name
+ (krb5_context,
+  const char *,
+  krb5_principal * );
+krb5_error_code krb5_unparse_name
+ (krb5_context,
+  krb5_const_principal,
+  char ** );
+krb5_error_code krb5_unparse_name_ext
+ (krb5_context,
+  krb5_const_principal,
+  char **,
+  unsigned int *);
+
+krb5_error_code krb5_set_principal_realm
+ (krb5_context, krb5_principal, const char *);
+
+krb5_boolean krb5_address_search
+ (krb5_context,
+  const krb5_address *,
+  krb5_address * const *);
+krb5_boolean krb5_address_compare
+ (krb5_context,
+  const krb5_address *,
+  const krb5_address *);
+int krb5_address_order
+ (krb5_context,
+  const krb5_address *,
+  const krb5_address *);
+krb5_boolean krb5_realm_compare
+ (krb5_context,
+  krb5_const_principal,
+  krb5_const_principal);
+krb5_boolean krb5_principal_compare
+ (krb5_context,
+  krb5_const_principal,
+  krb5_const_principal);
+krb5_error_code krb5_init_keyblock
+  (krb5_context, krb5_enctype enctype,
+  size_t length, krb5_keyblock **out);
+
+
+
+
+
+
+krb5_error_code krb5_copy_keyblock
+ (krb5_context,
+  const krb5_keyblock *,
+  krb5_keyblock **);
+krb5_error_code krb5_copy_keyblock_contents
+ (krb5_context,
+  const krb5_keyblock *,
+  krb5_keyblock *);
+krb5_error_code krb5_copy_creds
+ (krb5_context,
+  const krb5_creds *,
+  krb5_creds **);
+krb5_error_code krb5_copy_data
+ (krb5_context,
+  const krb5_data *,
+  krb5_data **);
+krb5_error_code krb5_copy_principal
+ (krb5_context,
+  krb5_const_principal,
+  krb5_principal *);
+
+krb5_error_code krb5_copy_addr
+ (krb5_context,
+  const krb5_address *,
+  krb5_address **);
+
+krb5_error_code krb5_copy_addresses
+ (krb5_context,
+  krb5_address * const *,
+  krb5_address ***);
+krb5_error_code krb5_copy_ticket
+ (krb5_context,
+  const krb5_ticket *,
+  krb5_ticket **);
+krb5_error_code krb5_copy_authdata
+ (krb5_context,
+  krb5_authdata * const *,
+  krb5_authdata ***);
+krb5_error_code krb5_copy_authenticator
+ (krb5_context,
+  const krb5_authenticator *,
+  krb5_authenticator **);
+krb5_error_code krb5_copy_checksum
+ (krb5_context,
+  const krb5_checksum *,
+  krb5_checksum **);
+
+void krb5_init_ets
+ (krb5_context);
+void krb5_free_ets
+ (krb5_context);
+krb5_error_code krb5_generate_subkey
+ (krb5_context,
+  const krb5_keyblock *, krb5_keyblock **);
+krb5_error_code krb5_generate_seq_number
+ (krb5_context,
+  const krb5_keyblock *, krb5_ui_4 *);
+
+krb5_error_code krb5_get_server_rcache
+ (krb5_context,
+  const krb5_data *, krb5_rcache *);
+krb5_error_code krb5_build_principal_ext
+ (krb5_context, krb5_principal *, unsigned int, const char *, ...);
+krb5_error_code krb5_build_principal
+ (krb5_context, krb5_principal *, unsigned int, const char *, ...);
+
+
+
+
+
+
+
+krb5_error_code krb5_425_conv_principal
+ (krb5_context,
+  const char *name,
+  const char *instance, const char *realm,
+  krb5_principal *princ);
+
+krb5_error_code krb5_524_conv_principal
+ (krb5_context context, krb5_const_principal princ,
+  char *name, char *inst, char *realm);
+
+struct credentials;
+int krb5_524_convert_creds
+ (krb5_context context, krb5_creds *v5creds,
+  struct credentials *v4creds);
+
+
+
+
+
+
+
+krb5_error_code krb5_kt_register
+ (krb5_context,
+  const struct _krb5_kt_ops * );
+
+
+krb5_error_code krb5_kt_resolve
+ (krb5_context,
+  const char *,
+  krb5_keytab * );
+krb5_error_code krb5_kt_default_name
+ (krb5_context,
+  char *,
+  int );
+krb5_error_code krb5_kt_default
+ (krb5_context,
+  krb5_keytab * );
+krb5_error_code krb5_free_keytab_entry_contents
+ (krb5_context,
+  krb5_keytab_entry * );
+
+
+krb5_error_code krb5_kt_free_entry
+ (krb5_context,
+  krb5_keytab_entry * );
+
+
+
+krb5_error_code krb5_kt_remove_entry
+ (krb5_context,
+  krb5_keytab,
+  krb5_keytab_entry * );
+krb5_error_code krb5_kt_add_entry
+ (krb5_context,
+  krb5_keytab,
+  krb5_keytab_entry * );
+krb5_error_code krb5_principal2salt
+ (krb5_context,
+  krb5_const_principal, krb5_data *);
+
+krb5_error_code krb5_principal2salt_norealm
+ (krb5_context,
+  krb5_const_principal, krb5_data *);
+
+
+
+
+krb5_error_code krb5_cc_resolve
+ (krb5_context,
+  const char *,
+  krb5_ccache * );
+const char * krb5_cc_default_name
+ (krb5_context);
+krb5_error_code krb5_cc_set_default_name
+ (krb5_context, const char *);
+krb5_error_code krb5_cc_default
+ (krb5_context,
+  krb5_ccache *);
+
+unsigned int krb5_get_notification_message
+ (void);
+
+
+krb5_error_code krb5_cc_copy_creds
+ (krb5_context context,
+   krb5_ccache incc,
+   krb5_ccache outcc);
+
+
+
+
+krb5_error_code krb5_check_transited_list
+ (krb5_context, const krb5_data *trans,
+  const krb5_data *realm1, const krb5_data *realm2);
+
+
+
+
+void krb5_free_realm_tree
+ (krb5_context,
+  krb5_principal *);
+
+
+
+void krb5_free_principal
+ (krb5_context, krb5_principal );
+void krb5_free_authenticator
+ (krb5_context, krb5_authenticator * );
+
+void krb5_free_authenticator_contents
+ (krb5_context, krb5_authenticator * );
+
+void krb5_free_addresses
+ (krb5_context, krb5_address ** );
+
+void krb5_free_address
+ (krb5_context, krb5_address * );
+
+void krb5_free_authdata
+ (krb5_context, krb5_authdata ** );
+
+void krb5_free_enc_tkt_part
+ (krb5_context, krb5_enc_tkt_part * );
+
+void krb5_free_ticket
+ (krb5_context, krb5_ticket * );
+
+void krb5_free_tickets
+ (krb5_context, krb5_ticket ** );
+void krb5_free_kdc_req
+ (krb5_context, krb5_kdc_req * );
+void krb5_free_kdc_rep
+ (krb5_context, krb5_kdc_rep * );
+void krb5_free_last_req
+ (krb5_context, krb5_last_req_entry ** );
+void krb5_free_enc_kdc_rep_part
+ (krb5_context, krb5_enc_kdc_rep_part * );
+
+void krb5_free_error
+ (krb5_context, krb5_error * );
+
+void krb5_free_ap_req
+ (krb5_context, krb5_ap_req * );
+void krb5_free_ap_rep
+ (krb5_context, krb5_ap_rep * );
+void krb5_free_cred
+ (krb5_context, krb5_cred *);
+
+void krb5_free_creds
+ (krb5_context, krb5_creds *);
+void krb5_free_cred_contents
+ (krb5_context, krb5_creds *);
+
+void krb5_free_cred_enc_part
+ (krb5_context, krb5_cred_enc_part *);
+
+void krb5_free_checksum
+ (krb5_context, krb5_checksum *);
+void krb5_free_checksum_contents
+ (krb5_context, krb5_checksum *);
+void krb5_free_keyblock
+ (krb5_context, krb5_keyblock *);
+void krb5_free_keyblock_contents
+ (krb5_context, krb5_keyblock *);
+
+void krb5_free_pa_data
+ (krb5_context, krb5_pa_data **);
+
+void krb5_free_ap_rep_enc_part
+ (krb5_context, krb5_ap_rep_enc_part *);
+
+void krb5_free_tkt_authent
+ (krb5_context, krb5_tkt_authent *);
+void krb5_free_pwd_data
+ (krb5_context, krb5_pwd_data *);
+void krb5_free_pwd_sequences
+ (krb5_context, passwd_phrase_element **);
+
+void krb5_free_data
+ (krb5_context, krb5_data *);
+void krb5_free_data_contents
+ (krb5_context, krb5_data *);
+void krb5_free_unparsed_name
+ (krb5_context, char *);
+void krb5_free_cksumtypes
+ (krb5_context, krb5_cksumtype *);
+
+
+krb5_error_code krb5_us_timeofday
+ (krb5_context,
+  krb5_int32 *,
+  krb5_int32 * );
+krb5_error_code krb5_timeofday
+ (krb5_context,
+  krb5_int32 * );
+
+krb5_error_code krb5_os_localaddr
+ (krb5_context,
+  krb5_address ***);
+krb5_error_code krb5_get_default_realm
+ (krb5_context,
+   char ** );
+krb5_error_code krb5_set_default_realm
+ (krb5_context,
+     const char * );
+void krb5_free_default_realm
+ (krb5_context,
+     char * );
+krb5_error_code krb5_sname_to_principal
+ (krb5_context,
+  const char *,
+     const char *,
+     krb5_int32,
+     krb5_principal *);
+krb5_error_code
+krb5_change_password
+ (krb5_context context, krb5_creds *creds, char *newpw,
+   int *result_code, krb5_data *result_code_string,
+   krb5_data *result_string);
+krb5_error_code
+krb5_set_password
+ (krb5_context context, krb5_creds *creds, char *newpw, krb5_principal change_password_for,
+   int *result_code, krb5_data *result_code_string, krb5_data *result_string);
+krb5_error_code
+krb5_set_password_using_ccache
+ (krb5_context context, krb5_ccache ccache, char *newpw, krb5_principal change_password_for,
+   int *result_code, krb5_data *result_code_string, krb5_data *result_string);
+
+
+krb5_error_code krb5_set_config_files
+ (krb5_context, const char **);
+
+krb5_error_code krb5_get_default_config_files
+ (char ***filenames);
+
+void krb5_free_config_files
+ (char **filenames);
+
+
+krb5_error_code
+krb5_get_profile
+ (krb5_context, struct _profile_t * *);
+
+
+krb5_error_code krb5_send_tgs
+ (krb5_context,
+  krb5_flags,
+  const krb5_ticket_times *,
+  const krb5_enctype *,
+  krb5_const_principal,
+  krb5_address * const *,
+  krb5_authdata * const *,
+  krb5_pa_data * const *,
+  const krb5_data *,
+  krb5_creds *,
+  krb5_response * );
+
+
+
+krb5_error_code krb5_get_in_tkt
+ (krb5_context,
+  krb5_flags,
+  krb5_address * const *,
+  krb5_enctype *,
+  krb5_preauthtype *,
+  krb5_error_code ( * )(krb5_context,
+     krb5_enctype,
+     krb5_data *,
+     krb5_const_pointer,
+     krb5_keyblock **),
+  krb5_const_pointer,
+  krb5_error_code ( * )(krb5_context,
+     const krb5_keyblock *,
+     krb5_const_pointer,
+     krb5_kdc_rep * ),
+  krb5_const_pointer,
+  krb5_creds *,
+  krb5_ccache,
+  krb5_kdc_rep ** );
+
+krb5_error_code krb5_get_in_tkt_with_password
+ (krb5_context,
+  krb5_flags,
+  krb5_address * const *,
+  krb5_enctype *,
+  krb5_preauthtype *,
+  const char *,
+  krb5_ccache,
+  krb5_creds *,
+  krb5_kdc_rep ** );
+
+krb5_error_code krb5_get_in_tkt_with_skey
+ (krb5_context,
+  krb5_flags,
+  krb5_address * const *,
+  krb5_enctype *,
+  krb5_preauthtype *,
+  const krb5_keyblock *,
+  krb5_ccache,
+  krb5_creds *,
+  krb5_kdc_rep ** );
+
+krb5_error_code krb5_get_in_tkt_with_keytab
+ (krb5_context,
+  krb5_flags,
+  krb5_address * const *,
+  krb5_enctype *,
+  krb5_preauthtype *,
+  krb5_keytab,
+  krb5_ccache,
+  krb5_creds *,
+  krb5_kdc_rep ** );
+
+
+
+krb5_error_code krb5_decode_kdc_rep
+ (krb5_context,
+  krb5_data *,
+  const krb5_keyblock *,
+  krb5_kdc_rep ** );
+
+
+krb5_error_code krb5_rd_req
+ (krb5_context,
+  krb5_auth_context *,
+  const krb5_data *,
+  krb5_const_principal,
+  krb5_keytab,
+  krb5_flags *,
+  krb5_ticket **);
+
+
+krb5_error_code krb5_rd_req_decoded
+ (krb5_context,
+  krb5_auth_context *,
+  const krb5_ap_req *,
+  krb5_const_principal,
+  krb5_keytab,
+  krb5_flags *,
+  krb5_ticket **);
+
+krb5_error_code krb5_rd_req_decoded_anyflag
+ (krb5_context,
+  krb5_auth_context *,
+  const krb5_ap_req *,
+  krb5_const_principal,
+  krb5_keytab,
+  krb5_flags *,
+  krb5_ticket **);
+
+
+krb5_error_code krb5_kt_read_service_key
+ (krb5_context,
+  krb5_pointer,
+  krb5_principal,
+  krb5_kvno,
+  krb5_enctype,
+  krb5_keyblock **);
+krb5_error_code krb5_mk_safe
+ (krb5_context,
+  krb5_auth_context,
+  const krb5_data *,
+  krb5_data *,
+  krb5_replay_data *);
+krb5_error_code krb5_mk_priv
+ (krb5_context,
+  krb5_auth_context,
+  const krb5_data *,
+  krb5_data *,
+  krb5_replay_data *);
+
+krb5_error_code krb5_cc_register
+ (krb5_context,
+  krb5_cc_ops *,
+  krb5_boolean );
+
+
+krb5_error_code krb5_sendauth
+ (krb5_context,
+  krb5_auth_context *,
+  krb5_pointer,
+  char *,
+  krb5_principal,
+  krb5_principal,
+  krb5_flags,
+  krb5_data *,
+  krb5_creds *,
+  krb5_ccache,
+  krb5_error **,
+  krb5_ap_rep_enc_part **,
+  krb5_creds **);
+
+krb5_error_code krb5_recvauth
+ (krb5_context,
+  krb5_auth_context *,
+  krb5_pointer,
+  char *,
+  krb5_principal,
+  krb5_int32,
+  krb5_keytab,
+  krb5_ticket **);
+krb5_error_code krb5_recvauth_version
+ (krb5_context,
+  krb5_auth_context *,
+  krb5_pointer,
+  krb5_principal,
+  krb5_int32,
+  krb5_keytab,
+  krb5_ticket **,
+  krb5_data *);
+
+
+krb5_error_code krb5_walk_realm_tree
+ (krb5_context,
+  const krb5_data *,
+  const krb5_data *,
+  krb5_principal **,
+  int);
+
+
+krb5_error_code krb5_mk_ncred
+ (krb5_context,
+  krb5_auth_context,
+  krb5_creds **,
+  krb5_data **,
+  krb5_replay_data *);
+
+krb5_error_code krb5_mk_1cred
+ (krb5_context,
+  krb5_auth_context,
+  krb5_creds *,
+  krb5_data **,
+  krb5_replay_data *);
+
+krb5_error_code krb5_rd_cred
+ (krb5_context,
+  krb5_auth_context,
+  krb5_data *,
+  krb5_creds ***,
+  krb5_replay_data *);
+
+krb5_error_code krb5_fwd_tgt_creds
+ (krb5_context,
+  krb5_auth_context,
+  char *,
+  krb5_principal,
+  krb5_principal,
+  krb5_ccache,
+  int forwardable,
+  krb5_data *);
+
+krb5_error_code krb5_auth_con_init
+ (krb5_context,
+  krb5_auth_context *);
+
+krb5_error_code krb5_auth_con_free
+ (krb5_context,
+  krb5_auth_context);
+
+krb5_error_code krb5_auth_con_setflags
+ (krb5_context,
+  krb5_auth_context,
+  krb5_int32);
+
+krb5_error_code krb5_auth_con_getflags
+ (krb5_context,
+  krb5_auth_context,
+  krb5_int32 *);
+
+krb5_error_code
+krb5_auth_con_set_checksum_func (krb5_context, krb5_auth_context,
+     krb5_mk_req_checksum_func, void *);
+
+krb5_error_code
+krb5_auth_con_get_checksum_func( krb5_context, krb5_auth_context,
+     krb5_mk_req_checksum_func *, void **);
+
+krb5_error_code krb5_auth_con_setaddrs
+ (krb5_context,
+  krb5_auth_context,
+  krb5_address *,
+  krb5_address *);
+
+krb5_error_code krb5_auth_con_getaddrs
+ (krb5_context,
+  krb5_auth_context,
+  krb5_address **,
+  krb5_address **);
+
+krb5_error_code krb5_auth_con_setports
+ (krb5_context,
+  krb5_auth_context,
+  krb5_address *,
+  krb5_address *);
+
+krb5_error_code krb5_auth_con_setuseruserkey
+ (krb5_context,
+  krb5_auth_context,
+  krb5_keyblock *);
+
+krb5_error_code krb5_auth_con_getkey
+ (krb5_context,
+  krb5_auth_context,
+  krb5_keyblock **);
+
+krb5_error_code krb5_auth_con_getsendsubkey(
+    krb5_context, krb5_auth_context, krb5_keyblock **);
+
+krb5_error_code krb5_auth_con_getrecvsubkey(
+    krb5_context, krb5_auth_context, krb5_keyblock **);
+
+krb5_error_code krb5_auth_con_setsendsubkey(
+    krb5_context, krb5_auth_context, krb5_keyblock *);
+
+krb5_error_code krb5_auth_con_setrecvsubkey(
+    krb5_context, krb5_auth_context, krb5_keyblock *);
+
+
+krb5_error_code krb5_auth_con_getlocalsubkey
+ (krb5_context,
+  krb5_auth_context,
+  krb5_keyblock **);
+
+krb5_error_code krb5_auth_con_getremotesubkey
+ (krb5_context,
+  krb5_auth_context,
+  krb5_keyblock **);
+
+
+
+krb5_error_code krb5_auth_con_set_req_cksumtype
+ (krb5_context,
+  krb5_auth_context,
+  krb5_cksumtype);
+
+krb5_error_code krb5_auth_con_set_safe_cksumtype
+ (krb5_context,
+  krb5_auth_context,
+  krb5_cksumtype);
+
+
+krb5_error_code krb5_auth_con_getlocalseqnumber
+ (krb5_context,
+  krb5_auth_context,
+  krb5_int32 *);
+
+krb5_error_code krb5_auth_con_getremoteseqnumber
+ (krb5_context,
+  krb5_auth_context,
+  krb5_int32 *);
+
+
+krb5_error_code krb5_auth_con_initivector
+ (krb5_context,
+  krb5_auth_context);
+
+
+
+krb5_error_code krb5_auth_con_setivector
+ (krb5_context,
+  krb5_auth_context,
+  krb5_pointer);
+
+krb5_error_code krb5_auth_con_getivector
+ (krb5_context,
+  krb5_auth_context,
+  krb5_pointer *);
+
+
+krb5_error_code krb5_auth_con_setrcache
+ (krb5_context,
+  krb5_auth_context,
+  krb5_rcache);
+
+krb5_error_code krb5_auth_con_getrcache
+ (krb5_context,
+  krb5_auth_context,
+  krb5_rcache *);
+
+
+krb5_error_code krb5_auth_con_setpermetypes
+ (krb5_context,
+     krb5_auth_context,
+     const krb5_enctype *);
+
+krb5_error_code krb5_auth_con_getpermetypes
+ (krb5_context,
+     krb5_auth_context,
+     krb5_enctype **);
+
+
+krb5_error_code krb5_auth_con_getauthenticator
+ (krb5_context,
+  krb5_auth_context,
+  krb5_authenticator **);
+krb5_error_code krb5_read_message (krb5_context, krb5_pointer, krb5_data *);
+krb5_error_code krb5_write_message (krb5_context, krb5_pointer, krb5_data *);
+int krb5_net_read (krb5_context, int , char *, int);
+int krb5_net_write (krb5_context, int , const char *, int);
+
+
+krb5_error_code krb5_read_password
+ (krb5_context,
+  const char *,
+  const char *,
+  char *,
+  unsigned int * );
+krb5_error_code krb5_aname_to_localname
+ (krb5_context,
+  krb5_const_principal,
+  int,
+  char * );
+krb5_error_code krb5_get_host_realm
+ (krb5_context,
+  const char *,
+  char *** );
+krb5_error_code krb5_free_host_realm
+ (krb5_context,
+  char * const * );
+
+krb5_error_code krb5_get_realm_domain
+ (krb5_context,
+  const char *,
+  char ** );
+
+krb5_boolean krb5_kuserok
+ (krb5_context,
+  krb5_principal, const char *);
+krb5_error_code krb5_auth_con_genaddrs
+ (krb5_context,
+  krb5_auth_context,
+  int, int);
+
+krb5_error_code krb5_gen_portaddr
+ (krb5_context,
+  const krb5_address *,
+  krb5_const_pointer,
+  krb5_address **);
+krb5_error_code krb5_gen_replay_name
+ (krb5_context,
+  const krb5_address *,
+  const char *,
+  char **);
+krb5_error_code krb5_make_fulladdr
+ (krb5_context,
+  krb5_address *,
+  krb5_address *,
+  krb5_address *);
+
+
+krb5_error_code krb5_set_real_time
+ (krb5_context, krb5_int32, krb5_int32);
+
+
+krb5_error_code krb5_set_debugging_time
+ (krb5_context, krb5_int32, krb5_int32);
+krb5_error_code krb5_use_natural_time
+ (krb5_context);
+
+krb5_error_code krb5_get_time_offsets
+ (krb5_context, krb5_int32 *, krb5_int32 *);
+
+krb5_error_code krb5_set_time_offsets
+ (krb5_context, krb5_int32, krb5_int32);
+
+
+
+krb5_error_code krb5_string_to_enctype
+ (char *, krb5_enctype *);
+krb5_error_code krb5_string_to_salttype
+ (char *, krb5_int32 *);
+krb5_error_code krb5_string_to_cksumtype
+ (char *, krb5_cksumtype *);
+krb5_error_code krb5_string_to_timestamp
+ (char *, krb5_timestamp *);
+krb5_error_code krb5_string_to_deltat
+ (char *, krb5_deltat *);
+krb5_error_code krb5_enctype_to_string
+ (krb5_enctype, char *, size_t);
+krb5_error_code krb5_salttype_to_string
+ (krb5_int32, char *, size_t);
+krb5_error_code krb5_cksumtype_to_string
+ (krb5_cksumtype, char *, size_t);
+krb5_error_code krb5_timestamp_to_string
+ (krb5_timestamp, char *, size_t);
+krb5_error_code krb5_timestamp_to_sfstring
+ (krb5_timestamp, char *, size_t, char *);
+krb5_error_code krb5_deltat_to_string
+ (krb5_deltat, char *, size_t);
+typedef struct _krb5_prompt {
+    char *prompt;
+    int hidden;
+    krb5_data *reply;
+} krb5_prompt;
+
+typedef krb5_error_code ( *krb5_prompter_fct)(krb5_context context,
+          void *data,
+          const char *name,
+          const char *banner,
+          int num_prompts,
+          krb5_prompt prompts[]);
+
+
+krb5_error_code
+krb5_prompter_posix (krb5_context context,
+  void *data,
+  const char *name,
+  const char *banner,
+  int num_prompts,
+  krb5_prompt prompts[]);
+
+typedef struct _krb5_get_init_creds_opt {
+    krb5_flags flags;
+    krb5_deltat tkt_life;
+    krb5_deltat renew_life;
+    int forwardable;
+    int proxiable;
+    krb5_enctype *etype_list;
+    int etype_list_length;
+    krb5_address **address_list;
+    krb5_preauthtype *preauth_list;
+    int preauth_list_length;
+    krb5_data *salt;
+} krb5_get_init_creds_opt;
+void
+krb5_get_init_creds_opt_init
+(krb5_get_init_creds_opt *opt);
+
+void
+krb5_get_init_creds_opt_set_tkt_life
+(krb5_get_init_creds_opt *opt,
+  krb5_deltat tkt_life);
+
+void
+krb5_get_init_creds_opt_set_renew_life
+(krb5_get_init_creds_opt *opt,
+  krb5_deltat renew_life);
+
+void
+krb5_get_init_creds_opt_set_forwardable
+(krb5_get_init_creds_opt *opt,
+  int forwardable);
+
+void
+krb5_get_init_creds_opt_set_proxiable
+(krb5_get_init_creds_opt *opt,
+  int proxiable);
+
+void
+krb5_get_init_creds_opt_set_etype_list
+(krb5_get_init_creds_opt *opt,
+  krb5_enctype *etype_list,
+  int etype_list_length);
+
+void
+krb5_get_init_creds_opt_set_address_list
+(krb5_get_init_creds_opt *opt,
+  krb5_address **addresses);
+
+void
+krb5_get_init_creds_opt_set_preauth_list
+(krb5_get_init_creds_opt *opt,
+  krb5_preauthtype *preauth_list,
+  int preauth_list_length);
+
+void
+krb5_get_init_creds_opt_set_salt
+(krb5_get_init_creds_opt *opt,
+  krb5_data *salt);
+
+
+
+krb5_error_code
+krb5_get_init_creds_password
+(krb5_context context,
+  krb5_creds *creds,
+  krb5_principal client,
+  char *password,
+  krb5_prompter_fct prompter,
+  void *data,
+  krb5_deltat start_time,
+  char *in_tkt_service,
+  krb5_get_init_creds_opt *k5_gic_options);
+
+krb5_error_code
+krb5_get_init_creds_keytab
+(krb5_context context,
+  krb5_creds *creds,
+  krb5_principal client,
+  krb5_keytab arg_keytab,
+  krb5_deltat start_time,
+  char *in_tkt_service,
+  krb5_get_init_creds_opt *k5_gic_options);
+
+typedef struct _krb5_verify_init_creds_opt {
+    krb5_flags flags;
+    int ap_req_nofail;
+} krb5_verify_init_creds_opt;
+
+
+
+void
+krb5_verify_init_creds_opt_init
+(krb5_verify_init_creds_opt *k5_vic_options);
+void
+krb5_verify_init_creds_opt_set_ap_req_nofail
+(krb5_verify_init_creds_opt *k5_vic_options,
+  int ap_req_nofail);
+
+krb5_error_code
+krb5_verify_init_creds
+(krb5_context context,
+  krb5_creds *creds,
+  krb5_principal ap_req_server,
+  krb5_keytab ap_req_keytab,
+  krb5_ccache *ccache,
+  krb5_verify_init_creds_opt *k5_vic_options);
+
+krb5_error_code
+krb5_get_validated_creds
+(krb5_context context,
+  krb5_creds *creds,
+  krb5_principal client,
+  krb5_ccache ccache,
+  char *in_tkt_service);
+
+krb5_error_code
+krb5_get_renewed_creds
+(krb5_context context,
+  krb5_creds *creds,
+  krb5_principal client,
+  krb5_ccache ccache,
+  char *in_tkt_service);
+
+krb5_error_code
+krb5_decode_ticket
+(const krb5_data *code,
+  krb5_ticket **rep);
+
+void
+krb5_appdefault_string
+(krb5_context context,
+  const char *appname,
+         const krb5_data *realm,
+   const char *option,
+  const char *default_value,
+  char ** ret_value);
+
+void
+krb5_appdefault_boolean
+(krb5_context context,
+  const char *appname,
+         const krb5_data *realm,
+   const char *option,
+  int default_value,
+  int *ret_value);
+
+
+
+
+
+
+krb5_error_code krb5_realm_iterator_create
+ (krb5_context context, void **iter_p);
+
+krb5_error_code krb5_realm_iterator
+ (krb5_context context, void **iter_p, char **ret_realm);
+
+void krb5_realm_iterator_free
+ (krb5_context context, void **iter_p);
+
+void krb5_free_realm_string
+ (krb5_context context, char *str);
+typedef krb5_int32 krb5_prompt_type;
+
+krb5_prompt_type* krb5_get_prompt_types
+ (krb5_context context);
+
+
+
+
+
+
+
+typedef __builtin_va_list __gnuc_va_list;
+typedef __gnuc_va_list va_list;
+
+
+typedef long errcode_t;
+typedef void (*et_old_error_hook_func) (const char *, errcode_t,
+     const char *, va_list ap);
+
+struct error_table {
+              char const * const * msgs;
+        long base;
+ unsigned int n_msgs;
+};
+
+
+
+
+
+
+extern void com_err
+ (const char *, errcode_t, const char *, ...);
+extern void com_err_va
+ (const char *whoami, errcode_t code, const char *fmt,
+  va_list ap);
+extern const char * error_message
+ (errcode_t)
+                                   ;
+extern errcode_t add_error_table
+ ( const struct error_table *)
+                                   ;
+extern errcode_t remove_error_table
+ (const struct error_table *)
+                                   ;
+extern et_old_error_hook_func set_com_err_hook (et_old_error_hook_func);
+extern et_old_error_hook_func reset_com_err_hook (void);
+extern const struct error_table et_krb5_error_table;
+
+
+
+extern void initialize_krb5_error_table (void) ;
+extern const struct error_table et_kdb5_error_table;
+
+
+
+extern void initialize_kdb5_error_table (void) ;
+extern const struct error_table et_kv5m_error_table;
+
+
+
+extern void initialize_kv5m_error_table (void) ;
+extern const struct error_table et_k524_error_table;
+
+
+
+extern void initialize_k524_error_table (void) ;
+extern const struct error_table et_asn1_error_table;
+
+
+
+extern void initialize_asn1_error_table (void) ;
+typedef struct _profile_t *profile_t;
+typedef char* profile_filespec_t;
+typedef char* profile_filespec_list_t;
+typedef const char * const_profile_filespec_t;
+typedef const char * const_profile_filespec_list_t;
+
+long profile_init
+ (const_profile_filespec_t *files, profile_t *ret_profile);
+
+long profile_init_path
+ (const_profile_filespec_list_t filelist, profile_t *ret_profile);
+
+long profile_flush
+ (profile_t profile);
+long profile_flush_to_file
+ (profile_t profile, const_profile_filespec_t outfile);
+long profile_flush_to_buffer
+ (profile_t profile, char **bufp);
+void profile_free_buffer
+ (profile_t profile, char *buf);
+
+long profile_is_writable
+ (profile_t profile, int *writable);
+long profile_is_modified
+ (profile_t profile, int *modified);
+
+void profile_abandon
+ (profile_t profile);
+
+void profile_release
+ (profile_t profile);
+
+long profile_get_values
+ (profile_t profile, const char *const *names, char ***ret_values);
+
+void profile_free_list
+ (char **list);
+
+long profile_get_string
+ (profile_t profile, const char *name, const char *subname,
+   const char *subsubname, const char *def_val,
+   char **ret_string);
+long profile_get_integer
+ (profile_t profile, const char *name, const char *subname,
+   const char *subsubname, int def_val,
+   int *ret_default);
+
+long profile_get_boolean
+ (profile_t profile, const char *name, const char *subname,
+   const char *subsubname, int def_val,
+   int *ret_default);
+
+long profile_get_relation_names
+ (profile_t profile, const char **names, char ***ret_names);
+
+long profile_get_subsection_names
+ (profile_t profile, const char **names, char ***ret_names);
+
+long profile_iterator_create
+ (profile_t profile, const char *const *names,
+     int flags, void **ret_iter);
+
+void profile_iterator_free
+ (void **iter_p);
+
+long profile_iterator
+ (void **iter_p, char **ret_name, char **ret_value);
+
+void profile_release_string (char *str);
+
+long profile_update_relation
+ (profile_t profile, const char **names,
+     const char *old_value, const char *new_value);
+
+long profile_clear_relation
+ (profile_t profile, const char **names);
+
+long profile_rename_section
+ (profile_t profile, const char **names,
+     const char *new_name);
+
+long profile_add_relation
+ (profile_t profile, const char **names,
+     const char *new_value);
+extern const struct error_table et_prof_error_table;
+
+
+
+extern void initialize_prof_error_table (void) ;
+
+
+
+
+
+
+
+
+
+
+struct iovec
+  {
+    void *iov_base;
+    size_t iov_len;
+  };
+extern ssize_t readv (int __fd, const struct iovec *__iovec, int __count)
+  ;
+extern ssize_t writev (int __fd, const struct iovec *__iovec, int __count)
+  ;
+extern ssize_t preadv (int __fd, const struct iovec *__iovec, int __count,
+         __off_t __offset) ;
+extern ssize_t pwritev (int __fd, const struct iovec *__iovec, int __count,
+   __off_t __offset) ;
+
+
+
+
+
+
+
+typedef __socklen_t socklen_t;
+
+
+
+
+enum __socket_type
+{
+  SOCK_STREAM = 1,
+
+
+  SOCK_DGRAM = 2,
+
+
+  SOCK_RAW = 3,
+
+  SOCK_RDM = 4,
+
+  SOCK_SEQPACKET = 5,
+
+
+  SOCK_DCCP = 6,
+
+  SOCK_PACKET = 10,
+
+
+
+
+
+
+
+  SOCK_CLOEXEC = 02000000,
+
+
+  SOCK_NONBLOCK = 00004000
+
+
+};
+typedef unsigned short int sa_family_t;
+
+
+struct sockaddr
+  {
+    sa_family_t sa_family;
+    char sa_data[14];
+  };
+struct sockaddr_storage
+  {
+    sa_family_t ss_family;
+    unsigned long int __ss_align;
+    char __ss_padding[(128 - (2 * sizeof (unsigned long int)))];
+  };
+
+
+
+enum
+  {
+    MSG_OOB = 0x01,
+
+    MSG_PEEK = 0x02,
+
+    MSG_DONTROUTE = 0x04,
+
+
+
+
+
+
+    MSG_CTRUNC = 0x08,
+
+    MSG_PROXY = 0x10,
+
+    MSG_TRUNC = 0x20,
+
+    MSG_DONTWAIT = 0x40,
+
+    MSG_EOR = 0x80,
+
+    MSG_WAITALL = 0x100,
+
+    MSG_FIN = 0x200,
+
+    MSG_SYN = 0x400,
+
+    MSG_CONFIRM = 0x800,
+
+    MSG_RST = 0x1000,
+
+    MSG_ERRQUEUE = 0x2000,
+
+    MSG_NOSIGNAL = 0x4000,
+
+    MSG_MORE = 0x8000,
+
+    MSG_WAITFORONE = 0x10000,
+
+    MSG_FASTOPEN = 0x20000000,
+
+
+    MSG_CMSG_CLOEXEC = 0x40000000
+
+
+
+  };
+
+
+
+
+struct msghdr
+  {
+    void *msg_name;
+    socklen_t msg_namelen;
+
+    struct iovec *msg_iov;
+    size_t msg_iovlen;
+
+    void *msg_control;
+    size_t msg_controllen;
+
+
+
+
+    int msg_flags;
+  };
+
+
+struct cmsghdr
+  {
+    size_t cmsg_len;
+
+
+
+
+    int cmsg_level;
+    int cmsg_type;
+
+    __extension__ unsigned char __cmsg_data [];
+
+  };
+extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr,
+          struct cmsghdr *__cmsg) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern __inline __attribute__ ((__gnu_inline__)) struct cmsghdr *
+__attribute__ ((__nothrow__ , __leaf__)) __cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)
+{
+  if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr))
+
+    return (struct cmsghdr *) 0;
+
+  __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg
+          + (((__cmsg->cmsg_len) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1)));
+  if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control
+     + __mhdr->msg_controllen)
+      || ((unsigned char *) __cmsg + (((__cmsg->cmsg_len) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1))
+   > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen)))
+
+    return (struct cmsghdr *) 0;
+  return __cmsg;
+}
+
+
+
+
+enum
+  {
+    SCM_RIGHTS = 0x01
+
+
+
+
+
+  };
+
+
+
+struct linger
+  {
+    int l_onoff;
+    int l_linger;
+  };
+
+
+
+
+struct osockaddr
+  {
+    unsigned short int sa_family;
+    unsigned char sa_data[14];
+  };
+
+
+
+
+enum
+{
+  SHUT_RD = 0,
+
+  SHUT_WR,
+
+  SHUT_RDWR
+
+};
+extern int socket (int __domain, int __type, int __protocol) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern int socketpair (int __domain, int __type, int __protocol,
+         int __fds[2]) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int bind (int __fd, const struct sockaddr * __addr, socklen_t __len)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int getsockname (int __fd, struct sockaddr *__restrict __addr,
+   socklen_t *__restrict __len) __attribute__ ((__nothrow__ , __leaf__));
+extern int connect (int __fd, const struct sockaddr * __addr, socklen_t __len);
+
+
+
+extern int getpeername (int __fd, struct sockaddr *__restrict __addr,
+   socklen_t *__restrict __len) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+extern ssize_t send (int __fd, const void *__buf, size_t __n, int __flags);
+
+
+
+
+
+
+extern ssize_t recv (int __fd, void *__buf, size_t __n, int __flags);
+
+
+
+
+
+
+extern ssize_t sendto (int __fd, const void *__buf, size_t __n,
+         int __flags, const struct sockaddr * __addr,
+         socklen_t __addr_len);
+extern ssize_t recvfrom (int __fd, void *__restrict __buf, size_t __n,
+    int __flags, struct sockaddr *__restrict __addr,
+    socklen_t *__restrict __addr_len);
+
+
+
+
+
+
+
+extern ssize_t sendmsg (int __fd, const struct msghdr *__message,
+   int __flags);
+extern ssize_t recvmsg (int __fd, struct msghdr *__message, int __flags);
+extern int getsockopt (int __fd, int __level, int __optname,
+         void *__restrict __optval,
+         socklen_t *__restrict __optlen) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern int setsockopt (int __fd, int __level, int __optname,
+         const void *__optval, socklen_t __optlen) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern int listen (int __fd, int __n) __attribute__ ((__nothrow__ , __leaf__));
+extern int accept (int __fd, struct sockaddr *__restrict __addr,
+     socklen_t *__restrict __addr_len);
+extern int shutdown (int __fd, int __how) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern int sockatmark (int __fd) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+extern int isfdtype (int __fd, int __fdtype) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+enum
+  {
+    IPPROTO_IP = 0,
+
+    IPPROTO_HOPOPTS = 0,
+
+    IPPROTO_ICMP = 1,
+
+    IPPROTO_IGMP = 2,
+
+    IPPROTO_IPIP = 4,
+
+    IPPROTO_TCP = 6,
+
+    IPPROTO_EGP = 8,
+
+    IPPROTO_PUP = 12,
+
+    IPPROTO_UDP = 17,
+
+    IPPROTO_IDP = 22,
+
+    IPPROTO_TP = 29,
+
+    IPPROTO_DCCP = 33,
+
+    IPPROTO_IPV6 = 41,
+
+    IPPROTO_ROUTING = 43,
+
+    IPPROTO_FRAGMENT = 44,
+
+    IPPROTO_RSVP = 46,
+
+    IPPROTO_GRE = 47,
+
+    IPPROTO_ESP = 50,
+
+    IPPROTO_AH = 51,
+
+    IPPROTO_ICMPV6 = 58,
+
+    IPPROTO_NONE = 59,
+
+    IPPROTO_DSTOPTS = 60,
+
+    IPPROTO_MTP = 92,
+
+    IPPROTO_ENCAP = 98,
+
+    IPPROTO_PIM = 103,
+
+    IPPROTO_COMP = 108,
+
+    IPPROTO_SCTP = 132,
+
+    IPPROTO_UDPLITE = 136,
+
+    IPPROTO_RAW = 255,
+
+    IPPROTO_MAX
+  };
+
+
+
+typedef uint16_t in_port_t;
+
+
+enum
+  {
+    IPPORT_ECHO = 7,
+    IPPORT_DISCARD = 9,
+    IPPORT_SYSTAT = 11,
+    IPPORT_DAYTIME = 13,
+    IPPORT_NETSTAT = 15,
+    IPPORT_FTP = 21,
+    IPPORT_TELNET = 23,
+    IPPORT_SMTP = 25,
+    IPPORT_TIMESERVER = 37,
+    IPPORT_NAMESERVER = 42,
+    IPPORT_WHOIS = 43,
+    IPPORT_MTP = 57,
+
+    IPPORT_TFTP = 69,
+    IPPORT_RJE = 77,
+    IPPORT_FINGER = 79,
+    IPPORT_TTYLINK = 87,
+    IPPORT_SUPDUP = 95,
+
+
+    IPPORT_EXECSERVER = 512,
+    IPPORT_LOGINSERVER = 513,
+    IPPORT_CMDSERVER = 514,
+    IPPORT_EFSSERVER = 520,
+
+
+    IPPORT_BIFFUDP = 512,
+    IPPORT_WHOSERVER = 513,
+    IPPORT_ROUTESERVER = 520,
+
+
+    IPPORT_RESERVED = 1024,
+
+
+    IPPORT_USERRESERVED = 5000
+  };
+
+
+
+typedef uint32_t in_addr_t;
+struct in_addr
+  {
+    in_addr_t s_addr;
+  };
+struct in6_addr
+  {
+    union
+      {
+ uint8_t __u6_addr8[16];
+
+ uint16_t __u6_addr16[8];
+ uint32_t __u6_addr32[4];
+
+      } __in6_u;
+
+
+
+
+
+  };
+
+extern const struct in6_addr in6addr_any;
+extern const struct in6_addr in6addr_loopback;
+struct sockaddr_in
+  {
+    sa_family_t sin_family;
+    in_port_t sin_port;
+    struct in_addr sin_addr;
+
+
+    unsigned char sin_zero[sizeof (struct sockaddr) -
+      (sizeof (unsigned short int)) -
+      sizeof (in_port_t) -
+      sizeof (struct in_addr)];
+  };
+
+
+struct sockaddr_in6
+  {
+    sa_family_t sin6_family;
+    in_port_t sin6_port;
+    uint32_t sin6_flowinfo;
+    struct in6_addr sin6_addr;
+    uint32_t sin6_scope_id;
+  };
+
+
+
+
+struct ip_mreq
+  {
+
+    struct in_addr imr_multiaddr;
+
+
+    struct in_addr imr_interface;
+  };
+
+struct ip_mreq_source
+  {
+
+    struct in_addr imr_multiaddr;
+
+
+    struct in_addr imr_interface;
+
+
+    struct in_addr imr_sourceaddr;
+  };
+
+
+
+
+struct ipv6_mreq
+  {
+
+    struct in6_addr ipv6mr_multiaddr;
+
+
+    unsigned int ipv6mr_interface;
+  };
+
+
+
+
+struct group_req
+  {
+
+    uint32_t gr_interface;
+
+
+    struct sockaddr_storage gr_group;
+  };
+
+struct group_source_req
+  {
+
+    uint32_t gsr_interface;
+
+
+    struct sockaddr_storage gsr_group;
+
+
+    struct sockaddr_storage gsr_source;
+  };
+
+
+
+struct ip_msfilter
+  {
+
+    struct in_addr imsf_multiaddr;
+
+
+    struct in_addr imsf_interface;
+
+
+    uint32_t imsf_fmode;
+
+
+    uint32_t imsf_numsrc;
+
+    struct in_addr imsf_slist[1];
+  };
+
+
+
+
+
+struct group_filter
+  {
+
+    uint32_t gf_interface;
+
+
+    struct sockaddr_storage gf_group;
+
+
+    uint32_t gf_fmode;
+
+
+    uint32_t gf_numsrc;
+
+    struct sockaddr_storage gf_slist[1];
+};
+struct ip_opts
+  {
+    struct in_addr ip_dst;
+    char ip_opts[40];
+  };
+
+
+struct ip_mreqn
+  {
+    struct in_addr imr_multiaddr;
+    struct in_addr imr_address;
+    int imr_ifindex;
+  };
+
+
+struct in_pktinfo
+  {
+    int ipi_ifindex;
+    struct in_addr ipi_spec_dst;
+    struct in_addr ipi_addr;
+  };
+extern uint32_t ntohl (uint32_t __netlong) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+extern uint16_t ntohs (uint16_t __netshort)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+extern uint32_t htonl (uint32_t __hostlong)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+extern uint16_t htons (uint16_t __hostshort)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+
+
+
+
+extern int bindresvport (int __sockfd, struct sockaddr_in *__sock_in) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int bindresvport6 (int __sockfd, struct sockaddr_in6 *__sock_in)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern in_addr_t inet_addr (const char *__cp) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern in_addr_t inet_lnaof (struct in_addr __in) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern struct in_addr inet_makeaddr (in_addr_t __net, in_addr_t __host)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern in_addr_t inet_netof (struct in_addr __in) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern in_addr_t inet_network (const char *__cp) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern char *inet_ntoa (struct in_addr __in) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern int inet_pton (int __af, const char *__restrict __cp,
+        void *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern const char *inet_ntop (int __af, const void *__restrict __cp,
+         char *__restrict __buf, socklen_t __len)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+extern int inet_aton (const char *__cp, struct in_addr *__inp) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern char *inet_neta (in_addr_t __net, char *__buf, size_t __len) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern char *inet_net_ntop (int __af, const void *__cp, int __bits,
+       char *__buf, size_t __len) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern int inet_net_pton (int __af, const char *__cp,
+     void *__buf, size_t __len) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern unsigned int inet_nsap_addr (const char *__cp,
+        unsigned char *__buf, int __len) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern char *inet_nsap_ntoa (int __len, const unsigned char *__cp,
+        char *__buf) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+struct rpcent
+{
+  char *r_name;
+  char **r_aliases;
+  int r_number;
+};
+
+extern void setrpcent (int __stayopen) __attribute__ ((__nothrow__ , __leaf__));
+extern void endrpcent (void) __attribute__ ((__nothrow__ , __leaf__));
+extern struct rpcent *getrpcbyname (const char *__name) __attribute__ ((__nothrow__ , __leaf__));
+extern struct rpcent *getrpcbynumber (int __number) __attribute__ ((__nothrow__ , __leaf__));
+extern struct rpcent *getrpcent (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int getrpcbyname_r (const char *__name, struct rpcent *__result_buf,
+      char *__buffer, size_t __buflen,
+      struct rpcent **__result) __attribute__ ((__nothrow__ , __leaf__));
+
+extern int getrpcbynumber_r (int __number, struct rpcent *__result_buf,
+        char *__buffer, size_t __buflen,
+        struct rpcent **__result) __attribute__ ((__nothrow__ , __leaf__));
+
+extern int getrpcent_r (struct rpcent *__result_buf, char *__buffer,
+   size_t __buflen, struct rpcent **__result) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+struct netent
+{
+  char *n_name;
+  char **n_aliases;
+  int n_addrtype;
+  uint32_t n_net;
+};
+
+
+
+
+
+
+
+
+extern int *__h_errno_location (void) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+extern void herror (const char *__str) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern const char *hstrerror (int __err_num) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+struct hostent
+{
+  char *h_name;
+  char **h_aliases;
+  int h_addrtype;
+  int h_length;
+  char **h_addr_list;
+
+
+
+};
+
+
+
+
+
+
+extern void sethostent (int __stay_open);
+
+
+
+
+
+extern void endhostent (void);
+
+
+
+
+
+
+extern struct hostent *gethostent (void);
+
+
+
+
+
+
+extern struct hostent *gethostbyaddr (const void *__addr, __socklen_t __len,
+          int __type);
+
+
+
+
+
+extern struct hostent *gethostbyname (const char *__name);
+extern struct hostent *gethostbyname2 (const char *__name, int __af);
+extern int gethostent_r (struct hostent *__restrict __result_buf,
+    char *__restrict __buf, size_t __buflen,
+    struct hostent **__restrict __result,
+    int *__restrict __h_errnop);
+
+extern int gethostbyaddr_r (const void *__restrict __addr, __socklen_t __len,
+       int __type,
+       struct hostent *__restrict __result_buf,
+       char *__restrict __buf, size_t __buflen,
+       struct hostent **__restrict __result,
+       int *__restrict __h_errnop);
+
+extern int gethostbyname_r (const char *__restrict __name,
+       struct hostent *__restrict __result_buf,
+       char *__restrict __buf, size_t __buflen,
+       struct hostent **__restrict __result,
+       int *__restrict __h_errnop);
+
+extern int gethostbyname2_r (const char *__restrict __name, int __af,
+        struct hostent *__restrict __result_buf,
+        char *__restrict __buf, size_t __buflen,
+        struct hostent **__restrict __result,
+        int *__restrict __h_errnop);
+extern void setnetent (int __stay_open);
+
+
+
+
+
+extern void endnetent (void);
+
+
+
+
+
+
+extern struct netent *getnetent (void);
+
+
+
+
+
+
+extern struct netent *getnetbyaddr (uint32_t __net, int __type);
+
+
+
+
+
+extern struct netent *getnetbyname (const char *__name);
+extern int getnetent_r (struct netent *__restrict __result_buf,
+   char *__restrict __buf, size_t __buflen,
+   struct netent **__restrict __result,
+   int *__restrict __h_errnop);
+
+extern int getnetbyaddr_r (uint32_t __net, int __type,
+      struct netent *__restrict __result_buf,
+      char *__restrict __buf, size_t __buflen,
+      struct netent **__restrict __result,
+      int *__restrict __h_errnop);
+
+extern int getnetbyname_r (const char *__restrict __name,
+      struct netent *__restrict __result_buf,
+      char *__restrict __buf, size_t __buflen,
+      struct netent **__restrict __result,
+      int *__restrict __h_errnop);
+
+
+
+
+struct servent
+{
+  char *s_name;
+  char **s_aliases;
+  int s_port;
+  char *s_proto;
+};
+
+
+
+
+
+
+extern void setservent (int __stay_open);
+
+
+
+
+
+extern void endservent (void);
+
+
+
+
+
+
+extern struct servent *getservent (void);
+
+
+
+
+
+
+extern struct servent *getservbyname (const char *__name, const char *__proto);
+
+
+
+
+
+
+extern struct servent *getservbyport (int __port, const char *__proto);
+extern int getservent_r (struct servent *__restrict __result_buf,
+    char *__restrict __buf, size_t __buflen,
+    struct servent **__restrict __result);
+
+extern int getservbyname_r (const char *__restrict __name,
+       const char *__restrict __proto,
+       struct servent *__restrict __result_buf,
+       char *__restrict __buf, size_t __buflen,
+       struct servent **__restrict __result);
+
+extern int getservbyport_r (int __port, const char *__restrict __proto,
+       struct servent *__restrict __result_buf,
+       char *__restrict __buf, size_t __buflen,
+       struct servent **__restrict __result);
+
+
+
+
+struct protoent
+{
+  char *p_name;
+  char **p_aliases;
+  int p_proto;
+};
+
+
+
+
+
+
+extern void setprotoent (int __stay_open);
+
+
+
+
+
+extern void endprotoent (void);
+
+
+
+
+
+
+extern struct protoent *getprotoent (void);
+
+
+
+
+
+extern struct protoent *getprotobyname (const char *__name);
+
+
+
+
+
+extern struct protoent *getprotobynumber (int __proto);
+extern int getprotoent_r (struct protoent *__restrict __result_buf,
+     char *__restrict __buf, size_t __buflen,
+     struct protoent **__restrict __result);
+
+extern int getprotobyname_r (const char *__restrict __name,
+        struct protoent *__restrict __result_buf,
+        char *__restrict __buf, size_t __buflen,
+        struct protoent **__restrict __result);
+
+extern int getprotobynumber_r (int __proto,
+          struct protoent *__restrict __result_buf,
+          char *__restrict __buf, size_t __buflen,
+          struct protoent **__restrict __result);
+extern int setnetgrent (const char *__netgroup);
+
+
+
+
+
+
+
+extern void endnetgrent (void);
+extern int getnetgrent (char **__restrict __hostp,
+   char **__restrict __userp,
+   char **__restrict __domainp);
+extern int innetgr (const char *__netgroup, const char *__host,
+      const char *__user, const char *__domain);
+
+
+
+
+
+
+
+extern int getnetgrent_r (char **__restrict __hostp,
+     char **__restrict __userp,
+     char **__restrict __domainp,
+     char *__restrict __buffer, size_t __buflen);
+extern int rcmd (char **__restrict __ahost, unsigned short int __rport,
+   const char *__restrict __locuser,
+   const char *__restrict __remuser,
+   const char *__restrict __cmd, int *__restrict __fd2p);
+extern int rcmd_af (char **__restrict __ahost, unsigned short int __rport,
+      const char *__restrict __locuser,
+      const char *__restrict __remuser,
+      const char *__restrict __cmd, int *__restrict __fd2p,
+      sa_family_t __af);
+extern int rexec (char **__restrict __ahost, int __rport,
+    const char *__restrict __name,
+    const char *__restrict __pass,
+    const char *__restrict __cmd, int *__restrict __fd2p);
+extern int rexec_af (char **__restrict __ahost, int __rport,
+       const char *__restrict __name,
+       const char *__restrict __pass,
+       const char *__restrict __cmd, int *__restrict __fd2p,
+       sa_family_t __af);
+extern int ruserok (const char *__rhost, int __suser,
+      const char *__remuser, const char *__locuser);
+extern int ruserok_af (const char *__rhost, int __suser,
+         const char *__remuser, const char *__locuser,
+         sa_family_t __af);
+extern int iruserok (uint32_t __raddr, int __suser,
+       const char *__remuser, const char *__locuser);
+extern int iruserok_af (const void *__raddr, int __suser,
+   const char *__remuser, const char *__locuser,
+   sa_family_t __af);
+extern int rresvport (int *__alport);
+extern int rresvport_af (int *__alport, sa_family_t __af);
+
+
+
+
+
+
+struct addrinfo
+{
+  int ai_flags;
+  int ai_family;
+  int ai_socktype;
+  int ai_protocol;
+  socklen_t ai_addrlen;
+  struct sockaddr *ai_addr;
+  char *ai_canonname;
+  struct addrinfo *ai_next;
+};
+extern int getaddrinfo (const char *__restrict __name,
+   const char *__restrict __service,
+   const struct addrinfo *__restrict __req,
+   struct addrinfo **__restrict __pai);
+
+
+extern void freeaddrinfo (struct addrinfo *__ai) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern const char *gai_strerror (int __ecode) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern int getnameinfo (const struct sockaddr *__restrict __sa,
+   socklen_t __salen, char *__restrict __host,
+   socklen_t __hostlen, char *__restrict __serv,
+   socklen_t __servlen, int __flags);
+
+
+
+
+
+
+
+
+
+
+
+extern int __sigismember (const __sigset_t *, int);
+extern int __sigaddset (__sigset_t *, int);
+extern int __sigdelset (__sigset_t *, int);
+extern __inline __attribute__ ((__gnu_inline__)) int __sigismember (const __sigset_t *__set, int __sig) { unsigned long int __mask = (((unsigned long int) 1) << (((__sig) - 1) % (8 * sizeof (unsigned long int)))); unsigned long int __word = (((__sig) - 1) / (8 * sizeof (unsigned long int))); return (__set->__val[__word] & __mask) ? 1 : 0; }
+extern __inline __attribute__ ((__gnu_inline__)) int __sigaddset ( __sigset_t *__set, int __sig) { unsigned long int __mask = (((unsigned long int) 1) << (((__sig) - 1) % (8 * sizeof (unsigned long int)))); unsigned long int __word = (((__sig) - 1) / (8 * sizeof (unsigned long int))); return ((__set->__val[__word] |= __mask), 0); }
+extern __inline __attribute__ ((__gnu_inline__)) int __sigdelset ( __sigset_t *__set, int __sig) { unsigned long int __mask = (((unsigned long int) 1) << (((__sig) - 1) % (8 * sizeof (unsigned long int)))); unsigned long int __word = (((__sig) - 1) / (8 * sizeof (unsigned long int))); return ((__set->__val[__word] &= ~__mask), 0); }
+
+
+
+
+
+
+
+typedef __sig_atomic_t sig_atomic_t;
+
+
+
+
+
+
+
+
+typedef union sigval
+  {
+    int sival_int;
+    void *sival_ptr;
+  } sigval_t;
+typedef __clock_t __sigchld_clock_t;
+
+
+
+typedef struct
+  {
+    int si_signo;
+    int si_errno;
+
+    int si_code;
+
+    union
+      {
+ int _pad[((128 / sizeof (int)) - 4)];
+
+
+ struct
+   {
+     __pid_t si_pid;
+     __uid_t si_uid;
+   } _kill;
+
+
+ struct
+   {
+     int si_tid;
+     int si_overrun;
+     sigval_t si_sigval;
+   } _timer;
+
+
+ struct
+   {
+     __pid_t si_pid;
+     __uid_t si_uid;
+     sigval_t si_sigval;
+   } _rt;
+
+
+ struct
+   {
+     __pid_t si_pid;
+     __uid_t si_uid;
+     int si_status;
+     __sigchld_clock_t si_utime;
+     __sigchld_clock_t si_stime;
+   } _sigchld;
+
+
+ struct
+   {
+     void *si_addr;
+     short int si_addr_lsb;
+   } _sigfault;
+
+
+ struct
+   {
+     long int si_band;
+     int si_fd;
+   } _sigpoll;
+
+
+ struct
+   {
+     void *_call_addr;
+     int _syscall;
+     unsigned int _arch;
+   } _sigsys;
+      } _sifields;
+  } siginfo_t ;
+enum
+{
+  SI_ASYNCNL = -60,
+
+  SI_TKILL = -6,
+
+  SI_SIGIO,
+
+  SI_ASYNCIO,
+
+  SI_MESGQ,
+
+  SI_TIMER,
+
+  SI_QUEUE,
+
+  SI_USER,
+
+  SI_KERNEL = 0x80
+
+};
+
+
+
+enum
+{
+  ILL_ILLOPC = 1,
+
+  ILL_ILLOPN,
+
+  ILL_ILLADR,
+
+  ILL_ILLTRP,
+
+  ILL_PRVOPC,
+
+  ILL_PRVREG,
+
+  ILL_COPROC,
+
+  ILL_BADSTK
+
+};
+
+
+enum
+{
+  FPE_INTDIV = 1,
+
+  FPE_INTOVF,
+
+  FPE_FLTDIV,
+
+  FPE_FLTOVF,
+
+  FPE_FLTUND,
+
+  FPE_FLTRES,
+
+  FPE_FLTINV,
+
+  FPE_FLTSUB
+
+};
+
+
+enum
+{
+  SEGV_MAPERR = 1,
+
+  SEGV_ACCERR
+
+};
+
+
+enum
+{
+  BUS_ADRALN = 1,
+
+  BUS_ADRERR,
+
+  BUS_OBJERR,
+
+  BUS_MCEERR_AR,
+
+  BUS_MCEERR_AO
+
+};
+
+
+enum
+{
+  TRAP_BRKPT = 1,
+
+  TRAP_TRACE
+
+};
+
+
+enum
+{
+  CLD_EXITED = 1,
+
+  CLD_KILLED,
+
+  CLD_DUMPED,
+
+  CLD_TRAPPED,
+
+  CLD_STOPPED,
+
+  CLD_CONTINUED
+
+};
+
+
+enum
+{
+  POLL_IN = 1,
+
+  POLL_OUT,
+
+  POLL_MSG,
+
+  POLL_ERR,
+
+  POLL_PRI,
+
+  POLL_HUP
+
+};
+typedef struct sigevent
+  {
+    sigval_t sigev_value;
+    int sigev_signo;
+    int sigev_notify;
+
+    union
+      {
+ int _pad[((64 / sizeof (int)) - 4)];
+
+
+
+ __pid_t _tid;
+
+ struct
+   {
+     void (*_function) (sigval_t);
+     pthread_attr_t *_attribute;
+   } _sigev_thread;
+      } _sigev_un;
+  } sigevent_t;
+
+
+
+
+
+
+enum
+{
+  SIGEV_SIGNAL = 0,
+
+  SIGEV_NONE,
+
+  SIGEV_THREAD,
+
+
+  SIGEV_THREAD_ID = 4
+
+};
+
+
+
+
+typedef void (*__sighandler_t) (int);
+
+
+
+
+extern __sighandler_t __sysv_signal (int __sig, __sighandler_t __handler)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern __sighandler_t signal (int __sig, __sighandler_t __handler)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+extern int kill (__pid_t __pid, int __sig) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+extern int killpg (__pid_t __pgrp, int __sig) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern int raise (int __sig) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern __sighandler_t ssignal (int __sig, __sighandler_t __handler)
+     __attribute__ ((__nothrow__ , __leaf__));
+extern int gsignal (int __sig) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern void psignal (int __sig, const char *__s);
+
+
+
+
+extern void psiginfo (const siginfo_t *__pinfo, const char *__s);
+extern int __sigpause (int __sig_or_mask, int __is_sig);
+extern int sigblock (int __mask) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__deprecated__));
+
+
+extern int sigsetmask (int __mask) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__deprecated__));
+
+
+extern int siggetmask (void) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__deprecated__));
+typedef __sighandler_t sig_t;
+
+
+
+
+
+extern int sigemptyset (sigset_t *__set) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int sigfillset (sigset_t *__set) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int sigaddset (sigset_t *__set, int __signo) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int sigdelset (sigset_t *__set, int __signo) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int sigismember (const sigset_t *__set, int __signo)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+struct sigaction
+  {
+
+
+    union
+      {
+
+ __sighandler_t sa_handler;
+
+ void (*sa_sigaction) (int, siginfo_t *, void *);
+      }
+    __sigaction_handler;
+
+
+
+
+
+
+
+    __sigset_t sa_mask;
+
+
+    int sa_flags;
+
+
+    void (*sa_restorer) (void);
+  };
+
+
+extern int sigprocmask (int __how, const sigset_t *__restrict __set,
+   sigset_t *__restrict __oset) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+extern int sigsuspend (const sigset_t *__set) __attribute__ ((__nonnull__ (1)));
+
+
+extern int sigaction (int __sig, const struct sigaction *__restrict __act,
+        struct sigaction *__restrict __oact) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int sigpending (sigset_t *__set) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+extern int sigwait (const sigset_t *__restrict __set, int *__restrict __sig)
+     __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+
+
+
+extern int sigwaitinfo (const sigset_t *__restrict __set,
+   siginfo_t *__restrict __info) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+extern int sigtimedwait (const sigset_t *__restrict __set,
+    siginfo_t *__restrict __info,
+    const struct timespec *__restrict __timeout)
+     __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int sigqueue (__pid_t __pid, int __sig, const union sigval __val)
+     __attribute__ ((__nothrow__ , __leaf__));
+extern const char *const _sys_siglist[65];
+extern const char *const sys_siglist[65];
+
+
+struct sigvec
+  {
+    __sighandler_t sv_handler;
+    int sv_mask;
+
+    int sv_flags;
+
+  };
+extern int sigvec (int __sig, const struct sigvec *__vec,
+     struct sigvec *__ovec) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+struct _fpx_sw_bytes
+{
+  __uint32_t magic1;
+  __uint32_t extended_size;
+  __uint64_t xstate_bv;
+  __uint32_t xstate_size;
+  __uint32_t padding[7];
+};
+
+struct _fpreg
+{
+  unsigned short significand[4];
+  unsigned short exponent;
+};
+
+struct _fpxreg
+{
+  unsigned short significand[4];
+  unsigned short exponent;
+  unsigned short padding[3];
+};
+
+struct _xmmreg
+{
+  __uint32_t element[4];
+};
+struct _fpstate
+{
+
+  __uint16_t cwd;
+  __uint16_t swd;
+  __uint16_t ftw;
+  __uint16_t fop;
+  __uint64_t rip;
+  __uint64_t rdp;
+  __uint32_t mxcsr;
+  __uint32_t mxcr_mask;
+  struct _fpxreg _st[8];
+  struct _xmmreg _xmm[16];
+  __uint32_t padding[24];
+};
+
+struct sigcontext
+{
+  __uint64_t r8;
+  __uint64_t r9;
+  __uint64_t r10;
+  __uint64_t r11;
+  __uint64_t r12;
+  __uint64_t r13;
+  __uint64_t r14;
+  __uint64_t r15;
+  __uint64_t rdi;
+  __uint64_t rsi;
+  __uint64_t rbp;
+  __uint64_t rbx;
+  __uint64_t rdx;
+  __uint64_t rax;
+  __uint64_t rcx;
+  __uint64_t rsp;
+  __uint64_t rip;
+  __uint64_t eflags;
+  unsigned short cs;
+  unsigned short gs;
+  unsigned short fs;
+  unsigned short __pad0;
+  __uint64_t err;
+  __uint64_t trapno;
+  __uint64_t oldmask;
+  __uint64_t cr2;
+  __extension__ union
+    {
+      struct _fpstate * fpstate;
+      __uint64_t __fpstate_word;
+    };
+  __uint64_t __reserved1 [8];
+};
+
+
+
+struct _xsave_hdr
+{
+  __uint64_t xstate_bv;
+  __uint64_t reserved1[2];
+  __uint64_t reserved2[5];
+};
+
+struct _ymmh_state
+{
+  __uint32_t ymmh_space[64];
+};
+
+struct _xstate
+{
+  struct _fpstate fpstate;
+  struct _xsave_hdr xstate_hdr;
+  struct _ymmh_state ymmh;
+};
+
+
+extern int sigreturn (struct sigcontext *__scp) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+
+
+
+extern int siginterrupt (int __sig, int __interrupt) __attribute__ ((__nothrow__ , __leaf__));
+
+struct sigstack
+  {
+    void *ss_sp;
+    int ss_onstack;
+  };
+
+
+
+enum
+{
+  SS_ONSTACK = 1,
+
+  SS_DISABLE
+
+};
+typedef struct sigaltstack
+  {
+    void *ss_sp;
+    int ss_flags;
+    size_t ss_size;
+  } stack_t;
+
+
+__extension__ typedef long long int greg_t;
+
+
+
+
+
+typedef greg_t gregset_t[23];
+struct _libc_fpxreg
+{
+  unsigned short int significand[4];
+  unsigned short int exponent;
+  unsigned short int padding[3];
+};
+
+struct _libc_xmmreg
+{
+  __uint32_t element[4];
+};
+
+struct _libc_fpstate
+{
+
+  __uint16_t cwd;
+  __uint16_t swd;
+  __uint16_t ftw;
+  __uint16_t fop;
+  __uint64_t rip;
+  __uint64_t rdp;
+  __uint32_t mxcsr;
+  __uint32_t mxcr_mask;
+  struct _libc_fpxreg _st[8];
+  struct _libc_xmmreg _xmm[16];
+  __uint32_t padding[24];
+};
+
+
+typedef struct _libc_fpstate *fpregset_t;
+
+
+typedef struct
+  {
+    gregset_t gregs;
+
+    fpregset_t fpregs;
+    __extension__ unsigned long long __reserved1 [8];
+} mcontext_t;
+
+
+typedef struct ucontext
+  {
+    unsigned long int uc_flags;
+    struct ucontext *uc_link;
+    stack_t uc_stack;
+    mcontext_t uc_mcontext;
+    __sigset_t uc_sigmask;
+    struct _libc_fpstate __fpregs_mem;
+  } ucontext_t;
+
+
+
+
+
+extern int sigstack (struct sigstack *__ss, struct sigstack *__oss)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__deprecated__));
+
+
+
+extern int sigaltstack (const struct sigaltstack *__restrict __ss,
+   struct sigaltstack *__restrict __oss) __attribute__ ((__nothrow__ , __leaf__));
+extern int pthread_sigmask (int __how,
+       const __sigset_t *__restrict __newmask,
+       __sigset_t *__restrict __oldmask)__attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int pthread_kill (pthread_t __threadid, int __signo) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+extern int __libc_current_sigrtmin (void) __attribute__ ((__nothrow__ , __leaf__));
+
+extern int __libc_current_sigrtmax (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+
+
+
+
+struct timezone
+  {
+    int tz_minuteswest;
+    int tz_dsttime;
+  };
+
+typedef struct timezone *__restrict __timezone_ptr_t;
+extern int gettimeofday (struct timeval *__restrict __tv,
+    __timezone_ptr_t __tz) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+extern int settimeofday (const struct timeval *__tv,
+    const struct timezone *__tz)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern int adjtime (const struct timeval *__delta,
+      struct timeval *__olddelta) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+enum __itimer_which
+  {
+
+    ITIMER_REAL = 0,
+
+
+    ITIMER_VIRTUAL = 1,
+
+
+
+    ITIMER_PROF = 2
+
+  };
+
+
+
+struct itimerval
+  {
+
+    struct timeval it_interval;
+
+    struct timeval it_value;
+  };
+
+
+
+
+
+
+typedef int __itimer_which_t;
+
+
+
+
+extern int getitimer (__itimer_which_t __which,
+        struct itimerval *__value) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern int setitimer (__itimer_which_t __which,
+        const struct itimerval *__restrict __new,
+        struct itimerval *__restrict __old) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern int utimes (const char *__file, const struct timeval __tvp[2])
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int lutimes (const char *__file, const struct timeval __tvp[2])
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int futimes (int __fd, const struct timeval __tvp[2]) __attribute__ ((__nothrow__ , __leaf__));
+
+struct if_nameindex
+  {
+    unsigned int if_index;
+    char *if_name;
+  };
+
+
+
+
+enum
+  {
+    IFF_UP = 0x1,
+
+    IFF_BROADCAST = 0x2,
+
+    IFF_DEBUG = 0x4,
+
+    IFF_LOOPBACK = 0x8,
+
+    IFF_POINTOPOINT = 0x10,
+
+    IFF_NOTRAILERS = 0x20,
+
+    IFF_RUNNING = 0x40,
+
+    IFF_NOARP = 0x80,
+
+    IFF_PROMISC = 0x100,
+
+
+
+    IFF_ALLMULTI = 0x200,
+
+
+    IFF_MASTER = 0x400,
+
+    IFF_SLAVE = 0x800,
+
+
+    IFF_MULTICAST = 0x1000,
+
+
+    IFF_PORTSEL = 0x2000,
+
+    IFF_AUTOMEDIA = 0x4000,
+
+    IFF_DYNAMIC = 0x8000
+
+  };
+
+
+
+
+
+
+struct ifaddr
+  {
+    struct sockaddr ifa_addr;
+    union
+      {
+ struct sockaddr ifu_broadaddr;
+ struct sockaddr ifu_dstaddr;
+      } ifa_ifu;
+    struct iface *ifa_ifp;
+    struct ifaddr *ifa_next;
+  };
+struct ifmap
+  {
+    unsigned long int mem_start;
+    unsigned long int mem_end;
+    unsigned short int base_addr;
+    unsigned char irq;
+    unsigned char dma;
+    unsigned char port;
+
+  };
+
+
+
+
+
+struct ifreq
+  {
+
+
+    union
+      {
+ char ifrn_name[16];
+      } ifr_ifrn;
+
+    union
+      {
+ struct sockaddr ifru_addr;
+ struct sockaddr ifru_dstaddr;
+ struct sockaddr ifru_broadaddr;
+ struct sockaddr ifru_netmask;
+ struct sockaddr ifru_hwaddr;
+ short int ifru_flags;
+ int ifru_ivalue;
+ int ifru_mtu;
+ struct ifmap ifru_map;
+ char ifru_slave[16];
+ char ifru_newname[16];
+ __caddr_t ifru_data;
+      } ifr_ifru;
+  };
+struct ifconf
+  {
+    int ifc_len;
+    union
+      {
+ __caddr_t ifcu_buf;
+ struct ifreq *ifcu_req;
+      } ifc_ifcu;
+  };
+
+
+
+
+
+
+
+
+extern unsigned int if_nametoindex (const char *__ifname) __attribute__ ((__nothrow__ , __leaf__));
+extern char *if_indextoname (unsigned int __ifindex, char *__ifname) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern struct if_nameindex *if_nameindex (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern void if_freenameindex (struct if_nameindex *__ptr) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+typedef struct iovec sg_buf;
+static __inline__ struct sockaddr_in *sa2sin (struct sockaddr *sa)
+{
+    return (struct sockaddr_in *) (void *) sa;
+}
+
+static __inline__ struct sockaddr_in6 *sa2sin6 (struct sockaddr *sa)
+{
+    return (struct sockaddr_in6 *) (void *) sa;
+}
+
+static __inline__ struct sockaddr *ss2sa (struct sockaddr_storage *ss)
+{
+    return (struct sockaddr *) ss;
+}
+static __inline__ struct sockaddr_in *ss2sin (struct sockaddr_storage *ss)
+{
+    return (struct sockaddr_in *) ss;
+}
+
+static __inline__ struct sockaddr_in6 *ss2sin6 (struct sockaddr_storage *ss)
+{
+    return (struct sockaddr_in6 *) ss;
+}
+typedef struct _krb5_alt_method {
+ krb5_magic magic;
+ krb5_int32 method;
+ unsigned int length;
+ krb5_octet *data;
+} krb5_alt_method;
+typedef struct _krb5_etype_info_entry {
+ krb5_magic magic;
+ krb5_enctype etype;
+ unsigned int length;
+ krb5_octet *salt;
+    krb5_data s2kparams;
+} krb5_etype_info_entry;
+typedef krb5_etype_info_entry ** krb5_etype_info;
+typedef struct _krb5_predicted_sam_response {
+ krb5_magic magic;
+ krb5_keyblock sam_key;
+ krb5_flags sam_flags;
+ krb5_timestamp stime;
+ krb5_int32 susec;
+ krb5_principal client;
+ krb5_data msd;
+} krb5_predicted_sam_response;
+
+typedef struct _krb5_sam_challenge {
+ krb5_magic magic;
+ krb5_int32 sam_type;
+ krb5_flags sam_flags;
+ krb5_data sam_type_name;
+ krb5_data sam_track_id;
+ krb5_data sam_challenge_label;
+ krb5_data sam_challenge;
+ krb5_data sam_response_prompt;
+ krb5_data sam_pk_for_sad;
+ krb5_int32 sam_nonce;
+ krb5_checksum sam_cksum;
+} krb5_sam_challenge;
+
+typedef struct _krb5_sam_key {
+ krb5_magic magic;
+ krb5_keyblock sam_key;
+} krb5_sam_key;
+
+typedef struct _krb5_enc_sam_response_enc {
+ krb5_magic magic;
+ krb5_int32 sam_nonce;
+ krb5_timestamp sam_timestamp;
+ krb5_int32 sam_usec;
+ krb5_data sam_sad;
+} krb5_enc_sam_response_enc;
+
+typedef struct _krb5_sam_response {
+ krb5_magic magic;
+ krb5_int32 sam_type;
+ krb5_flags sam_flags;
+ krb5_data sam_track_id;
+ krb5_enc_data sam_enc_key;
+ krb5_enc_data sam_enc_nonce_or_ts;
+ krb5_int32 sam_nonce;
+ krb5_timestamp sam_patimestamp;
+} krb5_sam_response;
+
+typedef struct _krb5_sam_challenge_2 {
+ krb5_data sam_challenge_2_body;
+ krb5_checksum **sam_cksum;
+} krb5_sam_challenge_2;
+
+typedef struct _krb5_sam_challenge_2_body {
+ krb5_magic magic;
+ krb5_int32 sam_type;
+ krb5_flags sam_flags;
+ krb5_data sam_type_name;
+ krb5_data sam_track_id;
+ krb5_data sam_challenge_label;
+ krb5_data sam_challenge;
+ krb5_data sam_response_prompt;
+ krb5_data sam_pk_for_sad;
+ krb5_int32 sam_nonce;
+ krb5_enctype sam_etype;
+} krb5_sam_challenge_2_body;
+
+typedef struct _krb5_sam_response_2 {
+ krb5_magic magic;
+ krb5_int32 sam_type;
+ krb5_flags sam_flags;
+ krb5_data sam_track_id;
+ krb5_enc_data sam_enc_nonce_or_sad;
+ krb5_int32 sam_nonce;
+} krb5_sam_response_2;
+
+typedef struct _krb5_enc_sam_response_enc_2 {
+ krb5_magic magic;
+ krb5_int32 sam_nonce;
+ krb5_data sam_sad;
+} krb5_enc_sam_response_enc_2;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+extern void *memcpy (void *__restrict __dest, const void *__restrict __src,
+       size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern void *memmove (void *__dest, const void *__src, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+
+
+
+extern void *memccpy (void *__restrict __dest, const void *__restrict __src,
+        int __c, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+
+
+extern void *memset (void *__s, int __c, size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int memcmp (const void *__s1, const void *__s2, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
+extern void *memchr (const void *__s, int __c, size_t __n)
+      __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+extern char *strncpy (char *__restrict __dest,
+        const char *__restrict __src, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern char *strcat (char *__restrict __dest, const char *__restrict __src)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+extern char *strncat (char *__restrict __dest, const char *__restrict __src,
+        size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int strcmp (const char *__s1, const char *__s2)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
+
+extern int strncmp (const char *__s1, const char *__s2, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int strcoll (const char *__s1, const char *__s2)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
+
+extern size_t strxfrm (char *__restrict __dest,
+         const char *__restrict __src, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+
+extern int strcoll_l (const char *__s1, const char *__s2, __locale_t __l)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2, 3)));
+
+extern size_t strxfrm_l (char *__dest, const char *__src, size_t __n,
+    __locale_t __l) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2, 4)));
+
+
+
+
+
+extern char *strdup (const char *__s)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+extern char *strndup (const char *__string, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) __attribute__ ((__nonnull__ (1)));
+
+extern char *strchr (const char *__s, int __c)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));
+extern char *strrchr (const char *__s, int __c)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+extern size_t strcspn (const char *__s, const char *__reject)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern size_t strspn (const char *__s, const char *__accept)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
+extern char *strpbrk (const char *__s, const char *__accept)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
+extern char *strstr (const char *__haystack, const char *__needle)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+
+extern char *strtok (char *__restrict __s, const char *__restrict __delim)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+
+
+
+
+extern char *__strtok_r (char *__restrict __s,
+    const char *__restrict __delim,
+    char **__restrict __save_ptr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2, 3)));
+
+extern char *strtok_r (char *__restrict __s, const char *__restrict __delim,
+         char **__restrict __save_ptr)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2, 3)));
+
+
+extern size_t strlen (const char *__s)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+extern size_t strnlen (const char *__string, size_t __maxlen)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+extern char *strerror (int __errnum) __attribute__ ((__nothrow__ , __leaf__));
+
+extern int strerror_r (int __errnum, char *__buf, size_t __buflen) __asm__ ("" "__xpg_strerror_r") __attribute__ ((__nothrow__ , __leaf__))
+
+                        __attribute__ ((__nonnull__ (2)));
+extern char *strerror_l (int __errnum, __locale_t __l) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern void __bzero (void *__s, size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern void bcopy (const void *__src, void *__dest, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern void bzero (void *__s, size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern int bcmp (const void *__s1, const void *__s2, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
+extern char *index (const char *__s, int __c)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));
+extern char *rindex (const char *__s, int __c)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+extern int ffs (int __i) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+extern int strcasecmp (const char *__s1, const char *__s2)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern int strncasecmp (const char *__s1, const char *__s2, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
+extern char *strsep (char **__restrict __stringp,
+       const char *__restrict __delim)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+
+extern char *strsignal (int __sig) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern char *__stpcpy (char *__restrict __dest, const char *__restrict __src)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+extern char *stpcpy (char *__restrict __dest, const char *__restrict __src)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+extern char *__stpncpy (char *__restrict __dest,
+   const char *__restrict __src, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+extern char *stpncpy (char *__restrict __dest,
+        const char *__restrict __src, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+extern void *__rawmemchr (const void *__s, int __c);
+extern __inline __attribute__ ((__gnu_inline__)) size_t __strcspn_c1 (const char *__s, int __reject);
+extern __inline __attribute__ ((__gnu_inline__)) size_t
+__strcspn_c1 (const char *__s, int __reject)
+{
+  size_t __result = 0;
+  while (__s[__result] != '\0' && __s[__result] != __reject)
+    ++__result;
+  return __result;
+}
+
+extern __inline __attribute__ ((__gnu_inline__)) size_t __strcspn_c2 (const char *__s, int __reject1,
+         int __reject2);
+extern __inline __attribute__ ((__gnu_inline__)) size_t
+__strcspn_c2 (const char *__s, int __reject1, int __reject2)
+{
+  size_t __result = 0;
+  while (__s[__result] != '\0' && __s[__result] != __reject1
+  && __s[__result] != __reject2)
+    ++__result;
+  return __result;
+}
+
+extern __inline __attribute__ ((__gnu_inline__)) size_t __strcspn_c3 (const char *__s, int __reject1,
+         int __reject2, int __reject3);
+extern __inline __attribute__ ((__gnu_inline__)) size_t
+__strcspn_c3 (const char *__s, int __reject1, int __reject2,
+       int __reject3)
+{
+  size_t __result = 0;
+  while (__s[__result] != '\0' && __s[__result] != __reject1
+  && __s[__result] != __reject2 && __s[__result] != __reject3)
+    ++__result;
+  return __result;
+}
+extern __inline __attribute__ ((__gnu_inline__)) size_t __strspn_c1 (const char *__s, int __accept);
+extern __inline __attribute__ ((__gnu_inline__)) size_t
+__strspn_c1 (const char *__s, int __accept)
+{
+  size_t __result = 0;
+
+  while (__s[__result] == __accept)
+    ++__result;
+  return __result;
+}
+
+extern __inline __attribute__ ((__gnu_inline__)) size_t __strspn_c2 (const char *__s, int __accept1,
+        int __accept2);
+extern __inline __attribute__ ((__gnu_inline__)) size_t
+__strspn_c2 (const char *__s, int __accept1, int __accept2)
+{
+  size_t __result = 0;
+
+  while (__s[__result] == __accept1 || __s[__result] == __accept2)
+    ++__result;
+  return __result;
+}
+
+extern __inline __attribute__ ((__gnu_inline__)) size_t __strspn_c3 (const char *__s, int __accept1,
+        int __accept2, int __accept3);
+extern __inline __attribute__ ((__gnu_inline__)) size_t
+__strspn_c3 (const char *__s, int __accept1, int __accept2, int __accept3)
+{
+  size_t __result = 0;
+
+  while (__s[__result] == __accept1 || __s[__result] == __accept2
+  || __s[__result] == __accept3)
+    ++__result;
+  return __result;
+}
+extern __inline __attribute__ ((__gnu_inline__)) char *__strpbrk_c2 (const char *__s, int __accept1,
+        int __accept2);
+extern __inline __attribute__ ((__gnu_inline__)) char *
+__strpbrk_c2 (const char *__s, int __accept1, int __accept2)
+{
+
+  while (*__s != '\0' && *__s != __accept1 && *__s != __accept2)
+    ++__s;
+  return *__s == '\0' ? ((void *)0) : (char *) (size_t) __s;
+}
+
+extern __inline __attribute__ ((__gnu_inline__)) char *__strpbrk_c3 (const char *__s, int __accept1,
+        int __accept2, int __accept3);
+extern __inline __attribute__ ((__gnu_inline__)) char *
+__strpbrk_c3 (const char *__s, int __accept1, int __accept2, int __accept3)
+{
+
+  while (*__s != '\0' && *__s != __accept1 && *__s != __accept2
+  && *__s != __accept3)
+    ++__s;
+  return *__s == '\0' ? ((void *)0) : (char *) (size_t) __s;
+}
+extern __inline __attribute__ ((__gnu_inline__)) char *__strtok_r_1c (char *__s, char __sep, char **__nextp);
+extern __inline __attribute__ ((__gnu_inline__)) char *
+__strtok_r_1c (char *__s, char __sep, char **__nextp)
+{
+  char *__result;
+  if (__s == ((void *)0))
+    __s = *__nextp;
+  while (*__s == __sep)
+    ++__s;
+  __result = ((void *)0);
+  if (*__s != '\0')
+    {
+      __result = __s++;
+      while (*__s != '\0')
+ if (*__s++ == __sep)
+   {
+     __s[-1] = '\0';
+     break;
+   }
+    }
+  *__nextp = __s;
+  return __result;
+}
+extern char *__strsep_g (char **__stringp, const char *__delim);
+extern __inline __attribute__ ((__gnu_inline__)) char *__strsep_1c (char **__s, char __reject);
+extern __inline __attribute__ ((__gnu_inline__)) char *
+__strsep_1c (char **__s, char __reject)
+{
+  char *__retval = *__s;
+  if (__retval != ((void *)0) && (*__s = (__extension__ (__builtin_constant_p (__reject) && !__builtin_constant_p (__retval) && (__reject) == '\0' ? (char *) __rawmemchr (__retval, __reject) : __builtin_strchr (__retval, __reject)))) != ((void *)0))
+    *(*__s)++ = '\0';
+  return __retval;
+}
+
+extern __inline __attribute__ ((__gnu_inline__)) char *__strsep_2c (char **__s, char __reject1, char __reject2);
+extern __inline __attribute__ ((__gnu_inline__)) char *
+__strsep_2c (char **__s, char __reject1, char __reject2)
+{
+  char *__retval = *__s;
+  if (__retval != ((void *)0))
+    {
+      char *__cp = __retval;
+      while (1)
+ {
+   if (*__cp == '\0')
+     {
+       __cp = ((void *)0);
+   break;
+     }
+   if (*__cp == __reject1 || *__cp == __reject2)
+     {
+       *__cp++ = '\0';
+       break;
+     }
+   ++__cp;
+ }
+      *__s = __cp;
+    }
+  return __retval;
+}
+
+extern __inline __attribute__ ((__gnu_inline__)) char *__strsep_3c (char **__s, char __reject1, char __reject2,
+       char __reject3);
+extern __inline __attribute__ ((__gnu_inline__)) char *
+__strsep_3c (char **__s, char __reject1, char __reject2, char __reject3)
+{
+  char *__retval = *__s;
+  if (__retval != ((void *)0))
+    {
+      char *__cp = __retval;
+      while (1)
+ {
+   if (*__cp == '\0')
+     {
+       __cp = ((void *)0);
+   break;
+     }
+   if (*__cp == __reject1 || *__cp == __reject2 || *__cp == __reject3)
+     {
+       *__cp++ = '\0';
+       break;
+     }
+   ++__cp;
+ }
+      *__s = __cp;
+    }
+  return __retval;
+}
+extern char *__strdup (const char *__string) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__));
+extern char *__strndup (const char *__string, size_t __n)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__));
+
+
+
+
+
+
+
+
+
+
+
+typedef __useconds_t useconds_t;
+extern int access (const char *__name, int __type) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+extern int faccessat (int __fd, const char *__file, int __type, int __flag)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2))) ;
+extern __off_t lseek (int __fd, __off_t __offset, int __whence) __attribute__ ((__nothrow__ , __leaf__));
+extern int close (int __fd);
+
+
+
+
+
+
+extern ssize_t read (int __fd, void *__buf, size_t __nbytes) ;
+
+
+
+
+
+extern ssize_t write (int __fd, const void *__buf, size_t __n) ;
+extern ssize_t pread (int __fd, void *__buf, size_t __nbytes,
+        __off_t __offset) ;
+
+
+
+
+
+
+extern ssize_t pwrite (int __fd, const void *__buf, size_t __n,
+         __off_t __offset) ;
+extern int pipe (int __pipedes[2]) __attribute__ ((__nothrow__ , __leaf__)) ;
+extern unsigned int alarm (unsigned int __seconds) __attribute__ ((__nothrow__ , __leaf__));
+extern unsigned int sleep (unsigned int __seconds);
+
+
+
+
+
+
+
+extern __useconds_t ualarm (__useconds_t __value, __useconds_t __interval)
+     __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+extern int usleep (__useconds_t __useconds);
+extern int pause (void);
+
+
+
+extern int chown (const char *__file, __uid_t __owner, __gid_t __group)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+
+
+extern int fchown (int __fd, __uid_t __owner, __gid_t __group) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+extern int lchown (const char *__file, __uid_t __owner, __gid_t __group)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+
+
+
+
+
+extern int fchownat (int __fd, const char *__file, __uid_t __owner,
+       __gid_t __group, int __flag)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2))) ;
+
+
+
+extern int chdir (const char *__path) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+
+
+extern int fchdir (int __fd) __attribute__ ((__nothrow__ , __leaf__)) ;
+extern char *getcwd (char *__buf, size_t __size) __attribute__ ((__nothrow__ , __leaf__)) ;
+extern char *getwd (char *__buf)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) __attribute__ ((__deprecated__)) ;
+
+
+
+
+extern int dup (int __fd) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+extern int dup2 (int __fd, int __fd2) __attribute__ ((__nothrow__ , __leaf__));
+extern char **__environ;
+
+
+
+
+
+
+
+extern int execve (const char *__path, char *const __argv[],
+     char *const __envp[]) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+
+extern int fexecve (int __fd, char *const __argv[], char *const __envp[])
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+
+
+
+
+extern int execv (const char *__path, char *const __argv[])
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+extern int execle (const char *__path, const char *__arg, ...)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+extern int execl (const char *__path, const char *__arg, ...)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+extern int execvp (const char *__file, char *const __argv[])
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+
+extern int execlp (const char *__file, const char *__arg, ...)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+extern int nice (int __inc) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+extern void _exit (int __status) __attribute__ ((__noreturn__));
+
+
+
+
+
+enum
+  {
+    _PC_LINK_MAX,
+
+    _PC_MAX_CANON,
+
+    _PC_MAX_INPUT,
+
+    _PC_NAME_MAX,
+
+    _PC_PATH_MAX,
+
+    _PC_PIPE_BUF,
+
+    _PC_CHOWN_RESTRICTED,
+
+    _PC_NO_TRUNC,
+
+    _PC_VDISABLE,
+
+    _PC_SYNC_IO,
+
+    _PC_ASYNC_IO,
+
+    _PC_PRIO_IO,
+
+    _PC_SOCK_MAXBUF,
+
+    _PC_FILESIZEBITS,
+
+    _PC_REC_INCR_XFER_SIZE,
+
+    _PC_REC_MAX_XFER_SIZE,
+
+    _PC_REC_MIN_XFER_SIZE,
+
+    _PC_REC_XFER_ALIGN,
+
+    _PC_ALLOC_SIZE_MIN,
+
+    _PC_SYMLINK_MAX,
+
+    _PC_2_SYMLINKS
+
+  };
+
+
+enum
+  {
+    _SC_ARG_MAX,
+
+    _SC_CHILD_MAX,
+
+    _SC_CLK_TCK,
+
+    _SC_NGROUPS_MAX,
+
+    _SC_OPEN_MAX,
+
+    _SC_STREAM_MAX,
+
+    _SC_TZNAME_MAX,
+
+    _SC_JOB_CONTROL,
+
+    _SC_SAVED_IDS,
+
+    _SC_REALTIME_SIGNALS,
+
+    _SC_PRIORITY_SCHEDULING,
+
+    _SC_TIMERS,
+
+    _SC_ASYNCHRONOUS_IO,
+
+    _SC_PRIORITIZED_IO,
+
+    _SC_SYNCHRONIZED_IO,
+
+    _SC_FSYNC,
+
+    _SC_MAPPED_FILES,
+
+    _SC_MEMLOCK,
+
+    _SC_MEMLOCK_RANGE,
+
+    _SC_MEMORY_PROTECTION,
+
+    _SC_MESSAGE_PASSING,
+
+    _SC_SEMAPHORES,
+
+    _SC_SHARED_MEMORY_OBJECTS,
+
+    _SC_AIO_LISTIO_MAX,
+
+    _SC_AIO_MAX,
+
+    _SC_AIO_PRIO_DELTA_MAX,
+
+    _SC_DELAYTIMER_MAX,
+
+    _SC_MQ_OPEN_MAX,
+
+    _SC_MQ_PRIO_MAX,
+
+    _SC_VERSION,
+
+    _SC_PAGESIZE,
+
+
+    _SC_RTSIG_MAX,
+
+    _SC_SEM_NSEMS_MAX,
+
+    _SC_SEM_VALUE_MAX,
+
+    _SC_SIGQUEUE_MAX,
+
+    _SC_TIMER_MAX,
+
+
+
+
+    _SC_BC_BASE_MAX,
+
+    _SC_BC_DIM_MAX,
+
+    _SC_BC_SCALE_MAX,
+
+    _SC_BC_STRING_MAX,
+
+    _SC_COLL_WEIGHTS_MAX,
+
+    _SC_EQUIV_CLASS_MAX,
+
+    _SC_EXPR_NEST_MAX,
+
+    _SC_LINE_MAX,
+
+    _SC_RE_DUP_MAX,
+
+    _SC_CHARCLASS_NAME_MAX,
+
+
+    _SC_2_VERSION,
+
+    _SC_2_C_BIND,
+
+    _SC_2_C_DEV,
+
+    _SC_2_FORT_DEV,
+
+    _SC_2_FORT_RUN,
+
+    _SC_2_SW_DEV,
+
+    _SC_2_LOCALEDEF,
+
+
+    _SC_PII,
+
+    _SC_PII_XTI,
+
+    _SC_PII_SOCKET,
+
+    _SC_PII_INTERNET,
+
+    _SC_PII_OSI,
+
+    _SC_POLL,
+
+    _SC_SELECT,
+
+    _SC_UIO_MAXIOV,
+
+    _SC_IOV_MAX = _SC_UIO_MAXIOV,
+
+    _SC_PII_INTERNET_STREAM,
+
+    _SC_PII_INTERNET_DGRAM,
+
+    _SC_PII_OSI_COTS,
+
+    _SC_PII_OSI_CLTS,
+
+    _SC_PII_OSI_M,
+
+    _SC_T_IOV_MAX,
+
+
+
+    _SC_THREADS,
+
+    _SC_THREAD_SAFE_FUNCTIONS,
+
+    _SC_GETGR_R_SIZE_MAX,
+
+    _SC_GETPW_R_SIZE_MAX,
+
+    _SC_LOGIN_NAME_MAX,
+
+    _SC_TTY_NAME_MAX,
+
+    _SC_THREAD_DESTRUCTOR_ITERATIONS,
+
+    _SC_THREAD_KEYS_MAX,
+
+    _SC_THREAD_STACK_MIN,
+
+    _SC_THREAD_THREADS_MAX,
+
+    _SC_THREAD_ATTR_STACKADDR,
+
+    _SC_THREAD_ATTR_STACKSIZE,
+
+    _SC_THREAD_PRIORITY_SCHEDULING,
+
+    _SC_THREAD_PRIO_INHERIT,
+
+    _SC_THREAD_PRIO_PROTECT,
+
+    _SC_THREAD_PROCESS_SHARED,
+
+
+    _SC_NPROCESSORS_CONF,
+
+    _SC_NPROCESSORS_ONLN,
+
+    _SC_PHYS_PAGES,
+
+    _SC_AVPHYS_PAGES,
+
+    _SC_ATEXIT_MAX,
+
+    _SC_PASS_MAX,
+
+
+    _SC_XOPEN_VERSION,
+
+    _SC_XOPEN_XCU_VERSION,
+
+    _SC_XOPEN_UNIX,
+
+    _SC_XOPEN_CRYPT,
+
+    _SC_XOPEN_ENH_I18N,
+
+    _SC_XOPEN_SHM,
+
+
+    _SC_2_CHAR_TERM,
+
+    _SC_2_C_VERSION,
+
+    _SC_2_UPE,
+
+
+    _SC_XOPEN_XPG2,
+
+    _SC_XOPEN_XPG3,
+
+    _SC_XOPEN_XPG4,
+
+
+    _SC_CHAR_BIT,
+
+    _SC_CHAR_MAX,
+
+    _SC_CHAR_MIN,
+
+    _SC_INT_MAX,
+
+    _SC_INT_MIN,
+
+    _SC_LONG_BIT,
+
+    _SC_WORD_BIT,
+
+    _SC_MB_LEN_MAX,
+
+    _SC_NZERO,
+
+    _SC_SSIZE_MAX,
+
+    _SC_SCHAR_MAX,
+
+    _SC_SCHAR_MIN,
+
+    _SC_SHRT_MAX,
+
+    _SC_SHRT_MIN,
+
+    _SC_UCHAR_MAX,
+
+    _SC_UINT_MAX,
+
+    _SC_ULONG_MAX,
+
+    _SC_USHRT_MAX,
+
+
+    _SC_NL_ARGMAX,
+
+    _SC_NL_LANGMAX,
+
+    _SC_NL_MSGMAX,
+
+    _SC_NL_NMAX,
+
+    _SC_NL_SETMAX,
+
+    _SC_NL_TEXTMAX,
+
+
+    _SC_XBS5_ILP32_OFF32,
+
+    _SC_XBS5_ILP32_OFFBIG,
+
+    _SC_XBS5_LP64_OFF64,
+
+    _SC_XBS5_LPBIG_OFFBIG,
+
+
+    _SC_XOPEN_LEGACY,
+
+    _SC_XOPEN_REALTIME,
+
+    _SC_XOPEN_REALTIME_THREADS,
+
+
+    _SC_ADVISORY_INFO,
+
+    _SC_BARRIERS,
+
+    _SC_BASE,
+
+    _SC_C_LANG_SUPPORT,
+
+    _SC_C_LANG_SUPPORT_R,
+
+    _SC_CLOCK_SELECTION,
+
+    _SC_CPUTIME,
+
+    _SC_THREAD_CPUTIME,
+
+    _SC_DEVICE_IO,
+
+    _SC_DEVICE_SPECIFIC,
+
+    _SC_DEVICE_SPECIFIC_R,
+
+    _SC_FD_MGMT,
+
+    _SC_FIFO,
+
+    _SC_PIPE,
+
+    _SC_FILE_ATTRIBUTES,
+
+    _SC_FILE_LOCKING,
+
+    _SC_FILE_SYSTEM,
+
+    _SC_MONOTONIC_CLOCK,
+
+    _SC_MULTI_PROCESS,
+
+    _SC_SINGLE_PROCESS,
+
+    _SC_NETWORKING,
+
+    _SC_READER_WRITER_LOCKS,
+
+    _SC_SPIN_LOCKS,
+
+    _SC_REGEXP,
+
+    _SC_REGEX_VERSION,
+
+    _SC_SHELL,
+
+    _SC_SIGNALS,
+
+    _SC_SPAWN,
+
+    _SC_SPORADIC_SERVER,
+
+    _SC_THREAD_SPORADIC_SERVER,
+
+    _SC_SYSTEM_DATABASE,
+
+    _SC_SYSTEM_DATABASE_R,
+
+    _SC_TIMEOUTS,
+
+    _SC_TYPED_MEMORY_OBJECTS,
+
+    _SC_USER_GROUPS,
+
+    _SC_USER_GROUPS_R,
+
+    _SC_2_PBS,
+
+    _SC_2_PBS_ACCOUNTING,
+
+    _SC_2_PBS_LOCATE,
+
+    _SC_2_PBS_MESSAGE,
+
+    _SC_2_PBS_TRACK,
+
+    _SC_SYMLOOP_MAX,
+
+    _SC_STREAMS,
+
+    _SC_2_PBS_CHECKPOINT,
+
+
+    _SC_V6_ILP32_OFF32,
+
+    _SC_V6_ILP32_OFFBIG,
+
+    _SC_V6_LP64_OFF64,
+
+    _SC_V6_LPBIG_OFFBIG,
+
+
+    _SC_HOST_NAME_MAX,
+
+    _SC_TRACE,
+
+    _SC_TRACE_EVENT_FILTER,
+
+    _SC_TRACE_INHERIT,
+
+    _SC_TRACE_LOG,
+
+
+    _SC_LEVEL1_ICACHE_SIZE,
+
+    _SC_LEVEL1_ICACHE_ASSOC,
+
+    _SC_LEVEL1_ICACHE_LINESIZE,
+
+    _SC_LEVEL1_DCACHE_SIZE,
+
+    _SC_LEVEL1_DCACHE_ASSOC,
+
+    _SC_LEVEL1_DCACHE_LINESIZE,
+
+    _SC_LEVEL2_CACHE_SIZE,
+
+    _SC_LEVEL2_CACHE_ASSOC,
+
+    _SC_LEVEL2_CACHE_LINESIZE,
+
+    _SC_LEVEL3_CACHE_SIZE,
+
+    _SC_LEVEL3_CACHE_ASSOC,
+
+    _SC_LEVEL3_CACHE_LINESIZE,
+
+    _SC_LEVEL4_CACHE_SIZE,
+
+    _SC_LEVEL4_CACHE_ASSOC,
+
+    _SC_LEVEL4_CACHE_LINESIZE,
+
+
+
+    _SC_IPV6 = _SC_LEVEL1_ICACHE_SIZE + 50,
+
+    _SC_RAW_SOCKETS,
+
+
+    _SC_V7_ILP32_OFF32,
+
+    _SC_V7_ILP32_OFFBIG,
+
+    _SC_V7_LP64_OFF64,
+
+    _SC_V7_LPBIG_OFFBIG,
+
+
+    _SC_SS_REPL_MAX,
+
+
+    _SC_TRACE_EVENT_NAME_MAX,
+
+    _SC_TRACE_NAME_MAX,
+
+    _SC_TRACE_SYS_MAX,
+
+    _SC_TRACE_USER_EVENT_MAX,
+
+
+    _SC_XOPEN_STREAMS,
+
+
+    _SC_THREAD_ROBUST_PRIO_INHERIT,
+
+    _SC_THREAD_ROBUST_PRIO_PROTECT
+
+  };
+
+
+enum
+  {
+    _CS_PATH,
+
+
+    _CS_V6_WIDTH_RESTRICTED_ENVS,
+
+
+
+    _CS_GNU_LIBC_VERSION,
+
+    _CS_GNU_LIBPTHREAD_VERSION,
+
+
+    _CS_V5_WIDTH_RESTRICTED_ENVS,
+
+
+
+    _CS_V7_WIDTH_RESTRICTED_ENVS,
+
+
+
+    _CS_LFS_CFLAGS = 1000,
+
+    _CS_LFS_LDFLAGS,
+
+    _CS_LFS_LIBS,
+
+    _CS_LFS_LINTFLAGS,
+
+    _CS_LFS64_CFLAGS,
+
+    _CS_LFS64_LDFLAGS,
+
+    _CS_LFS64_LIBS,
+
+    _CS_LFS64_LINTFLAGS,
+
+
+    _CS_XBS5_ILP32_OFF32_CFLAGS = 1100,
+
+    _CS_XBS5_ILP32_OFF32_LDFLAGS,
+
+    _CS_XBS5_ILP32_OFF32_LIBS,
+
+    _CS_XBS5_ILP32_OFF32_LINTFLAGS,
+
+    _CS_XBS5_ILP32_OFFBIG_CFLAGS,
+
+    _CS_XBS5_ILP32_OFFBIG_LDFLAGS,
+
+    _CS_XBS5_ILP32_OFFBIG_LIBS,
+
+    _CS_XBS5_ILP32_OFFBIG_LINTFLAGS,
+
+    _CS_XBS5_LP64_OFF64_CFLAGS,
+
+    _CS_XBS5_LP64_OFF64_LDFLAGS,
+
+    _CS_XBS5_LP64_OFF64_LIBS,
+
+    _CS_XBS5_LP64_OFF64_LINTFLAGS,
+
+    _CS_XBS5_LPBIG_OFFBIG_CFLAGS,
+
+    _CS_XBS5_LPBIG_OFFBIG_LDFLAGS,
+
+    _CS_XBS5_LPBIG_OFFBIG_LIBS,
+
+    _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS,
+
+
+    _CS_POSIX_V6_ILP32_OFF32_CFLAGS,
+
+    _CS_POSIX_V6_ILP32_OFF32_LDFLAGS,
+
+    _CS_POSIX_V6_ILP32_OFF32_LIBS,
+
+    _CS_POSIX_V6_ILP32_OFF32_LINTFLAGS,
+
+    _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS,
+
+    _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS,
+
+    _CS_POSIX_V6_ILP32_OFFBIG_LIBS,
+
+    _CS_POSIX_V6_ILP32_OFFBIG_LINTFLAGS,
+
+    _CS_POSIX_V6_LP64_OFF64_CFLAGS,
+
+    _CS_POSIX_V6_LP64_OFF64_LDFLAGS,
+
+    _CS_POSIX_V6_LP64_OFF64_LIBS,
+
+    _CS_POSIX_V6_LP64_OFF64_LINTFLAGS,
+
+    _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS,
+
+    _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS,
+
+    _CS_POSIX_V6_LPBIG_OFFBIG_LIBS,
+
+    _CS_POSIX_V6_LPBIG_OFFBIG_LINTFLAGS,
+
+
+    _CS_POSIX_V7_ILP32_OFF32_CFLAGS,
+
+    _CS_POSIX_V7_ILP32_OFF32_LDFLAGS,
+
+    _CS_POSIX_V7_ILP32_OFF32_LIBS,
+
+    _CS_POSIX_V7_ILP32_OFF32_LINTFLAGS,
+
+    _CS_POSIX_V7_ILP32_OFFBIG_CFLAGS,
+
+    _CS_POSIX_V7_ILP32_OFFBIG_LDFLAGS,
+
+    _CS_POSIX_V7_ILP32_OFFBIG_LIBS,
+
+    _CS_POSIX_V7_ILP32_OFFBIG_LINTFLAGS,
+
+    _CS_POSIX_V7_LP64_OFF64_CFLAGS,
+
+    _CS_POSIX_V7_LP64_OFF64_LDFLAGS,
+
+    _CS_POSIX_V7_LP64_OFF64_LIBS,
+
+    _CS_POSIX_V7_LP64_OFF64_LINTFLAGS,
+
+    _CS_POSIX_V7_LPBIG_OFFBIG_CFLAGS,
+
+    _CS_POSIX_V7_LPBIG_OFFBIG_LDFLAGS,
+
+    _CS_POSIX_V7_LPBIG_OFFBIG_LIBS,
+
+    _CS_POSIX_V7_LPBIG_OFFBIG_LINTFLAGS,
+
+
+    _CS_V6_ENV,
+
+    _CS_V7_ENV
+
+  };
+
+
+extern long int pathconf (const char *__path, int __name)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+extern long int fpathconf (int __fd, int __name) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern long int sysconf (int __name) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern size_t confstr (int __name, char *__buf, size_t __len) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern __pid_t getpid (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern __pid_t getppid (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern __pid_t getpgrp (void) __attribute__ ((__nothrow__ , __leaf__));
+extern __pid_t __getpgid (__pid_t __pid) __attribute__ ((__nothrow__ , __leaf__));
+
+extern __pid_t getpgid (__pid_t __pid) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+extern int setpgid (__pid_t __pid, __pid_t __pgid) __attribute__ ((__nothrow__ , __leaf__));
+extern int setpgrp (void) __attribute__ ((__nothrow__ , __leaf__));
+extern __pid_t setsid (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern __pid_t getsid (__pid_t __pid) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern __uid_t getuid (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern __uid_t geteuid (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern __gid_t getgid (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern __gid_t getegid (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern int getgroups (int __size, __gid_t __list[]) __attribute__ ((__nothrow__ , __leaf__)) ;
+extern int setuid (__uid_t __uid) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+extern int setreuid (__uid_t __ruid, __uid_t __euid) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+extern int seteuid (__uid_t __uid) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+
+
+extern int setgid (__gid_t __gid) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+extern int setregid (__gid_t __rgid, __gid_t __egid) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+extern int setegid (__gid_t __gid) __attribute__ ((__nothrow__ , __leaf__)) ;
+extern __pid_t fork (void) __attribute__ ((__nothrow__));
+
+
+
+
+
+
+
+extern __pid_t vfork (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern char *ttyname (int __fd) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern int ttyname_r (int __fd, char *__buf, size_t __buflen)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2))) ;
+
+
+
+extern int isatty (int __fd) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern int ttyslot (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern int link (const char *__from, const char *__to)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))) ;
+
+
+
+
+extern int linkat (int __fromfd, const char *__from, int __tofd,
+     const char *__to, int __flags)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2, 4))) ;
+
+
+
+
+extern int symlink (const char *__from, const char *__to)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))) ;
+
+
+
+
+extern ssize_t readlink (const char *__restrict __path,
+    char *__restrict __buf, size_t __len)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))) ;
+
+
+
+
+extern int symlinkat (const char *__from, int __tofd,
+        const char *__to) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 3))) ;
+
+
+extern ssize_t readlinkat (int __fd, const char *__restrict __path,
+      char *__restrict __buf, size_t __len)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2, 3))) ;
+
+
+
+extern int unlink (const char *__name) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern int unlinkat (int __fd, const char *__name, int __flag)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+
+
+
+extern int rmdir (const char *__path) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+extern __pid_t tcgetpgrp (int __fd) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int tcsetpgrp (int __fd, __pid_t __pgrp_id) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+extern char *getlogin (void);
+
+
+
+
+
+
+
+extern int getlogin_r (char *__name, size_t __name_len) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+extern int setlogin (const char *__name) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+extern char *optarg;
+extern int optind;
+
+
+
+
+extern int opterr;
+
+
+
+extern int optopt;
+extern int getopt (int ___argc, char *const *___argv, const char *__shortopts)
+       __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+extern int gethostname (char *__name, size_t __len) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+extern int sethostname (const char *__name, size_t __len)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+
+
+extern int sethostid (long int __id) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+
+extern int getdomainname (char *__name, size_t __len)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+extern int setdomainname (const char *__name, size_t __len)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+
+
+
+
+extern int vhangup (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern int revoke (const char *__file) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+
+
+
+
+
+
+extern int profil (unsigned short int *__sample_buffer, size_t __size,
+     size_t __offset, unsigned int __scale)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+extern int acct (const char *__name) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern char *getusershell (void) __attribute__ ((__nothrow__ , __leaf__));
+extern void endusershell (void) __attribute__ ((__nothrow__ , __leaf__));
+extern void setusershell (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern int daemon (int __nochdir, int __noclose) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+
+
+extern int chroot (const char *__path) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+
+
+
+extern char *getpass (const char *__prompt) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+
+
+extern int fsync (int __fd);
+extern long int gethostid (void);
+
+
+extern void sync (void) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern int getpagesize (void) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));
+
+
+
+
+extern int getdtablesize (void) __attribute__ ((__nothrow__ , __leaf__));
+extern int truncate (const char *__file, __off_t __length)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) ;
+extern int ftruncate (int __fd, __off_t __length) __attribute__ ((__nothrow__ , __leaf__)) ;
+extern int brk (void *__addr) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+
+extern void *sbrk (intptr_t __delta) __attribute__ ((__nothrow__ , __leaf__));
+extern long int syscall (long int __sysno, ...) __attribute__ ((__nothrow__ , __leaf__));
+extern int lockf (int __fd, int __cmd, __off_t __len) ;
+extern int fdatasync (int __fildes);
+
+
+
+struct stat
+  {
+    __dev_t st_dev;
+
+
+
+
+    __ino_t st_ino;
+
+
+
+
+
+
+
+    __nlink_t st_nlink;
+    __mode_t st_mode;
+
+    __uid_t st_uid;
+    __gid_t st_gid;
+
+    int __pad0;
+
+    __dev_t st_rdev;
+
+
+
+
+    __off_t st_size;
+
+
+
+    __blksize_t st_blksize;
+
+    __blkcnt_t st_blocks;
+    struct timespec st_atim;
+    struct timespec st_mtim;
+    struct timespec st_ctim;
+    __syscall_slong_t __unused[3];
+  };
+extern int stat (const char *__restrict __file,
+   struct stat *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+
+
+
+extern int fstat (int __fd, struct stat *__buf) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+extern int fstatat (int __fd, const char *__restrict __file,
+      struct stat *__restrict __buf, int __flag)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2, 3)));
+extern int lstat (const char *__restrict __file,
+    struct stat *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
+extern int chmod (const char *__file, __mode_t __mode)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+extern int lchmod (const char *__file, __mode_t __mode)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+extern int fchmod (int __fd, __mode_t __mode) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern int fchmodat (int __fd, const char *__file, __mode_t __mode,
+       int __flag)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2))) ;
+
+
+
+
+
+
+extern __mode_t umask (__mode_t __mask) __attribute__ ((__nothrow__ , __leaf__));
+extern int mkdir (const char *__path, __mode_t __mode)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+extern int mkdirat (int __fd, const char *__path, __mode_t __mode)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+
+
+
+
+
+
+extern int mknod (const char *__path, __mode_t __mode, __dev_t __dev)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+extern int mknodat (int __fd, const char *__path, __mode_t __mode,
+      __dev_t __dev) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+
+
+
+
+
+extern int mkfifo (const char *__path, __mode_t __mode)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
+
+
+
+
+
+extern int mkfifoat (int __fd, const char *__path, __mode_t __mode)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+
+
+
+
+
+extern int utimensat (int __fd, const char *__path,
+        const struct timespec __times[2],
+        int __flags)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2)));
+
+
+
+
+extern int futimens (int __fd, const struct timespec __times[2]) __attribute__ ((__nothrow__ , __leaf__));
+extern int __fxstat (int __ver, int __fildes, struct stat *__stat_buf)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3)));
+extern int __xstat (int __ver, const char *__filename,
+      struct stat *__stat_buf) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2, 3)));
+extern int __lxstat (int __ver, const char *__filename,
+       struct stat *__stat_buf) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2, 3)));
+extern int __fxstatat (int __ver, int __fildes, const char *__filename,
+         struct stat *__stat_buf, int __flag)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3, 4)));
+extern int __xmknod (int __ver, const char *__path, __mode_t __mode,
+       __dev_t *__dev) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2, 4)));
+
+extern int __xmknodat (int __ver, int __fd, const char *__path,
+         __mode_t __mode, __dev_t *__dev)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3, 5)));
+
+
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+__attribute__ ((__nothrow__ , __leaf__)) stat (const char *__path, struct stat *__statbuf)
+{
+  return __xstat (1, __path, __statbuf);
+}
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+__attribute__ ((__nothrow__ , __leaf__)) lstat (const char *__path, struct stat *__statbuf)
+{
+  return __lxstat (1, __path, __statbuf);
+}
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+__attribute__ ((__nothrow__ , __leaf__)) fstat (int __fd, struct stat *__statbuf)
+{
+  return __fxstat (1, __fd, __statbuf);
+}
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+__attribute__ ((__nothrow__ , __leaf__)) fstatat (int __fd, const char *__filename, struct stat *__statbuf, int __flag)
+
+{
+  return __fxstatat (1, __fd, __filename, __statbuf, __flag);
+}
+
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+__attribute__ ((__nothrow__ , __leaf__)) mknod (const char *__path, __mode_t __mode, __dev_t __dev)
+{
+  return __xmknod (0, __path, __mode, &__dev);
+}
+
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+__attribute__ ((__nothrow__ , __leaf__)) mknodat (int __fd, const char *__path, __mode_t __mode, __dev_t __dev)
+
+{
+  return __xmknodat (0, __fd, __path, __mode, &__dev);
+}
+
+
+
+
+
+
+
+
+struct flock
+  {
+    short int l_type;
+    short int l_whence;
+
+    __off_t l_start;
+    __off_t l_len;
+
+
+
+
+    __pid_t l_pid;
+  };
+
+
+extern int fcntl (int __fd, int __cmd, ...);
+extern int open (const char *__file, int __oflag, ...) __attribute__ ((__nonnull__ (1)));
+extern int openat (int __fd, const char *__file, int __oflag, ...)
+     __attribute__ ((__nonnull__ (2)));
+extern int creat (const char *__file, mode_t __mode) __attribute__ ((__nonnull__ (1)));
+extern int posix_fadvise (int __fd, off_t __offset, off_t __len,
+     int __advise) __attribute__ ((__nothrow__ , __leaf__));
+extern int posix_fallocate (int __fd, off_t __offset, off_t __len);
+
+
+
+
+extern int flock (int __fd, int __operation) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+struct _IO_FILE;
+
+
+
+typedef struct _IO_FILE FILE;
+
+
+
+
+
+typedef struct _IO_FILE __FILE;
+
+
+
+
+typedef struct
+{
+  int __count;
+  union
+  {
+
+    unsigned int __wch;
+
+
+
+    char __wchb[4];
+  } __value;
+} __mbstate_t;
+typedef struct
+{
+  __off_t __pos;
+  __mbstate_t __state;
+} _G_fpos_t;
+typedef struct
+{
+  __off64_t __pos;
+  __mbstate_t __state;
+} _G_fpos64_t;
+struct _IO_jump_t; struct _IO_FILE;
+typedef void _IO_lock_t;
+
+
+
+
+
+struct _IO_marker {
+  struct _IO_marker *_next;
+  struct _IO_FILE *_sbuf;
+
+
+
+  int _pos;
+};
+
+
+enum __codecvt_result
+{
+  __codecvt_ok,
+  __codecvt_partial,
+  __codecvt_error,
+  __codecvt_noconv
+};
+struct _IO_FILE {
+  int _flags;
+
+
+
+
+  char* _IO_read_ptr;
+  char* _IO_read_end;
+  char* _IO_read_base;
+  char* _IO_write_base;
+  char* _IO_write_ptr;
+  char* _IO_write_end;
+  char* _IO_buf_base;
+  char* _IO_buf_end;
+
+  char *_IO_save_base;
+  char *_IO_backup_base;
+  char *_IO_save_end;
+
+  struct _IO_marker *_markers;
+
+  struct _IO_FILE *_chain;
+
+  int _fileno;
+
+
+
+  int _flags2;
+
+  __off_t _old_offset;
+
+
+
+  unsigned short _cur_column;
+  signed char _vtable_offset;
+  char _shortbuf[1];
+
+
+
+  _IO_lock_t *_lock;
+  __off64_t _offset;
+  void *__pad1;
+  void *__pad2;
+  void *__pad3;
+  void *__pad4;
+  size_t __pad5;
+
+  int _mode;
+
+  char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
+
+};
+
+
+typedef struct _IO_FILE _IO_FILE;
+
+
+struct _IO_FILE_plus;
+
+extern struct _IO_FILE_plus _IO_2_1_stdin_;
+extern struct _IO_FILE_plus _IO_2_1_stdout_;
+extern struct _IO_FILE_plus _IO_2_1_stderr_;
+typedef __ssize_t __io_read_fn (void *__cookie, char *__buf, size_t __nbytes);
+
+
+
+
+
+
+
+typedef __ssize_t __io_write_fn (void *__cookie, const char *__buf,
+     size_t __n);
+
+
+
+
+
+
+
+typedef int __io_seek_fn (void *__cookie, __off64_t *__pos, int __w);
+
+
+typedef int __io_close_fn (void *__cookie);
+extern int __underflow (_IO_FILE *);
+extern int __uflow (_IO_FILE *);
+extern int __overflow (_IO_FILE *, int);
+extern int _IO_getc (_IO_FILE *__fp);
+extern int _IO_putc (int __c, _IO_FILE *__fp);
+extern int _IO_feof (_IO_FILE *__fp) __attribute__ ((__nothrow__ , __leaf__));
+extern int _IO_ferror (_IO_FILE *__fp) __attribute__ ((__nothrow__ , __leaf__));
+
+extern int _IO_peekc_locked (_IO_FILE *__fp);
+
+
+
+
+
+extern void _IO_flockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__));
+extern void _IO_funlockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__));
+extern int _IO_ftrylockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__));
+extern int _IO_vfscanf (_IO_FILE * __restrict, const char * __restrict,
+   __gnuc_va_list, int *__restrict);
+extern int _IO_vfprintf (_IO_FILE *__restrict, const char *__restrict,
+    __gnuc_va_list);
+extern __ssize_t _IO_padn (_IO_FILE *, int, __ssize_t);
+extern size_t _IO_sgetn (_IO_FILE *, void *, size_t);
+
+extern __off64_t _IO_seekoff (_IO_FILE *, __off64_t, int, int);
+extern __off64_t _IO_seekpos (_IO_FILE *, __off64_t, int);
+
+extern void _IO_free_backup_area (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__));
+
+
+typedef _G_fpos_t fpos_t;
+
+
+
+
+
+
+
+extern struct _IO_FILE *stdin;
+extern struct _IO_FILE *stdout;
+extern struct _IO_FILE *stderr;
+
+
+
+
+
+
+
+extern int remove (const char *__filename) __attribute__ ((__nothrow__ , __leaf__));
+
+extern int rename (const char *__old, const char *__new) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+extern int renameat (int __oldfd, const char *__old, int __newfd,
+       const char *__new) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+
+extern FILE *tmpfile (void) ;
+extern char *tmpnam (char *__s) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+
+extern char *tmpnam_r (char *__s) __attribute__ ((__nothrow__ , __leaf__)) ;
+extern char *tempnam (const char *__dir, const char *__pfx)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) ;
+
+
+
+
+
+
+
+
+extern int fclose (FILE *__stream);
+
+
+
+
+extern int fflush (FILE *__stream);
+
+extern int fflush_unlocked (FILE *__stream);
+
+
+
+
+
+
+extern FILE *fopen (const char *__restrict __filename,
+      const char *__restrict __modes) ;
+
+
+
+
+extern FILE *freopen (const char *__restrict __filename,
+        const char *__restrict __modes,
+        FILE *__restrict __stream) ;
+
+extern FILE *fdopen (int __fd, const char *__modes) __attribute__ ((__nothrow__ , __leaf__)) ;
+extern FILE *fmemopen (void *__s, size_t __len, const char *__modes)
+  __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+extern FILE *open_memstream (char **__bufloc, size_t *__sizeloc) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+
+
+extern void setbuf (FILE *__restrict __stream, char *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern int setvbuf (FILE *__restrict __stream, char *__restrict __buf,
+      int __modes, size_t __n) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+extern void setbuffer (FILE *__restrict __stream, char *__restrict __buf,
+         size_t __size) __attribute__ ((__nothrow__ , __leaf__));
+
+
+extern void setlinebuf (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+
+
+
+
+
+extern int fprintf (FILE *__restrict __stream,
+      const char *__restrict __format, ...);
+
+
+
+
+extern int printf (const char *__restrict __format, ...);
+
+extern int sprintf (char *__restrict __s,
+      const char *__restrict __format, ...) __attribute__ ((__nothrow__));
+
+
+
+
+
+extern int vfprintf (FILE *__restrict __s, const char *__restrict __format,
+       __gnuc_va_list __arg);
+
+
+
+
+extern int vprintf (const char *__restrict __format, __gnuc_va_list __arg);
+
+extern int vsprintf (char *__restrict __s, const char *__restrict __format,
+       __gnuc_va_list __arg) __attribute__ ((__nothrow__));
+
+
+
+
+
+extern int snprintf (char *__restrict __s, size_t __maxlen,
+       const char *__restrict __format, ...)
+     __attribute__ ((__nothrow__)) __attribute__ ((__format__ (__printf__, 3, 4)));
+
+extern int vsnprintf (char *__restrict __s, size_t __maxlen,
+        const char *__restrict __format, __gnuc_va_list __arg)
+     __attribute__ ((__nothrow__)) __attribute__ ((__format__ (__printf__, 3, 0)));
+
+extern int vdprintf (int __fd, const char *__restrict __fmt,
+       __gnuc_va_list __arg)
+     __attribute__ ((__format__ (__printf__, 2, 0)));
+extern int dprintf (int __fd, const char *__restrict __fmt, ...)
+     __attribute__ ((__format__ (__printf__, 2, 3)));
+
+
+
+
+
+
+
+
+extern int fscanf (FILE *__restrict __stream,
+     const char *__restrict __format, ...) ;
+
+
+
+
+extern int scanf (const char *__restrict __format, ...) ;
+
+extern int sscanf (const char *__restrict __s,
+     const char *__restrict __format, ...) __attribute__ ((__nothrow__ , __leaf__));
+extern int fscanf (FILE *__restrict __stream, const char *__restrict __format, ...) __asm__ ("" "__isoc99_fscanf")
+
+                               ;
+extern int scanf (const char *__restrict __format, ...) __asm__ ("" "__isoc99_scanf")
+                              ;
+extern int sscanf (const char *__restrict __s, const char *__restrict __format, ...) __asm__ ("" "__isoc99_sscanf") __attribute__ ((__nothrow__ , __leaf__))
+
+                      ;
+
+
+
+
+
+
+
+
+extern int vfscanf (FILE *__restrict __s, const char *__restrict __format,
+      __gnuc_va_list __arg)
+     __attribute__ ((__format__ (__scanf__, 2, 0))) ;
+
+
+
+
+
+extern int vscanf (const char *__restrict __format, __gnuc_va_list __arg)
+     __attribute__ ((__format__ (__scanf__, 1, 0))) ;
+
+
+extern int vsscanf (const char *__restrict __s,
+      const char *__restrict __format, __gnuc_va_list __arg)
+     __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__format__ (__scanf__, 2, 0)));
+extern int vfscanf (FILE *__restrict __s, const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vfscanf")
+
+
+
+     __attribute__ ((__format__ (__scanf__, 2, 0))) ;
+extern int vscanf (const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vscanf")
+
+     __attribute__ ((__format__ (__scanf__, 1, 0))) ;
+extern int vsscanf (const char *__restrict __s, const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vsscanf") __attribute__ ((__nothrow__ , __leaf__))
+
+
+
+     __attribute__ ((__format__ (__scanf__, 2, 0)));
+
+
+
+
+
+
+
+
+
+extern int fgetc (FILE *__stream);
+extern int getc (FILE *__stream);
+
+
+
+
+
+extern int getchar (void);
+
+extern int getc_unlocked (FILE *__stream);
+extern int getchar_unlocked (void);
+extern int fgetc_unlocked (FILE *__stream);
+
+
+
+
+
+
+
+
+
+
+
+extern int fputc (int __c, FILE *__stream);
+extern int putc (int __c, FILE *__stream);
+
+
+
+
+
+extern int putchar (int __c);
+
+extern int fputc_unlocked (int __c, FILE *__stream);
+
+
+
+
+
+
+
+extern int putc_unlocked (int __c, FILE *__stream);
+extern int putchar_unlocked (int __c);
+
+
+
+
+
+
+extern int getw (FILE *__stream);
+
+
+extern int putw (int __w, FILE *__stream);
+
+
+
+
+
+
+
+
+extern char *fgets (char *__restrict __s, int __n, FILE *__restrict __stream)
+     ;
+
+extern __ssize_t __getdelim (char **__restrict __lineptr,
+          size_t *__restrict __n, int __delimiter,
+          FILE *__restrict __stream) ;
+extern __ssize_t getdelim (char **__restrict __lineptr,
+        size_t *__restrict __n, int __delimiter,
+        FILE *__restrict __stream) ;
+
+
+
+
+
+
+
+extern __ssize_t getline (char **__restrict __lineptr,
+       size_t *__restrict __n,
+       FILE *__restrict __stream) ;
+
+
+
+
+
+
+
+
+extern int fputs (const char *__restrict __s, FILE *__restrict __stream);
+
+
+
+
+
+extern int puts (const char *__s);
+
+
+
+
+
+
+extern int ungetc (int __c, FILE *__stream);
+
+
+
+
+
+
+extern size_t fread (void *__restrict __ptr, size_t __size,
+       size_t __n, FILE *__restrict __stream) ;
+
+
+
+
+extern size_t fwrite (const void *__restrict __ptr, size_t __size,
+        size_t __n, FILE *__restrict __s);
+
+extern size_t fread_unlocked (void *__restrict __ptr, size_t __size,
+         size_t __n, FILE *__restrict __stream) ;
+extern size_t fwrite_unlocked (const void *__restrict __ptr, size_t __size,
+          size_t __n, FILE *__restrict __stream);
+
+
+
+
+
+
+
+
+extern int fseek (FILE *__stream, long int __off, int __whence);
+
+
+
+
+extern long int ftell (FILE *__stream) ;
+
+
+
+
+extern void rewind (FILE *__stream);
+
+extern int fseeko (FILE *__stream, __off_t __off, int __whence);
+
+
+
+
+extern __off_t ftello (FILE *__stream) ;
+
+
+
+
+
+
+extern int fgetpos (FILE *__restrict __stream, fpos_t *__restrict __pos);
+
+
+
+
+extern int fsetpos (FILE *__stream, const fpos_t *__pos);
+
+
+
+extern void clearerr (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
+
+extern int feof (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+extern int ferror (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+extern void clearerr_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
+extern int feof_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
+extern int ferror_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+
+
+
+
+extern void perror (const char *__s);
+
+
+
+
+
+
+extern int sys_nerr;
+extern const char *const sys_errlist[];
+
+
+
+
+extern int fileno (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+
+
+extern int fileno_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
+extern FILE *popen (const char *__command, const char *__modes) ;
+
+
+
+
+
+extern int pclose (FILE *__stream);
+
+
+
+
+
+extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__));
+extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
+
+
+
+extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
+
+
+extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
+extern __inline __attribute__ ((__gnu_inline__)) int
+vprintf (const char *__restrict __fmt, __gnuc_va_list __arg)
+{
+  return vfprintf (stdout, __fmt, __arg);
+}
+
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+getchar (void)
+{
+  return _IO_getc (stdin);
+}
+
+
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+fgetc_unlocked (FILE *__fp)
+{
+  return (__builtin_expect (((__fp)->_IO_read_ptr >= (__fp)->_IO_read_end), 0) ? __uflow (__fp) : *(unsigned char *) (__fp)->_IO_read_ptr++);
+}
+
+
+
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+getc_unlocked (FILE *__fp)
+{
+  return (__builtin_expect (((__fp)->_IO_read_ptr >= (__fp)->_IO_read_end), 0) ? __uflow (__fp) : *(unsigned char *) (__fp)->_IO_read_ptr++);
+}
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+getchar_unlocked (void)
+{
+  return (__builtin_expect (((stdin)->_IO_read_ptr >= (stdin)->_IO_read_end), 0) ? __uflow (stdin) : *(unsigned char *) (stdin)->_IO_read_ptr++);
+}
+
+
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+putchar (int __c)
+{
+  return _IO_putc (__c, stdout);
+}
+
+
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+fputc_unlocked (int __c, FILE *__stream)
+{
+  return (__builtin_expect (((__stream)->_IO_write_ptr >= (__stream)->_IO_write_end), 0) ? __overflow (__stream, (unsigned char) (__c)) : (unsigned char) (*(__stream)->_IO_write_ptr++ = (__c)));
+}
+
+
+
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+putc_unlocked (int __c, FILE *__stream)
+{
+  return (__builtin_expect (((__stream)->_IO_write_ptr >= (__stream)->_IO_write_end), 0) ? __overflow (__stream, (unsigned char) (__c)) : (unsigned char) (*(__stream)->_IO_write_ptr++ = (__c)));
+}
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+putchar_unlocked (int __c)
+{
+  return (__builtin_expect (((stdout)->_IO_write_ptr >= (stdout)->_IO_write_end), 0) ? __overflow (stdout, (unsigned char) (__c)) : (unsigned char) (*(stdout)->_IO_write_ptr++ = (__c)));
+}
+extern __inline __attribute__ ((__gnu_inline__)) int
+__attribute__ ((__nothrow__ , __leaf__)) feof_unlocked (FILE *__stream)
+{
+  return (((__stream)->_flags & 0x10) != 0);
+}
+
+
+extern __inline __attribute__ ((__gnu_inline__)) int
+__attribute__ ((__nothrow__ , __leaf__)) ferror_unlocked (FILE *__stream)
+{
+  return (((__stream)->_flags & 0x20) != 0);
+}
+
+
+
+struct addrlist;
+
+
+krb5_error_code krb5_lock_file (krb5_context, int, int);
+krb5_error_code krb5_unlock_file (krb5_context, int);
+krb5_error_code krb5_sendto_kdc (krb5_context, const krb5_data *,
+     const krb5_data *, krb5_data *, int *, int);
+krb5_error_code krb5int_sendto (krb5_context, const krb5_data *,
+    const struct addrlist *, krb5_data *,
+    struct sockaddr *, socklen_t *, int *);
+krb5_error_code krb5_get_krbhst (krb5_context, const krb5_data *, char *** );
+krb5_error_code krb5_free_krbhst (krb5_context, char * const * );
+krb5_error_code krb5_create_secure_file (krb5_context, const char * pathname);
+krb5_error_code krb5_sync_disk_file (krb5_context, FILE *fp);
+
+krb5_error_code krb5int_get_fq_local_hostname (char *, size_t);
+
+krb5_error_code krb5_os_init_context (krb5_context);
+
+void krb5_os_free_context (krb5_context);
+
+
+
+krb5_error_code os_get_default_config_files
+    (profile_filespec_t **pfiles, krb5_boolean secure);
+
+krb5_error_code krb5_find_config_files (void);
+
+krb5_error_code krb5_os_hostaddr
+ (krb5_context, const char *, krb5_address ***);
+
+
+
+struct addrlist {
+    struct addrinfo **addrs;
+    int naddrs;
+    int space;
+};
+
+extern void krb5int_free_addrlist (struct addrlist *);
+extern int krb5int_grow_addrlist (struct addrlist *, int);
+extern int krb5int_add_host_to_list (struct addrlist *, const char *,
+         int, int, int, int);
+
+krb5_error_code
+krb5int_locate_server (krb5_context,
+         const krb5_data *realm,
+         struct addrlist *,
+
+         int want_masters,
+
+         const char *profilename,
+
+         const char *dnsname,
+         int is_stream_service,
+
+
+
+
+
+         int dflport1, int dflport2,
+         int family);
+
+
+
+
+
+struct krb5_enc_provider {
+
+
+    size_t block_size, keybytes, keylength;
+
+
+    krb5_error_code (*encrypt) (const krb5_keyblock *key,
+    const krb5_data *cipher_state,
+    const krb5_data *input,
+    krb5_data *output);
+
+    krb5_error_code (*decrypt) (const krb5_keyblock *key,
+    const krb5_data *ivec,
+    const krb5_data *input,
+    krb5_data *output);
+
+    krb5_error_code (*make_key) (const krb5_data *randombits,
+     krb5_keyblock *key);
+
+  krb5_error_code (*init_state) (const krb5_keyblock *key,
+     krb5_keyusage keyusage, krb5_data *out_state);
+  krb5_error_code (*free_state) (krb5_data *state);
+
+};
+
+struct krb5_hash_provider {
+    size_t hashsize, blocksize;
+
+
+    krb5_error_code (*hash) (unsigned int icount, const krb5_data *input,
+        krb5_data *output);
+};
+
+struct krb5_keyhash_provider {
+    size_t hashsize;
+
+    krb5_error_code (*hash) (const krb5_keyblock *key,
+        krb5_keyusage keyusage,
+        const krb5_data *ivec,
+        const krb5_data *input,
+        krb5_data *output);
+
+    krb5_error_code (*verify) (const krb5_keyblock *key,
+          krb5_keyusage keyusage,
+          const krb5_data *ivec,
+          const krb5_data *input,
+          const krb5_data *hash,
+          krb5_boolean *valid);
+};
+
+typedef void (*krb5_encrypt_length_func) (const struct krb5_enc_provider *enc,
+  const struct krb5_hash_provider *hash,
+  size_t inputlen, size_t *length);
+
+typedef krb5_error_code (*krb5_crypt_func) (const struct krb5_enc_provider *enc,
+  const struct krb5_hash_provider *hash,
+  const krb5_keyblock *key, krb5_keyusage keyusage,
+  const krb5_data *ivec,
+  const krb5_data *input, krb5_data *output);
+
+typedef krb5_error_code (*krb5_str2key_func) (const struct krb5_enc_provider *enc, const krb5_data *string,
+  const krb5_data *salt, const krb5_data *parm, krb5_keyblock *key);
+
+struct krb5_keytypes {
+    krb5_enctype etype;
+    char *in_string;
+    char *out_string;
+    const struct krb5_enc_provider *enc;
+    const struct krb5_hash_provider *hash;
+    krb5_encrypt_length_func encrypt_len;
+    krb5_crypt_func encrypt;
+    krb5_crypt_func decrypt;
+    krb5_str2key_func str2key;
+    krb5_cksumtype required_ctype;
+};
+
+struct krb5_cksumtypes {
+    krb5_cksumtype ctype;
+    unsigned int flags;
+    char *in_string;
+    char *out_string;
+
+
+
+
+    krb5_enctype keyed_etype;
+
+
+
+
+
+    const struct krb5_keyhash_provider *keyhash;
+    const struct krb5_hash_provider *hash;
+
+
+
+
+
+    unsigned int trunc_size;
+};
+void krb5_nfold
+(unsigned int inbits, const unsigned char *in,
+  unsigned int outbits, unsigned char *out);
+
+krb5_error_code krb5_hmac
+(const struct krb5_hash_provider *hash,
+  const krb5_keyblock *key, unsigned int icount,
+  const krb5_data *input, krb5_data *output);
+
+krb5_error_code krb5int_pbkdf2_hmac_sha1 (const krb5_data *, unsigned long,
+       const krb5_data *,
+       const krb5_data *);
+krb5_error_code krb5int_des_init_state
+(const krb5_keyblock *key, krb5_keyusage keyusage, krb5_data *new_state);
+
+
+
+
+
+krb5_error_code krb5int_default_free_state
+(krb5_data *state);
+
+
+
+
+
+krb5_error_code krb5int_c_combine_keys
+(krb5_context context, krb5_keyblock *key1, krb5_keyblock *key2,
+  krb5_keyblock *outkey);
+
+
+
+
+extern void krb5int_prng_cleanup (void);
+
+
+
+
+
+
+
+extern const struct krb5_enc_provider krb5int_enc_arcfour;
+extern const struct krb5_hash_provider krb5int_hash_md5;
+
+
+
+
+
+krb5_error_code krb5_crypto_os_localaddr
+ (krb5_address ***);
+
+krb5_error_code krb5_crypto_us_timeofday
+ (krb5_int32 *,
+  krb5_int32 *);
+
+time_t gmt_mktime (struct tm *);
+
+
+
+
+
+krb5_error_code krb5_encrypt_helper
+(krb5_context context, const krb5_keyblock *key,
+  krb5_keyusage keyusage, const krb5_data *plain,
+  krb5_enc_data *cipher);
+typedef struct _krb5_tl_data {
+    struct _krb5_tl_data* tl_data_next;
+    krb5_int16 tl_data_type;
+    krb5_ui_2 tl_data_length;
+    krb5_octet * tl_data_contents;
+} krb5_tl_data;
+
+
+
+
+
+
+
+typedef struct _krb5_key_data {
+    krb5_int16 key_data_ver;
+    krb5_int16 key_data_kvno;
+    krb5_int16 key_data_type[2];
+    krb5_ui_2 key_data_length[2];
+    krb5_octet * key_data_contents[2];
+} krb5_key_data;
+
+
+
+typedef struct _krb5_keysalt {
+    krb5_int16 type;
+    krb5_data data;
+} krb5_keysalt;
+
+typedef struct _krb5_db_entry_new {
+    krb5_magic magic;
+    krb5_ui_2 len;
+    krb5_flags attributes;
+    krb5_deltat max_life;
+    krb5_deltat max_renewable_life;
+    krb5_timestamp expiration;
+    krb5_timestamp pw_expiration;
+    krb5_timestamp last_success;
+    krb5_timestamp last_failed;
+    krb5_kvno fail_auth_count;
+    krb5_int16 n_tl_data;
+    krb5_int16 n_key_data;
+    krb5_ui_2 e_length;
+    krb5_octet * e_data;
+
+    krb5_principal princ;
+    krb5_tl_data * tl_data;
+    krb5_key_data * key_data;
+} krb5_db_entry;
+extern char *krb5_mkey_pwd_prompt1;
+extern char *krb5_mkey_pwd_prompt2;
+krb5_error_code krb5_db_set_name (krb5_context, char * );
+krb5_error_code krb5_db_init (krb5_context);
+krb5_error_code krb5_db_fini (krb5_context);
+krb5_error_code krb5_db_get_age (krb5_context, char *, time_t * );
+krb5_error_code krb5_db_create (krb5_context, char *, krb5_int32 );
+krb5_error_code krb5_db_rename (krb5_context, char *, char * );
+krb5_error_code krb5_db_get_principal (krb5_context, krb5_const_principal ,
+           krb5_db_entry *, int *,
+           krb5_boolean * );
+void krb5_db_free_principal (krb5_context, krb5_db_entry *, int );
+krb5_error_code krb5_db_put_principal (krb5_context, krb5_db_entry *, int * );
+krb5_error_code krb5_db_delete_principal (krb5_context, krb5_const_principal,
+       int * );
+krb5_error_code krb5_db_iterate (krb5_context,
+     krb5_error_code (* ) (krb5_pointer,
+             krb5_db_entry *),
+     krb5_pointer);
+krb5_error_code krb5_db_iterate_ext (krb5_context,
+         krb5_error_code (* ) (krb5_pointer,
+                   krb5_db_entry *),
+         krb5_pointer, int, int);
+krb5_error_code krb5_db_verify_master_key (krb5_context, krb5_principal,
+        krb5_keyblock *);
+krb5_error_code krb5_db_store_mkey (krb5_context, char *, krb5_principal,
+        krb5_keyblock *);
+
+krb5_error_code krb5_db_setup_mkey_name (krb5_context, const char *,
+      const char *, char **,
+      krb5_principal *);
+
+krb5_error_code krb5_db_set_mkey (krb5_context, krb5_keyblock *);
+
+krb5_error_code krb5_db_get_mkey (krb5_context, krb5_keyblock **);
+krb5_error_code krb5_db_destroy (krb5_context, char * );
+krb5_error_code krb5_db_lock (krb5_context, int );
+krb5_error_code krb5_db_unlock (krb5_context);
+krb5_error_code krb5_db_set_nonblocking (krb5_context, krb5_boolean,
+      krb5_boolean * );
+krb5_boolean krb5_db_set_lockmode (krb5_context, krb5_boolean);
+krb5_error_code krb5_db_fetch_mkey (krb5_context, krb5_principal, krb5_enctype,
+        krb5_boolean, krb5_boolean, char *,
+        krb5_data *,
+        krb5_keyblock * );
+
+krb5_error_code krb5_db_open_database (krb5_context);
+krb5_error_code krb5_db_close_database (krb5_context);
+
+krb5_error_code krb5_dbekd_encrypt_key_data (krb5_context,
+          const krb5_keyblock *,
+          const krb5_keyblock *,
+          const krb5_keysalt *,
+          int,
+          krb5_key_data *);
+krb5_error_code krb5_dbekd_decrypt_key_data (krb5_context,
+          const krb5_keyblock *,
+          const krb5_key_data *,
+          krb5_keyblock *,
+          krb5_keysalt *);
+krb5_error_code krb5_dbe_create_key_data (krb5_context,
+       krb5_db_entry *);
+krb5_error_code krb5_dbe_update_tl_data (krb5_context,
+      krb5_db_entry *,
+      krb5_tl_data *);
+krb5_error_code krb5_dbe_lookup_tl_data (krb5_context,
+      krb5_db_entry *,
+      krb5_tl_data *);
+krb5_error_code krb5_dbe_update_last_pwd_change (krb5_context,
+       krb5_db_entry *,
+       krb5_timestamp);
+krb5_error_code krb5_dbe_lookup_last_pwd_change (krb5_context,
+       krb5_db_entry *,
+       krb5_timestamp *);
+krb5_error_code krb5_dbe_update_mod_princ_data (krb5_context,
+      krb5_db_entry *,
+      krb5_timestamp,
+      krb5_const_principal);
+krb5_error_code krb5_dbe_lookup_mod_princ_data (krb5_context,
+      krb5_db_entry *,
+      krb5_timestamp *,
+      krb5_principal *);
+int krb5_encode_princ_dbkey (krb5_context, krb5_data *, krb5_const_principal);
+void krb5_free_princ_dbkey (krb5_context, krb5_data *);
+krb5_error_code krb5_encode_princ_contents (krb5_context, krb5_data *,
+         krb5_db_entry *);
+void krb5_free_princ_contents (krb5_context, krb5_data *);
+krb5_error_code krb5_decode_princ_contents (krb5_context, krb5_data *,
+         krb5_db_entry *);
+void krb5_dbe_free_contents (krb5_context, krb5_db_entry *);
+
+krb5_error_code krb5_dbe_find_enctype (krb5_context, krb5_db_entry *,
+           krb5_int32,
+           krb5_int32,
+           krb5_int32,
+           krb5_key_data **);
+
+krb5_error_code krb5_dbe_search_enctype (krb5_context,
+      krb5_db_entry *,
+      krb5_int32 *,
+      krb5_int32,
+      krb5_int32,
+      krb5_int32,
+      krb5_key_data **);
+
+struct __krb5_key_salt_tuple;
+
+krb5_error_code krb5_dbe_cpw (krb5_context,
+         krb5_keyblock *,
+         struct __krb5_key_salt_tuple *,
+         int,
+         char *,
+         int,
+         krb5_boolean,
+         krb5_db_entry *);
+krb5_error_code krb5_dbe_apw (krb5_context,
+         krb5_keyblock *,
+         struct __krb5_key_salt_tuple *,
+         int,
+         char *,
+         krb5_db_entry *);
+krb5_error_code krb5_dbe_crk (krb5_context,
+         krb5_keyblock *,
+         struct __krb5_key_salt_tuple *,
+         int,
+         krb5_boolean,
+         krb5_db_entry *);
+krb5_error_code krb5_dbe_ark (krb5_context,
+         krb5_keyblock *,
+         struct __krb5_key_salt_tuple *,
+         int,
+         krb5_db_entry *);
+
+krb5_error_code krb5_ser_db_context_init (krb5_context);
+
+
+
+
+
+
+
+typedef struct _krb5_os_context {
+ krb5_magic magic;
+ krb5_int32 time_offset;
+ krb5_int32 usec_offset;
+ krb5_int32 os_flags;
+ char * default_ccname;
+} *krb5_os_context;
+typedef struct _krb5_pa_enc_ts {
+    krb5_timestamp patimestamp;
+    krb5_int32 pausec;
+} krb5_pa_enc_ts;
+
+typedef krb5_error_code (*krb5_preauth_obtain_proc)
+    (krb5_context,
+      krb5_pa_data *,
+      krb5_etype_info,
+      krb5_keyblock *,
+      krb5_error_code ( * )(krb5_context,
+       const krb5_enctype,
+       krb5_data *,
+       krb5_const_pointer,
+       krb5_keyblock **),
+      krb5_const_pointer,
+      krb5_creds *,
+      krb5_kdc_req *,
+      krb5_pa_data **);
+
+typedef krb5_error_code (*krb5_preauth_process_proc)
+    (krb5_context,
+      krb5_pa_data *,
+      krb5_kdc_req *,
+      krb5_kdc_rep *,
+      krb5_error_code ( * )(krb5_context,
+       const krb5_enctype,
+       krb5_data *,
+       krb5_const_pointer,
+       krb5_keyblock **),
+      krb5_const_pointer,
+      krb5_error_code ( * )(krb5_context,
+       const krb5_keyblock *,
+       krb5_const_pointer,
+       krb5_kdc_rep * ),
+      krb5_keyblock **,
+      krb5_creds *,
+      krb5_int32 *,
+      krb5_int32 *);
+
+typedef struct _krb5_preauth_ops {
+    krb5_magic magic;
+    int type;
+    int flags;
+    krb5_preauth_obtain_proc obtain;
+    krb5_preauth_process_proc process;
+} krb5_preauth_ops;
+
+krb5_error_code krb5_obtain_padata
+     (krb5_context,
+  krb5_pa_data **,
+  krb5_error_code ( * )(krb5_context,
+            const krb5_enctype,
+            krb5_data *,
+            krb5_const_pointer,
+            krb5_keyblock **),
+  krb5_const_pointer,
+  krb5_creds *,
+  krb5_kdc_req *);
+
+krb5_error_code krb5_process_padata
+ (krb5_context,
+  krb5_kdc_req *,
+  krb5_kdc_rep *,
+  krb5_error_code ( * )(krb5_context,
+            const krb5_enctype,
+            krb5_data *,
+            krb5_const_pointer,
+            krb5_keyblock **),
+  krb5_const_pointer,
+  krb5_error_code ( * )(krb5_context,
+            const krb5_keyblock *,
+            krb5_const_pointer,
+            krb5_kdc_rep * ),
+  krb5_keyblock **,
+  krb5_creds *,
+  krb5_int32 *);
+
+void krb5_free_etype_info
+    (krb5_context, krb5_etype_info);
+krb5_error_code
+krb5int_copy_data_contents (krb5_context, const krb5_data *, krb5_data *);
+
+typedef krb5_error_code (*krb5_gic_get_as_key_fct)
+    (krb5_context,
+       krb5_principal,
+       krb5_enctype,
+       krb5_prompter_fct,
+       void *prompter_data,
+       krb5_data *salt,
+     krb5_data *s2kparams,
+       krb5_keyblock *as_key,
+       void *gak_data);
+
+krb5_error_code
+krb5_get_init_creds
+(krb5_context context,
+  krb5_creds *creds,
+  krb5_principal client,
+  krb5_prompter_fct prompter,
+  void *prompter_data,
+  krb5_deltat start_time,
+  char *in_tkt_service,
+  krb5_get_init_creds_opt *gic_options,
+  krb5_gic_get_as_key_fct gak,
+  void *gak_data,
+  int *master,
+  krb5_kdc_rep **as_reply);
+
+void krb5int_populate_gic_opt (
+    krb5_context, krb5_get_init_creds_opt *,
+    krb5_flags options, krb5_address * const *addrs, krb5_enctype *ktypes,
+    krb5_preauthtype *pre_auth_types, krb5_creds *creds);
+
+
+krb5_error_code krb5_do_preauth
+(krb5_context, krb5_kdc_req *,
+  krb5_pa_data **, krb5_pa_data ***,
+  krb5_data *salt, krb5_data *s2kparams,
+ krb5_enctype *,
+  krb5_keyblock *,
+  krb5_prompter_fct, void *,
+  krb5_gic_get_as_key_fct, void *);
+
+void krb5_free_sam_challenge
+ (krb5_context, krb5_sam_challenge * );
+void krb5_free_sam_challenge_2
+ (krb5_context, krb5_sam_challenge_2 * );
+void krb5_free_sam_challenge_2_body
+ (krb5_context, krb5_sam_challenge_2_body *);
+void krb5_free_sam_response
+ (krb5_context, krb5_sam_response * );
+void krb5_free_sam_response_2
+ (krb5_context, krb5_sam_response_2 * );
+void krb5_free_predicted_sam_response
+ (krb5_context, krb5_predicted_sam_response * );
+void krb5_free_enc_sam_response_enc
+ (krb5_context, krb5_enc_sam_response_enc * );
+void krb5_free_enc_sam_response_enc_2
+ (krb5_context, krb5_enc_sam_response_enc_2 * );
+void krb5_free_sam_challenge_contents
+ (krb5_context, krb5_sam_challenge * );
+void krb5_free_sam_challenge_2_contents
+ (krb5_context, krb5_sam_challenge_2 * );
+void krb5_free_sam_challenge_2_body_contents
+ (krb5_context, krb5_sam_challenge_2_body * );
+void krb5_free_sam_response_contents
+ (krb5_context, krb5_sam_response * );
+void krb5_free_sam_response_2_contents
+ (krb5_context, krb5_sam_response_2 *);
+void krb5_free_predicted_sam_response_contents
+ (krb5_context, krb5_predicted_sam_response * );
+void krb5_free_enc_sam_response_enc_contents
+ (krb5_context, krb5_enc_sam_response_enc * );
+void krb5_free_enc_sam_response_enc_2_contents
+ (krb5_context, krb5_enc_sam_response_enc_2 * );
+
+void krb5_free_pa_enc_ts
+ (krb5_context, krb5_pa_enc_ts *);
+
+
+
+struct _krb5_context {
+ krb5_magic magic;
+ krb5_enctype *in_tkt_ktypes;
+ int in_tkt_ktype_count;
+ krb5_enctype *tgs_ktypes;
+ int tgs_ktype_count;
+ struct _krb5_os_context os_context[1];
+ char *default_realm;
+ profile_t profile;
+ void *db_context;
+ int ser_ctx_count;
+ void *ser_ctx;
+
+ krb5_deltat clockskew;
+ krb5_cksumtype kdc_req_sumtype;
+ krb5_cksumtype default_ap_req_sumtype;
+ krb5_cksumtype default_safe_sumtype;
+ krb5_flags kdc_default_options;
+ krb5_flags library_options;
+ krb5_boolean profile_secure;
+ int fcc_default_format;
+ int scc_default_format;
+ krb5_prompt_type *prompt_types;
+
+
+
+ int udp_pref_limit;
+
+
+
+
+
+
+
+ krb5_enctype *conf_tgs_ktypes;
+ int conf_tgs_ktypes_count;
+
+ krb5_boolean use_conf_ktypes;
+
+
+        krb5_boolean profile_in_memory;
+
+};
+typedef struct _krb5_safe {
+    krb5_magic magic;
+    krb5_data user_data;
+    krb5_timestamp timestamp;
+    krb5_int32 usec;
+
+    krb5_ui_4 seq_number;
+    krb5_address *s_address;
+    krb5_address *r_address;
+    krb5_checksum *checksum;
+} krb5_safe;
+
+typedef struct _krb5_priv {
+    krb5_magic magic;
+    krb5_enc_data enc_part;
+} krb5_priv;
+
+typedef struct _krb5_priv_enc_part {
+    krb5_magic magic;
+    krb5_data user_data;
+    krb5_timestamp timestamp;
+    krb5_int32 usec;
+    krb5_ui_4 seq_number;
+    krb5_address *s_address;
+    krb5_address *r_address;
+} krb5_priv_enc_part;
+
+void krb5_free_safe
+ (krb5_context, krb5_safe * );
+void krb5_free_priv
+ (krb5_context, krb5_priv * );
+void krb5_free_priv_enc_part
+ (krb5_context, krb5_priv_enc_part * );
+krb5_error_code encode_krb5_authenticator
+ (const krb5_authenticator *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_ticket
+ (const krb5_ticket *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_encryption_key
+ (const krb5_keyblock *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_enc_tkt_part
+ (const krb5_enc_tkt_part *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_enc_kdc_rep_part
+ (const krb5_enc_kdc_rep_part *rep, krb5_data **code);
+
+
+krb5_error_code encode_krb5_as_rep
+ (const krb5_kdc_rep *rep, krb5_data **code);
+
+
+krb5_error_code encode_krb5_tgs_rep
+ (const krb5_kdc_rep *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_ap_req
+ (const krb5_ap_req *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_ap_rep
+ (const krb5_ap_rep *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_ap_rep_enc_part
+ (const krb5_ap_rep_enc_part *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_as_req
+ (const krb5_kdc_req *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_tgs_req
+ (const krb5_kdc_req *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_kdc_req_body
+ (const krb5_kdc_req *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_safe
+ (const krb5_safe *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_safe_with_body
+ (const krb5_safe *rep, const krb5_data *body, krb5_data **code);
+
+krb5_error_code encode_krb5_priv
+ (const krb5_priv *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_enc_priv_part
+ (const krb5_priv_enc_part *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_cred
+ (const krb5_cred *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_enc_cred_part
+ (const krb5_cred_enc_part *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_error
+ (const krb5_error *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_authdata
+ (const krb5_authdata **rep, krb5_data **code);
+
+krb5_error_code encode_krb5_pwd_sequence
+ (const passwd_phrase_element *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_pwd_data
+ (const krb5_pwd_data *rep, krb5_data **code);
+
+krb5_error_code encode_krb5_padata_sequence
+ (const krb5_pa_data ** rep, krb5_data **code);
+
+krb5_error_code encode_krb5_alt_method
+ (const krb5_alt_method *, krb5_data **code);
+
+krb5_error_code encode_krb5_etype_info
+ (const krb5_etype_info_entry **, krb5_data **code);
+krb5_error_code encode_krb5_etype_info2
+ (const krb5_etype_info_entry **, krb5_data **code);
+
+krb5_error_code encode_krb5_enc_data
+     (const krb5_enc_data *, krb5_data **);
+
+krb5_error_code encode_krb5_pa_enc_ts
+     (const krb5_pa_enc_ts *, krb5_data **);
+
+krb5_error_code encode_krb5_sam_challenge
+ (const krb5_sam_challenge * , krb5_data **);
+
+krb5_error_code encode_krb5_sam_key
+ (const krb5_sam_key * , krb5_data **);
+
+krb5_error_code encode_krb5_enc_sam_response_enc
+ (const krb5_enc_sam_response_enc * , krb5_data **);
+
+krb5_error_code encode_krb5_sam_response
+ (const krb5_sam_response * , krb5_data **);
+
+krb5_error_code encode_krb5_sam_challenge_2
+ (const krb5_sam_challenge_2 * , krb5_data **);
+
+krb5_error_code encode_krb5_sam_challenge_2_body
+ (const krb5_sam_challenge_2_body * , krb5_data **);
+
+krb5_error_code encode_krb5_enc_sam_response_enc_2
+ (const krb5_enc_sam_response_enc_2 * , krb5_data **);
+
+krb5_error_code encode_krb5_sam_response_2
+ (const krb5_sam_response_2 * , krb5_data **);
+
+krb5_error_code encode_krb5_predicted_sam_response
+ (const krb5_predicted_sam_response * , krb5_data **);
+
+krb5_error_code encode_krb5_sam_challenge
+       (const krb5_sam_challenge * , krb5_data **);
+
+krb5_error_code encode_krb5_sam_key
+       (const krb5_sam_key * , krb5_data **);
+
+krb5_error_code encode_krb5_enc_sam_response_enc
+       (const krb5_enc_sam_response_enc * , krb5_data **);
+
+krb5_error_code encode_krb5_sam_response
+       (const krb5_sam_response * , krb5_data **);
+
+krb5_error_code encode_krb5_predicted_sam_response
+       (const krb5_predicted_sam_response * , krb5_data **);
+
+krb5_error_code encode_krb5_setpw_req
+(const krb5_principal target, char *password, krb5_data **code);
+
+
+
+
+
+krb5_error_code decode_krb5_sam_challenge
+       (const krb5_data *, krb5_sam_challenge **);
+
+krb5_error_code decode_krb5_enc_sam_key
+       (const krb5_data *, krb5_sam_key **);
+
+krb5_error_code decode_krb5_enc_sam_response_enc
+       (const krb5_data *, krb5_enc_sam_response_enc **);
+
+krb5_error_code decode_krb5_sam_response
+       (const krb5_data *, krb5_sam_response **);
+
+krb5_error_code decode_krb5_predicted_sam_response
+       (const krb5_data *, krb5_predicted_sam_response **);
+
+krb5_error_code decode_krb5_sam_challenge_2
+ (const krb5_data *, krb5_sam_challenge_2 **);
+
+krb5_error_code decode_krb5_sam_challenge_2_body
+ (const krb5_data *, krb5_sam_challenge_2_body **);
+
+krb5_error_code decode_krb5_enc_sam_response_enc_2
+ (const krb5_data *, krb5_enc_sam_response_enc_2 **);
+
+krb5_error_code decode_krb5_sam_response_2
+ (const krb5_data *, krb5_sam_response_2 **);
+
+
+
+
+
+
+krb5_error_code krb5_validate_times
+       (krb5_context,
+         krb5_ticket_times *);
+krb5_error_code decode_krb5_authenticator
+ (const krb5_data *code, krb5_authenticator **rep);
+
+krb5_error_code decode_krb5_ticket
+ (const krb5_data *code, krb5_ticket **rep);
+
+krb5_error_code decode_krb5_encryption_key
+ (const krb5_data *output, krb5_keyblock **rep);
+
+krb5_error_code decode_krb5_enc_tkt_part
+ (const krb5_data *output, krb5_enc_tkt_part **rep);
+
+krb5_error_code decode_krb5_enc_kdc_rep_part
+ (const krb5_data *output, krb5_enc_kdc_rep_part **rep);
+
+krb5_error_code decode_krb5_as_rep
+ (const krb5_data *output, krb5_kdc_rep **rep);
+
+krb5_error_code decode_krb5_tgs_rep
+ (const krb5_data *output, krb5_kdc_rep **rep);
+
+krb5_error_code decode_krb5_ap_req
+ (const krb5_data *output, krb5_ap_req **rep);
+
+krb5_error_code decode_krb5_ap_rep
+ (const krb5_data *output, krb5_ap_rep **rep);
+
+krb5_error_code decode_krb5_ap_rep_enc_part
+ (const krb5_data *output, krb5_ap_rep_enc_part **rep);
+
+krb5_error_code decode_krb5_as_req
+ (const krb5_data *output, krb5_kdc_req **rep);
+
+krb5_error_code decode_krb5_tgs_req
+ (const krb5_data *output, krb5_kdc_req **rep);
+
+krb5_error_code decode_krb5_kdc_req_body
+ (const krb5_data *output, krb5_kdc_req **rep);
+
+krb5_error_code decode_krb5_safe
+ (const krb5_data *output, krb5_safe **rep);
+
+krb5_error_code decode_krb5_safe_with_body
+ (const krb5_data *output, krb5_safe **rep, krb5_data *body);
+
+krb5_error_code decode_krb5_priv
+ (const krb5_data *output, krb5_priv **rep);
+
+krb5_error_code decode_krb5_enc_priv_part
+ (const krb5_data *output, krb5_priv_enc_part **rep);
+
+krb5_error_code decode_krb5_cred
+ (const krb5_data *output, krb5_cred **rep);
+
+krb5_error_code decode_krb5_enc_cred_part
+ (const krb5_data *output, krb5_cred_enc_part **rep);
+
+krb5_error_code decode_krb5_error
+ (const krb5_data *output, krb5_error **rep);
+
+krb5_error_code decode_krb5_authdata
+ (const krb5_data *output, krb5_authdata ***rep);
+
+krb5_error_code decode_krb5_pwd_sequence
+ (const krb5_data *output, passwd_phrase_element **rep);
+
+krb5_error_code decode_krb5_pwd_data
+ (const krb5_data *output, krb5_pwd_data **rep);
+
+krb5_error_code decode_krb5_padata_sequence
+ (const krb5_data *output, krb5_pa_data ***rep);
+
+krb5_error_code decode_krb5_alt_method
+ (const krb5_data *output, krb5_alt_method **rep);
+
+krb5_error_code decode_krb5_etype_info
+ (const krb5_data *output, krb5_etype_info_entry ***rep);
+
+krb5_error_code decode_krb5_etype_info2
+ (const krb5_data *output, krb5_etype_info_entry ***rep);
+
+krb5_error_code decode_krb5_enc_data
+ (const krb5_data *output, krb5_enc_data **rep);
+
+krb5_error_code decode_krb5_pa_enc_ts
+ (const krb5_data *output, krb5_pa_enc_ts **rep);
+
+krb5_error_code decode_krb5_sam_challenge
+ (const krb5_data *, krb5_sam_challenge **);
+
+krb5_error_code decode_krb5_sam_key
+ (const krb5_data *, krb5_sam_key **);
+
+krb5_error_code decode_krb5_enc_sam_response_enc
+ (const krb5_data *, krb5_enc_sam_response_enc **);
+
+krb5_error_code decode_krb5_sam_response
+ (const krb5_data *, krb5_sam_response **);
+
+krb5_error_code decode_krb5_predicted_sam_response
+ (const krb5_data *, krb5_predicted_sam_response **);
+krb5_error_code krb5_encrypt_tkt_part
+ (krb5_context,
+  const krb5_keyblock *,
+  krb5_ticket * );
+
+
+krb5_error_code krb5_encode_kdc_rep
+ (krb5_context,
+  const krb5_msgtype,
+  const krb5_enc_kdc_rep_part *,
+  int using_subkey,
+  const krb5_keyblock *,
+  krb5_kdc_rep *,
+  krb5_data ** );
+
+krb5_error_code krb5_validate_times
+ (krb5_context,
+  krb5_ticket_times *);
+krb5_boolean krb5int_auth_con_chkseqnum
+ (krb5_context ctx, krb5_auth_context ac, krb5_ui_4 in_seq);
+
+
+
+struct __krb5_serializer {
+    krb5_magic odtype;
+    krb5_error_code (*sizer) (krb5_context,
+        krb5_pointer,
+        size_t *);
+    krb5_error_code (*externalizer) (krb5_context,
+        krb5_pointer,
+        krb5_octet **,
+        size_t *);
+    krb5_error_code (*internalizer) (krb5_context,
+        krb5_pointer *,
+        krb5_octet **,
+        size_t *);
+};
+typedef const struct __krb5_serializer * krb5_ser_handle;
+typedef struct __krb5_serializer krb5_ser_entry;
+
+krb5_ser_handle krb5_find_serializer
+ (krb5_context,
+  krb5_magic);
+krb5_error_code krb5_register_serializer
+ (krb5_context,
+   const krb5_ser_entry *);
+
+
+krb5_error_code krb5_size_opaque
+ (krb5_context,
+  krb5_magic,
+  krb5_pointer,
+  size_t *);
+
+
+krb5_error_code krb5_externalize_opaque
+ (krb5_context,
+  krb5_magic,
+  krb5_pointer,
+  krb5_octet **,
+  size_t *);
+
+
+krb5_error_code krb5_internalize_opaque
+ (krb5_context,
+  krb5_magic,
+  krb5_pointer *,
+  krb5_octet **,
+  size_t *);
+
+
+krb5_error_code krb5_externalize_data
+ (krb5_context,
+  krb5_pointer,
+  krb5_octet **,
+  size_t *);
+
+
+
+
+
+krb5_error_code krb5_ser_context_init
+ (krb5_context);
+
+
+krb5_error_code krb5_ser_auth_context_init
+ (krb5_context);
+
+
+krb5_error_code krb5_ser_keytab_init
+ (krb5_context);
+
+
+krb5_error_code krb5_ser_ccache_init
+ (krb5_context);
+
+
+krb5_error_code krb5_ser_rcache_init
+ (krb5_context);
+
+
+krb5_error_code krb5_ser_pack_int32
+ (krb5_int32,
+  krb5_octet **,
+  size_t *);
+krb5_error_code krb5_ser_unpack_int32
+ (krb5_int32 *,
+  krb5_octet **,
+  size_t *);
+
+krb5_error_code krb5_ser_pack_int64
+ (krb5_int64, krb5_octet **, size_t *);
+krb5_error_code krb5_ser_unpack_int64
+ (krb5_int64 *, krb5_octet **, size_t *);
+
+krb5_error_code krb5_ser_pack_bytes
+ (krb5_octet *,
+  size_t,
+  krb5_octet **,
+  size_t *);
+krb5_error_code krb5_ser_unpack_bytes
+ (krb5_octet *,
+  size_t,
+  krb5_octet **,
+  size_t *);
+
+krb5_error_code krb5int_cc_default
+ (krb5_context, krb5_ccache *);
+
+krb5_error_code krb5_cc_retrieve_cred_default
+ (krb5_context, krb5_ccache, krb5_flags,
+   krb5_creds *, krb5_creds *);
+
+void krb5int_set_prompt_types
+ (krb5_context, krb5_prompt_type *);
+
+krb5_error_code
+krb5int_generate_and_save_subkey (krb5_context, krb5_auth_context,
+      krb5_keyblock * );
+
+
+
+krb5_error_code krb5int_mk_chpw_req
+ (krb5_context context, krb5_auth_context auth_context,
+    krb5_data *ap_req, char *passwd, krb5_data *packet);
+krb5_error_code krb5int_rd_chpw_rep
+ (krb5_context context, krb5_auth_context auth_context,
+         krb5_data *packet, int *result_code,
+         krb5_data *result_data);
+krb5_error_code krb5_chpw_result_code_string
+ (krb5_context context, int result_code,
+   char **result_codestr);
+krb5_error_code krb5int_mk_setpw_req
+ (krb5_context context, krb5_auth_context auth_context,
+    krb5_data *ap_req, krb5_principal targetprinc, char *passwd, krb5_data *packet);
+krb5_error_code krb5int_rd_setpw_rep
+ (krb5_context context, krb5_auth_context auth_context,
+         krb5_data *packet, int *result_code,
+         krb5_data *result_data);
+krb5_error_code krb5int_setpw_result_code_string
+ (krb5_context context, int result_code,
+   const char **result_codestr);
+
+struct srv_dns_entry {
+    struct srv_dns_entry *next;
+    int priority;
+    int weight;
+    unsigned short port;
+    char *host;
+};
+krb5_error_code
+krb5int_make_srv_query_realm(const krb5_data *realm,
+        const char *service,
+        const char *protocol,
+        struct srv_dns_entry **answers);
+void krb5int_free_srv_dns_data(struct srv_dns_entry *);
+struct ktext;
+
+typedef struct _krb5int_access {
+
+    const struct krb5_hash_provider *md5_hash_provider;
+    const struct krb5_enc_provider *arcfour_enc_provider;
+    krb5_error_code (* krb5_hmac) (const struct krb5_hash_provider *hash,
+       const krb5_keyblock *key,
+       unsigned int icount, const krb5_data *input,
+       krb5_data *output);
+
+    krb5_error_code (*locate_server) (krb5_context, const krb5_data *,
+          struct addrlist *, int,
+          const char *, const char *,
+          int, int, int, int);
+    krb5_error_code (*sendto_udp) (krb5_context, const krb5_data *msg,
+       const struct addrlist *, krb5_data *reply,
+       struct sockaddr *, socklen_t *, int *);
+    krb5_error_code (*add_host_to_list)(struct addrlist *lp,
+     const char *hostname,
+     int port, int secport,
+     int socktype, int family);
+    void (*free_addrlist) (struct addrlist *);
+
+    krb5_error_code (*make_srv_query_realm)(const krb5_data *realm,
+         const char *service,
+         const char *protocol,
+         struct srv_dns_entry **answers);
+    void (*free_srv_dns_data)(struct srv_dns_entry *);
+    int (*use_dns_kdc)(krb5_context);
+
+
+    krb5_int32 (*krb_life_to_time)(krb5_int32, int);
+    int (*krb_time_to_life)(krb5_int32, krb5_int32);
+    int (*krb524_encode_v4tkt)(struct ktext *, char *, unsigned int *);
+    krb5_error_code (*krb5int_c_mandatory_cksumtype)
+        (krb5_context, krb5_enctype, krb5_cksumtype *);
+    krb5_error_code ( *krb5_ser_pack_int64)
+        (krb5_int64, krb5_octet **, size_t *);
+    krb5_error_code ( *krb5_ser_unpack_int64)
+        (krb5_int64 *, krb5_octet **, size_t *);
+} krb5int_access;
+
+
+
+
+
+krb5_error_code krb5int_accessor
+ (krb5int_access*, krb5_int32);
+extern krb5_int32 krb5int_krb_life_to_time(krb5_int32, int);
+extern int krb5int_krb_time_to_life(krb5_int32, krb5_int32);
+
+
+int krb5int_encode_v4tkt
+ (struct ktext *v4tkt, char *buf, unsigned int *encoded_len);
+
+
+int krb5int_524_sendto_kdc
+        (krb5_context context, const krb5_data * message,
+  const krb5_data * realm, krb5_data * reply,
+  struct sockaddr *, socklen_t *);
+
+
+
+struct _krb5_ccache {
+    krb5_magic magic;
+    const struct _krb5_cc_ops *ops;
+    krb5_pointer data;
+};
+
+struct _krb5_cc_ops {
+    krb5_magic magic;
+    char *prefix;
+    const char * ( *get_name) (krb5_context, krb5_ccache);
+    krb5_error_code ( *resolve) (krb5_context, krb5_ccache *,
+         const char *);
+    krb5_error_code ( *gen_new) (krb5_context, krb5_ccache *);
+    krb5_error_code ( *init) (krb5_context, krb5_ccache,
+         krb5_principal);
+    krb5_error_code ( *destroy) (krb5_context, krb5_ccache);
+    krb5_error_code ( *close) (krb5_context, krb5_ccache);
+    krb5_error_code ( *store) (krb5_context, krb5_ccache,
+         krb5_creds *);
+    krb5_error_code ( *retrieve) (krb5_context, krb5_ccache,
+         krb5_flags, krb5_creds *,
+         krb5_creds *);
+    krb5_error_code ( *get_princ) (krb5_context, krb5_ccache,
+         krb5_principal *);
+    krb5_error_code ( *get_first) (krb5_context, krb5_ccache,
+         krb5_cc_cursor *);
+    krb5_error_code ( *get_next) (krb5_context, krb5_ccache,
+         krb5_cc_cursor *, krb5_creds *);
+    krb5_error_code ( *end_get) (krb5_context, krb5_ccache,
+         krb5_cc_cursor *);
+    krb5_error_code ( *remove_cred) (krb5_context, krb5_ccache,
+         krb5_flags, krb5_creds *);
+    krb5_error_code ( *set_flags) (krb5_context, krb5_ccache,
+         krb5_flags);
+};
+
+extern const krb5_cc_ops *krb5_cc_dfl_ops;
+
+typedef struct _krb5_donot_replay {
+    krb5_magic magic;
+    krb5_ui_4 hash;
+    char *server;
+    char *client;
+    krb5_int32 cusec;
+    krb5_timestamp ctime;
+} krb5_donot_replay;
+
+krb5_error_code krb5_rc_default
+ (krb5_context,
+  krb5_rcache *);
+krb5_error_code krb5_rc_resolve_type
+ (krb5_context,
+  krb5_rcache *,char *);
+krb5_error_code krb5_rc_resolve_full
+ (krb5_context,
+  krb5_rcache *,char *);
+char * krb5_rc_get_type
+ (krb5_context,
+  krb5_rcache);
+char * krb5_rc_default_type
+ (krb5_context);
+char * krb5_rc_default_name
+ (krb5_context);
+krb5_error_code krb5_auth_to_rep
+ (krb5_context,
+  krb5_tkt_authent *,
+  krb5_donot_replay *);
+
+
+krb5_error_code krb5_rc_initialize
+ (krb5_context, krb5_rcache,krb5_deltat);
+krb5_error_code krb5_rc_recover_or_initialize
+ (krb5_context, krb5_rcache,krb5_deltat);
+krb5_error_code krb5_rc_recover
+ (krb5_context, krb5_rcache);
+krb5_error_code krb5_rc_destroy
+ (krb5_context, krb5_rcache);
+krb5_error_code krb5_rc_close
+ (krb5_context, krb5_rcache);
+krb5_error_code krb5_rc_store
+ (krb5_context, krb5_rcache,krb5_donot_replay *);
+krb5_error_code krb5_rc_expunge
+ (krb5_context, krb5_rcache);
+krb5_error_code krb5_rc_get_lifespan
+ (krb5_context, krb5_rcache,krb5_deltat *);
+char * krb5_rc_get_name
+ (krb5_context, krb5_rcache);
+krb5_error_code krb5_rc_resolve
+ (krb5_context, krb5_rcache, char *);
+
+typedef struct _krb5_kt_ops {
+    krb5_magic magic;
+    char *prefix;
+
+    krb5_error_code ( *resolve)
+ (krb5_context,
+   const char *,
+   krb5_keytab *);
+    krb5_error_code ( *get_name)
+ (krb5_context,
+   krb5_keytab,
+   char *,
+   unsigned int);
+    krb5_error_code ( *close)
+ (krb5_context,
+   krb5_keytab);
+    krb5_error_code ( *get)
+ (krb5_context,
+   krb5_keytab,
+   krb5_const_principal,
+   krb5_kvno,
+   krb5_enctype,
+   krb5_keytab_entry *);
+    krb5_error_code ( *start_seq_get)
+ (krb5_context,
+   krb5_keytab,
+   krb5_kt_cursor *);
+    krb5_error_code ( *get_next)
+ (krb5_context,
+   krb5_keytab,
+   krb5_keytab_entry *,
+   krb5_kt_cursor *);
+    krb5_error_code ( *end_get)
+ (krb5_context,
+   krb5_keytab,
+   krb5_kt_cursor *);
+
+    krb5_error_code ( *add)
+ (krb5_context,
+   krb5_keytab,
+   krb5_keytab_entry *);
+    krb5_error_code ( *remove)
+ (krb5_context,
+   krb5_keytab,
+    krb5_keytab_entry *);
+
+
+    const krb5_ser_entry *serializer;
+} krb5_kt_ops;
+
+extern const krb5_kt_ops krb5_kt_dfl_ops;
+
+extern krb5_error_code krb5int_translate_gai_error (int);
+
+
+extern krb5_error_code
+krb5int_c_mandatory_cksumtype (krb5_context, krb5_enctype, krb5_cksumtype *);
+
+extern int krb5int_crypto_init (void);
+extern int krb5int_prng_init(void);
+
+
+
+
+struct _krb5_auth_context {
+    krb5_magic magic;
+    krb5_address * remote_addr;
+    krb5_address * remote_port;
+    krb5_address * local_addr;
+    krb5_address * local_port;
+    krb5_keyblock * keyblock;
+    krb5_keyblock * send_subkey;
+    krb5_keyblock * recv_subkey;
+
+    krb5_int32 auth_context_flags;
+    krb5_ui_4 remote_seq_number;
+    krb5_ui_4 local_seq_number;
+    krb5_authenticator *authentp;
+    krb5_cksumtype req_cksumtype;
+    krb5_cksumtype safe_cksumtype;
+    krb5_pointer i_vector;
+    krb5_rcache rcache;
+    krb5_enctype * permitted_etypes;
+  krb5_mk_req_checksum_func checksum_func;
+  void *checksum_func_data;
+};
+
+
+
+
+static const char sendauth_version[] = "KRB5_SENDAUTH_V1.0";
+
+static krb5_error_code
+recvauth_common(krb5_context context,
+  krb5_auth_context * auth_context,
+
+  krb5_pointer fd,
+  char *appl_version,
+  krb5_principal server,
+  krb5_int32 flags,
+  krb5_keytab keytab,
+
+  krb5_ticket ** ticket,
+  krb5_data *version)
+{
+    krb5_auth_context new_auth_context;
+    krb5_flags ap_option;
+    krb5_error_code retval, problem;
+    krb5_data inbuf;
+    krb5_data outbuf;
+    krb5_rcache rcache = 0;
+    krb5_octet response;
+    krb5_data null_server;
+    int need_error_free = 0;
+    int local_rcache = 0, local_authcon = 0;
+
+
+
+
+
+
+
+ problem = 0;
+
+ if (!(flags & 0x0001)) {
+
+
+
+     if ((retval = krb5_read_message(context, fd, &inbuf)))
+  return(retval);
+     if (
+        __extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p (
+        inbuf.data
+        ) && __builtin_constant_p (
+        sendauth_version
+        ) && (__s1_len = strlen (
+        inbuf.data
+        ), __s2_len = strlen (
+        sendauth_version
+        ), (!((size_t)(const void *)((
+        inbuf.data
+        ) + 1) - (size_t)(const void *)(
+        inbuf.data
+        ) == 1) || __s1_len >= 4) && (!((size_t)(const void *)((
+        sendauth_version
+        ) + 1) - (size_t)(const void *)(
+        sendauth_version
+        ) == 1) || __s2_len >= 4)) ? __builtin_strcmp (
+        inbuf.data
+        , 
+        sendauth_version
+        ) : (__builtin_constant_p (
+        inbuf.data
+        ) && ((size_t)(const void *)((
+        inbuf.data
+        ) + 1) - (size_t)(const void *)(
+        inbuf.data
+        ) == 1) && (__s1_len = strlen (
+        inbuf.data
+        ), __s1_len < 4) ? (__builtin_constant_p (
+        sendauth_version
+        ) && ((size_t)(const void *)((
+        sendauth_version
+        ) + 1) - (size_t)(const void *)(
+        sendauth_version
+        ) == 1) ? __builtin_strcmp (
+        inbuf.data
+        , 
+        sendauth_version
+        ) : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (
+        sendauth_version
+        ); int __result = (((const unsigned char *) (const char *) (
+        inbuf.data
+        ))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (
+        inbuf.data
+        ))[1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (
+        inbuf.data
+        ))[2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (
+        inbuf.data
+        ))[3] - __s2[3]); } } __result; }))) : (__builtin_constant_p (
+        sendauth_version
+        ) && ((size_t)(const void *)((
+        sendauth_version
+        ) + 1) - (size_t)(const void *)(
+        sendauth_version
+        ) == 1) && (__s2_len = strlen (
+        sendauth_version
+        ), __s2_len < 4) ? (__builtin_constant_p (
+        inbuf.data
+        ) && ((size_t)(const void *)((
+        inbuf.data
+        ) + 1) - (size_t)(const void *)(
+        inbuf.data
+        ) == 1) ? __builtin_strcmp (
+        inbuf.data
+        , 
+        sendauth_version
+        ) : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (
+        inbuf.data
+        ); int __result = (((const unsigned char *) (const char *) (
+        sendauth_version
+        ))[0] - __s2[0]); if (__s2_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (
+        sendauth_version
+        ))[1] - __s2[1]); if (__s2_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (
+        sendauth_version
+        ))[2] - __s2[2]); if (__s2_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (
+        sendauth_version
+        ))[3] - __s2[3]); } } __result; })))) : __builtin_strcmp (
+        inbuf.data
+        , 
+        sendauth_version
+        )))); })
+                                            ) {
+  free((char *)(inbuf.data));
+  problem = (-1765328180L);
+     }
+     free((char *)(inbuf.data));
+ }
+ if (flags & 0x0002)
+     problem = (-1765328180L);
+
+
+
+
+ if ((retval = krb5_read_message(context, fd, &inbuf)))
+  return(retval);
+ if (appl_version && 
+                    __extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p (
+                    inbuf.data
+                    ) && __builtin_constant_p (
+                    appl_version
+                    ) && (__s1_len = strlen (
+                    inbuf.data
+                    ), __s2_len = strlen (
+                    appl_version
+                    ), (!((size_t)(const void *)((
+                    inbuf.data
+                    ) + 1) - (size_t)(const void *)(
+                    inbuf.data
+                    ) == 1) || __s1_len >= 4) && (!((size_t)(const void *)((
+                    appl_version
+                    ) + 1) - (size_t)(const void *)(
+                    appl_version
+                    ) == 1) || __s2_len >= 4)) ? __builtin_strcmp (
+                    inbuf.data
+                    , 
+                    appl_version
+                    ) : (__builtin_constant_p (
+                    inbuf.data
+                    ) && ((size_t)(const void *)((
+                    inbuf.data
+                    ) + 1) - (size_t)(const void *)(
+                    inbuf.data
+                    ) == 1) && (__s1_len = strlen (
+                    inbuf.data
+                    ), __s1_len < 4) ? (__builtin_constant_p (
+                    appl_version
+                    ) && ((size_t)(const void *)((
+                    appl_version
+                    ) + 1) - (size_t)(const void *)(
+                    appl_version
+                    ) == 1) ? __builtin_strcmp (
+                    inbuf.data
+                    , 
+                    appl_version
+                    ) : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (
+                    appl_version
+                    ); int __result = (((const unsigned char *) (const char *) (
+                    inbuf.data
+                    ))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (
+                    inbuf.data
+                    ))[1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (
+                    inbuf.data
+                    ))[2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (
+                    inbuf.data
+                    ))[3] - __s2[3]); } } __result; }))) : (__builtin_constant_p (
+                    appl_version
+                    ) && ((size_t)(const void *)((
+                    appl_version
+                    ) + 1) - (size_t)(const void *)(
+                    appl_version
+                    ) == 1) && (__s2_len = strlen (
+                    appl_version
+                    ), __s2_len < 4) ? (__builtin_constant_p (
+                    inbuf.data
+                    ) && ((size_t)(const void *)((
+                    inbuf.data
+                    ) + 1) - (size_t)(const void *)(
+                    inbuf.data
+                    ) == 1) ? __builtin_strcmp (
+                    inbuf.data
+                    , 
+                    appl_version
+                    ) : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (
+                    inbuf.data
+                    ); int __result = (((const unsigned char *) (const char *) (
+                    appl_version
+                    ))[0] - __s2[0]); if (__s2_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (
+                    appl_version
+                    ))[1] - __s2[1]); if (__s2_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (
+                    appl_version
+                    ))[2] - __s2[2]); if (__s2_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (
+                    appl_version
+                    ))[3] - __s2[3]); } } __result; })))) : __builtin_strcmp (
+                    inbuf.data
+                    , 
+                    appl_version
+                    )))); })
+                                                    ) {
+  free((char *)(inbuf.data));
+  if (!problem)
+   problem = (-1765328179L);
+ }
+ if (version && !problem)
+     *version = inbuf;
+ else
+     free((char *)(inbuf.data));
+
+
+
+
+
+ switch (problem) {
+ case 0:
+  response = 0;
+  break;
+ case (-1765328180L):
+  response = 1;
+  break;
+ case (-1765328179L):
+  response = 2;
+  break;
+ default:
+
+
+
+  response = 255;
+
+
+
+
+
+  break;
+ }
+
+
+
+
+ if ((krb5_net_write(context, *((int *)fd), (char *)&response, 1)) < 0) {
+  return(problem);
+ }
+ if (problem)
+     return(problem);
+
+
+
+
+
+
+    if ((retval = krb5_read_message(context, fd, &inbuf)))
+        return retval;
+
+    if (*auth_context == 
+                        ((void *)0)
+                            ) {
+ problem = krb5_auth_con_init(context, &new_auth_context);
+ *auth_context = new_auth_context;
+ local_authcon = 1;
+    }
+    krb5_auth_con_getrcache(context, *auth_context, &rcache);
+    if ((!problem) && rcache == 
+                               ((void *)0)
+                                   ) {
+
+
+
+        if (server) {
+            problem = krb5_get_server_rcache(context,
+   (((0) < (server)->length) ? (server)->data + (0) : 
+  ((void *)0)
+  ), &rcache);
+        } else {
+         null_server.length = 7;
+         null_server.data = "default";
+         problem = krb5_get_server_rcache(context, &null_server, &rcache);
+        }
+        if (!problem)
+     problem = krb5_auth_con_setrcache(context, *auth_context, rcache);
+ local_rcache = 1;
+    }
+    if (!problem) {
+ problem = krb5_rd_req(context, auth_context, &inbuf, server,
+         keytab, &ap_option, ticket);
+ free((char *)(inbuf.data));
+    }
+
+
+
+
+
+
+    if (problem) {
+ krb5_error error;
+ const char *message;
+
+ memset((char *)&error, 0, sizeof(error));
+ krb5_us_timeofday(context, &error.stime, &error.susec);
+ if(server)
+  error.server = server;
+ else {
+
+
+  (void) krb5_parse_name(context, "????", &error.server);
+  need_error_free = 1;
+ }
+
+ error.error = problem - (-1765328384L);
+ if (error.error > 127)
+  error.error = 60;
+ message = error_message(problem);
+ error.text.length = strlen(message) + 1;
+ if (!(error.text.data = malloc(error.text.length))) {
+     retval = 
+             12
+                   ;
+     goto cleanup;
+ }
+ strcpy(error.text.data, message);
+ if ((retval = krb5_mk_error(context, &error, &outbuf))) {
+     free(error.text.data);
+     goto cleanup;
+ }
+ free(error.text.data);
+ if(need_error_free)
+  krb5_free_principal(context, error.server);
+
+    } else {
+ outbuf.length = 0;
+ outbuf.data = 0;
+    }
+
+    retval = krb5_write_message(context, fd, &outbuf);
+    if (outbuf.data) {
+ free((char *)(outbuf.data));
+
+     retval = problem;
+     goto cleanup;
+    }
+    if (retval)
+ goto cleanup;
+
+
+    if ((ap_option & 0x20000000)) {
+ if ((retval = krb5_mk_rep(context, *auth_context, &outbuf))) {
+     return(retval);
+ }
+ retval = krb5_write_message(context, fd, &outbuf);
+ free((char *)(outbuf.data));
+    }
+
+cleanup:;
+    if (retval) {
+ if (local_authcon) {
+     krb5_auth_con_free(context, *auth_context);
+ } else if (local_rcache && rcache != 
+                                     ((void *)0)
+                                         ) {
+     krb5_rc_close(context, rcache);
+     krb5_auth_con_setrcache(context, *auth_context, 
+                                                    ((void *)0)
+                                                        );
+ }
+    }
+    return retval; /* { dg-bogus "analysis bailed out early" "" { xfail *-*-* } } */
+}
+
+krb5_error_code
+krb5_recvauth(krb5_context context, krb5_auth_context *auth_context, krb5_pointer fd, char *appl_version, krb5_principal server, krb5_int32 flags, krb5_keytab keytab, krb5_ticket **ticket)
+{
+    return recvauth_common (context, auth_context, fd, appl_version,
+       server, flags, keytab, ticket, 0);
+}
+
+krb5_error_code
+krb5_recvauth_version(krb5_context context,
+        krb5_auth_context *auth_context,
+
+        krb5_pointer fd,
+        krb5_principal server,
+        krb5_int32 flags,
+        krb5_keytab keytab,
+
+        krb5_ticket **ticket,
+        krb5_data *version)
+{
+    return recvauth_common (context, auth_context, fd, 0,
+       server, flags, keytab, ticket, version);
+}
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
new file mode 100644
index 0000000..585a06f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-1.c
@@ -0,0 +1,12 @@
+/* { dg-additional-options "-fanalyzer-region-model" } */
+
+void test_1 (void)
+{
+  int x, y;
+  int *p = &x;
+  x = 3;
+  *p = 4;
+  y = x;
+
+  __analyzer_eval (y == 4); /* { dg-warning "TRUE" } */
+}
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
new file mode 100644
index 0000000..93e7f1a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-2.c
@@ -0,0 +1,19 @@
+/* { dg-additional-options "-fanalyzer-region-model" } */
+
+#include <stdlib.h>
+
+void test_2 (void)
+{
+  void *p = malloc (10);
+  char *buf1 = (char*)p;
+  buf1[0] = 'a'; /* { dg-warning "use of possibly-NULL 'buf1'" } */
+
+  int *buf2 = (int*)p;
+  buf2[0] = 0;
+
+  char c = buf1[0];
+
+  __analyzer_eval (c == 0); /* { dg-warning "TRUE" } */
+
+  free (p);
+}
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
new file mode 100644
index 0000000..5310c38
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-3.c
@@ -0,0 +1,24 @@
+/* { dg-additional-options "-fanalyzer-region-model" } */
+
+#include <stdlib.h>
+
+struct s1
+{
+  int d;
+};
+struct s2
+{
+  struct s1 *p;
+};
+void test_3 (void)
+{
+  struct s1 data;
+  struct s2 *sp;
+  int a[2];
+  sp = malloc(sizeof(struct s2));
+  sp->p = &data; /* { dg-warning "use of possibly-NULL 'sp'" } */
+  sp->p->d = 3;
+  a[1] = data.d;
+  __analyzer_eval (a[1] == 3); /* { dg-warning "TRUE" } */
+
+} /* { dg-warning "leak of 'sp'" } */
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
new file mode 100644
index 0000000..818166c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/NOT_FOR_UPSTREAM-clang-paper-example-4.c
@@ -0,0 +1,31 @@
+/* { dg-additional-options "-fanalyzer-region-model" } */
+
+#include <stddef.h>
+
+struct s { int data[2]; };
+void test_4 (struct s buf)
+{
+  int i = 3, *q = NULL;
+  struct s* p;
+  if (buf.data[1] == 1)
+    q = &i;
+  p = &buf;
+  if (p->data[1] == 1)
+    p->data[0] = *q; /* { dg-bogus "use of NULL 'q'" } */
+  return;
+}
+
+/* To avoid a false positive read-through-uninit at *q, we need to handle
+   phi nodes, and to enforce constraints.
+
+   region_model's constraint model is now good enough to rule out
+   the path as infeasible.
+
+   Looks like at the first:
+
+     if (buf.data[1] == 1)
+
+   we have an unknown value for buf.data[1], but it's the unknown
+   value for "buf" as a whole; we should have created unknown values
+   for the child (or for accesses to the parent unknown value).  */
+
diff --git a/gcc/testsuite/gcc.dg/analyzer/abort.c b/gcc/testsuite/gcc.dg/analyzer/abort.c
index cfbf74c..285a4c7 100644
--- a/gcc/testsuite/gcc.dg/analyzer/abort.c
+++ b/gcc/testsuite/gcc.dg/analyzer/abort.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fanalyzer-region-model" } */
+
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/gcc/testsuite/gcc.dg/analyzer/alloca-leak.c b/gcc/testsuite/gcc.dg/analyzer/alloca-leak.c
new file mode 100644
index 0000000..6d9fe34
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/alloca-leak.c
@@ -0,0 +1,8 @@
+#include <alloca.h>
+
+void *test (void)
+{
+  void *ptr = alloca (64);
+  return ptr;
+}
+/* TODO: warn about escaping alloca.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c b/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
new file mode 100644
index 0000000..ab44d07
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
@@ -0,0 +1,57 @@
+#include <stdlib.h>
+
+extern void foo(void *ptrA, void *ptrB, void *ptrC) /* { dg-message "argument 1 of 'foo' must be non-null" } */
+  __attribute__((nonnull (1, 3)));
+
+extern void bar(void *ptrA, void *ptrB, void *ptrC) /* { dg-message "argument 1 of 'bar' must be non-null" } */
+  __attribute__((nonnull));
+
+// TODO: complain about NULL and possible NULL args
+// FIXME: ought to complain about NULL args
+
+void test_1 (void *p, void *q, void *r)
+{
+  foo(p, q, r);
+  foo(NULL, q, r);
+  foo(p, NULL, r);
+  foo(p, q, NULL);
+}
+
+void test_1a (void *q, void *r)
+{
+  void *p = NULL;
+  foo(p, q, r); /* { dg-warning "use of NULL 'p' where non-null expected" } */
+  /* { dg-message "argument 1 \\('p'\\) NULL where non-null expected" "" { target *-*-* } .-1 } */
+}
+
+void test_2 (void *p, void *q, void *r)
+{
+  bar(p, q, r);
+  bar(NULL, q, r);
+  bar(p, NULL, r);
+  bar(p, q, NULL);
+}
+
+void test_3 (void *q, void *r)
+{
+  void *p = malloc(1024); /* { dg-message "this call could return NULL" } */
+
+  foo(p, q, r); /* { dg-warning "use of possibly-NULL 'p' where non-null expected" } */
+  /* { dg-message "argument 1 \\('p'\\) could be NULL where non-null expected" "" { target *-*-* } .-1 } */
+
+  foo(p, q, r);
+
+  free(p);
+}
+
+void test_4 (void *q, void *r)
+{
+  void *p = malloc(1024); /* { dg-message "this call could return NULL" } */
+
+  bar(p, q, r); /* { dg-warning "use of possibly-NULL 'p' where non-null expected" } */
+  /* { dg-message "argument 1 \\('p'\\) could be NULL where non-null expected" "" { target *-*-* } .-1 } */
+
+  bar(p, q, r);
+
+  free(p);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals.c b/gcc/testsuite/gcc.dg/analyzer/conditionals.c
index 578a9b8..28f6978 100644
--- a/gcc/testsuite/gcc.dg/analyzer/conditionals.c
+++ b/gcc/testsuite/gcc.dg/analyzer/conditionals.c
@@ -1,3 +1,5 @@
+/* { 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
new file mode 100644
index 0000000..14543a9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
@@ -0,0 +1,790 @@
+/* { dg-additional-options "-fanalyzer-region-model" } */
+
+#include <stdlib.h>
+#include <string.h>
+
+struct foo
+{
+  int i;
+};
+
+/* Fields of a local.  */
+
+void test_1 (void)
+{
+  struct foo f;
+  f.i = 1;
+  __analyzer_eval (f.i == 1); /* { dg-warning "TRUE" } */
+}
+
+/* Fields of a param.  */
+
+void test_2 (struct foo f)
+{
+  __analyzer_eval (f.i == 2); /* { dg-warning "UNKNOWN" } */
+  f.i = 2;
+  __analyzer_eval (f.i == 2); /* { dg-warning "TRUE" } */
+}
+
+/* Fields of a param ptr.  */
+
+void test_3 (struct foo *f)
+{
+  __analyzer_eval (f->i == 3); /* { dg-warning "UNKNOWN" } */
+  f->i = 3;
+  __analyzer_eval (f->i == 3); /* { dg-warning "TRUE" } */
+}
+
+/* Fields of a global ptr.  */
+struct foo *global_foo_ptr;
+
+void test_3a (void)
+{
+  struct foo *tmp = global_foo_ptr;
+  __analyzer_eval (global_foo_ptr->i == 3); /* { dg-warning "UNKNOWN" } */
+  global_foo_ptr->i = 3;
+  __analyzer_eval (global_foo_ptr->i == 3); /* { dg-warning "TRUE" } */
+}
+
+/* Pointer to a local.  */
+
+void test_4 (void)
+{
+  int i;
+  int *p = &i;
+  i = 1;
+  *p = 2;
+  __analyzer_eval (i == 2); /* { dg-warning "TRUE" } */
+}
+
+/* Local array.  */
+
+void test_5 (void)
+{
+  int a[10];
+  a[3] = 5; /* ARRAY_REF.  */
+  __analyzer_eval (a[3] == 5); /* { dg-warning "TRUE" } */
+}
+
+/* Local array, but using an unknown index.  */
+
+void test_5a (int idx)
+{
+  int a[10];
+  a[idx] = 5; /* ARRAY_REF.  */
+  __analyzer_eval (a[idx] == 5); /* { dg-warning "TRUE" } */
+}
+
+/* Array passed in as a param.  */
+
+void test_6 (int a[10])
+{
+  /* POINTER_PLUS_EXPR then a MEM_REF.  */
+  __analyzer_eval (a[3] == 42); /* { dg-warning "UNKNOWN" } */
+  a[3] = 42;
+  __analyzer_eval (a[3] == 42); /* { dg-warning "TRUE" } */
+}
+
+/* Array passed in as a param ptr.  */
+
+void test_7 (int *a)
+{
+  __analyzer_eval (a[3] == 42); /* { dg-warning "UNKNOWN" } */
+  a[3] = 42;
+  __analyzer_eval (a[3] == 42); /* { dg-warning "TRUE" } */
+}
+
+/* Globals.  */
+
+int glob_a;
+
+void test_10 (void)
+{
+  __analyzer_eval (glob_a == 42); /* { dg-warning "UNKNOWN" } */
+  glob_a = 42;
+  __analyzer_eval (glob_a == 42); /* { dg-warning "TRUE" } */
+}
+
+/* malloc.  */
+
+void test_11 (void)
+{
+  void *p = malloc (256);
+  void *q = malloc (256);
+
+  /* malloc results should be unique.  */
+  __analyzer_eval (p == q); /* { dg-warning "FALSE" } */
+  __analyzer_eval (p != q); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p <= q); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p >= q); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p < q); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p > q); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (p == p); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p != p); /* { dg-warning "FALSE" } */
+  __analyzer_eval (p <= p); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p >= p); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p < p); /* { dg-warning "FALSE" } */
+  __analyzer_eval (p > p); /* { dg-warning "FALSE" } */
+
+  free (p);
+  free (q);
+  // TODO: mark freed memory as freed
+  //__analyzer_break ();
+}
+
+/* alloca.  */
+
+void test_12 (void)
+{
+  void *p = alloca (256);
+  void *q = alloca (256);
+
+  /* alloca results should be unique.  */
+  __analyzer_eval (p == q); /* { dg-warning "FALSE" } */
+
+  // FIXME: complain about uses of poisoned values
+}
+
+/* Use of uninit value.  */
+int test_12a (void)
+{
+  int i;
+  return i; // FIXME: do we see the return stmt?
+}
+
+void test_12b (void *p, void *q)
+{
+  __analyzer_eval (p == q); /* { dg-warning "UNKNOWN" } */
+}
+
+int test_12c (void)
+{
+  int i;
+  int j;
+
+  j = i; // FIXME: should complain about this
+
+  return j;
+}
+
+struct coord
+{
+  int x;
+  int y;
+};
+
+int test_12d (struct coord c)
+{
+  struct coord d;
+  d = c;
+  __analyzer_eval (d.x == c.x); /* { dg-warning "TRUE" } */
+  __analyzer_eval (d.y == c.y); /* { dg-warning "TRUE" } */
+  __analyzer_eval (d.x == d.y); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+  /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */
+  /* TODO(xfail): d and c share an unknown value of type "struct coord".
+     But d.x and d.y should be different unknown values (although they inherit
+     from d's region.  */
+}
+
+/* Nested structs.  */
+
+struct outer
+{
+  struct middle {
+    struct inner {
+      float f;
+    } in;
+  } mid;
+};
+
+void test_13 (struct outer *o)
+{
+  __analyzer_eval (o->mid.in.f == 0.f); /* { dg-warning "UNKNOWN" } */
+  o->mid.in.f = 0.f;
+  __analyzer_eval (o->mid.in.f == 0.f); /* { dg-warning "TRUE" } */
+}
+
+void test_14 (struct outer o)
+{
+  __analyzer_eval (o.mid.in.f == 0.f); /* { dg-warning "UNKNOWN" } */
+  o.mid.in.f = 0.f;
+  __analyzer_eval (o.mid.in.f == 0.f); /* { dg-warning "TRUE" } */
+}
+
+void test_15 (const char *str)
+{
+  char ch = str[0];
+  __analyzer_eval (ch == 'a'); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (ch == str[0]); /* { dg-warning "TRUE" } */
+
+  ch = 'a';
+  __analyzer_eval (ch == 'a'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (str[0] == 'a'); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_16 (void)
+{
+  const char *msg = "hello world";
+  __analyzer_eval (msg[0] == 'h'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+
+  __analyzer_eval (msg[1] == 'e'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+
+  __analyzer_eval (strlen (msg) == 11); /* { dg-warning "TRUE" } */
+}
+
+void test_16a (const char *msg)
+{
+  __analyzer_eval (strlen (msg) == 11); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_16b (const char *msg)
+{
+  __analyzer_eval (strlen (msg) == strlen (msg)); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+}
+
+extern int unknown_result (void);
+
+void test_16c (int i)
+{
+  int j;
+
+  j = i;
+  __analyzer_eval (j == i); /* { dg-warning "TRUE" } */
+
+  j = unknown_result ();
+  __analyzer_eval (j == i); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_16c_a (void)
+{
+  int i, j;
+
+  i = unknown_result ();
+  j = unknown_result ();
+  __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */
+}
+
+int global_int_16d;
+
+void test_16d (int i)
+{
+  global_int_16d = i;
+  __analyzer_eval (global_int_16d == i); /* { dg-warning "TRUE" } */
+
+  global_int_16d = unknown_result ();
+  __analyzer_eval (global_int_16d == i); /* { dg-warning "UNKNOWN" } */
+}
+
+extern void might_write_to (int *);
+
+void test_16e (int i)
+{
+  int j;
+
+  j = i;
+  __analyzer_eval (j == i); /* { dg-warning "TRUE" } */
+
+  might_write_to (&j);
+  __analyzer_eval (j == i); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+  /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+}
+
+/* TODO: and more complicated graph-like examples, where anything that's
+   reachable from the pointer might be modified.  */
+
+void test_17 (int i)
+{
+  int j = 42;
+  __analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */
+  i = j;
+  __analyzer_eval (i == j); /* { dg-warning "TRUE" } */
+}
+
+void test_18 (int i)
+{
+  int j;
+  __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+
+  j = i;
+
+  __analyzer_eval (i == j); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i >= j); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i <= j); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (i != j); /* { dg-warning "FALSE" } */
+  __analyzer_eval (i > j); /* { dg-warning "FALSE" } */
+  __analyzer_eval (i < j); /* { dg-warning "FALSE" } */
+}
+
+void test_19 (void)
+{
+  int i, j;
+  /* Compare two uninitialized locals.  */
+    __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+  /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+}
+
+void test_20 (int i, int j)
+{
+  __analyzer_eval (i + 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i + j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i - 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i - j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i * 2); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i * j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i / 2); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i / j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i % 2); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i % j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i & 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i & j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i | 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i | j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i ^ 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i ^ j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i >> 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i >> j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i << 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i << j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i && 0); /* { dg-warning "FALSE" } */
+  __analyzer_eval (i && 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i && j); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+  /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */
+  /* { dg-warning "TRUE" "" { target *-*-* } .-2 } */
+  // TODO(xfail): ^^^ this implicitly generates control flow edges
+
+  __analyzer_eval (i || 0); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+  /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */
+  /* { dg-warning "TRUE" "" { target *-*-* } .-2 } */
+  // TODO(xfail): ^^^ this implicitly generates control flow edges
+
+  __analyzer_eval (i || 1); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i || j); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+  /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */
+  /* { dg-warning "TRUE" "" { target *-*-* } .-2 } */
+  // TODO(xfail): ^^^ this implicitly generates control flow edges
+
+  __analyzer_eval (~i); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (-i); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (+i); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+  /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */
+  /* { dg-warning "TRUE" "" { target *-*-* } .-2 } */
+  // TODO(xfail): ^^^ we already have control flow edges above, I believe
+
+  /* TODO:
+     - division by zero
+     - any other operators?  */
+  // etc
+
+  /* Anything added above should be added to the next function also.  */
+}
+
+/* As above, but where the values are known due to the region model,
+   but not known to GCC's regular optimizations (folding and SSA).  */
+
+void test_21 (void)
+{
+  int i, j, zero;
+  int *pi = &i;
+  int *pj = &j;
+  int *pzero = &zero;
+  *pi = 5;
+  *pj = 3;
+  *pzero = 0;
+
+  __analyzer_eval (i + j == 8); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i - j == 2); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i * j == 15); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i / j == 1); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i % j == 2); /* { dg-warning "TRUE" } */
+
+  /* Division by zero.  */
+  // TODO: should we warn for this?
+  __analyzer_eval (i / zero); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i % zero); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval ((i & 1) == (5 & 1)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i & j) == (5 & 3)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i | 1) == (5 | 1)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i | j) == (5 | 3)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i ^ 1) == (5 ^ 1)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i ^ j) == (5 ^ 3)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i >> 1) == (5 >> 1)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i >> j) == (5 >> 3)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i << 1) == (5 << 1)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i << j) == (5 << 3)); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i && 0); /* { dg-warning "FALSE" } */
+  __analyzer_eval (i && 1); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i && j); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (i || 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i || 1); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i || j); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (~i == ~5); /* { dg-warning "TRUE" } */
+  __analyzer_eval (-i == -5); /* { dg-warning "TRUE" } */
+  __analyzer_eval (+i == +5); /* { dg-warning "TRUE" } */
+}
+
+void test_22 (int i, int j)
+{
+  __analyzer_eval (i + j == i + j); /* { dg-warning "TRUE" } */
+  // FIXME: this is getting folded; can we build a non-folded equivalent?
+}
+
+void test_23 (struct foo *f, struct foo *g)
+{
+  int i, j, k;
+  i = f->i + g->i;
+  j = f->i + g->i;
+  k = f->i * g->i;
+  __analyzer_eval (i == j); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  /* TODO(xfail): we'd need to record that the two unknown values are both
+     the sum of the two unknown input values (and thus are the same); not
+     yet sure if we want arbitrary expression trees in the representation
+     (analysis termination concerns).  */
+
+  __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_24 (struct foo *f)
+{
+  struct foo g;
+  g.i = 42;
+  __analyzer_eval (g.i == 42); /* { dg-warning "TRUE" } */
+
+  /* Overwriting a whole struct should invalidate our knowledge
+     about fields within it.  */
+  g = *f;
+  __analyzer_eval (g.i == 42); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+  /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+}
+
+void test_25 (struct foo *f)
+{
+  struct foo g;
+  g.i = 42;
+  f->i = 43;
+  __analyzer_eval (f->i == 43); /* { dg-warning "TRUE" } */
+  __analyzer_eval (g.i == 42); /* { dg-warning "TRUE" } */
+
+  /* Overwriting a whole struct where we know things about the
+     source value should update our knowledge about fields within
+     the dest value.  */
+  g = *f;
+  __analyzer_eval (g.i == 43); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+}
+
+void test_26 (struct coord *p, struct coord *q)
+{
+  p->x = 42;
+  q->y = 17;
+  __analyzer_eval (p->x == 42); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p->y); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (q->x); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (q->y == 17); /* { dg-warning "TRUE" } */
+
+  /* Overwriting a whole struct where we know some things about the
+     source value should update our knowledge about fields within
+     the dest value.  */
+  *p = *q;
+  __analyzer_eval (p->x); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+  /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */
+  // TODO(xfail): should have been overwritten
+  __analyzer_eval (p->y == 17); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail): should have been overwritten with q->y
+
+  __analyzer_eval (q->x); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (q->y == 17); /* { dg-warning "TRUE" } */
+}
+
+void test_27 (struct coord *p)
+{
+  memset (p, 0, sizeof (struct coord));
+  __analyzer_eval (p->x == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+  __analyzer_eval (p->y == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+}
+
+void test_28 (struct coord *p)
+{
+  memset (p, 0, sizeof (struct coord) * 10);
+  __analyzer_eval (p[0].x == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+  __analyzer_eval (p[0].y == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+
+  __analyzer_eval (p[9].x == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+  __analyzer_eval (p[9].y == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+
+  __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_29 (struct coord *p)
+{
+  struct coord *q;
+
+  p[0].x = 100024;
+  p[0].y = 100025;
+
+  p[7].x = 107024;
+  p[7].y = 107025;
+
+  p[9].x = 109024;
+  p[9].y = 109025;
+
+  __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */
+
+  q = &p[7];
+
+  __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+  __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+
+  q += 2;
+
+  __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+  __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):  
+}
+
+void test_30 (void *ptr)
+{
+  struct coord *p = (struct coord *)ptr;
+  struct coord *q = (struct coord *)ptr;
+
+  p->x = 42;
+
+  __analyzer_eval (p->x == 42); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->x == 42); /* { dg-warning "TRUE" } */
+}
+
+void test_31 (unsigned i)
+{
+  int j, k;
+
+  j = i < 100 ? i : 100; /* MIN_EXPR.  */
+  k = i < 100 ? 100 : i; /* MAX_EXPR.  */
+}
+
+enum color
+{
+  RED,
+  GREEN,
+  BLUE
+};
+
+void test_32 (enum color c)
+{
+  __analyzer_eval (c == GREEN); /* { dg-warning "UNKNOWN" } */
+
+  c = RED;
+
+  __analyzer_eval (c == RED); /* { dg-warning "TRUE" } */
+  __analyzer_eval (c == GREEN); /* { dg-warning "FALSE" } */
+}
+
+void test_33 (void)
+{
+  static int s;
+
+  __analyzer_eval (s == 42); /* { dg-warning "UNKNOWN" } */
+
+  s = 42;
+
+  __analyzer_eval (s == 42); /* { dg-warning "TRUE" } */
+}
+
+static int __attribute__((noinline))
+only_called_by_test_34 (int parm)
+{
+  __analyzer_eval (parm == 42); /* { dg-warning "TRUE" } */
+
+  return parm * 2;
+}
+
+void test_34 (void)
+{
+  int result = only_called_by_test_34 (42);
+  __analyzer_eval (result == 84); /* { dg-warning "TRUE" } */
+}
+
+void test_35 (int i, int j)
+{
+  __analyzer_eval (&i == &i); /* { dg-warning "TRUE" } */
+  __analyzer_eval (&i != &j); /* { dg-warning "TRUE" } */
+}
+
+static void __attribute__((noinline))
+write_through_ptr (int *dst, int val)
+{
+  *dst = val;
+}
+
+void test_36 (int i)
+{
+  __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+
+  write_through_ptr (&i, 42);
+
+  __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+}
+
+// TODO: this currently crashes the analyzer
+#if 0
+int test_37 (void)
+{
+  /* Read through uninitialized pointer.  */
+  int *ptr;
+  return *ptr;
+}
+#endif
+
+// TODO: this currently crashes the analyzer
+#if 0
+int test_38 (void)
+{
+  /* Read through NULL pointer.  */
+  int *ptr = NULL;
+  return *ptr;
+}
+#endif
+
+// TODO: this currently crashes the analyzer
+#if 0
+int test_39 (void)
+{
+  /* Read through non-NULL constant pointer.  */
+  int *ptr = (int *)0x1000;
+  return *ptr;
+}
+#endif
+
+int test_40 (int flag)
+{
+  int i;
+  if (flag)
+    i = 43;
+  else
+    i = 17;
+
+  if (flag)
+    __analyzer_eval (i == 43); /* { dg-warning "TRUE" } */
+  else
+    __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+}
+
+// TODO:
+
+struct link
+{
+  struct link *next;
+};
+
+// TODO:
+
+union u
+{
+  int i;
+  int *ptr;
+};
+
+/* Coverage:
+
+   DONE (perhaps with xfail):
+   - pointers
+   - structs
+   - enums
+   - static variables
+   - interprocedural
+   - null pointer dereference
+   - constant pointer dereference (but not zero)
+   - deref of uninit pointer
+
+   TODO:
+   - phi nodes
+   - constraints
+   - alloca
+   - malloc
+   - char
+   - float/double
+   - string constants
+   - arrays
+   - aliasing
+   - unions
+   - bitfield operations
+   - globals
+   - memcpy, memmove, bcopy, memccpy, meppcpy, strcpy, etc from <string.h>
+   - loops
+     - known bound
+     - unknown bound
+   - goto
+   - third-party libraries
+   - recursive functions
+   - mutually-recursive functions
+   - pointer arithmetic
+   - casting from pointer to integer
+   - casting from integer to pointer
+   - widening and narrowing conversions
+   - int <-> float conversions
+   - other casts
+   - try it on a bunch of real-world code
+   - setjmp/longjmp
+   - function pointers
+   - C++
+   etc.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/equivalence.c b/gcc/testsuite/gcc.dg/analyzer/equivalence.c
index fb28660..3986967 100644
--- a/gcc/testsuite/gcc.dg/analyzer/equivalence.c
+++ b/gcc/testsuite/gcc.dg/analyzer/equivalence.c
@@ -1,3 +1,5 @@
+/* { 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/fibonacci.c b/gcc/testsuite/gcc.dg/analyzer/fibonacci.c
index 1ca3726..3af90b1 100644
--- a/gcc/testsuite/gcc.dg/analyzer/fibonacci.c
+++ b/gcc/testsuite/gcc.dg/analyzer/fibonacci.c
@@ -1,7 +1,8 @@
 int fib (int n)
 {
-  if (n > 1)
+  if (n > 1) /* { dg-bogus "analysis bailed out early" "" { xfail *-*-* } } */
     return fib (n - 1) + fib (n - 2);
   else
     return n;
-}
+  /* TODO(xfail) */
+} 
diff --git a/gcc/testsuite/gcc.dg/analyzer/fields.c b/gcc/testsuite/gcc.dg/analyzer/fields.c
new file mode 100644
index 0000000..37dbf1c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fields.c
@@ -0,0 +1,41 @@
+typedef long unsigned int size_t;
+
+extern size_t strlen (const char *__s)
+  __attribute__ ((__nothrow__ , __leaf__))
+  __attribute__ ((__pure__))
+  __attribute__ ((__nonnull__ (1)));
+
+extern void *malloc (size_t __size)
+  __attribute__ ((__nothrow__ , __leaf__))
+  __attribute__ ((__malloc__)) ;
+
+extern void free (void *__ptr)
+  __attribute__ ((__nothrow__ , __leaf__));
+
+typedef struct _krb5_data {
+  unsigned int length;
+  char *data;
+} krb5_data;
+
+typedef struct _krb5_error {
+  krb5_data text;
+} krb5_error;
+
+extern const char *error_message (int);
+
+int
+recvauth_common (int problem)
+{
+  if (problem) {
+    krb5_error error;
+    const char *message = error_message(problem);
+    error.text.length = strlen(message) + 1;
+    if (!(error.text.data = malloc(error.text.length))) {
+      goto cleanup;
+    }
+    free(error.text.data);
+  }
+
+ cleanup:
+  return problem; /* { dg-bogus "leak" "" { xfail *-*-* } } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c b/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c
index 8623a9f..44263bf 100644
--- a/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fanalyzer-region-model" } */
+
 #include <stdlib.h>
 
 typedef void (*fn_ptr_t) (void *);
@@ -8,10 +10,36 @@ calls_free (void *victim)
   free (victim);
 }
 
+void
+no_op (void *ptr)
+{
+}
+
 void test_1 (void *ptr)
 {
   fn_ptr_t fn_ptr = calls_free;
+  __analyzer_eval (fn_ptr == calls_free); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fn_ptr != NULL); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fn_ptr == NULL); /* { dg-warning "FALSE" } */
+  __analyzer_eval (fn_ptr == no_op); /* { dg-warning "FALSE" } */
+
   fn_ptr (ptr);
   fn_ptr (ptr);
 }
 // TODO: issue a double-free warning at 2nd call to fn_ptr.
+
+/* As above, but with an extra indirection to try to thwart
+   the optimizer.  */
+
+void test_2 (void *ptr, fn_ptr_t *fn_ptr)
+{
+  *fn_ptr = calls_free;
+  __analyzer_eval (*fn_ptr == calls_free); /* { dg-warning "TRUE" } */
+  __analyzer_eval (*fn_ptr != NULL); /* { dg-warning "TRUE" } */
+  __analyzer_eval (*fn_ptr == NULL); /* { dg-warning "FALSE" } */
+  __analyzer_eval (*fn_ptr == no_op); /* { dg-warning "FALSE" } */
+
+  (*fn_ptr) (ptr);
+  (*fn_ptr) (ptr);
+}
+// TODO: issue a double-free warning at 2nd call to fn_ptr.
diff --git a/gcc/testsuite/gcc.dg/analyzer/gzio.c b/gcc/testsuite/gcc.dg/analyzer/gzio.c
index 3a036d1..dba5c70 100644
--- a/gcc/testsuite/gcc.dg/analyzer/gzio.c
+++ b/gcc/testsuite/gcc.dg/analyzer/gzio.c
@@ -9,7 +9,8 @@ typedef struct gz_stream {
 gz_stream *s;
 static void gz_open(const char *path)
 {
-  s->stream.next_out = s->outbuf = (unsigned char *)malloc(16384);
+  s->stream.next_out = s->outbuf = (unsigned char *)malloc(16384); /* { dg-bogus "leak of '<unknown>'" "" { xfail *-*-* } } */
+  /* TODO(xfail): */
 }
 void gzopen(const char *path)
 { return gz_open(path); }
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop.c b/gcc/testsuite/gcc.dg/analyzer/loop.c
index 23294f2..936351b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/loop.c
+++ b/gcc/testsuite/gcc.dg/analyzer/loop.c
@@ -1,3 +1,7 @@
+// FIXME:
+/* { dg-additional-options "-fanalyzer-region-model" } */
+/* { dg-additional-options "-fno-analyzer-state-purge" } */
+
 void test(void)
 {
   int i;
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
index e30ff3c..1f139ae 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
@@ -1,8 +1,12 @@
+/* { dg-additional-options "-fanalyzer-region-model" } */
+
 #include <stdlib.h>
 #include <string.h>
 
 extern int foo (void);
 extern int bar (void);
+extern void could_free (void *);
+extern void cant_free (const void *); /* since it's a const void *.  */
 
 void test_1 (void)
 {
@@ -347,6 +351,31 @@ void test_31 (void)
   tmp.m_ptr = (struct link *)ptr;
 } /* { dg-warning "leak of 'ptr'" } */ 
 
+void test_32 (void)
+{
+  void *ptr = malloc (1024);
+  could_free (ptr);
+} /* { dg-bogus "leak" } */
+
+void test_33 (void)
+{
+  void *ptr = malloc (1024);
+  cant_free (ptr);
+} /* { dg-warning "leak of 'ptr'" } */ 
+
+void test_34 (void)
+{
+  float *q;
+  struct coord *p = malloc (sizeof (struct coord));
+  if (!p)
+    return;
+  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
+};
+
 #if 0
 void test_31 (void *p)
 {
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c
new file mode 100644
index 0000000..0121516
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c
@@ -0,0 +1,48 @@
+/* { dg-additional-options "-fanalyzer-region-model" } */
+
+#include <stdlib.h>
+
+extern void foo (void);
+extern void bar (void);
+
+void test_1 (int flag, int n);
+
+void caller_1_of_test_1 (int n)
+{
+  test_1 (1, n); /* { dg-bogus "test_1" } */
+  test_1 (0, n); /* { dg-bogus "test_1" } */
+}
+
+void __attribute__((noinline))
+test_1 (int flag, int n)
+{
+  int *ptr = (int *)malloc (sizeof (int));
+
+  if (flag)
+    {
+      int i;
+      for (i = 0; i < n; i++)
+	foo ();
+    }
+  else
+    bar ();
+
+  free (ptr);
+  free (ptr); /* { dg-warning "double-free" } */
+  /* FIXME: we get duplicates intraprocedurally, as there are two paths
+     through the function.
+     The calls in test_2 also generate additional duplicates.
+     How to verify lack of duplicates?
+     Putting a bogus on the interprocedual one detects that, at least.  */
+
+  if (flag)
+    foo ();
+  else
+    bar ();
+}
+
+void caller_2_of_test_1 (int n)
+{
+  test_1 (1, n); /* { dg-bogus "test_1" } */
+  test_1 (0, n); /* { dg-bogus "test_1" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c
index d033969..162799b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c
@@ -1,7 +1,7 @@
 #include <stddef.h>
 #include <stdlib.h>
 
-extern void do_stuff (void *);
+extern void do_stuff (const void *);
 
 #define LIMIT 1024
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local.c
new file mode 100644
index 0000000..d4d8557
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local.c
@@ -0,0 +1,83 @@
+#include <stdlib.h>
+
+extern int foo (int);
+
+static int do_stuff (int *p, int n)
+{
+  int sum = 0;
+  int i;
+  for (i = 0; i < n; i++)
+    p[i] = i;
+  for (i = 0; i < n; i++)
+    sum += foo (p[i]);
+  return sum;
+}
+
+/* Various examples of functions that use either a malloc buffer
+   or a local buffer, do something, then conditionally free the
+   buffer, tracking whether "free" is necessary in various
+   ways.  */
+
+#if 0
+int test_repeated_predicate (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  result = do_stuff (ptr, n);
+
+  if (n > 10)
+    free (ptr);
+
+  return result;
+}
+
+int test_explicit_flag (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+  int need_to_free = 0;
+
+  if (n > 10)
+    {
+      ptr = (int *)malloc (sizeof (int) * n);
+      need_to_free = 1;
+    }
+  else
+    ptr = buf;
+
+  result = do_stuff (ptr, n);
+
+  if (need_to_free)
+    free (ptr);
+
+  return result;
+}
+
+// TODO: currently this leads to an ICE:
+int test_pointer_comparison (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  result = do_stuff (ptr, n);
+
+  if (ptr != buf)
+    free (ptr);
+
+  return result;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/analyzer/operations.c b/gcc/testsuite/gcc.dg/analyzer/operations.c
index 9ea2327..a5e0997 100644
--- a/gcc/testsuite/gcc.dg/analyzer/operations.c
+++ b/gcc/testsuite/gcc.dg/analyzer/operations.c
@@ -1,3 +1,5 @@
+/* { 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 1584ebd..ede14f1 100644
--- a/gcc/testsuite/gcc.dg/analyzer/params-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/params-2.c
@@ -1,3 +1,5 @@
+/* { 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 ff405f2..d9bd530 100644
--- a/gcc/testsuite/gcc.dg/analyzer/params.c
+++ b/gcc/testsuite/gcc.dg/analyzer/params.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fanalyzer-region-model" } */
+
 static int called_function(int j)
 {
   int k;
diff --git a/gcc/testsuite/gcc.dg/analyzer/pragma-1.c b/gcc/testsuite/gcc.dg/analyzer/pragma-1.c
new file mode 100644
index 0000000..56abb84
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pragma-1.c
@@ -0,0 +1,26 @@
+/* Verify that we can disable analyzer warnings via pragmas.  */
+
+#include <stdlib.h>
+
+void test_1 (void *ptr)
+{
+  free (ptr);
+  free (ptr); /* { dg-warning "double-free" } */
+}
+
+void test_2 (void *ptr)
+{
+  _Pragma("GCC diagnostic push")
+  _Pragma("GCC diagnostic ignored \"-Wanalyzer-double-free\"")
+
+  free (ptr);
+  free (ptr);  /* { dg-bogus "double-free" } */
+
+  _Pragma("GCC diagnostic pop")
+}
+
+void test_3 (void *ptr)
+{
+  free (ptr);
+  free (ptr);  /* { dg-warning "double-free" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/switch.c b/gcc/testsuite/gcc.dg/analyzer/switch.c
index a55ba2b..02e2ec0 100644
--- a/gcc/testsuite/gcc.dg/analyzer/switch.c
+++ b/gcc/testsuite/gcc.dg/analyzer/switch.c
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-fanalyzer-region-model" } */
+
 void test (int i)
 {
   switch (i)
diff --git a/gcc/vec.c b/gcc/vec.c
index c418cb8..6e8ea06 100644
--- a/gcc/vec.c
+++ b/gcc/vec.c
@@ -517,6 +517,32 @@ test_reverse ()
   }
 }
 
+/* A test class that increments a counter every time its dtor is called.  */
+
+class count_dtor
+{
+ public:
+  count_dtor (int *counter) : m_counter (counter) {}
+  ~count_dtor () { (*m_counter)++; }
+
+ private:
+  int *m_counter;
+};
+
+/* Verify that auto_delete_vec deletes the elements within it.  */
+
+static void
+test_auto_delete_vec ()
+{
+  int dtor_count = 0;
+  {
+    auto_delete_vec <count_dtor> v;
+    v.safe_push (new count_dtor (&dtor_count));
+    v.safe_push (new count_dtor (&dtor_count));
+  }
+  ASSERT_EQ (dtor_count, 2);
+}
+
 /* Run all of the selftests within this file.  */
 
 void
@@ -534,6 +560,7 @@ vec_c_tests ()
   test_block_remove ();
   test_qsort ();
   test_reverse ();
+  test_auto_delete_vec ();
 }
 
 } // namespace selftest
diff --git a/notes.txt b/notes.txt
index 9f95f0f..4f4674f 100644
--- a/notes.txt
+++ b/notes.txt
@@ -6,3 +6,17 @@ Paths ought to be:
 * interprocedurally-valid
 * feasible
 ...but it's not clear to what extent we can enforce this.
+
+
+Useful debugging tips:
+
+Add:
+  __analyzer_break ();
+to the source being analyzed to trigger a breakpoint in the analyzer when
+that source is reached.  By putting a series of these in the source, it's
+much easier to effectively step through the program state as it's analyzed.
+
+__analyzer_dump ();
+
+will dump the copious information about the analyzer's state each time it
+reaches the call in its traversal of the source.
-- 
1.8.5.3

