From 43e6579394d25caf387eb17da3f01b9d29b1bb38 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 26 Sep 2019 16:23:36 -0400
Subject: [PATCH 58/63] FIXME: add dead code and old tests, DSL, etc

---
 gcc/analyzer/dead-code/address_of_function.sm      |   14 +
 gcc/analyzer/dead-code/analyzer-config.cc          |   54 +
 gcc/analyzer/dead-code/buffer_overflow.sm          |   75 ++
 gcc/analyzer/dead-code/constraints.cc              |  780 +++++++++++++++
 gcc/analyzer/dead-code/core-checker.cc             |  904 +++++++++++++++++
 gcc/analyzer/dead-code/fd_checker.sm               |   26 +
 gcc/analyzer/dead-code/fileptr.sm                  |   17 +
 gcc/analyzer/dead-code/libc-file-checker.cc        |  226 +++++
 gcc/analyzer/dead-code/libc-malloc-checker.cc      |  220 ++++
 gcc/analyzer/dead-code/malloc_checker.sm           |   74 ++
 gcc/analyzer/dead-code/points_to_stack.sm          |   60 ++
 gcc/analyzer/dead-code/python.sm                   |   61 ++
 gcc/analyzer/dead-code/signal_handler.sm           |   58 ++
 gcc/analyzer/dead-code/sizeof_allocation.sm        |   91 ++
 gcc/analyzer/dead-code/taint.sm                    |   71 ++
 gcc/analyzer/dead-code/toy-sm-impl-1.cc            | 1052 ++++++++++++++++++++
 gcc/analyzer/dead-code/toy-sm-impl-1.h             |    1 +
 gcc/testsuite/DEADCODE-analyzer.dg/analyzer.exp    |   49 +
 gcc/testsuite/DEADCODE-analyzer.dg/demo-3.c        |   31 +
 gcc/testsuite/DEADCODE-analyzer.dg/dg.exp          |   53 +
 .../DEADCODE-analyzer.dg/malloc/bad-size.c         |   20 +
 .../DEADCODE-analyzer.dg/memory/stack-address.c    |    8 +
 .../malloc-checker/fancy_control_flow/input.c      |   42 +
 .../malloc-checker/fancy_control_flow/script.py    |   23 +
 .../malloc-checker/fancy_control_flow/stderr.txt   |    4 +
 .../malloc-checker/fancy_control_flow/stdout.txt   |    0
 .../multiple-errors/unchecked_malloc/input.c       |   41 +
 .../multiple-errors/unchecked_malloc/script.py     |   46 +
 .../multiple-errors/unchecked_malloc/stderr.txt    |    4 +
 .../multiple-errors/unchecked_malloc/stdout.txt    |    0
 .../checkers/points_to_stack/putenv/input.c        |   31 +
 .../checkers/points_to_stack/putenv/script.py      |   31 +
 .../points_to_stack/return_of_ptr_to_local/input.c |   69 ++
 .../return_of_ptr_to_local/script.py               |   30 +
 .../return_of_ptr_to_local/stderr.txt              |    5 +
 .../return_of_ptr_to_local/stdout.txt              |    0
 .../checkers/signal_handler/example/input.c        |   52 +
 .../checkers/signal_handler/example/script.py      |   30 +
 .../sizeof_allocation/calloc_too_small/input.c     |   34 +
 .../sizeof_allocation/calloc_too_small/script.py   |   23 +
 .../sizeof_allocation/calloc_too_small/stderr.txt  |    2 +
 .../sizeof_allocation/calloc_too_small/stdout.txt  |    0
 .../sizeof_allocation/malloc_correct_size/input.c  |   31 +
 .../malloc_correct_size/script.py                  |   23 +
 .../malloc_correct_size/stdout.txt                 |    0
 .../sizeof_allocation/malloc_too_large/input.c     |   36 +
 .../sizeof_allocation/malloc_too_large/script.py   |   23 +
 .../sizeof_allocation/malloc_too_large/stdout.txt  |    0
 .../sizeof_allocation/malloc_too_small/input.c     |   34 +
 .../sizeof_allocation/malloc_too_small/script.py   |   23 +
 .../sizeof_allocation/malloc_too_small/stderr.txt  |    2 +
 .../sizeof_allocation/malloc_too_small/stdout.txt  |    0
 .../stateful_malloc_too_small/input.c              |   36 +
 .../stateful_malloc_too_small/script.py            |   40 +
 .../stateful_malloc_too_small/stderr.txt           |    4 +
 .../stateful_malloc_too_small/stdout.txt           |    0
 .../sizeof_allocation/unknown_size/input.c         |   32 +
 .../sizeof_allocation/unknown_size/script.py       |   38 +
 .../sizeof_allocation/unknown_size/stdout.txt      |    0
 .../TODO-analyzer/checkers/taint/example/script.py |   71 ++
 .../checkers/taint/example/stderr.txt              |   22 +
 gcc/testsuite/gcc.dg/TODO-analyzer/dot/input.c     |    1 +
 gcc/testsuite/gcc.dg/TODO-analyzer/dot/script.py   |   55 +
 gcc/testsuite/gcc.dg/TODO-analyzer/dot/stdout.txt  |   83 ++
 .../TODO-analyzer/interprocedural/complex/input.c  |   56 ++
 .../interprocedural/complex/script.py              |   23 +
 .../interprocedural/complex/stderr.txt             |   13 +
 .../interprocedural/complex/stdout.txt             |    0
 .../param-inherits-from-arg/input.c                |   34 +
 .../param-inherits-from-arg/script.py              |   33 +
 .../param-inherits-from-arg/stderr.txt             |    5 +
 .../param-inherits-from-arg/stdout.txt             |    0
 gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-f.c   |   34 +
 gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-g.c   |   31 +
 gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-h.c   |   31 +
 gcc/testsuite/gcc.dg/TODO-analyzer/lto/script.py   |   25 +
 gcc/testsuite/gcc.dg/TODO-analyzer/lto/stderr.txt  |   29 +
 gcc/testsuite/gcc.dg/TODO-analyzer/lto/stdout.txt  |    0
 gcc/testsuite/gcc.dg/TODO-analyzer/lto/test.h      |   22 +
 .../TODO-analyzer/parser/malloc-checker/input.c    |   91 ++
 .../TODO-analyzer/parser/malloc-checker/script.py  |  189 ++++
 .../TODO-analyzer/patterns/arg-of-fn-call/input.c  |   25 +
 .../patterns/arg-of-fn-call/script.py              |   32 +
 .../patterns/arg-of-fn-call/stderr.txt             |    2 +
 .../patterns/arg-of-fn-call/stdout.txt             |    0
 .../patterns/arg_must_not_be_null/input.c          |   44 +
 .../patterns/arg_must_not_be_null/script.py        |   39 +
 .../patterns/arg_must_not_be_null/stderr.txt       |   21 +
 .../patterns/arg_must_not_be_null/stdout.txt       |    0
 .../TODO-analyzer/patterns/args-of-fn-call/input.c |   25 +
 .../patterns/args-of-fn-call/script.py             |   34 +
 .../patterns/args-of-fn-call/stderr.txt            |    2 +
 .../patterns/args-of-fn-call/stdout.txt            |    0
 .../gcc.dg/TODO-analyzer/patterns/array/input.c    |   25 +
 .../gcc.dg/TODO-analyzer/patterns/array/script.py  |   33 +
 .../gcc.dg/TODO-analyzer/patterns/array/stderr.txt |    2 +
 .../gcc.dg/TODO-analyzer/patterns/array/stdout.txt |    0
 .../TODO-analyzer/patterns/named-pattern/input.c   |   30 +
 .../TODO-analyzer/patterns/named-pattern/script.py |   34 +
 .../patterns/named-pattern/stderr.txt              |    2 +
 .../patterns/named-pattern/stdout.txt              |    0
 .../gcc.dg/TODO-analyzer/patterns/or/input.c       |   39 +
 .../gcc.dg/TODO-analyzer/patterns/or/script.py     |   32 +
 .../gcc.dg/TODO-analyzer/patterns/or/stderr.txt    |    4 +
 .../gcc.dg/TODO-analyzer/patterns/or/stdout.txt    |    0
 .../patterns/result-of-fn-call/input.c             |   30 +
 .../patterns/result-of-fn-call/script.py           |   32 +
 .../patterns/result-of-fn-call/stderr.txt          |    2 +
 .../patterns/result-of-fn-call/stdout.txt          |    0
 .../TODO-analyzer/patterns/var-dereference/input.c |   32 +
 .../patterns/var-dereference/script.py             |   32 +
 .../patterns/var-dereference/stderr.txt            |    4 +
 .../patterns/var-dereference/stdout.txt            |    0
 .../TODO-analyzer/patterns/var-usage/input.c       |   23 +
 .../TODO-analyzer/patterns/var-usage/script.py     |   34 +
 .../TODO-analyzer/patterns/var-usage/stderr.txt    |    3 +
 .../TODO-analyzer/patterns/var-usage/stdout.txt    |    0
 117 files changed, 6199 insertions(+)
 create mode 100644 gcc/analyzer/dead-code/address_of_function.sm
 create mode 100644 gcc/analyzer/dead-code/analyzer-config.cc
 create mode 100644 gcc/analyzer/dead-code/buffer_overflow.sm
 create mode 100644 gcc/analyzer/dead-code/constraints.cc
 create mode 100644 gcc/analyzer/dead-code/core-checker.cc
 create mode 100644 gcc/analyzer/dead-code/fd_checker.sm
 create mode 100644 gcc/analyzer/dead-code/fileptr.sm
 create mode 100644 gcc/analyzer/dead-code/libc-file-checker.cc
 create mode 100644 gcc/analyzer/dead-code/libc-malloc-checker.cc
 create mode 100644 gcc/analyzer/dead-code/malloc_checker.sm
 create mode 100644 gcc/analyzer/dead-code/points_to_stack.sm
 create mode 100644 gcc/analyzer/dead-code/python.sm
 create mode 100644 gcc/analyzer/dead-code/signal_handler.sm
 create mode 100644 gcc/analyzer/dead-code/sizeof_allocation.sm
 create mode 100644 gcc/analyzer/dead-code/taint.sm
 create mode 100644 gcc/analyzer/dead-code/toy-sm-impl-1.cc
 create mode 100644 gcc/analyzer/dead-code/toy-sm-impl-1.h
 create mode 100644 gcc/testsuite/DEADCODE-analyzer.dg/analyzer.exp
 create mode 100644 gcc/testsuite/DEADCODE-analyzer.dg/demo-3.c
 create mode 100644 gcc/testsuite/DEADCODE-analyzer.dg/dg.exp
 create mode 100644 gcc/testsuite/DEADCODE-analyzer.dg/malloc/bad-size.c
 create mode 100644 gcc/testsuite/DEADCODE-analyzer.dg/memory/stack-address.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/putenv/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/putenv/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_correct_size/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_correct_size/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_correct_size/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_large/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_large/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_large/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/unknown_size/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/unknown_size/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/unknown_size/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/taint/example/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/checkers/taint/example/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/dot/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/dot/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/dot/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-f.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-g.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-h.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/lto/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/lto/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/lto/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/lto/test.h
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/parser/malloc-checker/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/parser/malloc-checker/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/input.c
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/script.py
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/stderr.txt
 create mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/stdout.txt

diff --git a/gcc/analyzer/dead-code/address_of_function.sm b/gcc/analyzer/dead-code/address_of_function.sm
new file mode 100644
index 0000000..9917ab8
--- /dev/null
+++ b/gcc/analyzer/dead-code/address_of_function.sm
@@ -0,0 +1,14 @@
+/* function not invoked */
+sm address_of_function {
+   /* global state */
+   decl any_function_ptr fnptr;
+
+   fnptr.*:
+      { fnptr } ->
+          {{
+             # how to properly characterize?
+             if is_simple_reference(fnptr):
+                 error('usage of %s when you probably meant %s()')
+                 # does this have a CWE?
+           }};
+}
\ No newline at end of file
diff --git a/gcc/analyzer/dead-code/analyzer-config.cc b/gcc/analyzer/dead-code/analyzer-config.cc
new file mode 100644
index 0000000..2a23dd1
--- /dev/null
+++ b/gcc/analyzer/dead-code/analyzer-config.cc
@@ -0,0 +1,54 @@
+/* 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 "analyzer.h"
+
+namespace ana
+{
+
+/* Build an analyzer instance.  */
+
+analyzer *
+make_analyzer (void)
+{
+  analyzer *a = new ana::analyzer ();
+  a->add_checker (make_core_checker (a));
+  a->add_checker (make_constraints_checker (a));
+  a->add_checker (make_core_divide_by_zero_checker ());
+  a->add_checker (make_core_function_call_checker ());
+  a->add_checker (make_libc_file_checker ());
+  a->add_checker (make_libc_malloc_checker ());
+  /* etc */
+
+  /* Ultimately this should read in configuration
+     and act accordingly.  */
+
+  return a;
+}
+
+} // namespace ana
diff --git a/gcc/analyzer/dead-code/buffer_overflow.sm b/gcc/analyzer/dead-code/buffer_overflow.sm
new file mode 100644
index 0000000..47c2470
--- /dev/null
+++ b/gcc/analyzer/dead-code/buffer_overflow.sm
@@ -0,0 +1,75 @@
+/*
+   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/>.
+
+   Checker to detect typos of the form:  malloc(sizeof(WRONG_TYPE))
+*/
+sm buffer_overflow {
+  state decl char * ptr;
+  decl any_pointer other_ptr;
+  decl any_expr nmemb;
+  decl any_expr sz;
+
+  /* We will use:
+       "size" for the size of the allocated buffer (when known),
+       "strlen" for the length of the string (not including NUL character),
+       when known
+     
+     None means "unknown" in both cases
+  */
+  {{
+      # Utility methods for getting at current state and size, given
+      # that the initial state has no key/value pairs set
+      def get_size(state):
+          if hasattr(state, 'size'):
+             return state.size
+          return None
+
+      def get_strlen(state):
+          if hasattr(state, 'strlen'):
+             return state.strlen
+          return None
+  }}
+
+  ptr.all:
+    /* Various allocators: */
+      /* libc: */
+      { ptr = malloc(sz) }
+    | { ptr = realloc(other_ptr, sz) }
+      /* libpython: */
+    | { ptr = PyMem_Malloc(sz) }
+    | { ptr = PyMem_Realloc(other_ptr, sz) }
+      /* glib: */
+    | { ptr = g_malloc(sz) }
+    | { ptr = g_malloc0(sz) }
+    | { ptr = g_realloc(other_ptr, sz) }
+    | { ptr = g_try_malloc(sz) }
+    | { ptr = g_try_malloc0(sz) }
+    | { ptr = g_try_realloc(other_ptr, sz) }
+      /* etc */
+      -> {{
+    set_state('ptr', size=sz, strlen=None)
+         }};
+
+    | {sz = strlen(ptr) }
+      -> {{
+          set_state('ptr',
+                    size=get_size(state),
+                    strlen=sz)
+        }}
+      /* etc */
+}
diff --git a/gcc/analyzer/dead-code/constraints.cc b/gcc/analyzer/dead-code/constraints.cc
new file mode 100644
index 0000000..93407c2
--- /dev/null
+++ b/gcc/analyzer/dead-code/constraints.cc
@@ -0,0 +1,780 @@
+/* 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 "print-tree.h"
+#include "graphviz.h"
+
+namespace ana
+{
+
+class constraints_checker;
+
+enum constraint_op
+{
+  CONSTRAINT_EQ,
+  CONSTRAINT_NE,
+  CONSTRAINT_LT,
+  CONSTRAINT_LE
+};
+
+static const char *
+constraint_op_code (enum constraint_op c_op)
+{
+  switch (c_op)
+    {
+    default:
+      gcc_unreachable ();
+    case CONSTRAINT_EQ: return "==";
+    case CONSTRAINT_NE: return "!=";
+    case CONSTRAINT_LT: return "<";
+    case CONSTRAINT_LE: return "<=";
+    }
+}
+
+class constraint {
+ public:
+  constraint (absvalue *lhs, enum constraint_op c_op, absvalue *rhs)
+    : m_lhs (lhs), m_op (c_op), m_rhs (rhs) {}
+
+  absvalue *m_lhs;
+  enum constraint_op m_op;
+  absvalue *m_rhs;
+};
+
+class known_constraints : public custom_state {
+ public:
+  custom_state *clone () const
+  {
+    return new known_constraints (*this);
+  }
+
+  void dump_to_dot_record (pretty_printer *pp) const;
+  void add_extra_nodes_and_edges (const node *n,
+				  graphviz_out *gv) const;
+
+  known_constraints () : m_constraints () {}
+  known_constraints (const known_constraints &other);
+
+  bool add_constraint (absvalue *lhs, enum tree_code op, absvalue *rhs);
+  bool add_constraint (context *ctxt, node *n, absvalue *lhs, bool value);
+  bool add_constraint (context *ctxt, node *n, tree lhs, bool value);
+
+  tristate
+  eval_condition (absvalue *lhs, enum tree_code op, absvalue *rhs);
+
+  tristate
+  eval_condition (context *ctxt, node *n,
+		  tree lhs, enum tree_code op, tree rhs);
+
+ private:
+  tristate impl_eq (absvalue *lhs, absvalue *rhs);
+  tristate impl_lt (absvalue *lhs, absvalue *rhs);
+  tristate impl_le (absvalue *lhs, absvalue *rhs);
+  tristate impl_ne (absvalue *lhs, absvalue *rhs) { return not impl_eq (lhs, rhs); }
+
+ private:
+  /* TODO: optimize */
+  vec<constraint *> m_constraints;
+  friend class constraints_checker;
+
+};
+
+/* The checker itself.  */
+
+class constraints_checker : public stateful_checker <known_constraints>, public constraints
+{
+ public:
+  void on_node (node *n, context *ctxt);
+  void before_call (node *n, gcall *call, context *ctxt);
+  void after_call (node *n, gcall *call, context *ctxt);
+  void dead_value (node *n, context *ctxt);
+
+  /* Implementation of constraints.  */
+  tristate
+  eval_condition (node *n, absvalue *lhs, enum tree_code op, absvalue *rhs);
+
+  bool
+  add_constraint (state *s, absvalue *lhs, enum tree_code op, absvalue *rhs);
+
+ private:
+};
+
+} // namespace ana
+
+ana::checker *ana::make_constraints_checker (analyzer *a)
+{
+  constraints_checker *ch = new constraints_checker ();
+  a->set_constraints (ch);
+  return ch;
+}
+
+/* class known_constraints : public per_checker_state */
+
+ana::known_constraints::known_constraints (const known_constraints &other)
+: m_constraints ()
+{
+  /* FIXME: do we really need to copy explicitly?  */
+  int i;
+  constraint *c;
+  FOR_EACH_VEC_ELT (other.m_constraints, i, c)
+    m_constraints.safe_push (c);
+}
+
+void
+ana::known_constraints::dump_to_dot_record (pretty_printer *pp) const
+{
+  int i;
+  constraint *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      pp_printf (pp, "constraint %i: ", i);
+      c->m_lhs->dump_to_dot_record (pp);
+      pp_printf (pp, "  %s ", constraint_op_code (c->m_op));
+      c->m_rhs->dump_to_dot_record (pp);
+      pp_newline (pp);
+      pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+    }
+}
+
+void
+ana::known_constraints::add_extra_nodes_and_edges (const node *n, graphviz_out *gv) const
+{
+  /* Begin a subgraph.  */
+  gv->println ("subgraph \"cluster_known_constraints_%p\" {",
+	       (void *)this);
+  gv->indent ();
+  gv->println ("style=\"dashed\";");
+  gv->println ("color=\"blue\";");
+  gv->println ("clusterrank=\"local\";");
+  gv->println ("label=\"known_constraints\";");
+
+  /* Add nodes and edges visualizing the known constraints.  */
+  pretty_printer *pp = gv->m_pp;
+  int i;
+  constraint *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      gv->write_indent ();
+      pp_printf (pp,
+		 "known_constraints_%p_node_%p [label=\"",
+		 (void *)this,
+		 c->m_lhs);
+      c->m_lhs->dump_to_dot_record (pp);
+      pp_string (pp, "\"];\n");
+
+      gv->write_indent ();
+      pp_printf (pp,
+		 "known_constraints_%p_node_%p [label=\"",
+		 (void *)this,
+		 c->m_rhs);
+      c->m_rhs->dump_to_dot_record (pp);
+      pp_string (pp, "\"];\n");
+
+      const char *style = "\"solid\"";
+      const char *color = "black";
+      int weight = 0;
+      /* FIXME: for some reason, setting constraint = "true" plays havoc
+	 with the layout, despite having clusterrank=local.
+	 Keep it false for now.  */
+      const char *constraint = "false";
+      const char *label = constraint_op_code (c->m_op);
+      gv->write_indent ();
+
+      /* Adding edge labels here leads to failures from graphviz of this form:
+	 libpath/shortest.c:324: triangulation failed
+	 libpath/shortest.c:192: source point not in any triangle
+	 Error: in routesplines, Pshortestpath failed
+	 which appears to be:
+	 http://www.graphviz.org/mantisbt/view.php?id=2382
+	 However, it appears to work if we set taillabel instead.  */
+      pp_printf (pp,
+		 "known_constraints_%p_node_%p -> known_constraints_%p_node_%p "
+		 "[style=%s,color=%s,weight=%d,constraint=%s, len=0.1, taillabel=\"%s\"];\n",
+		 (void *)this, (void *)c->m_lhs,
+		 (void *)this, (void *)c->m_rhs,
+		 style, color, weight,
+		 constraint, label);
+    }
+
+  /* Terminate "subgraph" */
+  gv->outdent ();
+  gv->println ("}");
+}
+
+/* Attempt to add the constraint, returning true if the result is
+   satisfiable, false if it is unsatisfiable.  */
+
+bool
+ana::known_constraints::add_constraint (absvalue *lhs, enum tree_code op, absvalue *rhs)
+{
+  gcc_assert (lhs);
+  gcc_assert (rhs);
+
+#if 0
+  fprintf (stdout, "add_constraint: ");
+
+  pretty_printer pp;
+  pp.buffer->stream = stdout;
+  dump_to_dot_record (&pp);
+  pp_flush (&pp);
+
+  debug (lhs);
+  fprintf (stdout, " %s ", op_symbol_code (op));
+  debug (rhs);
+  fprintf (stdout, "\n");
+#endif
+
+  tristate t = eval_condition (lhs, op, rhs);
+
+  /* Discard constraints that are already known.  */
+  if (t.is_true ())
+    return true;
+
+  /* Reject unsatisfiable constraints.  */
+  if (t.is_false ())
+    return false;
+
+  /* For now, simply accumulate constraints, without attempting any further
+     optimization.  */
+  constraint *c = NULL;
+  switch (op)
+    {
+    case EQ_EXPR: c = new constraint (lhs, CONSTRAINT_EQ, rhs); break;
+    case GE_EXPR: c = new constraint (rhs, CONSTRAINT_LT, lhs); break;
+    case LE_EXPR: c = new constraint (lhs, CONSTRAINT_LE, rhs); break;
+    case NE_EXPR: c = new constraint (lhs, CONSTRAINT_NE, rhs); break;
+    case GT_EXPR: c = new constraint (rhs, CONSTRAINT_LE, lhs); break;
+    case LT_EXPR: c = new constraint (lhs, CONSTRAINT_LT, rhs); break;
+    default:
+      /* do nothing.  */
+      break;
+    }
+  if (c)
+    m_constraints.safe_push (c);
+  return true;
+}
+
+bool
+ana::known_constraints::add_constraint (context *ctxt, node *n, absvalue *v_lhs, bool value)
+{
+  gcc_assert (v_lhs);
+
+  /* Make "true" and "false" as !=/== zero */
+  enum tree_code op = value ? NE_EXPR : EQ_EXPR;
+
+  absvalue *v_rhs = ctxt->eval_rvalue (n, integer_zero_node);
+
+  return add_constraint (v_lhs, op, v_rhs);
+}
+
+bool
+ana::known_constraints::add_constraint (context *ctxt, node *n, tree t_lhs, bool value)
+{
+  absvalue *v_lhs = ctxt->eval_rvalue (n, t_lhs);  
+  gcc_assert (v_lhs);
+  return add_constraint (ctxt, n, v_lhs, value);
+}
+
+ana::tristate
+ana::known_constraints::eval_condition (absvalue *lhs, enum tree_code op, absvalue *rhs)
+{
+#if 0
+  fprintf (stderr, "eval_condition: ");
+
+  pretty_printer pp;
+  pp.buffer->stream = stderr;
+  dump_to_dot_record (&pp);
+  pp_flush (&pp);
+
+  debug (lhs);
+  fprintf (stderr, " %s ", op_symbol_code (op));
+  debug (rhs);
+  fprintf (stderr, "\n");
+#endif
+
+  tristate t = tristate (tristate::TS_UNKNOWN);
+
+  switch (op)
+    {
+    case EQ_EXPR: t = impl_eq (lhs, rhs); break;
+    case GE_EXPR: t = impl_lt (rhs, lhs); break;
+    case LE_EXPR: t = impl_le (lhs, rhs); break;
+    case NE_EXPR: t = impl_ne (lhs, rhs); break;
+    case GT_EXPR: t = impl_le (rhs, lhs); break;
+    case LT_EXPR: t = impl_lt (lhs, rhs); break;
+    default:
+      break;
+    }
+
+#if 0
+  fprintf (stderr, "result: %s\n", t.as_string ());
+#endif
+  return t;
+}
+
+ana::tristate
+ana::known_constraints::eval_condition (context *ctxt,
+					node *n,
+					tree t_lhs,
+					enum tree_code op,
+					tree t_rhs)
+{
+  absvalue *v_lhs = ctxt->eval_rvalue (n, t_lhs);
+  absvalue *v_rhs = ctxt->eval_rvalue (n, t_rhs);
+  return eval_condition (v_lhs, op, v_rhs);
+}
+
+
+/* TODO: implement some logic for avoid infinite recursion
+   e.g. a bitset of edges that have been followed
+   (i.e. constraint indices).  */
+
+/* Does A == B ? */
+ana::tristate
+ana::known_constraints::impl_eq (absvalue *a, absvalue *b)
+{
+  if (a == b)
+    return tristate (tristate::TS_TRUE);
+
+  if (a && b)
+    {
+      tristate t = a->eq (b);
+      if (t.is_known ())
+	return t;
+    }
+
+#if 0
+  if (TREE_CODE (a) == INTEGER_CST
+      && TREE_CODE (b) == INTEGER_CST)
+    {
+      return tristate (tree_int_cst_compare (a, b) == 0);
+    }
+
+  int i;
+  constraint *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      if (a == c->m_lhs)
+	{
+	  switch (c->m_op)
+	    {
+	    default:
+	      gcc_unreachable ();
+
+	    case CONSTRAINT_EQ:
+	      /* We have a == R;  a == b iff b == R; recurse.  */
+	      {
+		tristate t = impl_eq (b, c->m_rhs);
+		if (t.is_known ())
+		  return t;
+	      }
+	      break;
+
+	    case CONSTRAINT_NE:
+	      /* We have a != R;  hence b == R would imply !(a == b).  Recurse. */
+	      {
+		tristate t = impl_eq (b, c->m_rhs);
+		if (t.is_true ())
+		  return tristate (tristate::TS_FALSE);
+	      }
+	      break;
+
+	    case CONSTRAINT_LT:
+	      /* We have (a < R);  hence R <= b would imply !(a == b).  Recurse. */
+	      {
+		tristate t = impl_le (c->m_rhs, b);
+		if (t.is_true ())
+		  return tristate (tristate::TS_FALSE);
+	      }
+	      break;
+
+	    case CONSTRAINT_LE:
+	      /* We have (a <= R);  hence R < b would imply !(a == b).  Recurse. */
+	      {
+		tristate t = impl_lt (c->m_rhs, b);
+		if (t.is_true ())
+		  return tristate (tristate::TS_FALSE);
+	      }
+	      break;
+	    }
+	}
+
+      if (b == c->m_lhs)
+	{
+	  switch (c->m_op)
+	    {
+	    default:
+	      gcc_unreachable ();
+
+	    case CONSTRAINT_EQ:
+	      /* We have b == R;  a == b iff a == R; recurse.  */
+	      {
+		tristate t = impl_eq (a, c->m_rhs);
+		if (t.is_known ())
+		  return t;
+	      }
+	      break;
+
+	    case CONSTRAINT_NE:
+	      /* We have b != R;  hence a == R would imply !(a == b).  Recurse. */
+	      {
+		tristate t = impl_eq (a, c->m_rhs);
+		if (t.is_true ())
+		  return tristate (tristate::TS_FALSE);
+	      }
+	      break;
+
+	    case CONSTRAINT_LT:
+	      /* We have (b < R);  hence R <= a would imply !(a == b).  Recurse. */
+	      {
+		tristate t = impl_le (c->m_rhs, a);
+		if (t.is_true ())
+		  return tristate (tristate::TS_FALSE);
+	      }
+	      break;
+
+	    case CONSTRAINT_LE:
+	      /* We have (b <= R);  hence R < a would imply !(a == b).  Recurse. */
+	      {
+		tristate t = impl_lt (c->m_rhs, a);
+		if (t.is_true ())
+		  return tristate (tristate::TS_FALSE);
+	      }
+	      break;
+	    }
+	}
+
+      /* TODO: need to do rhs also.  */
+    }
+#endif
+
+  return tristate (tristate::TS_UNKNOWN);
+}
+
+/* Is A < B ? */
+ana::tristate
+ana::known_constraints::impl_lt (absvalue *a, absvalue *b)
+{
+  if (a == b)
+    return tristate (tristate::TS_FALSE);
+
+#if 0
+  if (TREE_CODE (a) == INTEGER_CST
+      && TREE_CODE (b) == INTEGER_CST)
+    {
+      return tristate (tree_int_cst_lt (a, b));
+    }
+#endif
+
+  int i;
+  constraint *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      if (a == c->m_lhs)
+	{
+	  switch (c->m_op)
+	    {
+	    default:
+	      gcc_unreachable ();
+
+	    case CONSTRAINT_EQ:
+	      /* We have (a == R);  a < b iff R < b; recurse.  */
+	      {
+		tristate t = impl_lt (c->m_rhs, b);
+		if (t.is_known ())
+		  return t;
+	      }
+	      break;
+
+	    case CONSTRAINT_NE:
+	      /* We have (a != R). */
+	      break;
+
+	    case CONSTRAINT_LT:
+	      /* We have (a < R); (R <= b) implies (a < b).  */
+	      {
+		tristate t = impl_le (c->m_rhs, b);
+		if (t.is_true ())
+		  return t;
+	      }
+	      break;
+
+	    case CONSTRAINT_LE:
+	      /* We have (a <= R); (R < b) implies (a < b).  */
+	      {
+		tristate t = impl_lt (c->m_rhs, b);
+		if (t.is_true ())
+		  return t;
+	      }
+	      break;
+	    }
+	}
+
+      if (b == c->m_lhs)
+	{
+	  switch (c->m_op)
+	    {
+	    default:
+	      gcc_unreachable ();
+
+	    case CONSTRAINT_EQ:
+	      /* We have (b == R);  a < b iff a < R; recurse.  */
+	      {
+		tristate t = impl_lt (a, c->m_rhs);
+		if (t.is_known ())
+		  return t;
+	      }
+	      break;
+
+	    case CONSTRAINT_NE:
+	      /* We have (b != R). */
+	      break;
+
+	    case CONSTRAINT_LT:
+	      /* We have (b < R); (R < a) implies !(a < b).  */
+	      {
+		tristate t = impl_lt (c->m_rhs, a);
+		if (t.is_true ())
+		  return tristate (tristate::TS_FALSE);
+	      }
+	      break;
+
+	    case CONSTRAINT_LE:
+	      /* We have (b <= R); (R <= a) implies !(a < b).  */
+	      {
+		tristate t = impl_le (c->m_rhs, a);
+		if (t.is_true ())
+		  return tristate (tristate::TS_FALSE);
+	      }
+	      break;
+	    }
+	}
+
+      /* TODO: need to do rhs also.  */
+    }
+
+  return tristate (tristate::TS_UNKNOWN);
+}
+
+/* Is A <= B ? */
+ana::tristate
+ana::known_constraints::impl_le (absvalue *a, absvalue *b)
+{
+  if (a == b)
+    return tristate (tristate::TS_TRUE);
+
+#if 0
+  if (TREE_CODE (a) == INTEGER_CST
+      && TREE_CODE (b) == INTEGER_CST)
+    {
+      return tristate (tree_int_cst_le (a, b));
+    }
+#endif
+
+  int i;
+  constraint *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      if (a == c->m_lhs)
+	{
+	  switch (c->m_op)
+	    {
+	    default:
+	      gcc_unreachable ();
+
+	    case CONSTRAINT_EQ:
+	      /* We have (a == R);  a <= b iff R <= b; recurse.  */
+	      {
+		tristate t = impl_le (c->m_rhs, b);
+		if (t.is_known ())
+		  return t;
+	      }
+	      break;
+
+	    case CONSTRAINT_NE:
+	      break;
+
+	    case CONSTRAINT_LT:
+	      /* We have (a < R); (R <= b) implies (a <= b).  */
+	      {
+		tristate t = impl_le (c->m_rhs, b);
+		if (t.is_true ())
+		  return t;
+	      }
+	      break;
+
+	    case CONSTRAINT_LE:
+	      /* We have (a <= R); (R <= b) implies (a <= b).  */
+	      {
+		tristate t = impl_le (c->m_rhs, b);
+		if (t.is_true ())
+		  return t;
+	      }
+	      break;
+	    }
+	}
+
+      if (b == c->m_lhs)
+	{
+	  switch (c->m_op)
+	    {
+	    default:
+	      gcc_unreachable ();
+
+	    case CONSTRAINT_EQ:
+	      /* We have (b == R);  a <= b iff a <= R; recurse.  */
+	      {
+		tristate t = impl_le (a, c->m_rhs);
+		if (t.is_known ())
+		  return t;
+	      }
+	      break;
+
+	    case CONSTRAINT_NE:
+	      break;
+
+	    case CONSTRAINT_LT:
+	      /* We have (b < R); (R < a) implies !(a <= b).  */
+	      {
+		tristate t = impl_lt (c->m_rhs, b);
+		if (t.is_true ())
+		  return tristate (tristate::TS_FALSE);
+	      }
+	      break;
+
+	    case CONSTRAINT_LE:
+	      /* We have (b <= R); (R < a) implies !(a <= b).  */
+	      {
+		tristate t = impl_lt (c->m_rhs, a);
+		if (t.is_true ())
+		  return tristate (tristate::TS_FALSE);
+	      }
+	      break;
+	    }
+	}
+      /* TODO: need to do rhs also.  */
+    }
+
+  return tristate (tristate::TS_UNKNOWN);
+}
+
+/* class constraints_checker : public checker */
+
+void
+ana::constraints_checker::on_node (node *n, context *ctxt)
+{
+  // TODO
+  if (0)
+    {
+      printf ("node: %p\n", n);
+      n->dump (stderr);
+    }
+
+  /* Handle the effect of statements.  */
+  gimple *stmt = n->get_location ()->get_stmt ();
+  if (!stmt)
+    return;
+
+  gassign *assign = dyn_cast <gassign *> (stmt);
+  if (assign)
+    {
+      if (0)
+	{
+	  printf ("handle assignment: ");
+	  print_gimple_stmt (stderr, assign, 0, (dump_flags_t)0);
+	}
+      enum tree_code subcode = gimple_assign_rhs_code (assign);
+      tree lhs = gimple_assign_lhs (assign);
+      switch (get_gimple_rhs_class (subcode))
+	{
+	case GIMPLE_SINGLE_RHS:
+	    break;
+	case GIMPLE_UNARY_RHS:
+	  if (subcode == NOP_EXPR)
+	    {
+	      tree op1 = gimple_assign_rhs1 (assign);
+	      tristate t = checker_state (n)->eval_condition (ctxt, n,
+							      op1, NE_EXPR, integer_zero_node);
+	      if (t.is_known ())
+		checker_state (n)->add_constraint (ctxt, n, lhs, t.is_true ());
+	    }
+	  break;
+	case GIMPLE_BINARY_RHS:
+	  {
+	    /* Handle "a = b OP c" where b OP C is known.  */
+	    tree op1 = gimple_assign_rhs1 (assign);
+	    tree op2 = gimple_assign_rhs2 (assign);
+	    tristate t = checker_state (n)->eval_condition (ctxt,
+							    n, op1, subcode, op2);
+	    if (t.is_known ())
+	      checker_state (n)->add_constraint (ctxt, n, lhs, t.is_true ());
+	  }
+	  break;
+
+	case GIMPLE_TERNARY_RHS:
+	  break;
+
+	default:
+	case GIMPLE_INVALID_RHS:
+	  gcc_unreachable ();
+	  break;
+	}
+    }
+}
+
+void
+ana::constraints_checker::before_call (node *n, gcall *call, context *ctxt)
+{
+  // TODO
+}
+
+void
+ana::constraints_checker::after_call (node *n, gcall *call, context *ctxt)
+{
+  // TODO
+}
+
+void
+ana::constraints_checker::dead_value (node *n, context *ctxt)
+{
+  // TODO
+}
+
+ana::tristate
+ana::constraints_checker::eval_condition (node *n, absvalue *lhs, enum tree_code op, absvalue *rhs)
+{
+  return checker_state (n)->eval_condition (lhs, op, rhs);
+}
+
+bool
+ana::constraints_checker::add_constraint (state *s,
+					  absvalue *lhs, enum tree_code op, absvalue *rhs)
+{
+  return checker_state (s)->add_constraint (lhs, op, rhs);
+}
diff --git a/gcc/analyzer/dead-code/core-checker.cc b/gcc/analyzer/dead-code/core-checker.cc
new file mode 100644
index 0000000..47c98ff
--- /dev/null
+++ b/gcc/analyzer/dead-code/core-checker.cc
@@ -0,0 +1,904 @@
+/* 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 "print-tree.h"
+#include "graphviz.h"
+
+namespace ana
+{
+
+/* Fundamental memory-modeling.  */
+
+class absvalue;
+class region;
+  class field_region;
+class core_state;
+
+enum memspace_kind {
+   MEMSPACE_KIND_STACK,
+   MEMSPACE_KIND_STATIC,
+   MEMSPACE_KIND_HEAP,
+   NUM_MEMSPACE_KINDS
+};
+
+static const char * const memspace_kind_str[NUM_MEMSPACE_KINDS] = {
+  "STACK",
+  "STATIC",
+  "HEAP"
+};
+
+class region : public absvalue
+{
+ public:
+  region *get_parent () const { return m_parent; }
+
+  region *dyn_cast_region () { return this; }
+
+  virtual enum memspace_kind get_memspace () const;
+
+  field_region *get_field (tree t_field, core_state *state);
+
+  virtual void write_label_for_parent_edge (pretty_printer *pp) {}
+
+ protected:
+  region (region *parent) : m_parent (parent), m_fields () {}
+
+ private:
+  region *m_parent;
+  hash_map <tree, field_region *> m_fields;
+};
+
+class memspace_region : public region
+{
+ public:
+  memspace_region (enum memspace_kind kind)
+    : region (NULL), m_kind (kind) {}
+
+  void dump_to_dot_record (pretty_printer *pp) const
+  {
+    pp_printf (pp, memspace_kind_str[m_kind]);
+  }
+
+  enum memspace_kind get_memspace () const { return m_kind; }
+
+ private:
+  enum memspace_kind m_kind;
+};
+
+class var_region : public region
+{
+ public:
+  var_region (region *parent, tree var_decl)
+    : region (parent), m_var_decl (var_decl) {}
+
+  void dump_to_dot_record (pretty_printer *pp) const
+  {
+    pp_printf (pp, "var_region: ");
+  }
+
+ private:
+  tree m_var_decl;
+};
+
+class field_region : public region
+{
+ public:
+  field_region (region *parent, tree field) : region (parent), m_field (field) {}
+
+  void dump_to_dot_record (pretty_printer *pp) const
+  {
+    pp_printf (pp, "field_region: ");
+  }
+
+  void write_label_for_parent_edge (pretty_printer *pp)
+  {
+    pp_printf (pp, ".");
+    dump_generic_node (pp, m_field, 0, (dump_flags_t)0, false);
+  }
+
+ private:
+  tree m_field;
+};
+
+class symbolic_region : public region
+{
+ public:
+  symbolic_region (region *parent)
+    : region (parent) {}
+
+  void dump_to_dot_record (pretty_printer *pp) const
+  {
+    pp_printf (pp, "symbolic_region: ");
+  }
+
+ private:
+};
+
+class core_state : public custom_state {
+ public:
+  custom_state *clone () const
+  {
+    return new core_state (*this);
+  }
+
+  void dump_to_dot_record (pretty_printer *pp) const;
+  void add_extra_nodes_and_edges (const node *n, graphviz_out *gv) const;
+
+  core_state ();
+  core_state (const core_state &other);
+
+  void on_assign (gassign *assign, context *ctxt, node *n);
+
+  region *eval_lvalue (tree expr);
+  region *eval_rvalue_loc (tree expr);
+  absvalue *eval_rvalue (tree expr);
+  void set_lvalue (region *lhs, absvalue *rhs);
+
+  region *get_stack () { return get_memspace_region (MEMSPACE_KIND_STACK); }
+
+  region *get_memspace_region (enum memspace_kind);
+
+  void add_region (region *r);
+
+ private:
+  region *m_memspace_regions[NUM_MEMSPACE_KINDS];
+  vec <region *> m_regions;
+
+  /* Mapping from variables to storage locations */
+  hash_map <tree, region *> m_env;
+  typedef hash_map <tree, region *>::iterator env_iterator;
+
+  /* Mapping from storage locations to values. */
+  hash_map <region *, absvalue *> m_store;
+  typedef hash_map <region *, absvalue *>::iterator store_iterator;
+};
+
+/* The checker itself.  */
+
+class core_checker : public stateful_checker <core_state>, public memory_model
+{
+ public:
+  void on_node (node *n, context *ctxt);
+  void before_call (node *n, gcall *call, context *ctxt);
+  void after_call (node *n, gcall *call, context *ctxt);
+  void dead_value (node */*n*/, context *ctxt) {}
+
+  /* Implementation of memory_model.  */
+  absvalue *eval_rvalue (node *n, tree expr);
+  absvalue *eval_rvalue (state *s, tree expr);
+};
+
+} // namespace ana
+
+ana::checker *ana::make_core_checker (analyzer *a)
+{
+  core_checker *ch = new core_checker ();
+  a->set_memory_model (ch);
+  return ch;
+}
+
+enum ana::memspace_kind
+ana::region::get_memspace () const
+{
+  gcc_assert (m_parent);
+  return m_parent->get_memspace ();
+}
+
+ana::field_region *
+ana::region::get_field (tree t_field, core_state *state)
+{
+  gcc_assert (t_field);
+  gcc_assert (state);
+
+  field_region **pfield = m_fields.get (t_field);
+  if (pfield)
+    return *pfield;
+  else
+    {
+      field_region *r = new field_region (this, t_field);
+      m_fields.put (t_field, r);
+      state->add_region (r);
+      return r;
+    }
+}
+
+/* class core_state : public per_checker_state */
+
+ana::core_state::core_state () : m_regions (), m_env (), m_store ()
+{
+  for (int i = 0; i < NUM_MEMSPACE_KINDS; i++)
+    m_memspace_regions[i] = NULL;
+}
+
+ana::core_state::core_state (const core_state &other)
+: m_regions (), m_env (), m_store ()
+{
+  int i;
+  region *r;
+
+  for (i = 0; i < NUM_MEMSPACE_KINDS; i++)
+    {
+      m_memspace_regions[i] = other.m_memspace_regions[i];
+    }
+
+  FOR_EACH_VEC_ELT (other.m_regions, i, r)
+    m_regions.safe_push (r);
+
+  /* Ideally we need a deep copy method for hash_map.  */
+  for (env_iterator iter = other.m_env.begin ();
+       iter != other.m_env.end ();
+       ++iter)
+    {
+      tree expr = (*iter).first;
+      region *r = (*iter).second;
+      m_env.get_or_insert (expr) = r;
+    }
+
+  for (store_iterator iter = other.m_store.begin ();
+       iter != other.m_store.end ();
+       ++iter)
+    {
+      region *r = (*iter).first;
+      absvalue *v = (*iter).second;
+      m_store.get_or_insert (r) = v;
+    }
+}
+
+void
+ana::core_state::dump_to_dot_record (pretty_printer *pp) const
+{
+  for (env_iterator iter = m_env.begin ();
+       iter != m_env.end ();
+       ++iter)
+    {
+      tree expr = (*iter).first;
+      pp_printf (pp, "expr: ");
+      dump_generic_node (pp, expr, 0, (dump_flags_t)0, false);
+
+      region *r = (*iter).second;
+      pp_printf (pp, " region: %p", r);
+      //r->dump_to_dot_record (pp);
+
+      absvalue **pvalue = const_cast <ana::core_state *> (this)->m_store.get (r);
+      pp_printf (pp, " value: ");
+      if (pvalue)
+	{
+	  absvalue *value = *pvalue;
+	  pp_printf (pp, " %p", value);
+	  //value->dump_to_dot_record (pp);
+	}
+      else
+	{
+	  pp_printf (pp, "(NULL)");
+	}
+      pp_newline (pp);
+      pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+    }
+}
+
+void
+ana::core_state::add_extra_nodes_and_edges (const node *n, graphviz_out *gv) const
+{
+  /* Begin a subgraph.  */
+  gv->println ("subgraph \"cluster_core_state_%p\" {", (void *)this);
+  gv->indent ();
+  gv->println ("style=\"dashed\";");
+  gv->println ("color=\"green\";");
+  gv->println ("label=\"core_state\";");
+  gv->println ("clusterrank=\"local\";");
+
+  pretty_printer *pp = gv->m_pp;
+
+    for (env_iterator iter = m_env.begin ();
+	 iter != m_env.end ();
+	 ++iter)
+      {
+	tree expr = (*iter).first;
+	region *r = (*iter).second;
+
+	/* Create node for "expr".  */
+	gv->write_indent ();
+	pp_printf (pp,
+		   "core_state_%p_expr_%p "
+		   "[shape=%s, style=\"filled\" fillcolor=\"yellow\" label=\"",
+		   (const void *)this, (void *)expr, "record");
+	pp_left_brace (pp);
+	pp_write_text_to_stream (pp);
+	dump_generic_node (pp, TREE_TYPE (expr), 0, (dump_flags_t)0, false);
+	pp_string (pp, " ");
+	dump_generic_node (pp, expr, 0, (dump_flags_t)0, false);
+	pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+	pp_right_brace (pp);
+	pp_string (pp, "\"];");
+	pp_flush (pp);
+	pp_newline (pp);
+      }
+
+    int i;
+    region *r;
+    FOR_EACH_VEC_ELT (m_regions, i, r)
+      {
+	pp_printf (pp,
+		   "core_state_%p_region_%p "
+		   "[shape=%s, style=\"filled\" fillcolor=\"cyan\" label=\"",
+		   (const void *)this, (void *)r, "record");
+	pp_left_brace (pp);
+	pp_write_text_to_stream (pp);
+	r->dump_to_dot_record (pp);
+	absvalue **pvalue = const_cast <ana::core_state *> (this)->m_store.get (r);
+	pp_printf (pp, " value: ");
+	if (pvalue)
+	  {
+	    absvalue *value = *pvalue;
+	    pp_printf (pp, " %p", value);
+	    if (value)
+	      value->dump_to_dot_record (pp);
+	  }
+	else
+	  {
+	    pp_printf (pp, "(NULL)");
+	  }
+	pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+
+	pp_right_brace (pp);
+	pp_string (pp, "\"];");
+	pp_flush (pp);
+	pp_newline (pp);
+
+	/* Draw edge from child to parent.  */
+	if (r->get_parent ())
+	  {
+	    gv->write_indent ();
+	    pp_printf (pp,
+		       "core_state_%p_region_%p:s -> core_state_%p_region_%p:n [label=\"",
+		       (const void *)this, (void *)r,
+		       (const void *)this, (void *)r->get_parent ());
+	    pp_write_text_to_stream (pp);
+	    r->write_label_for_parent_edge (pp);
+	    pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+	    pp_string (pp, "\"];");
+	    pp_flush (pp);
+	    pp_newline (pp);
+	  }
+
+	/* For pointers, draw edge to the region it points to.  */
+	if (pvalue)
+	  {
+	    if (*pvalue)
+	      {
+		region *pointee = (*pvalue)->dyn_cast_region ();
+		if (pointee)
+		  {
+		    gv->write_indent ();
+		    pp_printf (pp,
+			       "core_state_%p_region_%p:e -> core_state_%p_region_%p:w [style=dotted];\n",
+			       (const void *)this, (void *)r,
+			       (const void *)this, (void *)pointee);
+		  }
+	      }
+	  }
+      }
+
+  for (env_iterator iter = m_env.begin ();
+       iter != m_env.end ();
+       ++iter)
+    {
+      tree expr = (*iter).first;
+      region *r = (*iter).second;
+
+      /* Create edge from expr's node to that for its region (in
+	 the store subgraph).  */
+      gv->write_indent ();
+      pp_printf (pp,
+		 "core_state_%p_expr_%p -> core_state_%p_region_%p;\n",
+		 (const void *)this, (void *)expr,
+		 (const void *)this, (void *)r);
+    }
+
+  /* Terminate outermost subgraph.  */
+  gv->outdent ();
+  gv->println ("}");
+}
+
+void
+ana::core_state::on_assign (gassign *assign, context *ctxt, node *n)
+{
+  //printf ("on_assign\n");
+  tree lhs = gimple_assign_lhs (assign);
+  //debug_tree (lhs);
+  enum tree_code subcode = gimple_assign_rhs_code (assign);
+  switch (get_gimple_rhs_class (subcode))
+    {
+    case GIMPLE_SINGLE_RHS:
+      {
+	tree rhs = gimple_assign_rhs1 (assign);
+	//debug_tree (rhs);
+	switch (TREE_CODE (rhs))
+	  {
+	  case ADDR_EXPR:
+	    {
+	      printf ("got ADDR_EXPR\n");
+	      tree ref = TREE_OPERAND (rhs, 0);
+	      absvalue *a = eval_lvalue (ref);
+	      set_lvalue (eval_lvalue (lhs), a);
+	    }
+	    break;
+
+	  case INTEGER_CST:
+	    {
+	      absvalue *a = new int_constant (rhs);
+	      set_lvalue (eval_lvalue (lhs), a);
+	    }
+	    break;
+
+	  case CONSTRUCTOR:
+	    {
+	      // TODO
+	    }
+	    break;
+
+	  case COMPONENT_REF:
+	    {
+	      // TODO
+	      absvalue *a = eval_rvalue (rhs);
+	      set_lvalue (eval_lvalue (lhs), a);
+	    }
+	    break;
+
+	  case SSA_NAME:
+	    {
+	      absvalue *a = eval_rvalue (rhs);
+	      set_lvalue (eval_lvalue (lhs), a);
+	    }
+	    break;
+
+	  default:	    
+	    gcc_unreachable (); // TODO
+	    break;
+	  }
+      }
+      break;
+
+    case GIMPLE_UNARY_RHS:
+      {
+	tree rhs = gimple_assign_rhs1 (assign);
+	switch (subcode)
+	  {
+	  CASE_CONVERT:
+	    {
+	      /* FIXME: for now we don't properly handle conversions.  */
+	      set_lvalue (eval_lvalue (lhs), eval_rvalue (rhs));	      
+	    }
+	    break;
+
+	  default:
+	    gcc_unreachable (); // TODO
+	    break;
+	  }
+      }
+      break;
+
+    case GIMPLE_BINARY_RHS:
+      {
+	/* Handle conditionals.  */
+	// TODO: what about other kinds of binary op?
+	tree op1 = gimple_assign_rhs1 (assign);
+	tree op2 = gimple_assign_rhs2 (assign);
+	tristate t = ctxt->eval_condition (n, op1, subcode, op2);
+	absvalue *v_result;
+	if (t.is_known ())
+	  v_result = new int_constant (t.is_true ()
+				       ? integer_one_node : integer_zero_node);
+	else
+	  v_result = new unknown ();
+	set_lvalue(eval_lvalue(lhs), v_result);
+      }
+      break;
+
+    case GIMPLE_TERNARY_RHS:
+      gcc_unreachable (); // TODO
+      break;
+
+    default:
+    case GIMPLE_INVALID_RHS:
+      gcc_unreachable ();
+      break;
+    }
+}
+
+ana::region *
+ana::core_state::eval_lvalue (tree expr)
+{
+  switch (TREE_CODE (expr))
+    {
+    case PARM_DECL:
+    case VAR_DECL:
+      {
+	region **p_region = m_env.get (expr);
+	if (!p_region)
+	  {
+	    /* FIXME: is it really on the stack?  */
+	    region *r = new var_region (get_stack (), expr);
+	    m_regions.safe_push (r);
+	    m_env.put (expr, r);
+	    return r;
+	  }
+	return *p_region;
+      }
+
+    case SSA_NAME:
+      {
+	tree var = SSA_NAME_VAR (expr);
+	if (var)
+	  return eval_lvalue (var);
+	else
+	  {
+	    /* "expr" is a temporary.  */
+	    region **p_region = m_env.get (expr);
+	    if (!p_region)
+	      {
+		/* FIXME: is it really on the stack?  */
+		region *r = new var_region (get_stack (), expr);
+		m_regions.safe_push (r);
+		m_env.put (expr, r);
+		return r;
+	      }
+	    return *p_region;
+	  }
+      }
+
+    case COMPONENT_REF:
+      {
+	tree t_ref = TREE_OPERAND (expr, 0);
+	tree t_field = TREE_OPERAND (expr, 1);
+	region *r = eval_lvalue (t_ref);
+	if (r)
+	  return r->get_field (t_field, this);
+	else
+	  {
+	    gcc_unreachable ();
+	    return NULL; // FIXME
+	  }
+      }
+
+    case MEM_REF:
+      {
+	tree t_ptr = TREE_OPERAND (expr, 0);
+	tree t_offset = TREE_OPERAND (expr, 1);
+	gcc_assert (TREE_CODE (t_offset) == INTEGER_CST);
+	//gcc_assert (t_offset == zero);
+
+	region *r_ptr = eval_rvalue_loc (t_ptr);
+	if (r_ptr)
+	  return r_ptr;
+	else
+	  {
+	    region *r_star_ptr = new symbolic_region (NULL);
+	    m_regions.safe_push (r_star_ptr);
+	    set_lvalue (eval_lvalue (t_ptr), r_star_ptr);
+	    return r_star_ptr;
+          }
+      }
+
+    default:
+      gcc_unreachable ();
+      break;
+    }
+}
+
+ana::region *
+ana::core_state::eval_rvalue_loc (tree expr)
+{
+  switch (TREE_CODE (expr))
+    {
+    case SSA_NAME:
+      {
+	tree var = SSA_NAME_VAR (expr);
+	if (!var)
+	  /* Temporary.  */
+	  var = expr;
+	region *r = eval_lvalue (var);
+	absvalue **p_v = m_store.get (r);
+	if (p_v)
+	  {
+	    if (*p_v)
+	      {
+		region *pointee = (*p_v)->dyn_cast_region ();
+		if (pointee)
+		  return pointee;
+		else
+		  /* FIXME: what to do here?  */
+		  gcc_unreachable ();
+	      }
+	    else
+	      return NULL;
+	  }
+	else
+	  {
+	    /* Unknown value.  */
+	    return NULL;
+	  }
+      }
+      break;
+
+    default:
+      gcc_unreachable ();
+      break;
+    }
+}
+
+/* Attempt to eval the rvalue; return non-NULL.  */
+
+ana::absvalue *
+ana::core_state::eval_rvalue (tree expr)
+{
+  switch (TREE_CODE (expr))
+    {
+    case COMPONENT_REF:
+      {
+	region *r = eval_lvalue (expr);
+	absvalue **p_val = m_store.get (r);
+	if (p_val)
+	  return *p_val;
+	else
+	  {
+	    gcc_unreachable ();
+	    return NULL; /* FIXME */
+	  }
+      }
+      break;
+
+    case SSA_NAME:
+      {
+	region *r = eval_lvalue (expr);
+	absvalue **p_val = m_store.get (r);
+	if (p_val)
+	  return *p_val;
+	else
+	  {
+	    absvalue *v = new unknown ();
+	    m_store.put (r, v);
+	    return v;
+	  }
+      }
+      break;
+
+    case INTEGER_CST:
+      {
+	/* FIXME: cache/consolidate these?  */
+	return new int_constant (expr);
+      }
+
+    default:
+      gcc_unreachable ();
+      break;
+    }
+}
+
+void
+ana::core_state::set_lvalue (region *lhs, absvalue *rhs)
+{
+  m_store.get_or_insert (lhs) = rhs;
+}
+
+ana::region *
+ana::core_state::get_memspace_region (enum memspace_kind kind)
+{
+  /* Lazily create the base memspace regions.  */
+  if (!m_memspace_regions[kind])
+    {
+      m_memspace_regions[kind] = new memspace_region (kind);
+      m_regions.safe_push (m_memspace_regions[kind]);
+    }
+  return m_memspace_regions[kind];
+}
+
+void
+ana::core_state::add_region (region *r)
+{
+  m_regions.safe_push (r);
+}
+
+/* class core_checker : public checker */
+
+void
+ana::core_checker::on_node (node *n, context *ctxt)
+{
+  // TODO
+  gimple *stmt = n->get_location ()->get_stmt ();
+  if (!stmt)
+    return;
+
+  gassign *assign = dyn_cast <gassign *> (stmt);
+  if (assign)
+    {
+      core_state *state = checker_state (n);
+      state->on_assign (assign, ctxt, n);
+    }
+
+  greturn *return_stmt = dyn_cast <greturn *> (stmt);
+  if (return_stmt)
+    {
+      /* Complain about return of pointer to on-stack variable.  */
+      tree t_retval = gimple_return_retval (return_stmt);
+      if (t_retval)
+	{
+	  core_state *state = checker_state (n);
+	  absvalue *retval = state->eval_rvalue (t_retval);
+	  if (retval)
+	    {
+	      /* TODO: only complain if it's of pointer type.  */
+	      region *pointee = retval->dyn_cast_region ();
+	      if (pointee)
+		if (MEMSPACE_KIND_STACK == pointee->get_memspace ())
+		  {
+		      bug *b = new bug ("return of pointer to stack", n); // TODO: identify it
+		      // TODO: follow the symbols of interest
+		      emit_bug (b, ctxt);
+		  }
+	    }
+	}
+    }
+}
+
+void
+ana::core_checker::before_call (node *n, gcall *call, context *ctxt)
+{
+  /* Handle the builtin "__analyzer_dump" by dumping state to stderr.  */
+  if (is_named_call_p (call, "__analyzer_dump", 0))
+    {
+      n->dump (stdout);
+    }
+
+  /* Handle the builtin "__analyzer_eval" by evaluating the input
+     and dumping as a "warning", so that test cases can use dg-warning
+     to validate the result.  */
+  if (is_named_call_p (call, "__analyzer_eval", 1))
+    {
+      tree t_arg = gimple_call_arg (call, 0);
+      tristate t = ctxt->eval_condition (n,
+					 t_arg, NE_EXPR, integer_zero_node);
+      warning_at (n->get_source_location (), 0, t.as_string ());
+    }
+}
+
+void
+ana::core_checker::after_call (node */*n*/, gcall */*call*/, context *ctxt)
+{
+  // TODO
+}
+
+ana::absvalue *
+ana::core_checker::eval_rvalue (node *n, tree expr)
+{
+  core_state *state = checker_state (n);
+  return state->eval_rvalue (expr);
+}
+
+ana::absvalue *
+ana::core_checker::eval_rvalue (state *s, tree expr)
+{
+  core_state *state = checker_state (s);
+  return state->eval_rvalue (expr);
+}
+
+/************************************************************************/
+
+/* Additional checkers.  */
+
+namespace ana {
+class core_divide_by_zero_checker : public stateless_checker
+{
+ public:
+  void on_node (node *n, context *ctxt);
+  void before_call (node */*n*/, gcall */*call*/, context *ctxt) {}
+  void after_call (node */*n*/, gcall */*call*/, context *ctxt) {}
+  void dead_value (node */*n*/, context *ctxt) {}
+};
+
+class core_function_call_checker : public stateless_checker
+{
+ public:
+  void on_node (node */*n*/, context *ctxt) {}
+  void before_call (node *n, gcall *call, context *ctxt);
+  void after_call (node *n, gcall *call, context *ctxt);
+  void dead_value (node */*n*/, context *ctxt) {}
+};
+
+} // namespace ana
+
+/* class core_divide_by_zero_checker : public stateless_checker */
+void
+ana::core_divide_by_zero_checker::on_node (node *n, context *ctxt)
+{
+  // TODO
+  gimple *stmt = n->get_location ()->get_stmt ();
+  if (!stmt)
+    return;
+
+  gassign *assign = dyn_cast <gassign *> (stmt);
+  if (assign)
+    {
+      tree lhs = gimple_assign_lhs (assign);
+      //debug_tree (lhs);
+      enum tree_code subcode = gimple_assign_rhs_code (assign);
+      switch (subcode)
+	{
+	case TRUNC_DIV_EXPR:
+	case CEIL_DIV_EXPR:
+	case FLOOR_DIV_EXPR:
+	case ROUND_DIV_EXPR:
+	case TRUNC_MOD_EXPR:
+	case CEIL_MOD_EXPR:
+	case FLOOR_MOD_EXPR:
+	case ROUND_MOD_EXPR:
+	case RDIV_EXPR:
+	case EXACT_DIV_EXPR:
+	case FIX_TRUNC_EXPR:
+	  {
+	    tree op1 = gimple_assign_rhs1 (assign);
+	    tree op2 = gimple_assign_rhs2 (assign);
+	  }
+	  break;
+
+	default:
+	  break;
+	}
+    }
+}
+
+/* class core_function_call_checker : public stateless_checker */
+void
+ana::core_function_call_checker::before_call (node *n, gcall *call, context *ctxt)
+{
+  /* TODO:
+     warn about:
+     - uninitialized args
+     - passing NULL as a param that requires a non-NULL arg
+     - calling a function ptr that may be NULL
+     - etc.  */
+}
+
+void
+ana::core_function_call_checker::after_call (node *n, gcall *call, context *ctxt)
+{
+  // TODO
+}
+
+ana::checker *
+ana::make_core_divide_by_zero_checker ()
+{
+  return new core_divide_by_zero_checker ();
+}
+
+ana::checker *
+ana::make_core_function_call_checker ()
+{
+  return new core_function_call_checker ();
+}
diff --git a/gcc/analyzer/dead-code/fd_checker.sm b/gcc/analyzer/dead-code/fd_checker.sm
new file mode 100644
index 0000000..cef881e
--- /dev/null
+++ b/gcc/analyzer/dead-code/fd_checker.sm
@@ -0,0 +1,26 @@
+/*
+    Work-in-progress checker for file-descriptor APIs
+ */
+        
+sm fd_checker {
+   state decl int fd;
+   decl any_expr buf, count;
+   
+   /* open() and creat return a new fd, or -1, setting errno */
+   fd.all:
+       { fd = open() } -> fd.unknown
+     | { fd = create() } -> fd.unknown
+     ;
+
+   fd.unknown:
+       { fd == -1 } -> true=fd.error, false=fd.open
+     | { fd != -1 } -> true=fd.open, false=fd.error
+     ;
+
+   fd.open:
+       { close(fd) } -> fd.maybeclosed;
+
+   fd.maybeclosed:
+       { close(fd) } -> {error('double close of %s' % fd)}
+
+}
diff --git a/gcc/analyzer/dead-code/fileptr.sm b/gcc/analyzer/dead-code/fileptr.sm
new file mode 100644
index 0000000..ae1a9e1
--- /dev/null
+++ b/gcc/analyzer/dead-code/fileptr.sm
@@ -0,0 +1,17 @@
+sm fileptr_checker {
+   state decl FILE* fp;
+
+   decl any_expr path, mode;
+   decl any_expr fd;
+   decl any_expr stream;
+   
+   fp.all:
+       { fp = fopen(path, mode) } -> fp.unchecked
+     | { fp = fdopen(fd, mode) } -> fp.unchecked
+     | { fp = freopen(path, mode, stream) } -> fp.unchecked
+     ;
+
+   fp.unchecked:
+       { fclose(fp) } -> { error('use of possibly NULL %s' % fp) };
+
+}
diff --git a/gcc/analyzer/dead-code/libc-file-checker.cc b/gcc/analyzer/dead-code/libc-file-checker.cc
new file mode 100644
index 0000000..7854203
--- /dev/null
+++ b/gcc/analyzer/dead-code/libc-file-checker.cc
@@ -0,0 +1,226 @@
+/* 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"
+
+namespace ana
+{
+
+class libc_file_checker;
+
+/* Checker for libc FILE * operations.  */
+
+/* State for the checker.  */
+enum file_state {FILE_MAYBE_OPEN, FILE_OPEN, FILE_CLOSED};
+
+class file_states : public custom_state {
+ public:
+  custom_state *clone () const
+  {
+    return new file_states (*this);
+  }
+
+  void dump_to_dot_record (pretty_printer *pp) const;
+
+  file_states () : m_states () {}
+  file_states (const file_states &other);
+
+  void set_tree_state (tree t, enum file_state value);
+  bool get_tree_state (tree t, enum file_state &out);
+
+ private:
+  /* TODO: this should be a map from symbolic values to states,
+     rather than from trees. */
+  hash_map <tree, enum file_state> m_states;
+  typedef hash_map <tree, enum file_state>::iterator state_iterator;
+  friend class libc_file_checker;
+};
+
+/* The checker itself.  */
+
+class libc_file_checker : public stateful_checker <file_states>
+{
+ public:
+  void on_node (node *n, context *ctxt);
+  void before_call (node *n, gcall *call, context *ctxt);
+  void after_call (node *n, gcall *call, context *ctxt);
+  void dead_value (node *n, context *ctxt);
+
+ private:
+  void report_double_close (node *n, tree fp, context *ctxt);
+  void report_leaked_file (node *n, tree fp, context *ctxt);
+
+};
+
+} // namespace ana
+
+ana::checker *ana::make_libc_file_checker ()
+{
+  return new libc_file_checker ();
+}
+
+/* class file_states : public per_checker_state */
+
+static const char * const state_names[] = {
+  "FILE_MAYBE_OPEN",
+  "FILE_OPEN",
+  "FILE_CLOSED"
+};
+
+ana::file_states::file_states (const file_states &other) : m_states ()
+{
+  /* We can't simple copy m_states, or the states will become linked;
+     ideally we need a deep copy method for hash_map.  */
+  for (state_iterator iter = other.m_states.begin ();
+       iter != other.m_states.end ();
+       ++iter)
+    {
+      tree t = (*iter).first;
+      enum file_state value = (*iter).second;
+      m_states.get_or_insert (t) = value;
+    }
+}
+
+void
+ana::file_states::dump_to_dot_record (pretty_printer *pp) const
+{
+  for (state_iterator iter = m_states.begin ();
+       iter != m_states.end ();
+       ++iter)
+    {
+      tree t = (*iter).first;
+      enum file_state value = (*iter).second;
+      pp_printf (pp, "file_state: ");
+      dump_generic_node (pp, t, 0, (dump_flags_t)0, false);
+      pp_printf (pp, ": %s", state_names[value]);
+      pp_newline (pp);
+      pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+    }
+}
+
+void
+ana::file_states::set_tree_state (tree t, enum file_state value)
+{
+  if (TREE_CODE (t) != SSA_NAME)
+    return; // for now
+  m_states.get_or_insert (t) = value;
+}
+
+bool
+ana::file_states::get_tree_state (tree t, enum file_state &out)
+{
+  if (TREE_CODE (t) != SSA_NAME)
+    return false; // for now
+  enum file_state *state = m_states.get (t);
+  if (state)
+    {
+      out = *state;
+      return true;
+    }
+  else
+    return false;
+}
+
+/* class libc_file_checker : public checker */
+
+void
+ana::libc_file_checker::on_node (node */*n*/, context *ctxt)
+{
+  // TODO
+}
+
+void
+ana::libc_file_checker::before_call (node *n, gcall *call, context *ctxt)
+{
+  // TODO
+  if (is_named_call_p (call, "fclose", 1))
+    {
+      //printf ("before_call to: fclose\n");
+      tree fp = gimple_call_arg (call, 0);
+      if (fp)
+	{
+	  enum file_state val;
+	  if (checker_state (n)->get_tree_state (fp, val))
+	    if (val == FILE_CLOSED)
+	      report_double_close (n, fp, ctxt);
+	}
+    }
+}
+
+void
+ana::libc_file_checker::after_call (node *n, gcall *call, context *ctxt)
+{
+  // TODO
+  if (is_named_call_p (call, "fopen", 2))
+    {
+      //printf ("after_call to: fopen\n");
+      tree lhs = gimple_call_lhs (call);
+      if (lhs)
+	checker_state (n)->set_tree_state (lhs, FILE_MAYBE_OPEN);
+    }
+
+  if (is_named_call_p (call, "fclose", 1))
+    {
+      //printf ("after_call to: fclose\n");
+      tree fp = gimple_call_arg (call, 0);
+      if (fp)
+	checker_state (n)->set_tree_state (fp, FILE_CLOSED);
+    }
+}
+
+void
+ana::libc_file_checker::dead_value (node *n, context *ctxt)
+{
+  file_states *states = checker_state (n);
+  for (file_states::state_iterator iter = states->m_states.begin ();
+       iter != states->m_states.end ();
+       ++iter)
+    {
+      tree t = (*iter).first;
+      enum file_state value = (*iter).second;
+
+      if (value == FILE_MAYBE_OPEN)
+	report_leaked_file (n, t, ctxt);
+    }
+}
+
+void
+ana::libc_file_checker::report_double_close (node *n, tree /*fp*/, context *ctxt)
+{
+  bug *b = new bug ("double-close of FILE *", n);
+  // TODO: follow the symbol of interest (fp)
+  emit_bug (b, ctxt);
+}
+
+void
+ana::libc_file_checker::report_leaked_file (node *n, tree /*fp*/, context *ctxt)
+{
+  bug *b = new bug ("leak of FILE *", n); // TODO: name of fp?
+  // TODO: follow the symbol of interest (fp)
+  emit_bug (b, ctxt);
+}
diff --git a/gcc/analyzer/dead-code/libc-malloc-checker.cc b/gcc/analyzer/dead-code/libc-malloc-checker.cc
new file mode 100644
index 0000000..3565c85
--- /dev/null
+++ b/gcc/analyzer/dead-code/libc-malloc-checker.cc
@@ -0,0 +1,220 @@
+/* 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"
+
+namespace ana
+{
+
+/* Checker for libc malloc/free/realloc.  */
+
+/* State for the checker.  */
+enum ptr_state {PTR_MALLOCED, /* or NULL */
+		PTR_FREED};
+
+class ptr_states : public custom_state {
+ public:
+  custom_state *clone () const
+  {
+    return new ptr_states (*this);
+  }
+
+  void dump_to_dot_record (pretty_printer *pp) const;
+
+  ptr_states () : m_states () {}
+  ptr_states (const ptr_states &other);
+
+  void set_tree_state (tree t, enum ptr_state value);
+  bool get_tree_state (tree t, enum ptr_state &out);
+
+ private:
+  /* TODO: this should be a map from symbolic values to states,
+     rather than from trees. */
+  hash_map <tree, enum ptr_state> m_states;
+  typedef hash_map <tree, enum ptr_state>::iterator state_iterator;
+  friend class libc_malloc_checker;
+};
+
+class libc_malloc_checker : public stateful_checker <ptr_states>
+{
+ public:
+  void on_node (node *n, context *ctxt);
+  void before_call (node *n, gcall *call, context *ctxt);
+  void after_call (node *n, gcall *call, context *ctxt);
+  void dead_value (node *n, context *ctxt);
+
+ private:
+  void report_double_free (node *n, tree ptr, context *ctxt);
+  void report_leak (node *n, tree ptr, context *ctxt);
+};
+
+} // namespace ana
+
+ana::checker *ana::make_libc_malloc_checker ()
+{
+  return new libc_malloc_checker ();
+}
+
+/* class ptr_states : public per_checker_state */
+
+static const char * const state_names[] = {
+  "PTR_MALLOCED",
+  "PTR_FREE"
+};
+
+ana::ptr_states::ptr_states (const ptr_states &other) : m_states ()
+{
+  /* We can't simple copy m_states, or the states will become linked;
+     ideally we need a deep copy method for hash_map.  */
+  for (state_iterator iter = other.m_states.begin ();
+       iter != other.m_states.end ();
+       ++iter)
+    {
+      tree t = (*iter).first;
+      enum ptr_state value = (*iter).second;
+      m_states.get_or_insert (t) = value;
+    }
+}
+
+void
+ana::ptr_states::dump_to_dot_record (pretty_printer *pp) const
+{
+  for (state_iterator iter = m_states.begin ();
+       iter != m_states.end ();
+       ++iter)
+    {
+      tree t = (*iter).first;
+      enum ptr_state value = (*iter).second;
+      pp_printf (pp, "ptr_state: ");
+      dump_generic_node (pp, t, 0, (dump_flags_t)0, false);
+      pp_printf (pp, ": %s", state_names[value]);
+      pp_newline (pp);
+      pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+    }
+}
+
+void
+ana::ptr_states::set_tree_state (tree t, enum ptr_state value)
+{
+  if (TREE_CODE (t) != SSA_NAME)
+    return; // for now
+  m_states.get_or_insert (t) = value;
+}
+
+bool
+ana::ptr_states::get_tree_state (tree t, enum ptr_state &out)
+{
+  if (TREE_CODE (t) != SSA_NAME)
+    return false; // for now
+  enum ptr_state *state = m_states.get (t);
+  if (state)
+    {
+      out = *state;
+      return true;
+    }
+  else
+    return false;
+}
+
+/* class libc_malloc_checker : public checker */
+
+void
+ana::libc_malloc_checker::on_node (node * /*n*/, context *ctxt)
+{
+  // TODO
+}
+
+void
+ana::libc_malloc_checker::before_call (node *n, gcall *call, context *ctxt)
+{
+  // TODO
+  if (is_named_call_p (call, "free", 1))
+    {
+      tree ptr = gimple_call_arg (call, 0);
+      if (ptr)
+	{
+	  enum ptr_state val;
+	  if (checker_state (n)->get_tree_state (ptr, val))
+	    if (val == PTR_FREED)
+	      report_double_free (n, ptr, ctxt);
+	}
+    }
+}
+
+void
+ana::libc_malloc_checker::after_call (node *n, gcall *call, context *ctxt)
+{
+  // TODO
+  if (is_named_call_p (call, "malloc", 1))
+    {
+      tree lhs = gimple_call_lhs (call);
+      if (lhs)
+	checker_state (n)->set_tree_state (lhs, PTR_MALLOCED);
+      else
+	; /* TODO: report leak.  */
+    }
+
+  if (is_named_call_p (call, "free", 1))
+    {
+      tree ptr = gimple_call_arg (call, 0);
+      if (ptr)
+	checker_state (n)->set_tree_state (ptr, PTR_FREED);
+    }
+}
+
+void
+ana::libc_malloc_checker::dead_value (node *n, context *ctxt)
+{
+  ptr_states *states = checker_state (n);
+  for (ptr_states::state_iterator iter = states->m_states.begin ();
+       iter != states->m_states.end ();
+       ++iter)
+    {
+      tree t = (*iter).first;
+      enum ptr_state value = (*iter).second;
+
+      if (value == PTR_MALLOCED)
+	report_leak (n, t, ctxt);
+    }
+}
+
+void
+ana::libc_malloc_checker::report_double_free (node *n, tree ptr, context *ctxt)
+{
+  bug *b = new bug ("double-free", n);
+  // TODO: follow the symbol of interest (ptr)
+  emit_bug (b, ctxt);
+}
+
+void
+ana::libc_malloc_checker::report_leak (node *n, tree ptr, context *ctxt)
+{
+  bug *b = new bug ("leak", n);
+  // TODO: follow the symbol of interest (ptr)
+  emit_bug (b, ctxt);
+}
diff --git a/gcc/analyzer/dead-code/malloc_checker.sm b/gcc/analyzer/dead-code/malloc_checker.sm
new file mode 100644
index 0000000..39ab433
--- /dev/null
+++ b/gcc/analyzer/dead-code/malloc_checker.sm
@@ -0,0 +1,74 @@
+/*
+    Work-in-progress checker for malloc/free usage
+ */
+sm malloc_checker {
+  stateful decl any_pointer ptr;
+
+  ptr.*:
+    { ptr = malloc() } ->  ptr.unchecked;
+
+  ptr.*:
+    { ptr = 0 } ->  ptr.null;
+
+  ptr.start, ptr.unchecked:
+      { ptr == 0 } -> true=ptr.null, false=ptr.nonnull
+    | { ptr != 0 } -> true=ptr.nonnull, false=ptr.null
+    ;
+
+  ptr.free:
+      { ptr == 0 } -> true=ptr.null
+    | { ptr != 0 } -> false=ptr.null
+    ;
+
+  ptr.unchecked:
+    { *ptr }
+      -> {{
+            error('dereference of possibly-NULL pointer %s' % ptr,
+                  // "CWE-690: Unchecked Return Value to NULL Pointer Dereference"
+                  cwe='CWE-690')
+         }}, ptr.nonnull;
+
+  ptr.null:
+    { *ptr }
+      -> {{
+            error('dereference of NULL pointer %s' % ptr,
+                  // "CWE-690: Unchecked Return Value to NULL Pointer Dereference"
+                  cwe='CWE-690')
+         }}, ptr.stop;
+
+  ptr.start, ptr.unchecked, ptr.null, ptr.nonnull:
+    { free(ptr) } -> ptr.free;
+
+  ptr.free:
+      { free(ptr) }
+          -> {{
+                error('double-free of %s' % ptr,
+                      // "CWE-415: Double Free"
+                      cwe='CWE-415')
+             }}, ptr.stop
+    | { ptr }
+          -> {{
+                error('use-after-free of %s' % ptr,
+                      // "CWE-416: Use After Free"
+                      cwe='CWE-416')
+             }}, ptr.stop
+    ;
+
+  ptr.unchecked:
+    @arg_must_not_be_null
+      -> {{
+            error('possibly-NULL pointer %s passed as argument %i to %s'
+                  % (ptr, argnumber, function),
+                  // "CWE-690: Unchecked Return Value to NULL Pointer Dereference"
+                  cwe='CWE-690')
+          }}, ptr.nonnull;
+
+  ptr.null:
+    @arg_must_not_be_null
+      -> {{
+            error('NULL pointer %s passed as argument %i to %s'
+                  % (ptr, argnumber, function),
+                  // "CWE-690: Unchecked Return Value to NULL Pointer Dereference"
+                  cwe='CWE-690')
+         }}, ptr.stop;
+}
diff --git a/gcc/analyzer/dead-code/points_to_stack.sm b/gcc/analyzer/dead-code/points_to_stack.sm
new file mode 100644
index 0000000..63e0058
--- /dev/null
+++ b/gcc/analyzer/dead-code/points_to_stack.sm
@@ -0,0 +1,60 @@
+/*
+   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/>.
+*/
+
+/*
+   Checker for CWE-562: Return of Stack Variable Address
+
+   This will only detect simple cases: it doesn't attempt to track e.g.
+   pointers within a return struct, or within a reference graph.
+
+   Also, it potentially exhibits false positives for the case where
+   a pointer to a variable deeper in the stack is returned by a frame
+   higher up, where the usage of the area is within the lifetime of
+   the stack frame holding it.
+
+ */
+sm points_to_stack {
+   stateful decl any_pointer ptr;
+   decl any_variable var;
+
+   {{
+        def is_on_stack(var):
+           import gcc
+
+           if isinstance(var.context, gcc.FunctionDecl):
+               if not var.static:
+                  return True
+   }}
+
+   ptr.*:
+      { ptr = &var } ->
+        {{
+            if is_on_stack(var):
+                set_state('ptr.points_to_stack', var_pointed_to=var)
+        }};
+
+   ptr.points_to_stack:
+       { return ptr }
+         -> {{
+                error('return of "%s" returns address within stack of local variable "%s"'
+                      % (ptr, state.var_pointed_to),
+                      # "CWE-562: Return of Stack Variable Address":
+                      cwe='CWE-562')
+            }};
+}
\ No newline at end of file
diff --git a/gcc/analyzer/dead-code/python.sm b/gcc/analyzer/dead-code/python.sm
new file mode 100644
index 0000000..86b1b0a
--- /dev/null
+++ b/gcc/analyzer/dead-code/python.sm
@@ -0,0 +1,61 @@
+/*
+   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/>.
+*/
+
+/* TODO: autogenerate this checker from the API description? */
+/* Is this actually implementable using the sm framework? */
+
+sm python {
+  state decl expr ptr;
+  decl any_pointer other_ptr;
+  decl any_expr sz;
+
+  ptr.all:
+      { ptr = PyList_New(sz) }
+        -> {{
+                set_state('ptr.unchecked_newref',
+                          ob_type='PyList_Type')
+                # How much can we link states?
+                set_field_state('ob_refcnt',
+                                'newref')
+                # Other idea:
+                set_field_fact(ptr, 'ob_refcnt', '>=', 1)
+                # (this can be manipulated Py_INCREF and Py_DECREF sanely, I think)
+                # what about the tp_dealloc call clearing things though?
+           }}
+
+  ptr.unchecked_newref:
+      /* FIXME: we'll want to inherit the kv pairs in the new states: */
+      { ptr == 0 } -> true=ptr.null, false=ptr.nonnull_newref
+    | { ptr != 0 } -> true=ptr.nonnull_rewref, false=ptr.null
+    | { *ptr } -> {{ warning('use of possibly-NULL pointer %s' % ptr) }}, ptr.nonnull_newref
+    ;
+
+  ptr.null:
+    { *ptr } -> {{ error('use of NULL pointer %s' % ptr) }}
+
+  ptr.unchecked_newref, ptr.nonnull_newref:
+      $leaked$ -> {{ error('leak of %s' % ptr) }};
+
+  /* how to handle Py_INCREF and Py_DECREF ? 
+       Idea: track the ob_refcnt as an entirely separate checker, with
+       states on ob_refcnt fields?
+       (potentially have linkage between checkers?)
+  */
+  ptr.nonnull_newref
+}
diff --git a/gcc/analyzer/dead-code/signal_handler.sm b/gcc/analyzer/dead-code/signal_handler.sm
new file mode 100644
index 0000000..8ef96de
--- /dev/null
+++ b/gcc/analyzer/dead-code/signal_handler.sm
@@ -0,0 +1,58 @@
+/* 
+  SIG30-C: https://www.securecoding.cert.org/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers
+
+   I *think* this needs to be two-pass: find the signal handlers, then run
+   a normal state/vs handler state thing.
+
+*/
+
+sm find_signal_handlers {
+   stateful decl any_function_pointer fnptr;
+   decl any_expr signum;
+
+   {{
+        signal_handlers = set()
+   }}
+
+   fnptr.*:
+      { signal(signum, fnptr) }
+        -> {{
+              raise foo
+              # this isn't getting reached; I *think* it's because "handler" isn't in the smexprs
+              # scope for the Context
+
+              signal_handlers.add(fnptr)
+              print('signal_handlers: %r' % signal_handlers)
+           }};
+}
+
+sm async_unsafe {
+   /* (global state) */
+   stateful decl any_expr FIXME;
+
+    /* 
+      need to somehow wire in async calls to the functions in
+      signal_handlers into the supergraph
+      Or to just prime the states somehow:
+     */
+   {{
+       """
+       for fn in signal_handlers:
+           ep = add_entrypoint(fn,
+                               entrydesc='called as signal handler',
+                               exitdesc='returning from signal handler')
+           ep.set_state('within_signal_handler')
+           # instance of an EntryPoint class
+           """
+   }}
+
+/* FIXME:
+   within_signal_handler:
+      {{ fprintf(arg, ...) }}
+      -> {{
+            error('use of signal-unsafe function %s from within a signal-handler' % function,
+                  other_ids=[Id('SIG30-C',
+                                 url='https://www.securecoding.cert.org/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers')])
+         }};
+*/
+}
\ No newline at end of file
diff --git a/gcc/analyzer/dead-code/sizeof_allocation.sm b/gcc/analyzer/dead-code/sizeof_allocation.sm
new file mode 100644
index 0000000..ca63e1d
--- /dev/null
+++ b/gcc/analyzer/dead-code/sizeof_allocation.sm
@@ -0,0 +1,91 @@
+/*
+   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/>.
+
+   Checker to detect typos of the form:  malloc(sizeof(WRONG_TYPE))
+*/
+sm sizeof_allocation {
+  stateful decl any_pointer ptr;
+  decl any_pointer other_ptr;
+  decl any_expr nmemb;
+  decl any_expr sz;
+
+  {{
+    def is_known_int(var):
+        # We can only run the checker on constant integer expressions
+        import gcc
+        return isinstance(var.gccexpr, gcc.IntegerCst)
+
+    def check_size(ptr, allocated_size):
+        import gcc
+        type_pointed_to = ptr.type.dereference
+        if not isinstance(type_pointed_to, gcc.VoidType):
+            required_size = type_pointed_to.sizeof
+            if allocated_size < required_size:
+                error("allocation too small: pointer to %s (%i bytes)"
+                      " initialized with allocation of %i bytes"
+                      % (type_pointed_to, required_size, allocated_size),
+                      cwe="CWE-131") # "Incorrect Calculation of Buffer Size"
+
+        # Handle cases where the cast happens on another line:
+        set_state("ptr.sized", size=int(allocated_size))
+  }}
+
+  ptr.*:
+    /* Various allocators: */
+      /* libc: */
+      { ptr = malloc(sz) }
+    | { ptr = realloc(other_ptr, sz) }
+      /* libpython: */
+    | { ptr = PyMem_Malloc(sz) }
+    | { ptr = PyMem_Realloc(other_ptr, sz) }
+      /* glib: */
+    | { ptr = g_malloc(sz) }
+    | { ptr = g_malloc0(sz) }
+    | { ptr = g_realloc(other_ptr, sz) }
+    | { ptr = g_try_malloc(sz) }
+    | { ptr = g_try_malloc0(sz) }
+    | { ptr = g_try_realloc(other_ptr, sz) }
+      /* etc */
+      -> {{
+            if is_known_int(sz):
+                check_size(ptr, sz)
+         }};
+
+  ptr.*:
+      /* libc: */
+      { ptr = calloc(nmemb, sz) }
+      /* glib: */
+    | { ptr = g_malloc_n(nmemb, sz) }
+    | { ptr = g_malloc0_n(nmemb, sz) }
+    | { ptr = g_realloc_n(other_pointer, nmemb, sz) }
+    | { ptr = g_try_malloc_n(nmemb, sz) }
+    | { ptr = g_try_malloc0_n(nmemb, sz) }
+    | { ptr = g_try_realloc_n(other_pointer, nmemb, sz) }
+      -> {{
+            if is_known_int(nmemb):
+                if is_known_int(sz):
+                    check_size(ptr, int(nmemb) * int(sz))
+         }};
+
+  ptr.sized:
+     { other_ptr = ptr } ->
+       {{
+            check_size(other_ptr, allocated_size=state.size)
+       }};
+
+}
diff --git a/gcc/analyzer/dead-code/taint.sm b/gcc/analyzer/dead-code/taint.sm
new file mode 100644
index 0000000..7b305ab
--- /dev/null
+++ b/gcc/analyzer/dead-code/taint.sm
@@ -0,0 +1,71 @@
+sm taint {
+  stateful decl any_expr x;
+
+  decl any_expr y;
+  decl any_expr arr;
+  decl any_expr size;
+  decl any_expr nmemb;
+  decl any_expr stream;
+
+  /* Patterns that detect upper-bound and lower-bound checks: */
+  pat check_ub { x < y } | { x <= y };
+  pat check_lb { x > y } | { x >= y };
+  pat check_eq { x == y };
+  pat check_ne { x != y };
+
+  /* Sources of tainted data: */
+  x.*:
+    /* reading data from files: */
+      {fread(x, size, nmemb, stream)} -> x.tainted
+    | {read(fd, x, count)} -> x.tainted
+    ;
+
+  /*
+    When a tainted value is compared, mark it as now having either
+    an upper-bound, or a lower-bound:
+  */
+  x.tainted:
+      check_ub -> true=x.has_ub, false=x.has_lb
+    | check_lb -> true=x.has_lb, false=x.has_ub
+    ;
+
+  /*
+    If a value has only one bound, check to see if it gains the other bound:
+  */
+   x.has_lb:
+      check_ub -> true=x.ok
+    | check_lb -> false=x.ok
+    ;
+   x.has_ub:
+      check_ub -> false=x.ok
+    | check_lb -> true=x.ok
+    ;
+
+  /* If a value is successfully compared against a specific value, it's OK */
+  x.tainted, x.has_lb, x.has_ub:
+      check_eq -> true=x.ok
+    | check_ne -> false=x.ok
+    ;
+
+  /* Detect when a tainted value is used in a dangerous way: */
+  x.tainted:
+      { arr[x] } -> {{
+          error('use of tainted value %s in array lookup without bounds checking' % x,
+                cwe='CWE-129') # "Improper Validation of Array Index"
+        }};
+
+  x.has_lb:
+      { arr[x] } -> {{
+          error('use of tainted value %s in array lookup without upper-bound check' % x,
+                cwe='CWE-129') # "Improper Validation of Array Index"
+        }};
+
+  /* Unsigned types have an implicit lower bound: */
+  x.has_ub:
+      { arr[x] } -> {{
+          if not x.type.unsigned:
+             error('use of tainted value %s in array lookup without lower-bound check' % x,
+                   cwe='CWE-129') # "Improper Validation of Array Index"
+        }};
+
+}
diff --git a/gcc/analyzer/dead-code/toy-sm-impl-1.cc b/gcc/analyzer/dead-code/toy-sm-impl-1.cc
new file mode 100644
index 0000000..beb2c20
--- /dev/null
+++ b/gcc/analyzer/dead-code/toy-sm-impl-1.cc
@@ -0,0 +1,1052 @@
+/* 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 <string>
+#include "supergraph.h"
+#include "toy-sm-state-machine.h"
+
+static const bool DEBUG = false;
+//static const bool DEBUG = true;
+
+static int verbosity = 2;
+
+////////////////////////////////////////////////////////////////////////////
+
+class context;
+class sm_instance;
+
+////////////////////////////////////////////////////////////////////////////
+
+// FIXME: horrible hack, so that we can use %qE, which needs subclassing of
+// pretty_printer.  Do we need a cloning op?
+
+static char *
+global_dc_print (const char *fmt, ...)
+{
+  pretty_printer *pp = global_dc->printer;
+  pp_clear_output_area (pp);
+
+  text_info ti;
+  rich_location rich_loc (line_table, UNKNOWN_LOCATION);
+
+  va_list ap;
+
+  va_start (ap, fmt);
+
+  ti.format_spec = _(fmt);
+  ti.args_ptr = &ap;
+  ti.err_no = 0;
+  ti.x_data = NULL;
+  ti.m_richloc = &rich_loc;
+
+#if 1
+#else
+  pp_show_color (pp) = pp_show_color (global_dc->printer);
+  pp_format_decoder (pp) = pp_format_decoder (global_dc->printer);
+#endif
+  pp_format (pp, &ti);
+  pp_output_formatted_text (pp);
+
+  va_end (ap);
+
+  char *result = xstrdup (pp_formatted_text (pp));
+
+  pp_clear_output_area (pp);
+
+  return result;
+}
+
+struct var_state_1_t
+{
+  //typedef var_state_1_t value_type;
+
+  var_state_1_t (tree var, state_machine::state_t state)
+  : m_var (var), m_state (state)
+  {}
+
+  std::string to_str (const state_machine &sm) const
+  {
+    char *tmp = global_dc_print ("%qE: %qs",
+				 m_var, sm.get_state_name (m_state));
+    std::string result = tmp;
+    free (tmp);
+    return result;
+  }
+
+  tree m_var;
+  state_machine::state_t m_state;
+};
+struct summary_point
+{
+  state_machine::state_t m_gstate;
+  var_state_1_t m_varstate;
+};
+struct summary_edge
+{
+  summary_point start;
+  summary_point end;
+};
+
+template <typename Type>
+struct pod_hash_traits : typed_noop_remove<Type>
+{
+  typedef Type value_type;
+  typedef Type compare_type;
+  static inline hashval_t hash (value_type);
+  static inline bool equal (const value_type &existing,
+			    const value_type &candidate);
+  static inline void mark_deleted (Type &);
+  static inline void mark_empty (Type &);
+  static inline bool is_deleted (Type);
+  static inline bool is_empty (Type);
+};
+
+template <> struct default_hash_traits<var_state_1_t>
+: public pod_hash_traits<var_state_1_t>
+{
+};
+
+template <>
+inline hashval_t
+pod_hash_traits<var_state_1_t>::hash (value_type v)
+{
+  // FIXME:
+  return ((hashval_t)v.m_state);
+}
+
+template <>
+inline bool
+pod_hash_traits<var_state_1_t>::equal (const value_type &existing,
+				     const value_type &candidate)
+{
+  return (existing.m_var == candidate.m_var
+	  && existing.m_state == candidate.m_state);
+}
+template <>
+inline void
+pod_hash_traits<var_state_1_t>::mark_deleted (value_type &v)
+{
+  v = var_state_1_t (NULL_TREE, 0);
+}
+template <>
+inline void
+pod_hash_traits<var_state_1_t>::mark_empty (value_type &v)
+{
+  v = var_state_1_t (NULL_TREE, 0);
+}
+template <>
+inline bool
+pod_hash_traits<var_state_1_t>::is_deleted (value_type v)
+{
+  return (v.m_var == NULL_TREE && v.m_state == 0);
+}
+template <>
+inline bool
+pod_hash_traits<var_state_1_t>::is_empty (value_type v)
+{
+  return (v.m_var == NULL_TREE && v.m_state == 0);
+}
+
+/* FIXME: This changes as we do the traversal: */
+
+struct sm_instance_1
+{
+  sm_instance_1 () : m_gstate (0), m_active_vars () {}
+
+  std::string to_str (const state_machine &sm) const
+  {
+    // FIXME: obstack?
+    std::string result = "<";
+    result += sm.get_state_name (m_gstate);
+    result += ", {";
+    bool first = true;
+    for (hash_set<var_state_1_t>::iterator iter = m_active_vars.begin ();
+	 iter != m_active_vars.end ();
+	 ++iter)
+      {
+	if (first)
+	  first = false;
+	else
+	  result += ", ";
+	result += (*iter).to_str (sm);
+      }
+    result += "}>";
+    return result;
+  }
+
+  state_machine::state_t m_gstate;
+  hash_set<var_state_1_t> m_active_vars;
+};
+
+class context
+{
+public:
+  // FIXME: for now, just global state
+  // FIXME: but should be a map from ndoes to state info
+  // e.g. a bitmap of states that are known to be reachable at entry
+  // to that node (possibly tagged with a minimal path that reaches it?)
+  //state_machine::state_t m_state;
+
+  struct per_node_t
+  {
+    hash_set<summary_edge *> m_add;
+    hash_set<summary_edge *> m_transition;
+  };
+  /* FIXME: This is shared by the traversal: */
+  hash_map <supernode *, per_node_t> m_per_node;
+
+  void on_transition (sm_instance_1 *sm_inst,
+		      const state_machine &sm,
+		      diagnostic_path *p,
+		      int stack_depth,
+		      const gimple *stmt, tree fndecl, tree dest,
+		      state_machine::state_t from,
+		      state_machine::state_t to);
+
+#if 0
+  // FIXME: map from tree to state_t (and where that state came from?)
+  hash_map <tree, state_t> m_states;
+  typedef hash_map <tree, state_t>::iterator state_iterator;
+#endif
+};
+
+void
+context::on_transition (sm_instance_1 *sm_inst,
+			const state_machine &sm,
+			diagnostic_path *p,
+			int stack_depth,
+			const gimple *stmt, tree fndecl,
+			tree dest,
+			state_machine::state_t from,
+			state_machine::state_t to)
+{
+  if (DEBUG)
+    inform (stmt->location, "DBG: %s; transition: %qE: %qs -> %qs",
+	    sm_inst->to_str (sm).c_str (),
+	    dest, sm.get_state_name (from), sm.get_state_name (to));
+
+  // TODO: filter: only the transitions for which the start state is reachable
+  // "cache_misses" ?
+
+  if (from == 0 || sm_inst->m_active_vars.contains (var_state_1_t (dest, from)))
+    {
+      p->add_event (stmt->location, fndecl, stack_depth,
+		    "%s; transition: %qE: %qs -> %qs",
+		    sm_inst->to_str (sm).c_str (), dest,
+		    sm.get_state_name (from), sm.get_state_name (to));
+      sm_inst->m_active_vars.add (var_state_1_t (dest, to));
+    }
+
+  // FIXME: also do the equivalence classes?
+}
+
+static std::string
+backtrace_to_str (const supergraph *sg,
+		  const auto_vec<cgraph_edge *> &backtrace)
+{
+  std::string result ("[");
+  // FIXME: this is probably not optimal
+
+  int i;
+  cgraph_edge *e;
+  FOR_EACH_VEC_ELT (backtrace, i, e)
+    {
+      if (i > 0)
+	result += ", ";
+      result += sg->get_caller_next_node (e)->to_str ();
+    }
+
+  result += "]";
+
+  return result;
+}
+
+/* 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 old_path_var
+{
+public:
+  old_path_var (tree t, int stack_depth)
+  : m_tree (t), m_stack_depth (stack_depth)
+  {
+    // TODO: ignore stack depth for globals and constants
+  }
+
+  std::string to_str () const;
+
+  tree m_tree;
+  int m_stack_depth; // or -1 for globals?
+};
+
+static std::string
+expr_to_string (tree t)
+{
+  char *tmp = global_dc_print ("%qE", t);
+  std::string result = tmp;
+  free (tmp);
+  return result;
+}
+
+std::string
+old_path_var::to_str () const
+{
+  return (std::string ("(")
+	  + expr_to_string (m_tree)
+	  + " @ "
+	  + std::to_string (m_stack_depth)
+	  + ")");
+}
+
+/* TODO: I *think* we need equiv classes in order to be able to cope with:
+
+     something_that_calls_free (ptr);
+
+   also transitioning "ptr" in this frame to "freed".  (but maybe summaries
+   help with this also?)  */
+
+#if 0
+/* FIXME.  */
+
+class equiv_class
+{
+public:
+  equiv_class ();
+  equiv_class (const equiv_class &other);
+
+  std::string to_str () const;
+
+  void add (old_path_var v) { m_vars.safe_push (v); }
+  //void del (old_path_var v);
+
+  // FIXME: this is a bad representation, probably should be a set.
+  auto_vec<old_path_var> m_vars;
+};
+
+equiv_class::equiv_class ()
+{
+}
+
+equiv_class::equiv_class (const equiv_class &other)
+{
+  // FIXME: copy-ctor for auto_vec!
+  int i;
+  old_path_var *v;
+  FOR_EACH_VEC_ELT (other.m_vars, i, v)
+    m_vars.safe_push (*v);
+}
+
+std::string
+equiv_class::to_str () const
+{
+  std::string result ("{");
+  int i;
+  old_path_var *v;
+  FOR_EACH_VEC_ELT (m_vars, i, v)
+    {
+      if (i > 0)
+	result += " == ";
+      result += v->to_str ();
+    }
+  return result + "}";
+}
+
+#endif
+
+// TODO: maybe reimplement in terms of constraints for now:
+
+enum constraint_op
+{
+  CONSTRAINT_EQ,
+  CONSTRAINT_NE,
+  CONSTRAINT_LT,
+  CONSTRAINT_LE
+};
+
+static const char *
+constraint_op_code (enum constraint_op c_op)
+{
+  switch (c_op)
+    {
+    default:
+      gcc_unreachable ();
+    case CONSTRAINT_EQ: return "==";
+    case CONSTRAINT_NE: return "!=";
+    case CONSTRAINT_LT: return "<";
+    case CONSTRAINT_LE: return "<=";
+    }
+}
+
+class old_constraint {
+ public:
+  old_constraint (old_path_var lhs, enum constraint_op c_op, old_path_var rhs)
+    : m_lhs (lhs), m_op (c_op), m_rhs (rhs) {}
+
+  std::string to_str () const;
+
+  int get_max_stack_depth () const
+  {
+    return std::max (m_lhs.m_stack_depth, m_rhs.m_stack_depth);
+  }
+
+  old_path_var m_lhs;
+  enum constraint_op m_op;
+  old_path_var m_rhs;
+};
+
+std::string
+old_constraint::to_str () const
+{
+  return (m_lhs.to_str ()
+	  + constraint_op_code (m_op)
+	  + m_rhs.to_str ());
+}
+
+/* Facts that we know, for the current path_state.  */
+
+class old_path_constraints
+{
+public:
+  old_path_constraints ();
+  old_path_constraints (const old_path_constraints &other);
+
+  std::string to_str () const;
+
+  void on_assignment (old_path_var dst, old_path_var src);
+  void on_stack_frame_popped (int new_max_depth);
+
+#if 0
+  auto_vec<equiv_class *> m_equiv_classes;
+#endif
+  auto_vec<old_constraint> m_constraints;
+};
+
+old_path_constraints::old_path_constraints ()
+{
+}
+
+old_path_constraints::old_path_constraints (const old_path_constraints &other)
+{
+  // FIXME: copy-ctor for auto_vec!
+  int i;
+#if 0
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (other.m_equiv_classes, i, ec)
+    m_equiv_classes.safe_push (new equiv_class (*ec));
+#endif
+
+  old_constraint *c;
+  FOR_EACH_VEC_ELT (other.m_constraints, i, c)
+    m_constraints.safe_push (*c);
+}
+
+std::string
+old_path_constraints::to_str () const
+{
+  std::string result ("{");
+
+  int i;
+#if 0
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    {
+      if (i > 0)
+	result += ", ";
+      result += ec->to_str ();
+    }
+#endif
+
+  old_constraint *c;
+  FOR_EACH_VEC_ELT (m_constraints, i, c)
+    {
+      if (i > 0)
+	result += ", ";
+      result += c->to_str ();
+    }
+
+  result += "}";
+  return result;
+}
+
+void
+old_path_constraints::on_assignment (old_path_var dst, old_path_var src)
+{
+  // FIXME: need a better implementation; for now
+#if 0
+  // just always add a new equivalence class:
+  equiv_class *ec = new equiv_class ();
+  m_equiv_classes.safe_push (ec);
+  ec->add (dst);
+  ec->add (src);
+#endif
+
+  m_constraints.safe_push (old_constraint (dst, CONSTRAINT_EQ, src));
+}
+
+void
+old_path_constraints::on_stack_frame_popped (int new_max_depth)
+{
+  // TODO: purge any ecs relating to the old frame:
+#if 0
+  int i;
+  equiv_class *ec;
+  FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+    {
+      for (unsigned j = 0; j < ec->m_vars.length (); j++)
+	if (ec->m_vars[j].m_stack_depth > new_max_depth)
+	  ec->m_vars.unordered_remove (j);
+    }
+#endif
+  // TODO: do it for constraints
+  for (unsigned i = 0; i < m_constraints.length (); i++)
+    if (m_constraints[i].get_max_stack_depth () > new_max_depth)
+      m_constraints.unordered_remove (i);
+}
+
+
+/* The state of a particular point at one point in the traversal (not including
+   state-machine information):
+   - the diagnostic_path that we're building (in case we need to
+     emit a diagnostic)
+   - the backtrace for unwinding calls,
+   - TODO: constraints (e.g. for diamond CFGs).  */
+
+class old_path_state
+{
+public:
+  old_path_state () : m_recursion_depth (0) {}
+  old_path_state (const old_path_state &other);
+
+  std::string to_str (const supergraph *sg) const;
+
+  int get_stack_depth () const { return m_backtrace.length (); }
+
+  diagnostic_path m_path;
+  auto_vec<cgraph_edge *> m_backtrace;
+  old_path_constraints m_constraints;
+  int m_recursion_depth;
+};
+
+/* FIXME.  */
+// TODO: maybe make this less inefficient (lots of copying)
+
+old_path_state::old_path_state (const old_path_state &other)
+: m_path (other.m_path),
+  m_backtrace (),
+  m_constraints (other.m_constraints),
+  m_recursion_depth (other.m_recursion_depth)
+{
+  // FIXME: copy-ctor for auto_vec!
+  int i;
+  cgraph_edge *edge;
+  FOR_EACH_VEC_ELT (other.m_backtrace, i, edge)
+    m_backtrace.safe_push (edge);
+}
+
+/* FIXME.  */
+
+std::string
+old_path_state::to_str (const supergraph *sg) const
+{
+  return (std::string ("<path length: ")
+	  + std::to_string (m_path.m_events.length())
+	  + std::string (" backtrace: ") + backtrace_to_str (sg, m_backtrace)
+	  + std::string (" constraints: ") + m_constraints.to_str ()
+	  + std::string (">"));
+}
+
+/* FIXME.  */
+
+class path_finder
+{
+public:
+  path_finder (const supergraph *supergraph,
+	       const state_machine &sm,
+	       context *ctxt)
+  : m_supergraph (supergraph), m_sm (sm), m_ctxt (ctxt),
+    m_traversals (0), m_max_recursion_depth (0)
+  {
+  }
+
+  void dfs_traversal (const supernode *node,
+		      old_path_state state,
+		      sm_instance_1 *sm_inst,
+		      auto_bitmap &visited);
+
+  void on_stmt (const supernode *node,
+		old_path_state &state,
+		sm_instance_1 *sm_inst,
+		const gimple *stmt);
+
+  //private:
+  /* Various things that are global to the whole traversal.  */
+  const supergraph *m_supergraph;
+  const state_machine &m_sm;
+  context *m_ctxt;
+
+  /* Stats.  */
+public:
+  int m_traversals;
+  int m_max_recursion_depth;
+};
+
+/* Interface to path_finder::dfs_traversal for a state_machine to call
+   into.  */
+
+class foo_sm_context : public sm_context
+{
+public:
+  foo_sm_context (const state_machine &sm,
+		  path_finder &pf,
+		  old_path_state &state,
+		  sm_instance_1 *sm_inst)
+  : sm_context (), m_sm (sm), m_pf (pf), m_state (state),
+    m_sm_inst (sm_inst)
+  {}
+
+#if 1
+  void on_transition (const supernode *node, const gimple *stmt,
+		      tree var,
+		      state_machine::state_t from,
+		      state_machine::state_t to) FINAL OVERRIDE
+  {
+    // FIXME
+    tree fndecl = node->m_fun->decl;
+    m_pf.m_ctxt->on_transition (m_sm_inst, m_sm, &m_state.m_path,
+				m_state.get_stack_depth (), stmt, fndecl,
+				var, from, to);
+  }
+#endif
+
+  //state_machine::state_t get_state (tree var) FINAL OVERRIDE {/* FIXME */}
+  //void transition (tree var, state_machine::state_t to) FINAL OVERRIDE {/* FIXME */}
+  void error (const supernode *node, const gimple *stmt,
+	      tree var, state_machine::state_t state, const char *fmt, ...) FINAL OVERRIDE {/* FIXME */}
+
+private:
+  const state_machine &m_sm;
+  path_finder &m_pf;
+  old_path_state &m_state;
+  sm_instance_1 *m_sm_inst;
+};
+
+void
+path_finder::dfs_traversal (const supernode *node,
+			    old_path_state state,
+			    sm_instance_1 *sm_inst,
+			    auto_bitmap &visited)
+{
+  m_traversals++;
+  state.m_recursion_depth++;
+  if (m_max_recursion_depth < state.m_recursion_depth)
+    m_max_recursion_depth = state.m_recursion_depth;
+
+  std::string sm_inst_str = sm_inst->to_str (m_sm);
+  if (DEBUG)
+    inform (node->get_start_location (),
+	    "DBG: dfs_traversal on %qD supernode=%i, sm_inst: %s, state: %s",
+	    node->m_fun->decl, node->m_index, sm_inst_str.c_str (),
+	    state.to_str (m_supergraph).c_str ());
+
+  bitmap_set_bit (visited, node->m_index);
+
+  if (verbosity >= 2)
+    state.m_path.add_event (node->get_start_location (),
+			    node->m_fun->decl, state.get_stack_depth (),
+			    "%qD supernode:%i sm_inst: %s state: %s",
+			    node->m_fun->decl, node->m_index,
+			    sm_inst_str.c_str (),
+			    state.to_str (m_supergraph).c_str ());
+
+  int i;
+  gimple *stmt;
+  FOR_EACH_VEC_ELT (node->m_stmts, i, stmt)
+    on_stmt (node, state, sm_inst, stmt);
+
+  if (node->return_p ())
+    {
+      tree callee = node->m_fun->decl;
+
+      // TODO: respect "visited"
+
+      /* Get IVP successor, if any, via backtrace.  */
+      if (state.m_backtrace.length () == 0)
+	{
+	  gcc_rich_location rich_loc (node->m_fun->function_end_locus);
+	  rich_loc.set_path (&state.m_path);
+	  // FIXME: set/reset current_function_decl so as to get "In function"
+	  // announcements
+	  //fprintf (stderr, "============================================================================\n");
+	  inform (&rich_loc, "END OF PATH: length: %i (presumably started within %qD)",
+		  state.m_path.m_events.length (),
+		  node->m_fun->decl);
+	  //fprintf (stderr, "----------------------------------------------------------------------------\n");
+	  return;
+	}
+
+      /* Recurse, taking a copy of the path.  */
+      old_path_state next (state);
+      cgraph_edge *edge = next.m_backtrace.pop ();
+
+      supernode *dest = m_supergraph->get_caller_next_node (edge);
+
+      // FIXME: need to purge state, and update return value based on *sm_inst:
+      sm_instance_1 next_sm_inst;
+      if (!VOID_TYPE_P (TREE_TYPE (DECL_RESULT (callee))))
+	{
+	  if (dest->m_returning_call
+	      && gimple_call_lhs (dest->m_returning_call))
+	    {
+	      for (hash_set<var_state_1_t>::iterator iter = sm_inst->m_active_vars.begin ();
+		   iter != sm_inst->m_active_vars.end ();
+		   ++iter)
+		{
+		  // purge to just the return value, and assign to lhs of m_returning_call:
+		  if ((*iter).m_var == DECL_RESULT (callee))
+		    {
+		      next_sm_inst.m_active_vars.add
+			(var_state_1_t (gimple_call_lhs (dest->m_returning_call),
+				      (*iter).m_state));
+		    }
+		  // FIXME: should this be done by the next node?
+		}
+	    }
+	}
+      // TODO: purge state from constraints
+      next.m_constraints.on_stack_frame_popped (next.get_stack_depth ());
+
+      if (DEBUG)
+	inform (UNKNOWN_LOCATION, "from %s to %s",
+		sm_inst->to_str (m_sm).c_str (),
+		next_sm_inst.to_str (m_sm).c_str ());
+
+      gcc_assert (dest->m_returning_call);
+      if (verbosity >= 1)
+	next.m_path.add_event
+	  (dest->m_returning_call->location,
+	   dest->m_fun->decl, // FIXME: or node?
+	   next.get_stack_depth (),
+	   "returning from %qD to %qD (supernode %i -> %i)",
+	   node->m_fun->decl,
+	   dest->m_fun->decl,
+	   node->m_index,
+	   dest->m_index);
+      dfs_traversal (dest, next, &next_sm_inst, visited);
+    }
+  else
+    {
+      superedge *succ;
+      FOR_EACH_VEC_ELT (node->m_succs, i, succ)
+	{
+	  // Don't revisit BBs we've already seen
+	  if (bitmap_bit_p (visited, succ->m_dest->m_index))
+	    {
+	      // FIXME: disabled for now
+	      // TODO: generalize this to take into account state traversals:
+	      // FIXME: we may want to do it based on edges, rather than nodes:
+	      // consider multiple edges in a switch statement, which only
+	      // differ via phi nodes (see demo-2.c)
+#if 0
+	      if (DEBUG)
+		inform (UNKNOWN_LOCATION,
+			"DBG: already visited supernode %i; skipping",
+			succ->m_dest->m_index);
+	      continue;
+#endif
+	    }
+
+	  /* Recurse, taking a copy of the path and backtrace.  */
+	  sm_instance_1 next_sm_inst (*sm_inst);
+	  old_path_state next (state);
+
+	  switch (succ->m_kind)
+	    {
+	    default:
+	      gcc_unreachable ();
+	    case SUPEREDGE_CFG_EDGE:
+	      if (verbosity >= 2)
+		{
+		  char *edge_desc = succ->get_description ();
+		  if (strlen (edge_desc) > 0)
+		    {
+		      char *wrapped = concat ("(", edge_desc, ") ", NULL);
+		      free (edge_desc);
+		      edge_desc = wrapped;
+		    }
+		  next.m_path.add_event
+		    (node->get_end_location (),
+		     node->m_fun->decl,
+		     state.get_stack_depth (),		     
+		     "taking CFG edge %s%i->%i within %qD (supernode %i -> %i)",
+		     edge_desc,
+		     node->m_bb->index,
+		     succ->m_dest->m_bb->index,
+		     node->m_fun->decl,
+		     node->m_index,
+		     succ->m_dest->m_index);
+		  free (edge_desc);
+		}
+	      break;
+	    case SUPEREDGE_CALL:
+	      {
+		// TODO: update next_sm_inst to reflect params
+		sm_instance_1 call_sm_inst;
+
+		/* Record where we're returning to (and update the
+		   stack depth of "next").  */
+		next.m_backtrace.safe_push (succ->get_callgraph_edge ());
+
+		tree callee = succ->m_dest->m_fun->decl;
+		gcall *call = node->get_final_call ();
+
+		int i = 0;
+		for (tree parm = DECL_ARGUMENTS (callee); parm;
+		     parm = DECL_CHAIN (parm), ++i)
+		  {
+		    tree arg = gimple_call_arg (call, i);
+		    if (DEBUG)
+		      inform (call->location, "arg[%i]: %qE parm[%i]: %qD",
+			      i, arg, i, parm);
+		    /* Use: parm, arg, next_sm_inst, to map from args in caller
+		       to parms in callee */
+		    for (hash_set<var_state_1_t>::iterator iter
+			   = next_sm_inst.m_active_vars.begin ();
+			 iter != next_sm_inst.m_active_vars.end ();
+			 ++iter)
+		      {
+			if ((*iter).m_var == arg)
+			  {
+			    call_sm_inst.m_active_vars.add
+			      (var_state_1_t (parm, (*iter).m_state));
+			  }
+		      }
+		    /* Associate the parm in the new stack frame with
+		       the arg in the old stack frame in the constraints
+		       of the next state.  */
+		    next.m_constraints.on_assignment
+		      (old_path_var (parm, next.get_stack_depth ()),
+		       old_path_var (arg, state.get_stack_depth ()));
+		  }
+		if (DEBUG)
+		  inform (UNKNOWN_LOCATION, "from %s to %s",
+			  next_sm_inst.to_str (m_sm).c_str (),
+			  call_sm_inst.to_str (m_sm).c_str ());
+#if 0
+		// FIXME: seeing crashes with this
+		// hash_set appears not to have a copy-ctor...
+		next_sm_inst = call_sm_inst;
+#endif
+
+		if (verbosity >= 0)
+		  next.m_path.add_event
+		    (node->get_end_location (),
+		     node->m_fun->decl, // FIXME: or dest?
+		     state.get_stack_depth (),
+		     "calling from %qD to %qD (supernode %i -> %i)",
+		     node->m_fun->decl,
+		     succ->m_dest->m_fun->decl,
+		     node->m_index,
+		     succ->m_dest->m_index);
+	      }
+	      break;
+	    case SUPEREDGE_RETURN:
+	      // This should be covered above:
+	      gcc_unreachable ();
+	      break;
+	    case SUPEREDGE_INTRAPROCEDURAL_CALL:
+	      /* Skip these.  */
+	      continue;
+#if 0
+	      next.m_path.add_event
+		(UNKNOWN_LOCATION,
+		 node->m_fun->decl,
+		 "intraprocedural edge at call within %qD from supernode %i to supernode %i",
+		 node->m_fun->decl,
+		 node->m_index,
+		 succ->m_dest->m_index);
+	      // FIXME: do we actually want to do this?
+	      break;
+#endif
+	    }
+
+	  dfs_traversal (succ->m_dest, next,
+			 &next_sm_inst, visited);
+	}
+    }
+}
+
+void
+path_finder::on_stmt (const supernode *node,
+		      old_path_state &state,
+		      sm_instance_1 *sm_inst,
+		      const gimple *stmt)
+{
+  if (DEBUG)
+    {
+      fprintf (stderr, "DBG: ");
+      print_gimple_stmt (stderr, const_cast <gimple *> (stmt), // FIXME<<<
+			 0, (dump_flags_t)0);
+    }
+
+  // FIXME: this is for debugging; which stmts do we want to log in the path?
+  {
+    pretty_printer pp;
+    pp_gimple_stmt_1 (&pp, const_cast <gimple *> (stmt), 0, (dump_flags_t)0);
+    if (verbosity >= 1)
+      state.m_path.add_event (stmt->location, node->m_fun->decl,
+			      state.get_stack_depth (),
+			      "on_stmt: %qs %s",
+			      pp_formatted_text (&pp),
+			      sm_inst->to_str (m_sm).c_str ());
+  }
+
+  foo_sm_context sm_ctxt (m_sm, *this, state, sm_inst);
+  m_sm.on_stmt (&sm_ctxt, node, stmt);
+
+  if (const gassign *assign = dyn_cast <const gassign *> (stmt))
+    {
+      tree lhs = gimple_assign_lhs (assign);
+      tree rhs = gimple_assign_rhs1 (assign);
+      // FIXME: what about other kinds of assignment?
+      if (DEBUG)
+	inform (stmt->location, "got assignment: %qE <- %qE", lhs, rhs);
+
+      for (hash_set<var_state_1_t>::iterator iter = sm_inst->m_active_vars.begin ();
+	   iter != sm_inst->m_active_vars.end ();
+	   ++iter)
+	{
+	  if ((*iter).m_var == rhs)
+	    sm_inst->m_active_vars.add (var_state_1_t (lhs, (*iter).m_state));
+	}
+    }
+
+  // FIXME: linker issues with helper for "const greturn *"
+  if (greturn *return_ = dyn_cast <greturn *> (const_cast <gimple *> (stmt)))
+    {
+      tree retval = gimple_return_retval (return_);
+      if (retval)
+	{
+	  if (DEBUG)
+	    inform (stmt->location, "got return: %qE", retval);
+	  tree callee = node->m_fun->decl;
+	  tree result = DECL_RESULT (callee);
+	  for (hash_set<var_state_1_t>::iterator iter = sm_inst->m_active_vars.begin ();
+	       iter != sm_inst->m_active_vars.end ();
+	       ++iter)
+	    {
+	      if ((*iter).m_var == retval)
+		sm_inst->m_active_vars.add (var_state_1_t (result, (*iter).m_state));
+	    }
+	}
+    }
+}
+
+class foo_annotator : public dot_annotator
+{
+public:
+  void add_node_annotations (pretty_printer *pp,
+			     const supernode &n) const FINAL OVERRIDE
+  {
+    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);
+    pp_right_brace (pp);
+    pp_string (pp, "\"];\n\n");
+    pp_flush (pp);
+  }
+};
+
+/* FIXME: toy implementation */
+
+static void
+toy_impl (const state_machine &sm, function *)
+{
+#if 1
+  supergraph sg;
+  sg.dump_dot ("test.dot");
+
+  context ctxt;
+  path_finder pf (&sg, sm, &ctxt);
+
+  cgraph_node *node;
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+  {
+    function *fun = node->get_fun ();
+
+    old_path_state init_state;
+    auto_bitmap visited;
+    sm_instance_1 sm_inst;
+    pf.dfs_traversal (sg.get_node_for_function_entry (fun),
+		      init_state, &sm_inst, visited);
+  }
+
+  inform (UNKNOWN_LOCATION,
+	  "num traversal calls: %i; max recursion depth: %i",
+	  pf.m_traversals, pf.m_max_recursion_depth);
+
+  foo_annotator a;
+  sg.dump_dot ("test-2.dot", &a);
+#else
+  gimple_stmt_iterator gsi;
+  basic_block bb;
+
+  auto_vec<basic_block> worklist;
+  FOR_EACH_BB_FN (bb, fun)
+    worklist.safe_push (bb);
+
+  /* Process the worklist.  */
+  while (!worklist.is_empty ())
+    {
+      basic_block bb = worklist.pop ();
+
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+	{
+	  gimple *stmt = gsi_stmt (gsi);
+	  print_gimple_stmt (stdout, stmt, 0, (dump_flags_t)0);
+	  sm.on_stmt (stmt);
+	}
+    }
+#endif
+}
+
+void
+toy_impl_1 (function *fn)
+{
+  malloc_state_machine sm;
+
+  toy_impl (sm, fn);
+}
diff --git a/gcc/analyzer/dead-code/toy-sm-impl-1.h b/gcc/analyzer/dead-code/toy-sm-impl-1.h
new file mode 100644
index 0000000..f017b84
--- /dev/null
+++ b/gcc/analyzer/dead-code/toy-sm-impl-1.h
@@ -0,0 +1 @@
+extern void toy_impl_1 (function *fn);
diff --git a/gcc/testsuite/DEADCODE-analyzer.dg/analyzer.exp b/gcc/testsuite/DEADCODE-analyzer.dg/analyzer.exp
new file mode 100644
index 0000000..515a676
--- /dev/null
+++ b/gcc/testsuite/DEADCODE-analyzer.dg/analyzer.exp
@@ -0,0 +1,49 @@
+# Test code for analyzer_plugin.so
+load_lib dg.exp
+load_lib prune.exp
+load_lib target-supports.exp
+load_lib gcc-defs.exp
+load_lib timeout.exp
+load_lib target-libpath.exp
+load_lib gcc.exp
+load_lib dejagnu.exp
+
+proc analyzer_target_compile { source dest type options } {
+    gcc_target_compile $source $dest $type $options
+}
+
+load_lib gcc-dg.exp
+
+proc analyzer-dg-test { prog do_what extra_tool_flags } {
+    verbose "within analyzer-dg-test..."
+    verbose "  prog: $prog"
+    verbose "  do_what: $do_what"
+    verbose "  extra_tool_flags: $extra_tool_flags"
+
+    set result \
+	[gcc-dg-test-1 analyzer_target_compile $prog $do_what $extra_tool_flags]
+
+    set comp_output [lindex $result 0]
+    set output_file [lindex $result 1]
+
+    return [list $comp_output $output_file]
+}
+
+# Initialize dg.
+dg-init
+
+# Gather a list of all tests.
+
+set tests [find $srcdir/$subdir *.c]
+
+set tests [lsort $tests]
+
+verbose "tests: $tests"
+
+set DEFAULT_CFLAGS "--analyzer"
+
+# Main loop.  This will invoke analyzer-dg-test on each test-*.c file.
+dg-runtest $tests "" $DEFAULT_CFLAGS
+
+# All done.
+dg-finish
diff --git a/gcc/testsuite/DEADCODE-analyzer.dg/demo-3.c b/gcc/testsuite/DEADCODE-analyzer.dg/demo-3.c
new file mode 100644
index 0000000..cab0f09
--- /dev/null
+++ b/gcc/testsuite/DEADCODE-analyzer.dg/demo-3.c
@@ -0,0 +1,31 @@
+#include <stdlib.h>
+
+extern void __dump_malloc_state ();
+
+typedef struct boxed_int
+{
+  int val;
+} boxed_int;
+
+extern boxed_int *make_boxed_int (int i);
+
+/* Can we use magic fnnames to capture internal state, and thus write
+   DejaGnu tests for this?  */
+
+boxed_int *
+make_boxed_int (int val)
+{
+  boxed_int *new_ptr;
+
+  __dump_malloc_state (); /* { dg-message "malloc state: <start, {}> } */
+
+  new_ptr = (boxed_int *)malloc (sizeof (boxed_int));
+
+  __dump_malloc_state (); /* { dg-message "malloc state: <start, {'new_ptr': 'unchecked'}> } */
+
+  new_ptr->val = val;
+
+  __dump_malloc_state ();
+
+  return new_ptr;
+}
diff --git a/gcc/testsuite/DEADCODE-analyzer.dg/dg.exp b/gcc/testsuite/DEADCODE-analyzer.dg/dg.exp
new file mode 100644
index 0000000..7fda22c
--- /dev/null
+++ b/gcc/testsuite/DEADCODE-analyzer.dg/dg.exp
@@ -0,0 +1,53 @@
+# Test code for analyzer_plugin.so
+
+#global tool
+#set tool "gcc"
+load_lib gcc-dg.exp
+
+#load_lib dg.exp
+#load_lib prune.exp
+#load_lib target-supports.exp
+#load_lib gcc-defs.exp
+#load_lib timeout.exp
+#load_lib target-libpath.exp
+#load_lib gcc.exp
+#load_lib dejagnu.exp
+
+proc analyzer_target_compile { source dest type options } {
+    gcc_target_compile $source $dest $type $options
+}
+
+
+proc analyzer-dg-test { prog do_what extra_tool_flags } {
+    verbose "within analyzer-dg-test..."
+    verbose "  prog: $prog"
+    verbose "  do_what: $do_what"
+    verbose "  extra_tool_flags: $extra_tool_flags"
+
+    set result \
+	[gcc-dg-test-1 analyzer_target_compile $prog $do_what $extra_tool_flags]
+
+    set comp_output [lindex $result 0]
+    set output_file [lindex $result 1]
+
+    return [list $comp_output $output_file]
+}
+
+# Initialize dg.
+dg-init
+
+# Gather a list of all tests.
+
+set tests [find $srcdir/$subdir *.c]
+
+set tests [lsort $tests]
+
+verbose "tests: $tests"
+
+set DEFAULT_CFLAGS "--analyzer"
+
+# Main loop.  This will invoke analyzer-dg-test on each test-*.c file.
+dg-runtest $tests "" $DEFAULT_CFLAGS
+
+# All done.
+dg-finish
diff --git a/gcc/testsuite/DEADCODE-analyzer.dg/malloc/bad-size.c b/gcc/testsuite/DEADCODE-analyzer.dg/malloc/bad-size.c
new file mode 100644
index 0000000..1456c90
--- /dev/null
+++ b/gcc/testsuite/DEADCODE-analyzer.dg/malloc/bad-size.c
@@ -0,0 +1,20 @@
+#include <stdlib.h>
+
+struct foo
+{
+  int i;
+};
+
+struct bar
+{
+  struct foo f;
+  int j;
+};
+
+struct bar *test (void)
+{
+  struct bar *p;
+  p = (struct bar *)malloc (sizeof (struct foo)); /* should warn here */
+  p->j = 42;
+  return p;
+}
diff --git a/gcc/testsuite/DEADCODE-analyzer.dg/memory/stack-address.c b/gcc/testsuite/DEADCODE-analyzer.dg/memory/stack-address.c
new file mode 100644
index 0000000..602b26a
--- /dev/null
+++ b/gcc/testsuite/DEADCODE-analyzer.dg/memory/stack-address.c
@@ -0,0 +1,8 @@
+int *test(void)
+{
+  /* Enough logic to prevent -Wreturn-local-addr from seeing it.  */
+  int i;
+  int *p;
+  p = &i;
+  return p; /* { dg-error "return of pointer to stack" } */
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/input.c
new file mode 100644
index 0000000..01a2387
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/input.c
@@ -0,0 +1,42 @@
+/*
+   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(int i, int j)
+{
+  int k;
+  void *ptr;
+  for (k = i; k < j; k++) {
+    switch(k) {
+    case 0:
+      ptr = malloc(1024);
+      break;
+    case 1:
+      break;
+    case 2:
+      break;
+    default:
+      break;
+    }
+  }
+  memset(ptr, 0, 4096);
+  free(ptr);
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/script.py
new file mode 100644
index 0000000..d6390c5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/script.py
@@ -0,0 +1,23 @@
+# -*- 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/fancy_control_flow/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/stderr.txt
new file mode 100644
index 0000000..2744fa4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/stderr.txt
@@ -0,0 +1,4 @@
+tests/sm/checkers/malloc-checker/fancy_control_flow/input.c: In function 'test':
+tests/sm/checkers/malloc-checker/fancy_control_flow/input.c:40:nn: error: possibly-NULL pointer ptr passed as argument 1 to memset [CWE-690]
+tests/sm/checkers/malloc-checker/fancy_control_flow/input.c:30:nn: note: ptr assigned to the result of malloc()
+tests/sm/checkers/malloc-checker/fancy_control_flow/input.c:40:nn: note: possibly-NULL pointer ptr passed as argument 1 to memset
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/fancy_control_flow/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/input.c
new file mode 100644
index 0000000..e1b0e28
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/input.c
@@ -0,0 +1,41 @@
+/*
+   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>
+#include <string.h>
+
+struct foo {
+  int i;
+  int j;
+  int k;
+};
+
+struct foo *test(void)
+{
+  struct foo *p = (struct foo*)malloc(4096);
+
+  /* BUG: usage of p without checking if malloc return NULL: */
+  p->i = 1;
+
+  /* Only the first such usage should be reported: */
+  p->j = 2;
+  p->k = 3;
+
+  return p;
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/script.py
new file mode 100644
index 0000000..263594c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/script.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+#   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/>.
+
+from sm import main
+from sm.parser import parse_file
+
+def selftest(ctxt, solution):
+    if 0:
+        import sys
+        solution.dump(sys.stderr)
+
+    node_call = ctxt.find_call_of('malloc')
+    ssaname = node_call.stmt.lhs
+
+    p = ctxt.find_var(node_call, 'p')
+    node_write_to_p_i = ctxt.get_nodes().assigning_constant(1).one()
+    ctxt.assert_statenames_for_expr(node_write_to_p_i, p,
+                                    frozenset(['ptr.unchecked']))
+
+    # It should now have given up, with the p in the "ptr.nonnull" state:
+    node_write_to_p_j = ctxt.get_nodes().assigning_constant(2).one()
+    ctxt.assert_statenames_for_expr(node_write_to_p_j, p,
+                                    frozenset(['ptr.nonnull']))
+
+    node_write_to_p_k = ctxt.get_nodes().assigning_constant(3).one()
+    ctxt.assert_statenames_for_expr(node_write_to_p_k, p,
+                                    frozenset(['ptr.nonnull']))
+
+
+checker = parse_file('sm/checkers/malloc_checker.sm')
+main([checker], selftest=selftest)
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/stderr.txt
new file mode 100644
index 0000000..7861bb4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/stderr.txt
@@ -0,0 +1,4 @@
+tests/sm/checkers/malloc-checker/multiple-errors/unchecked_malloc/input.c: In function 'test':
+tests/sm/checkers/malloc-checker/multiple-errors/unchecked_malloc/input.c:34:nn: error: dereference of possibly-NULL pointer p [CWE-690]
+tests/sm/checkers/malloc-checker/multiple-errors/unchecked_malloc/input.c:31:nn: note: p assigned to the result of malloc()
+tests/sm/checkers/malloc-checker/multiple-errors/unchecked_malloc/input.c:34:nn: note: dereference of possibly-NULL pointer p
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/malloc-checker/multiple-errors/unchecked_malloc/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/putenv/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/putenv/input.c
new file mode 100644
index 0000000..b400778
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/putenv/input.c
@@ -0,0 +1,31 @@
+/*
+   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 <stdio.h>
+
+void test_bad_putenv(const char *key, const char *value)
+{
+  char buffer[1024];
+  
+  snprintf(buffer, 1024, "%s=%s", key, value);
+
+  /* BUG: buffer is on the stack, and some implementations of putenv only
+     store a pointer to the string: */
+  (void)putenv(buffer);
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/putenv/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/putenv/script.py
new file mode 100644
index 0000000..ef77626
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/putenv/script.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+#   Copyright 2012, 2013 David Malcolm <dmalcolm@redhat.com>
+#   Copyright 2012, 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/>.
+
+import gcc
+
+from sm import main
+from sm.parser import parse_file
+
+def selftest(ctxt, solution):
+    if 1:
+        import sys
+        solution.dump(sys.stderr)
+
+checker = parse_file('sm/checkers/points_to_stack.sm')
+print(checker)
+main([checker], selftest=selftest)
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/input.c
new file mode 100644
index 0000000..730a400
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/input.c
@@ -0,0 +1,69 @@
+/*
+   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 <stdio.h>
+
+char *test(const char *name)
+{
+  char buffer[1024];
+  char *result = NULL;
+
+  snprintf(buffer, 1024, "hello %s", name);
+
+  /* Note that if we simply
+       "return buffer;"
+     gcc is smart enough by itself to issue
+        "warning: function returns address of local variable [enabled by default]"
+     from c-typeck.c:c_finish_return as it builds the return statement.
+
+     However, if we introduce some dataflow, this warning isn't issued by
+     that code - but our points_to_stack.sm script can it:
+  */
+  result = buffer;
+
+  /* BUG: returns pointer to variable on the stack: */
+  return result;
+}
+
+char *test2(const char *name)
+{
+  static char buffer[1024];
+  char *result = NULL;
+
+  snprintf(buffer, 1024, "hello %s", name);
+
+  result = buffer;
+
+  /* Not a bug: buffer is static */
+  return result;
+}
+
+char other_buffer[1024];
+
+char *test3(const char *name)
+{
+  char *result = NULL;
+
+  snprintf(other_buffer, 1024, "hello %s", name);
+
+  result = other_buffer;
+
+  /* Not a bug: other_buffer is global */
+  return result;
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/script.py
new file mode 100644
index 0000000..60feca7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/script.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+#   Copyright 2012, 2013 David Malcolm <dmalcolm@redhat.com>
+#   Copyright 2012, 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/>.
+
+import gcc
+
+from sm import main
+from sm.parser import parse_file
+
+def selftest(ctxt, solution):
+    if 0:
+        import sys
+        solution.dump(sys.stderr)
+
+checker = parse_file('sm/checkers/points_to_stack.sm')
+main([checker], selftest=selftest)
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/stderr.txt
new file mode 100644
index 0000000..55b4ce0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/stderr.txt
@@ -0,0 +1,5 @@
+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/points_to_stack/return_of_ptr_to_local/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/points_to_stack/return_of_ptr_to_local/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/input.c
new file mode 100644
index 0000000..282c9bc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/input.c
@@ -0,0 +1,52 @@
+/*
+   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 <stdio.h>
+#include <signal.h>
+
+extern void body_of_program(void);
+
+int logging = 1;
+
+void custom_logger(const char *msg)
+{
+  if (logging) {
+    (void)fprintf(stderr, "LOG: %s", msg);
+  }
+}
+
+void handler(int signum)
+{
+  /* BUG: log calls fprintf, and this is not allowed from a signal
+     handler: */
+  custom_logger("got signal");
+}
+
+int main(int argc, const char *argv)
+{
+  custom_logger("started");
+
+  signal(SIGINT, handler);
+
+  body_of_program();
+
+  custom_logger("stopped");
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/script.py
new file mode 100644
index 0000000..80fe4ad
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/signal_handler/example/script.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+#   Copyright 2012, 2013 David Malcolm <dmalcolm@redhat.com>
+#   Copyright 2012, 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/>.
+
+import gcc
+
+from sm import main
+from sm.parser import parse_file
+
+def selftest(ctxt, solution):
+    if 1:
+        import sys
+        solution.dump(sys.stderr)
+
+checker = parse_file('sm/checkers/signal_handler.sm')
+main([checker], selftest=selftest)
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/input.c
new file mode 100644
index 0000000..e5053c5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/input.c
@@ -0,0 +1,34 @@
+/*
+   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>
+
+struct foo {
+  char buffer[1024];
+};
+
+struct bar {
+  char buffer[128];
+};
+
+struct foo *test(void)
+{
+  return (struct foo*)calloc(4, sizeof(struct bar));
+  /* BUG: sizeof(bar), rather than sizeof(foo), hence not enough space */
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/script.py
new file mode 100644
index 0000000..cd01c16
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/script.py
@@ -0,0 +1,23 @@
+# -*- 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/sizeof_allocation.sm')
+main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/stderr.txt
new file mode 100644
index 0000000..f62e50e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/stderr.txt
@@ -0,0 +1,2 @@
+tests/sm/checkers/sizeof_allocation/calloc_too_small/input.c: In function 'test':
+tests/sm/checkers/sizeof_allocation/calloc_too_small/input.c:32:nn: error: allocation too small: pointer to struct foo (1024 bytes) initialized with allocation of 512 bytes [CWE-131]
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/calloc_too_small/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_correct_size/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_correct_size/input.c
new file mode 100644
index 0000000..e0a994e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_correct_size/input.c
@@ -0,0 +1,31 @@
+/*
+   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>
+
+struct foo {
+  char buffer[256];
+  int i, j, k;
+  float x, y, z;
+};
+
+struct foo *test(void)
+{
+  return (struct foo*)malloc(sizeof(struct foo));
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_correct_size/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_correct_size/script.py
new file mode 100644
index 0000000..cd01c16
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_correct_size/script.py
@@ -0,0 +1,23 @@
+# -*- 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/sizeof_allocation.sm')
+main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_correct_size/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_correct_size/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_large/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_large/input.c
new file mode 100644
index 0000000..71b619e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_large/input.c
@@ -0,0 +1,36 @@
+/*
+   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>
+
+struct foo {
+  char buffer[128];
+};
+
+struct bar {
+  char buffer[256];
+};
+
+struct foo *test(void)
+{
+  return (struct foo*)malloc(sizeof(struct bar));
+  /* not a reportable bug: although the sizeof() is wrong, the size is large
+     enough, and some code does runtime size calculation that we don't want to
+     issue a false positive over */
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_large/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_large/script.py
new file mode 100644
index 0000000..cd01c16
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_large/script.py
@@ -0,0 +1,23 @@
+# -*- 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/sizeof_allocation.sm')
+main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_large/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_large/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/input.c
new file mode 100644
index 0000000..948a2de
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/input.c
@@ -0,0 +1,34 @@
+/*
+   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>
+
+struct foo {
+  char buffer[256];
+};
+
+struct bar {
+  char buffer[128];
+};
+
+struct foo *test(void)
+{
+  return (struct foo*)malloc(sizeof(struct bar));
+  /* BUG: sizeof(bar), rather than sizeof(foo), hence not enough space */
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/script.py
new file mode 100644
index 0000000..cd01c16
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/script.py
@@ -0,0 +1,23 @@
+# -*- 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/sizeof_allocation.sm')
+main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/stderr.txt
new file mode 100644
index 0000000..fcb1fd0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/stderr.txt
@@ -0,0 +1,2 @@
+tests/sm/checkers/sizeof_allocation/malloc_too_small/input.c: In function 'test':
+tests/sm/checkers/sizeof_allocation/malloc_too_small/input.c:32:nn: error: allocation too small: pointer to struct foo (256 bytes) initialized with allocation of 128 bytes [CWE-131]
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/malloc_too_small/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/input.c
new file mode 100644
index 0000000..a3b3f9a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/input.c
@@ -0,0 +1,36 @@
+/*
+   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>
+
+struct foo {
+  char buffer[256];
+};
+
+struct bar {
+  char buffer[128];
+};
+
+struct foo *test(void)
+{
+  void *ptr;
+  ptr = malloc(sizeof(struct bar));
+  /* BUG: sizeof(bar), rather than sizeof(foo), hence not enough space */
+  return ptr;
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/script.py
new file mode 100644
index 0000000..4f333a0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/script.py
@@ -0,0 +1,40 @@
+# -*- 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
+from sm.solver import State
+
+def selftest(ctxt, solution):
+    if 0:
+        import sys
+        solution.dump(sys.stderr)
+
+    # Verify that the set_state() due to the pattern match for:
+    #     ptr = malloc(sizeof(struct bar));
+    # transitions the state of ptr from "ptr.start" to a "ptr.sized" instance
+    # with the correct size
+    node = ctxt.find_call_of('malloc')
+    ctxt.assert_statenames_for_varname(node, 'ptr', {'ptr.start'})
+
+    node = ctxt.get_successor(node)
+    ctxt.assert_states_for_varname(node, 'ptr',
+                                   {State('ptr.sized', size=128)})
+
+checker = parse_file('sm/checkers/sizeof_allocation.sm')
+main([checker], selftest=selftest)
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/stderr.txt
new file mode 100644
index 0000000..59aeba1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/stderr.txt
@@ -0,0 +1,4 @@
+tests/sm/checkers/sizeof_allocation/stateful_malloc_too_small/input.c: In function 'test':
+tests/sm/checkers/sizeof_allocation/stateful_malloc_too_small/input.c:35:nn: error: allocation too small: pointer to struct foo (256 bytes) initialized with allocation of 128 bytes [CWE-131]
+tests/sm/checkers/sizeof_allocation/stateful_malloc_too_small/input.c:33:nn: note: ptr assigned to the result of malloc(128)
+tests/sm/checkers/sizeof_allocation/stateful_malloc_too_small/input.c:35:nn: note: allocation too small: pointer to struct foo (256 bytes) initialized with allocation of 128 bytes
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/stateful_malloc_too_small/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/unknown_size/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/unknown_size/input.c
new file mode 100644
index 0000000..ffaab54
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/unknown_size/input.c
@@ -0,0 +1,32 @@
+/*
+   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>
+
+struct foo {
+  char buffer[256];
+};
+
+struct foo *test(int i)
+{
+  void *ptr;
+  ptr = malloc(i);
+  /* might or might not be a bug; we don't know, so don't issue a bug */
+  return ptr;
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/unknown_size/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/unknown_size/script.py
new file mode 100644
index 0000000..02f3146
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/unknown_size/script.py
@@ -0,0 +1,38 @@
+# -*- 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
+from sm.solver import State
+
+def selftest(ctxt, solution):
+    if 0:
+        import sys
+        solution.dump(sys.stderr)
+
+    # Verify that ptr doesn't change state due to
+    #     ptr = malloc(i);
+    # since the checker only tracks constant integer values:
+    node = ctxt.find_call_of('malloc')
+    ctxt.assert_statenames_for_varname(node, 'ptr', {'ptr.start'})
+
+    node = ctxt.get_successor(node)
+    ctxt.assert_statenames_for_varname(node, 'ptr', {'ptr.start'})
+
+checker = parse_file('sm/checkers/sizeof_allocation.sm')
+main([checker], selftest=selftest)
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/unknown_size/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/sizeof_allocation/unknown_size/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/taint/example/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/taint/example/script.py
new file mode 100644
index 0000000..9287534
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/taint/example/script.py
@@ -0,0 +1,71 @@
+# -*- 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/>.
+
+import gcc
+
+from sm import main
+from sm.parser import parse_file
+
+def selftest(ctxt, solution):
+    if 0:
+        import sys
+        solution.dump(sys.stderr)
+
+    # Verify the various state transitions within "test3"
+
+    # Verify that the:
+    #    D.2837_3 = fread (&tmp, 260, 1, f_2(D));
+    # transitions "tmp" from "x.start" to "x.tainted"
+    node = ctxt.find_call_of('fread', within='test3')
+    ctxt.assert_statenames_for_varname(node, 'tmp', {'x.start'})
+
+    node = ctxt.get_successor(node)
+    ctxt.assert_statenames_for_varname(node, 'tmp', {'x.tainted'})
+
+    # Verify that the:
+    #    if (D.n >= 0)
+    # transitions "D.n" from "x.tainted" to "x.has_lb"
+    node = ctxt.find_comparison_against(gcc.GeExpr, 0, within='test3')
+    tempvar = node.stmt.lhs
+    ctxt.assert_statenames_for_expr(node, tempvar, {'x.tainted'})
+
+    node = ctxt.get_true_successor(node)
+    ctxt.assert_statenames_for_expr(node, tempvar, {'x.has_lb'})
+
+    # Verify that the:
+    #    if (D.n <= 255)
+    # transitions "D.n" from "x.has_lb" to "x.ok"
+    node = ctxt.find_comparison_against(gcc.LeExpr, 255, within='test3')
+    tempvar = node.stmt.lhs
+    ctxt.assert_statenames_for_expr(node, tempvar, {'x.has_lb'})
+
+    node = ctxt.get_true_successor(node)
+    ctxt.assert_statenames_for_expr(node, tempvar, {'x.ok'})
+
+    # Verify that it's within the same equivcls as "tmp.i"
+    tmp_i = ctxt.get_expr_by_str(node, 'tmp.i')
+    ctxt.assert_statenames_for_expr(node, tmp_i, {'x.ok'})
+
+checker = parse_file('sm/checkers/taint.sm')
+#print(checker)
+dot = checker.to_dot('test_script')
+#print(dot)
+if 0:
+    from gccutils import invoke_dot
+    invoke_dot(dot)
+main([checker], selftest=selftest)
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/taint/example/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/taint/example/stderr.txt
new file mode 100644
index 0000000..f0976f0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/checkers/taint/example/stderr.txt
@@ -0,0 +1,22 @@
+tests/sm/checkers/taint/example/input.c: In function 'test':
+tests/sm/checkers/taint/example/input.c:37:nn: error: use of tainted value tmp.i in array lookup without bounds checking [CWE-129]
+tests/sm/checkers/taint/example/input.c:34:nn: note: tmp passed to fread()
+tests/sm/checkers/taint/example/input.c:37:nn: note: state of tmp ("x.tainted") propagated to tmp.i
+tests/sm/checkers/taint/example/input.c:37:nn: note: use of tainted value tmp.i in array lookup without bounds checking
+tests/sm/checkers/taint/example/input.c: In function 'test4':
+tests/sm/checkers/taint/example/input.c:68:nn: error: use of tainted value tmp.i in array lookup without upper-bound check [CWE-129]
+tests/sm/checkers/taint/example/input.c:65:nn: note: tmp passed to fread()
+tests/sm/checkers/taint/example/input.c:66:nn: note: state of tmp ("x.tainted") propagated to tmp.i
+tests/sm/checkers/taint/example/input.c:66:nn: note: tmp.i compared against 0
+tests/sm/checkers/taint/example/input.c:68:nn: note: use of tainted value tmp.i in array lookup without upper-bound check
+tests/sm/checkers/taint/example/input.c: In function 'test5':
+tests/sm/checkers/taint/example/input.c:81:nn: error: use of tainted value tmp.i in array lookup without lower-bound check [CWE-129]
+tests/sm/checkers/taint/example/input.c:78:nn: note: tmp passed to fread()
+tests/sm/checkers/taint/example/input.c:79:nn: note: state of tmp ("x.tainted") propagated to tmp.i
+tests/sm/checkers/taint/example/input.c:79:nn: note: tmp.i compared against 255
+tests/sm/checkers/taint/example/input.c:81:nn: note: use of tainted value tmp.i in array lookup without lower-bound check
+tests/sm/checkers/taint/example/input.c: In function 'test6':
+tests/sm/checkers/taint/example/input.c:101:nn: error: use of tainted value tmp.i in array lookup without bounds checking [CWE-129]
+tests/sm/checkers/taint/example/input.c:98:nn: note: tmp passed to fread()
+tests/sm/checkers/taint/example/input.c:101:nn: note: state of tmp ("x.tainted") propagated to tmp.i
+tests/sm/checkers/taint/example/input.c:101:nn: note: use of tainted value tmp.i in array lookup without bounds checking
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/dot/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/dot/input.c
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/dot/input.c
@@ -0,0 +1 @@
+
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/dot/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/dot/script.py
new file mode 100644
index 0000000..450cdd6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/dot/script.py
@@ -0,0 +1,55 @@
+# -*- 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/>.
+
+SCRIPT = '''
+sm malloc_checker {
+  state decl any_pointer ptr;
+
+  ptr.all:
+    { ptr = malloc() } =>  ptr.unknown;
+
+  ptr.unknown, ptr.null, ptr.nonnull:
+      { ptr == 0 } => true=ptr.null, false=ptr.nonnull
+    | { ptr != 0 } => true=ptr.nonnull, false=ptr.null
+    ;
+
+  ptr.unknown:
+    { *ptr } => { error('use of possibly-NULL pointer %s' % ptr)};
+
+  ptr.null:
+    { *ptr } => { error('use of NULL pointer %s' % ptr)};
+
+  ptr.all, ptr.unknown, ptr.null, ptr.nonnull:
+    { free(ptr) } => ptr.free;
+
+  ptr.free:
+      { free(ptr) } => { error('double-free of %s' % ptr)}
+    | { ptr } => {error('use-after-free of %s' % ptr)}
+    ;
+}
+'''
+
+from sm.parser import parse_file
+ch = parse_file('sm/checkers/malloc_checker.sm')
+if 0:
+    print(ch)
+dot = ch.to_dot('test_script')
+print(dot)
+if 0:
+    from gccutils import invoke_dot
+    invoke_dot(dot)
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/dot/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/dot/stdout.txt
new file mode 100644
index 0000000..15abf23
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/dot/stdout.txt
@@ -0,0 +1,83 @@
+digraph test_script {
+  subgraph malloc_checker {
+    ptr_unchecked [label=<ptr.unchecked>];
+    ptr_null [label=<ptr.null>];
+    ptr_start [label=<ptr.start>];
+    ptr_nonnull [label=<ptr.nonnull>];
+    ptr_free [label=<ptr.free>];
+
+    ptr_unchecked -> ptr_unchecked [label=<{ ptr = malloc() }>];
+    ptr_null -> ptr_unchecked [label=<{ ptr = malloc() }>];
+    ptr_start -> ptr_unchecked [label=<{ ptr = malloc() }>];
+    ptr_nonnull -> ptr_unchecked [label=<{ ptr = malloc() }>];
+    ptr_free -> ptr_unchecked [label=<{ ptr = malloc() }>];
+    ptr_unchecked -> ptr_null [label=<{ ptr = 0 }>];
+    ptr_null -> ptr_null [label=<{ ptr = 0 }>];
+    ptr_start -> ptr_null [label=<{ ptr = 0 }>];
+    ptr_nonnull -> ptr_null [label=<{ ptr = 0 }>];
+    ptr_free -> ptr_null [label=<{ ptr = 0 }>];
+    ptr_unchecked -> ptr_null [label=<{ ptr == 0 } is True>];
+    ptr_unchecked -> ptr_nonnull [label=<{ ptr == 0 } is False>];
+    ptr_unchecked -> ptr_nonnull [label=<{ ptr != 0 } is True>];
+    ptr_unchecked -> ptr_null [label=<{ ptr != 0 } is False>];
+    ptr_null -> ptr_null [label=<{ ptr == 0 } is True>];
+    ptr_null -> ptr_nonnull [label=<{ ptr == 0 } is False>];
+    ptr_null -> ptr_nonnull [label=<{ ptr != 0 } is True>];
+    ptr_null -> ptr_null [label=<{ ptr != 0 } is False>];
+    ptr_start -> ptr_null [label=<{ ptr == 0 } is True>];
+    ptr_start -> ptr_nonnull [label=<{ ptr == 0 } is False>];
+    ptr_start -> ptr_nonnull [label=<{ ptr != 0 } is True>];
+    ptr_start -> ptr_null [label=<{ ptr != 0 } is False>];
+    ptr_nonnull -> ptr_null [label=<{ ptr == 0 } is True>];
+    ptr_nonnull -> ptr_nonnull [label=<{ ptr == 0 } is False>];
+    ptr_nonnull -> ptr_nonnull [label=<{ ptr != 0 } is True>];
+    ptr_nonnull -> ptr_null [label=<{ ptr != 0 } is False>];
+    ptr_free -> ptr_null [label=<{ ptr == 0 } is True>];
+    ptr_free -> ptr_nonnull [label=<{ ptr == 0 } is False>];
+    ptr_free -> ptr_nonnull [label=<{ ptr != 0 } is True>];
+    ptr_free -> ptr_null [label=<{ ptr != 0 } is False>];
+    ptr_unchecked -> ptr_unchecked [label=<{ *ptr }: 
+            error(&apos;dereference of possibly-NULL pointer %s&apos; % ptr,
+                  # &quot;CWE-690: Unchecked Return Value to NULL Pointer Dereference&quot;
+                  cwe=&apos;CWE-690&apos;)
+         >];
+    ptr_unchecked -> ptr_nonnull [label=<{ *ptr }>];
+    ptr_null -> ptr_null [label=<{ *ptr }: 
+            error(&apos;dereference of NULL pointer %s&apos; % ptr,
+                  # &quot;CWE-690: Unchecked Return Value to NULL Pointer Dereference&quot;
+                  cwe=&apos;CWE-690&apos;)
+         >];
+    ptr_null -> ptr_stop [label=<{ *ptr }>];
+    ptr_start -> ptr_free [label=<{ free(ptr) } >];
+    ptr_unchecked -> ptr_free [label=<{ free(ptr) } >];
+    ptr_null -> ptr_free [label=<{ free(ptr) } >];
+    ptr_nonnull -> ptr_free [label=<{ free(ptr) } >];
+    ptr_free -> ptr_free [label=<{ free(ptr) } : 
+                error(&apos;double-free of %s&apos; % ptr,
+                      # &quot;CWE-415: Double Free&quot;
+                      cwe=&apos;CWE-415&apos;)
+             >];
+    ptr_free -> ptr_stop [label=<{ free(ptr) } >];
+    ptr_free -> ptr_free [label=<{ ptr }: 
+                error(&apos;use-after-free of %s&apos; % ptr,
+                      # &quot;CWE-416: Use After Free&quot;
+                      cwe=&apos;CWE-416&apos;)
+             >];
+    ptr_free -> ptr_stop [label=<{ ptr }>];
+    ptr_unchecked -> ptr_unchecked [label=<$arg_must_not_be_null$: 
+            error(&apos;possibly-NULL pointer %s passed as argument %i to %s&apos;
+                  % (ptr, argnumber, function),
+                  # &quot;CWE-690: Unchecked Return Value to NULL Pointer Dereference&quot;
+                  cwe=&apos;CWE-690&apos;)
+          >];
+    ptr_unchecked -> ptr_nonnull [label=<$arg_must_not_be_null$>];
+    ptr_null -> ptr_null [label=<$arg_must_not_be_null$: 
+            error(&apos;NULL pointer %s passed as argument %i to %s&apos;
+                  % (ptr, argnumber, function),
+                  # &quot;CWE-690: Unchecked Return Value to NULL Pointer Dereference&quot;
+                  cwe=&apos;CWE-690&apos;)
+         >];
+    ptr_null -> ptr_stop [label=<$arg_must_not_be_null$>];
+  }
+}
+
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/input.c
new file mode 100644
index 0000000..5a01138
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/input.c
@@ -0,0 +1,56 @@
+/*
+   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>
+
+static void *f(int a)
+{
+  if (a) {
+    void *p = malloc(4096);
+    return p;
+  } else {
+    return NULL;
+  }
+}
+
+static void g(int b, void *q)
+{
+  if (b==2) {
+    g(b, q); /* contrived infinite recursion */
+  }
+  free(q);
+}
+
+void h(int c)
+{
+  int *r = (int*)f(c);
+  r[0] = 42; /* BUG: the malloc in f could have failed */
+  g(c, r);
+  free(r); /* BUG: doublefree here, given that g frees the ptr */
+}
+
+static int factorial(int n)
+{
+  if (n < 2) {
+    return 1;
+  } else {
+    return n * factorial(n-1);
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/script.py
new file mode 100644
index 0000000..d6390c5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/script.py
@@ -0,0 +1,23 @@
+# -*- 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/interprocedural/complex/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/stderr.txt
new file mode 100644
index 0000000..c219105
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/stderr.txt
@@ -0,0 +1,13 @@
+tests/sm/interprocedural/complex/input.c: In function 'h':
+tests/sm/interprocedural/complex/input.c:44:nn: error: dereference of NULL pointer r [CWE-690]
+tests/sm/interprocedural/complex/input.c:44:nn: note: dereference of NULL pointer r
+tests/sm/interprocedural/complex/input.c:44:nn: error: dereference of possibly-NULL pointer r [CWE-690]
+tests/sm/interprocedural/complex/input.c:44:nn: note: dereference of possibly-NULL pointer r
+tests/sm/interprocedural/complex/input.c:46:nn: error: double-free of r [CWE-415]
+tests/sm/interprocedural/complex/input.c:44:nn: note: dereference of r
+tests/sm/interprocedural/complex/input.c:38:nn: note: q passed to free()
+tests/sm/interprocedural/complex/input.c:46:nn: note: double-free of r
+tests/sm/interprocedural/complex/input.c:46:nn: error: use-after-free of r [CWE-416]
+tests/sm/interprocedural/complex/input.c:44:nn: note: dereference of r
+tests/sm/interprocedural/complex/input.c:38:nn: note: q passed to free()
+tests/sm/interprocedural/complex/input.c:46:nn: note: use-after-free of r
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/complex/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/input.c
new file mode 100644
index 0000000..062cf99
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/input.c
@@ -0,0 +1,34 @@
+/*
+   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>
+
+static void uses_ptr(int *q)
+{
+  /* BUG: ptr came from unchecked malloc in test() */
+  *q = 42;
+}
+
+int *test(void)
+{
+  int *p = (int*)malloc(sizeof(int));
+  uses_ptr(p);
+  return p;
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/script.py
new file mode 100644
index 0000000..78a1740
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/script.py
@@ -0,0 +1,33 @@
+# -*- 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
+
+def selftest(ctxt, solution):
+    if 0:
+        import sys
+        solution.dump(sys.stderr)
+
+    # Verify that uses_ptr()'s "q" param  inherits "ptr.unchecked"
+    # from the argument "p"
+    node = ctxt.find_implementation_of('uses_ptr')
+    ctxt.assert_statenames_for_varname(node, 'q', {'ptr.unchecked'})
+
+checker = parse_file('sm/checkers/malloc_checker.sm')
+main([checker], selftest=selftest)
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/stderr.txt
new file mode 100644
index 0000000..4f59744
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/stderr.txt
@@ -0,0 +1,5 @@
+tests/sm/interprocedural/param-inherits-from-arg/input.c: In function 'uses_ptr':
+tests/sm/interprocedural/param-inherits-from-arg/input.c:26:nn: error: dereference of possibly-NULL pointer q [CWE-690]
+tests/sm/interprocedural/param-inherits-from-arg/input.c:31:nn: note: p assigned to the result of malloc()
+tests/sm/interprocedural/param-inherits-from-arg/input.c:32:nn: note: call from test() to uses_ptr(): state of p ("ptr.unchecked") propagated to q
+tests/sm/interprocedural/param-inherits-from-arg/input.c:26:nn: note: dereference of possibly-NULL pointer q
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/interprocedural/param-inherits-from-arg/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-f.c b/gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-f.c
new file mode 100644
index 0000000..3a2360c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-f.c
@@ -0,0 +1,34 @@
+/*
+   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>
+
+#include "test.h"
+
+void *f(int a)
+{
+  if (a) {
+    void *p = malloc(4096);
+    return p;
+  } else {
+    return NULL;
+  }
+}
+
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-g.c b/gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-g.c
new file mode 100644
index 0000000..816527d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-g.c
@@ -0,0 +1,31 @@
+/*
+   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>
+
+#include "test.h"
+
+void g(int b, void *q)
+{
+  if (b==2) {
+    g(b, q); /* contrived infinite recursion */
+  }
+  free(q);
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-h.c b/gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-h.c
new file mode 100644
index 0000000..0ae7fd3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/lto/input-h.c
@@ -0,0 +1,31 @@
+/*
+   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>
+
+#include "test.h"
+
+void h(int c)
+{
+  int *r = (int*)f(c);
+  r[0] = 42; /* BUG: the malloc in f could have failed */
+  g(c, r);
+  free(r); /* BUG: doublefree here, given that g frees the ptr */
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/lto/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/lto/script.py
new file mode 100644
index 0000000..e0cf5db
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/lto/script.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+#   Copyright 2012, 2013 David Malcolm <dmalcolm@redhat.com>
+#   Copyright 2012, 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/>.
+
+from sm import main
+from sm.options import Options
+from sm.parser import parse_file
+
+opt = Options(during_lto=True)
+checker = parse_file('sm/checkers/malloc_checker.sm')
+main([checker], options=opt)
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/lto/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/lto/stderr.txt
new file mode 100644
index 0000000..28afeb9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/lto/stderr.txt
@@ -0,0 +1,29 @@
+tests/sm/lto/input-g.c: In function 'g':
+tests/sm/lto/input-g.c:30:7: error: double-free of q [CWE-415]
+tests/sm/lto/input-g.c:30:7: note: q passed to free()
+tests/sm/lto/input-g.c:28:6: note: return from g() to g()
+tests/sm/lto/input-g.c:30:7: note: double-free of q
+tests/sm/lto/input-g.c:30:7: error: use-after-free of q [CWE-416]
+tests/sm/lto/input-g.c:30:7: note: q passed to free()
+tests/sm/lto/input-g.c:28:6: note: return from g() to g()
+tests/sm/lto/input-g.c:30:7: note: use-after-free of q
+tests/sm/lto/input-h.c: In function 'h':
+tests/sm/lto/input-h.c:28:8: error: dereference of NULL pointer r [CWE-690]
+tests/sm/lto/input-f.c:31:5: note: return value assigned to 0
+tests/sm/lto/input-h.c:27:8: note: return from f() to h(): state of return value ("ptr.null") propagated to r
+tests/sm/lto/input-h.c:28:8: note: dereference of NULL pointer r
+tests/sm/lto/input-h.c:28:8: error: dereference of possibly-NULL pointer r [CWE-690]
+tests/sm/lto/input-f.c:28:11: note: p assigned to the result of malloc()
+tests/sm/lto/input-h.c:27:8: note: return from f() to h(): state of return value ("ptr.unchecked") propagated to r
+tests/sm/lto/input-h.c:28:8: note: dereference of possibly-NULL pointer r
+tests/sm/lto/input-h.c:30:7: error: double-free of r [CWE-415]
+tests/sm/lto/input-g.c:30:7: note: q passed to free()
+tests/sm/lto/input-h.c:29:4: note: return from g() to h(): state of q ("ptr.free") propagated to r
+tests/sm/lto/input-h.c:30:7: note: double-free of r
+tests/sm/lto/input-h.c:30:7: error: use-after-free of r [CWE-416]
+tests/sm/lto/input-g.c:30:7: note: q passed to free()
+tests/sm/lto/input-h.c:29:4: note: return from g() to h(): state of q ("ptr.free") propagated to r
+tests/sm/lto/input-h.c:30:7: note: use-after-free of r
+lto-wrapper: gcc returned 1 exit status
+/usr/bin/ld: lto-wrapper failed
+collect2: error: ld returned 1 exit status
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/lto/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/lto/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/lto/test.h b/gcc/testsuite/gcc.dg/TODO-analyzer/lto/test.h
new file mode 100644
index 0000000..f5825c8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/lto/test.h
@@ -0,0 +1,22 @@
+/*
+   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/>.
+*/
+
+extern void *f(int a);
+extern void g(int b, void *q);
+extern void h(int c);
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/parser/malloc-checker/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/parser/malloc-checker/input.c
new file mode 100644
index 0000000..aafcadd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/parser/malloc-checker/input.c
@@ -0,0 +1,91 @@
+/*
+   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>
+
+#if 1
+void double_free(void *ptr)
+{
+  free(ptr);
+
+  /* BUG: double-free: */
+  free(ptr);
+}
+#endif
+
+
+#if 1
+void unchecked_malloc(void)
+{
+  void *ptr = malloc(4096);
+  memset(ptr, 0, 4096);
+}
+#endif
+
+#if 1
+int correct_usage(void)
+{
+  void *ptr = malloc(4096);
+  if (!ptr)
+    return -1; /* FIXME: with a plain return we have a BB with no gimple, and that breaks my checker */
+  memset(ptr, 0, 4096);
+  free(ptr);
+  return 0;
+}
+#endif
+
+#if 1
+int two_ptrs(void)
+{
+  void *p = malloc(4096);
+  void *q = malloc(4096);
+  if (p) {
+    memset(p, 0, 4096); /* Not a bug: checked */
+  } else {
+    memset(q, 0, 4096); /* BUG: not checked */
+  }
+  free(p);
+  free(q);
+  return 0;
+}
+#endif
+
+#if 1
+void fancy_control_flow(int i, int j)
+{
+  int k;
+  void *ptr;
+  for (k = i; k < j; k++) {
+    switch(k) {
+    case 0:
+      ptr = malloc(1024);
+      break;
+    case 1:
+      break;
+    case 2:
+      break;
+    default:
+      break;
+    }
+  }
+  memset(ptr, 0, 4096);
+  free(ptr);
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/parser/malloc-checker/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/parser/malloc-checker/script.py
new file mode 100644
index 0000000..a75230a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/parser/malloc-checker/script.py
@@ -0,0 +1,189 @@
+# -*- 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/>.
+
+import unittest
+
+from sm.checker import Checker, Sm, AnyPointer, StateClause, PatternRule, \
+    ResultOfFnCall, ArgsOfFnCall, Comparison, VarDereference, VarUsage, \
+    TransitionTo, BooleanOutcome, PythonOutcome
+from sm.parser import parse_string
+
+#parse('tests/sm/parser/malloc-checker.sm')
+
+class ParserTests(unittest.TestCase):
+    def test_complex_example(self):
+        ch = parse_string('''
+sm malloc_checker {
+  stateful decl any_pointer ptr;
+
+  ptr.all:
+    { ptr = malloc() } =>  ptr.unknown;
+
+  ptr.unknown, ptr.null, ptr.nonnull:
+      { ptr == 0 } => true=ptr.null, false=ptr.nonnull
+    | { ptr != 0 } => true=ptr.nonnull, false=ptr.null
+    ;
+
+  ptr.unknown:
+    { *ptr } => {{ error('use of possibly-NULL pointer %s' % ptr) }};
+
+  ptr.null:
+    { *ptr } => {{ error('use of NULL pointer %s' % ptr) }};
+
+  ptr.all, ptr.unknown, ptr.null, ptr.nonnull:
+    { free(ptr) } => ptr.free;
+
+  ptr.free:
+      { free(ptr) } => {{ error('double-free of %s' % ptr) }}
+    | { ptr } => {{ error('use-after-free of %s' % ptr) }}
+    ;
+}
+''')
+        self.assert_(isinstance(ch, Checker))
+
+        # Verify that the str() of the parsed Checker looks sane:
+        self.assertEqual(str(ch).splitlines(),
+'''sm malloc_checker {
+  stateful decl any_pointer ptr;
+
+  ptr.all:
+    { ptr = malloc() } => ptr.unknown;
+
+  ptr.unknown, ptr.null, ptr.nonnull:
+    { ptr == 0 } => true=ptr.null, false=ptr.nonnull;
+    | { ptr != 0 } => true=ptr.nonnull, false=ptr.null;
+
+  ptr.unknown:
+    { *ptr } => {{ error('use of possibly-NULL pointer %s' % ptr) }};
+
+  ptr.null:
+    { *ptr } => {{ error('use of NULL pointer %s' % ptr) }};
+
+  ptr.all, ptr.unknown, ptr.null, ptr.nonnull:
+    { free(ptr) }  => ptr.free;
+
+  ptr.free:
+    { free(ptr) }  => {{ error('double-free of %s' % ptr) }};
+    | { ptr } => {{ error('use-after-free of %s' % ptr) }};
+
+}'''.splitlines())
+
+        self.assertEqual(len(ch.sms), 1)
+        sm = ch.sms[0]
+        self.assertEqual(sm.name, 'malloc_checker')
+        self.assertEqual(len(sm.clauses), 7)
+
+        decl = sm.clauses[0]
+        self.assertEqual(decl, AnyPointer(has_state=True, name='ptr'))
+
+        # Verify parsing of:
+        #   ptr.all:
+        #     { ptr = malloc() } =>  ptr.unknown;
+        sc = sm.clauses[1]
+        self.assertEqual(sc.statelist, ['ptr.all'])
+        self.assertEqual(len(sc.patternrulelist), 1)
+        pr = sc.patternrulelist[0]
+        self.assertEqual(pr.pattern,
+                         ResultOfFnCall(lhs='ptr',
+                                        fnname='malloc',
+                                        args=[]))
+        self.assertEqual(pr.outcomes,
+                         [TransitionTo(statename='ptr.unknown')])
+
+        # Verify parsing of:
+        #   ptr.unknown, ptr.null, ptr.nonnull:
+        #       { ptr == 0 } => true=ptr.null, false=ptr.nonnull
+        #     | { ptr != 0 } => true=ptr.nonnull, false=ptr.null
+        #     ;
+        sc = sm.clauses[2]
+        self.assertEqual(sc.statelist,
+                         ['ptr.unknown', 'ptr.null', 'ptr.nonnull'])
+        self.assertEqual(len(sc.patternrulelist), 2)
+        pr = sc.patternrulelist[0]
+        self.assertEqual(pr.pattern, Comparison(lhs='ptr', op='==', rhs=0))
+        self.assertEqual(pr.outcomes,
+                         [BooleanOutcome(guard=True, outcome=TransitionTo(statename='ptr.null')),
+                          BooleanOutcome(guard=False, outcome=TransitionTo(statename='ptr.nonnull'))])
+        pr = sc.patternrulelist[1]
+        self.assertEqual(pr.pattern, Comparison(lhs='ptr', op='!=', rhs=0))
+        self.assertEqual(pr.outcomes,
+                         [BooleanOutcome(guard=True, outcome=TransitionTo(statename='ptr.nonnull')),
+                          BooleanOutcome(guard=False, outcome=TransitionTo(statename='ptr.null'))])
+
+
+        # Verify parsing of:
+        #   ptr.unknown:
+        #     { *ptr } => {{ error('use of possibly-NULL pointer %s' % ptr) }};
+        sc = sm.clauses[3]
+        self.assertEqual(sc.statelist,
+                         ['ptr.unknown'])
+        self.assertEqual(len(sc.patternrulelist), 1)
+        pr = sc.patternrulelist[0]
+        self.assertEqual(pr.pattern, VarDereference(var='ptr'))
+        self.assertEqual(pr.outcomes,
+                         [PythonOutcome(src=(" error('use of possibly-NULL pointer %s' % ptr) "),
+                                        lineoffset=13)])
+
+        # Verify parsing of:
+        #   ptr.null:
+        #     { *ptr } => {{ error('use of NULL pointer %s' % ptr) }};
+        sc = sm.clauses[4]
+        self.assertEqual(sc.statelist,
+                         ['ptr.null'])
+        self.assertEqual(len(sc.patternrulelist), 1)
+        pr = sc.patternrulelist[0]
+        self.assertEqual(pr.pattern, VarDereference(var='ptr'))
+        self.assertEqual(pr.outcomes,
+                         [PythonOutcome(src=(" error('use of NULL pointer %s' % ptr) "),
+                                        lineoffset=16)])
+
+        # Verify parsing of:
+        #   ptr.all, ptr.unknown, ptr.null, ptr.nonnull:
+        #     { free(ptr) } => ptr.free;
+        sc = sm.clauses[5]
+        self.assertEqual(sc.statelist,
+                         ['ptr.all', 'ptr.unknown', 'ptr.null', 'ptr.nonnull'])
+        self.assertEqual(len(sc.patternrulelist), 1)
+        pr = sc.patternrulelist[0]
+        self.assertEqual(pr.pattern, ArgsOfFnCall(fnname='free', args=['ptr']))
+        self.assertEqual(pr.outcomes,
+                         [TransitionTo(statename='ptr.free')])
+
+        # Verify parsing of:
+        #   ptr.free:
+        #     { free(ptr) } => {{ error('double-free of %s' % ptr) }}
+        #     | { ptr } => {{ error('use-after-free of %s' % ptr) }}
+        sc = sm.clauses[6]
+        self.assertEqual(sc.statelist,
+                         ['ptr.free'])
+        self.assertEqual(len(sc.patternrulelist), 2)
+        pr = sc.patternrulelist[0]
+        self.assertEqual(pr.pattern, ArgsOfFnCall(fnname='free', args=['ptr']))
+        self.assertEqual(pr.outcomes,
+                         [PythonOutcome(src=" error('double-free of %s' % ptr) ",
+                                        lineoffset=22)])
+        pr = sc.patternrulelist[1]
+        self.assertEqual(pr.pattern, VarUsage(var='ptr'))
+        self.assertEqual(pr.outcomes,
+                         [PythonOutcome(src=" error('use-after-free of %s' % ptr) ",
+                                        lineoffset=23)])
+
+import sys
+sys.argv = ['foo', '-v']
+
+unittest.main()
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/input.c
new file mode 100644
index 0000000..04a6880
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/input.c
@@ -0,0 +1,25 @@
+/*
+   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/>.
+*/
+
+extern void test2(void *);
+
+void test1(void *ptr)
+{
+  test2(ptr);
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/script.py
new file mode 100644
index 0000000..b9f41c2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/script.py
@@ -0,0 +1,32 @@
+# -*- 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_string
+
+SCRIPT = '''
+sm arg_of_fn_call {
+  stateful decl any_pointer ptr;
+
+  ptr.*:
+    { test2(ptr) } => {{ error('test2() was called with %s' % ptr) }};
+}
+'''
+
+checker = parse_string(SCRIPT)
+main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/stderr.txt
new file mode 100644
index 0000000..1c2cba2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/stderr.txt
@@ -0,0 +1,2 @@
+tests/sm/patterns/arg-of-fn-call/input.c: In function 'test1':
+tests/sm/patterns/arg-of-fn-call/input.c:24:nn: error: test2() was called with ptr
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg-of-fn-call/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/input.c
new file mode 100644
index 0000000..fdef90c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/input.c
@@ -0,0 +1,44 @@
+/*
+   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>
+
+extern void foo(void *ptrA, void *ptrB, void *ptrC)
+  __attribute__((nonnull (1, 3)));
+
+extern void bar(void *ptrA, void *ptrB, void *ptrC)
+  __attribute__((nonnull (1, 3)));
+
+void foo(void *ptrA, void *ptrB, void *ptrC)
+{
+}
+
+void test(void)
+{
+  void *p;
+  void *q;
+  void *r;
+
+  p = NULL;
+  q = NULL;
+  r = NULL;
+
+  foo(p, q, r);
+  bar(p, q, r);
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/script.py
new file mode 100644
index 0000000..a270eb0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/script.py
@@ -0,0 +1,39 @@
+# -*- 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_string
+
+SCRIPT = '''
+sm example_usage_of_nonnull_arg {
+  stateful decl any_pointer ptr;
+
+  ptr.*:
+    { ptr = 0 } => ptr.null;
+
+  ptr.null:
+    $arg_must_not_be_null$
+      => {{
+            error('%s() was called with NULL %s as argument %i/index %i: %r'
+                  % (function, ptr, argnumber, argindex, parameter))
+         }};
+}
+'''
+
+checker = parse_string(SCRIPT)
+main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/stderr.txt
new file mode 100644
index 0000000..554a1aa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/stderr.txt
@@ -0,0 +1,21 @@
+tests/sm/patterns/arg_must_not_be_null/input.c: In function 'test':
+tests/sm/patterns/arg_must_not_be_null/input.c:42:nn: error: foo() was called with NULL p as argument 1/index 0: gcc.ParmDecl('ptrA')
+tests/sm/patterns/arg_must_not_be_null/input.c:38:nn: note: p assigned to 0
+tests/sm/patterns/arg_must_not_be_null/input.c:42:nn: note: call from test() to foo(): state of p ("ptr.null") propagated to ptrA
+tests/sm/patterns/arg_must_not_be_null/input.c:42:nn: note: return from foo() to test(): state of ptrA ("ptr.null") propagated to p
+tests/sm/patterns/arg_must_not_be_null/input.c:42:nn: note: foo() was called with NULL p as argument 1/index 0: gcc.ParmDecl('ptrA')
+tests/sm/patterns/arg_must_not_be_null/input.c:42:nn: error: foo() was called with NULL r as argument 3/index 2: gcc.ParmDecl('ptrC')
+tests/sm/patterns/arg_must_not_be_null/input.c:40:nn: note: r assigned to 0
+tests/sm/patterns/arg_must_not_be_null/input.c:42:nn: note: call from test() to foo(): state of p ("ptr.null") propagated to ptrC
+tests/sm/patterns/arg_must_not_be_null/input.c:42:nn: note: return from foo() to test(): state of ptrC ("ptr.null") propagated to r
+tests/sm/patterns/arg_must_not_be_null/input.c:42:nn: note: foo() was called with NULL r as argument 3/index 2: gcc.ParmDecl('ptrC')
+tests/sm/patterns/arg_must_not_be_null/input.c:43:nn: error: bar() was called with NULL p as argument 1/index 0: None
+tests/sm/patterns/arg_must_not_be_null/input.c:40:nn: note: r assigned to 0
+tests/sm/patterns/arg_must_not_be_null/input.c:42:nn: note: call from test() to foo(): state of p ("ptr.null") propagated to ptrA
+tests/sm/patterns/arg_must_not_be_null/input.c:42:nn: note: return from foo() to test(): state of ptrA ("ptr.null") propagated to p
+tests/sm/patterns/arg_must_not_be_null/input.c:43:nn: note: bar() was called with NULL p as argument 1/index 0: None
+tests/sm/patterns/arg_must_not_be_null/input.c:43:nn: error: bar() was called with NULL r as argument 3/index 2: None
+tests/sm/patterns/arg_must_not_be_null/input.c:39:nn: note: q assigned to 0
+tests/sm/patterns/arg_must_not_be_null/input.c:42:nn: note: call from test() to foo(): state of p ("ptr.null") propagated to ptrC
+tests/sm/patterns/arg_must_not_be_null/input.c:42:nn: note: return from foo() to test(): state of ptrC ("ptr.null") propagated to r
+tests/sm/patterns/arg_must_not_be_null/input.c:43:nn: note: bar() was called with NULL r as argument 3/index 2: None
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/arg_must_not_be_null/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/input.c
new file mode 100644
index 0000000..6c912b7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/input.c
@@ -0,0 +1,25 @@
+/*
+   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/>.
+*/
+
+extern void test2(void *param0, int param1, char param2);
+
+void test1(void *ptr, int i)
+{
+  test2(ptr, 0, 'A');
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/script.py
new file mode 100644
index 0000000..691e9a4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/script.py
@@ -0,0 +1,34 @@
+# -*- 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_string
+
+SCRIPT = '''
+sm arg_of_fn_call {
+  stateful decl any_pointer arg0;
+  decl any_expr arg1;
+  decl any_expr arg2;
+
+  arg0.*:
+    { test2(arg0, arg1, arg2) } => {{ error('test2() was called with arg0:%s arg1:%s arg2:%s' % (arg0, arg1, arg2)) }};
+}
+'''
+
+checker = parse_string(SCRIPT)
+main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/stderr.txt
new file mode 100644
index 0000000..798cb97
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/stderr.txt
@@ -0,0 +1,2 @@
+tests/sm/patterns/args-of-fn-call/input.c: In function 'test1':
+tests/sm/patterns/args-of-fn-call/input.c:24:nn: error: test2() was called with arg0:ptr arg1:0 arg2:65
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/args-of-fn-call/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/input.c
new file mode 100644
index 0000000..6ce27ac
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/input.c
@@ -0,0 +1,25 @@
+/*
+   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/>.
+*/
+
+void test1(void)
+{
+  char buf[256];
+
+  buf[0] = 42;
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/script.py
new file mode 100644
index 0000000..4f72951
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/script.py
@@ -0,0 +1,33 @@
+# -*- 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_string
+
+SCRIPT = '''
+sm array_access {
+  decl any_expr idx;
+  stateful decl any_expr arr;
+
+  arr.*:
+    { arr[idx] } => {{ error('lookup within array "%s" at index %s' % (arr, idx)) }};
+}
+'''
+
+checker = parse_string(SCRIPT)
+main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/stderr.txt
new file mode 100644
index 0000000..ff30854
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/stderr.txt
@@ -0,0 +1,2 @@
+tests/sm/patterns/array/input.c: In function 'test1':
+tests/sm/patterns/array/input.c:24:nn: error: lookup within array "buf" at index 0
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/array/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/input.c
new file mode 100644
index 0000000..a6f25ed
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/input.c
@@ -0,0 +1,30 @@
+/*
+   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/>.
+*/
+
+extern void foo(void *);
+extern void bar(void *);
+
+void test1(void *ptr)
+{
+  if (ptr) {
+    foo(ptr);
+  } else {
+    bar(ptr);
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/script.py
new file mode 100644
index 0000000..63b19b7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/script.py
@@ -0,0 +1,34 @@
+# -*- 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_string
+
+SCRIPT = '''
+sm checked_against_null {
+  stateful decl any_pointer ptr;
+
+  pat checked_against_null { ptr != 0 };
+
+  ptr.*:
+    checked_against_null => {{ error('%s was checked against NULL' % ptr) }};
+}
+'''
+
+checker = parse_string(SCRIPT)
+main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/stderr.txt
new file mode 100644
index 0000000..4802151
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/stderr.txt
@@ -0,0 +1,2 @@
+tests/sm/patterns/named-pattern/input.c: In function 'test1':
+tests/sm/patterns/named-pattern/input.c:25:nn: error: ptr was checked against NULL
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/named-pattern/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/input.c
new file mode 100644
index 0000000..bb9c38c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/input.c
@@ -0,0 +1,39 @@
+/*
+   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/>.
+*/
+
+extern void foo(void *);
+extern void bar(void *);
+
+void test1(void *ptr)
+{
+  if (ptr) {
+    foo(ptr);
+  } else {
+    bar(ptr);
+  }
+}
+
+void test2(void *ptr)
+{
+  if (!ptr) {
+    foo(ptr);
+  } else {
+    bar(ptr);
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/script.py
new file mode 100644
index 0000000..833ef78
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/script.py
@@ -0,0 +1,32 @@
+# -*- 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_string
+
+SCRIPT = '''
+sm checked_against_null {
+  stateful decl any_pointer ptr;
+
+  ptr.*:
+    { ptr != 0 } | {ptr == 0 } => {{ error('%s was checked against NULL' % ptr) }};
+}
+'''
+
+checker = parse_string(SCRIPT)
+main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/stderr.txt
new file mode 100644
index 0000000..2eb5619
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/stderr.txt
@@ -0,0 +1,4 @@
+tests/sm/patterns/or/input.c: In function 'test1':
+tests/sm/patterns/or/input.c:25:nn: error: ptr was checked against NULL
+tests/sm/patterns/or/input.c: In function 'test2':
+tests/sm/patterns/or/input.c:34:nn: error: ptr was checked against NULL
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/or/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/input.c
new file mode 100644
index 0000000..b39c880
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/input.c
@@ -0,0 +1,30 @@
+/*
+   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/>.
+*/
+
+extern void *never_call_this(void);
+
+void test1(void)
+{
+  void *ptr = never_call_this();
+}
+
+void test2(void)
+{
+  /* empty */
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/script.py
new file mode 100644
index 0000000..eea7ef8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/script.py
@@ -0,0 +1,32 @@
+# -*- 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_string
+
+SCRIPT = '''
+sm result_of_fn_call {
+  stateful decl any_pointer ptr;
+
+  ptr.*:
+    { ptr = never_call_this() } => {{ error('never_call_this was called %s' % ptr) }};
+}
+'''
+
+checker = parse_string(SCRIPT)
+main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/stderr.txt
new file mode 100644
index 0000000..ff49839
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/stderr.txt
@@ -0,0 +1,2 @@
+tests/sm/patterns/result-of-fn-call/input.c: In function 'test1':
+tests/sm/patterns/result-of-fn-call/input.c:24:nn: error: never_call_this was called ptr
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/result-of-fn-call/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/input.c
new file mode 100644
index 0000000..346f675
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/input.c
@@ -0,0 +1,32 @@
+/*
+   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/>.
+*/
+
+int test1(int *p)
+{
+  return *p;
+}
+
+struct foo {
+  int i;
+};
+
+int test2(struct foo *f)
+{
+  return f->i;
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/script.py
new file mode 100644
index 0000000..4e29c27
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/script.py
@@ -0,0 +1,32 @@
+# -*- 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_string
+
+SCRIPT = '''
+sm var_dereference {
+  stateful decl any_pointer ptr;
+
+  ptr.*:
+    { *ptr } => {{ error('%s was dereferenced' % ptr) }};
+}
+'''
+
+checker = parse_string(SCRIPT)
+main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/stderr.txt
new file mode 100644
index 0000000..65da25e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/stderr.txt
@@ -0,0 +1,4 @@
+tests/sm/patterns/var-dereference/input.c: In function 'test1':
+tests/sm/patterns/var-dereference/input.c:22:nn: error: p was dereferenced
+tests/sm/patterns/var-dereference/input.c: In function 'test2':
+tests/sm/patterns/var-dereference/input.c:31:nn: error: f was dereferenced
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-dereference/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/input.c
new file mode 100644
index 0000000..3518530
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/input.c
@@ -0,0 +1,23 @@
+/*
+   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/>.
+*/
+
+void *test1(void *ptr)
+{
+  return ptr;
+}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/script.py
new file mode 100644
index 0000000..013cf5d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/script.py
@@ -0,0 +1,34 @@
+# -*- 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_string
+
+SCRIPT = '''
+sm result_of_fn_call {
+  stateful decl any_pointer ptr;
+
+  ptr.*:
+    { ptr } => {{ error('%s was used' % ptr) }};
+}
+'''
+
+# TODO: for now this picks up on uses of temporaries
+
+checker = parse_string(SCRIPT)
+main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/stderr.txt
new file mode 100644
index 0000000..f253b50
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/stderr.txt
@@ -0,0 +1,3 @@
+tests/sm/patterns/var-usage/input.c: In function 'test1':
+tests/sm/patterns/var-usage/input.c:22:nn: error: ptr was used
+tests/sm/patterns/var-usage/input.c:22:nn: error: return value was used
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/var-usage/stdout.txt
new file mode 100644
index 0000000..e69de29
-- 
1.8.5.3

