diff mbox series

[41/41] analyzer: test suite

Message ID 20200108090302.2425-42-dmalcolm@redhat.com
State New
Headers show
Series v5 of analyzer patch kit | expand

Commit Message

David Malcolm Jan. 8, 2020, 9:03 a.m. UTC
Needs review.

Changed in v5:
- updated for removal of analyzer-specific builtins:
  https://gcc.gnu.org/ml/gcc-patches/2019-12/msg01310.html

Changed in v4:
- more tests, including a test for .dot output and an LTO test
- update for change from "--analyzer" to "-fanalyzer"

This patch adds the testsuite for the analyzer.

gcc/testsuite/ChangeLog:
	* gcc.dg/analyzer/CVE-2005-1689-minimal.c: New test.
	* gcc.dg/analyzer/abort.c: New test.
	* gcc.dg/analyzer/alloca-leak.c: New test.
	* gcc.dg/analyzer/analyzer-decls.h: New header.
	* gcc.dg/analyzer/analyzer-verbosity-0.c: New test.
	* gcc.dg/analyzer/analyzer-verbosity-1.c: New test.
	* gcc.dg/analyzer/analyzer-verbosity-2.c: New test.
	* gcc.dg/analyzer/analyzer.exp: New suite.
	* gcc.dg/analyzer/attribute-nonnull.c: New test.
	* gcc.dg/analyzer/call-summaries-1.c: New test.
	* gcc.dg/analyzer/conditionals-2.c: New test.
	* gcc.dg/analyzer/conditionals-3.c: New test.
	* gcc.dg/analyzer/conditionals-notrans.c: New test.
	* gcc.dg/analyzer/conditionals-trans.c: New test.
	* gcc.dg/analyzer/data-model-1.c: New test.
	* gcc.dg/analyzer/data-model-2.c: New test.
	* gcc.dg/analyzer/data-model-3.c: New test.
	* gcc.dg/analyzer/data-model-4.c: New test.
	* gcc.dg/analyzer/data-model-5.c: New test.
	* gcc.dg/analyzer/data-model-5b.c: New test.
	* gcc.dg/analyzer/data-model-5c.c: New test.
	* gcc.dg/analyzer/data-model-5d.c: New test.
	* gcc.dg/analyzer/data-model-6.c: New test.
	* gcc.dg/analyzer/data-model-7.c: New test.
	* gcc.dg/analyzer/data-model-8.c: New test.
	* gcc.dg/analyzer/data-model-9.c: New test.
	* gcc.dg/analyzer/data-model-11.c: New test.
	* gcc.dg/analyzer/data-model-12.c: New test.
	* gcc.dg/analyzer/data-model-13.c: New test.
	* gcc.dg/analyzer/data-model-14.c: New test.
	* gcc.dg/analyzer/data-model-15.c: New test.
	* gcc.dg/analyzer/data-model-16.c: New test.
	* gcc.dg/analyzer/data-model-17.c: New test.
	* gcc.dg/analyzer/data-model-18.c: New test.
	* gcc.dg/analyzer/data-model-19.c: New test.
	* gcc.dg/analyzer/data-model-path-1.c: New test.
	* gcc.dg/analyzer/disabling.c: New test.
	* gcc.dg/analyzer/dot-output.c: New test.
	* gcc.dg/analyzer/double-free-lto-1-a.c: New test.
	* gcc.dg/analyzer/double-free-lto-1-b.c: New test.
	* gcc.dg/analyzer/double-free-lto-1.h: New header.
	* gcc.dg/analyzer/equivalence.c: New test.
	* gcc.dg/analyzer/explode-1.c: New test.
	* gcc.dg/analyzer/explode-2.c: New test.
	* gcc.dg/analyzer/factorial.c: New test.
	* gcc.dg/analyzer/fibonacci.c: New test.
	* gcc.dg/analyzer/fields.c: New test.
	* gcc.dg/analyzer/file-1.c: New test.
	* gcc.dg/analyzer/file-2.c: New test.
	* gcc.dg/analyzer/function-ptr-1.c: New test.
	* gcc.dg/analyzer/function-ptr-2.c: New test.
	* gcc.dg/analyzer/function-ptr-3.c: New test.
	* gcc.dg/analyzer/gzio-2.c: New test.
	* gcc.dg/analyzer/gzio-3.c: New test.
	* gcc.dg/analyzer/gzio-3a.c: New test.
	* gcc.dg/analyzer/gzio.c: New test.
	* gcc.dg/analyzer/infinite-recursion.c: New test.
	* gcc.dg/analyzer/loop-2.c: New test.
	* gcc.dg/analyzer/loop-2a.c: New test.
	* gcc.dg/analyzer/loop-3.c: New test.
	* gcc.dg/analyzer/loop-4.c: New test.
	* gcc.dg/analyzer/loop.c: New test.
	* gcc.dg/analyzer/malloc-1.c: New test.
	* gcc.dg/analyzer/malloc-2.c: New test.
	* gcc.dg/analyzer/malloc-3.c: New test.
	* gcc.dg/analyzer/malloc-callbacks.c: New test.
	* gcc.dg/analyzer/malloc-dce.c: New test.
	* gcc.dg/analyzer/malloc-dedupe-1.c: New test.
	* gcc.dg/analyzer/malloc-ipa-1.c: New test.
	* gcc.dg/analyzer/malloc-ipa-10.c: New test.
	* gcc.dg/analyzer/malloc-ipa-11.c: New test.
	* gcc.dg/analyzer/malloc-ipa-12.c: New test.
	* gcc.dg/analyzer/malloc-ipa-13.c: New test.
	* gcc.dg/analyzer/malloc-ipa-2.c: New test.
	* gcc.dg/analyzer/malloc-ipa-3.c: New test.
	* gcc.dg/analyzer/malloc-ipa-4.c: New test.
	* gcc.dg/analyzer/malloc-ipa-5.c: New test.
	* gcc.dg/analyzer/malloc-ipa-6.c: New test.
	* gcc.dg/analyzer/malloc-ipa-7.c: New test.
	* gcc.dg/analyzer/malloc-ipa-8-double-free.c: New test.
	* gcc.dg/analyzer/malloc-ipa-8-lto-a.c: New test.
	* gcc.dg/analyzer/malloc-ipa-8-lto-b.c: New test.
	* gcc.dg/analyzer/malloc-ipa-8-lto-c.c: New test.
	* gcc.dg/analyzer/malloc-ipa-8-lto.h: New test.
	* gcc.dg/analyzer/malloc-ipa-8-unchecked.c: New test.
	* gcc.dg/analyzer/malloc-ipa-9.c: New test.
	* gcc.dg/analyzer/malloc-macro-inline-events.c: New test.
	* gcc.dg/analyzer/malloc-macro-separate-events.c: New test.
	* gcc.dg/analyzer/malloc-macro.h: New header.
	* gcc.dg/analyzer/malloc-many-paths-1.c: New test.
	* gcc.dg/analyzer/malloc-many-paths-2.c: New test.
	* gcc.dg/analyzer/malloc-many-paths-3.c: New test.
	* gcc.dg/analyzer/malloc-paths-1.c: New test.
	* gcc.dg/analyzer/malloc-paths-10.c: New test.
	* gcc.dg/analyzer/malloc-paths-2.c: New test.
	* gcc.dg/analyzer/malloc-paths-3.c: New test.
	* gcc.dg/analyzer/malloc-paths-4.c: New test.
	* gcc.dg/analyzer/malloc-paths-5.c: New test.
	* gcc.dg/analyzer/malloc-paths-6.c: New test.
	* gcc.dg/analyzer/malloc-paths-7.c: New test.
	* gcc.dg/analyzer/malloc-paths-8.c: New test.
	* gcc.dg/analyzer/malloc-paths-9.c: New test.
	* gcc.dg/analyzer/malloc-vs-local-1a.c: New test.
	* gcc.dg/analyzer/malloc-vs-local-1b.c: New test.
	* gcc.dg/analyzer/malloc-vs-local-2.c: New test.
	* gcc.dg/analyzer/malloc-vs-local-3.c: New test.
	* gcc.dg/analyzer/malloc-vs-local-4.c: New test.
	* gcc.dg/analyzer/operations.c: New test.
	* gcc.dg/analyzer/params-2.c: New test.
	* gcc.dg/analyzer/params.c: New test.
	* gcc.dg/analyzer/paths-1.c: New test.
	* gcc.dg/analyzer/paths-1a.c: New test.
	* gcc.dg/analyzer/paths-2.c: New test.
	* gcc.dg/analyzer/paths-3.c: New test.
	* gcc.dg/analyzer/paths-4.c: New test.
	* gcc.dg/analyzer/paths-5.c: New test.
	* gcc.dg/analyzer/paths-6.c: New test.
	* gcc.dg/analyzer/paths-7.c: New test.
	* gcc.dg/analyzer/pattern-test-1.c: New test.
	* gcc.dg/analyzer/pattern-test-2.c: New test.
	* gcc.dg/analyzer/pointer-merging.c: New test.
	* gcc.dg/analyzer/pr61861.c: New test.
	* gcc.dg/analyzer/pragma-1.c: New test.
	* gcc.dg/analyzer/scope-1.c: New test.
	* gcc.dg/analyzer/sensitive-1.c: New test.
	* gcc.dg/analyzer/setjmp-1.c: New test.
	* gcc.dg/analyzer/setjmp-2.c: New test.
	* gcc.dg/analyzer/setjmp-3.c: New test.
	* gcc.dg/analyzer/setjmp-4.c: New test.
	* gcc.dg/analyzer/setjmp-5.c: New test.
	* gcc.dg/analyzer/setjmp-6.c: New test.
	* gcc.dg/analyzer/setjmp-7.c: New test.
	* gcc.dg/analyzer/setjmp-7a.c: New test.
	* gcc.dg/analyzer/setjmp-8.c: New test.
	* gcc.dg/analyzer/setjmp-9.c: New test.
	* gcc.dg/analyzer/signal-1.c: New test.
	* gcc.dg/analyzer/signal-2.c: New test.
	* gcc.dg/analyzer/signal-3.c: New test.
	* gcc.dg/analyzer/signal-4a.c: New test.
	* gcc.dg/analyzer/signal-4b.c: New test.
	* gcc.dg/analyzer/strcmp-1.c: New test.
	* gcc.dg/analyzer/switch.c: New test.
	* gcc.dg/analyzer/taint-1.c: New test.
	* gcc.dg/analyzer/zlib-1.c: New test.
	* gcc.dg/analyzer/zlib-2.c: New test.
	* gcc.dg/analyzer/zlib-3.c: New test.
	* gcc.dg/analyzer/zlib-4.c: New test.
	* gcc.dg/analyzer/zlib-5.c: New test.
	* gcc.dg/analyzer/zlib-6.c: New test.
	* lib/gcc-defs.exp (dg-check-dot): New procedure.
	* lib/target-supports.exp (check_dot_available): New procedure.
	(check_effective_target_analyzer): New.
	* lib/target-supports-dg.exp (dg-require-dot): New procedure.
---
 .../gcc.dg/analyzer/CVE-2005-1689-minimal.c   |   30 +
 gcc/testsuite/gcc.dg/analyzer/abort.c         |   72 ++
 gcc/testsuite/gcc.dg/analyzer/alloca-leak.c   |    8 +
 .../gcc.dg/analyzer/analyzer-decls.h          |   36 +
 .../gcc.dg/analyzer/analyzer-verbosity-0.c    |  162 +++
 .../gcc.dg/analyzer/analyzer-verbosity-1.c    |  190 +++
 .../gcc.dg/analyzer/analyzer-verbosity-2.c    |  221 ++++
 gcc/testsuite/gcc.dg/analyzer/analyzer.exp    |   49 +
 .../gcc.dg/analyzer/attribute-nonnull.c       |   81 ++
 .../gcc.dg/analyzer/call-summaries-1.c        |   14 +
 .../gcc.dg/analyzer/conditionals-2.c          |   45 +
 .../gcc.dg/analyzer/conditionals-3.c          |   47 +
 .../gcc.dg/analyzer/conditionals-notrans.c    |  159 +++
 .../gcc.dg/analyzer/conditionals-trans.c      |  144 +++
 gcc/testsuite/gcc.dg/analyzer/data-model-1.c  | 1085 +++++++++++++++++
 gcc/testsuite/gcc.dg/analyzer/data-model-10.c |   17 +
 gcc/testsuite/gcc.dg/analyzer/data-model-11.c |    6 +
 gcc/testsuite/gcc.dg/analyzer/data-model-12.c |   13 +
 gcc/testsuite/gcc.dg/analyzer/data-model-13.c |   21 +
 gcc/testsuite/gcc.dg/analyzer/data-model-14.c |   24 +
 gcc/testsuite/gcc.dg/analyzer/data-model-15.c |   34 +
 gcc/testsuite/gcc.dg/analyzer/data-model-16.c |   52 +
 gcc/testsuite/gcc.dg/analyzer/data-model-17.c |   20 +
 gcc/testsuite/gcc.dg/analyzer/data-model-18.c |   22 +
 gcc/testsuite/gcc.dg/analyzer/data-model-19.c |   31 +
 gcc/testsuite/gcc.dg/analyzer/data-model-2.c  |   13 +
 gcc/testsuite/gcc.dg/analyzer/data-model-3.c  |   15 +
 gcc/testsuite/gcc.dg/analyzer/data-model-4.c  |   16 +
 gcc/testsuite/gcc.dg/analyzer/data-model-5.c  |  100 ++
 gcc/testsuite/gcc.dg/analyzer/data-model-5b.c |   91 ++
 gcc/testsuite/gcc.dg/analyzer/data-model-5c.c |   84 ++
 gcc/testsuite/gcc.dg/analyzer/data-model-5d.c |   64 +
 gcc/testsuite/gcc.dg/analyzer/data-model-6.c  |   14 +
 gcc/testsuite/gcc.dg/analyzer/data-model-7.c  |   20 +
 gcc/testsuite/gcc.dg/analyzer/data-model-8.c  |   26 +
 gcc/testsuite/gcc.dg/analyzer/data-model-9.c  |   33 +
 .../gcc.dg/analyzer/data-model-path-1.c       |   13 +
 gcc/testsuite/gcc.dg/analyzer/disabling.c     |   10 +
 gcc/testsuite/gcc.dg/analyzer/dot-output.c    |   33 +
 .../gcc.dg/analyzer/double-free-lto-1-a.c     |   16 +
 .../gcc.dg/analyzer/double-free-lto-1-b.c     |    8 +
 .../gcc.dg/analyzer/double-free-lto-1.h       |    1 +
 gcc/testsuite/gcc.dg/analyzer/equivalence.c   |   31 +
 gcc/testsuite/gcc.dg/analyzer/explode-1.c     |   60 +
 gcc/testsuite/gcc.dg/analyzer/explode-2.c     |   50 +
 gcc/testsuite/gcc.dg/analyzer/factorial.c     |    7 +
 gcc/testsuite/gcc.dg/analyzer/fibonacci.c     |    9 +
 gcc/testsuite/gcc.dg/analyzer/fields.c        |   41 +
 gcc/testsuite/gcc.dg/analyzer/file-1.c        |   37 +
 gcc/testsuite/gcc.dg/analyzer/file-2.c        |   18 +
 .../gcc.dg/analyzer/function-ptr-1.c          |    8 +
 .../gcc.dg/analyzer/function-ptr-2.c          |   44 +
 .../gcc.dg/analyzer/function-ptr-3.c          |   17 +
 gcc/testsuite/gcc.dg/analyzer/gzio-2.c        |   11 +
 gcc/testsuite/gcc.dg/analyzer/gzio-3.c        |   31 +
 gcc/testsuite/gcc.dg/analyzer/gzio-3a.c       |   27 +
 gcc/testsuite/gcc.dg/analyzer/gzio.c          |   17 +
 .../gcc.dg/analyzer/infinite-recursion.c      |   55 +
 gcc/testsuite/gcc.dg/analyzer/loop-2.c        |   37 +
 gcc/testsuite/gcc.dg/analyzer/loop-2a.c       |   40 +
 gcc/testsuite/gcc.dg/analyzer/loop-3.c        |   17 +
 gcc/testsuite/gcc.dg/analyzer/loop-4.c        |   43 +
 gcc/testsuite/gcc.dg/analyzer/loop.c          |   35 +
 gcc/testsuite/gcc.dg/analyzer/malloc-1.c      |  585 +++++++++
 gcc/testsuite/gcc.dg/analyzer/malloc-2.c      |   23 +
 gcc/testsuite/gcc.dg/analyzer/malloc-3.c      |    8 +
 .../gcc.dg/analyzer/malloc-callbacks.c        |   84 ++
 gcc/testsuite/gcc.dg/analyzer/malloc-dce.c    |   12 +
 .../gcc.dg/analyzer/malloc-dedupe-1.c         |   46 +
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-1.c  |   24 +
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c |   32 +
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c |   95 ++
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-12.c |    7 +
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c |   30 +
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c  |   34 +
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-3.c  |   23 +
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-4.c  |   13 +
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-5.c  |   13 +
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-6.c  |   22 +
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c  |   29 +
 .../analyzer/malloc-ipa-8-double-free.c       |  172 +++
 .../gcc.dg/analyzer/malloc-ipa-8-lto-a.c      |   12 +
 .../gcc.dg/analyzer/malloc-ipa-8-lto-b.c      |   18 +
 .../gcc.dg/analyzer/malloc-ipa-8-lto-c.c      |   17 +
 .../gcc.dg/analyzer/malloc-ipa-8-lto.h        |   12 +
 .../gcc.dg/analyzer/malloc-ipa-8-unchecked.c  |   66 +
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-9.c  |   18 +
 .../analyzer/malloc-macro-inline-events.c     |   45 +
 .../analyzer/malloc-macro-separate-events.c   |   15 +
 gcc/testsuite/gcc.dg/analyzer/malloc-macro.h  |    2 +
 .../gcc.dg/analyzer/malloc-many-paths-1.c     |   14 +
 .../gcc.dg/analyzer/malloc-many-paths-2.c     |   30 +
 .../gcc.dg/analyzer/malloc-many-paths-3.c     |   36 +
 .../gcc.dg/analyzer/malloc-paths-1.c          |   15 +
 .../gcc.dg/analyzer/malloc-paths-10.c         |   20 +
 .../gcc.dg/analyzer/malloc-paths-2.c          |   13 +
 .../gcc.dg/analyzer/malloc-paths-3.c          |   14 +
 .../gcc.dg/analyzer/malloc-paths-4.c          |   20 +
 .../gcc.dg/analyzer/malloc-paths-5.c          |   43 +
 .../gcc.dg/analyzer/malloc-paths-6.c          |   11 +
 .../gcc.dg/analyzer/malloc-paths-7.c          |   21 +
 .../gcc.dg/analyzer/malloc-paths-8.c          |   54 +
 .../gcc.dg/analyzer/malloc-paths-9.c          |  298 +++++
 .../gcc.dg/analyzer/malloc-vs-local-1a.c      |  181 +++
 .../gcc.dg/analyzer/malloc-vs-local-1b.c      |  176 +++
 .../gcc.dg/analyzer/malloc-vs-local-2.c       |  179 +++
 .../gcc.dg/analyzer/malloc-vs-local-3.c       |   66 +
 .../gcc.dg/analyzer/malloc-vs-local-4.c       |   40 +
 gcc/testsuite/gcc.dg/analyzer/operations.c    |   44 +
 gcc/testsuite/gcc.dg/analyzer/params-2.c      |   17 +
 gcc/testsuite/gcc.dg/analyzer/params.c        |   34 +
 gcc/testsuite/gcc.dg/analyzer/paths-1.c       |   18 +
 gcc/testsuite/gcc.dg/analyzer/paths-1a.c      |   18 +
 gcc/testsuite/gcc.dg/analyzer/paths-2.c       |   27 +
 gcc/testsuite/gcc.dg/analyzer/paths-3.c       |   49 +
 gcc/testsuite/gcc.dg/analyzer/paths-4.c       |   51 +
 gcc/testsuite/gcc.dg/analyzer/paths-5.c       |   12 +
 gcc/testsuite/gcc.dg/analyzer/paths-6.c       |  119 ++
 gcc/testsuite/gcc.dg/analyzer/paths-7.c       |   59 +
 .../gcc.dg/analyzer/pattern-test-1.c          |   28 +
 .../gcc.dg/analyzer/pattern-test-2.c          |   29 +
 .../gcc.dg/analyzer/pointer-merging.c         |   16 +
 gcc/testsuite/gcc.dg/analyzer/pr61861.c       |    2 +
 gcc/testsuite/gcc.dg/analyzer/pragma-1.c      |   26 +
 gcc/testsuite/gcc.dg/analyzer/scope-1.c       |   23 +
 gcc/testsuite/gcc.dg/analyzer/sensitive-1.c   |   55 +
 gcc/testsuite/gcc.dg/analyzer/setjmp-1.c      |    1 +
 gcc/testsuite/gcc.dg/analyzer/setjmp-2.c      |   98 ++
 gcc/testsuite/gcc.dg/analyzer/setjmp-3.c      |  107 ++
 gcc/testsuite/gcc.dg/analyzer/setjmp-4.c      |  108 ++
 gcc/testsuite/gcc.dg/analyzer/setjmp-5.c      |   66 +
 gcc/testsuite/gcc.dg/analyzer/setjmp-6.c      |   31 +
 gcc/testsuite/gcc.dg/analyzer/setjmp-7.c      |   36 +
 gcc/testsuite/gcc.dg/analyzer/setjmp-7a.c     |  110 ++
 gcc/testsuite/gcc.dg/analyzer/setjmp-8.c      |  108 ++
 gcc/testsuite/gcc.dg/analyzer/setjmp-9.c      |  110 ++
 gcc/testsuite/gcc.dg/analyzer/signal-1.c      |   31 +
 gcc/testsuite/gcc.dg/analyzer/signal-2.c      |   34 +
 gcc/testsuite/gcc.dg/analyzer/signal-3.c      |   23 +
 gcc/testsuite/gcc.dg/analyzer/signal-4a.c     |   74 ++
 gcc/testsuite/gcc.dg/analyzer/signal-4b.c     |   89 ++
 gcc/testsuite/gcc.dg/analyzer/strcmp-1.c      |   35 +
 gcc/testsuite/gcc.dg/analyzer/switch.c        |   30 +
 gcc/testsuite/gcc.dg/analyzer/taint-1.c       |  128 ++
 gcc/testsuite/gcc.dg/analyzer/zlib-1.c        |   69 ++
 gcc/testsuite/gcc.dg/analyzer/zlib-2.c        |   51 +
 gcc/testsuite/gcc.dg/analyzer/zlib-3.c        |  214 ++++
 gcc/testsuite/gcc.dg/analyzer/zlib-4.c        |   20 +
 gcc/testsuite/gcc.dg/analyzer/zlib-5.c        |   51 +
 gcc/testsuite/gcc.dg/analyzer/zlib-6.c        |   47 +
 gcc/testsuite/lib/gcc-defs.exp                |   21 +
 gcc/testsuite/lib/target-supports-dg.exp      |   10 +
 gcc/testsuite/lib/target-supports.exp         |   21 +
 153 files changed, 8813 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-minimal.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/abort.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/alloca-leak.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-0.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/analyzer.exp
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/call-summaries-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/conditionals-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/conditionals-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/conditionals-trans.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-10.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-11.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-12.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-13.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-14.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-15.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-16.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-17.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-18.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-19.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-4.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-5.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-5b.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-5c.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-5d.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-6.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-7.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-8.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-9.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/disabling.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/dot-output.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-a.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-b.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/double-free-lto-1.h
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/equivalence.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/explode-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/explode-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/factorial.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fibonacci.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/fields.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/file-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/file-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/function-ptr-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/function-ptr-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/gzio-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/gzio-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/gzio-3a.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/gzio.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/infinite-recursion.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/loop-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/loop-2a.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/loop-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/loop-4.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/loop.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-callbacks.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-dce.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-12.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-4.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-5.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-6.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto-a.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto-b.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto-c.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto.h
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-9.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-macro-separate-events.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-macro.h
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-10.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-4.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-5.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-6.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-7.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1b.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-4.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/operations.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/params-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/params.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-1a.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-4.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-5.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-6.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-7.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/pointer-merging.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/pr61861.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/pragma-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/scope-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/sensitive-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-4.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-5.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-6.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-7.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-7a.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-8.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-9.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/signal-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/signal-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/signal-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/signal-4a.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/signal-4b.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/strcmp-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/switch.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/zlib-1.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/zlib-2.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/zlib-3.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/zlib-4.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/zlib-5.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/zlib-6.c

Comments

Jeff Law Jan. 10, 2020, 4:15 p.m. UTC | #1
On Wed, 2020-01-08 at 04:03 -0500, David Malcolm wrote:
> Needs review.
> 
> Changed in v5:
> - updated for removal of analyzer-specific builtins:
>   https://gcc.gnu.org/ml/gcc-patches/2019-12/msg01310.html
> 
> Changed in v4:
> - more tests, including a test for .dot output and an LTO test
> - update for change from "--analyzer" to "-fanalyzer"
> 
> This patch adds the testsuite for the analyzer.
> 
> gcc/testsuite/ChangeLog:
> 	* gcc.dg/analyzer/CVE-2005-1689-minimal.c: New test.
> 	* gcc.dg/analyzer/abort.c: New test.
> 	* gcc.dg/analyzer/alloca-leak.c: New test.
> 	* gcc.dg/analyzer/analyzer-decls.h: New header.
> 	* gcc.dg/analyzer/analyzer-verbosity-0.c: New test.
> 	* gcc.dg/analyzer/analyzer-verbosity-1.c: New test.
> 	* gcc.dg/analyzer/analyzer-verbosity-2.c: New test.
> 	* gcc.dg/analyzer/analyzer.exp: New suite.
> 	* gcc.dg/analyzer/attribute-nonnull.c: New test.
> 	* gcc.dg/analyzer/call-summaries-1.c: New test.
> 	* gcc.dg/analyzer/conditionals-2.c: New test.
> 	* gcc.dg/analyzer/conditionals-3.c: New test.
> 	* gcc.dg/analyzer/conditionals-notrans.c: New test.
> 	* gcc.dg/analyzer/conditionals-trans.c: New test.
> 	* gcc.dg/analyzer/data-model-1.c: New test.
> 	* gcc.dg/analyzer/data-model-2.c: New test.
> 	* gcc.dg/analyzer/data-model-3.c: New test.
> 	* gcc.dg/analyzer/data-model-4.c: New test.
> 	* gcc.dg/analyzer/data-model-5.c: New test.
> 	* gcc.dg/analyzer/data-model-5b.c: New test.
> 	* gcc.dg/analyzer/data-model-5c.c: New test.
> 	* gcc.dg/analyzer/data-model-5d.c: New test.
> 	* gcc.dg/analyzer/data-model-6.c: New test.
> 	* gcc.dg/analyzer/data-model-7.c: New test.
> 	* gcc.dg/analyzer/data-model-8.c: New test.
> 	* gcc.dg/analyzer/data-model-9.c: New test.
> 	* gcc.dg/analyzer/data-model-11.c: New test.
> 	* gcc.dg/analyzer/data-model-12.c: New test.
> 	* gcc.dg/analyzer/data-model-13.c: New test.
> 	* gcc.dg/analyzer/data-model-14.c: New test.
> 	* gcc.dg/analyzer/data-model-15.c: New test.
> 	* gcc.dg/analyzer/data-model-16.c: New test.
> 	* gcc.dg/analyzer/data-model-17.c: New test.
> 	* gcc.dg/analyzer/data-model-18.c: New test.
> 	* gcc.dg/analyzer/data-model-19.c: New test.
> 	* gcc.dg/analyzer/data-model-path-1.c: New test.
> 	* gcc.dg/analyzer/disabling.c: New test.
> 	* gcc.dg/analyzer/dot-output.c: New test.
> 	* gcc.dg/analyzer/double-free-lto-1-a.c: New test.
> 	* gcc.dg/analyzer/double-free-lto-1-b.c: New test.
> 	* gcc.dg/analyzer/double-free-lto-1.h: New header.
> 	* gcc.dg/analyzer/equivalence.c: New test.
> 	* gcc.dg/analyzer/explode-1.c: New test.
> 	* gcc.dg/analyzer/explode-2.c: New test.
> 	* gcc.dg/analyzer/factorial.c: New test.
> 	* gcc.dg/analyzer/fibonacci.c: New test.
> 	* gcc.dg/analyzer/fields.c: New test.
> 	* gcc.dg/analyzer/file-1.c: New test.
> 	* gcc.dg/analyzer/file-2.c: New test.
> 	* gcc.dg/analyzer/function-ptr-1.c: New test.
> 	* gcc.dg/analyzer/function-ptr-2.c: New test.
> 	* gcc.dg/analyzer/function-ptr-3.c: New test.
> 	* gcc.dg/analyzer/gzio-2.c: New test.
> 	* gcc.dg/analyzer/gzio-3.c: New test.
> 	* gcc.dg/analyzer/gzio-3a.c: New test.
> 	* gcc.dg/analyzer/gzio.c: New test.
> 	* gcc.dg/analyzer/infinite-recursion.c: New test.
> 	* gcc.dg/analyzer/loop-2.c: New test.
> 	* gcc.dg/analyzer/loop-2a.c: New test.
> 	* gcc.dg/analyzer/loop-3.c: New test.
> 	* gcc.dg/analyzer/loop-4.c: New test.
> 	* gcc.dg/analyzer/loop.c: New test.
> 	* gcc.dg/analyzer/malloc-1.c: New test.
> 	* gcc.dg/analyzer/malloc-2.c: New test.
> 	* gcc.dg/analyzer/malloc-3.c: New test.
> 	* gcc.dg/analyzer/malloc-callbacks.c: New test.
> 	* gcc.dg/analyzer/malloc-dce.c: New test.
> 	* gcc.dg/analyzer/malloc-dedupe-1.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-1.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-10.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-11.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-12.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-13.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-2.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-3.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-4.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-5.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-6.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-7.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-8-double-free.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-8-lto-a.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-8-lto-b.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-8-lto-c.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-8-lto.h: New test.
> 	* gcc.dg/analyzer/malloc-ipa-8-unchecked.c: New test.
> 	* gcc.dg/analyzer/malloc-ipa-9.c: New test.
> 	* gcc.dg/analyzer/malloc-macro-inline-events.c: New test.
> 	* gcc.dg/analyzer/malloc-macro-separate-events.c: New test.
> 	* gcc.dg/analyzer/malloc-macro.h: New header.
> 	* gcc.dg/analyzer/malloc-many-paths-1.c: New test.
> 	* gcc.dg/analyzer/malloc-many-paths-2.c: New test.
> 	* gcc.dg/analyzer/malloc-many-paths-3.c: New test.
> 	* gcc.dg/analyzer/malloc-paths-1.c: New test.
> 	* gcc.dg/analyzer/malloc-paths-10.c: New test.
> 	* gcc.dg/analyzer/malloc-paths-2.c: New test.
> 	* gcc.dg/analyzer/malloc-paths-3.c: New test.
> 	* gcc.dg/analyzer/malloc-paths-4.c: New test.
> 	* gcc.dg/analyzer/malloc-paths-5.c: New test.
> 	* gcc.dg/analyzer/malloc-paths-6.c: New test.
> 	* gcc.dg/analyzer/malloc-paths-7.c: New test.
> 	* gcc.dg/analyzer/malloc-paths-8.c: New test.
> 	* gcc.dg/analyzer/malloc-paths-9.c: New test.
> 	* gcc.dg/analyzer/malloc-vs-local-1a.c: New test.
> 	* gcc.dg/analyzer/malloc-vs-local-1b.c: New test.
> 	* gcc.dg/analyzer/malloc-vs-local-2.c: New test.
> 	* gcc.dg/analyzer/malloc-vs-local-3.c: New test.
> 	* gcc.dg/analyzer/malloc-vs-local-4.c: New test.
> 	* gcc.dg/analyzer/operations.c: New test.
> 	* gcc.dg/analyzer/params-2.c: New test.
> 	* gcc.dg/analyzer/params.c: New test.
> 	* gcc.dg/analyzer/paths-1.c: New test.
> 	* gcc.dg/analyzer/paths-1a.c: New test.
> 	* gcc.dg/analyzer/paths-2.c: New test.
> 	* gcc.dg/analyzer/paths-3.c: New test.
> 	* gcc.dg/analyzer/paths-4.c: New test.
> 	* gcc.dg/analyzer/paths-5.c: New test.
> 	* gcc.dg/analyzer/paths-6.c: New test.
> 	* gcc.dg/analyzer/paths-7.c: New test.
> 	* gcc.dg/analyzer/pattern-test-1.c: New test.
> 	* gcc.dg/analyzer/pattern-test-2.c: New test.
> 	* gcc.dg/analyzer/pointer-merging.c: New test.
> 	* gcc.dg/analyzer/pr61861.c: New test.
> 	* gcc.dg/analyzer/pragma-1.c: New test.
> 	* gcc.dg/analyzer/scope-1.c: New test.
> 	* gcc.dg/analyzer/sensitive-1.c: New test.
> 	* gcc.dg/analyzer/setjmp-1.c: New test.
> 	* gcc.dg/analyzer/setjmp-2.c: New test.
> 	* gcc.dg/analyzer/setjmp-3.c: New test.
> 	* gcc.dg/analyzer/setjmp-4.c: New test.
> 	* gcc.dg/analyzer/setjmp-5.c: New test.
> 	* gcc.dg/analyzer/setjmp-6.c: New test.
> 	* gcc.dg/analyzer/setjmp-7.c: New test.
> 	* gcc.dg/analyzer/setjmp-7a.c: New test.
> 	* gcc.dg/analyzer/setjmp-8.c: New test.
> 	* gcc.dg/analyzer/setjmp-9.c: New test.
> 	* gcc.dg/analyzer/signal-1.c: New test.
> 	* gcc.dg/analyzer/signal-2.c: New test.
> 	* gcc.dg/analyzer/signal-3.c: New test.
> 	* gcc.dg/analyzer/signal-4a.c: New test.
> 	* gcc.dg/analyzer/signal-4b.c: New test.
> 	* gcc.dg/analyzer/strcmp-1.c: New test.
> 	* gcc.dg/analyzer/switch.c: New test.
> 	* gcc.dg/analyzer/taint-1.c: New test.
> 	* gcc.dg/analyzer/zlib-1.c: New test.
> 	* gcc.dg/analyzer/zlib-2.c: New test.
> 	* gcc.dg/analyzer/zlib-3.c: New test.
> 	* gcc.dg/analyzer/zlib-4.c: New test.
> 	* gcc.dg/analyzer/zlib-5.c: New test.
> 	* gcc.dg/analyzer/zlib-6.c: New test.
> 	* lib/gcc-defs.exp (dg-check-dot): New procedure.
> 	* lib/target-supports.exp (check_dot_available): New procedure.
> 	(check_effective_target_analyzer): New.
> 	* lib/target-supports-dg.exp (dg-require-dot): New procedure.
I'm not digging into the details on this. 

Please check sourcebuild.texi and see if the new stuff you've added
(check-dot and target_analyzer) need to be documented.  If so, those
changes are pre-approved.

OK
jeff
diff mbox series

Patch

diff --git a/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-minimal.c b/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-minimal.c
new file mode 100644
index 000000000000..aa9deb3be196
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-minimal.c
@@ -0,0 +1,30 @@ 
+#include <stdlib.h>
+
+typedef struct _krb5_data {
+  char *data;
+} krb5_data;
+
+void
+test_1 (krb5_data inbuf, int flag)
+{
+  free(inbuf.data); /* { dg-message "first 'free' here" } */
+  free(inbuf.data); /* { dg-warning "double-'free' of 'inbuf.data'" } */
+}
+
+void
+test_2 (krb5_data inbuf, int flag)
+{
+  if (flag) {
+    free(inbuf.data); /* { dg-message "first 'free' here" } */
+  }
+  free(inbuf.data); /* { dg-warning "double-'free' of 'inbuf.data'" } */
+}
+
+void
+test_3 (krb5_data inbuf, int flag)
+{
+  if (flag) {
+    free((char *)inbuf.data); /* { dg-message "first 'free' here" } */
+  }
+  free((char *)inbuf.data); /* { dg-warning "double-'free' of 'inbuf.data'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/abort.c b/gcc/testsuite/gcc.dg/analyzer/abort.c
new file mode 100644
index 000000000000..ea1756e47cb0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/abort.c
@@ -0,0 +1,72 @@ 
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+extern void foo ();
+extern void bar ();
+
+void test_1 (int i)
+{
+  if (i == 42)
+    abort ();
+
+  __analyzer_eval (i != 42); /* { dg-warning "TRUE" } */
+}
+
+void test_2 (int i)
+{
+  if (i)
+    foo ();
+  else
+    bar ();
+
+  foo ();
+
+  if (i)
+    foo ();
+  else
+    abort ();
+
+  __analyzer_eval (i != 0); /* { dg-warning "TRUE" } */
+}
+
+/**************************************************************************/
+
+void calls_abort (const char *msg)
+{
+  fprintf (stderr, "%s", msg);
+  abort ();
+}
+
+void test_3 (void *ptr)
+{
+  if (!ptr)
+    calls_abort ("ptr was NULL");
+
+  __analyzer_eval (ptr != 0); /* { dg-warning "TRUE" } */
+}
+
+/**************************************************************************/
+
+extern void marked_noreturn (const char *msg)
+  __attribute__ ((__noreturn__));
+
+void test_4 (void *ptr)
+{
+  if (!ptr)
+    marked_noreturn ("ptr was NULL");
+
+  __analyzer_eval (ptr != 0); /* { dg-warning "TRUE" } */
+}
+
+/**************************************************************************/
+
+void test_5 (int i)
+{
+  assert (i < 10);
+
+  /* We have not defined NDEBUG, so this will call __assert_fail if
+     i >= 10, which is labelled with __attribute__ ((__noreturn__)).  */
+  __analyzer_eval (i < 10); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/alloca-leak.c b/gcc/testsuite/gcc.dg/analyzer/alloca-leak.c
new file mode 100644
index 000000000000..6d9fe3431cea
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/alloca-leak.c
@@ -0,0 +1,8 @@ 
+#include <alloca.h>
+
+void *test (void)
+{
+  void *ptr = alloca (64);
+  return ptr;
+}
+/* TODO: warn about escaping alloca.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h b/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h
new file mode 100644
index 000000000000..180e873b67c9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h
@@ -0,0 +1,36 @@ 
+#ifndef ANALYZER_DECLS_H
+#define ANALYZER_DECLS_H
+
+/* Function decls with special meaning to the analyzer.
+   None of these are actually implemented.  */
+
+/* Trigger a breakpoint in the analyzer when reached.  */
+extern void __analyzer_break (void);
+
+/* Dump copious information about the analyzer’s state when reached.  */
+extern void __analyzer_dump (void);
+
+/* Dump information after analysis on all of the exploded nodes at this
+   program point.
+
+   __analyzer_dump_exploded_nodes (0);
+   will dump just the number of nodes, and their IDs.
+
+   __analyzer_dump_exploded_nodes (1);
+   will also dump all of the states within those nodes.  */
+extern void __analyzer_dump_exploded_nodes (int);
+
+extern void __analyzer_dump_num_heap_regions (void);
+
+/* Emit a placeholder "note" diagnostic with a path to this call site,
+   if the analyzer finds a feasible path to it.  */
+extern void __analyzer_dump_path (void);
+
+/* Dump the region_model's state to stderr.  */
+extern void __analyzer_dump_region_model (void);
+
+/* Emit a warning with text "TRUE", FALSE" or "UNKNOWN" based on the
+   truthfulness of the argument.  */
+extern void __analyzer_eval (int);
+
+#endif /* #ifndef ANALYZER_DECLS_H.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-0.c b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-0.c
new file mode 100644
index 000000000000..1103cc6642d3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-0.c
@@ -0,0 +1,162 @@ 
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret -fanalyzer-verbosity=0" } */
+
+#include <stdlib.h>
+
+void calls_free_1 (void *ptr)
+{
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_1 (void *ptr, int a, int b)
+{
+  if (a)
+    calls_free_1 (ptr);
+
+  if (b)
+    {
+    }
+  else
+    calls_free_1 (ptr);
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |   free (ptr);
+      |   ^~~~~~~~~~
+  'test_1': event 1
+    |
+    |   NN |     calls_free_1 (ptr);
+    |      |     ^~~~~~~~~~~~~~~~~~
+    |      |     |
+    |      |     (1) calling 'calls_free_1' from 'test_1'
+    |
+    +--> 'calls_free_1': event 2
+           |
+           |   NN |   free (ptr);
+           |      |   ^~~~~~~~~~
+           |      |   |
+           |      |   (2) first 'free' here
+           |
+    <------+
+    |
+  'test_1': events 3-4
+    |
+    |   NN |     calls_free_1 (ptr);
+    |      |     ^~~~~~~~~~~~~~~~~~
+    |      |     |
+    |      |     (3) returning to 'test_1' from 'calls_free_1'
+    |......
+    |   NN |     calls_free_1 (ptr);
+    |      |     ~~~~~~~~~~~~~~~~~~
+    |      |     |
+    |      |     (4) passing freed pointer 'ptr' in call to 'calls_free_1' from 'test_1'
+    |
+    +--> 'calls_free_1': event 5
+           |
+           |   NN |   free (ptr);
+           |      |   ^~~~~~~~~~
+           |      |   |
+           |      |   (5) second 'free' here; first 'free' was at (2)
+           |
+  { dg-end-multiline-output "" } */
+
+void calls_free_2 (void *ptr)
+{
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_2 (void *ptr, int a, int b)
+{
+  switch (a)
+    {
+    default:
+      break;
+    case 1:
+      break;
+    case 3:
+      calls_free_2 (ptr);
+      break;
+    }
+
+  switch (b)
+    {
+    default:
+      calls_free_2 (ptr);
+      break;
+    case 1:
+      break;
+    case 42:
+      break;
+    }
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |   free (ptr);
+      |   ^~~~~~~~~~
+  'test_2': event 1
+    |
+    |   NN |       calls_free_2 (ptr);
+    |      |       ^~~~~~~~~~~~~~~~~~
+    |      |       |
+    |      |       (1) calling 'calls_free_2' from 'test_2'
+    |
+    +--> 'calls_free_2': event 2
+           |
+           |   NN |   free (ptr);
+           |      |   ^~~~~~~~~~
+           |      |   |
+           |      |   (2) first 'free' here
+           |
+    <------+
+    |
+  'test_2': events 3-4
+    |
+    |   NN |       calls_free_2 (ptr);
+    |      |       ^~~~~~~~~~~~~~~~~~
+    |      |       |
+    |      |       (3) returning to 'test_2' from 'calls_free_2'
+    |......
+    |   NN |       calls_free_2 (ptr);
+    |      |       ~~~~~~~~~~~~~~~~~~
+    |      |       |
+    |      |       (4) passing freed pointer 'ptr' in call to 'calls_free_2' from 'test_2'
+    |
+    +--> 'calls_free_2': event 5
+           |
+           |   NN |   free (ptr);
+           |      |   ^~~~~~~~~~
+           |      |   |
+           |      |   (5) second 'free' here; first 'free' was at (2)
+           |
+  { dg-end-multiline-output "" } */
+
+// TODO: range cases
+
+/* The call/return to this function shouldn't appear in the path.  */
+
+void called_by_test_3 (void)
+{
+}
+
+void test_3 (void *ptr)
+{
+  free (ptr);
+  called_by_test_3 ();
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |   free (ptr);
+      |   ^~~~~~~~~~
+  'test_3': events 1-2
+    |
+    |   NN |   free (ptr);
+    |      |   ^~~~~~~~~~
+    |      |   |
+    |      |   (1) first 'free' here
+    |   NN |   called_by_test_3 ();
+    |   NN |   free (ptr);
+    |      |   ~~~~~~~~~~
+    |      |   |
+    |      |   (2) second 'free' here; first 'free' was at (1)
+    |
+  { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-1.c b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-1.c
new file mode 100644
index 000000000000..80039d5d9d24
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-1.c
@@ -0,0 +1,190 @@ 
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret -fanalyzer-verbosity=1" } */
+
+#include <stdlib.h>
+
+void calls_free_1 (void *ptr)
+{
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_1 (void *ptr, int a, int b)
+{
+  if (a)
+    calls_free_1 (ptr);
+
+  if (b)
+    {
+    }
+  else
+    calls_free_1 (ptr);
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |   free (ptr);
+      |   ^~~~~~~~~~
+  'test_1': events 1-2
+    |
+    |   NN | void test_1 (void *ptr, int a, int b)
+    |      |      ^~~~~~
+    |      |      |
+    |      |      (1) entry to 'test_1'
+    |......
+    |   NN |     calls_free_1 (ptr);
+    |      |     ~~~~~~~~~~~~~~~~~~
+    |      |     |
+    |      |     (2) calling 'calls_free_1' from 'test_1'
+    |
+    +--> 'calls_free_1': events 3-4
+           |
+           |   NN | void calls_free_1 (void *ptr)
+           |      |      ^~~~~~~~~~~~
+           |      |      |
+           |      |      (3) entry to 'calls_free_1'
+           |   NN | {
+           |   NN |   free (ptr);
+           |      |   ~~~~~~~~~~
+           |      |   |
+           |      |   (4) first 'free' here
+           |
+    <------+
+    |
+  'test_1': events 5-6
+    |
+    |   NN |     calls_free_1 (ptr);
+    |      |     ^~~~~~~~~~~~~~~~~~
+    |      |     |
+    |      |     (5) returning to 'test_1' from 'calls_free_1'
+    |......
+    |   NN |     calls_free_1 (ptr);
+    |      |     ~~~~~~~~~~~~~~~~~~
+    |      |     |
+    |      |     (6) passing freed pointer 'ptr' in call to 'calls_free_1' from 'test_1'
+    |
+    +--> 'calls_free_1': events 7-8
+           |
+           |   NN | void calls_free_1 (void *ptr)
+           |      |      ^~~~~~~~~~~~
+           |      |      |
+           |      |      (7) entry to 'calls_free_1'
+           |   NN | {
+           |   NN |   free (ptr);
+           |      |   ~~~~~~~~~~
+           |      |   |
+           |      |   (8) second 'free' here; first 'free' was at (4)
+           |
+  { dg-end-multiline-output "" } */
+
+void calls_free_2 (void *ptr)
+{
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_2 (void *ptr, int a, int b)
+{
+  switch (a)
+    {
+    default:
+      break;
+    case 1:
+      break;
+    case 3:
+      calls_free_2 (ptr);
+      break;
+    }
+
+  switch (b)
+    {
+    default:
+      calls_free_2 (ptr);
+      break;
+    case 1:
+      break;
+    case 42:
+      break;
+    }
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |   free (ptr);
+      |   ^~~~~~~~~~
+  'test_2': events 1-2
+    |
+    |   NN | void test_2 (void *ptr, int a, int b)
+    |      |      ^~~~~~
+    |      |      |
+    |      |      (1) entry to 'test_2'
+    |......
+    |   NN |       calls_free_2 (ptr);
+    |      |       ~~~~~~~~~~~~~~~~~~
+    |      |       |
+    |      |       (2) calling 'calls_free_2' from 'test_2'
+    |
+    +--> 'calls_free_2': events 3-4
+           |
+           |   NN | void calls_free_2 (void *ptr)
+           |      |      ^~~~~~~~~~~~
+           |      |      |
+           |      |      (3) entry to 'calls_free_2'
+           |   NN | {
+           |   NN |   free (ptr);
+           |      |   ~~~~~~~~~~
+           |      |   |
+           |      |   (4) first 'free' here
+           |
+    <------+
+    |
+  'test_2': events 5-6
+    |
+    |   NN |       calls_free_2 (ptr);
+    |      |       ^~~~~~~~~~~~~~~~~~
+    |      |       |
+    |      |       (5) returning to 'test_2' from 'calls_free_2'
+    |......
+    |   NN |       calls_free_2 (ptr);
+    |      |       ~~~~~~~~~~~~~~~~~~
+    |      |       |
+    |      |       (6) passing freed pointer 'ptr' in call to 'calls_free_2' from 'test_2'
+    |
+    +--> 'calls_free_2': events 7-8
+           |
+           |   NN | void calls_free_2 (void *ptr)
+           |      |      ^~~~~~~~~~~~
+           |      |      |
+           |      |      (7) entry to 'calls_free_2'
+           |   NN | {
+           |   NN |   free (ptr);
+           |      |   ~~~~~~~~~~
+           |      |   |
+           |      |   (8) second 'free' here; first 'free' was at (4)
+           |
+  { dg-end-multiline-output "" } */
+
+/* The call/return to this function shouldn't appear in the path.  */
+
+void called_by_test_3 (void)
+{
+}
+
+void test_3 (void *ptr)
+{
+  free (ptr);
+  called_by_test_3 ();
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |   free (ptr);
+      |   ^~~~~~~~~~
+  'test_3': events 1-2
+    |
+    |   NN |   free (ptr);
+    |      |   ^~~~~~~~~~
+    |      |   |
+    |      |   (1) first 'free' here
+    |   NN |   called_by_test_3 ();
+    |   NN |   free (ptr);
+    |      |   ~~~~~~~~~~
+    |      |   |
+    |      |   (2) second 'free' here; first 'free' was at (1)
+    |
+  { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-2.c b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-2.c
new file mode 100644
index 000000000000..9b87d43544fe
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-2.c
@@ -0,0 +1,221 @@ 
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret -fanalyzer-verbosity=2" } */
+
+#include <stdlib.h>
+
+void calls_free_1 (void *ptr)
+{
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_1 (void *ptr, int a, int b)
+{
+  if (a)
+    calls_free_1 (ptr);
+
+  if (b)
+    {
+    }
+  else
+    calls_free_1 (ptr);
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |   free (ptr);
+      |   ^~~~~~~~~~
+  'test_1': events 1-4
+    |
+    |   NN | void test_1 (void *ptr, int a, int b)
+    |      |      ^~~~~~
+    |      |      |
+    |      |      (1) entry to 'test_1'
+    |   NN | {
+    |   NN |   if (a)
+    |      |      ~
+    |      |      |
+    |      |      (2) following 'true' branch (when 'a != 0')...
+    |   NN |     calls_free_1 (ptr);
+    |      |     ~~~~~~~~~~~~~~~~~~
+    |      |     |
+    |      |     (3) ...to here
+    |      |     (4) calling 'calls_free_1' from 'test_1'
+    |
+    +--> 'calls_free_1': events 5-6
+           |
+           |   NN | void calls_free_1 (void *ptr)
+           |      |      ^~~~~~~~~~~~
+           |      |      |
+           |      |      (5) entry to 'calls_free_1'
+           |   NN | {
+           |   NN |   free (ptr);
+           |      |   ~~~~~~~~~~
+           |      |   |
+           |      |   (6) first 'free' here
+           |
+    <------+
+    |
+  'test_1': events 7-10
+    |
+    |   NN |     calls_free_1 (ptr);
+    |      |     ^~~~~~~~~~~~~~~~~~
+    |      |     |
+    |      |     (7) returning to 'test_1' from 'calls_free_1'
+    |   NN | 
+    |   NN |   if (b)
+    |      |      ~
+    |      |      |
+    |      |      (8) following 'false' branch (when 'b == 0')...
+    |......
+    |   NN |     calls_free_1 (ptr);
+    |      |     ~~~~~~~~~~~~~~~~~~
+    |      |     |
+    |      |     (9) ...to here
+    |      |     (10) passing freed pointer 'ptr' in call to 'calls_free_1' from 'test_1'
+    |
+    +--> 'calls_free_1': events 11-12
+           |
+           |   NN | void calls_free_1 (void *ptr)
+           |      |      ^~~~~~~~~~~~
+           |      |      |
+           |      |      (11) entry to 'calls_free_1'
+           |   NN | {
+           |   NN |   free (ptr);
+           |      |   ~~~~~~~~~~
+           |      |   |
+           |      |   (12) second 'free' here; first 'free' was at (6)
+           |
+  { dg-end-multiline-output "" } */
+
+void calls_free_2 (void *ptr)
+{
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_2 (void *ptr, int a, int b)
+{
+  switch (a)
+    {
+    default:
+      break;
+    case 1:
+      break;
+    case 3:
+      calls_free_2 (ptr);
+      break;
+    }
+
+  switch (b)
+    {
+    default:
+      calls_free_2 (ptr);
+      break;
+    case 1:
+      break;
+    case 42:
+      break;
+    }
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |   free (ptr);
+      |   ^~~~~~~~~~
+  'test_2': events 1-4
+    |
+    |   NN | void test_2 (void *ptr, int a, int b)
+    |      |      ^~~~~~
+    |      |      |
+    |      |      (1) entry to 'test_2'
+    |   NN | {
+    |   NN |   switch (a)
+    |      |   ~~~~~~
+    |      |   |
+    |      |   (2) following 'case 3:' branch...
+    |......
+    |   NN |     case 3:
+    |      |     ~~~~
+    |      |     |
+    |      |     (3) ...to here
+    |   NN |       calls_free_2 (ptr);
+    |      |       ~~~~~~~~~~~~~~~~~~
+    |      |       |
+    |      |       (4) calling 'calls_free_2' from 'test_2'
+    |
+    +--> 'calls_free_2': events 5-6
+           |
+           |   NN | void calls_free_2 (void *ptr)
+           |      |      ^~~~~~~~~~~~
+           |      |      |
+           |      |      (5) entry to 'calls_free_2'
+           |   NN | {
+           |   NN |   free (ptr);
+           |      |   ~~~~~~~~~~
+           |      |   |
+           |      |   (6) first 'free' here
+           |
+    <------+
+    |
+  'test_2': events 7-10
+    |
+    |   NN |       calls_free_2 (ptr);
+    |      |       ^~~~~~~~~~~~~~~~~~
+    |      |       |
+    |      |       (7) returning to 'test_2' from 'calls_free_2'
+    |......
+    |   NN |   switch (b)
+    |      |   ~~~~~~
+    |      |   |
+    |      |   (8) following 'default:' branch...
+    |   NN |     {
+    |   NN |     default:
+    |      |     ~~~~~~~
+    |      |     |
+    |      |     (9) ...to here
+    |   NN |       calls_free_2 (ptr);
+    |      |       ~~~~~~~~~~~~~~~~~~
+    |      |       |
+    |      |       (10) passing freed pointer 'ptr' in call to 'calls_free_2' from 'test_2'
+    |
+    +--> 'calls_free_2': events 11-12
+           |
+           |   NN | void calls_free_2 (void *ptr)
+           |      |      ^~~~~~~~~~~~
+           |      |      |
+           |      |      (11) entry to 'calls_free_2'
+           |   NN | {
+           |   NN |   free (ptr);
+           |      |   ~~~~~~~~~~
+           |      |   |
+           |      |   (12) second 'free' here; first 'free' was at (6)
+           |
+  { dg-end-multiline-output "" } */
+
+// TODO: range cases
+
+/* The call/return to this function shouldn't appear in the path.  */
+
+void called_by_test_3 (void)
+{
+}
+
+void test_3 (void *ptr)
+{
+  free (ptr);
+  called_by_test_3 ();
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |   free (ptr);
+      |   ^~~~~~~~~~
+  'test_3': events 1-2
+    |
+    |   NN |   free (ptr);
+    |      |   ^~~~~~~~~~
+    |      |   |
+    |      |   (1) first 'free' here
+    |   NN |   called_by_test_3 ();
+    |   NN |   free (ptr);
+    |      |   ~~~~~~~~~~
+    |      |   |
+    |      |   (2) second 'free' here; first 'free' was at (1)
+    |
+  { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer.exp b/gcc/testsuite/gcc.dg/analyzer/analyzer.exp
new file mode 100644
index 000000000000..ac9c49511aa0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/analyzer.exp
@@ -0,0 +1,49 @@ 
+#   Copyright (C) 2019 Free Software Foundation, Inc.
+
+# This program 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 GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+
+# If the analyzer has not been enabled, bail.
+if { ![check_effective_target_analyzer] } {
+    return
+}
+
+global DEFAULT_CFLAGS
+if [info exists DEFAULT_CFLAGS] then {
+  set save_default_cflags $DEFAULT_CFLAGS
+}
+
+# If a testcase doesn't have special options, use these.
+set DEFAULT_CFLAGS "-fanalyzer -fdiagnostics-path-format=separate-events -Wanalyzer-too-complex -fanalyzer-call-summaries"
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \
+	"" $DEFAULT_CFLAGS
+
+# All done.
+dg-finish
+
+if [info exists save_default_cflags] {
+  set DEFAULT_CFLAGS $save_default_cflags
+} else {
+  unset DEFAULT_CFLAGS
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c b/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
new file mode 100644
index 000000000000..8c27b3ae7a45
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c
@@ -0,0 +1,81 @@ 
+#include <stdlib.h>
+
+extern void foo(void *ptrA, void *ptrB, void *ptrC) /* { dg-message "argument 1 of 'foo' must be non-null" } */
+  __attribute__((nonnull (1, 3)));
+
+extern void bar(void *ptrA, void *ptrB, void *ptrC) /* { dg-message "argument 1 of 'bar' must be non-null" } */
+  __attribute__((nonnull));
+
+// TODO: complain about NULL and possible NULL args
+// FIXME: ought to complain about NULL args
+
+void test_1 (void *p, void *q, void *r)
+{
+  foo(p, q, r);
+  foo(NULL, q, r);
+  foo(p, NULL, r);
+  foo(p, q, NULL);
+}
+
+void test_1a (void *q, void *r)
+{
+  void *p = NULL;
+  foo(p, q, r); /* { dg-warning "use of NULL 'p' where non-null expected" } */
+  /* { dg-message "argument 1 \\('p'\\) NULL where non-null expected" "" { target *-*-* } .-1 } */
+}
+
+void test_2 (void *p, void *q, void *r)
+{
+  bar(p, q, r);
+  bar(NULL, q, r);
+  bar(p, NULL, r);
+  bar(p, q, NULL);
+}
+
+void test_3 (void *q, void *r)
+{
+  void *p = malloc(1024); /* { dg-message "\\(1\\) this call could return NULL" } */
+
+  foo(p, q, r); /* { dg-warning "use of possibly-NULL 'p' where non-null expected" } */
+  /* { dg-message "argument 1 \\('p'\\) from \\(1\\) could be NULL where non-null expected" "" { target *-*-* } .-1 } */
+
+  foo(p, q, r);
+
+  free(p);
+}
+
+void test_4 (void *q, void *r)
+{
+  void *p = malloc(1024); /* { dg-message "\\(1\\) this call could return NULL" } */
+
+  bar(p, q, r); /* { dg-warning "use of possibly-NULL 'p' where non-null expected" } */
+  /* { dg-message "argument 1 \\('p'\\) from \\(1\\) could be NULL where non-null expected" "" { target *-*-* } .-1 } */
+
+  bar(p, q, r);
+
+  free(p);
+}
+
+/* Verify that we detect passing NULL to a __attribute__((nonnull)) function
+   when it's called via a function pointer.  */
+
+typedef void (*bar_t)(void *ptrA, void *ptrB, void *ptrC);
+
+static bar_t __attribute__((noinline))
+get_bar (void)
+{
+  return bar;
+}
+
+void test_5 (void *q, void *r)
+{
+  void *p = malloc(1024); /* { dg-message "\\(1\\) this call could return NULL" } */
+  bar_t cb = get_bar ();
+  cb(p, q, r); /* { dg-warning "use of possibly-NULL 'p' where non-null expected" } */
+  /* { dg-message "argument 1 \\('p'\\) from \\(1\\) could be NULL where non-null expected" "" { target *-*-* } .-1 } */
+  /* TODO: do we want an event showing where cb is assigned "bar"?  */
+
+  cb(p, q, r);
+
+  free(p);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/call-summaries-1.c b/gcc/testsuite/gcc.dg/analyzer/call-summaries-1.c
new file mode 100644
index 000000000000..a64b230cef32
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/call-summaries-1.c
@@ -0,0 +1,14 @@ 
+/* { dg-additional-options "-fanalyzer-call-summaries" } */
+
+#include <stdlib.h>
+
+void calls_free (void *p)
+{
+  free (p); /* { dg-warning "double-'free' of 'p'" } */
+}
+
+void test (void *q)
+{
+  calls_free (q);
+  calls_free (q);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals-2.c b/gcc/testsuite/gcc.dg/analyzer/conditionals-2.c
new file mode 100644
index 000000000000..6f291f4861b2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/conditionals-2.c
@@ -0,0 +1,45 @@ 
+// TODO: run this test case at every optimization level
+/* { dg-additional-options "-O2" } */
+
+#include <stddef.h>
+#include "analyzer-decls.h"
+
+#define Z_NULL 0
+
+static void __attribute__((noinline))
+test_1_callee (void *p, void *q)
+{
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  __analyzer_eval (p == Z_NULL); /* { dg-warning "FALSE" } */
+  __analyzer_eval (p != Z_NULL); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q == Z_NULL); /* { dg-warning "FALSE" } */ 
+  __analyzer_eval (q != Z_NULL); /* { dg-warning "TRUE" } */ 
+}
+
+void test_1 (void *p, void *q)
+{
+  if (p == Z_NULL || q == Z_NULL)
+    return;
+
+  test_1_callee (p, q);
+}
+
+static void __attribute__((noinline))
+test_2_callee (void *p, void *q)
+{
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  __analyzer_eval (p == Z_NULL); /* { dg-warning "FALSE" } */
+  __analyzer_eval (p != Z_NULL); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q == Z_NULL); /* { dg-warning "FALSE" } */ 
+  __analyzer_eval (q != Z_NULL); /* { dg-warning "TRUE" } */ 
+}
+
+void test_2 (void *p, void *q)
+{
+  if (p != Z_NULL && q != Z_NULL)
+    test_2_callee (p, q);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals-3.c b/gcc/testsuite/gcc.dg/analyzer/conditionals-3.c
new file mode 100644
index 000000000000..5f29f215dd17
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/conditionals-3.c
@@ -0,0 +1,47 @@ 
+/* { dg-additional-options "-fno-analyzer-state-merge" } */
+
+#include "analyzer-decls.h"
+
+static void only_called_when_flag_a_true (int i)
+{
+  __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+}
+
+static void only_called_when_flag_b_true (int i)
+{
+  __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+}
+
+int test_1 (int flag_a, int flag_b)
+{
+  int i = 17;
+
+  __analyzer_eval (flag_a); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */
+
+  if (flag_a)
+    {
+      __analyzer_eval (flag_a); /* { dg-warning "TRUE" } */
+      __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */
+      i = 42;
+    }
+
+  __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */
+
+  if (flag_a)
+    {
+      __analyzer_eval (flag_a); /* { dg-warning "TRUE" } */
+      __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+      __analyzer_eval (i == 17); /* { dg-warning "FALSE" } */
+      only_called_when_flag_a_true (i);
+    }  
+  else
+    {
+      __analyzer_eval (flag_a); /* { dg-warning "FALSE" } */
+      __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i == 42); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+      only_called_when_flag_b_true (i);
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c b/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c
new file mode 100644
index 000000000000..8e4ea5f58a71
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c
@@ -0,0 +1,159 @@ 
+/* { dg-additional-options "-fno-analyzer-transitivity" } */
+#include "analyzer-decls.h"
+
+void test (int i, int j)
+{
+  if (i > 4)
+    {
+      __analyzer_eval (i > 4); /* { dg-warning "TRUE" } */
+      __analyzer_eval (i <= 4); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i > 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+
+      __analyzer_eval (i > 5); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i != 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+
+      __analyzer_eval (i == 3); /* { dg-warning "FALSE" "" { xfail *-*-* } } */
+      /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+
+      __analyzer_eval (i != 4); /* { dg-warning "TRUE" } */
+      __analyzer_eval (i == 4); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i == 5); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i != 5); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i < 5); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i <= 5); /* { dg-warning "UNKNOWN" } */
+
+      /* Tests of transitivity.  */
+      if (j < i)
+	{
+	  __analyzer_eval (j < i); /* { dg-warning "TRUE" } */
+	  __analyzer_eval (j <= 4); /* { dg-warning "UNKNOWN" } */
+	}
+      else
+	{
+	  __analyzer_eval (j >= i); /* { dg-warning "TRUE" } */
+	  __analyzer_eval (j > 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+	  /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+	}
+    }
+  else
+    {
+      __analyzer_eval (i > 4); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i <= 4); /* { dg-warning "TRUE" } */
+      __analyzer_eval (i > 3); /* { dg-warning "UNKNOWN" } */
+
+      __analyzer_eval (i > 5); /* { dg-warning "FALSE" "" { xfail *-*-* } } */
+	  /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+      __analyzer_eval (i != 3); /* { dg-warning "UNKNOWN" } */
+
+      __analyzer_eval (i == 3); /* { dg-warning "UNKNOWN" } */
+
+      __analyzer_eval (i != 4); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i == 4); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i == 5); /* { dg-warning "FALSE" "" { xfail *-*-* } } */
+      /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+      __analyzer_eval (i != 5); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+      __analyzer_eval (i < 5); /* { dg-warning "TRUE" } */
+      __analyzer_eval (i <= 5); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+    }
+}
+
+void test_2 (int i, int j, int k)
+{
+  if (i >= j)
+    {
+      __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */
+      if (j >= k)
+	{
+	  __analyzer_eval (i >= k); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+	  /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+	  __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */
+	  if (k >= i)
+	    __analyzer_eval (i == k); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+	  /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+	}
+    }
+}
+
+void test_3 (int flag, unsigned int i)
+{
+  if (!flag) {
+    return;
+  }
+
+  __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+      
+  if (i>0) {
+    __analyzer_eval (i > 0); /* { dg-warning "TRUE" } */
+    __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+  } else {
+    __analyzer_eval (i <= 0); /* { dg-warning "TRUE" } */
+    __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+  }
+
+  __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+}
+
+void test_range_int_gt_lt (int i)
+{
+  if (i > 3)
+    if (i < 5)
+      __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+}
+
+void test_range_float_gt_lt (float f)
+{
+  if (f > 3)
+    if (f < 5)
+      __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_ge_lt (int i)
+{
+  if (i >= 4)
+    if (i < 5)
+      __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+}
+
+void test_range_float_ge_lt (float f)
+{
+  if (f >= 4)
+    if (f < 5)
+      __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_gt_le (int i)
+{
+  if (i > 3)
+    if (i <= 4)
+      __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+}
+
+void test_range_float_gt_le (float f)
+{
+  if (f > 3)
+    if (f <= 4)
+      __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_ge_le (int i)
+{
+  if (i >= 4)
+    if (i <= 4)
+      __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+}
+
+void test_range_float_ge_le (float f)
+{
+  if (f >= 4)
+    if (f <= 4)
+      __analyzer_eval (f == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals-trans.c b/gcc/testsuite/gcc.dg/analyzer/conditionals-trans.c
new file mode 100644
index 000000000000..ab34618c4110
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/conditionals-trans.c
@@ -0,0 +1,144 @@ 
+/* { dg-additional-options "-fanalyzer-transitivity" } */
+#include "analyzer-decls.h"
+
+void test (int i, int j)
+{
+  if (i > 4)
+    {
+      __analyzer_eval (i > 4); /* { dg-warning "TRUE" } */
+      __analyzer_eval (i <= 4); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i > 3); /* { dg-warning "TRUE" } */
+
+      __analyzer_eval (i > 5); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i != 3); /* { dg-warning "TRUE" } */
+
+      __analyzer_eval (i == 3); /* { dg-warning "FALSE" } */
+
+      __analyzer_eval (i != 4); /* { dg-warning "TRUE" } */
+      __analyzer_eval (i == 4); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i == 5); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i != 5); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i < 5); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i <= 5); /* { dg-warning "UNKNOWN" } */
+
+      /* Tests of transitivity.  */
+      if (j < i)
+	{
+	  __analyzer_eval (j < i); /* { dg-warning "TRUE" } */
+	  __analyzer_eval (j <= 4); /* { dg-warning "UNKNOWN" } */
+	}
+      else
+	{
+	  __analyzer_eval (j >= i); /* { dg-warning "TRUE" } */
+	  __analyzer_eval (j > 4); /* { dg-warning "TRUE" } */
+	}
+    }
+  else
+    {
+      __analyzer_eval (i > 4); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i <= 4); /* { dg-warning "TRUE" } */
+      __analyzer_eval (i > 3); /* { dg-warning "UNKNOWN" } */
+
+      __analyzer_eval (i > 5); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i != 3); /* { dg-warning "UNKNOWN" } */
+
+      __analyzer_eval (i == 3); /* { dg-warning "UNKNOWN" } */
+
+      __analyzer_eval (i != 4); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i == 4); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i == 5); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i != 5); /* { dg-warning "TRUE" } */
+      __analyzer_eval (i < 5); /* { dg-warning "TRUE" } */
+      __analyzer_eval (i <= 5); /* { dg-warning "TRUE" } */
+    }
+}
+
+void test_2 (int i, int j, int k)
+{
+  if (i >= j)
+    {
+      __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */
+      if (j >= k)
+	{
+	  __analyzer_eval (i >= k); /* { dg-warning "TRUE" } */
+	  __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */
+	  if (k >= i)
+	    __analyzer_eval (i == k); /* { dg-warning "TRUE" } */
+	}
+    }
+}
+
+void test_3 (int flag, unsigned int i)
+{
+  if (!flag) {
+    return;
+  }
+
+  __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+      
+  if (i>0) {
+    __analyzer_eval (i > 0); /* { dg-warning "TRUE" } */
+    __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+  } else {
+    __analyzer_eval (i <= 0); /* { dg-warning "TRUE" } */
+    __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+  }
+
+  __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+}
+
+void test_range_int_gt_lt (int i)
+{
+  if (i > 3)
+    if (i < 5)
+      __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */
+}
+
+void test_range_float_gt_lt (float f)
+{
+  if (f > 3)
+    if (f < 5)
+      __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_ge_lt (int i)
+{
+  if (i >= 4)
+    if (i < 5)
+      __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */
+}
+
+void test_range_float_ge_lt (float f)
+{
+  if (f >= 4)
+    if (f < 5)
+      __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_gt_le (int i)
+{
+  if (i > 3)
+    if (i <= 4)
+      __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */
+}
+
+void test_range_float_gt_le (float f)
+{
+  if (f > 3)
+    if (f <= 4)
+      __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_ge_le (int i)
+{
+  if (i >= 4)
+    if (i <= 4)
+      __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */
+}
+
+void test_range_float_ge_le (float f)
+{
+  if (f >= 4)
+    if (f <= 4)
+      __analyzer_eval (f == 4); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
new file mode 100644
index 000000000000..7260e3bb59ad
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c
@@ -0,0 +1,1085 @@ 
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "analyzer-decls.h"
+
+struct foo
+{
+  int i;
+};
+
+/* Fields of a local.  */
+
+void test_1 (void)
+{
+  struct foo f;
+  f.i = 1;
+  __analyzer_eval (f.i == 1); /* { dg-warning "TRUE" } */
+}
+
+/* Fields of a param.  */
+
+void test_2 (struct foo f)
+{
+  __analyzer_eval (f.i == 2); /* { dg-warning "UNKNOWN" } */
+  f.i = 2;
+  __analyzer_eval (f.i == 2); /* { dg-warning "TRUE" } */
+}
+
+/* Fields of a param ptr.  */
+
+void test_3 (struct foo *f)
+{
+  __analyzer_eval (f->i == 3); /* { dg-warning "UNKNOWN" } */
+  f->i = 3;
+  __analyzer_eval (f->i == 3); /* { dg-warning "TRUE" } */
+}
+
+/* Fields of a global ptr.  */
+struct foo *global_foo_ptr;
+
+void test_3a (void)
+{
+  struct foo *tmp = global_foo_ptr;
+  __analyzer_eval (global_foo_ptr->i == 3); /* { dg-warning "UNKNOWN" } */
+  global_foo_ptr->i = 3;
+  __analyzer_eval (global_foo_ptr->i == 3); /* { dg-warning "TRUE" } */
+}
+
+/* Pointer to a local.  */
+
+void test_4 (void)
+{
+  int i;
+  int *p = &i;
+  i = 1;
+  *p = 2;
+  __analyzer_eval (i == 2); /* { dg-warning "TRUE" } */
+}
+
+/* Local array.  */
+
+void test_5 (void)
+{
+  int a[10];
+  a[3] = 5; /* ARRAY_REF.  */
+  __analyzer_eval (a[3] == 5); /* { dg-warning "TRUE" } */
+}
+
+/* Local array, but using an unknown index.  */
+
+void test_5a (int idx)
+{
+  int a[10];
+  a[idx] = 5; /* ARRAY_REF.  */
+  __analyzer_eval (a[idx] == 5); /* { dg-warning "TRUE" } */
+}
+
+/* Array passed in as a param.  */
+
+void test_6 (int a[10])
+{
+  /* POINTER_PLUS_EXPR then a MEM_REF.  */
+  __analyzer_eval (a[3] == 42); /* { dg-warning "UNKNOWN" } */
+  a[3] = 42;
+  __analyzer_eval (a[3] == 42); /* { dg-warning "TRUE" } */
+}
+
+/* Array passed in as a param ptr.  */
+
+void test_7 (int *a)
+{
+  __analyzer_eval (a[3] == 42); /* { dg-warning "UNKNOWN" } */
+  a[3] = 42;
+  __analyzer_eval (a[3] == 42); /* { dg-warning "TRUE" } */
+}
+
+/* Globals.  */
+
+int glob_a;
+
+void test_10 (void)
+{
+  __analyzer_eval (glob_a == 42); /* { dg-warning "UNKNOWN" } */
+  glob_a = 42;
+  __analyzer_eval (glob_a == 42); /* { dg-warning "TRUE" } */
+}
+
+/* malloc.  */
+
+void test_11 (void)
+{
+  void *p = malloc (256);
+  void *q = malloc (256);
+
+  /* malloc results should be unique.  */
+  __analyzer_eval (p == q); /* { dg-warning "FALSE" } */
+  __analyzer_eval (p != q); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p <= q); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p >= q); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p < q); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p > q); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (p == p); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p != p); /* { dg-warning "FALSE" } */
+  __analyzer_eval (p <= p); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p >= p); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p < p); /* { dg-warning "FALSE" } */
+  __analyzer_eval (p > p); /* { dg-warning "FALSE" } */
+
+  free (p);
+  free (q);
+  // TODO: mark freed memory as freed
+  //__analyzer_break ();
+}
+
+/* alloca.  */
+
+void test_12 (void)
+{
+  void *p = alloca (256);
+  void *q = alloca (256);
+
+  /* alloca results should be unique.  */
+  __analyzer_eval (p == q); /* { dg-warning "FALSE" } */
+
+  // FIXME: complain about uses of poisoned values
+}
+
+/* Use of uninit value.  */
+int test_12a (void)
+{
+  int i;
+  return i; // FIXME: do we see the return stmt?
+}
+
+void test_12b (void *p, void *q)
+{
+  __analyzer_eval (p == q); /* { dg-warning "UNKNOWN" } */
+}
+
+int test_12c (void)
+{
+  int i;
+  int j;
+
+  j = i; // FIXME: should complain about this
+
+  return j;
+}
+
+struct coord
+{
+  int x;
+  int y;
+};
+
+int test_12d (struct coord c)
+{
+  struct coord d;
+  d = c;
+  __analyzer_eval (d.x == c.x); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  /* TODO(xfail): c and d share the same unknown value of type "coord", but
+     attempts to access the fields lead to different unknown values.  */
+
+  __analyzer_eval (d.y == c.y); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail): likewise
+
+  __analyzer_eval (d.x == d.y); /* { dg-warning "UNKNOWN" } */
+  /* d and c share an unknown value of type "struct coord".
+     But d.x and d.y should be different unknown values (although they inherit
+     from d's region).  */
+}
+
+/* Nested structs.  */
+
+struct outer
+{
+  struct middle {
+    struct inner {
+      float f;
+    } in;
+  } mid;
+};
+
+void test_13 (struct outer *o)
+{
+  __analyzer_eval (o->mid.in.f == 0.f); /* { dg-warning "UNKNOWN" } */
+  o->mid.in.f = 0.f;
+  __analyzer_eval (o->mid.in.f == 0.f); /* { dg-warning "TRUE" } */
+}
+
+void test_14 (struct outer o)
+{
+  __analyzer_eval (o.mid.in.f == 0.f); /* { dg-warning "UNKNOWN" } */
+  o.mid.in.f = 0.f;
+  __analyzer_eval (o.mid.in.f == 0.f); /* { dg-warning "TRUE" } */
+}
+
+void test_15 (const char *str)
+{
+  char ch = str[0];
+  __analyzer_eval (ch == 'a'); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (ch == str[0]); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+
+  ch = 'a';
+  __analyzer_eval (ch == 'a'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (str[0] == 'a'); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_16 (void)
+{
+  const char *msg = "hello world";
+
+  __analyzer_eval (msg != NULL); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (msg[0] == 'h'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+
+  __analyzer_eval (msg[1] == 'e'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+
+  __analyzer_eval (strlen (msg) == 11); /* { dg-warning "TRUE" } */
+}
+
+static const char *__attribute__((noinline))
+get_hello_world (void)
+{
+  return "hello world";
+}
+
+void test_16_alt (void)
+{
+  const char *msg = get_hello_world ();
+
+  __analyzer_eval (msg != NULL); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (msg[0] == 'h'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+
+  __analyzer_eval (msg[1] == 'e'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+
+  __analyzer_eval (strlen (msg) == 11); /* { dg-warning "TRUE" } */
+}
+
+void test_16a (const char *msg)
+{
+  __analyzer_eval (strlen (msg) == 11); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_16b (const char *msg)
+{
+  __analyzer_eval (strlen (msg) == strlen (msg)); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+}
+
+extern int unknown_result (void);
+
+void test_16c (int i)
+{
+  int j;
+
+  j = i;
+  __analyzer_eval (j == i); /* { dg-warning "TRUE" } */
+
+  j = unknown_result ();
+  __analyzer_eval (j == i); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_16c_a (void)
+{
+  int i, j;
+
+  i = unknown_result ();
+  j = unknown_result ();
+  __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */
+}
+
+int global_int_16d;
+
+void test_16d (int i)
+{
+  global_int_16d = i;
+  __analyzer_eval (global_int_16d == i); /* { dg-warning "TRUE" } */
+
+  global_int_16d = unknown_result ();
+  __analyzer_eval (global_int_16d == i); /* { dg-warning "UNKNOWN" } */
+}
+
+extern void might_write_to (int *);
+
+void test_16e (int i)
+{
+  int j;
+
+  j = i;
+  __analyzer_eval (j == i); /* { dg-warning "TRUE" } */
+
+  might_write_to (&j);
+  __analyzer_eval (j == i); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+  /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+}
+
+/* TODO: and more complicated graph-like examples, where anything that's
+   reachable from the pointer might be modified.  */
+
+void test_17 (int i)
+{
+  int j = 42;
+  __analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */
+  i = j;
+  __analyzer_eval (i == j); /* { dg-warning "TRUE" } */
+}
+
+void test_18 (int i)
+{
+  int j;
+  __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+
+  j = i;
+
+  __analyzer_eval (i == j); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i >= j); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i <= j); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (i != j); /* { dg-warning "FALSE" } */
+  __analyzer_eval (i > j); /* { dg-warning "FALSE" } */
+  __analyzer_eval (i < j); /* { dg-warning "FALSE" } */
+}
+
+void test_19 (void)
+{
+  int i, j;
+  /* Compare two uninitialized locals.  */
+    __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_20 (int i, int j)
+{
+  __analyzer_eval (i + 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i + j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i - 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i - j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i * 2); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i * j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i / 2); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i / j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i % 2); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i % j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i & 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i & j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i | 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i | j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i ^ 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i ^ j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i >> 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i >> j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i << 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i << j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i && 0); /* { dg-warning "FALSE" } */
+  __analyzer_eval (i && 1); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i && j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i || 0); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (i || 1); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i || j); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval (~i); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (-i); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (+i); /* { dg-warning "UNKNOWN" } */
+
+  /* Anything added above should be added to the next function also.  */
+}
+
+/* As above, but where the values are known due to the region model,
+   but not known to GCC's regular optimizations (folding and SSA).  */
+
+void test_21 (void)
+{
+  int i, j, zero;
+  int *pi = &i;
+  int *pj = &j;
+  int *pzero = &zero;
+  *pi = 5;
+  *pj = 3;
+  *pzero = 0;
+
+  __analyzer_eval (i + j == 8); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i - j == 2); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i * j == 15); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i / j == 1); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i % j == 2); /* { dg-warning "TRUE" } */
+
+  /* Division by zero.  */
+  // TODO: should we warn for this?
+  __analyzer_eval (i / zero); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (i % zero); /* { dg-warning "UNKNOWN" } */
+
+  __analyzer_eval ((i & 1) == (5 & 1)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i & j) == (5 & 3)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i | 1) == (5 | 1)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i | j) == (5 | 3)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i ^ 1) == (5 ^ 1)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i ^ j) == (5 ^ 3)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i >> 1) == (5 >> 1)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i >> j) == (5 >> 3)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i << 1) == (5 << 1)); /* { dg-warning "TRUE" } */
+  __analyzer_eval ((i << j) == (5 << 3)); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i && 0); /* { dg-warning "FALSE" } */
+  __analyzer_eval (i && 1); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i && j); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (i || 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i || 1); /* { dg-warning "TRUE" } */
+  __analyzer_eval (i || j); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (~i == ~5); /* { dg-warning "TRUE" } */
+  __analyzer_eval (-i == -5); /* { dg-warning "TRUE" } */
+  __analyzer_eval (+i == +5); /* { dg-warning "TRUE" } */
+}
+
+void test_22 (int i, int j)
+{
+  __analyzer_eval (i + j == i + j); /* { dg-warning "TRUE" } */
+  // FIXME: this is getting folded; can we build a non-folded equivalent?
+}
+
+void test_23 (struct foo *f, struct foo *g)
+{
+  int i, j, k;
+  i = f->i + g->i;
+  j = f->i + g->i;
+  k = f->i * g->i;
+  __analyzer_eval (i == j); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  /* TODO(xfail): we'd need to record that the two unknown values are both
+     the sum of the two unknown input values (and thus are the same); not
+     yet sure if we want arbitrary expression trees in the representation
+     (analysis termination concerns).  */
+
+  __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_24 (struct foo *f)
+{
+  struct foo g;
+  g.i = 42;
+  __analyzer_eval (g.i == 42); /* { dg-warning "TRUE" } */
+
+  /* Overwriting a whole struct should invalidate our knowledge
+     about fields within it.  */
+  g = *f;
+  __analyzer_eval (g.i == 42); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+  /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+}
+
+void test_25 (struct foo *f)
+{
+  struct foo g;
+  g.i = 42;
+  f->i = 43;
+  __analyzer_eval (f->i == 43); /* { dg-warning "TRUE" } */
+  __analyzer_eval (g.i == 42); /* { dg-warning "TRUE" } */
+
+  /* Overwriting a whole struct where we know things about the
+     source value should update our knowledge about fields within
+     the dest value.  */
+  g = *f;
+  __analyzer_eval (g.i == 43); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */
+  // TODO(xfail)
+}
+
+void test_26 (struct coord *p, struct coord *q)
+{
+  p->x = 42;
+  q->y = 17;
+  __analyzer_eval (p->x == 42); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p->y); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (q->x); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (q->y == 17); /* { dg-warning "TRUE" } */
+
+  /* Overwriting a whole struct where we know some things about the
+     source value should update our knowledge about fields within
+     the dest value.  */
+  *p = *q;
+  __analyzer_eval (p->x); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+  /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */
+  // TODO(xfail): should have been overwritten
+  __analyzer_eval (p->y == 17); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail): should have been overwritten with q->y
+
+  __analyzer_eval (q->x); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (q->y == 17); /* { dg-warning "TRUE" } */
+}
+
+void test_27 (struct coord *p)
+{
+  memset (p, 0, sizeof (struct coord));
+  __analyzer_eval (p->x == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+  __analyzer_eval (p->y == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+}
+
+void test_28 (struct coord *p)
+{
+  memset (p, 0, sizeof (struct coord) * 10);
+  __analyzer_eval (p[0].x == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+  __analyzer_eval (p[0].y == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+
+  __analyzer_eval (p[9].x == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+  __analyzer_eval (p[9].y == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail):
+
+  __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_29 (struct coord *p)
+{
+  struct coord *q;
+
+  p[0].x = 100024;
+  p[0].y = 100025;
+
+  p[7].x = 107024;
+  p[7].y = 107025;
+
+  p[9].x = 109024;
+  p[9].y = 109025;
+
+  __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */
+
+  q = &p[7];
+
+  __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" } */
+
+  q += 2;
+
+  __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */
+
+  q -= 2;
+
+  __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+}
+
+void test_29a (struct coord p[])
+{
+  struct coord *q;
+
+  p[0].x = 100024;
+  p[0].y = 100025;
+
+  p[7].x = 107024;
+  p[7].y = 107025;
+
+  p[9].x = 109024;
+  p[9].y = 109025;
+
+  __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */
+
+  q = &p[7];
+
+  __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" } */
+
+  q += 2;
+
+  __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */
+
+  q -= 2;
+
+  __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+}
+
+void test_29b (void)
+{
+  struct coord p[11];
+  struct coord *q;
+
+  p[0].x = 100024;
+  p[0].y = 100025;
+
+  p[7].x = 107024;
+  p[7].y = 107025;
+
+  p[9].x = 109024;
+  p[9].y = 109025;
+
+  __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */
+
+  q = &p[7];
+
+  __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" } */
+
+  q += 2;
+
+  __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */
+
+  q -= 2;
+
+  __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+}
+
+void test_29c (int len)
+{
+  struct coord p[len];
+  struct coord *q;
+
+  p[0].x = 100024;
+  p[0].y = 100025;
+
+  p[7].x = 107024;
+  p[7].y = 107025;
+
+  p[9].x = 109024;
+  p[9].y = 109025;
+
+  __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */
+
+  q = &p[7];
+
+  __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" } */
+
+  q += 2;
+
+  __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */
+
+  q -= 2;
+
+  __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+}
+
+void test_30 (void *ptr)
+{
+  struct coord *p = (struct coord *)ptr;
+  struct coord *q = (struct coord *)ptr;
+
+  p->x = 42;
+
+  __analyzer_eval (p->x == 42); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q->x == 42); /* { dg-warning "TRUE" } */
+}
+
+void test_31 (unsigned i)
+{
+  int j, k;
+
+  j = i < 100 ? i : 100; /* MIN_EXPR.  */
+  k = i < 100 ? 100 : i; /* MAX_EXPR.  */
+}
+
+enum color
+{
+  RED,
+  GREEN,
+  BLUE
+};
+
+void test_32 (enum color c)
+{
+  __analyzer_eval (c == GREEN); /* { dg-warning "UNKNOWN" } */
+
+  c = RED;
+
+  __analyzer_eval (c == RED); /* { dg-warning "TRUE" } */
+  __analyzer_eval (c == GREEN); /* { dg-warning "FALSE" } */
+}
+
+void test_33 (void)
+{
+  static int s;
+
+  __analyzer_eval (s == 42); /* { dg-warning "UNKNOWN" } */
+
+  s = 42;
+
+  __analyzer_eval (s == 42); /* { dg-warning "TRUE" } */
+}
+
+static int __attribute__((noinline))
+only_called_by_test_34 (int parm)
+{
+  __analyzer_eval (parm == 42); /* { dg-warning "TRUE" } */
+
+  return parm * 2;
+}
+
+void test_34 (void)
+{
+  int result = only_called_by_test_34 (42);
+  __analyzer_eval (result == 84); /* { dg-warning "TRUE" } */
+}
+
+void test_35 (int i, int j)
+{
+  __analyzer_eval (&i == &i); /* { dg-warning "TRUE" } */
+  __analyzer_eval (&i != &j); /* { dg-warning "TRUE" } */
+}
+
+static void __attribute__((noinline))
+write_through_ptr (int *dst, int val)
+{
+  *dst = val;
+}
+
+void test_36 (int i)
+{
+  __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+
+  write_through_ptr (&i, 42);
+
+  __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+}
+
+/* Read through uninitialized pointer.  */
+
+int test_37 (void)
+{
+  int *ptr;
+  return *ptr; /* { dg-warning "use of uninitialized value 'ptr'" } */
+}
+
+/* Write through uninitialized pointer.  */
+
+void test_37a (int i)
+{
+  int *ptr;
+  *ptr = i; /* { dg-warning "use of uninitialized value 'ptr'" } */
+}
+
+// TODO: the various other ptr deref poisonings
+
+/* Read through NULL pointer.  */
+
+int test_38 (void)
+{
+  int *ptr = NULL;
+  return *ptr; /* { dg-warning "dereference of NULL 'ptr'" } */
+}
+
+/* Write through NULL pointer.  */
+
+int test_38a (int i)
+{
+  int *ptr = NULL;
+  *ptr = i; /* { dg-warning "dereference of NULL 'ptr'" } */
+}
+
+/* Read through non-NULL constant pointer.  */
+
+int test_39 (void)
+{
+  int *ptr = (int *)0x1000;
+  return *ptr;
+}
+
+int test_40 (int flag)
+{
+  int i;
+  if (flag)
+    i = 43;
+  else
+    i = 17;
+
+  /* With state-merging, we lose the relationship between 'flag' and 'i'.  */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  if (flag)
+    __analyzer_eval (i == 43); /* { dg-warning "UNKNOWN" } */
+  else
+    __analyzer_eval (i == 17); /* { dg-warning "UNKNOWN" } */
+}
+
+struct link
+{
+  struct link *next;
+  int f;
+};
+
+/* Traversing a singly-linked list.  */
+
+void foo (struct link *in)
+{
+  struct link a;
+  struct link b;
+  struct link c;
+  a.next = &b;
+  b.next = &c;
+  in->next = &a;
+  c.f = 42;
+  __analyzer_eval (in->next->next->next->f == 42);  /* { dg-warning "TRUE" } */
+}
+
+union u
+{
+  int i;
+  int *ptr;
+};
+
+void test_41 (void)
+{
+  union u u;
+  u.i = 42;
+  __analyzer_eval (u.i == 42); /* { dg-warning "TRUE" } */
+  __analyzer_eval (u.ptr == NULL); /* { dg-warning "UNKNOWN" } */
+
+  /* Writes to a union member should invalidate knowledge about other members.  */
+  u.ptr = NULL;
+  __analyzer_eval (u.ptr == NULL); /* { dg-warning "TRUE" } */
+  __analyzer_eval (u.i == 42); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_42 (void)
+{
+  int i;
+  float f;
+  i = 42;
+  f = i;
+  __analyzer_eval (f == 42.0); /* { dg-warning "TRUE" } */
+}
+
+void test_43 (void)
+{
+  int i;
+  float f;
+  f = 42.0f;
+  i = f;
+  __analyzer_eval (i == 42);  /* { dg-warning "TRUE" } */
+}
+
+struct sbits
+{
+  int b0 : 1;
+  int b123 : 3;
+  int b456 : 3;
+  int b7 : 1;
+};
+
+void test_44 (void)
+{
+  struct sbits bits;
+  bits.b0 = 1;
+  __analyzer_eval (bits.b0 == 1); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */
+  // TODO(xfail): ^^^^
+
+  bits.b456 = 5;
+  __analyzer_eval (bits.b456 == 5); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */
+  // TODO(xfail): ^^^^
+};
+
+struct ubits
+{
+  unsigned int b0 : 1;
+  unsigned int b123 : 3;
+  unsigned int b456 : 3;
+  unsigned int b7 : 1;
+};
+
+/* FIXME: this requires BIT_FIELD_REF to work.  */
+
+void test_45 (void)
+{
+  struct ubits bits;
+  bits.b0 = 1;
+  __analyzer_eval (bits.b0 == 1); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail): ^^^^
+
+  bits.b456 = 5;
+  __analyzer_eval (bits.b456 == 5); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  // TODO(xfail): ^^^^
+};
+
+extern const char *char_ptr;
+
+int test_46 (void)
+{
+  if (strcmp("literal", char_ptr))
+    return 1;
+  return 0;
+}
+
+char test_47 (void)
+{
+  static const char* my_version = "1.1.3";
+  return my_version[0];
+}
+
+unsigned test_48 (unsigned char *p, unsigned char *q)
+{
+  return (unsigned int)(p - q);
+}
+
+typedef struct {
+  const char *filename;
+  short lineno;
+} loc;
+
+static loc loc_last;
+
+void test_49 (void)
+{
+  loc_last = __extension__(loc) { "", 328 };
+  loc_last = __extension__(loc) { "", 333 };
+}
+
+void test_50 (void *p, void *q)
+{
+  __analyzer_eval (p == q); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p == p); /* { dg-warning "TRUE" } */
+  __analyzer_eval (q == q); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p == (struct coord *)p); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p == (const struct coord *)p); /* { dg-warning "TRUE" } */
+
+  struct coord *cp = (struct coord *)p;
+  __analyzer_eval (p == cp); /* { dg-warning "TRUE" } */
+
+  struct coord *cq = (struct coord *)q;
+  __analyzer_eval (q == cq); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (cp == cq); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_51 (struct coord c)
+{
+  struct coord d;
+  memcpy (&d, &c, sizeof (struct coord));
+  __analyzer_eval (c.x == d.x); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  __analyzer_eval (c.y == d.y); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+}
+
+struct big
+{
+  int ia[1024];  
+};
+
+void test_52 (struct big b)
+{
+  struct big d;
+  memcpy (&d, &b, sizeof (struct big));
+  __analyzer_eval (b.ia[0] == d.ia[0]); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+}
+
+void test_53 (const char *msg)
+{
+  (void)fprintf(stderr, "LOG: %s", msg);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-10.c b/gcc/testsuite/gcc.dg/analyzer/data-model-10.c
new file mode 100644
index 000000000000..c261edcc827f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-10.c
@@ -0,0 +1,17 @@ 
+#include <stdlib.h>
+
+struct foo
+{
+  char **m_f;
+};
+
+struct foo *
+test (void)
+{
+  struct foo *new_table = (struct foo *) malloc(sizeof(struct foo));
+  if (!new_table)
+    return NULL;
+  new_table->m_f = (char **)malloc(sizeof(char **));
+  *new_table->m_f = NULL; /* { dg-warning "dereference of possibly-NULL '<unknown>'" } */ // FIXME: something better than "unknown" here
+  return new_table;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-11.c b/gcc/testsuite/gcc.dg/analyzer/data-model-11.c
new file mode 100644
index 000000000000..27663247c327
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-11.c
@@ -0,0 +1,6 @@ 
+int test (void)
+{
+  unsigned char *s = "abc";
+  char *t = "xyz";
+  return s[1] + t[1];
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-12.c b/gcc/testsuite/gcc.dg/analyzer/data-model-12.c
new file mode 100644
index 000000000000..653b7ad62846
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-12.c
@@ -0,0 +1,13 @@ 
+/* Mismatching decl of foo.  */
+
+int foo ();
+
+int bar (void)
+{
+  return foo() + 1;
+}
+
+int foo (int x, int y)
+{
+  return x * y;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-13.c b/gcc/testsuite/gcc.dg/analyzer/data-model-13.c
new file mode 100644
index 000000000000..e7cb8455b7dc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-13.c
@@ -0,0 +1,21 @@ 
+#include <stdlib.h>
+
+union
+{
+  void *ptr_val;
+  int int_val;
+} global_union;
+
+void test_1 (void)
+{
+  global_union.ptr_val = malloc (1024);
+}
+
+void test_2 (void)
+{
+  global_union.ptr_val = malloc (1024); /* { dg-message "allocated here" } */
+  global_union.int_val = 0;
+} /* { dg-warning "leak of '<unknown>' " } */
+/* TODO: something better than "<unknown>".  */
+/* TODO: better location for the leak.  */
+
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-14.c b/gcc/testsuite/gcc.dg/analyzer/data-model-14.c
new file mode 100644
index 000000000000..f9bb540b0229
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-14.c
@@ -0,0 +1,24 @@ 
+/* FIXME: we shouldn't need this.  */
+/* { dg-additional-options "-fanalyzer-fine-grained" } */
+
+#include <stdlib.h>
+
+void *global_ptr;
+
+void test_1 (int i)
+{
+  global_ptr = malloc (1024); /* { dg-message "allocated here" } */
+  *(int *)&global_ptr = i; /* { dg-warning "leak of '<unknown>'" } */
+  // TODO: something better than "<unknown>" here ^^^
+}
+
+void test_2 (int i)
+{
+  void *p = malloc (1024); /* { dg-message "allocated here" "" { xfail *-*-* } } */
+  // TODO(xfail)
+  global_ptr = p;
+  *(int *)&p = i;
+  p = global_ptr;
+  free (p);
+  free (global_ptr); /* { dg-warning "double-'free' of 'p'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-15.c b/gcc/testsuite/gcc.dg/analyzer/data-model-15.c
new file mode 100644
index 000000000000..12e84a12420b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-15.c
@@ -0,0 +1,34 @@ 
+#include <string.h>
+
+struct coord
+{
+  double x;
+  double y;
+  double z;
+};
+
+struct tri {
+  struct coord verts[3];
+};
+
+double test_1 (void)
+{
+  struct tri t;
+  memset (&t, 0, sizeof (struct tri));
+  return t.verts[1].y;
+}
+
+int test_2 (const struct coord *c1, const struct coord *c2, double r_squared)
+{
+  double dx = c1->x - c2->x;
+  double dy = c1->y - c2->y;
+  double dz = c1->z - c2->z;
+  return (dx * dx) + (dy * dy) + (dz * dz) <= r_squared;
+}
+
+int test_3 (const struct coord *c1, const struct coord *c2, struct coord *out)
+{
+  out->x = c1->x + c2->x;
+  out->y = c1->y + c2->y;
+  out->z = c1->z + c2->z;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-16.c b/gcc/testsuite/gcc.dg/analyzer/data-model-16.c
new file mode 100644
index 000000000000..a8acfbeeb77a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-16.c
@@ -0,0 +1,52 @@ 
+/* Labels as values.  */
+
+#include "analyzer-decls.h"
+
+extern void foo (void);
+
+void *x, *y, *z;
+
+void test (void)
+{
+ label0:
+  foo ();
+ label1:
+  foo ();
+ label2:
+  foo ();
+
+  x = &&label0;
+  y = &&label1;
+  z = &&label2;
+
+  __analyzer_eval (x == x); /* { dg-warning "TRUE" } */
+  __analyzer_eval (x == y); /* { dg-warning "FALSE" } */
+}
+
+void test_2 (int i)
+{
+  static void *array[] = { &&label0, &&label1, &&label2 };
+  goto *array[i];
+
+ label0:
+  foo ();
+ label1:
+  foo ();
+ label2:
+  foo ();
+}
+
+void test_3 (int i)
+{
+  static const int array[] = { &&label0 - &&label0,
+			       &&label1 - &&label0,
+			       &&label2 - &&label0 };
+  goto *(&&label0 + array[i]);
+
+ label0:
+  foo ();
+ label1:
+  foo ();
+ label2:
+  foo ();
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-17.c b/gcc/testsuite/gcc.dg/analyzer/data-model-17.c
new file mode 100644
index 000000000000..d50b84ab43ae
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-17.c
@@ -0,0 +1,20 @@ 
+typedef struct foo {} foo_t;
+
+typedef void (*func_t)(foo_t *s);
+
+void cb_1 (foo_t *s);
+void cb_2 (foo_t *s);
+
+typedef struct config_s {
+  func_t func;
+} config;
+
+static const config table[2] = {
+  { cb_1 },
+  { cb_2 }
+};
+
+int deflate (foo_t *s, int which)
+{
+  (*(table[which].func))(s);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-18.c b/gcc/testsuite/gcc.dg/analyzer/data-model-18.c
new file mode 100644
index 000000000000..7b096b0674d6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-18.c
@@ -0,0 +1,22 @@ 
+#include "analyzer-decls.h"
+
+void test (int *p, int i, int j)
+{
+  p[3] = 42;
+  __analyzer_eval (p[3] == 42); /* { dg-warning "TRUE" } */
+  __analyzer_eval (*(p + 3) == 42); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[i] == 42); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p[j] == 42); /* { dg-warning "UNKNOWN" } */
+
+  //__analyzer_dump ();
+
+  p[i] = 17;
+
+  //__analyzer_dump ();
+
+  __analyzer_eval (p[3] == 42); /* { dg-warning "UNKNOWN" } */
+  __analyzer_eval (p[i] == 17); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[j] == 17); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+  /* { dg-bogus "TRUE" "" { xfail *-*-* } .-1 } */
+  // FIXME(xfails) ^^^
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-19.c b/gcc/testsuite/gcc.dg/analyzer/data-model-19.c
new file mode 100644
index 000000000000..69978383d1b2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-19.c
@@ -0,0 +1,31 @@ 
+/* { dg-additional-options "-fgimple" } */
+
+typedef long long int i64;
+
+int __GIMPLE (ssa)
+test (i64 * pA, i64 iB)
+{
+  __complex__ long long int D_37702;
+  int D_37701;
+  long long int _1;
+  long long int _2;
+  long long int _3;
+  _Bool _4;
+  __complex__ long long int _8;
+  int _10;
+
+  __BB(2):
+  _1 = __MEM <i64> (pA_6(D));
+  _8 = .ADD_OVERFLOW (_1, iB_7(D));
+  _2 = __real _8;
+  __MEM <i64> (pA_6(D)) = _2;
+  _3 = __imag _8;
+  _4 = (_Bool) _3;
+  _10 = (int) _4;
+  goto __BB3;
+
+  __BB(3):
+L0:
+  return _10;
+
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-2.c b/gcc/testsuite/gcc.dg/analyzer/data-model-2.c
new file mode 100644
index 000000000000..d5be03ba5652
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-2.c
@@ -0,0 +1,13 @@ 
+/* { dg-additional-options "-O2" } */
+/* TODO:is there a way to automatically run the tests on various
+   optimizations levels, and with/without debuginfo, rather than
+   hardcoding options?  Adapt from torture .exp, presumably.  */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+int test_1 (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-3.c b/gcc/testsuite/gcc.dg/analyzer/data-model-3.c
new file mode 100644
index 000000000000..3d572eb8d73f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-3.c
@@ -0,0 +1,15 @@ 
+/* { dg-additional-options "-O2" } */
+/* TODO:is there a way to automatically run the tests on various
+   optimizations levels, and with/without debuginfo, rather than
+   hardcoding options?  Adapt from torture .exp, presumably.  */
+
+#include <stdio.h>
+int
+main ()
+{
+  FILE *f = fopen ("conftest.out", "w");
+  return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-4.c b/gcc/testsuite/gcc.dg/analyzer/data-model-4.c
new file mode 100644
index 000000000000..33f90871dfbc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-4.c
@@ -0,0 +1,16 @@ 
+/* { dg-additional-options "-fexceptions" } */
+/* TODO:is there a way to automatically run the tests on various
+   optimizations levels, and with/without debuginfo, rather than
+   hardcoding options?  Adapt from torture .exp, presumably.  */
+
+#include <stdio.h>
+int
+main ()
+{
+  FILE *f = fopen ("conftest.out", "w");
+  return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
new file mode 100644
index 000000000000..5cf334768d82
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5.c
@@ -0,0 +1,100 @@ 
+/* A toy re-implementation of CPython's object model.  */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct base_obj
+{
+  struct type_obj *ob_type;
+  int ob_refcnt;
+} base_obj;
+
+typedef struct type_obj
+{
+  base_obj tp_base;
+  void (*tp_dealloc) (base_obj *);
+} type_obj;
+
+typedef struct tuple_obj
+{
+  base_obj tup_base;
+  int num_elements;
+  base_obj elements[];
+} tuple_obj;
+
+typedef struct list_obj
+{
+  base_obj list_base;
+  int num_elements;
+  base_obj *elements;
+} list_obj;
+
+typedef struct string_obj
+{
+  base_obj str_base;
+  size_t str_len;
+  char str_buf[];
+} string_obj;
+
+extern void type_del (base_obj *);
+extern void tuple_del (base_obj *);
+extern void str_del (base_obj *);
+
+type_obj type_type = {
+  { &type_type, 1},
+  type_del
+};
+
+type_obj tuple_type = {
+  { &type_type, 1},
+  tuple_del
+};
+
+type_obj str_type = {
+  { &str_type, 1},
+  str_del
+};
+
+base_obj *alloc_obj (type_obj *ob_type, size_t sz)
+{
+  base_obj *obj = (base_obj *)malloc (sz);
+  if (!obj)
+    return NULL;
+  obj->ob_type = ob_type;
+  obj->ob_refcnt = 1;
+  return obj;
+}
+
+base_obj *new_string_obj (const char *str)
+{
+  //__analyzer_dump ();
+  size_t len = strlen (str);
+#if 1
+  string_obj *str_obj
+    = (string_obj *)alloc_obj (&str_type, sizeof (string_obj) + len + 1);
+#else
+  string_obj *str_obj = (string_obj *)malloc (sizeof (string_obj) + len + 1);
+  if (!str_obj)
+    return NULL;
+  str_obj->str_base.ob_type = &str_type;
+  str_obj->str_base.ob_refcnt = 1;
+#endif
+  str_obj->str_len = len; /* { dg-warning "dereference of NULL 'str_obj'" } */
+  memcpy (str_obj->str_buf, str, len);
+  str_obj->str_buf[len] = '\0';
+  return (base_obj *)str_obj;
+}
+
+void unref (base_obj *obj)
+{
+  if (--obj->ob_refcnt == 0) /* { dg-bogus "dereference of uninitialized pointer 'obj'" } */
+    obj->ob_type->tp_dealloc (obj);
+}
+
+void test_1 (const char *str)
+{
+  base_obj *obj = new_string_obj (str);
+  //__analyzer_dump();
+  unref (obj);
+} /* { dg-bogus "leak" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5b.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5b.c
new file mode 100644
index 000000000000..b0203af9975c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5b.c
@@ -0,0 +1,91 @@ 
+/* A toy re-implementation of CPython's object model.  */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct base_obj base_obj;
+typedef struct type_obj type_obj;
+typedef struct string_obj string_obj;
+
+struct base_obj
+{
+  struct type_obj *ob_type;
+  int ob_refcnt;
+};
+
+struct type_obj
+{
+  base_obj tp_base;
+  void (*tp_dealloc) (base_obj *);
+};
+
+struct string_obj
+{
+  base_obj str_base;
+  size_t str_len;
+  char str_buf[];
+};
+
+extern void type_del (base_obj *);
+extern void str_del (base_obj *);
+
+type_obj type_type = {
+  { &type_type, 1},
+  type_del
+};
+
+type_obj str_type = {
+  { &str_type, 1},
+  str_del
+};
+
+base_obj *alloc_obj (type_obj *ob_type, size_t sz)
+{
+  base_obj *obj = (base_obj *)malloc (sz);
+  if (!obj)
+    return NULL;
+  obj->ob_type = ob_type;
+  obj->ob_refcnt = 1;
+  return obj;
+}
+
+string_obj *new_string_obj (const char *str)
+{
+  //__analyzer_dump ();
+  size_t len = strlen (str);
+#if 1
+  string_obj *str_obj
+    = (string_obj *)alloc_obj (&str_type, sizeof (string_obj) + len + 1);
+#else
+  string_obj *str_obj = (string_obj *)malloc (sizeof (string_obj) + len + 1);
+  if (!str_obj)
+    return NULL;
+  str_obj->str_base.ob_type = &str_type;
+  str_obj->str_base.ob_refcnt = 1;
+#endif
+  str_obj->str_len = len; /* { dg-warning "dereference of NULL 'str_obj'" } */
+  memcpy (str_obj->str_buf, str, len);
+  str_obj->str_buf[len] = '\0';
+  return str_obj;
+}
+
+void unref (string_obj *obj)
+{
+  //__analyzer_dump();
+  if (--obj->str_base.ob_refcnt == 0)
+    {
+      //__analyzer_dump();
+      obj->str_base.ob_type->tp_dealloc ((base_obj *)obj); /* { dg-bogus "use of uninitialized value '<unknown>'" "" { xfail *-*-* } } */
+      // TODO (xfail): not sure what's going on here
+    }
+}
+
+void test_1 (const char *str)
+{
+  string_obj *obj = new_string_obj (str);
+  //__analyzer_dump();
+  if (obj)
+    unref (obj);
+} /* { dg-bogus "leak of 'obj'" "" { xfail *-*-* } } */
+// TODO (xfail): not sure why this is treated as leaking
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5c.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5c.c
new file mode 100644
index 000000000000..1e52350c6c16
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5c.c
@@ -0,0 +1,84 @@ 
+/* A toy re-implementation of CPython's object model.  */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct base_obj base_obj;
+typedef struct type_obj type_obj;
+typedef struct string_obj string_obj;
+
+struct base_obj
+{
+  struct type_obj *ob_type;
+  int ob_refcnt;
+};
+
+struct type_obj
+{
+  base_obj tp_base;
+};
+
+struct string_obj
+{
+  base_obj str_base;
+  size_t str_len;
+  char str_buf[];
+};
+
+type_obj type_type = {
+  { &type_type, 1},
+};
+
+type_obj str_type = {
+  { &str_type, 1},
+};
+
+base_obj *alloc_obj (type_obj *ob_type, size_t sz)
+{
+  base_obj *obj = (base_obj *)malloc (sz);
+  if (!obj)
+    return NULL;
+  obj->ob_type = ob_type;
+  obj->ob_refcnt = 1;
+  return obj;
+}
+
+string_obj *new_string_obj (const char *str)
+{
+  //__analyzer_dump ();
+  size_t len = strlen (str);
+#if 1
+  string_obj *str_obj
+    = (string_obj *)alloc_obj (&str_type, sizeof (string_obj) + len + 1);
+#else
+  string_obj *str_obj = (string_obj *)malloc (sizeof (string_obj) + len + 1);
+  if (!str_obj)
+    return NULL;
+  str_obj->str_base.ob_type = &str_type;
+  str_obj->str_base.ob_refcnt = 1;
+#endif
+  str_obj->str_len = len; /* { dg-warning "dereference of NULL 'str_obj'" } */
+  memcpy (str_obj->str_buf, str, len);
+  str_obj->str_buf[len] = '\0';
+  return str_obj;
+}
+
+void unref (string_obj *obj)
+{
+  //__analyzer_dump();
+  if (--obj->str_base.ob_refcnt == 0)
+    {
+      //__analyzer_dump();
+      free (obj);
+    }
+}
+
+void test_1 (const char *str)
+{
+  string_obj *obj = new_string_obj (str);
+  //__analyzer_dump();
+  if (obj)
+    unref (obj);
+} /* { dg-bogus "leak of 'obj'" "" { xfail *-*-* } } */
+// TODO (xfail): not sure why this is treated as leaking
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5d.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5d.c
new file mode 100644
index 000000000000..8c7bfa91a968
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5d.c
@@ -0,0 +1,64 @@ 
+/* A toy re-implementation of CPython's object model.  */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+typedef struct base_obj base_obj;
+typedef struct type_obj type_obj;
+typedef struct string_obj string_obj;
+
+struct base_obj
+{
+  struct type_obj *ob_type;
+  int ob_refcnt;
+};
+
+struct type_obj
+{
+  base_obj tp_base;
+};
+
+struct string_obj
+{
+  base_obj str_base;
+  size_t str_len;
+  char str_buf[];
+};
+
+type_obj type_type = {
+  { &type_type, 1},
+};
+
+type_obj str_type = {
+  { &str_type, 1},
+};
+
+base_obj *alloc_obj (type_obj *ob_type, size_t sz)
+{
+  base_obj *obj = (base_obj *)malloc (sz);
+  if (!obj)
+    return NULL;
+  obj->ob_type = ob_type;
+  obj->ob_refcnt = 1;
+  return obj;
+}
+
+void unref (base_obj *obj)
+{
+  //__analyzer_dump();
+  if (--obj->ob_refcnt == 0)
+    free (obj);
+}
+
+void test_1 ()
+{
+  base_obj *obj = alloc_obj (&str_type, sizeof (string_obj));
+  if (obj)
+    {
+      __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '1'" } */
+      unref (obj);
+      __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-6.c b/gcc/testsuite/gcc.dg/analyzer/data-model-6.c
new file mode 100644
index 000000000000..78a797ead76e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-6.c
@@ -0,0 +1,14 @@ 
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+/* Verify that we don't accumulate state after a malloc/free pair.  */
+
+void test (void)
+{
+  void *ptr;
+  __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */
+  ptr = malloc (1024);
+  __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '1'" } */
+  free (ptr);
+  __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-7.c b/gcc/testsuite/gcc.dg/analyzer/data-model-7.c
new file mode 100644
index 000000000000..67a681be607f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-7.c
@@ -0,0 +1,20 @@ 
+/* { dg-additional-options "-fno-analyzer-state-merge" } */
+#include "analyzer-decls.h"
+
+int test_40 (int flag)
+{
+  int i;
+  if (flag)
+    i = 43;
+  else
+    i = 17;
+
+  /* Without state-merging, we retain the relationship between 'flag' and 'i'.  */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  if (flag)
+    __analyzer_eval (i == 43); /* { dg-warning "TRUE" } */
+  else
+    __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-8.c b/gcc/testsuite/gcc.dg/analyzer/data-model-8.c
new file mode 100644
index 000000000000..aff903691960
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-8.c
@@ -0,0 +1,26 @@ 
+#include "analyzer-decls.h"
+
+struct base
+{
+  int i;
+};
+
+struct sub
+{
+  struct base b;
+  int j;
+};
+
+void test (void)
+{
+  struct sub s;
+  s.b.i = 3;
+  s.j = 4;
+  __analyzer_eval (s.b.i == 3); /* { dg-warning "TRUE" } */
+  __analyzer_eval (s.j == 4); /* { dg-warning "TRUE" } */
+
+  struct base *bp = (struct base *)&s;
+
+  __analyzer_eval (bp->i == 3); /* { dg-warning "TRUE"  "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-9.c b/gcc/testsuite/gcc.dg/analyzer/data-model-9.c
new file mode 100644
index 000000000000..bab4b573f5bd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-9.c
@@ -0,0 +1,33 @@ 
+#include <stdlib.h>
+#include <string.h>
+#include "analyzer-decls.h"
+
+struct foo
+{
+  int i;
+};
+
+/* TODO: verify that we know that calloc zeros its memory.  */
+
+void test_1 (void)
+{
+  struct foo *f = calloc (1, sizeof (struct foo));
+  if (f == NULL)
+    return;
+  __analyzer_eval (f->i == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+  free (f);
+}
+
+/* TODO: verify that we know the behavior of memset.  */
+
+void test_2 (void)
+{
+  struct foo *f = malloc (sizeof (struct foo));
+  if (f == NULL)
+    return;
+  memset (f, 0, sizeof (struct foo));
+  __analyzer_eval (f->i == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+  free (f);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c
new file mode 100644
index 000000000000..d7058ea18e03
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c
@@ -0,0 +1,13 @@ 
+#include <stddef.h>
+
+static int *__attribute__((noinline))
+callee (void)
+{
+  return NULL;
+}
+
+void test_1 (void)
+{
+  int *p = callee (); /* { dg-message "return of NULL to 'test_1' from 'callee'" } */
+  *p = 42; /* { dg-warning "dereference of NULL 'p'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/disabling.c b/gcc/testsuite/gcc.dg/analyzer/disabling.c
new file mode 100644
index 000000000000..a696d1e021e0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/disabling.c
@@ -0,0 +1,10 @@ 
+/* Verify that we can override -fanalyzer with -fno-analyzer.  */
+/* { dg-additional-options "-fno-analyzer" } */
+
+#include <stdlib.h>
+
+void test (void *ptr)
+{
+  free (ptr);
+  free (ptr); /* { dg-bogus "free" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/dot-output.c b/gcc/testsuite/gcc.dg/analyzer/dot-output.c
new file mode 100644
index 000000000000..586e14421e0e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/dot-output.c
@@ -0,0 +1,33 @@ 
+/* Verify that the various .dot output files from the analyzer are readable
+   by .dot.  */
+
+/* { dg-require-dot "" } */
+/* { dg-additional-options "-fdump-analyzer-callgraph -fdump-analyzer-exploded-graph -fdump-analyzer-state-purge -fdump-analyzer-supergraph" } */
+
+#include <stdlib.h>
+
+int some_call (int i, char ch)
+{
+  return i * i;
+}
+
+int *test (int *buf, int n, int *out)
+{
+  int i;
+  int *result = malloc (sizeof (int) * n);
+  
+  /* A loop, to ensure we have phi nodes.  */
+  for (i = 0; i < n; i++)
+    result[i] = buf[i] + i; /* { dg-warning "possibly-NULL" "" { xfail *-*-* } } */
+  /* TODO(xfail): why isn't the warning appearing? */
+
+  /* Example of a "'" (to test quoting).  */
+  *out = some_call (i, 'a');
+  
+  return result;
+}
+
+/* { dg-final { dg-check-dot "dot-output.c.callgraph.dot" } } */
+/* { dg-final { dg-check-dot "dot-output.c.eg.dot" } } */
+/* { dg-final { dg-check-dot "dot-output.c.state-purge.dot" } } */
+/* { dg-final { dg-check-dot "dot-output.c.supergraph.dot" } } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-a.c b/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-a.c
new file mode 100644
index 000000000000..61e784677324
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-a.c
@@ -0,0 +1,16 @@ 
+/* { dg-do link } */
+/* { dg-require-effective-target lto } */
+/* { dg-additional-options "-flto" } */
+/* { dg-additional-sources double-free-lto-1-b.c } */
+
+#include <stdlib.h>
+#include "double-free-lto-1.h"
+
+void test (void *ptr)
+{
+  calls_free (ptr); /* { dg-message "calling 'calls_free' from 'test'" } */
+  free (ptr); /* { dg-warning "double-'free' of 'ptr_.+'" } */
+  // TODO: report "ptr", rather than an SSA name
+}
+
+int main() { return 0; }
diff --git a/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-b.c b/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-b.c
new file mode 100644
index 000000000000..6041e6983a2f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-b.c
@@ -0,0 +1,8 @@ 
+#include <stdlib.h>
+
+#include "double-free-lto-1.h"
+
+extern void calls_free (void *ptr)
+{
+  free (ptr); 
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1.h b/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1.h
new file mode 100644
index 000000000000..c5e21570d00d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1.h
@@ -0,0 +1 @@ 
+extern void calls_free (void *ptr);
diff --git a/gcc/testsuite/gcc.dg/analyzer/equivalence.c b/gcc/testsuite/gcc.dg/analyzer/equivalence.c
new file mode 100644
index 000000000000..609b6fdef50c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/equivalence.c
@@ -0,0 +1,31 @@ 
+#include "analyzer-decls.h"
+
+void test (int p, int q, int r)
+{
+  if (p == 42)
+    {
+      __analyzer_eval (p == 42);  /* { dg-warning "TRUE" } */
+      __analyzer_eval (p != 42);  /* { dg-warning "FALSE" } */
+      if (q == 42)
+	{
+	  __analyzer_eval (p == q);  /* { dg-warning "TRUE" } */
+	}
+      else
+	{
+	  __analyzer_eval (p != q);  /* { dg-warning "TRUE" } */
+	}
+    }
+  else
+    {
+      __analyzer_eval (p == 42);  /* { dg-warning "FALSE" } */
+      __analyzer_eval (p != 42);  /* { dg-warning "TRUE" } */
+      if (q == 42)
+	{
+	  __analyzer_eval (p == q);  /* { dg-warning "FALSE" } */
+	}
+      else
+	{
+	  __analyzer_eval (p == q);  /* { dg-warning "UNKNOWN" } */
+	}
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/explode-1.c b/gcc/testsuite/gcc.dg/analyzer/explode-1.c
new file mode 100644
index 000000000000..6b62e8e871c8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/explode-1.c
@@ -0,0 +1,60 @@ 
+/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+
+#include <stdlib.h>
+
+extern int get (void);
+
+/* In theory each of p0...p8 can be in various malloc states,
+   independently, so the total combined number of states
+   at any program point within the loop is NUM_VARS * NUM_STATES.  */
+
+void test (void)
+{
+  void *p0, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+  void **pp;
+  while (get ())
+    {
+      switch (get ())
+	{
+	default:
+	case 0:
+	  pp = &p0;
+	  break;
+	case 1:
+	  pp = &p1;
+	  break;
+	case 2:
+	  pp = &p2;
+	  break;
+	case 3:
+	  pp = &p3;
+	  break;
+	case 4:
+	  pp = &p4;
+	  break;
+	case 5:
+	  pp = &p5;
+	  break;
+	case 6:
+	  pp = &p6;
+	  break;
+	case 7:
+	  pp = &p7;
+	  break;
+	}
+
+      switch (get ())
+	{
+	default:
+	case 0:
+	  *pp = malloc (16);
+	  break;
+	case 1:
+	  free (*pp);
+	  break;
+	case 2:
+	  /* no-op.  */
+	  break;
+	}
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/explode-2.c b/gcc/testsuite/gcc.dg/analyzer/explode-2.c
new file mode 100644
index 000000000000..d786aa93896b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/explode-2.c
@@ -0,0 +1,50 @@ 
+/* In theory each of p0...p3 can be in various malloc states,
+   independently, so the total combined number of states
+   at any program point within the loop is NUM_VARS * NUM_STATES.
+
+   Set the limits high enough that we can fully explore this.  */ 
+
+/* { dg-additional-options "--param analyzer-max-enodes-per-program-point=200 --param analyzer-bb-explosion-factor=50" } */
+
+#include <stdlib.h>
+
+extern int get (void);
+
+void test (void)
+{
+  void *p0, *p1, *p2, *p3;
+  while (get ())
+    {
+      switch (get ())
+	{
+	default:
+	case 0:
+	  p0 = malloc (16);
+	  break;
+	case 1:
+	  free (p0); /* { dg-warning "double-'free' of 'p0'" } */
+	  break;
+
+	case 2:
+	  p1 = malloc (16);
+	  break;
+	case 3:
+	  free (p1); /* { dg-warning "double-'free' of 'p1'" } */
+	  break;
+
+	case 4:
+	  p2 = malloc (16);
+	  break;
+	case 5:
+	  free (p2); /* { dg-warning "double-'free' of 'p2'" } */
+	  break;
+
+	case 6:
+	  p3 = malloc (16);
+	  break;
+	case 7:
+	  free (p3); /* { dg-warning "double-'free' of 'p3'" } */
+	  break;
+	}
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/factorial.c b/gcc/testsuite/gcc.dg/analyzer/factorial.c
new file mode 100644
index 000000000000..384713a245a4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/factorial.c
@@ -0,0 +1,7 @@ 
+int factorial (int n)
+{
+  if (n > 1)
+    return n * factorial (n - 1);
+  else
+    return 1;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/fibonacci.c b/gcc/testsuite/gcc.dg/analyzer/fibonacci.c
new file mode 100644
index 000000000000..5d4a4e02e39e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fibonacci.c
@@ -0,0 +1,9 @@ 
+int fib (int n)
+{
+  if (n > 1)
+    return fib (n - 1) + fib (n - 2);
+  else
+    return n;
+} 
+
+/* { dg-regexp "\[^\n\r\]+: warning: analysis bailed out early \\(\[0-9\]+ 'after-snode' enodes; \[0-9\]+ enodes\\) \[^\n\r\]*" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/fields.c b/gcc/testsuite/gcc.dg/analyzer/fields.c
new file mode 100644
index 000000000000..de55208070a2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fields.c
@@ -0,0 +1,41 @@ 
+typedef long unsigned int size_t;
+
+extern size_t strlen (const char *__s)
+  __attribute__ ((__nothrow__ , __leaf__))
+  __attribute__ ((__pure__))
+  __attribute__ ((__nonnull__ (1)));
+
+extern void *malloc (size_t __size)
+  __attribute__ ((__nothrow__ , __leaf__))
+  __attribute__ ((__malloc__)) ;
+
+extern void free (void *__ptr)
+  __attribute__ ((__nothrow__ , __leaf__));
+
+typedef struct _krb5_data {
+  unsigned int length;
+  char *data;
+} krb5_data;
+
+typedef struct _krb5_error {
+  krb5_data text;
+} krb5_error;
+
+extern const char *error_message (int);
+
+int
+recvauth_common (int problem)
+{
+  if (problem) {
+    krb5_error error;
+    const char *message = error_message(problem);
+    error.text.length = strlen(message) + 1;
+    if (!(error.text.data = malloc(error.text.length))) {
+      goto cleanup;
+    }
+    free(error.text.data);
+  }
+
+ cleanup:
+  return problem; /* { dg-bogus "leak" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/file-1.c b/gcc/testsuite/gcc.dg/analyzer/file-1.c
new file mode 100644
index 000000000000..91d9685d9e9e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/file-1.c
@@ -0,0 +1,37 @@ 
+#include <stdio.h>
+
+void
+test_1 (const char *path)
+{
+  FILE *f = fopen (path, "r"); /* { dg-message "opened here" } */
+  if (!f)
+    return;
+
+  fclose (f); /* { dg-message "\\(4\\) \\.\\.\\.to here" } */
+  /* { dg-message "\\(5\\) first 'fclose' here" "" { target *-*-* } .-1 } */
+  fclose (f); /* { dg-warning "double 'fclose' of FILE 'f'" } */ 
+  /* { dg-message "second 'fclose' here; first 'fclose' was at \\(5\\)" "" { target *-*-* } .-1 } */
+}
+
+void
+test_2 (const char *src, const char *dst)
+{
+  FILE *f_in = fopen (src, "r"); /* { dg-message "\\(1\\) opened here" } */
+  if (!f_in)
+    return;
+
+  FILE *f_out = fopen (src, "w");
+  if (!f_out)
+    return; /* { dg-warning "leak of FILE 'f_in'" } */
+  /* { dg-message "\\(7\\) 'f_in' leaks here; was opened at \\(1\\)" "" { target *-*-* } .-1 } */
+
+  fclose (f_out);
+  fclose (f_in);
+}
+
+void
+test_3 (const char *path)
+{
+  FILE *f = fopen (path, "r"); /* { dg-message "opened here" } */
+  return; /* { dg-warning "leak of FILE 'f'" } */ 
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/file-2.c b/gcc/testsuite/gcc.dg/analyzer/file-2.c
new file mode 100644
index 000000000000..aa0457071eaf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/file-2.c
@@ -0,0 +1,18 @@ 
+#include <stdio.h>
+
+struct foo
+{
+  FILE *m_f;
+};
+
+void test (const char *path)
+{
+  struct foo f;
+  f.m_f = fopen (path, "r");
+
+  if (!f.m_f)
+    return; /* { dg-bogus "leak of FILE" } */
+
+  fclose (f.m_f);
+  fclose (f.m_f); /* { dg-warning "double 'fclose' of FILE 'f.m_f'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/function-ptr-1.c b/gcc/testsuite/gcc.dg/analyzer/function-ptr-1.c
new file mode 100644
index 000000000000..38c3e597d7e0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/function-ptr-1.c
@@ -0,0 +1,8 @@ 
+#include <stdlib.h>
+
+typedef void *(*fn_ptr_t) (void *);
+
+void *test_1 (fn_ptr_t fn_ptr, void *data)
+{
+  return fn_ptr (data);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c b/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c
new file mode 100644
index 000000000000..411b1b39377f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c
@@ -0,0 +1,44 @@ 
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+typedef void (*fn_ptr_t) (void *);
+
+void
+calls_free (void *victim)
+{
+  free (victim);
+}
+
+void
+no_op (void *ptr)
+{
+}
+
+void test_1 (void *ptr)
+{
+  fn_ptr_t fn_ptr = calls_free;
+  __analyzer_eval (fn_ptr == calls_free); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fn_ptr != NULL); /* { dg-warning "TRUE" } */
+  __analyzer_eval (fn_ptr == NULL); /* { dg-warning "FALSE" } */
+  __analyzer_eval (fn_ptr == no_op); /* { dg-warning "FALSE" } */
+
+  fn_ptr (ptr);
+  fn_ptr (ptr);
+}
+// TODO: issue a double-'free' warning at 2nd call to fn_ptr.
+
+/* As above, but with an extra indirection to try to thwart
+   the optimizer.  */
+
+void test_2 (void *ptr, fn_ptr_t *fn_ptr)
+{
+  *fn_ptr = calls_free;
+  __analyzer_eval (*fn_ptr == calls_free); /* { dg-warning "TRUE" } */
+  __analyzer_eval (*fn_ptr != NULL); /* { dg-warning "TRUE" } */
+  __analyzer_eval (*fn_ptr == NULL); /* { dg-warning "FALSE" } */
+  __analyzer_eval (*fn_ptr == no_op); /* { dg-warning "FALSE" } */
+
+  (*fn_ptr) (ptr);
+  (*fn_ptr) (ptr);
+}
+// TODO: issue a double-'free' warning at 2nd call to fn_ptr.
diff --git a/gcc/testsuite/gcc.dg/analyzer/function-ptr-3.c b/gcc/testsuite/gcc.dg/analyzer/function-ptr-3.c
new file mode 100644
index 000000000000..348ee4a0cb3f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/function-ptr-3.c
@@ -0,0 +1,17 @@ 
+#include <stdlib.h>
+
+typedef void *(*alloc_func_t) (size_t);
+typedef void (*free_func_t) (void *);
+
+typedef struct callbacks
+{
+  alloc_func_t alloc_cb;
+  free_func_t dealloc_cb;
+} callbacks_t;
+
+void test (void)
+{
+  callbacks_t cb;
+  cb.alloc_cb = (alloc_func_t)0;
+  cb.dealloc_cb = (free_func_t)0;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/gzio-2.c b/gcc/testsuite/gcc.dg/analyzer/gzio-2.c
new file mode 100644
index 000000000000..855ecc8f73d4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/gzio-2.c
@@ -0,0 +1,11 @@ 
+void gzseek (long offset, int whence)
+{
+  if (whence == 2)
+    return;
+  if (whence == 0)
+    offset -= 1;
+  if (offset < 0)
+    return;
+  while (offset > 0) {
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/gzio-3.c b/gcc/testsuite/gcc.dg/analyzer/gzio-3.c
new file mode 100644
index 000000000000..0a11f65fdcaf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/gzio-3.c
@@ -0,0 +1,31 @@ 
+typedef long unsigned int size_t;
+typedef struct _IO_FILE FILE;
+extern size_t fread(void *__restrict __ptr, size_t __size, size_t __n,
+                    FILE *__restrict __stream);
+typedef unsigned char Byte;
+typedef unsigned int uInt;
+typedef unsigned long uLong;
+
+typedef struct z_stream_s {
+  uInt avail_in;
+  uInt avail_out;
+} z_stream;
+
+typedef struct gz_stream {
+  z_stream stream;
+  FILE *file;
+} gz_stream;
+
+void test_1_callee(gz_stream *s, Byte *buf) {
+  Byte *next_out = buf;
+  uInt n = s->stream.avail_in;
+  if (n > 0) {
+    next_out += n;
+  }
+  s->stream.avail_out -= fread(next_out, 1, s->stream.avail_out, s->file);
+}
+
+void test_1_caller(gz_stream *s) {
+  unsigned char c;
+  test_1_callee(s, &c);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/gzio-3a.c b/gcc/testsuite/gcc.dg/analyzer/gzio-3a.c
new file mode 100644
index 000000000000..15ed0103fe07
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/gzio-3a.c
@@ -0,0 +1,27 @@ 
+typedef long unsigned int size_t;
+typedef struct _IO_FILE FILE;
+extern size_t fread(void *__restrict __ptr, size_t __size, size_t __n,
+                    FILE *__restrict __stream);
+typedef unsigned char Byte;
+typedef unsigned int uInt;
+typedef unsigned long uLong;
+
+typedef struct gz_stream {
+  FILE *file;
+  uInt avail_in;
+  uInt avail_out;
+} gz_stream;
+
+void test_1_callee(gz_stream *s, Byte *buf) {
+  Byte *next_out = buf;
+  uInt n = s->avail_in;
+  if (n > 0) {
+    next_out += n;
+  }
+  s->avail_out -= fread(next_out, 1, s->avail_out, s->file);
+}
+
+void test_1_caller(gz_stream *s) {
+  unsigned char c;
+  test_1_callee(s, &c);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/gzio.c b/gcc/testsuite/gcc.dg/analyzer/gzio.c
new file mode 100644
index 000000000000..54efa77d0561
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/gzio.c
@@ -0,0 +1,17 @@ 
+#include <stdlib.h>
+typedef struct z_stream_s {
+  unsigned char *next_out;
+} z_stream;
+typedef struct gz_stream {
+  z_stream stream;
+  unsigned char *outbuf;
+} gz_stream;
+gz_stream *s;
+static void gz_open(const char *path)
+{
+  s->stream.next_out = s->outbuf = (unsigned char *)malloc(16384); /* { dg-bogus "leak" } */
+}
+void gzopen(const char *path)
+{
+  gz_open(path);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/infinite-recursion.c b/gcc/testsuite/gcc.dg/analyzer/infinite-recursion.c
new file mode 100644
index 000000000000..b770e128bc68
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/infinite-recursion.c
@@ -0,0 +1,55 @@ 
+extern void marker_A(void);
+extern void marker_B(void);
+extern void marker_C(void);
+extern void marker_D(void);
+
+void test(int flag)
+{
+  marker_A();
+
+  if (flag) {
+    marker_B();
+
+    /* Recurse, infinitely, as it happens: */
+    test(flag);
+
+    marker_C();
+  }
+
+  marker_D();
+}
+
+/* A cycle of 4 mutually-recursive functions (but only for certain inputs).  */
+
+extern void mutual_test_1 (int flag);
+extern void mutual_test_2 (int flag);
+extern void mutual_test_3 (int flag);
+extern void mutual_test_4 (int flag);
+
+void mutual_test_1 (int flag)
+{
+  marker_A ();
+  if (flag)
+    mutual_test_2 (flag);
+}
+
+void mutual_test_2 (int flag)
+{
+  marker_B ();
+  if (flag)
+    mutual_test_3 (flag);
+}
+
+void mutual_test_3 (int flag)
+{
+  marker_C ();
+  if (flag)
+    mutual_test_4 (flag);
+}
+
+void mutual_test_4 (int flag)
+{
+  marker_D ();
+  if (flag)
+    mutual_test_1 (flag);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-2.c b/gcc/testsuite/gcc.dg/analyzer/loop-2.c
new file mode 100644
index 000000000000..595f23915caa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-2.c
@@ -0,0 +1,37 @@ 
+/* { dg-additional-options "-fno-analyzer-state-purge" } */
+#include "analyzer-decls.h"
+
+struct s
+{
+  int i;
+};
+
+void test(void)
+{
+  struct s s;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+
+  for (s.i=0; s.i<256; s.i++) {
+    __analyzer_eval (s.i < 256); /* { dg-warning "TRUE" } */
+      /* (should report TRUE twice). */
+
+    __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+    //__analyzer_eval (s.i == 0); /* { d-todo-g-warning "UNKNOWN" "" { xfail *-*-* } } */
+      /* { d-todo-g-warning "TRUE" "" { target *-*-* } .-1 } */
+      /* TODO(xfail^^^): we're only capturing the first iteration, so
+	 we erroneously get i == 0.  */
+
+      //__analyzer_eval (s.i >= 0); /* { d-todo-g-warning "TRUE" } */
+  }
+
+  __analyzer_eval (s.i >= 256); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (s.i == 256); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  /* TODO(xfail^^^): ideally it should figure out i == 256 at exit.  */
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-2a.c b/gcc/testsuite/gcc.dg/analyzer/loop-2a.c
new file mode 100644
index 000000000000..d50bfe0f31b7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-2a.c
@@ -0,0 +1,40 @@ 
+/* { dg-additional-options "-fno-analyzer-state-purge" } */
+#include "analyzer-decls.h"
+
+union u
+{
+  int i;
+};
+
+void test(void)
+{
+  union u u;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+
+  for (u.i=0; u.i<256; u.i++) {
+    __analyzer_eval (u.i < 256); /* { dg-warning "TRUE" } */
+    /* { dg-warning "TRUE" "" { xfail *-*-* } .-1 } */
+    /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-2 } */
+    /* (should report TRUE twice). */
+
+    __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+    //__analyzer_eval (u.i == 0); /* { d-todo-g-warning "UNKNOWN" "" { xfail *-*-* } } */
+      /* { d-todo-g-warning "TRUE" "" { target *-*-* } .-1 } */
+      /* TODO(xfail^^^): we're only capturing the first iteration, so
+	 we erroneously get i == 0.  */
+
+      //__analyzer_eval (u.i >= 0); /* { d-todo-g-warning "TRUE" } */
+  }
+
+  __analyzer_eval (u.i >= 256); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+
+  __analyzer_eval (u.i == 256); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  /* TODO(xfail^^^): ideally it should figure out i == 256 at exit.  */
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-3.c b/gcc/testsuite/gcc.dg/analyzer/loop-3.c
new file mode 100644
index 000000000000..1d01771d7cb7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-3.c
@@ -0,0 +1,17 @@ 
+#include <stdlib.h>
+
+void test(int c)
+{
+  int i;
+  char *buffer = (char*)malloc(256);
+
+  for (i=0; i<255; i++) {
+    buffer[i] = c; /* { dg-warning "use after 'free' of 'buffer'" } */
+		   /* BUG: the malloc could have failed
+		      TODO: the checker doesn't yet pick up on this, perhaps
+		      due to the pointer arithmetic not picking up on the
+		      state */
+    free(buffer); /* { dg-warning "double-'free' of 'buffer'" } */
+  }
+
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-4.c b/gcc/testsuite/gcc.dg/analyzer/loop-4.c
new file mode 100644
index 000000000000..105237570d1f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/loop-4.c
@@ -0,0 +1,43 @@ 
+// FIXME:
+/* { dg-additional-options "-fno-analyzer-state-purge" } */
+
+/* Example of nested loops.  */
+
+#include "analyzer-decls.h"
+
+void test(void)
+{
+  int i, j, k;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  for (i=0; i<256; i++) {
+
+    __analyzer_eval (i >= 0); /* { dg-warning "TRUE" } */
+      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+
+    __analyzer_eval (i < 256); /* { dg-warning "TRUE" } */
+
+    for (j=0; j<256; j++) {
+
+      __analyzer_eval (j >= 0); /* { dg-warning "TRUE" } */
+      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+
+      __analyzer_eval (j < 256); /* { dg-warning "TRUE" } */
+
+      __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+      for (k=0; k<256; k++) {
+
+	__analyzer_eval (k >= 0); /* { dg-warning "TRUE" } */
+	/* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+
+	__analyzer_eval (k < 256); /* { dg-warning "TRUE" } */
+
+	__analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */
+      }
+    }
+  }
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/loop.c b/gcc/testsuite/gcc.dg/analyzer/loop.c
new file mode 100644
index 000000000000..3f29fa6146e2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/loop.c
@@ -0,0 +1,35 @@ 
+/* { dg-additional-options "-fno-analyzer-state-purge" } */
+
+#include "analyzer-decls.h"
+
+void test(void)
+{
+  int i;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  for (i=0; i<256; i++) {
+      __analyzer_eval (i < 256); /* { dg-warning "TRUE" } */
+      /* (should report TRUE twice). */
+
+      __analyzer_eval (i == 0); /* { dg-warning "TRUE" } */
+      /* { dg-warning "FALSE" "" { xfail *-*-* } .-1 } */
+      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-2 } */
+      /* TODO(xfail^^^): ideally we ought to figure out i > 0 after 1st iteration.  */
+
+      __analyzer_eval (i >= 0); /* { dg-warning "TRUE" } */
+      /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-2 } */
+      /* TODO(xfail^^^): ideally we ought to figure out i >= 0 for all iterations.  */
+
+      __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  }
+
+  __analyzer_eval (i >= 256); /* { dg-warning "TRUE" } */
+
+  __analyzer_eval (i == 256); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  /* TODO(xfail^^^): it only figures out i >= 256, rather than i == 256.  */
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
new file mode 100644
index 000000000000..b9a724d0034d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c
@@ -0,0 +1,585 @@ 
+#include <stdlib.h>
+#include <string.h>
+
+extern int foo (void);
+extern int bar (void);
+extern void could_free (void *);
+extern void cant_free (const void *); /* since it's a const void *.  */
+
+void test_1 (void)
+{
+  void *ptr = malloc (1024);
+  free (ptr);
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_2 (void *ptr)
+{
+  free (ptr);
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_2a (void *ptr)
+{
+  __builtin_free (ptr);
+  __builtin_free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+int *test_3 (void)
+{
+  int *ptr = (int *)malloc (sizeof (int));
+  *ptr = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+  return ptr;
+}
+
+int *test_3a (void)
+{
+  int *ptr = (int *)__builtin_malloc (sizeof (int));
+  *ptr = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+  return ptr;
+}
+
+int *test_4 (void)
+{
+  int *ptr = (int *)malloc (sizeof (int));
+  if (ptr)
+    *ptr = 42;
+  else
+    *ptr = 43; /* { dg-warning "dereference of NULL 'ptr'" } */
+  return ptr;
+}
+
+int test_5 (int *ptr)
+{
+  free (ptr);
+  return *ptr; /* { dg-warning "use after 'free' of 'ptr'" } */
+}
+
+void test_6 (void *ptr)
+{
+  void *q;
+  q = ptr;
+  free (ptr);
+  free (q); /* { dg-warning "double-'free' of 'q'" } */
+  /* The above case requires us to handle equivalence classes in
+     state transitions.  */
+}
+
+void test_7 (void)
+{
+  void *ptr = malloc(4096);
+  if (!ptr)
+    return;
+  memset(ptr, 0, 4096);
+  free(ptr);
+}
+
+void *test_8 (void)
+{
+  void *ptr = malloc(4096);
+  if (!ptr)
+    return NULL;
+  memset(ptr, 0, 4096);
+  return ptr;
+  /* This needs phi nodes to affect equivalence classes, or we get a false report
+     of a leak.  */
+}
+
+void test_9 (void)
+{
+  void *ptr = malloc (1024);
+
+  int i;
+  for (i = 0; i < 1024; i++)
+    free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_10 (void)
+{
+  void *ptr = malloc (1024);
+
+  int i;
+  for (i = 0; i < 1024; i++)
+    foo ();
+
+  free (ptr);
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ 
+}
+
+void test_11 (void)
+{
+  void *ptr = malloc (1024);
+
+  while (foo ())
+    bar ();
+
+  free (ptr);
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_12 (void)
+{
+  void *ptr = malloc (1024);
+
+  while (1)
+    {
+      free (ptr);
+      free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+    }
+}
+
+void test_13 (void)
+{
+  void *p = malloc (1024); /* { dg-message "allocated here" } */
+  void *q = malloc (1024);
+
+  foo ();
+  if (!q)
+    {
+      free (q);
+      return; /* { dg-warning "leak of 'p'" } */ 
+    }
+  bar ();
+  free (q);
+  free (p);
+}
+
+void test_14 (void)
+{
+  void *p, *q;
+  p = malloc (1024);
+  if (!p)
+    return;
+
+  q = malloc (1024);
+  if (!q)
+    {
+      free (p);
+      free (q);
+      /* oops: missing "return".  */
+    }
+  bar ();
+  free (q); /* Although this looks like a double-'free' of q,
+	       it's known to be NULL for the case where free is
+	       called twice on it.  */
+  free (p); /* { dg-warning "double-'free' of 'p'" } */
+}
+
+void test_15 (void)
+{
+  void *p = NULL, *q = NULL;
+
+  p = malloc (1024);
+  if (!p)
+    goto fail;
+
+  foo ();
+
+  q = malloc (1024);
+  if (!q)
+    goto fail;
+
+  bar ();
+
+ fail:
+  free (q);
+  free (p);
+}
+
+void test_16 (void)
+{
+  void *p, *q;
+
+  p = malloc (1024);
+  if (!p)
+    goto fail;
+
+  foo ();
+
+  q = malloc (1024);
+  if (!q)
+    goto fail;
+
+  bar ();
+
+ fail:
+  free (q); /* { dg-warning "free of uninitialized 'q'" "" { xfail *-*-* } } */ 
+  /* TODO(xfail): implement uninitialized detection.  */
+  free (p);
+}
+
+void test_17 (void)
+{
+  void *ptr = malloc (1024); /* { dg-message "allocated here" } */
+} /* { dg-warning "leak of 'ptr'" } */ 
+
+void test_18 (void)
+{
+  void *ptr = malloc (64); /* { dg-message "allocated here" } */
+  ptr = NULL; /* { dg-warning "leak of 'ptr'" } */ 
+}
+
+void test_19 (void)
+{
+  void *ptr = malloc (64);
+  free (ptr);
+  ptr = NULL;
+  free (ptr);
+}
+
+void *global_ptr_20;
+
+void test_20 (void)
+{
+  global_ptr_20 = malloc (1024);
+}
+
+int *test_21 (int i)
+{
+  int *ptr = malloc (sizeof (int));
+  if (!ptr)
+    abort ();
+  *ptr = i;
+  return ptr;
+}
+
+void test_22 (void)
+{
+  void *ptr = malloc (1024);
+
+  int i;
+  for (i = 5; i < 10; i++)
+    foo ();
+
+  free (ptr);
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ 
+}
+
+int *test_23 (int n)
+{
+  int *ptr = (int *)calloc (n, sizeof (int));
+  ptr[0] = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+  return ptr;
+}
+
+int *test_23a (int n)
+{
+  int *ptr = (int *)__builtin_calloc (n, sizeof (int));
+  ptr[0] = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+  return ptr;
+}
+
+int test_24 (void)
+{
+  void *ptr = alloca (sizeof (int)); /* { dg-message "memory is allocated on the stack here" } */
+  free (ptr); /* { dg-warning "'free' of memory allocated on the stack by 'alloca' \\('ptr'\\) will corrupt the heap \\\[CWE-590\\\]" } */
+}
+
+int test_25 (void)
+{
+  char tmp[100];
+  void *p = tmp; /* { dg-message "pointer is from here" } */
+  free (p); /* { dg-warning "'free' of 'p' which points to memory not on the heap \\\[CWE-590\\\]" } */
+  /* TODO: more precise messages here.  */
+}
+
+char global_buffer[100];
+
+int test_26 (void)
+{
+  void *p = global_buffer; /* { dg-message "pointer is from here" } */
+  free (p); /* { dg-warning "'free' of 'p' which points to memory not on the heap \\\[CWE-590\\\]" } */
+  /* TODO: more precise messages here.  */
+}
+
+struct coord {
+  float x;
+  float y;
+};
+
+struct coord *test_27 (void)
+{
+  struct coord *p = (struct coord *) malloc (sizeof (struct coord)); /* { dg-message "this call could return NULL" } */
+  p->x = 0.f;  /* { dg-warning "dereference of possibly-NULL 'p'" } */
+
+  /* Only the first such usage should be reported: */
+  p->y = 0.f;
+
+  return p;
+}
+
+struct coord *test_28 (void)
+{
+  struct coord *p = NULL;
+  p->x = 0.f; /* { dg-warning "dereference of NULL 'p'" } */
+
+  /* Only the first such usage should be reported: */
+  p->y = 0.f;
+
+  return p;
+}
+
+struct link
+{
+  struct link *m_ptr;
+};
+
+struct link *test_29 (void)
+{
+  struct link *res = (struct link *)malloc (sizeof (struct link));
+  if (!res)
+    return NULL;
+  res->m_ptr = (struct link *)malloc (sizeof (struct link));
+  return res;
+}
+
+struct link *test_29a (void)
+{
+  struct link *res = (struct link *)malloc (sizeof (struct link));
+  if (!res)
+    return NULL;
+  res->m_ptr = (struct link *)malloc (sizeof (struct link));
+  if (!res->m_ptr)
+    {
+      free (res);
+      return NULL;
+    }
+  res->m_ptr->m_ptr = (struct link *)malloc (sizeof (struct link));
+  return res;
+}
+
+/* Without consolidation by EC, this one shows two leaks:
+     warning: leak of '<unknown>'
+     warning: leak of 'tmp.m_ptr' 
+   We should only show the latter (favoring the most user-readable
+   expression in the equivalence class).  */
+void test_30 (void)
+{
+  struct link tmp;
+  tmp.m_ptr = (struct link *)malloc (sizeof (struct link)); /* { dg-message "allocated here" } */
+} /* { dg-warning "leak of 'tmp.m_ptr'" } */ 
+/* { dg-bogus "leak of '<unknown>'" "" { xfail *-*-* } .-1 } */ 
+
+void test_31 (void)
+{
+  struct link tmp;
+  void *ptr = malloc (sizeof (struct link)); /* { dg-message "allocated here" } */
+  tmp.m_ptr = (struct link *)ptr;
+} /* { dg-warning "leak of 'ptr'" } */ 
+/* { dg-bogus "leak of 'tmp.m_ptr'" "" { xfail *-*-* } .-1 } */ 
+
+void test_32 (void)
+{
+  void *ptr = malloc (1024);
+  could_free (ptr);
+} /* { dg-bogus "leak" } */
+
+void test_33 (void)
+{
+  void *ptr = malloc (1024); /* { dg-message "allocated here" } */
+  cant_free (ptr);
+} /* { dg-warning "leak of 'ptr'" } */ 
+
+void test_34 (void)
+{
+  float *q;
+  struct coord *p = malloc (sizeof (struct coord));
+  if (!p)
+    return;
+  p->x = 0.0f;
+  q = &p->x;
+  free (p);
+  *q = 1.0f; /* { dg-warning "use after 'free' of 'q'" } */
+};
+
+int test_35 (void)
+{
+  void *ptr = malloc(4096);
+  if (!ptr)
+    return -1;
+  memset(ptr, 0, 4096);
+  free(ptr);
+  return 0;
+}
+
+void test_36 (void)
+{
+  void *ptr = malloc(4096);
+  if (!ptr)
+    return;
+  memset(ptr, 0, 4096);
+  free(ptr);
+}
+
+void *test_37a (void)
+{
+  void *ptr = malloc(4096); /* { dg-message "this call could return NULL" } */
+  memset(ptr, 0, 4096); /* { dg-warning "use of possibly-NULL 'ptr' where non-null expected" } */
+  return ptr;
+}
+
+int test_37b (void)
+{
+  void *p = malloc(4096);
+  void *q = malloc(4096); /* { dg-message "this call could return NULL" } */
+  if (p) {
+    memset(p, 0, 4096); /* Not a bug: checked */
+  } else {
+    memset(q, 0, 4096); /* { dg-warning "use of possibly-NULL 'q' where non-null expected" } */
+  }
+  free(p);
+  free(q);
+  return 0;
+}
+
+extern void might_use_ptr (void *ptr);
+
+void test_38(int i)
+{
+  void *p;
+
+  p = malloc(1024);
+  if (p) {
+    free(p);
+    might_use_ptr(p); /* { dg-warning "use after 'free' of 'p'" "" { xfail *-*-* } } */
+    // TODO: xfail
+  }
+}
+
+int *
+test_39 (int i)
+{
+  int *p = (int*)malloc(sizeof(int*)); /* { dg-message "this call could return NULL" } */
+  *p = i; /* { dg-warning "dereference of possibly-NULL 'p'" } */
+  return p;
+}
+
+int *
+test_40 (int i)
+{
+  int *p = (int*)malloc(sizeof(int*));
+  i = *p; /* { dg-warning "dereference of possibly-NULL 'p'" } */
+  /* TODO: (it's also uninitialized) */
+  return p;
+}
+
+char *
+test_41 (int flag)
+{
+  char *buffer;
+
+  if (flag) {
+    buffer = (char*)malloc(4096);
+  } else {
+    buffer = NULL;
+  }
+
+  buffer[0] = 'a'; /* { dg-warning "dereference of possibly-NULL 'buffer'" } */
+  /* { dg-warning "dereference of NULL 'buffer'" "" { target *-*-* } .-1 } */
+
+  return buffer;
+}
+
+void test_42a (void)
+{
+  void *p = malloc (1024); /* { dg-message "allocated here" } */
+  free (p + 64); /* this could well corrupt the heap.  */
+  /* TODO: ^^^ we should warn about this.  */
+} /* { dg-warning "leak of 'p'" } */
+/* TODO: presumably we should complain about the bogus free, but then
+   maybe not complain about the leak.  */
+// CWE-761: Free of Pointer not at Start of Buffer
+
+void test_42b (void)
+{
+  void *p = malloc (1024); /* { dg-message "allocated here" } */
+  free (p - 64); /* this could well corrupt the heap.  */
+  /* TODO: ^^^ we should warn about this.  */
+} /* { dg-warning "leak of 'p'" } */
+/* TODO: presumably we should complain about the bogus free, but then
+   maybe not complain about the leak.  */
+// CWE-761: Free of Pointer not at Start of Buffer
+
+void test_42c (void)
+{
+  void *p = malloc (1024);
+  void *q = p + 64;
+  free (q - 64); /* this is probably OK.  */
+} /* { dg-bogus "leak of 'p'" "" { xfail *-*-* } } */
+// TODO(xfail)
+
+#if 0
+void test_31 (void *p)
+{
+  void *q = realloc (p, 1024);
+  free (p); /* FIXME: this is a double-'free'.  */
+  free (q);
+}
+
+void test_32 (void)
+{
+  void *p = malloc (64);
+  p = realloc (p, 1024); /* FIXME: this leaks if it fails.  */
+  free (p);
+}
+#endif
+
+struct link global_link;
+
+void test_43 (void)
+{
+  global_link.m_ptr = malloc (sizeof (struct link)); /* { dg-message "allocated here" } */
+  global_link.m_ptr = NULL;
+} /* { dg-warning "leak of '<unknown>'" } */
+/* TODO: should be more precise than just '<unknown>', and
+   ideally would be at the assigment to NULL.  */
+
+struct link *global_ptr;
+
+void test_44 (void)
+{
+  global_ptr = malloc (sizeof (struct link));
+  if (!global_ptr)
+    return;
+  global_ptr->m_ptr = malloc (sizeof (struct link)); /* { dg-message "allocated here" } */
+  free (global_ptr); /* { dg-warning "leak of '<unknown>'" } */
+  /* TODO: should be more precise than just '<unknown>'.  */
+}
+
+extern void might_take_ownership (void *ptr);
+
+void test_45 (void)
+{
+  void *p = malloc (1024);
+  might_take_ownership (p);
+}
+
+void test_46 (void)
+{
+  struct link *p = (struct link *)malloc (sizeof (struct link));
+  if (!p)
+    return;
+  struct link *q = (struct link *)malloc (sizeof (struct link));
+  p->m_ptr = q;
+  might_take_ownership (p);
+}
+
+extern int maybe_alloc (char **);
+
+int test_47 (void)
+{
+  char *p = ((void *)0);
+  int p_size = 0;
+
+  p = malloc (16);
+  if (p) {
+    free (p);
+  } else {
+    int retval = maybe_alloc (&p); /* this might write to "p".  */
+    if (retval)
+      return (retval);
+    p_size = strlen(p); /* { dg-bogus "non-null expected" } */
+    free (p);
+  }
+  return p_size;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-2.c b/gcc/testsuite/gcc.dg/analyzer/malloc-2.c
new file mode 100644
index 000000000000..6d073f566193
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-2.c
@@ -0,0 +1,23 @@ 
+/* Tests for precision-of-wording within malloc warnings.  */
+
+typedef __SIZE_TYPE__ size_t;
+extern void *malloc(size_t);
+extern void free(void *);
+extern char *strcpy(char *__restrict __dest, const char *__restrict __src)
+    __attribute__((__nothrow__, __leaf__)) __attribute__((__nonnull__(1, 2)));
+
+void test_1 (void)
+{
+  void *p = malloc (1024); /* { dg-message "\\(1\\) this call could return NULL" } */
+  strcpy ((char *)p, "hello world"); /* { dg-warning "use of possibly-NULL 'p' where non-null expected" } */
+  /* { dg-message "\\(2\\) argument 1 \\('p'\\) from \\(1\\) could be NULL where non-null expected" "" { target *-*-* } .-1 } */
+  free (p);
+}
+
+int *test_2 (void)
+{
+  int *i = malloc (sizeof (int)); /* { dg-message "\\(1\\) this call could return NULL" } */
+  *i = 42; /* { dg-warning "dereference of possibly-NULL 'i'" } */
+  /* { dg-message "\\(2\\) 'i' could be NULL: unchecked value from \\(1\\)" "" { target *-*-* } .-1 } */
+  return i; 
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-3.c b/gcc/testsuite/gcc.dg/analyzer/malloc-3.c
new file mode 100644
index 000000000000..5afb6b3b0f7b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-3.c
@@ -0,0 +1,8 @@ 
+#include <stdlib.h>
+
+/* Don't complain about leaks due to exiting from "main".  */
+
+void main (void)
+{
+  void *p = malloc (1024);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-callbacks.c b/gcc/testsuite/gcc.dg/analyzer/malloc-callbacks.c
new file mode 100644
index 000000000000..eb5545e5da00
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-callbacks.c
@@ -0,0 +1,84 @@ 
+#include <stdlib.h>
+
+typedef void *(*allocator_t) (size_t);
+typedef void (*deallocator_t) (void *);
+
+static allocator_t __attribute__((noinline))
+get_malloc (void)
+{
+  return malloc;
+}
+
+static allocator_t __attribute__((noinline))
+get_alloca (void)
+{
+  return alloca;
+}
+
+static deallocator_t __attribute__((noinline))
+get_free (void)
+{
+  return free;
+}
+
+void test_1 (void *ptr)
+{
+  deallocator_t dealloc_fn = free;
+  dealloc_fn (ptr); /* { dg-message "first 'free' here" } */
+  dealloc_fn (ptr); /* { dg-warning "double-'free'" } */
+}
+
+void test_2 (void *ptr)
+{
+  deallocator_t dealloc_fn = get_free ();
+  dealloc_fn (ptr); /* { dg-message "first 'free' here" } */
+  dealloc_fn (ptr); /* { dg-warning "double-'free'" } */
+}
+
+static void __attribute__((noinline))
+called_by_test_3 (void *ptr, deallocator_t dealloc_fn)
+{
+  dealloc_fn (ptr); /* { dg-warning "double-'free'" } */
+}
+
+void test_3 (void *ptr)
+{
+  called_by_test_3 (ptr, free);
+  called_by_test_3 (ptr, free);
+}
+
+int *test_4 (void)
+{
+  allocator_t alloc_fn = get_malloc ();
+  int *ptr = alloc_fn (sizeof (int)); /* { dg-message "this call could return NULL" } */
+  *ptr = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+  return ptr;
+}
+
+int *test_5 (void)
+{
+  allocator_t alloc_fn = get_alloca ();
+  deallocator_t dealloc_fn = get_free ();
+  int *ptr = alloc_fn (sizeof (int)); /* { dg-message "pointer is from here" } */
+  /* TODO: message should read "memory is allocated on the stack here".  */
+  dealloc_fn (ptr); /* { dg-warning "'free' of 'ptr' which points to memory not on the heap" } */
+}
+
+static void __attribute__((noinline))
+called_by_test_6a (void *ptr)
+{
+  free (ptr); /* { dg-warning "double-'free'" "" { xfail *-*-* } } */
+}
+
+static deallocator_t __attribute__((noinline))
+called_by_test_6b (void)
+{
+  return called_by_test_6a;
+}
+
+void test_6 (void *ptr)
+{
+  deallocator_t dealloc_fn = called_by_test_6b ();
+  dealloc_fn (ptr);
+  dealloc_fn (ptr);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-dce.c b/gcc/testsuite/gcc.dg/analyzer/malloc-dce.c
new file mode 100644
index 000000000000..1b4b8788b86b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-dce.c
@@ -0,0 +1,12 @@ 
+/* { dg-additional-options "-O2" } */
+
+#include <stdlib.h>
+
+void test(void)
+{
+    void *ptr = malloc(512);
+    free(ptr);
+    free(ptr); /* { dg-warning "double-'free'" "" { xfail *-*-* } } */
+}
+/* With optimization, the whole of test() goes away in the "cddce" pass
+   before the analysis pass sees it, and hence we get no error message.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c
new file mode 100644
index 000000000000..233ab485b5af
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c
@@ -0,0 +1,46 @@ 
+#include <stdlib.h>
+
+extern void foo (void);
+extern void bar (void);
+
+void test_1 (int flag, int n);
+
+void caller_1_of_test_1 (int n)
+{
+  test_1 (1, n); /* { dg-bogus "test_1" } */
+  test_1 (0, n); /* { dg-bogus "test_1" } */
+}
+
+void __attribute__((noinline))
+test_1 (int flag, int n)
+{
+  int *ptr = (int *)malloc (sizeof (int));
+
+  if (flag)
+    {
+      int i;
+      for (i = 0; i < n; i++)
+	foo ();
+    }
+  else
+    bar ();
+
+  free (ptr);
+  free (ptr); /* { dg-warning "double-'free'" } */
+  /* FIXME: we get duplicates intraprocedurally, as there are two paths
+     through the function.
+     The calls in test_2 also generate additional duplicates.
+     How to verify lack of duplicates?
+     Putting a bogus on the interprocedual one detects that, at least.  */
+
+  if (flag)
+    foo ();
+  else
+    bar ();
+}
+
+void caller_2_of_test_1 (int n)
+{
+  test_1 (1, n); /* { dg-bogus "test_1" } */
+  test_1 (0, n); /* { dg-bogus "test_1" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-1.c
new file mode 100644
index 000000000000..ad536ce5f02f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-1.c
@@ -0,0 +1,24 @@ 
+#include <stdlib.h>
+
+void *
+calls_malloc (void)
+{
+  void *result = malloc (1024);
+  return result;
+}
+
+int *test_1 (int i)
+{
+  int *ptr = (int *)calls_malloc ();
+  *ptr = i; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+  return ptr;
+}
+
+/* Same as test_1, to exercise the caches.  */
+
+int *test_2 (int i)
+{
+  int *ptr = (int *)calls_malloc ();
+  *ptr = i; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+  return ptr;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c
new file mode 100644
index 000000000000..7e8f274e7bc8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c
@@ -0,0 +1,32 @@ 
+#include <stdlib.h>
+
+void
+calls_free (void *victim) /* { dg-message "\\(3\\) entry to 'calls_free'" } */
+/* { dg-message "\\(7\\) entry to 'calls_free'" "" { target *-*-* } .-1 } */
+{
+  free (victim); /* { dg-warning "double-'free' of 'victim'" } */
+  /* { dg-message "\\(4\\) first 'free' here" "" { target *-*-* } .-1 } */
+  /* { dg-message "\\(8\\) second 'free' here; first 'free' was at \\(4\\)" "" { target *-*-* } .-2 } */
+
+  /* TODO: would this be better emitted at the callsite,
+     for such a simple wrapper?  */
+}
+
+void do_stuff (void)
+{
+  /* Empty.  Irrelevant, and thus should not be expanded into in paths.  */
+}
+
+void test (void *ptr) /* { dg-message "\\(1\\) entry to 'test'" } */
+{
+  do_stuff ();
+
+  calls_free (ptr); /* { dg-message "\\(2\\) calling 'calls_free' from 'test'" } */
+  /* { dg-message "\\(5\\) returning to 'test' from 'calls_free'" "" { target *-*-* } .-1 } */
+
+  do_stuff ();
+
+  calls_free (ptr); /* { dg-message "\\(6\\) passing freed pointer 'ptr' in call to 'calls_free' from 'test'" } */
+
+  do_stuff ();
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c
new file mode 100644
index 000000000000..d02250f16f60
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c
@@ -0,0 +1,95 @@ 
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <stdlib.h>
+
+extern int some_condition ();
+extern void do_stuff (int);
+
+void
+may_call_free (void *victim)
+{
+  if (some_condition ())
+    return;
+
+  free (victim); /* { dg-warning "double-'free' of 'victim'" } */ 
+}
+
+void test (void *ptr)
+{
+  do_stuff (1);
+
+  may_call_free (ptr);
+
+  do_stuff (2);
+
+  may_call_free (ptr);
+
+  do_stuff (3);
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |   free (victim);
+      |   ^~~~~~~~~~~~~
+  'test': events 1-2
+    |
+    |   NN | void test (void *ptr)
+    |      |      ^~~~
+    |      |      |
+    |      |      (1) entry to 'test'
+    |......
+    |   NN |   may_call_free (ptr);
+    |      |   ~~~~~~~~~~~~~~~~~~~
+    |      |   |
+    |      |   (2) calling 'may_call_free' from 'test'
+    |
+    +--> 'may_call_free': events 3-6
+           |
+           |   NN | may_call_free (void *victim)
+           |      | ^~~~~~~~~~~~~
+           |      | |
+           |      | (3) entry to 'may_call_free'
+           |   NN | {
+           |   NN |   if (some_condition ())
+           |      |      ~
+           |      |      |
+           |      |      (4) following 'false' branch...
+           |......
+           |   NN |   free (victim);
+           |      |   ~~~~~~~~~~~~~
+           |      |   |
+           |      |   (5) ...to here
+           |      |   (6) first 'free' here
+           |
+    <------+
+    |
+  'test': events 7-8
+    |
+    |   NN |   may_call_free (ptr);
+    |      |   ^~~~~~~~~~~~~~~~~~~
+    |      |   |
+    |      |   (7) returning to 'test' from 'may_call_free'
+    |......
+    |   NN |   may_call_free (ptr);
+    |      |   ~~~~~~~~~~~~~~~~~~~
+    |      |   |
+    |      |   (8) passing freed pointer 'ptr' in call to 'may_call_free' from 'test'
+    |
+    +--> 'may_call_free': events 9-12
+           |
+           |   NN | may_call_free (void *victim)
+           |      | ^~~~~~~~~~~~~
+           |      | |
+           |      | (9) entry to 'may_call_free'
+           |   NN | {
+           |   NN |   if (some_condition ())
+           |      |      ~
+           |      |      |
+           |      |      (10) following 'false' branch...
+           |......
+           |   NN |   free (victim);
+           |      |   ~~~~~~~~~~~~~
+           |      |   |
+           |      |   (11) ...to here
+           |      |   (12) second 'free' here; first 'free' was at (6)
+           |
+  { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-12.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-12.c
new file mode 100644
index 000000000000..fce5437edd16
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-12.c
@@ -0,0 +1,7 @@ 
+#include <stdlib.h>
+
+void recursive_free (void *ptr)
+{
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+  recursive_free (ptr);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c
new file mode 100644
index 000000000000..2e3d80ed5cc6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c
@@ -0,0 +1,30 @@ 
+/* { dg-additional-options "-fanalyzer-verbosity=1" } */
+
+#include <stdlib.h>
+
+void
+calls_free (void *victim)
+{
+  free (victim); /* { dg-warning "double-'free' of 'victim'" } */
+}
+
+extern void do_stuff (void);
+
+struct foo
+{
+  void *m_p;
+};
+
+void test (struct foo f)
+{
+  do_stuff ();
+
+  calls_free (f.m_p);
+
+  do_stuff ();
+
+  calls_free (f.m_p); /* { dg-message "passing freed pointer '<unknown>' in call to 'calls_free' from 'test'" } */
+  // TODO: something better than '<unknown>'
+
+  do_stuff ();
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c
new file mode 100644
index 000000000000..efeb94bc1feb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c
@@ -0,0 +1,34 @@ 
+/* { dg-additional-options "-fanalyzer-verbosity=1" } */
+
+#include <stdlib.h>
+
+void
+calls_free (void *victim) /* { dg-message "\\(3\\) entry to 'calls_free'" } */
+/* { dg-message "\\(7\\) entry to 'calls_free'" "" { target *-*-* } .-1 } */
+{
+  free (victim); /* { dg-warning "double-'free' of 'victim'" } */
+  /* { dg-message "\\(4\\) first 'free' here" "" { target *-*-* } .-1 } */
+  /* { dg-message "\\(8\\) second 'free' here; first 'free' was at \\(4\\)" "" { target *-*-* } .-2 } */
+
+  /* TODO: would this be better emitted at the callsite,
+     for such a simple wrapper?  */
+}
+
+extern void do_stuff (void);
+
+void test (void *ptr) /* { dg-message "\\(1\\) entry to 'test'" } */
+{
+  do_stuff ();
+
+  calls_free (ptr); /* { dg-message "\\(2\\) calling 'calls_free' from 'test'" } */
+  /* { dg-message "\\(5\\) returning to 'test' from 'calls_free'" "" { target *-*-* } .-1 } */
+
+  do_stuff ();
+
+  calls_free (ptr); /* { dg-message "\\(6\\) passing freed pointer 'ptr' in call to 'calls_free' from 'test'" } */
+
+  do_stuff ();
+}
+
+
+
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-3.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-3.c
new file mode 100644
index 000000000000..3dcfae8f0a29
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-3.c
@@ -0,0 +1,23 @@ 
+#include <stdlib.h>
+
+void *
+calls_malloc (void)
+{
+  void *result = malloc (1024);
+  return result;
+}
+
+void
+calls_free (void *victim)
+{
+  free (victim); /* { dg-warning "double-'free' of 'victim'" } */
+  /* TODO: this would be better emitted at the callsite,
+     for such a simple wrapper.  */
+}
+
+void test (void)
+{
+  void *ptr = calls_malloc ();
+  calls_free (ptr);
+  calls_free (ptr); /* BUG: double-'free'.  */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-4.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-4.c
new file mode 100644
index 000000000000..82d50bd3c936
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-4.c
@@ -0,0 +1,13 @@ 
+#include <stdlib.h>
+
+static void calls_free(int *q)
+{
+  free(q);
+}
+
+void test(void *p)
+{
+  calls_free(p);
+
+  free(p); /* { dg-warning "double-'free' of 'p'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-5.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-5.c
new file mode 100644
index 000000000000..c66ecb5c5635
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-5.c
@@ -0,0 +1,13 @@ 
+#include <stdlib.h>
+
+static int *calls_malloc(void)
+{
+  return malloc(sizeof(int));
+}
+
+int *test(void)
+{
+  int *p = calls_malloc(); /* { dg-message "possible return of NULL to 'test' from 'calls_malloc'" } */
+  *p = 42; /* { dg-warning "dereference of possibly-NULL 'p'" } */
+  return p;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-6.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-6.c
new file mode 100644
index 000000000000..62f8b55c3643
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-6.c
@@ -0,0 +1,22 @@ 
+#include <stdlib.h>
+
+void *
+calls_malloc (void)
+{
+  void *result = malloc (1024); /* { dg-message "allocated here" } */
+  return result; /* { dg-warning "leak of 'result'" } */
+}
+
+void test_1 ()
+{
+  calls_malloc (); /* { dg-message "calling 'calls_malloc' from 'test_1'" } */
+}
+
+static void callee (int i)
+{
+}
+
+void test_2 (int i)
+{
+  callee (i);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c
new file mode 100644
index 000000000000..0742370d2f91
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c
@@ -0,0 +1,29 @@ 
+#include <stdlib.h>
+
+/**************************************************************************/
+
+static void maybe_calls_free_1(int *q, int flag)
+{
+  if (flag)
+    free(q); /* { dg-warning "double-'free' of 'q'" } */
+}
+
+void test_1(void *p)
+{
+  maybe_calls_free_1(p, 1);
+  maybe_calls_free_1(p, 1); 
+}
+
+/**************************************************************************/
+
+static void maybe_calls_free_2(int *q, int flag)
+{
+  if (flag)
+    free(q); /* { dg-bogus "double-'free'" } */
+}
+
+void test_2(void *p)
+{
+  maybe_calls_free_2(p, 0);
+  maybe_calls_free_2(p, 0);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c
new file mode 100644
index 000000000000..50d907f738c1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c
@@ -0,0 +1,172 @@ 
+/* Example of a multilevel wrapper around malloc/free, with a double-'free'.  */
+
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fanalyzer-checker=malloc -fanalyzer-verbose-state-changes -fdiagnostics-show-caret" } */
+
+#include <stdlib.h>
+
+void *wrapped_malloc (size_t size)
+{
+  return malloc (size);
+}
+
+void wrapped_free (void *ptr)
+{
+  free (ptr); /* { dg-warning "double-'free' of 'ptr' \\\[CWE-415\\\]" } */
+}
+
+typedef struct boxed_int
+{
+  int i;
+} boxed_int;
+
+boxed_int *
+make_boxed_int (int i)
+{
+  boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
+  if (!result)
+    abort ();
+  result->i = i;
+  return result;
+}
+
+void
+free_boxed_int (boxed_int *bi)
+{
+  wrapped_free (bi);
+}
+
+void test (int i)
+{
+  boxed_int *obj = make_boxed_int (i);
+
+  free_boxed_int (obj);
+
+  free_boxed_int (obj);
+}
+
+/* double-'free'.  */
+/* { dg-begin-multiline-output "" }
+   NN |   free (ptr);
+      |   ^~~~~~~~~~
+  'test': events 1-2
+    |
+    |   NN | void test (int i)
+    |      |      ^~~~
+    |      |      |
+    |      |      (1) entry to 'test'
+    |   NN | {
+    |   NN |   boxed_int *obj = make_boxed_int (i);
+    |      |                    ~~~~~~~~~~~~~~~~~~
+    |      |                    |
+    |      |                    (2) calling 'make_boxed_int' from 'test'
+    |
+    +--> 'make_boxed_int': events 3-6
+           |
+           |   NN | make_boxed_int (int i)
+           |      | ^~~~~~~~~~~~~~
+           |      | |
+           |      | (3) entry to 'make_boxed_int'
+           |......
+           |   NN |   if (!result)
+           |      |      ~
+           |      |      |
+           |      |      (4) following 'false' branch (when 'result' is non-NULL)...
+           |   NN |     abort ();
+           |   NN |   result->i = i;
+           |      |   ~~~~~~~~~~~~~
+           |      |             |
+           |      |             (5) ...to here
+           |   NN |   return result;
+           |      |          ~~~~~~
+           |      |          |
+           |      |          (6) state of '<return-value>': 'start' -> 'nonnull' (origin: NULL)
+           |
+    <------+
+    |
+  'test': events 7-8
+    |
+    |   NN |   boxed_int *obj = make_boxed_int (i);
+    |      |                    ^~~~~~~~~~~~~~~~~~
+    |      |                    |
+    |      |                    (7) returning to 'test' from 'make_boxed_int'
+    |   NN | 
+    |   NN |   free_boxed_int (obj);
+    |      |   ~~~~~~~~~~~~~~~~~~~~
+    |      |   |
+    |      |   (8) calling 'free_boxed_int' from 'test'
+    |
+    +--> 'free_boxed_int': events 9-10
+           |
+           |   NN | free_boxed_int (boxed_int *bi)
+           |      | ^~~~~~~~~~~~~~
+           |      | |
+           |      | (9) entry to 'free_boxed_int'
+           |   NN | {
+           |   NN |   wrapped_free (bi);
+           |      |   ~~~~~~~~~~~~~~~~~
+           |      |   |
+           |      |   (10) calling 'wrapped_free' from 'free_boxed_int'
+           |
+           +--> 'wrapped_free': events 11-12
+                  |
+                  |   NN | void wrapped_free (void *ptr)
+                  |      |      ^~~~~~~~~~~~
+                  |      |      |
+                  |      |      (11) entry to 'wrapped_free'
+                  |   NN | {
+                  |   NN |   free (ptr);
+                  |      |   ~~~~~~~~~~
+                  |      |   |
+                  |      |   (12) first 'free' here (state of 'ptr': 'nonnull' -> 'freed', origin: NULL)
+                  |
+           <------+
+           |
+         'free_boxed_int': event 13
+           |
+           |   NN |   wrapped_free (bi);
+           |      |   ^~~~~~~~~~~~~~~~~
+           |      |   |
+           |      |   (13) returning to 'free_boxed_int' from 'wrapped_free'
+           |
+    <------+
+    |
+  'test': events 14-15
+    |
+    |   NN |   free_boxed_int (obj);
+    |      |   ^~~~~~~~~~~~~~~~~~~~
+    |      |   |
+    |      |   (14) returning to 'test' from 'free_boxed_int'
+    |   NN | 
+    |   NN |   free_boxed_int (obj);
+    |      |   ~~~~~~~~~~~~~~~~~~~~
+    |      |   |
+    |      |   (15) passing freed pointer 'obj' in call to 'free_boxed_int' from 'test'
+    |
+    +--> 'free_boxed_int': events 16-17
+           |
+           |   NN | free_boxed_int (boxed_int *bi)
+           |      | ^~~~~~~~~~~~~~
+           |      | |
+           |      | (16) entry to 'free_boxed_int'
+           |   NN | {
+           |   NN |   wrapped_free (bi);
+           |      |   ~~~~~~~~~~~~~~~~~
+           |      |   |
+           |      |   (17) passing freed pointer 'bi' in call to 'wrapped_free' from 'free_boxed_int'
+           |
+           +--> 'wrapped_free': events 18-19
+                  |
+                  |   NN | void wrapped_free (void *ptr)
+                  |      |      ^~~~~~~~~~~~
+                  |      |      |
+                  |      |      (18) entry to 'wrapped_free'
+                  |   NN | {
+                  |   NN |   free (ptr);
+                  |      |   ~~~~~~~~~~
+                  |      |   |
+                  |      |   (19) second 'free' here; first 'free' was at (12) ('ptr' is in state 'freed')
+                  |
+   { dg-end-multiline-output "" } */
+
+/* TODO: the event describing the allocation is uninteresting and probably
+   should be purged.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto-a.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto-a.c
new file mode 100644
index 000000000000..c34e4beee4b0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto-a.c
@@ -0,0 +1,12 @@ 
+#include <stdlib.h>
+#include "malloc-ipa-8-lto.h"
+
+void *wrapped_malloc (size_t size)
+{
+  return malloc (size);
+}
+
+void wrapped_free (void *ptr)
+{
+  free (ptr);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto-b.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto-b.c
new file mode 100644
index 000000000000..c9d7df1a4522
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto-b.c
@@ -0,0 +1,18 @@ 
+#include <stdlib.h>
+#include "malloc-ipa-8-lto.h"
+
+boxed_int *
+make_boxed_int (int i)
+{
+  boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
+  if (!result)
+    abort ();
+  result->i = i;
+  return result;
+}
+
+void
+free_boxed_int (boxed_int *bi)
+{
+  wrapped_free (bi);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto-c.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto-c.c
new file mode 100644
index 000000000000..d332db13ef05
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto-c.c
@@ -0,0 +1,17 @@ 
+/* { dg-do link } */
+/* { dg-require-effective-target lto } */
+/* { dg-additional-options "-flto" } */
+/* { dg-additional-sources "malloc-ipa-8-lto-a.c malloc-ipa-8-lto-b.c" } */
+
+#include <stdlib.h>
+#include "malloc-ipa-8-lto.h"
+
+void test (int i)
+{
+  boxed_int *obj = make_boxed_int (i);
+
+  free_boxed_int (obj);
+  free (obj); /* { dg-warning "double-free" } */
+}
+
+int main() { return 0; }
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto.h b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto.h
new file mode 100644
index 000000000000..f24eefc0d0ea
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-lto.h
@@ -0,0 +1,12 @@ 
+#include <stddef.h>
+
+extern void *wrapped_malloc (size_t size);
+extern void wrapped_free (void *ptr);
+
+typedef struct boxed_int
+{
+  int i;
+} boxed_int;
+
+extern boxed_int *make_boxed_int (int i);
+extern void free_boxed_int (boxed_int *bi);
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c
new file mode 100644
index 000000000000..f204331f0a95
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c
@@ -0,0 +1,66 @@ 
+/* Example of a multilevel wrapper around malloc, with an unchecked write.  */
+
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fanalyzer-checker=malloc -fdiagnostics-show-caret -fanalyzer-verbose-state-changes" } */
+
+#include <stdlib.h>
+
+void *wrapped_malloc (size_t size)
+{
+  return malloc (size);
+}
+
+typedef struct boxed_int
+{
+  int i;
+} boxed_int;
+
+boxed_int *
+make_boxed_int (int i)
+{
+  boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
+  result->i = i; /* { dg-warning "dereference of possibly-NULL 'result'" } */
+  return result;
+}
+
+/* "dereference of possibly-NULL 'result' [CWE-690]".  */
+/* { dg-begin-multiline-output "" }
+   NN |   result->i = i;
+      |   ~~~~~~~~~~^~~
+  'make_boxed_int': events 1-2
+    |
+    |   NN | make_boxed_int (int i)
+    |      | ^~~~~~~~~~~~~~
+    |      | |
+    |      | (1) entry to 'make_boxed_int'
+    |   NN | {
+    |   NN |   boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
+    |      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    |      |                                    |
+    |      |                                    (2) calling 'wrapped_malloc' from 'make_boxed_int'
+    |
+    +--> 'wrapped_malloc': events 3-4
+           |
+           |   NN | void *wrapped_malloc (size_t size)
+           |      |       ^~~~~~~~~~~~~~
+           |      |       |
+           |      |       (3) entry to 'wrapped_malloc'
+           |   NN | {
+           |   NN |   return malloc (size);
+           |      |          ~~~~~~~~~~~~~
+           |      |          |
+           |      |          (4) this call could return NULL (state of '<return-value>': 'start' -> 'unchecked', origin: NULL)
+           |
+    <------+
+    |
+  'make_boxed_int': events 5-6
+    |
+    |   NN |   boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
+    |      |                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    |      |                                    |
+    |      |                                    (5) possible return of NULL to 'make_boxed_int' from 'wrapped_malloc'
+    |   NN |   result->i = i;
+    |      |   ~~~~~~~~~~~~~                     
+    |      |             |
+    |      |             (6) 'result' could be NULL: unchecked value from (4) ('result' is in state 'unchecked')
+    |
+  { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-9.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-9.c
new file mode 100644
index 000000000000..a0c78fd6c4ec
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-9.c
@@ -0,0 +1,18 @@ 
+/* { dg-additional-options "-fdiagnostics-path-format=none -fanalyzer-verbosity=1" } */
+
+#include <stdlib.h>
+
+void
+two_frees (void *p, void *q)
+{
+  free (p);
+  free (q); /* { dg-warning "double-'free' of 'q'" } */
+  /* TODO: could be useful to identify that p == q when called from 'test'.  */
+}
+
+extern void do_stuff (void);
+
+void test (void *ptr)
+{
+  two_frees (ptr, ptr);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c
new file mode 100644
index 000000000000..fad6d6e63744
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c
@@ -0,0 +1,45 @@ 
+/* Test path-printing in the face of macros.  */
+
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include "malloc-macro.h"
+
+/* { dg-warning "double-'free' of 'ptr'" "" { target *-*-* } 2 } */
+
+int test (void *ptr)
+{
+  WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
+  WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
+
+  /* Erase the spans indicating the header file
+     (to avoid embedding path assumptions).  */
+  /* { dg-regexp "\[^|\]+/malloc-macro.h:\[0-9\]+:\[0-9\]+:" } */
+  /* { dg-regexp "\[^|\]+/malloc-macro.h:\[0-9\]+:\[0-9\]+:" } */
+
+  /* { dg-begin-multiline-output "" }
+   NN | #define WRAPPED_FREE(PTR) free(PTR)
+      |                           ^~~~~~~~~
+   NN |   WRAPPED_FREE (ptr);
+      |   ^~~~~~~~~~~~
+  'test': event 1
+    |
+    |
+    |   NN | #define WRAPPED_FREE(PTR) free(PTR)
+    |      |                           ^~~~~~~~~
+    |      |                           |
+    |      |                           (1) first 'free' here
+    |   NN |   WRAPPED_FREE (ptr);
+    |      |   ^~~~~~~~~~~~
+    |
+  'test': event 2
+    |
+    |
+    |   NN | #define WRAPPED_FREE(PTR) free(PTR)
+    |      |                           ^~~~~~~~~
+    |      |                           |
+    |      |                           (2) second 'free' here; first 'free' was at (1)
+    |   NN |   WRAPPED_FREE (ptr);
+    |      |   ^~~~~~~~~~~~
+    |
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-macro-separate-events.c b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-separate-events.c
new file mode 100644
index 000000000000..e7483af9230e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-separate-events.c
@@ -0,0 +1,15 @@ 
+/* Test path-printing in the face of macros.  */
+
+/* { dg-additional-options "-fdiagnostics-path-format=separate-events" } */
+
+#include "malloc-macro.h"
+
+/* { dg-warning "double-'free' of 'ptr'" "" { target *-*-* } 2 } */
+/* { dg-message "first 'free' here" "" { target *-*-* } 2 } */
+/* { dg-message "second 'free' here" "" { target *-*-* } 2 } */
+
+int test (void *ptr)
+{
+  WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
+  WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-macro.h b/gcc/testsuite/gcc.dg/analyzer/malloc-macro.h
new file mode 100644
index 000000000000..8c05b406bd90
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-macro.h
@@ -0,0 +1,2 @@ 
+#include <stdlib.h>
+#define WRAPPED_FREE(PTR) free(PTR)
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-1.c
new file mode 100644
index 000000000000..82ad547a2016
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-1.c
@@ -0,0 +1,14 @@ 
+#include <stdlib.h>
+
+/* Ensure that we don't need to laboriously walk every path to get
+   to the end of the function.  */
+
+int test_1 (int n)
+{
+  int i, j, k;
+  k = 0;
+  for (int i = 0; i < n; i++)
+    for (int j = 0; j < 1000; j++)
+      k++;
+  return k;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-2.c b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-2.c
new file mode 100644
index 000000000000..9f6440b52b2d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-2.c
@@ -0,0 +1,30 @@ 
+#include <stdlib.h>
+
+/* Ensure that we don't get an exponential growth in paths due to
+   repeated diamonds in the CFG.  */
+
+typedef struct obj {
+  int ob_refcnt;
+} PyObject;
+
+extern void Py_Dealloc (PyObject *op);
+
+#define Py_DECREF(op)                                   \
+    do {                                                \
+      if (--((PyObject*)(op))->ob_refcnt == 0)		\
+	Py_Dealloc((PyObject *)(op));			\
+    } while (0)
+
+int test (PyObject *obj_01, PyObject *obj_02, PyObject *obj_03,
+	  PyObject *obj_04, PyObject *obj_05, PyObject *obj_06,
+	  PyObject *obj_07, PyObject *obj_08, PyObject *obj_09,
+	  PyObject *obj_10, PyObject *obj_11, PyObject *obj_12,
+	  PyObject *obj_13, PyObject *obj_14, PyObject *obj_15
+)
+{
+  Py_DECREF (obj_01); Py_DECREF (obj_02); Py_DECREF (obj_03);
+  Py_DECREF (obj_04); Py_DECREF (obj_05); Py_DECREF (obj_06);
+  Py_DECREF (obj_07); Py_DECREF (obj_08); Py_DECREF (obj_09);
+  Py_DECREF (obj_10); Py_DECREF (obj_11); Py_DECREF (obj_12);
+  Py_DECREF (obj_13); Py_DECREF (obj_14); Py_DECREF (obj_15);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-3.c b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-3.c
new file mode 100644
index 000000000000..e5d27a4a33a6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-3.c
@@ -0,0 +1,36 @@ 
+#include <stdlib.h>
+
+extern int foo (void);
+
+int successes;
+int failures;
+
+#define ONE_DIAMOND \
+    do {                                                \
+      void *ptr = malloc (128);				\
+      if (foo ())					\
+	successes++;					\
+      else						\
+	failures++;					\
+      free (ptr);					\
+    } while (0)
+
+#define TEN_DIAMONDS \
+  do {								   \
+    ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND;	\
+    ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; \
+ } while (0)
+
+void test_3 (void *ptr)
+{
+  free (ptr);
+#if 1
+  ONE_DIAMOND;
+#else
+  /* TODO: enabling this leads to numerous duplicated reports,
+     all of them detailing all the extraneous info about the malloc/free
+     within the diamonds.  */
+  TEN_DIAMONDS;
+#endif
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-1.c
new file mode 100644
index 000000000000..5d989ea665b7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-1.c
@@ -0,0 +1,15 @@ 
+/* Verify that we emit sane paths for state machine errors.  */
+
+#include <stdlib.h>
+
+void test_1 (void)
+{
+  void *ptr = malloc (1024); /* { dg-line malloc } */
+  free (ptr); /* { dg-line first_free } */
+  free (ptr); /* { dg-line second_free } */
+
+  /* { dg-warning "double-'free' of 'ptr'"  "" { target *-*-* } second_free } */
+  /* { dg-message "\\(1\\) allocated here" "" { target *-*-* } malloc } */
+  /* { dg-message "\\(2\\) first 'free' here" "" { target *-*-* } first_free } */
+  /* { dg-message "\\(3\\) second 'free' here; first 'free' was at \\(2\\)" "" { target *-*-* } second_free } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-10.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-10.c
new file mode 100644
index 000000000000..2a2937ed043e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-10.c
@@ -0,0 +1,20 @@ 
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+int test (int flag)
+{
+  int other_flag;
+  if (flag)
+    other_flag = 1;
+  else
+    other_flag = 0;
+
+  /* With state-merging, we lose the relationship between 'flag' and 'other_flag'.  */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  if (other_flag)
+    __analyzer_eval (flag); /* { dg-warning "UNKNOWN" } */
+  else
+    __analyzer_eval (flag); /* { dg-warning "UNKNOWN" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-2.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-2.c
new file mode 100644
index 000000000000..a9bf7a94d601
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-2.c
@@ -0,0 +1,13 @@ 
+/* Verify that we emit sane paths for state machine errors.  */
+
+#include <stdlib.h>
+
+void test_2 (void *ptr)
+{
+  free (ptr); /* { dg-line first_free } */
+  free (ptr); /* { dg-line second_free } */
+
+  /* { dg-warning "double-'free' of 'ptr'"  "" { target *-*-* } second_free } */
+  /* { dg-message "\\(1\\) first 'free' here" "" { target *-*-* } first_free } */
+  /* { dg-message "\\(2\\) second 'free' here; first 'free' was at \\(1\\)" "" { target *-*-* } second_free } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-3.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-3.c
new file mode 100644
index 000000000000..ed6026019dcf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-3.c
@@ -0,0 +1,14 @@ 
+/* Verify that we emit sane paths for state machine errors.  */
+
+#include <stdlib.h>
+
+int *test_3 (void)
+{
+  int *ptr = (int *)malloc (sizeof (int)); /* { dg-line malloc } */
+  *ptr = 42; /* { dg-line unchecked_deref } */
+  return ptr;
+
+  /* { dg-warning "dereference of possibly-NULL 'ptr'" "" { target *-*-* } unchecked_deref } */
+  /* { dg-message "\\(1\\) this call could return NULL" "" { target *-*-* } malloc } */
+  /* { dg-message "\\(2\\) 'ptr' could be NULL" "" { target *-*-* } unchecked_deref } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-4.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-4.c
new file mode 100644
index 000000000000..3385245828b3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-4.c
@@ -0,0 +1,20 @@ 
+/* Verify that we emit sane paths for state machine errors.  */
+
+#include <stdlib.h>
+
+int *test_4 (void)
+{
+  int *ptr = (int *)malloc (sizeof (int)); /* { dg-line malloc } */
+  if (ptr) /* { dg-line cond } */
+    *ptr = 42;
+  else
+    *ptr = 43; /* { dg-line on_null_ptr } */
+  return ptr;
+
+  /* { dg-warning "dereference of NULL 'ptr'" "" { target *-*-* } on_null_ptr } */
+  /* { dg-message "\\(1\\) allocated here" "" { target *-*-* } malloc } */
+  /* { dg-message "\\(2\\) assuming 'ptr' is NULL" "" { target *-*-* } cond } */
+  /* { dg-message "\\(3\\) following 'false' branch \\(when 'ptr' is NULL\\)\\.\\.\\." "" { target *-*-* } cond } */
+  /* { dg-message "\\(4\\) \\.\\.\\.to here" "" { target *-*-* } on_null_ptr } */
+  /* { dg-message "\\(5\\) dereference of NULL 'ptr'" "" { target *-*-* } on_null_ptr } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-5.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-5.c
new file mode 100644
index 000000000000..b54a81fbdcf2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-5.c
@@ -0,0 +1,43 @@ 
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void do_stuff (void);
+
+int test (const char *filename, int flag)
+{
+  FILE *f;
+  int *p, *q;
+  int i;
+
+  p = (int *)malloc (sizeof (int)); /* { dg-line malloc_of_p } */
+  if (!p) /* { dg-line test_of_p } */
+    {
+      free (p);
+      return -1;
+    }
+
+  q = (int *)malloc (sizeof (int)); /* { dg-line malloc_of_q } */
+  if (!q) /* { dg-line test_of_q } */
+    {
+      free (p); /* { dg-line first_free_of_p } */
+      /* oops: forgot the "return" here, so it falls through.  */
+    }
+
+  do_stuff ();
+
+  free (p); /* { dg-line second_free_of_p } */
+  free (q);
+  return 0;
+
+  /* { dg-warning "double-'free' of 'p'" "" { target *-*-* } second_free_of_p } */
+  /* { dg-message "\\(1\\) allocated here" "" { target *-*-* } malloc_of_p } */
+  /* { dg-message "\\(2\\) assuming 'p' is non-NULL" "" { target *-*-* } test_of_p } */
+  /* { dg-message "\\(3\\) following 'false' branch \\(when 'p' is non-NULL\\)\\.\\.\\." "" { target *-*-* } test_of_p } */
+  /* { dg-message "\\(4\\) \\.\\.\\.to here" "" { target *-*-* } malloc_of_q } */
+  /* { dg-message "\\(5\\) following 'true' branch \\(when 'q' is NULL\\)\\.\\.\\." "" { target *-*-* } test_of_q } */
+  /* { dg-message "\\(6\\) \\.\\.\\.to here" "" { target *-*-* } first_free_of_p } */
+  /* { dg-message "\\(7\\) first 'free' here" "" { target *-*-* } first_free_of_p } */
+  /* { dg-message "\\(8\\) second 'free' here; first 'free' was at \\(7\\)" "" { target *-*-* } second_free_of_p } */
+
+  /* We don't care about the state changes to q.  */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-6.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-6.c
new file mode 100644
index 000000000000..1df6964544e2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-6.c
@@ -0,0 +1,11 @@ 
+#include <stdlib.h>
+
+void test (void *ptr)
+{
+  void *q;
+  q = ptr;
+  free (ptr);
+  free (q); /* { dg-warning "double-'free' of 'q'" } */
+  /* The above case requires us to handle equivalence classes in
+     state transitions.  */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-7.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-7.c
new file mode 100644
index 000000000000..eb55604f0a57
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-7.c
@@ -0,0 +1,21 @@ 
+#include <stdlib.h>
+
+extern int foo (void);
+extern int bar (void);
+
+void test (void)
+{
+  void *p = malloc (1024); /* { dg-message "\\(1\\) allocated here" } */ 
+  void *q = malloc (1024);
+
+  foo ();
+  if (!q) /* { dg-message "\\(2\\) following 'true' branch \\(when 'q' is NULL\\)\\.\\.\\." } */ 
+    {
+      free (q); /* { dg-message "\\(3\\) \\.\\.\\.to here" } */ 
+      return; /* { dg-warning "leak of 'p'" } */ 
+      /* { dg-message "\\(4\\) 'p' leaks here; was allocated at \\(1\\)" "" { target *-*-* } .-1 } */
+    }
+  bar ();
+  free (q);
+  free (p);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c
new file mode 100644
index 000000000000..bf858e04840d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c
@@ -0,0 +1,54 @@ 
+/* { dg-additional-options "-fanalyzer-transitivity" } */
+
+#include <stddef.h>
+#include <stdlib.h>
+
+extern void do_stuff (const void *);
+
+#define LIMIT 1024
+
+void test_1 (size_t sz)
+{
+  void *ptr;
+  if (sz >= LIMIT)
+    ptr = malloc (sz);
+  else
+    ptr = alloca (sz);
+
+  do_stuff (ptr);
+
+  if (sz >= LIMIT)
+    free (ptr);
+}
+
+void test_2 (size_t sz)
+{
+  void *ptr;
+  if (sz < LIMIT)
+    ptr = alloca (sz);
+  else
+    ptr = malloc (sz);
+
+  do_stuff (ptr);
+
+  if (sz >= LIMIT)
+    free (ptr);
+}
+
+void test_3 (size_t sz)
+{
+  void *ptr;
+  if (sz <= LIMIT)
+    ptr = alloca (sz); /* { dg-message "memory is allocated on the stack here" } */
+  else
+    ptr = malloc (sz);
+
+  do_stuff (ptr);
+
+  /* Bug: the "sz <= LIMIT" above should have been "sz < LIMIT",
+     so there's a free-of-alloca when sz == LIMIT.  */
+  if (sz >= LIMIT)
+    free (ptr); /* { dg-warning "'free' of memory allocated on the stack by 'alloca'" } */
+}
+/* { dg-bogus "leak of 'ptr'" } */
+/* This can't happen, as "sz > 1024" && "sz <= 1023" is impossible.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c
new file mode 100644
index 000000000000..6bee885e7de0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c
@@ -0,0 +1,298 @@ 
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <stdlib.h>
+
+void test_1 (void)
+{
+  void *ptr = malloc (1024);
+  free (ptr);
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'"  } */
+}
+/* { dg-begin-multiline-output "" }
+   NN |   free (ptr);
+      |   ^~~~~~~~~~
+  'test_1': events 1-3
+    |
+    |   NN |   void *ptr = malloc (1024);
+    |      |               ^~~~~~~~~~~~~
+    |      |               |
+    |      |               (1) allocated here
+    |   NN |   free (ptr);
+    |      |   ~~~~~~~~~~   
+    |      |   |
+    |      |   (2) first 'free' here
+    |   NN |   free (ptr);
+    |      |   ~~~~~~~~~~   
+    |      |   |
+    |      |   (3) second 'free' here; first 'free' was at (2)
+    |
+   { dg-end-multiline-output "" } */
+
+void test_2 (int x, int y)
+{
+  void *ptr = malloc (1024);
+  if (x)
+    free (ptr);
+  if (y)
+    free (ptr); /* { dg-warning "double-'free' of 'ptr'"  } */
+} /* { dg-warning "leak of 'ptr'"  } */
+
+/* "double-'free' of 'ptr'".  */
+/* { dg-begin-multiline-output "" }
+   NN |     free (ptr);
+      |     ^~~~~~~~~~
+  'test_2': events 1-7
+    |
+    |   NN |   void *ptr = malloc (1024);
+    |      |               ^~~~~~~~~~~~~
+    |      |               |
+    |      |               (1) allocated here
+    |   NN |   if (x)
+    |      |      ~         
+    |      |      |
+    |      |      (2) following 'true' branch (when 'x != 0')...
+    |   NN |     free (ptr);
+    |      |     ~~~~~~~~~~ 
+    |      |     |
+    |      |     (3) ...to here
+    |      |     (4) first 'free' here
+    |   NN |   if (y)
+    |      |      ~         
+    |      |      |
+    |      |      (5) following 'true' branch (when 'y != 0')...
+    |   NN |     free (ptr);
+    |      |     ~~~~~~~~~~ 
+    |      |     |
+    |      |     (6) ...to here
+    |      |     (7) second 'free' here; first 'free' was at (4)
+    |
+   { dg-end-multiline-output "" } */
+
+/* "leak of 'ptr'.  */
+/* { dg-begin-multiline-output "" }
+   NN | }
+      | ^
+  'test_2': events 1-6
+    |
+    |   NN |   void *ptr = malloc (1024);
+    |      |               ^~~~~~~~~~~~~
+    |      |               |
+    |      |               (1) allocated here
+    |   NN |   if (x)
+    |      |      ~         
+    |      |      |
+    |      |      (2) following 'false' branch (when 'x == 0')...
+    |   NN |     free (ptr);
+    |   NN |   if (y)
+    |      |      ~         
+    |      |      |
+    |      |      (3) ...to here
+    |      |      (4) following 'false' branch (when 'y == 0')...
+    |   NN |     free (ptr);
+    |   NN | }
+    |      | ~              
+    |      | |
+    |      | (5) ...to here
+    |      | (6) 'ptr' leaks here; was allocated at (1)
+    |
+   { dg-end-multiline-output "" } */
+
+int test_3 (int x, int y)
+{
+  int *ptr = (int *)malloc (sizeof (int));
+  *ptr = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+  if (x)
+    free (ptr);
+
+  *ptr = 19; /* { dg-warning "use after 'free' of 'ptr'" } */
+  // TODO: two warnings here:  one is from sm-malloc, the other from region model
+
+  if (y)
+    free (ptr); /* No double-'free' warning: we've already attempted
+		   to dereference it above.  */
+  return *ptr; /* { dg-warning "use after 'free' of 'ptr'" } */
+  // TODO: two warnings here:  one is from sm-malloc, the other from region model
+  /* { dg-warning "leak of 'ptr'" "" { target *-*-* } .-2 } */
+}
+
+/* "dereference of possibly-NULL 'ptr'".  */
+/* { dg-begin-multiline-output "" }
+   NN |   *ptr = 42;
+      |   ~~~~~^~~~
+  'test_3': events 1-2
+    |
+    |   NN |   int *ptr = (int *)malloc (sizeof (int));
+    |      |                     ^~~~~~~~~~~~~~~~~~~~~
+    |      |                     |
+    |      |                     (1) this call could return NULL
+    |   NN |   *ptr = 42;
+    |      |   ~~~~~~~~~          
+    |      |        |
+    |      |        (2) 'ptr' could be NULL: unchecked value from (1)
+    |
+   { dg-end-multiline-output "" } */
+
+/* "use after 'free' of 'ptr'".  */
+/* { dg-begin-multiline-output "" }
+   NN |   *ptr = 19;
+      |   ~~~~~^~~~
+  'test_3': events 1-6
+    |
+    |   NN |   int *ptr = (int *)malloc (sizeof (int));
+    |      |                     ^~~~~~~~~~~~~~~~~~~~~
+    |      |                     |
+    |      |                     (1) allocated here
+    |   NN |   *ptr = 42;
+    |      |   ~~~~~~~~~          
+    |      |        |
+    |      |        (2) assuming 'ptr' is non-NULL
+    |   NN |   if (x)
+    |      |      ~               
+    |      |      |
+    |      |      (3) following 'true' branch (when 'x != 0')...
+    |   NN |     free (ptr);
+    |      |     ~~~~~~~~~~       
+    |      |     |
+    |      |     (4) ...to here
+    |      |     (5) freed here
+    |   NN | 
+    |   NN |   *ptr = 19;
+    |      |   ~~~~~~~~~          
+    |      |        |
+    |      |        (6) use after 'free' of 'ptr'; freed at (5)
+    |
+   { dg-end-multiline-output "" } */
+
+/* "use after 'free' of 'ptr'".  */
+/* { dg-begin-multiline-output "" }
+   NN |   return *ptr;
+      |          ^~~~
+  'test_3': events 1-8
+    |
+    |   NN |   int *ptr = (int *)malloc (sizeof (int));
+    |      |                     ^~~~~~~~~~~~~~~~~~~~~
+    |      |                     |
+    |      |                     (1) allocated here
+    |   NN |   *ptr = 42;
+    |      |   ~~~~~~~~~          
+    |      |        |
+    |      |        (2) assuming 'ptr' is non-NULL
+    |   NN |   if (x)
+    |      |      ~               
+    |      |      |
+    |      |      (3) following 'false' branch (when 'x == 0')...
+    |......
+    |   NN |   *ptr = 19;
+    |      |   ~~~~~~~~~          
+    |      |        |
+    |      |        (4) ...to here
+    |......
+    |   NN |   if (y)
+    |      |      ~               
+    |      |      |
+    |      |      (5) following 'true' branch (when 'y != 0')...
+    |   NN |     free (ptr);
+    |      |     ~~~~~~~~~~       
+    |      |     |
+    |      |     (6) ...to here
+    |      |     (7) freed here
+    |   NN |      
+    |   NN |   return *ptr;
+    |      |          ~~~~        
+    |      |          |
+    |      |          (8) use after 'free' of 'ptr'; freed at (7)
+    |
+   { dg-end-multiline-output "" } */
+
+/* "leak of 'ptr'".  */
+/* { dg-begin-multiline-output "" }
+   NN |   return *ptr;
+      |          ^~~~
+  'test_3': events 1-7
+    |
+    |   NN |   int *ptr = (int *)malloc (sizeof (int));
+    |      |                     ^~~~~~~~~~~~~~~~~~~~~
+    |      |                     |
+    |      |                     (1) allocated here
+    |   NN |   *ptr = 42;
+    |      |   ~~~~~~~~~          
+    |      |        |
+    |      |        (2) assuming 'ptr' is non-NULL
+    |   NN |   if (x)
+    |      |      ~               
+    |      |      |
+    |      |      (3) following 'false' branch (when 'x == 0')...
+    |......
+    |   NN |   *ptr = 19;
+    |      |   ~~~~~~~~~          
+    |      |        |
+    |      |        (4) ...to here
+    |......
+    |   NN |   if (y)
+    |      |      ~               
+    |      |      |
+    |      |      (5) following 'false' branch (when 'y == 0')...
+    |......
+    |   NN |   return *ptr;
+    |      |          ~~~~        
+    |      |          |
+    |      |          (6) ...to here
+    |      |          (7) 'ptr' leaks here; was allocated at (1)
+    |
+   { dg-end-multiline-output "" } */
+
+/* "use after 'free' of 'ptr'".  */
+/* { dg-begin-multiline-output "" }
+   NN |   *ptr = 19;
+      |   ~~~~~^~~~
+  'test_3': events 1-3
+    |
+    |   NN |   if (x)
+    |      |      ^
+    |      |      |
+    |      |      (1) following 'true' branch (when 'x != 0')...
+    |   NN |     free (ptr);
+    |      |     ~~~~~~~~~~
+    |      |     |
+    |      |     (2) ...to here
+    |   NN | 
+    |   NN |   *ptr = 19;
+    |      |   ~~~~~~~~~
+    |      |        |
+    |      |        (3) use after 'free' of 'ptr' here
+    |
+   { dg-end-multiline-output "" } */
+
+/* "use after 'free' of 'ptr'".  */
+/* { dg-begin-multiline-output "" }
+   NN |   return *ptr;
+      |          ^~~~
+  'test_3': events 1-5
+    |
+    |   NN |   if (x)
+    |      |      ^
+    |      |      |
+    |      |      (1) following 'false' branch (when 'x == 0')...
+    |......
+    |   NN |   *ptr = 19;
+    |      |   ~~~~~~~~~
+    |      |        |
+    |      |        (2) ...to here
+    |......
+    |   NN |   if (y)
+    |      |      ~
+    |      |      |
+    |      |      (3) following 'true' branch (when 'y != 0')...
+    |   NN |     free (ptr);
+    |      |     ~~~~~~~~~~
+    |      |     |
+    |      |     (4) ...to here
+    |   NN |      to dereference it above
+    |   NN |   return *ptr;
+    |      |          ~~~~
+    |      |          |
+    |      |          (5) use after 'free' of 'ptr' here
+    |
+   { dg-end-multiline-output "" } */
+/* TODO: this is really a duplicate; can we either eliminate it, or
+   improve the path?  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c
new file mode 100644
index 000000000000..72360c2f9fad
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c
@@ -0,0 +1,181 @@ 
+/* { dg-additional-options "-fno-analyzer-call-summaries -fanalyzer-transitivity" } */
+
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+extern int foo (int);
+
+static int __attribute__((noinline))
+do_stuff (int *p, int n)
+{
+  int sum = 0;
+  int i;
+  for (i = 0; i < n; i++)
+    p[i] = i;
+  for (i = 0; i < n; i++)
+    sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+  return sum;
+}
+
+static int __attribute__((noinline))
+do_stuff_2 (int *p, int n)
+{
+  return 0;
+}
+
+/* Various examples of functions that use either a malloc buffer
+   or a local buffer, do something, then conditionally free the
+   buffer, tracking whether "free" is necessary in various
+   ways.
+
+   In each case, there ought to be only two paths through the function,
+   not four.  */
+
+/* Repeated (n > 10) predicate.  */
+
+int test_repeated_predicate_1 (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  result = do_stuff (ptr, n);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+  // FIXME: why 3 here?
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+  // FIXME: why 3 here?
+
+  if (n > 10)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
+
+/* A simpler version of the above.  */
+
+int test_repeated_predicate_2 (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  result = do_stuff_2 (ptr, n);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  if (n > 10)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
+
+/* A predicate that sets a flag for the 2nd test.  */
+
+int test_explicit_flag (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+  int need_to_free = 0;
+
+  if (n > 10)
+    {
+      ptr = (int *)malloc (sizeof (int) * n);
+      need_to_free = 1;
+    }
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  result = do_stuff (ptr, n);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+  // FIXME: why 3 here?
+
+  if (need_to_free)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
+
+/* Pointer comparison.  */
+
+int test_pointer_comparison (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  result = do_stuff (ptr, n);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+  // FIXME: why 3 here?
+
+  if (ptr != buf)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
+
+/* Set a flag based on a conditional, then use it, then reuse the
+   conditional.  */
+
+int test_initial_flag (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+  int on_heap = 0;
+
+  if (n > 10)
+    on_heap = 1;
+  else
+    on_heap = 0;
+
+  /* Due to state-merging, we lose the relationship between 'n > 10'
+     and 'on_heap' here; we have to rely on feasibility-checking
+     in the diagnostic_manager to reject the false warnings.  */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  if (on_heap)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  result = do_stuff (ptr, n);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 exploded nodes" } */
+  // FIXME: why 5 here?
+
+  if (n > 10)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1b.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1b.c
new file mode 100644
index 000000000000..1997bb7adb42
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1b.c
@@ -0,0 +1,176 @@ 
+/* { dg-additional-options "-fanalyzer-call-summaries" } */
+
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+extern int foo (int);
+
+static int __attribute__((noinline))
+do_stuff (int *p, int n)
+{
+  int sum = 0;
+  int i;
+  for (i = 0; i < n; i++)
+    p[i] = i;
+  for (i = 0; i < n; i++)
+    sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+  return sum;
+}
+
+static int __attribute__((noinline))
+do_stuff_2 (int *p, int n)
+{
+  return 0;
+}
+
+/* Various examples of functions that use either a malloc buffer
+   or a local buffer, do something, then conditionally free the
+   buffer, tracking whether "free" is necessary in various
+   ways.
+
+   In each case, there ought to be only two paths through the function,
+   not four.  */
+
+/* Repeated (n > 10) predicate.  */
+
+int test_repeated_predicate_1 (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  result = do_stuff (ptr, n);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  if (n > 10)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
+
+/* A simpler version of the above.  */
+
+int test_repeated_predicate_2 (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  result = do_stuff_2 (ptr, n);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  if (n > 10)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
+
+/* A predicate that sets a flag for the 2nd test.  */
+
+int test_explicit_flag (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+  int need_to_free = 0;
+
+  if (n > 10)
+    {
+      ptr = (int *)malloc (sizeof (int) * n);
+      need_to_free = 1;
+    }
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  result = do_stuff (ptr, n);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  if (need_to_free)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
+
+/* Pointer comparison.  */
+
+int test_pointer_comparison (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  result = do_stuff (ptr, n);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  if (ptr != buf)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
+
+/* Set a flag based on a conditional, then use it, then reuse the
+   conditional.  */
+
+int test_initial_flag (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+  int on_heap = 0;
+
+  if (n > 10)
+    on_heap = 1;
+  else
+    on_heap = 0;
+
+  /* Due to state-merging, we lose the relationship between 'n > 10'
+     and 'on_heap' here; we have to rely on feasibility-checking
+     in the diagnostic_manager to reject the false warnings.  */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  if (on_heap)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  result = do_stuff (ptr, n);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  if (n > 10)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c
new file mode 100644
index 000000000000..74d9687b960f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c
@@ -0,0 +1,179 @@ 
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+extern int foo (int);
+
+static int __attribute__((noinline))
+do_stuff_2 (int *p, int n)
+{
+  return 0;
+}
+
+/* As malloc-vs-local.c, but hand-inlining the logic.  */
+
+/* Repeated (n > 10) predicate.  */
+
+int test_repeated_predicate_1 (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  {
+    int *p = ptr;
+    int sum = 0;
+    int i;
+    for (i = 0; i < n; i++)
+      p[i] = i;
+    for (i = 0; i < n; i++)
+      sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+    result = sum;
+  }
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+  if (n > 10)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
+
+/* As above, but with just one loop.  */
+
+int test_repeated_predicate_1a (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  {
+    int *p = ptr;
+    int sum = 0;
+    int i;
+    for (i = 0; i < n; i++)
+      p[i] = i;
+    result = sum;
+  }
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+  if (n > 10)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
+
+/* A simpler version of the above.  */
+
+int test_repeated_predicate_2 (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  result = do_stuff_2 (ptr, n);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  if (n > 10)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
+
+/* A predicate that sets a flag for the 2nd test.  */
+
+int test_explicit_flag (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+  int need_to_free = 0;
+
+  if (n > 10)
+    {
+      ptr = (int *)malloc (sizeof (int) * n);
+      need_to_free = 1;
+    }
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  {
+    int *p = ptr;
+    int sum = 0;
+    int i;
+    for (i = 0; i < n; i++)
+      p[i] = i;
+    for (i = 0; i < n; i++)
+      sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+    result = sum;
+  }
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+  if (need_to_free)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
+
+/* Pointer comparison.  */
+
+int test_pointer_comparison (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  {
+    int *p = ptr;
+    int sum = 0;
+    int i;
+    for (i = 0; i < n; i++)
+      p[i] = i;
+    for (i = 0; i < n; i++)
+      sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+    result = sum;
+  }
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+  if (ptr != buf)
+    free (ptr); /* { dg-bogus "not on the heap" } */
+
+  return result; /* { dg-bogus "leak" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c
new file mode 100644
index 000000000000..fe9b240be5b7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c
@@ -0,0 +1,66 @@ 
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+extern int foo (int);
+
+static int __attribute__((noinline))
+do_stuff_2 (int *p, int n)
+{
+  return 0;
+}
+
+/* As malloc-vs-local-2.c, but with a memory leak for the "on the heap case"
+   by not attempting to free at the end.  */
+
+int test_1 (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  {
+    int *p = ptr;
+    int sum = 0;
+    int i;
+    for (i = 0; i < n; i++)
+      p[i] = i;
+    for (i = 0; i < n; i++)
+      sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+    result = sum;
+  }
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+  return result; /* { dg-message "leak of 'p'" } */
+  /* FIXME: should this be 'ptr'?  */
+}
+
+/* A simpler version of the above.  */
+
+int test_2 (int n)
+{
+  int buf[10];
+  int *ptr;
+  int result;
+
+  if (n > 10)
+    ptr = (int *)malloc (sizeof (int) * n);
+  else
+    ptr = buf;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  result = do_stuff_2 (ptr, n);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  return result; /* { dg-message "leak of 'ptr'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-4.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-4.c
new file mode 100644
index 000000000000..7410a717762b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-4.c
@@ -0,0 +1,40 @@ 
+#include <stdlib.h>
+
+void __attribute__((noinline)) callee_1 (int *ptr)
+{
+  *ptr = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+}
+
+int test_1 (int i, int flag)
+{
+  /* Double diamond CFG; either use &i, or a malloc-ed buffer.  */
+  int *ptr = &i;
+  if (flag)
+    ptr = (int *)malloc (sizeof (int));
+  callee_1 (ptr);
+  if (flag)
+    free (ptr);
+  return i;
+}
+
+void __attribute__((noinline)) callee_2 (int *ptr)
+{
+  *ptr = 42;
+}
+
+int test_2 (int flag)
+{
+  int i;
+
+  if (flag)
+    callee_2 (&i);
+
+  callee_2 (&i);
+
+  if (!flag)
+    {
+      void *ptr = malloc (16);
+      free (ptr);
+      free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/operations.c b/gcc/testsuite/gcc.dg/analyzer/operations.c
new file mode 100644
index 000000000000..0f8aad21afe7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/operations.c
@@ -0,0 +1,44 @@ 
+#include "analyzer-decls.h"
+
+void test (int i, int j)
+{
+  int k, m;
+
+  if (i > 42) {
+    __analyzer_eval (i > 42); /* { dg-warning "TRUE" } */
+
+    i += 3;
+
+    __analyzer_eval (i > 45); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+    /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+    /* TODO(xfail): do we really know this?  what about overflow?  */
+
+    i -= 1;
+
+    __analyzer_eval (i > 44); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+    /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+    /* TODO(xfail): do we really know this?  what about overflow?  */
+
+    i = 3 * i;
+
+    __analyzer_eval (i > 132); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+    /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+    /* TODO(xfail): do we really know this?  what about overflow?  */
+
+    i /= 2;
+
+    __analyzer_eval (i > 66); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+    /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+    /* TODO(xfail): do we really know this?  what about overflow?  */
+
+    /* We don't know anything about j, so we don't know anything about k: */
+    k = i + j;
+    __analyzer_eval (k == 0); /* { dg-warning "UNKNOWN" } */
+
+    /* However, we should now know that m > 67: */
+    m = i + 1;
+    __analyzer_eval (m > 67); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+    /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+    /* TODO(xfail): do we really know this?  what about overflow?  */
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/params-2.c b/gcc/testsuite/gcc.dg/analyzer/params-2.c
new file mode 100644
index 000000000000..433c658720f2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/params-2.c
@@ -0,0 +1,17 @@ 
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+static void ensure_equal (int a, int b)
+{
+  if (a != b)
+    abort ();
+}
+
+void test(int i, int j)
+{
+  __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */
+
+  ensure_equal (i, j);
+
+  __analyzer_eval (i == j); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/params.c b/gcc/testsuite/gcc.dg/analyzer/params.c
new file mode 100644
index 000000000000..02371da98871
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/params.c
@@ -0,0 +1,34 @@ 
+#include "analyzer-decls.h"
+
+static int called_function(int j)
+{
+  int k;
+
+  __analyzer_eval (j > 4); /* { dg-warning "TRUE" } */
+
+  k = j - 1;
+
+  __analyzer_eval (k > 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+  /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+  /* TODO(xfail): we're not then updating based on the assignment.  */
+
+  return k;
+}
+
+void test(int i)
+{
+  __analyzer_eval (i > 4); /* { dg-warning "UNKNOWN" } */
+
+  if (i > 4) {
+
+    __analyzer_eval (i > 4); /* { dg-warning "TRUE" } */
+
+    i = called_function(i);
+
+    __analyzer_eval (i > 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+    /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+    /* TODO(xfail): we're not updating from the returned value.  */
+  }
+
+  __analyzer_eval (i > 3); /* { dg-warning "UNKNOWN" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-1.c b/gcc/testsuite/gcc.dg/analyzer/paths-1.c
new file mode 100644
index 000000000000..064687744723
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-1.c
@@ -0,0 +1,18 @@ 
+#include "analyzer-decls.h"
+
+struct foo
+{
+  int m_flag;
+};
+
+extern void bar (int);
+
+void test (struct foo *pf)
+{
+  if (pf->m_flag)
+    bar (0);
+  else
+    bar (1);
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-1a.c b/gcc/testsuite/gcc.dg/analyzer/paths-1a.c
new file mode 100644
index 000000000000..8760de93499f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-1a.c
@@ -0,0 +1,18 @@ 
+#include "analyzer-decls.h"
+
+union foo
+{
+  int m_flag;
+};
+
+extern void bar (int);
+
+void test (union foo *pf)
+{
+  if (pf->m_flag)
+    bar (0);
+  else
+    bar (1);
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-2.c b/gcc/testsuite/gcc.dg/analyzer/paths-2.c
new file mode 100644
index 000000000000..c48a2d7758c5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-2.c
@@ -0,0 +1,27 @@ 
+#include "analyzer-decls.h"
+
+int test (int a)
+{
+  if (a != 42 && a != 113) {
+    return (-2);
+  }
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  return 0;
+}
+
+int test_2 (int a)
+{
+  if (a != 42 && a != 113 && a != 666) {
+    return (-2);
+  }
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-3.c b/gcc/testsuite/gcc.dg/analyzer/paths-3.c
new file mode 100644
index 000000000000..440213b79915
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-3.c
@@ -0,0 +1,49 @@ 
+/* { dg-additional-options "-fanalyzer-transitivity" } */
+
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+int test_1 (int a, int b)
+{
+  void *p;
+
+  if (a > 5)
+    if (b)
+      p = malloc (16);
+    else
+      p = malloc (32);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  if (a > 5)
+    {
+      free (p);
+      __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+    }
+
+  return 0; /* { dg-bogus "leak" } */
+}
+
+int test_2 (int a, int b)
+{
+  void *p;
+
+  if (a > 5)
+    if (b)
+      p = malloc (16);
+    else
+      p = malloc (32);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  if (a > 6) /* different condition */
+    {
+      free (p);
+      __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+    }
+
+  return 0; /* { dg-warning "leak of 'p'" } */
+  /* leaks when a == 5.  */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-4.c b/gcc/testsuite/gcc.dg/analyzer/paths-4.c
new file mode 100644
index 000000000000..34bd09eee057
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-4.c
@@ -0,0 +1,51 @@ 
+#include "analyzer-decls.h"
+
+struct state
+{
+  int mode;
+  int data;
+};
+
+extern void do_stuff (struct state *, int);
+
+int test_1 (struct state *s)
+{
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+  while (1)
+    {
+      __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+      __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+      do_stuff (s, s->mode);
+    }
+}
+
+int test_2 (struct state *s)
+{
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+  while (1)
+    {
+      __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+      __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+      switch (s->mode)
+	{
+	case 0:
+	  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+	  do_stuff (s, 0);
+	  break;
+	case 1:
+	  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+	  do_stuff (s, 17);
+	  break;
+	case 2:
+	  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+	  do_stuff (s, 5);
+	  break;
+	case 3:
+	  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+	  return 42;
+	case 4:
+	  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+	  return -3;
+	}
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-5.c b/gcc/testsuite/gcc.dg/analyzer/paths-5.c
new file mode 100644
index 000000000000..f96169d9d2bc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-5.c
@@ -0,0 +1,12 @@ 
+#include "analyzer-decls.h"
+
+void test (int *p, int n)
+{
+  int i;
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+  for (i = 0; i < n; i++)
+    {
+      p[i] = i; /* { dg-bogus "uninitialized" } */
+      __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-6.c b/gcc/testsuite/gcc.dg/analyzer/paths-6.c
new file mode 100644
index 000000000000..7a1a94228dd9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-6.c
@@ -0,0 +1,119 @@ 
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+/* Verify that ordering of writes doesn't matter when merging states.  */
+
+/* Test with locals.  */
+
+void test_1 (int flag)
+{
+  int a, b;
+  if (flag)
+    {
+      a = 3;
+      b = 4;
+    }
+  else
+    {
+      b = 4;
+      a = 3;
+    }
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  // FIXME: the above can vary between 2 and 3 exploded nodes
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+  __analyzer_eval (a == 3); /* { dg-warning "TRUE" } */
+  __analyzer_eval (b == 4); /* { dg-warning "TRUE" } */
+}
+
+/* Test with globals.  */
+
+int f, g, h;
+void test_2 (int flag)
+{
+  if (flag)
+    {
+      f = 3;
+      g = 4;
+    }
+  else
+    {
+      g = 4;
+      f = 3;
+    }
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+  __analyzer_eval (f == 3); /* { dg-warning "TRUE" } */
+  __analyzer_eval (g == 4); /* { dg-warning "TRUE" } */
+}
+
+/* All 6 orderings of writes to 3 globals.  */
+
+void test_3 (int i)
+{
+  switch (i)
+    {
+    default:
+    case 0:
+      f = 3;
+      g = 4;
+      h = 5;
+      break;
+
+    case 1:
+      f = 3;
+      h = 5;
+      g = 4;
+      break;
+
+    case 2:
+      g = 4;
+      f = 3;
+      h = 5;
+      break;
+
+    case 3:
+      g = 4;
+      h = 5;
+      f = 3;
+      break;
+
+    case 4:
+      h = 5;
+      f = 3;
+      g = 4;
+      break;
+
+    case 5:
+      h = 5;
+      g = 4;
+      f = 3;
+      break;
+    }
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "6 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded node" } */
+  __analyzer_eval (f == 3); /* { dg-warning "TRUE" } */
+  __analyzer_eval (g == 4); /* { dg-warning "TRUE" } */
+  __analyzer_eval (h == 5); /* { dg-warning "TRUE" } */
+}
+
+void test_4 (int flag)
+{
+  void *p, *q;
+  if (flag)
+    {
+      p = malloc (256);
+      q = malloc (256);
+    }
+  else
+    {
+      q = malloc (256);
+      p = malloc (256);
+    }
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+  free (p);
+  free (q);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-7.c b/gcc/testsuite/gcc.dg/analyzer/paths-7.c
new file mode 100644
index 000000000000..6a99e64439fc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/paths-7.c
@@ -0,0 +1,59 @@ 
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+extern int foo (int);
+
+int test (int flag, void *ptr, int *p, int n)
+{
+  int result;
+  int sum = 0;
+  int i;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  if (flag)
+    free (ptr);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  for (i = 0; i < n; i++)
+    p[i] = i;
+  for (i = 0; i < n; i++)
+    sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+  result = sum;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  if (flag)
+    free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+  return result;
+}
+
+int test_2 (int flag, int *p, int n)
+{
+  int result;
+  int sum = 0;
+  int i;
+
+  void *ptr = malloc (16);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  if (flag)
+    free (ptr);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+  for (i = 0; i < n; i++)
+    p[i] = i;
+  for (i = 0; i < n; i++)
+    sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+  result = sum;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 exploded nodes" } */
+  // FIXME: why 5 here?
+
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+  return result;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c b/gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c
new file mode 100644
index 000000000000..8a1ca58eb184
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c
@@ -0,0 +1,28 @@ 
+/* { dg-additional-options "-fanalyzer-checker=pattern-test" } */
+
+#include <stdlib.h>
+
+extern void foo(void *);
+extern void bar(void *);
+
+void test1(void *ptr)
+{
+  if (ptr) { /* { dg-warning "pattern match on 'ptr != 0'" } */
+  /* { dg-warning "pattern match on 'ptr == 0'" "" { target *-*-* } .-1 } */
+    foo(ptr);
+  } else {
+    bar(ptr);
+  }
+}
+
+void test_2 (void *p, void *q)
+{
+  if (p == NULL || q == NULL) /* { dg-line cond_2 }  */
+    return;
+  foo(p);
+
+  /* { dg-warning "pattern match on 'p == 0'" "" { target *-*-* } cond_2 } */
+  /* { dg-warning "pattern match on 'q == 0'" "" { target *-*-* } cond_2 } */
+  /* { dg-warning "pattern match on 'p != 0'" "" { target *-*-* } cond_2 } */
+  /* { dg-warning "pattern match on 'q != 0'" "" { target *-*-* } cond_2 } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c b/gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c
new file mode 100644
index 000000000000..392490034dfa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c
@@ -0,0 +1,29 @@ 
+/* { dg-additional-options "-fanalyzer-checker=pattern-test -O2" } */
+// TODO: run this at every optimization level
+
+#include <stdlib.h>
+
+extern void foo(void *);
+extern void bar(void *);
+
+void test1(void *ptr)
+{
+  if (ptr) { /* { dg-warning "pattern match on 'ptr != 0'" } */
+  /* { dg-warning "pattern match on 'ptr == 0'" "" { target *-*-* } .-1 } */
+    foo(ptr);
+  } else {
+    bar(ptr);
+  }
+}
+
+void test_2 (void *p, void *q)
+{
+  if (p == NULL || q == NULL) /* { dg-line cond_2 }  */
+    return;
+  foo(p);
+
+  /* { dg-warning "pattern match on '<unknown> == 0'" "" { target *-*-* } cond_2 } */
+  /* { dg-warning "pattern match on '<unknown> != 0'" "" { target *-*-* } cond_2 } */
+  /* { dg-warning "pattern match on 'p != 0'" "" { target *-*-* } cond_2 } */
+  /* { dg-warning "pattern match on 'q != 0'" "" { target *-*-* } cond_2 } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pointer-merging.c b/gcc/testsuite/gcc.dg/analyzer/pointer-merging.c
new file mode 100644
index 000000000000..dcf0ff048bb2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pointer-merging.c
@@ -0,0 +1,16 @@ 
+static char * __attribute__((noinline))
+test_1_callee (int flag, char *a, char *b)
+{
+  char *p;
+  if (flag)
+    p = a;
+  else
+    p = b;
+  return p;
+}
+
+char test_1_caller(int flag) {
+  char a = 42;
+  char b = 43;
+  return *test_1_callee(flag, &a, &b);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr61861.c b/gcc/testsuite/gcc.dg/analyzer/pr61861.c
new file mode 100644
index 000000000000..a85e74389052
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr61861.c
@@ -0,0 +1,2 @@ 
+/* { dg-additional-options "-Wno-int-conversion" } */
+#include "../pr61861.c"
diff --git a/gcc/testsuite/gcc.dg/analyzer/pragma-1.c b/gcc/testsuite/gcc.dg/analyzer/pragma-1.c
new file mode 100644
index 000000000000..2e533489405b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pragma-1.c
@@ -0,0 +1,26 @@ 
+/* Verify that we can disable analyzer warnings via pragmas.  */
+
+#include <stdlib.h>
+
+void test_1 (void *ptr)
+{
+  free (ptr);
+  free (ptr); /* { dg-warning "double-'free'" } */
+}
+
+void test_2 (void *ptr)
+{
+  _Pragma("GCC diagnostic push")
+  _Pragma("GCC diagnostic ignored \"-Wanalyzer-double-free\"")
+
+  free (ptr);
+  free (ptr);  /* { dg-bogus "double-'free'" } */
+
+  _Pragma("GCC diagnostic pop")
+}
+
+void test_3 (void *ptr)
+{
+  free (ptr);
+  free (ptr);  /* { dg-warning "double-'free'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/scope-1.c b/gcc/testsuite/gcc.dg/analyzer/scope-1.c
new file mode 100644
index 000000000000..f8acffa18e41
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/scope-1.c
@@ -0,0 +1,23 @@ 
+#include <stdlib.h>
+
+int test_1 (void)
+{
+  {
+    int *q = malloc (1024);
+  }
+
+  return 42; /* { dg-warning "leak of 'q'" } */
+  // FIXME: would be better to report it at the close-of-scope
+}
+
+int test_2 (void)
+{
+  {
+    void *q = malloc (1024);
+  }
+
+  int q = 42;
+
+  return q; /* { dg-warning "leak of 'q'" } */
+  // FIXME: would be better to report it at the close-of-scope
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/sensitive-1.c b/gcc/testsuite/gcc.dg/analyzer/sensitive-1.c
new file mode 100644
index 000000000000..1a5ab47d3e7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/sensitive-1.c
@@ -0,0 +1,55 @@ 
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+char test_1 (FILE *logfile)
+{
+  char *password = getpass (">"); /* { dg-message "\\(1\\) sensitive value acquired here" } */
+  fprintf (logfile, "got password %s\n", password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+  /* { dg-message "\\(2\\) sensitive value 'password' written to output file; acquired at \\(1\\)" "" { target *-*-* } .-1 } */
+}
+
+char test_2 (FILE *logfile, int i)
+{
+  char *password = getpass (">"); /* { dg-message "\\(1\\) sensitive value acquired here" } */
+  fprintf (logfile, "got password[%i]: %s\n", i, password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+  /* { dg-message "\\(2\\) sensitive value 'password' written to output file; acquired at \\(1\\)" "" { target *-*-* } .-1 } */
+}
+
+char test_3 (FILE *logfile)
+{
+  char *password = getpass (">"); /* { dg-message "\\(1\\) sensitive value acquired here" } */
+  printf ("got password %s\n", password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+  /* { dg-message "\\(2\\) sensitive value 'password' written to output file; acquired at \\(1\\)" "" { target *-*-* } .-1 } */
+}
+
+char test_4 (FILE *logfile)
+{
+  char *password = getpass (">"); /* { dg-message "\\(1\\) sensitive value acquired here" } */
+  fwrite (password, strlen (password), 1, logfile); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+  /* { dg-message "\\(2\\) sensitive value 'password' written to output file; acquired at \\(1\\)" "" { target *-*-* } .-1 } */
+}
+
+static void called_by_test_5 (const char *value)
+{
+  printf ("%s", value); /* { dg-warning "sensitive value 'value' written to output file \\\[CWE-532\\\]" } */
+}
+
+char test_5 (FILE *logfile)
+{
+  char *password = getpass (">");
+  called_by_test_5 (password); /* { dg-message "passing sensitive value 'password' in call to 'called_by_test_5' from 'test_5'" } */
+}
+
+static char *called_by_test_6 (void)
+{
+  return getpass (">"); /* { dg-message "sensitive value acquired here" } */
+}
+
+char test_6 (FILE *logfile)
+{
+  char *password = called_by_test_6 (); /* { dg-message "returning sensitive value to 'test_6' from 'called_by_test_6'" } */
+  printf ("%s", password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+}
+
+/* TODO: strdup etc, strcpy, memcpy, etc.  */
diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-1.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-1.c
new file mode 100644
index 000000000000..c9827fbd2989
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-1.c
@@ -0,0 +1 @@ 
+#include "../pr26983.c"
diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-2.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-2.c
new file mode 100644
index 000000000000..2ba3913b4c75
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-2.c
@@ -0,0 +1,98 @@ 
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+#include <stddef.h>
+#include "analyzer-decls.h"
+
+extern void foo (int);
+
+void test_1 (void)
+{
+  setjmp (NULL);
+}
+
+void test_2 (void)
+{
+  jmp_buf env;
+  int i;
+
+  foo (0);
+
+  i = setjmp(env);
+
+  foo (1);
+
+  if (i != 0)
+    {
+      foo (2);
+      __analyzer_dump_path (); /* { dg-message "path" } */
+    }
+  else
+    longjmp (env, 1);
+
+  foo (3);
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |       __analyzer_dump_path ();
+      |       ^~~~~~~~~~~~~~~~~~~~~~~
+  'test_2': event 1
+    |
+    |   NN |   i = setjmp(env);
+    |      |       ^~~~~~
+    |      |       |
+    |      |       (1) 'setjmp' called here
+    |
+  'test_2': events 2-4
+    |
+    |   NN |   if (i != 0)
+    |      |      ^
+    |      |      |
+    |      |      (2) following 'false' branch (when 'i == 0')...
+    |......
+    |   NN |     longjmp (env, 1);
+    |      |     ~~~~~~~~~~~~~~~~
+    |      |     |
+    |      |     (3) ...to here
+    |      |     (4) rewinding within 'test_2' from 'longjmp'...
+    |
+  'test_2': event 5
+    |
+    |   NN |   i = setjmp(env);
+    |      |       ^~~~~~
+    |      |       |
+    |      |       (5) ...to 'setjmp' (saved at (1))
+    |
+  'test_2': events 6-8
+    |
+    |   NN |   if (i != 0)
+    |      |      ^
+    |      |      |
+    |      |      (6) following 'true' branch (when 'i != 0')...
+    |   NN |     {
+    |   NN |       foo (2);
+    |      |       ~~~~~~~
+    |      |       |
+    |      |       (7) ...to here
+    |   NN |       __analyzer_dump_path ();
+    |      |       ~~~~~~~~~~~~~~~~~~~~~~~
+    |      |       |
+    |      |       (8) here
+    |
+    { dg-end-multiline-output "" } */
+
+void test_3 (void)
+{
+  longjmp (NULL, 0);
+}
+
+void test_4 (void)
+{
+  longjmp (NULL, 1);
+}
+
+void test_5 (void)
+{
+  jmp_buf env;
+  longjmp (env, 1);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-3.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-3.c
new file mode 100644
index 000000000000..675892c71cee
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-3.c
@@ -0,0 +1,107 @@ 
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+#include <stddef.h>
+#include "analyzer-decls.h"
+
+extern void foo (int);
+
+static jmp_buf env;
+
+static void inner (void)
+{
+  longjmp (env, 1);
+}
+
+void outer (void)
+{
+  int i;
+
+  foo (0);
+
+  i = setjmp(env);
+
+  if (i != 0)
+    {
+      foo (2);
+      __analyzer_dump_path (); /* { dg-message "path" } */
+    }
+  else
+    {
+      foo (1);
+      inner ();
+    }
+  foo (3);
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |       __analyzer_dump_path ();
+      |       ^~~~~~~~~~~~~~~~~~~~~~~
+  'outer': event 1
+    |
+    |   NN | void outer (void)
+    |      |      ^~~~~
+    |      |      |
+    |      |      (1) entry to 'outer'
+    |
+  'outer': event 2
+    |
+    |   NN |   i = setjmp(env);
+    |      |       ^~~~~~
+    |      |       |
+    |      |       (2) 'setjmp' called here
+    |
+  'outer': events 3-5
+    |
+    |   NN |   if (i != 0)
+    |      |      ^
+    |      |      |
+    |      |      (3) following 'false' branch (when 'i == 0')...
+    |......
+    |   NN |       foo (1);
+    |      |       ~~~~~~~
+    |      |       |
+    |      |       (4) ...to here
+    |   NN |       inner ();
+    |      |       ~~~~~~~~
+    |      |       |
+    |      |       (5) calling 'inner' from 'outer'
+    |
+    +--> 'inner': events 6-7
+           |
+           |   NN | static void inner (void)
+           |      |             ^~~~~
+           |      |             |
+           |      |             (6) entry to 'inner'
+           |   NN | {
+           |   NN |   longjmp (env, 1);
+           |      |   ~~~~~~~~~~~~~~~~
+           |      |   |
+           |      |   (7) rewinding from 'longjmp' in 'inner'...
+           |
+    <------+
+    |
+  'outer': event 8
+    |
+    |   NN |   i = setjmp(env);
+    |      |       ^~~~~~
+    |      |       |
+    |      |       (8) ...to 'setjmp' in 'outer' (saved at (2))
+    |
+  'outer': events 9-11
+    |
+    |   NN |   if (i != 0)
+    |      |      ^
+    |      |      |
+    |      |      (9) following 'true' branch (when 'i != 0')...
+    |   NN |     {
+    |   NN |       foo (2);
+    |      |       ~~~~~~~
+    |      |       |
+    |      |       (10) ...to here
+    |   NN |       __analyzer_dump_path ();
+    |      |       ~~~~~~~~~~~~~~~~~~~~~~~
+    |      |       |
+    |      |       (11) here
+    |
+    { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-4.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-4.c
new file mode 100644
index 000000000000..d92977318278
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-4.c
@@ -0,0 +1,108 @@ 
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+#include "analyzer-decls.h"
+
+extern int foo (int);
+static jmp_buf buf;
+
+void inner (int x)
+{
+  foo (x);
+  longjmp (buf, 1);
+  foo (x);
+}
+
+void outer (int y)
+{
+  foo (y);
+  inner (y);
+  foo (y);
+}
+
+int main (void)
+{   
+  if (!setjmp(buf))
+    outer (42);
+  else
+    __analyzer_dump_path (); /* { dg-message "path" } */
+  return 0;
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |     __analyzer_dump_path ();
+      |     ^~~~~~~~~~~~~~~~~~~~~~~
+  'main': event 1
+    |
+    |   NN | int main (void)
+    |      |     ^~~~
+    |      |     |
+    |      |     (1) entry to 'main'
+    |
+  'main': event 2
+    |
+    |   NN |   if (!setjmp(buf))
+    |      |        ^~~~~~
+    |      |        |
+    |      |        (2) 'setjmp' called here
+    |
+  'main': events 3-5
+    |
+    |   NN |   if (!setjmp(buf))
+    |      |      ^
+    |      |      |
+    |      |      (3) following 'true' branch...
+    |   NN |     outer (42);
+    |      |     ~~~~~~~~~~
+    |      |     |
+    |      |     (4) ...to here
+    |      |     (5) calling 'outer' from 'main'
+    |
+    +--> 'outer': events 6-7
+           |
+           |   NN | void outer (int y)
+           |      |      ^~~~~
+           |      |      |
+           |      |      (6) entry to 'outer'
+           |......
+           |   NN |   inner (y);
+           |      |   ~~~~~~~~~
+           |      |   |
+           |      |   (7) calling 'inner' from 'outer'
+           |
+           +--> 'inner': events 8-9
+                  |
+                  |   NN | void inner (int x)
+                  |      |      ^~~~~
+                  |      |      |
+                  |      |      (8) entry to 'inner'
+                  |......
+                  |   NN |   longjmp (buf, 1);
+                  |      |   ~~~~~~~~~~~~~~~~
+                  |      |   |
+                  |      |   (9) rewinding from 'longjmp' in 'inner'...
+                  |
+    <-------------+
+    |
+  'main': event 10
+    |
+    |   NN |   if (!setjmp(buf))
+    |      |        ^~~~~~
+    |      |        |
+    |      |        (10) ...to 'setjmp' in 'main' (saved at (2))
+    |
+  'main': events 11-13
+    |
+    |   NN |   if (!setjmp(buf))
+    |      |      ^
+    |      |      |
+    |      |      (11) following 'false' branch...
+    |......
+    |   NN |     __analyzer_dump_path ();
+    |      |     ~~~~~~~~~~~~~~~~~~~~~~~
+    |      |     |
+    |      |     (12) ...to here
+    |      |     (13) here
+    |
+    { dg-end-multiline-output "" } */
+
diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-5.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-5.c
new file mode 100644
index 000000000000..e9d49b41a6e3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-5.c
@@ -0,0 +1,66 @@ 
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+#include <stddef.h>
+#include "analyzer-decls.h"
+
+static jmp_buf env;
+
+static void inner (void)
+{
+  setjmp (env);
+}
+
+void outer (void)
+{
+  int i;
+
+  inner ();
+
+  longjmp (env, 42); /* { dg-warning "'longjmp' called after enclosing function of 'setjmp' has returned" } */
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |   longjmp (env, 42);
+      |   ^~~~~~~~~~~~~~~~~
+  'outer': events 1-2
+    |
+    |   NN | void outer (void)
+    |      |      ^~~~~
+    |      |      |
+    |      |      (1) entry to 'outer'
+    |......
+    |   NN |   inner ();
+    |      |   ~~~~~~~~
+    |      |   |
+    |      |   (2) calling 'inner' from 'outer'
+    |
+    +--> 'inner': event 3
+           |
+           |   NN | static void inner (void)
+           |      |             ^~~~~
+           |      |             |
+           |      |             (3) entry to 'inner'
+           |
+         'inner': event 4
+           |
+           |   NN |   setjmp (env);
+           |      |   ^~~~~~
+           |      |   |
+           |      |   (4) 'setjmp' called here
+           |
+    <------+
+    |
+  'outer': events 5-6
+    |
+    |   NN |   inner ();
+    |      |   ^~~~~~~~
+    |      |   |
+    |      |   (5) returning to 'outer' from 'inner'
+    |   NN | 
+    |   NN |   longjmp (env, 42);
+    |      |   ~~~~~~~~~~~~~~~~~
+    |      |   |
+    |      |   (6) here
+    |
+    { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-6.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-6.c
new file mode 100644
index 000000000000..84a6318ed3ca
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-6.c
@@ -0,0 +1,31 @@ 
+#include <setjmp.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+extern void foo (int);
+
+static jmp_buf env;
+
+static void inner (void)
+{
+  void *ptr = malloc (1024); /* { dg-message "allocated here" }  */
+  longjmp (env, 1); /* { dg-warning "leak of 'ptr'" } */
+  free (ptr);
+}
+
+void outer (void)
+{
+  int i;
+
+  foo (0);
+
+  i = setjmp(env);
+
+  if (i == 0)
+    {
+      foo (1);
+      inner ();
+    }
+
+  foo (3);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-7.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-7.c
new file mode 100644
index 000000000000..ee4183dfb2a7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-7.c
@@ -0,0 +1,36 @@ 
+#include <setjmp.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+extern void foo (int);
+
+static jmp_buf env;
+
+static void inner (void)
+{
+  longjmp (env, 1); /* { dg-warning "leak of 'ptr'" } */
+}
+
+static void middle (void)
+{
+  void *ptr = malloc (1024); /* { dg-message "allocated here" }  */
+  inner ();
+  free (ptr);
+}
+
+void outer (void)
+{
+  int i;
+
+  foo (0);
+
+  i = setjmp(env);
+
+  if (i == 0)
+    {
+      foo (1);
+      middle ();
+    }
+
+  foo (3);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-7a.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-7a.c
new file mode 100644
index 000000000000..6f99df5126e8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-7a.c
@@ -0,0 +1,110 @@ 
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+#include <stdlib.h>
+
+extern void foo (int);
+
+static jmp_buf env;
+
+static void inner (void)
+{
+  longjmp (env, 1); /* { dg-warning "leak of 'ptr'" } */
+}
+
+static void middle (void)
+{
+  void *ptr = malloc (1024);
+  inner ();
+  free (ptr);
+}
+
+void outer (void)
+{
+  int i;
+
+  foo (0);
+
+  i = setjmp(env);
+
+  if (i == 0)
+    {
+      foo (1);
+      middle ();
+    }
+
+  foo (3);
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |   longjmp (env, 1);
+      |   ^~~~~~~~~~~~~~~~
+  'outer': event 1
+    |
+    |   NN | void outer (void)
+    |      |      ^~~~~
+    |      |      |
+    |      |      (1) entry to 'outer'
+    |
+  'outer': event 2
+    |
+    |   NN |   i = setjmp(env);
+    |      |       ^~~~~~
+    |      |       |
+    |      |       (2) 'setjmp' called here
+    |
+  'outer': events 3-5
+    |
+    |   NN |   if (i == 0)
+    |      |      ^
+    |      |      |
+    |      |      (3) following 'true' branch (when 'i == 0')...
+    |   NN |     {
+    |   NN |       foo (1);
+    |      |       ~~~~~~~
+    |      |       |
+    |      |       (4) ...to here
+    |   NN |       middle ();
+    |      |       ~~~~~~~~~
+    |      |       |
+    |      |       (5) calling 'middle' from 'outer'
+    |
+    +--> 'middle': events 6-8
+           |
+           |   NN | static void middle (void)
+           |      |             ^~~~~~
+           |      |             |
+           |      |             (6) entry to 'middle'
+           |   NN | {
+           |   NN |   void *ptr = malloc (1024);
+           |      |               ~~~~~~~~~~~~~
+           |      |               |
+           |      |               (7) allocated here
+           |   NN |   inner ();
+           |      |   ~~~~~~~~   
+           |      |   |
+           |      |   (8) calling 'inner' from 'middle'
+           |
+           +--> 'inner': events 9-11
+                  |
+                  |   NN | static void inner (void)
+                  |      |             ^~~~~
+                  |      |             |
+                  |      |             (9) entry to 'inner'
+                  |   NN | {
+                  |   NN |   longjmp (env, 1);
+                  |      |   ~~~~~~~~~~~~~~~~
+                  |      |   |
+                  |      |   (10) 'ptr' leaks here; was allocated at (7)
+                  |      |   (11) rewinding from 'longjmp' in 'inner'...
+                  |
+    <-------------+
+    |
+  'outer': event 12
+    |
+    |   NN |   i = setjmp(env);
+    |      |       ^~~~~~
+    |      |       |
+    |      |       (12) ...to 'setjmp' in 'outer' (saved at (2))
+    |
+    { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-8.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-8.c
new file mode 100644
index 000000000000..4b2d3e432b67
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-8.c
@@ -0,0 +1,108 @@ 
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+#include <stddef.h>
+#include "analyzer-decls.h"
+
+extern void foo (int);
+
+static jmp_buf env;
+
+static void inner (void)
+{
+  /* Pass known 0 to longjmp. */
+  longjmp (env, 0);
+}
+
+void outer (void)
+{
+  int i;
+
+  foo (0);
+
+  i = setjmp(env);
+
+  if (i != 0)
+    {
+      foo (2);
+      __analyzer_dump_path (); /* { dg-message "path" } */
+    }
+  else
+    {
+      foo (1);
+      inner ();
+    }
+  foo (3);
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |       __analyzer_dump_path ();
+      |       ^~~~~~~~~~~~~~~~~~~~~~~
+  'outer': event 1
+    |
+    |   NN | void outer (void)
+    |      |      ^~~~~
+    |      |      |
+    |      |      (1) entry to 'outer'
+    |
+  'outer': event 2
+    |
+    |   NN |   i = setjmp(env);
+    |      |       ^~~~~~
+    |      |       |
+    |      |       (2) 'setjmp' called here
+    |
+  'outer': events 3-5
+    |
+    |   NN |   if (i != 0)
+    |      |      ^
+    |      |      |
+    |      |      (3) following 'false' branch (when 'i == 0')...
+    |......
+    |   NN |       foo (1);
+    |      |       ~~~~~~~
+    |      |       |
+    |      |       (4) ...to here
+    |   NN |       inner ();
+    |      |       ~~~~~~~~
+    |      |       |
+    |      |       (5) calling 'inner' from 'outer'
+    |
+    +--> 'inner': events 6-7
+           |
+           |   NN | static void inner (void)
+           |      |             ^~~~~
+           |      |             |
+           |      |             (6) entry to 'inner'
+           |......
+           |   NN |   longjmp (env, 0);
+           |      |   ~~~~~~~~~~~~~~~~
+           |      |   |
+           |      |   (7) rewinding from 'longjmp' in 'inner'...
+           |
+    <------+
+    |
+  'outer': event 8
+    |
+    |   NN |   i = setjmp(env);
+    |      |       ^~~~~~
+    |      |       |
+    |      |       (8) ...to 'setjmp' in 'outer' (saved at (2))
+    |
+  'outer': events 9-11
+    |
+    |   NN |   if (i != 0)
+    |      |      ^
+    |      |      |
+    |      |      (9) following 'true' branch (when 'i != 0')...
+    |   NN |     {
+    |   NN |       foo (2);
+    |      |       ~~~~~~~
+    |      |       |
+    |      |       (10) ...to here
+    |   NN |       __analyzer_dump_path ();
+    |      |       ~~~~~~~~~~~~~~~~~~~~~~~
+    |      |       |
+    |      |       (11) here
+    |
+    { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-9.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-9.c
new file mode 100644
index 000000000000..d9af85d25156
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-9.c
@@ -0,0 +1,110 @@ 
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+#include <stddef.h>
+#include "analyzer-decls.h"
+
+extern void foo (int);
+
+static jmp_buf env;
+
+extern int unknown_val;
+
+static void inner (void)
+{
+  /* Pass value that might be 0 to longjmp. */
+  longjmp (env, unknown_val);
+}
+
+void outer (void)
+{
+  int i;
+
+  foo (0);
+
+  i = setjmp(env);
+
+  if (i != 0)
+    {
+      foo (2);
+      __analyzer_dump_path (); /* { dg-message "path" } */
+    }
+  else
+    {
+      foo (1);
+      inner ();
+    }
+  foo (3);
+}
+
+/* { dg-begin-multiline-output "" }
+   NN |       __analyzer_dump_path ();
+      |       ^~~~~~~~~~~~~~~~~~~~~~~
+  'outer': event 1
+    |
+    |   NN | void outer (void)
+    |      |      ^~~~~
+    |      |      |
+    |      |      (1) entry to 'outer'
+    |
+  'outer': event 2
+    |
+    |   NN |   i = setjmp(env);
+    |      |       ^~~~~~
+    |      |       |
+    |      |       (2) 'setjmp' called here
+    |
+  'outer': events 3-5
+    |
+    |   NN |   if (i != 0)
+    |      |      ^
+    |      |      |
+    |      |      (3) following 'false' branch (when 'i == 0')...
+    |......
+    |   NN |       foo (1);
+    |      |       ~~~~~~~
+    |      |       |
+    |      |       (4) ...to here
+    |   NN |       inner ();
+    |      |       ~~~~~~~~
+    |      |       |
+    |      |       (5) calling 'inner' from 'outer'
+    |
+    +--> 'inner': events 6-7
+           |
+           |   NN | static void inner (void)
+           |      |             ^~~~~
+           |      |             |
+           |      |             (6) entry to 'inner'
+           |......
+           |   NN |   longjmp (env, unknown_val);
+           |      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~
+           |      |   |
+           |      |   (7) rewinding from 'longjmp' in 'inner'...
+           |
+    <------+
+    |
+  'outer': event 8
+    |
+    |   NN |   i = setjmp(env);
+    |      |       ^~~~~~
+    |      |       |
+    |      |       (8) ...to 'setjmp' in 'outer' (saved at (2))
+    |
+  'outer': events 9-11
+    |
+    |   NN |   if (i != 0)
+    |      |      ^
+    |      |      |
+    |      |      (9) following 'true' branch (when 'i != 0')...
+    |   NN |     {
+    |   NN |       foo (2);
+    |      |       ~~~~~~~
+    |      |       |
+    |      |       (10) ...to here
+    |   NN |       __analyzer_dump_path ();
+    |      |       ~~~~~~~~~~~~~~~~~~~~~~~
+    |      |       |
+    |      |       (11) here
+    |
+    { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/signal-1.c b/gcc/testsuite/gcc.dg/analyzer/signal-1.c
new file mode 100644
index 000000000000..4dcbcc0fc6bd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/signal-1.c
@@ -0,0 +1,31 @@ 
+/* Example of a bad call within a signal handler.
+   'handler' calls 'custom_logger' which calls 'fprintf', and 'fprintf' is
+   not allowed from a signal handler.  */
+
+#include <stdio.h>
+#include <signal.h>
+
+extern void body_of_program(void);
+
+void custom_logger(const char *msg)
+{
+  fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */
+}
+
+static void handler(int signum)
+{
+  custom_logger("got signal");
+}
+
+int main(int argc, const char *argv)
+{
+  custom_logger("started");
+
+  signal(SIGINT, handler); /* { dg-message "registering 'handler' as signal handler" } */
+
+  body_of_program();
+
+  custom_logger("stopped");
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/signal-2.c b/gcc/testsuite/gcc.dg/analyzer/signal-2.c
new file mode 100644
index 000000000000..a56acb060ec8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/signal-2.c
@@ -0,0 +1,34 @@ 
+/* Example of a bad call within a signal handler.
+   'handler' calls 'custom_logger' which calls 'fprintf', and 'fprintf' is
+   not allowed from a signal handler.  */
+
+#include <stdio.h>
+#include <signal.h>
+
+extern void body_of_program(void);
+
+int logging = 1;
+
+void custom_logger(const char *msg)
+{
+  if (logging)
+    fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */
+}
+
+static void handler(int signum)
+{
+  custom_logger("got signal");
+}
+
+int main(int argc, const char *argv)
+{
+  custom_logger("started");
+
+  signal(SIGINT, handler); /* { dg-message "registering 'handler' as signal handler" } */
+
+  body_of_program();
+
+  custom_logger("stopped");
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/signal-3.c b/gcc/testsuite/gcc.dg/analyzer/signal-3.c
new file mode 100644
index 000000000000..5b3088887771
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/signal-3.c
@@ -0,0 +1,23 @@ 
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+
+extern void body_of_program(void);
+
+void custom_logger(const char *msg)
+{
+  fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */
+}
+
+static void handler(int signum)
+{
+  custom_logger("got signal");
+}
+
+void test (void)
+{
+  void *ptr = malloc (1024);
+  signal(SIGINT, handler); /* { dg-message "registering 'handler' as signal handler" } */
+  body_of_program();
+  free (ptr);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/signal-4a.c b/gcc/testsuite/gcc.dg/analyzer/signal-4a.c
new file mode 100644
index 000000000000..19e6b83b032a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/signal-4a.c
@@ -0,0 +1,74 @@ 
+/* Verify how paths are printed for signal-handler diagnostics.  */
+
+/* { dg-options "-fanalyzer -fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+
+extern void body_of_program(void);
+
+void custom_logger(const char *msg)
+{
+  fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */
+}
+
+static void int_handler(int signum)
+{
+  custom_logger("got signal");
+}
+
+void test (void)
+{
+  void *ptr = malloc (1024);
+  signal(SIGINT, int_handler);
+  body_of_program();
+  free (ptr);
+}
+
+/* "call to 'fprintf' from within signal handler [CWE-479]".  */
+/* { dg-begin-multiline-output "" }
+   NN |   fprintf(stderr, "LOG: %s", msg);
+      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  'test': events 1-2
+    |
+    |   NN | void test (void)
+    |      |      ^~~~
+    |      |      |
+    |      |      (1) entry to 'test'
+    |......
+    |   NN |   signal(SIGINT, int_handler);
+    |      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    |      |   |
+    |      |   (2) registering 'int_handler' as signal handler
+    |
+  event 3
+    |
+    |cc1:
+    | (3): later on, when the signal is delivered to the process
+    |
+    +--> 'int_handler': events 4-5
+           |
+           |   NN | static void int_handler(int signum)
+           |      |             ^~~~~~~~~~~
+           |      |             |
+           |      |             (4) entry to 'int_handler'
+           |   NN | {
+           |   NN |   custom_logger("got signal");
+           |      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+           |      |   |
+           |      |   (5) calling 'custom_logger' from 'int_handler'
+           |
+           +--> 'custom_logger': events 6-7
+                  |
+                  |   NN | void custom_logger(const char *msg)
+                  |      |      ^~~~~~~~~~~~~
+                  |      |      |
+                  |      |      (6) entry to 'custom_logger'
+                  |   NN | {
+                  |   NN |   fprintf(stderr, "LOG: %s", msg);
+                  |      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                  |      |   |
+                  |      |   (7) call to 'fprintf' from within signal handler
+                  |
+  { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/signal-4b.c b/gcc/testsuite/gcc.dg/analyzer/signal-4b.c
new file mode 100644
index 000000000000..764af10b1b64
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/signal-4b.c
@@ -0,0 +1,89 @@ 
+/* Verify how paths are printed for signal-handler diagnostics.  */
+
+/* { dg-options "-fanalyzer -fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+
+extern void body_of_program(void);
+
+void custom_logger(const char *msg)
+{
+  fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */
+}
+
+static void int_handler(int signum)
+{
+  custom_logger("got signal");
+}
+
+static void register_handler ()
+{
+  signal(SIGINT, int_handler);
+}
+
+void test (void)
+{
+  register_handler ();
+  body_of_program();
+}
+
+/* "call to 'fprintf' from within signal handler [CWE-479]".  */
+/* { dg-begin-multiline-output "" }
+   NN |   fprintf(stderr, "LOG: %s", msg);
+      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  'test': events 1-2
+    |
+    |   NN | void test (void)
+    |      |      ^~~~
+    |      |      |
+    |      |      (1) entry to 'test'
+    |   NN | {
+    |   NN |   register_handler ();
+    |      |   ~~~~~~~~~~~~~~~~~~~
+    |      |   |
+    |      |   (2) calling 'register_handler' from 'test'
+    |
+    +--> 'register_handler': events 3-4
+           |
+           |   NN | static void register_handler ()
+           |      |             ^~~~~~~~~~~~~~~~
+           |      |             |
+           |      |             (3) entry to 'register_handler'
+           |   NN | {
+           |   NN |   signal(SIGINT, int_handler);
+           |      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+           |      |   |
+           |      |   (4) registering 'int_handler' as signal handler
+           |
+  event 5
+    |
+    |cc1:
+    | (5): later on, when the signal is delivered to the process
+    |
+    +--> 'int_handler': events 6-7
+           |
+           |   NN | static void int_handler(int signum)
+           |      |             ^~~~~~~~~~~
+           |      |             |
+           |      |             (6) entry to 'int_handler'
+           |   NN | {
+           |   NN |   custom_logger("got signal");
+           |      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+           |      |   |
+           |      |   (7) calling 'custom_logger' from 'int_handler'
+           |
+           +--> 'custom_logger': events 8-9
+                  |
+                  |   NN | void custom_logger(const char *msg)
+                  |      |      ^~~~~~~~~~~~~
+                  |      |      |
+                  |      |      (8) entry to 'custom_logger'
+                  |   NN | {
+                  |   NN |   fprintf(stderr, "LOG: %s", msg);
+                  |      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                  |      |   |
+                  |      |   (9) call to 'fprintf' from within signal handler
+                  |
+  { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/strcmp-1.c b/gcc/testsuite/gcc.dg/analyzer/strcmp-1.c
new file mode 100644
index 000000000000..cc50a6fdba59
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/strcmp-1.c
@@ -0,0 +1,35 @@ 
+/* Verify that we clarify the sense of paths involving strcmp.  */
+
+#include <string.h>
+#include <stdlib.h>
+
+int test_1 (const char *str, char *ptr)
+{
+  if (strcmp (str, "VALUE")) /* { dg-message "following 'true' branch \\(when the strings are non-equal\\)\\.\\.\\." } */
+    free (ptr);
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+int test_2 (const char *str, char *ptr)
+{
+  if (strcmp (str, "VALUE") == 0) /* { dg-message "following 'true' branch \\(when the strings are equal\\)\\.\\.\\." } */
+    free (ptr);
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+int test_3 (const char *str, char *ptr)
+{
+  if (!strcmp (str, "VALUE")) /* { dg-message "following 'true' branch \\(when the strings are equal\\)\\.\\.\\." } */
+    free (ptr);
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+int test_4 (const char *str, char *ptr)
+{
+  if (strcmp (str, "VALUE")) /* { dg-message "following 'false' branch \\(when the strings are equal\\)\\.\\.\\." } */
+    {
+    }
+  else
+    free (ptr);
+  free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/switch.c b/gcc/testsuite/gcc.dg/analyzer/switch.c
new file mode 100644
index 000000000000..ad4b6568dc2a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/switch.c
@@ -0,0 +1,30 @@ 
+/* { dg-additional-options "-fanalyzer-transitivity" } */
+
+#include "analyzer-decls.h"
+
+void test (int i)
+{
+  switch (i)
+    {
+    case 0:
+      __analyzer_eval (i == 0); /* { dg-warning "TRUE" } */
+      break;
+
+    case 3 ... 5:
+      __analyzer_eval (i >= 3); /* { dg-warning "TRUE" } */
+      __analyzer_eval (i <= 5); /* { dg-warning "TRUE" } */
+      break;
+
+    default:
+      __analyzer_eval (i == 0); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i == 2); /* { dg-warning "UNKNOWN" } */
+      __analyzer_eval (i == 3); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i == 4); /* { dg-warning "FALSE" "" { xfail *-*-* } } */
+      /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+      /* TODO(xfail^^^): we're only checking against endpoints of case
+	 ranges, not the insides.  */
+      __analyzer_eval (i == 5); /* { dg-warning "FALSE" } */
+      __analyzer_eval (i == 6); /* { dg-warning "UNKNOWN" } */
+      break;
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-1.c b/gcc/testsuite/gcc.dg/analyzer/taint-1.c
new file mode 100644
index 000000000000..cea5440d14c4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/taint-1.c
@@ -0,0 +1,128 @@ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct foo
+{
+  signed int i;
+  char buf[256];
+};
+
+char test_1(FILE *f)
+{
+  struct foo tmp;
+
+  if (1 == fread(&tmp, sizeof(tmp), 1, f)) { /* { dg-message "\\(1\\) 'tmp' gets an unchecked value here" } */
+                                             /* { dg-message "\\(2\\) following 'true' branch\\.\\.\\." "" { target *-*-* } .-1 } */
+    /* BUG: the following array lookup trusts that the input data's index is
+       in the range 0 <= i < 256; otherwise it's accessing the stack */
+    return tmp.buf[tmp.i]; // { dg-warning "use of tainted value 'tmp.i' in array lookup without bounds checking" } */
+    /* { dg-message "23: \\(3\\) \\.\\.\\.to here" "" { target *-*-* } .-1 } */
+    /* { dg-message "23: \\(4\\) 'tmp.i' has an unchecked value here \\(from 'tmp'\\)" "" { target *-*-* } .-2 } */
+    /* { dg-message "\\(5\\) use of tainted value 'tmp.i' in array lookup without bounds checking" "" { target *-*-* } .-3 } */
+    
+    // TOOD: better messages for state changes
+  }
+  return 0;
+}
+
+char test_2(struct foo *f, int i)
+{
+  /* not a bug: the data is not known to be tainted: */
+  return f->buf[f->i];
+}
+
+char test_3(FILE *f)
+{
+  struct foo tmp;
+
+  if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+    if (tmp.i >= 0 && tmp.i < 256) {
+      /* not a bug: the access is guarded by upper and lower bounds: */
+      return tmp.buf[tmp.i];
+    }
+  }
+  return 0;
+}
+
+char test_4(FILE *f)
+{
+  struct foo tmp;
+
+  if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+    if (tmp.i >= 0) { /* { dg-message "'tmp.i' has an unchecked value here \\(from 'tmp'\\)" } */
+      /* { dg-message "'tmp.i' has its lower bound checked here" "" { target *-*-* } .-1 } */
+      return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without upper-bounds checking" } */
+    }
+  }
+  return 0;
+}
+
+char test_5(FILE *f)
+{
+  struct foo tmp;
+
+  if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+    if (tmp.i < 256) { /* { dg-message "'tmp.i' has an unchecked value here \\(from 'tmp'\\)" } */
+      /* { dg-message "'tmp.i' has its upper bound checked here" "" { target *-*-* } .-1 } */
+      return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without lower-bounds checking" } */
+    }
+  }
+  return 0;
+}
+
+/* unsigned types have a natural lower bound of 0 */
+struct bar
+{
+  unsigned int i;
+  char buf[256];
+};
+
+char test_6(FILE *f)
+{
+  struct bar tmp;
+
+  if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+    return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without upper-bounds checking" } */
+  }
+  return 0;
+}
+
+char test_7(FILE *f)
+{
+  struct bar tmp;
+
+  if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+    if (tmp.i >= 0) {
+      return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without upper-bounds checking" } */
+    }
+  }
+  return 0;
+}
+
+char test_8(FILE *f)
+{
+  struct bar tmp;
+
+  if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+    if (tmp.i < 256) {
+      /* not a bug: has an upper bound, and an implicit lower bound: */
+      return tmp.buf[tmp.i];
+    }
+  }
+  return 0;
+}
+
+char test_9(FILE *f)
+{
+  struct foo tmp;
+
+  if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+    if (tmp.i == 42) {
+      /* not a bug: tmp.i compared against a specific value: */
+      return tmp.buf[tmp.i]; /* { dg-bogus "tainted" "" { xfail *-*-* } } */
+      // TODO: xfail
+    }
+  }
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-1.c b/gcc/testsuite/gcc.dg/analyzer/zlib-1.c
new file mode 100644
index 000000000000..5537c984e1aa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/zlib-1.c
@@ -0,0 +1,69 @@ 
+#include "analyzer-decls.h"
+
+typedef void (*free_func)(void *opaque, void *address);
+
+typedef struct z_stream_s {
+  struct internal_state *state;
+  free_func zfree;
+  void *opaque;
+} z_stream;
+
+struct internal_state {
+  z_stream *strm;
+  int status;
+  unsigned char *pending_buf;
+  unsigned char *window;
+  unsigned short *prev;
+  unsigned short *head;
+};
+
+int deflateEnd(z_stream *strm)
+{
+  int status;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  if (strm == 0 || strm->state == 0)
+    return (-2);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  status = strm->state->status;
+  if (status != 42 && status != 113 && status != 666) {
+    return (-2);
+  }
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  if (strm->state->pending_buf)
+    (*(strm->zfree))(strm->opaque, (void *)(strm->state->pending_buf));
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  if (strm->state->head)
+      (*(strm->zfree))(strm->opaque, (void *)(strm->state->head));
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  if (strm->state->prev)
+    (*(strm->zfree))(strm->opaque, (void *)(strm->state->prev));
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  if (strm->state->window)
+    (*(strm->zfree))(strm->opaque, (void *)(strm->state->window));
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  (*(strm->zfree))(strm->opaque, (void *)(strm->state));
+  strm->state = 0;
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  return status == 113 ? (-3) : 0;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-2.c b/gcc/testsuite/gcc.dg/analyzer/zlib-2.c
new file mode 100644
index 000000000000..d0b587cbbbea
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/zlib-2.c
@@ -0,0 +1,51 @@ 
+/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+
+typedef void * (*alloc_func)(void * opaque, unsigned items, unsigned size);
+typedef void (*free_func)(void * opaque, void * address);
+
+typedef struct z_stream_s {
+  char *msg;
+  alloc_func zalloc;
+  free_func zfree;
+  void * opaque;
+} z_stream;
+
+void * zcalloc(void * opaque, unsigned items, unsigned size);
+void zcfree(void * opaque, void * ptr);
+
+int deflateInit2_(z_stream *strm, int level, int method, int windowBits,
+                  int memLevel, int strategy, const char *version,
+                  int stream_size) {
+  int noheader = 0;
+  static const char *my_version = "1.1.3";
+
+  if (version == 0 || version[0] != my_version[0] ||
+      stream_size != sizeof(z_stream)) {
+    return (-6);
+  }
+  if (strm == 0)
+    return (-2);
+
+  strm->msg = 0;
+  if (strm->zalloc == 0) {
+    strm->zalloc = zcalloc;
+    strm->opaque = (void *)0;
+  }
+  if (strm->zfree == 0)
+    strm->zfree = zcfree;
+
+  if (level == (-1))
+    level = 6;
+
+  if (windowBits < 0) {
+    noheader = 1;
+    windowBits = -windowBits;
+  }
+  if (memLevel < 1 || memLevel > 9 || method != 8 || windowBits < 8 ||
+      windowBits > 15 || level < 0 || level > 9 || strategy < 0 ||
+      strategy > 2) {
+    return (-2);
+  }
+  (*((strm)->zalloc))((strm)->opaque, (1), 112);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-3.c b/gcc/testsuite/gcc.dg/analyzer/zlib-3.c
new file mode 100644
index 000000000000..5faada16f38f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/zlib-3.c
@@ -0,0 +1,214 @@ 
+/* { dg-additional-options "-O3 -Wno-analyzer-too-complex" } */
+/* TODO: reduce this; was triggering this assert:
+            gcc_assert (pruned_state != existing_state);
+*/
+
+typedef unsigned char Byte;
+typedef unsigned int uInt;
+
+typedef void *voidp;
+
+typedef voidp (*alloc_func)(voidp opaque, uInt items, uInt size);
+
+typedef struct z_stream_s {
+  alloc_func zalloc;
+  voidp opaque;
+} z_stream;
+
+typedef z_stream *z_streamp;
+
+typedef struct inflate_huft_s inflate_huft;
+
+struct inflate_huft_s {
+  struct {
+    Byte Exop;
+    Byte Bits;
+  } what;
+  uInt base;
+};
+
+static int huft_build(uInt *, uInt, uInt, const uInt *, const uInt *,
+                      inflate_huft **, uInt *, inflate_huft *, uInt *, uInt *);
+
+static int huft_build(uInt *b, uInt n, uInt s, const uInt *d, const uInt *e,
+                      inflate_huft **t, uInt *m, inflate_huft *hp, uInt *hn,
+                      uInt *v) {
+
+  uInt a;
+  uInt c[15 + 1];
+  uInt f;
+  int g;
+  int h;
+  register uInt i;
+  register uInt j;
+  register int k;
+  int l;
+  uInt mask;
+  register uInt *p;
+  inflate_huft *q;
+  struct inflate_huft_s r;
+  inflate_huft *u[15];
+  register int w;
+  uInt x[15 + 1];
+  uInt *xp;
+  int y;
+  uInt z;
+
+  p = c;
+
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  *p++ = 0;
+  p = b;
+  i = n;
+  do {
+    c[*p++]++;
+  } while (--i);
+  if (c[0] == n) {
+    *t = (inflate_huft *)0;
+    *m = 0;
+    return 0;
+  }
+
+  l = *m;
+  for (j = 1; j <= 15; j++)
+    if (c[j])
+      break;
+  k = j;
+  if ((uInt)l < j)
+    l = j;
+  for (i = 15; i; i--)
+    if (c[i])
+      break;
+  g = i;
+  if ((uInt)l > i)
+    l = i;
+  *m = l;
+
+  for (y = 1 << j; j < i; j++, y <<= 1)
+    if ((y -= c[j]) < 0)
+      return (-3);
+  if ((y -= c[i]) < 0)
+    return (-3);
+  c[i] += y;
+
+  x[1] = j = 0;
+  p = c + 1;
+  xp = x + 2;
+  while (--i) {
+    *xp++ = (j += *p++);
+  }
+
+  p = b;
+  i = 0;
+  do {
+    if ((j = *p++) != 0)
+      v[x[j]++] = i;
+  } while (++i < n);
+  n = x[g];
+
+  x[0] = i = 0;
+  p = v;
+  h = -1;
+  w = -l;
+  u[0] = (inflate_huft *)0;
+  q = (inflate_huft *)0;
+  z = 0;
+
+  for (; k <= g; k++) {
+    a = c[k];
+    while (a--) {
+
+      while (k > w + l) {
+        h++;
+        w += l;
+
+        z = g - w;
+        z = z > (uInt)l ? l : z;
+        if ((f = 1 << (j = k - w)) > a + 1) {
+          f -= a + 1;
+          xp = c + k;
+          if (j < z)
+            while (++j < z) {
+              if ((f <<= 1) <= *++xp)
+                break;
+              f -= *xp;
+            }
+        }
+        z = 1 << j;
+
+        if (*hn + z > 1440)
+          return (-4);
+        u[h] = q = hp + *hn;
+        *hn += z;
+
+        if (h) {
+          x[h] = i;
+          r.what.Bits = (Byte)l;
+          r.what.Exop = (Byte)j;
+          j = i >> (w - l);
+          r.base = (uInt)(q - u[h - 1] - j);
+          u[h - 1][j] = r;
+        } else
+          *t = q;
+      }
+
+      r.what.Bits = (Byte)(k - w);
+      if (p >= v + n)
+        r.what.Exop = 128 + 64;
+      else if (*p < s) {
+        r.what.Exop = (Byte)(*p < 256 ? 0 : 32 + 64);
+        r.base = *p++;
+      } else {
+        r.what.Exop = (Byte)(e[*p - s] + 16 + 64);
+        r.base = d[*p++ - s];
+      }
+
+      f = 1 << (k - w);
+      for (j = i >> w; j < z; j += f)
+        q[j] = r;
+
+      mask = (1 << w) - 1;
+      while ((i & mask) != x[h]) {
+        h--;
+        w -= l;
+        mask = (1 << w) - 1;
+      }
+    }
+  }
+
+  return y != 0 && g != 1 ? (-5) : 0;
+}
+
+extern const uInt cplens[31];
+extern const uInt cplext[31];
+extern const uInt cpdist[30];
+extern const uInt cpdext[30];
+
+int inflate_trees_dynamic(uInt nl, uInt nd, uInt *c, uInt *bl, uInt *bd,
+                          inflate_huft **tl, inflate_huft **td,
+                          inflate_huft *hp, z_streamp z) {
+  int r;
+  uInt hn = 0;
+  uInt *v;
+
+  if ((v = (uInt *)(*((z)->zalloc))((z)->opaque, (288), (sizeof(uInt)))) == 0)
+    return (-4);
+
+  r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v);
+  r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-4.c b/gcc/testsuite/gcc.dg/analyzer/zlib-4.c
new file mode 100644
index 000000000000..08261442128f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/zlib-4.c
@@ -0,0 +1,20 @@ 
+#include <stdlib.h>
+#include <string.h>
+
+typedef unsigned char Byte;
+typedef unsigned int uInt;
+typedef unsigned long uLong;
+
+#define Z_NULL  0
+
+void test ()
+{
+    uLong comprLen = 10000*sizeof(int);
+    uLong uncomprLen = comprLen;
+    Byte *compr    = (Byte*)calloc((uInt)comprLen, 1);
+    Byte *uncompr  = (Byte*)calloc((uInt)uncomprLen, 1);
+    if (compr == Z_NULL || uncompr == Z_NULL)
+      exit (1);
+    strcpy((char*)uncompr, "garbage");
+    exit (0);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-5.c b/gcc/testsuite/gcc.dg/analyzer/zlib-5.c
new file mode 100644
index 000000000000..715604dbe774
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/zlib-5.c
@@ -0,0 +1,51 @@ 
+/* { dg-additional-options "-O3" } */
+
+#include "analyzer-decls.h"
+
+typedef long unsigned int size_t;
+typedef unsigned char Byte;
+typedef unsigned int uInt;
+typedef unsigned long uLong;
+
+extern size_t strlen(const char *__s) __attribute__((__nothrow__, __leaf__))
+    __attribute__((__pure__)) __attribute__((__nonnull__(1)));
+extern void exit(int __status) __attribute__((__nothrow__, __leaf__))
+    __attribute__((__noreturn__));
+extern char *strcpy(char *__restrict __dest, const char *__restrict __src)
+    __attribute__((__nothrow__, __leaf__)) __attribute__((__nonnull__(1, 2)));
+extern void *calloc(size_t __nmemb, size_t __size)
+    __attribute__((__nothrow__, __leaf__)) __attribute__((__malloc__));
+
+extern int compress(Byte *dest, uLong *destLen, const Byte *source,
+                    uLong sourceLen);
+
+const char hello[] = "hello, hello!";
+
+void test_compress(Byte *compr, uLong comprLen, Byte *uncompr,
+                   uLong uncomprLen) {
+  int err;
+  uLong len = strlen(hello) + 1;
+
+  err = compress(compr, &comprLen, (const Byte *)hello, len);
+  if (err != 0)
+    exit(1);
+  strcpy((char *)uncompr, "garbage"); /* { dg-bogus "NULL" } */
+}
+
+int main(int argc, char *argv[]) {
+  Byte *compr, *uncompr;
+  uLong comprLen = 10000 * sizeof(int);
+  uLong uncomprLen = comprLen;
+
+  compr = (Byte *)calloc((uInt)comprLen, 1);
+  uncompr = (Byte *)calloc((uInt)uncomprLen, 1);
+  if (compr == 0 || uncompr == 0)
+    exit(1);
+
+  __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+  test_compress(compr, comprLen, uncompr, uncomprLen);
+
+  exit(0);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-6.c b/gcc/testsuite/gcc.dg/analyzer/zlib-6.c
new file mode 100644
index 000000000000..d68c387fa534
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/zlib-6.c
@@ -0,0 +1,47 @@ 
+typedef unsigned char Byte;
+typedef unsigned int uInt;
+typedef unsigned long uLong;
+
+typedef struct z_stream_s {
+  Byte *next_in;
+  uInt avail_in;
+  uLong total_in;
+} z_stream;
+
+typedef struct inflate_blocks_state {
+  uInt bitk;
+  uLong bitb;
+  Byte *write;
+} inflate_blocks_statef;
+
+extern int inflate_flush(inflate_blocks_statef *, z_stream *, int);
+
+int inflate_blocks(inflate_blocks_statef *s, z_stream *z, int r) {
+  uInt t;
+  uLong b;
+  uInt k;
+  Byte *p;
+  uInt n;
+  Byte *q;
+  uInt m;
+
+  while (k < (3)) {
+    {
+      if (n)
+        r = 0;
+      else {
+        {
+	  s->bitb = b;
+	  s->bitk = k;
+	  z->avail_in = n;
+	  z->total_in += p - z->next_in;
+	  z->next_in = p;
+          s->write = q;
+        }
+        return inflate_flush(s, z, r);
+      }
+    };
+    b |= ((uLong)(n--, *p++)) << k; /* { dg-warning "use of uninitialized value" } */
+    k += 8;
+  }
+}
diff --git a/gcc/testsuite/lib/gcc-defs.exp b/gcc/testsuite/lib/gcc-defs.exp
index da0d1bc868fd..180a9ab3a525 100644
--- a/gcc/testsuite/lib/gcc-defs.exp
+++ b/gcc/testsuite/lib/gcc-defs.exp
@@ -418,3 +418,24 @@  proc handle-dg-regexps { text } {
 
     return $text
 }
+
+# Verify that the initial arg is a valid .dot file
+# (by running dot -Tpng on it, and verifying the exit code is 0).
+
+proc dg-check-dot { args } {
+    verbose "dg-check-dot: args: $args" 2
+
+    set testcase [testname-for-summary]
+
+    set dotfile [lindex $args 0]
+    verbose "  dotfile: $dotfile" 2
+
+    set status [remote_exec host "dot" "-O -Tpng $dotfile"]
+    verbose "  status: $status" 2
+    if { [lindex $status 0] != 0 } {
+	fail "$testcase dg-check-dot $dotfile"
+	return 0
+    }
+
+    pass "$testcase dg-check-dot $dotfile"
+}
diff --git a/gcc/testsuite/lib/target-supports-dg.exp b/gcc/testsuite/lib/target-supports-dg.exp
index aecae8e071af..2a21424b8905 100644
--- a/gcc/testsuite/lib/target-supports-dg.exp
+++ b/gcc/testsuite/lib/target-supports-dg.exp
@@ -180,6 +180,16 @@  proc dg-require-iconv { args } {
     }
 }
 
+# If this host does not have "dot", skip this test.
+
+proc dg-require-dot { args } {
+    verbose "dg-require-dot" 2
+    if { ![ check_dot_available ] } {
+	upvar dg-do-what dg-do-what
+        set dg-do-what [list [lindex ${dg-do-what} 0] "N" "P"]
+    }
+}
+
 # If this target does not have sufficient stack size, skip this test.
 
 proc dg-require-stack-size { args } {
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index b6e9763ad005..62a745a7c7e8 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -514,6 +514,19 @@  proc check_gc_sections_available { } {
     }]
 }
 
+# Returns 1 if "dot" is supported on the host.
+
+proc check_dot_available { } {
+    verbose "check_dot_available" 2
+
+    set status [remote_exec host "dot" "-V"]
+    verbose "  status: $status" 2
+    if { [lindex $status 0] != 0 } {
+	return 0
+    }
+    return 1
+}
+
 # Return 1 if according to target_info struct and explicit target list
 # target is supposed to support trampolines.
  
@@ -8586,6 +8599,14 @@  proc check_effective_target_lto_incremental { } {
     } "-flto -r -nostdlib"]
 }
 
+# Return 1 if the compiler has been configured with analyzer support.
+
+proc check_effective_target_analyzer { } {
+    return [check_no_compiler_messages analyzer object {
+	void foo (void) { }
+    } "-fanalyzer"]
+}
+
 # Return 1 if -mx32 -maddress-mode=short can compile, 0 otherwise.
 
 proc check_effective_target_maybe_x32 { } {