[cogl/wip/sparse: 2/5] import sparse 0.4.4 snapshot



commit 8f15279ffd4340b94510ed1c3ffd4e1790283a7c
Author: Robert Bragg <robert linux intel com>
Date:   Mon Apr 9 19:55:21 2012 +0100

    import sparse 0.4.4 snapshot
    
    This imports a snapshot of sparse 0.4.4 under deps/sparse. The intention
    is that we will make some minor modifications to sparse to enable us to
    dump gtk-doc annotions and generate markup for function prototypes and
    structures in the Cogl public api to aid with api documentation and
    writing language bindings.

 deps/sparse/Documentation/data-structures.txt      |   54 +
 deps/sparse/Documentation/test-suite               |  112 +
 deps/sparse/FAQ                                    |   98 +
 deps/sparse/LICENSE                                |  198 ++
 deps/sparse/allocate.c                             |  129 +
 deps/sparse/allocate.h                             |   81 +
 deps/sparse/ast-inspect.c                          |  222 ++
 deps/sparse/ast-inspect.h                          |   17 +
 deps/sparse/ast-model.c                            |  468 +++
 deps/sparse/ast-model.h                            |   90 +
 deps/sparse/ast-view.c                             |   48 +
 deps/sparse/ast-view.h                             |    7 +
 deps/sparse/bitmap.h                               |   51 +
 deps/sparse/c2xml.c                                |  317 ++
 deps/sparse/cgcc                                   |  287 ++
 deps/sparse/cgcc.1                                 |   37 +
 deps/sparse/compat-bsd.c                           |   20 +
 deps/sparse/compat-cygwin.c                        |   40 +
 deps/sparse/compat-linux.c                         |    7 +
 deps/sparse/compat-mingw.c                         |   37 +
 deps/sparse/compat-solaris.c                       |   33 +
 deps/sparse/compat.h                               |   28 +
 deps/sparse/compat/mmap-blob.c                     |   37 +
 deps/sparse/compat/strtold.c                       |    6 +
 deps/sparse/compile-i386.c                         | 2391 ++++++++++++++
 deps/sparse/compile.c                              |   68 +
 deps/sparse/compile.h                              |   10 +
 deps/sparse/cse.c                                  |  400 +++
 deps/sparse/ctags.c                                |  211 ++
 deps/sparse/dissect.c                              |  578 ++++
 deps/sparse/dissect.h                              |   40 +
 deps/sparse/evaluate.c                             | 3390 ++++++++++++++++++++
 deps/sparse/example.c                              | 1955 +++++++++++
 deps/sparse/expand.c                               | 1247 +++++++
 deps/sparse/expression.c                           |  951 ++++++
 deps/sparse/expression.h                           |  222 ++
 deps/sparse/flow.c                                 | 1006 ++++++
 deps/sparse/flow.h                                 |   42 +
 deps/sparse/gdbhelpers                             |  307 ++
 deps/sparse/graph.c                                |  186 ++
 deps/sparse/gvpr/return-paths                      |  107 +
 deps/sparse/gvpr/subg-fwd                          |   79 +
 deps/sparse/gvpr/subg-rev                          |  101 +
 deps/sparse/ident-list.h                           |  117 +
 deps/sparse/inline.c                               |  569 ++++
 deps/sparse/lib.c                                  |  991 ++++++
 deps/sparse/lib.h                                  |  226 ++
 deps/sparse/linearize.c                            | 2214 +++++++++++++
 deps/sparse/linearize.h                            |  346 ++
 deps/sparse/liveness.c                             |  362 +++
 deps/sparse/memops.c                               |  196 ++
 deps/sparse/obfuscate.c                            |   60 +
 deps/sparse/parse.c                                | 2785 ++++++++++++++++
 deps/sparse/parse.dtd                              |   51 +
 deps/sparse/parse.h                                |  134 +
 deps/sparse/pre-process.c                          | 1845 +++++++++++
 deps/sparse/ptrlist.c                              |  248 ++
 deps/sparse/ptrlist.h                              |  282 ++
 deps/sparse/scope.c                                |  118 +
 deps/sparse/scope.h                                |   44 +
 deps/sparse/show-parse.c                           | 1158 +++++++
 deps/sparse/simplify.c                             |  950 ++++++
 deps/sparse/sort.c                                 |  290 ++
 deps/sparse/sparse.1                               |  317 ++
 deps/sparse/sparse.c                               |  287 ++
 deps/sparse/sparse.pc.in                           |    9 +
 deps/sparse/storage.c                              |  307 ++
 deps/sparse/storage.h                              |   79 +
 deps/sparse/symbol.c                               |  856 +++++
 deps/sparse/symbol.h                               |  393 +++
 deps/sparse/target.c                               |   46 +
 deps/sparse/target.h                               |   60 +
 deps/sparse/test-dissect.c                         |   97 +
 deps/sparse/test-inspect.c                         |   43 +
 deps/sparse/test-lexing.c                          |   33 +
 deps/sparse/test-linearize.c                       |   49 +
 deps/sparse/test-parsing.c                         |   75 +
 deps/sparse/test-sort.c                            |   46 +
 deps/sparse/test-unssa.c                           |   86 +
 deps/sparse/token.h                                |  217 ++
 deps/sparse/tokenize.c                             | 1002 ++++++
 deps/sparse/unssa.c                                |  139 +
 deps/sparse/validation/.gitignore                  |    4 +
 deps/sparse/validation/address_space.c             |   17 +
 deps/sparse/validation/asm-empty-clobber.c         |   28 +
 deps/sparse/validation/asm-goto-lables.c           |   22 +
 deps/sparse/validation/attr-warning.c              |    8 +
 deps/sparse/validation/attr_in_parameter.c         |   12 +
 deps/sparse/validation/attr_vector_size.c          |    7 +
 .../validation/bad-array-designated-initializer.c  |   13 +
 deps/sparse/validation/bad-assignment.c            |   14 +
 deps/sparse/validation/bad-cast.c                  |   15 +
 deps/sparse/validation/bad-ternary-cond.c          |   12 +
 deps/sparse/validation/bad-typeof.c                |   14 +
 deps/sparse/validation/badtype1.c                  |    6 +
 deps/sparse/validation/badtype2.c                  |   24 +
 deps/sparse/validation/badtype3.c                  |   27 +
 deps/sparse/validation/badtype4.c                  |   15 +
 deps/sparse/validation/binary-constant.c           |    7 +
 deps/sparse/validation/bitfields.c                 |   21 +
 deps/sparse/validation/bug_inline_switch.c         |   25 +
 deps/sparse/validation/builtin_safe1.c             |   38 +
 deps/sparse/validation/builtin_unreachable.c       |   15 +
 .../validation/calling-convention-attributes.c     |   26 +
 deps/sparse/validation/check_byte_count-ice.c      |   14 +
 deps/sparse/validation/choose_expr.c               |   17 +
 deps/sparse/validation/comma.c                     |   12 +
 deps/sparse/validation/compare-null-to-int.c       |   11 +
 deps/sparse/validation/cond_expr.c                 |   19 +
 deps/sparse/validation/cond_expr2.c                |   22 +
 deps/sparse/validation/context.c                   |  336 ++
 .../validation/declaration-after-statement-ansi.c  |   12 +
 .../validation/declaration-after-statement-c89.c   |   12 +
 .../validation/declaration-after-statement-c99.c   |    9 +
 .../declaration-after-statement-default.c          |    9 +
 deps/sparse/validation/definitions.c               |   12 +
 deps/sparse/validation/designated-init.c           |  195 ++
 deps/sparse/validation/double-semicolon.c          |    9 +
 deps/sparse/validation/dubious-bitwise-with-not.c  |   24 +
 deps/sparse/validation/enum_scope.c                |   11 +
 deps/sparse/validation/escapes.c                   |   21 +
 deps/sparse/validation/extern-inline.c             |   23 +
 deps/sparse/validation/field-overlap.c             |   16 +
 deps/sparse/validation/foul-bitwise.c              |   30 +
 .../function-pointer-modifier-inheritance.c        |   18 +
 deps/sparse/validation/identifier_list.c           |   18 +
 deps/sparse/validation/init-char-array.c           |   18 +
 .../validation/initializer-entry-defined-twice.c   |   53 +
 deps/sparse/validation/inline_compound_literals.c  |   22 +
 deps/sparse/validation/integer-promotions.c        |    7 +
 deps/sparse/validation/label-asm.c                 |   12 +
 deps/sparse/validation/label-attr.c                |    9 +
 deps/sparse/validation/label-scope.c               |   12 +
 deps/sparse/validation/local-label.c               |   11 +
 deps/sparse/validation/logical.c                   |   17 +
 deps/sparse/validation/member_of_typeof.c          |   10 +
 deps/sparse/validation/missing-ident.c             |   18 +
 deps/sparse/validation/multi_typedef.c             |   15 +
 deps/sparse/validation/nested-declarator.c         |   29 +
 deps/sparse/validation/nested-declarator2.c        |   41 +
 deps/sparse/validation/noderef.c                   |   51 +
 deps/sparse/validation/non-pointer-null.c          |    8 +
 deps/sparse/validation/old-initializer-nowarn.c    |    9 +
 deps/sparse/validation/old-initializer.c           |   12 +
 deps/sparse/validation/outer-scope.c               |   16 +
 deps/sparse/validation/phase2/backslash            |   62 +
 deps/sparse/validation/phase3/comments             |    9 +
 .../sparse/validation/preprocessor/preprocessor1.c |   14 +
 .../validation/preprocessor/preprocessor10.c       |   19 +
 .../validation/preprocessor/preprocessor11.c       |   31 +
 .../validation/preprocessor/preprocessor12.c       |   18 +
 .../validation/preprocessor/preprocessor13.c       |   23 +
 .../validation/preprocessor/preprocessor14.c       |   17 +
 .../validation/preprocessor/preprocessor15.c       |   16 +
 .../validation/preprocessor/preprocessor16.c       |   30 +
 .../validation/preprocessor/preprocessor17.c       |   15 +
 .../validation/preprocessor/preprocessor18.c       |   17 +
 .../validation/preprocessor/preprocessor19.c       |   18 +
 .../sparse/validation/preprocessor/preprocessor2.c |   15 +
 .../validation/preprocessor/preprocessor20.c       |   14 +
 .../validation/preprocessor/preprocessor20.h       |    6 +
 .../validation/preprocessor/preprocessor21.c       |   16 +
 .../validation/preprocessor/preprocessor22.c       |   35 +
 .../sparse/validation/preprocessor/preprocessor3.c |   32 +
 .../sparse/validation/preprocessor/preprocessor4.c |   15 +
 .../sparse/validation/preprocessor/preprocessor5.c |   14 +
 .../sparse/validation/preprocessor/preprocessor6.c |   29 +
 .../sparse/validation/preprocessor/preprocessor7.c |   14 +
 .../sparse/validation/preprocessor/preprocessor8.c |   38 +
 .../sparse/validation/preprocessor/preprocessor9.c |   16 +
 deps/sparse/validation/reserved.c                  |   40 +
 deps/sparse/validation/restrict-array.c            |   12 +
 deps/sparse/validation/restricted-typeof.c         |    8 +
 deps/sparse/validation/sizeof-bool.c               |   12 +
 deps/sparse/validation/sizeof-compound-postfix.c   |    8 +
 deps/sparse/validation/specifiers1.c               |  101 +
 deps/sparse/validation/specifiers2.c               |  152 +
 deps/sparse/validation/static-forward-decl.c       |   10 +
 deps/sparse/validation/struct-as.c                 |   19 +
 .../sparse/validation/struct-attribute-placement.c |    6 +
 deps/sparse/validation/struct-ns1.c                |   20 +
 deps/sparse/validation/struct-ns2.c                |   19 +
 deps/sparse/validation/struct-size1.c              |   21 +
 deps/sparse/validation/test-be.c                   |   46 +
 deps/sparse/validation/test-suite                  |  252 ++
 deps/sparse/validation/type1.c                     |   27 +
 deps/sparse/validation/typedef_shadow.c            |   12 +
 deps/sparse/validation/typeof-attribute.c          |   16 +
 deps/sparse/validation/typesign.c                  |   61 +
 deps/sparse/validation/varargs1.c                  |    8 +
 190 files changed, 35815 insertions(+), 0 deletions(-)
---
diff --git a/deps/sparse/Documentation/data-structures.txt b/deps/sparse/Documentation/data-structures.txt
new file mode 100644
index 0000000..3745b54
--- /dev/null
+++ b/deps/sparse/Documentation/data-structures.txt
@@ -0,0 +1,54 @@
+
+<JoshTriplett> As far as the parsing structures go...
+<JoshTriplett> The C parser exists in two main files: parse.c, which parses statements, and expression.c, which parses expressions.
+<JoshTriplett> parse.h contains the definition of struct statement, which represents a C statement.
+<JoshTriplett> That includes only those things which can't appear as an expression, which primarily includes control flow statements such as if, loops, switch/case, and goto.
+<JoshTriplett> expression.h contains the definition of struct expression, which represents a C expression.  That has a lot more content, since most C constructs can appear in expressions.
+<JoshTriplett> A series of statements forms a compound statement (STMT_COMPOUND).
+<JoshTriplett> That appears as another struct statement which has a statement_list member.
+<JoshTriplett> A function body consists of a compound statement.
+<JoshTriplett> When you look at a loop body, if or else body, or case body, you'll notice that they just have a struct statement, not a statement_list; they can have multiple statements by using a compound statement.
+<JoshTriplett> Also note that all loops get turned into a single "iterator" statement.
+<JoshTriplett> for, while, and do-while.
+<JoshTriplett> A symbol, then, represents a name in a C file.  A symbol might represent a variable, a function, a label, or various other things.
+<JoshTriplett> See symbol.h.
+<JoshTriplett> "struct symbol" represents one symbol.
+<JoshTriplett> As with the various other structures, it has some common data and a union of sub-structures for the parts that differ between different types.
+<JoshTriplett> Most of the interesting bits come in the NS_SYMBOL case.
+<JoshTriplett> Among other things, it has a struct statement for the body of a function (if any), a list of symbols for the arguments, an expression for a variable initializer, and so on.
+<JoshTriplett> Together, struct symbol, struct statement, and struct expression represent most of the abstract syntax tree for C.
+<JoshTriplett> So, that represents most of the "front-end" of Sparse: parsing C and generating that abstract syntax tree.
+<JoshTriplett> That much occurs in pretty much any program using the Sparse frontend.
+<JoshTriplett> The backend varies among programs.
+<JoshTriplett> For instance, the c2xml backend goes that far, then outputs XML.
+<JoshTriplett> The sparse static analysis backend has a few steps: it generates linearized bytecode, does some evaluation on that, and outputs some warnings.
+<JoshTriplett> Several other backends run that linearized bytecode stage.
+<JoshTriplett> The linearized bytecode itself has a set of nested structures.
+<JoshTriplett> linearize.h defines all of them.
+<JoshTriplett> At the top level, it has struct entrypoint.
+<JoshTriplett> That represents an entrypoint to the code, which would normally mean a function.
+<JoshTriplett> An entrypoint has a list of basic blocks.
+<JoshTriplett> struct basic_block.
+<JoshTriplett> A basic block represents a series of instructions with no branches.
+<JoshTriplett> Straight-line code.
+<JoshTriplett> A branch only occurs at the end of a basic block, and branches can only target the beginning of a basic block.
+<JoshTriplett> Typically, a conditional will consist of a basic block leading up to the branch, a basic block for the true case, a basic block for the false case, and a basic block where the two paths merge back together.
+<JoshTriplett> Either the true or the false case may not exist.
+<JoshTriplett> A loop will normally have a basic block for the loop body, which can branch to the top at the end or continue to the next basic block.
+<JoshTriplett> So basic blocks represent a node in the control flow graph.
+<JoshTriplett> The edges in that graph lead from one basic block to a basic block which can follow it in the execution of the program.
+<JoshTriplett> Each basic block has a series of instructions, "struct instruction".
+<JoshTriplett> "enum opcode" lists all the instructions.
+<JoshTriplett> Fairly high-level instruction set, corresponding directly to bits of C.
+<JoshTriplett> So you have an entrypoint, which has a graph of basic blocks, each of which has a list of instructions.
+<JoshTriplett> An entrypoint also has a pointer to the first instruction.
+<JoshTriplett> One last bit of trickiness: struct pseudo.
+<JoshTriplett> Have you ever heard of "static single assignment" or SSA form?
+<JoshTriplett> struct pseudo represents one of those single-assignment variables.
+<JoshTriplett> Each one has a pointer to the symbol it represents (which may have many pseudos referencing it).
+<JoshTriplett> Each one also has a pointer to the instruction that defines it.
+<JoshTriplett> That covers most of the major data structures in Sparse.
+<JoshTriplett> Now, given all that, some of the top-level stuff in sparse.c may make more sense.
+<JoshTriplett> For instance, the context checking works in terms of basic blocks.
+<JoshTriplett> Hopefully some of that helped you understand Sparse better.
+
diff --git a/deps/sparse/Documentation/test-suite b/deps/sparse/Documentation/test-suite
new file mode 100644
index 0000000..6c4f24f
--- /dev/null
+++ b/deps/sparse/Documentation/test-suite
@@ -0,0 +1,112 @@
+
+
+	Sparse test suite
+	~~~~~~~~~~~~~~~~~
+
+Sparse has a number of test cases in its validation directory. The test-suite
+script aims at making automated checking of these tests possible. It works by
+embedding tags in C comments in the test cases.
+
+check-name: (mandatory)
+	Name of the test.
+
+check-description: (optional)
+	A description of what the test checks.
+
+check-command: (optional)
+	There are different kinds of tests. Some can validate the sparse
+	preprocessor, while others will use sparse, cgcc, or even other backends
+	of the library. check-command allows you to give a custom command to
+	run the test-case.
+	The '$file' string is special. It will be expanded to the file name at
+	run time.
+	It defaults to "sparse $file".
+
+check-exit-value: (optional)
+	The expected exit value of check-command. It defaults to 0.
+
+check-output-start / check-output-end (optional)
+	The expected output (stdout and stderr) of check-command lies between
+	those two tags. It defaults to no output.
+
+check-known-to-fail (optional)
+	Mark the test as being known to fail.
+
+
+	Using test-suite
+	~~~~~~~~~~~~~~~~
+
+The test-suite script is called through the check target of the Makefile. It
+will try to check every test case it finds (find validation -name '*.c').
+
+It can be called to check a single test with:
+$ cd validation
+$ ./test-suite single preprocessor/preprocessor1.c
+     TEST     Preprocessor #1 (preprocessor/preprocessor1.c)
+preprocessor/preprocessor1.c passed !
+
+
+	Writing a test
+	~~~~~~~~~~~~~~
+
+test-suite comes with a format command to make a test easier to write:
+
+	test-suite format file [name [cmd]]
+
+name:
+	check-name value. If no name is provided, it defaults to the file name.
+cmd:
+	check-command value. If no cmd is provided, it defaults to
+	"sparse $file".
+
+The output of the test-suite format command can be redirected into the
+test case to create a test-suite formated file.
+
+$ ./test-suite format bad-assignment.c Assignment >> bad-assignment.c
+$ cat !$
+cat bad-assignment.c
+/*
+ * check-name: bad assignment
+ *
+ * check-command: sparse $file
+ * check-exit-value: 1
+ *
+ * check-output-start
+bad-assignment.c:3:6: error: Expected ; at end of statement
+bad-assignment.c:3:6: error: got \
+ * check-output-end
+ */
+
+You can define the check-command you want to use for the test. $file will be
+extended to the file name at run time.
+
+$ ./test-suite format validation/preprocessor2.c "Preprocessor #2" \
+		"sparse -E \$file" >> validation/preprocessor2.c
+$ cat !$
+cat validation/preprocessor2.c
+/*
+ * This one we happen to get right.
+ *
+ * It should result in a simple
+ *
+ *	a + b
+ *
+ * for a proper preprocessor.
+ */
+#define TWO a, b
+
+#define UNARY(x) BINARY(x)
+#define BINARY(x, y) x + y
+
+UNARY(TWO)
+/*
+ * check-name: Preprocessor #2
+ *
+ * check-command: sparse -E $file
+ * check-exit-value: 0
+ *
+ * check-output-start
+
+a + b
+ * check-output-end
+ */
diff --git a/deps/sparse/FAQ b/deps/sparse/FAQ
new file mode 100644
index 0000000..4cad826
--- /dev/null
+++ b/deps/sparse/FAQ
@@ -0,0 +1,98 @@
+	FAQ - Why sparse?
+
+Q.  Why not just use gcc?
+
+A.  Gcc is big, complex, and the gcc maintainers are not interested in
+    other uses of the gcc front-end.  In fact, gcc has explicitly
+    resisted splitting up the front and back ends and having some common
+    intermediate language because of religious license issues - you can
+    have multiple front ends and back ends, but they all have to be part
+    of gcc and licensed under the GPL. 
+
+    This all (in my opinion) makes gcc development harder than it should
+    be, and makes the end result very ungainly.  With "sparse", the
+    front-end is very explicitly separated into its own independent
+    project, and is totally independent from the users.  I don't want to
+    know what you do in the back-end, because I don't think I _should_
+    know or care. 
+
+
+Q.  Why not GPL?
+
+A.  See the previous question: I personally think that the front end
+    must be a totally separate project from the back end: any other
+    approach just leads to insanity.  However, at the same time clearly
+    we cannot write intermediate files etc crud (since then the back end
+    would have to re-parse the whole thing and would have to have its
+    own front end and just do a lot of things that do not make any sense
+    from a technical standpoint).
+
+    I like the GPL, but as rms says, "Linus is just an engineer". I
+    refuse to use a license if that license causes bad engineering
+    decisions.  I want the front-end to be considered a separate
+    project, yet the GPL considers the required linking to make the
+    combined thing a derived work. Which is against the whole point
+    of 'sparse'.
+
+    I'm not interested in code generation. I'm not interested in what
+    other people do with their back-ends.  I _am_ interested in making a
+    good front-end, and "good" means that people find it usable. And
+    they shouldn't be scared away by politics or licenses. If they want
+    to make their back-end be BSD/MIT licensed, that's great. And if
+    they want to have a proprietary back-end, that's ok by me too. It's
+    their loss, not mine.
+
+    At the same time, I'm a big believer in "quid pro quo". I wrote the
+    front-end, and if you make improvements to the semantic parsing part
+    (as opposed to just using the resulting parse tree), you'd better
+    cough up.  The front-end is intended to be an open-source project in
+    its own right, and if you improve the front end, you must give those
+    improvements back. That's your "quid" to my "quo".
+
+
+Q.  So what _is_ the license?
+
+A.  I don't know yet.  I originally thought it would be LGPL, but I'm
+    possibly going for a license that is _not_ subsumable by the GPL. 
+    In other words, I don't want to see a GPL'd project suck in the
+    LGPL'd front-end, and then make changes to the front end under the
+    GPL (this is something that the LGPL expressly allows, and see the
+    previous question for why I think it's the _only_ thing that I will
+    not allow). 
+
+    The current front-runner is the OSL ("Open Software License", see
+    http://www.opensource.org/licenses/osl.php), together with a note on
+    what makes source derivative and what does not to make it clear that
+    people can write back-ends for it without having to make those
+    back-ends available under the OSL. 
+
+
+Q.  Does it really parse C?
+
+A.  Yeah, well...  It parses a fairly complete subset of "extended C" as
+    defined by gcc.  HOWEVER, since I don't believe in K&R syntax for
+    function declarations or in giving automatic integer types, it
+    doesn't do that.  If you don't give types to your variables, they
+    won't have any types, and you can't use them.
+
+    Similarly, it will be very unhappy about undeclared functions,
+    rather than just assuming they have type "int". 
+
+    Note that a large rationale for me doing this project is for type
+    following, which to some degree explains why the thing is type-anal
+    and refuses to touch the old-style pre-ANSI non-typed (or weakly
+    typed) constructs. Maybe somebody else who is working on projects
+    where pre-ANSI C makes sense might be more inclined to care about
+    ancient C.  It's open source, after all. Go wild.
+
+
+Q.  What other sparse resources are available?
+
+A.  Website: http://www.kernel.org/pub/software/devel/sparse/
+
+    Mailing list: linux-sparse vger kernel org
+    See http://vger.kernel.org/vger-lists.html#linux-sparse for subscription
+    instructions and links to archives
+
+    Git repo: git://git.kernel.org/pub/scm/devel/sparse/sparse.git
+    gitweb: http://git.kernel.org/?p=devel/sparse/sparse.git
diff --git a/deps/sparse/LICENSE b/deps/sparse/LICENSE
new file mode 100644
index 0000000..be0fd01
--- /dev/null
+++ b/deps/sparse/LICENSE
@@ -0,0 +1,198 @@
+
+The 'sparse' C parser front-end library is copyrighted by Transmeta Corp
+and other authors and licensed under the "Open Software License v1.1" as
+obtained from www.opensource.org (and included here-in for easy
+reference) (that license itself is copyrighted by Larry Rosen). 
+
+Note that the "Original Work" that this license covers is only the
+front-end library itself, ie the code required to parse the source file
+and annotate the resulting parse tree with the semantic meaning (aka
+"types") of the sources.  Thus just the act of linking this library into
+another program (aka "back-end") does NOT in itself make that back-end
+be considered a derivative work of this Original Work. 
+
+However, any modifications, callbacks or other functionality that is
+added and run either directly or indirectly by the front-end are to be
+considered derived works of this library, and as such fall under the
+requirements of this license.
+
+				Linus Torvalds
+				Santa Clara, CA
+				April 15th, 2003
+
+
+[ This copy of the license is the flat-text version of original,
+  available in its full glory at
+
+	 http://www.opensource.org/licenses/osl.php
+
+  please refer to there for the authoritative and slightly more
+  pretty-printed version ]
+
+------
+
+			The Open Software License
+			v. 1.1
+
+This Open Software License (the "License") applies to any original work of 
+authorship (the "Original Work") whose owner (the "Licensor") has placed the 
+following notice immediately following the copyright notice for the Original 
+Work:
+
+	Licensed under the Open Software License version 1.1
+
+
+1) Grant of Copyright License. Licensor hereby grants You a world-wide, 
+royalty-free, non-exclusive, perpetual, non-sublicenseable license to do the 
+following:
+
+  a) to reproduce the Original Work in copies;
+
+  b) to prepare derivative works ("Derivative Works") based upon the
+     Original Work;
+
+  c) to distribute copies of the Original Work and Derivative Works to
+     the public, with the proviso that copies of Original Work or
+     Derivative Works that You distribute shall be licensed under the
+     Open Software License;
+
+  d) to perform the Original Work publicly; and
+
+  e) to display the Original Work publicly. 
+
+2) Grant of Patent License. Licensor hereby grants You a world-wide, 
+royalty-free, non-exclusive, perpetual, non-sublicenseable license, under 
+patent claims owned or controlled by the Licensor that are embodied in the 
+Original Work as furnished by the Licensor ("Licensed Claims") to make, use, 
+sell and offer for sale the Original Work. Licensor hereby grants You a 
+world-wide, royalty-free, non-exclusive, perpetual, non-sublicenseable license 
+under the Licensed Claims to make, use, sell and offer for sale Derivative Works.
+
+3) Grant of Source Code License. The term "Source Code" means the preferred 
+form of the Original Work for making modifications to it and all available 
+documentation describing how to modify the Original Work. Licensor hereby 
+agrees to provide a machine-readable copy of the Source Code of the Original 
+Work along with each copy of the Original Work that Licensor distributes. 
+Licensor reserves the right to satisfy this obligation by placing a 
+machine-readable copy of the Source Code in an information repository reasonably 
+calculated to permit inexpensive and convenient access by You for as long as
+ Licensor continues to distribute the Original Work, and by publishing the 
+address of that information repository in a notice immediately following the 
+copyright notice that applies to the Original Work.
+
+
+4) Exclusions From License Grant. Nothing in this License shall be deemed to 
+grant any rights to trademarks, copyrights, patents, trade secrets or any 
+other intellectual property of Licensor except as expressly stated herein. No 
+patent license is granted to make, use, sell or offer to sell embodiments of 
+any patent claims other than the Licensed Claims defined in Section 2. No 
+right is granted to the trademarks of Licensor even if such marks are included 
+in the Original Work. Nothing in this License shall be interpreted to prohibit 
+Licensor from licensing under different terms from this License any Original 
+Work that Licensor otherwise would have a right to license.
+
+5) External Deployment. The term "External Deployment" means the use or 
+distribution of the Original Work or Derivative Works in any way such that the 
+Original Work or Derivative Works may be used by anyone other than You, 
+whether the Original Work or Derivative Works are distributed to those persons 
+or made available as an application intended for use over a computer network. 
+As an express condition for the grants of license hereunder, You agree that 
+any External Deployment by You of a Derivative Work shall be deemed a 
+distribution and shall be licensed to all under the terms of this License, as 
+prescribed in section 1(c) herein.
+
+6) Attribution Rights. You must retain, in the Source Code of any Derivative 
+Works that You create, all copyright, patent or trademark notices from the 
+Source Code of the Original Work, as well as any notices of licensing and any 
+descriptive text identified therein as an "Attribution Notice." You must cause 
+the Source Code for any Derivative Works that You create to carry a prominent 
+Attribution Notice reasonably calculated to inform recipients that You have 
+modified the Original Work.
+
+7) Warranty and Disclaimer of Warranty. Licensor warrants that the copyright 
+in and to the Original Work is owned by the Licensor or that the Original Work 
+is distributed by Licensor under a valid current license from the copyright 
+owner. Except as expressly stated in the immediately proceeding sentence, the 
+Original Work is provided under this License on an "AS IS" BASIS and WITHOUT 
+WARRANTY, either express or implied, including, without limitation, the 
+warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. 
+This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No 
+license to Original Work is granted hereunder except under this disclaimer.
+
+8) Limitation of Liability. Under no circumstances and under no legal theory, 
+whether in tort (including negligence), contract, or otherwise, shall the 
+Licensor be liable to any person for any direct, indirect, special, incidental, 
+or consequential damages of any character arising as a result of this License 
+or the use of the Original Work including, without limitation, damages for 
+loss of goodwill, work stoppage, computer failure or malfunction, or any and 
+all other commercial damages or losses. This limitation of liability shall not 
+apply to liability for death or personal injury resulting from Licensor's 
+negligence to the extent applicable law prohibits such limitation. Some 
+jurisdictions do not allow the exclusion or limitation of incidental or 
+consequential damages, so this exclusion and limitation may not apply to You.
+
+
+9) Acceptance and Termination. If You distribute copies of the Original Work 
+or a Derivative Work, You must make a reasonable effort under the circumstances 
+to obtain the express and volitional assent of recipients to the terms of this 
+License. Nothing else but this License (or another written agreement between 
+Licensor and You) grants You permission to create Derivative Works based upon 
+the Original Work or to exercise any of the rights granted in Sections 1 herein, 
+and any attempt to do so except under the terms of this License (or another 
+written agreement between Licensor and You) is expressly prohibited by U.S. 
+copyright law, the equivalent laws of other countries, and by international 
+treaty. Therefore, by exercising any of the rights granted to You in Sections 
+1 herein, You indicate Your acceptance of this License and all of its terms and 
+conditions. This License shall terminate immediately and you may no longer 
+exercise any of the rights granted to You by this License upon Your failure to 
+honor the proviso in Section 1(c) herein.
+
+10) Mutual Termination for Patent Action. This License shall terminate 
+automatically and You may no longer exercise any of the rights granted to You 
+by this License if You file a lawsuit in any court alleging that any OSI 
+Certified open source software that is licensed under any license containing 
+this "Mutual Termination for Patent Action" clause infringes any patent claims 
+that are essential to use that software.
+
+11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this 
+License may be brought only in the courts of a jurisdiction wherein the Licensor 
+resides or in which Licensor conducts its primary business, and under the laws 
+of that jurisdiction excluding its conflict-of-law provisions. The application 
+of the United Nations Convention on Contracts for the International Sale of 
+Goods is expressly excluded. Any use of the Original Work outside the scope of 
+this License or after its termination shall be subject to the requirements and 
+penalties of the U.S. Copyright Act, 17 U.S.C. å101 et seq., the equivalent 
+laws of other countries, and international treaty. This section shall survive 
+the termination of this License.
+
+12) Attorneys Fees. In any action to enforce the terms of this License or 
+seeking damages relating thereto, the prevailing party shall be entitled to 
+recover its costs and expenses, including, without limitation, reasonable 
+attorneys' fees and costs incurred in connection with such action, including 
+any appeal of such action. This section shall survive the termination of this 
+License.
+
+13) Miscellaneous. This License represents the complete agreement concerning 
+the subject matter hereof. If any provision of this License is held to be 
+unenforceable, such provision shall be reformed only to the extent necessary 
+to make it enforceable.
+
+14) Definition of "You" in This License. "You" throughout this License, 
+whether in upper or lower case, means an individual or a legal entity exercising 
+rights under, and complying with all of the terms of, this License. For legal 
+entities, "You" includes any entity that controls, is controlled by, or is under 
+common control with you. For purposes of this definition, "control" means (i) 
+the power, direct or indirect, to cause the direction or management of such 
+entity, whether by contract or otherwise, or (ii) ownership of fifty percent 
+(50%) or more of the outstanding shares, or (iii) beneficial ownership of such 
+entity.
+
+15) Right to Use. You may use the Original Work in all ways not otherwise 
+restricted or conditioned by this License or by law, and Licensor promises not 
+to interfere with or be responsible for such uses by You.
+
+This license is Copyright (C) 2002 Lawrence E. Rosen. All rights reserved. 
+Permission is hereby granted to copy and distribute this license without 
+modification. This license may not be modified without the express written 
+permission of its copyright owner.
diff --git a/deps/sparse/allocate.c b/deps/sparse/allocate.c
new file mode 100644
index 0000000..5cc52a9
--- /dev/null
+++ b/deps/sparse/allocate.c
@@ -0,0 +1,129 @@
+/*
+ * allocate.c - simple space-efficient blob allocator.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ *
+ * Simple allocator for data that doesn't get partially free'd.
+ * The tokenizer and parser allocate a _lot_ of small data structures
+ * (often just two-three bytes for things like small integers),
+ * and since they all depend on each other you can't free them
+ * individually _anyway_. So do something that is very space-
+ * efficient: allocate larger "blobs", and give out individual
+ * small bits and pieces of it with no maintenance overhead.
+ */
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "compat.h"
+#include "token.h"
+#include "symbol.h"
+#include "scope.h"
+#include "expression.h"
+#include "linearize.h"
+
+void protect_allocations(struct allocator_struct *desc)
+{
+	desc->blobs = NULL;
+}
+
+void drop_all_allocations(struct allocator_struct *desc)
+{
+	struct allocation_blob *blob = desc->blobs;
+
+	desc->blobs = NULL;
+	desc->allocations = 0;
+	desc->total_bytes = 0;
+	desc->useful_bytes = 0;
+	desc->freelist = NULL;
+	while (blob) {
+		struct allocation_blob *next = blob->next;
+		blob_free(blob, desc->chunking);
+		blob = next;
+	}
+}
+
+void free_one_entry(struct allocator_struct *desc, void *entry)
+{
+	void **p = entry;
+	*p = desc->freelist;
+	desc->freelist = p;
+}
+
+void *allocate(struct allocator_struct *desc, unsigned int size)
+{
+	unsigned long alignment = desc->alignment;
+	struct allocation_blob *blob = desc->blobs;
+	void *retval;
+
+	/*
+	 * NOTE! The freelist only works with things that are
+	 *  (a) sufficiently aligned
+	 *  (b) use a constant size
+	 * Don't try to free allocators that don't follow
+	 * these rules.
+	 */
+	if (desc->freelist) {
+		void **p = desc->freelist;
+		retval = p;
+		desc->freelist = *p;
+		do {
+			*p = NULL;
+			p++;
+		} while ((size -= sizeof(void *)) > 0);
+		return retval;
+	}
+
+	desc->allocations++;
+	desc->useful_bytes += size;
+	size = (size + alignment - 1) & ~(alignment-1);
+	if (!blob || blob->left < size) {
+		unsigned int offset, chunking = desc->chunking;
+		struct allocation_blob *newblob = blob_alloc(chunking);
+		if (!newblob)
+			die("out of memory");
+		desc->total_bytes += chunking;
+		newblob->next = blob;
+		blob = newblob;
+		desc->blobs = newblob;
+		offset = offsetof(struct allocation_blob, data);
+		offset = (offset + alignment - 1) & ~(alignment-1);
+		blob->left = chunking - offset;
+		blob->offset = offset - offsetof(struct allocation_blob, data);
+	}
+	retval = blob->data + blob->offset;
+	blob->offset += size;
+	blob->left -= size;
+	return retval;
+}
+
+void show_allocations(struct allocator_struct *x)
+{
+	fprintf(stderr, "%s: %d allocations, %d bytes (%d total bytes, "
+			"%6.2f%% usage, %6.2f average size)\n",
+		x->name, x->allocations, x->useful_bytes, x->total_bytes,
+		100 * (double) x->useful_bytes / x->total_bytes,
+		(double) x->useful_bytes / x->allocations);
+}
+
+ALLOCATOR(ident, "identifiers");
+ALLOCATOR(token, "tokens");
+ALLOCATOR(context, "contexts");
+ALLOCATOR(symbol, "symbols");
+ALLOCATOR(expression, "expressions");
+ALLOCATOR(statement, "statements");
+ALLOCATOR(string, "strings");
+ALLOCATOR(scope, "scopes");
+__DO_ALLOCATOR(void, 0, 1, "bytes", bytes);
+ALLOCATOR(basic_block, "basic_block");
+ALLOCATOR(entrypoint, "entrypoint");
+ALLOCATOR(instruction, "instruction");
+ALLOCATOR(multijmp, "multijmp");
+ALLOCATOR(pseudo, "pseudo");
+
+
diff --git a/deps/sparse/allocate.h b/deps/sparse/allocate.h
new file mode 100644
index 0000000..9f1dc8c
--- /dev/null
+++ b/deps/sparse/allocate.h
@@ -0,0 +1,81 @@
+#ifndef ALLOCATE_H
+#define ALLOCATE_H
+
+struct allocation_blob {
+	struct allocation_blob *next;
+	unsigned int left, offset;
+	unsigned char data[];
+};
+
+struct allocator_struct {
+	const char *name;
+	struct allocation_blob *blobs;
+	unsigned int alignment;
+	unsigned int chunking;
+	void *freelist;
+	/* statistics */
+	unsigned int allocations, total_bytes, useful_bytes;
+};
+
+extern void protect_allocations(struct allocator_struct *desc);
+extern void drop_all_allocations(struct allocator_struct *desc);
+extern void *allocate(struct allocator_struct *desc, unsigned int size);
+extern void free_one_entry(struct allocator_struct *desc, void *entry);
+extern void show_allocations(struct allocator_struct *);
+
+#define __DECLARE_ALLOCATOR(type, x)		\
+	extern type *__alloc_##x(int);		\
+	extern void __free_##x(type *);		\
+	extern void show_##x##_alloc(void);	\
+	extern void clear_##x##_alloc(void);	\
+	extern void protect_##x##_alloc(void);
+#define DECLARE_ALLOCATOR(x) __DECLARE_ALLOCATOR(struct x, x)
+
+#define __DO_ALLOCATOR(type, objsize, objalign, objname, x)	\
+	static struct allocator_struct x##_allocator = {	\
+		.name = objname,				\
+		.alignment = objalign,				\
+		.chunking = CHUNK };				\
+	type *__alloc_##x(int extra)				\
+	{							\
+		return allocate(&x##_allocator, objsize+extra);	\
+	}							\
+	void __free_##x(type *entry)				\
+	{							\
+		free_one_entry(&x##_allocator, entry);		\
+	}							\
+	void show_##x##_alloc(void)				\
+	{							\
+		show_allocations(&x##_allocator);		\
+	}							\
+	void clear_##x##_alloc(void)				\
+	{							\
+		drop_all_allocations(&x##_allocator);		\
+	}							\
+	void protect_##x##_alloc(void)				\
+	{							\
+		protect_allocations(&x##_allocator);		\
+	}
+
+#define __ALLOCATOR(t, n, x) 					\
+	__DO_ALLOCATOR(t, sizeof(t), __alignof__(t), n, x)
+
+#define ALLOCATOR(x, n) __ALLOCATOR(struct x, n, x)
+
+DECLARE_ALLOCATOR(ident);
+DECLARE_ALLOCATOR(token);
+DECLARE_ALLOCATOR(context);
+DECLARE_ALLOCATOR(symbol);
+DECLARE_ALLOCATOR(expression);
+DECLARE_ALLOCATOR(statement);
+DECLARE_ALLOCATOR(string);
+DECLARE_ALLOCATOR(scope);
+__DECLARE_ALLOCATOR(void, bytes);
+DECLARE_ALLOCATOR(basic_block);
+DECLARE_ALLOCATOR(entrypoint);
+DECLARE_ALLOCATOR(instruction);
+DECLARE_ALLOCATOR(multijmp);
+DECLARE_ALLOCATOR(phi);
+DECLARE_ALLOCATOR(pseudo);
+
+#endif
diff --git a/deps/sparse/ast-inspect.c b/deps/sparse/ast-inspect.c
new file mode 100644
index 0000000..24d4a4a
--- /dev/null
+++ b/deps/sparse/ast-inspect.c
@@ -0,0 +1,222 @@
+
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "ast-inspect.h"
+#include "expression.h"
+
+static inline void inspect_ptr_list(AstNode *node, const char *name, void (*inspect)(AstNode *))
+{
+	struct ptr_list *ptrlist = node->ptr;
+	void *ptr;
+	int i = 0;
+
+	node->text = g_strdup_printf("%s %s:", node->text, name);
+	FOR_EACH_PTR(ptrlist, ptr) {
+		char *index = g_strdup_printf("%d: ", i++);
+		ast_append_child(node, index, ptr, inspect);
+	} END_FOR_EACH_PTR(ptr);
+}
+
+
+static const char *statement_type_name(enum statement_type type)
+{
+	static const char *statement_type_name[] = {
+		[STMT_NONE] = "STMT_NONE",
+		[STMT_DECLARATION] = "STMT_DECLARATION",
+		[STMT_EXPRESSION] = "STMT_EXPRESSION",
+		[STMT_COMPOUND] = "STMT_COMPOUND",
+		[STMT_IF] = "STMT_IF",
+		[STMT_RETURN] = "STMT_RETURN",
+		[STMT_CASE] = "STMT_CASE",
+		[STMT_SWITCH] = "STMT_SWITCH",
+		[STMT_ITERATOR] = "STMT_ITERATOR",
+		[STMT_LABEL] = "STMT_LABEL",
+		[STMT_GOTO] = "STMT_GOTO",
+		[STMT_ASM] = "STMT_ASM",
+		[STMT_CONTEXT] = "STMT_CONTEXT",
+		[STMT_RANGE] = "STMT_RANGE",
+	};
+	return statement_type_name[type] ?: "UNKNOWN_STATEMENT_TYPE";
+}
+
+void inspect_statement(AstNode *node)
+{
+	struct statement *stmt = node->ptr;
+	node->text = g_strdup_printf("%s %s:", node->text, statement_type_name(stmt->type));
+	switch (stmt->type) {
+		case STMT_COMPOUND:
+			ast_append_child(node, "stmts:", stmt->stmts, inspect_statement_list);
+			break;
+		case STMT_EXPRESSION:
+			ast_append_child(node, "expression:", stmt->expression, inspect_expression);
+			break;
+		case STMT_IF:
+			ast_append_child(node, "conditional:", stmt->if_conditional, inspect_expression);
+			ast_append_child(node, "if_true:", stmt->if_true, inspect_statement);
+			ast_append_child(node, "if_false:", stmt->if_false, inspect_statement);
+			break;
+		case STMT_ITERATOR:
+			ast_append_child(node, "break:", stmt->iterator_break, inspect_symbol);
+			ast_append_child(node, "continue:", stmt->iterator_continue, inspect_symbol);
+			ast_append_child(node, "pre_statement:", stmt->iterator_pre_statement,
+					 inspect_statement);
+			ast_append_child(node, "statement:", stmt->iterator_statement,
+					 inspect_statement);
+			ast_append_child(node, "post_statement:", stmt->iterator_post_statement,
+					 inspect_statement);
+			break;
+
+		case STMT_SWITCH:
+			ast_append_child(node, "switch_expression:", stmt->switch_expression, inspect_expression);
+			ast_append_child(node, "switch_statement:", stmt->switch_statement, inspect_statement);
+			ast_append_child(node, "switch_break:", stmt->switch_break, inspect_symbol);
+			ast_append_child(node, "switch_case:", stmt->switch_case, inspect_symbol);
+			break;
+		case STMT_CASE:
+			ast_append_child(node, "case_expression:", stmt->case_expression, inspect_expression);
+			ast_append_child(node, "case_to:", stmt->case_to, inspect_expression);
+			ast_append_child(node, "case_statement:", stmt->case_statement, inspect_statement);
+			ast_append_child(node, "case_label:", stmt->case_label, inspect_symbol);
+			break;
+		case STMT_RETURN:
+			ast_append_child(node, "ret_value:", stmt->ret_value, inspect_expression);
+			ast_append_child(node, "ret_target:", stmt->ret_target, inspect_symbol);
+			break;
+
+		default:
+			break;
+	}
+}
+
+
+void inspect_statement_list(AstNode *node)
+{
+	inspect_ptr_list(node, "statement_list", inspect_statement);
+}
+
+
+static const char *symbol_type_name(enum type type)
+{
+	static const char *type_name[] = {
+		[SYM_UNINITIALIZED] = "SYM_UNINITIALIZED",
+		[SYM_PREPROCESSOR] = "SYM_PREPROCESSOR",
+		[SYM_BASETYPE] = "SYM_BASETYPE",
+		[SYM_NODE] = "SYM_NODE",
+		[SYM_PTR] = "SYM_PTR",
+		[SYM_FN] = "SYM_FN",
+		[SYM_ARRAY] = "SYM_ARRAY",
+		[SYM_STRUCT] = "SYM_STRUCT",
+		[SYM_UNION] = "SYM_UNION",
+		[SYM_ENUM] = "SYM_ENUM",
+		[SYM_TYPEDEF] = "SYM_TYPEDEF",
+		[SYM_TYPEOF] = "SYM_TYPEOF",
+		[SYM_MEMBER] = "SYM_MEMBER",
+		[SYM_BITFIELD] = "SYM_BITFIELD",
+		[SYM_LABEL] = "SYM_LABEL",
+		[SYM_RESTRICT] = "SYM_RESTRICT",
+		[SYM_FOULED] = "SYM_FOULED",
+		[SYM_KEYWORD] = "SYM_KEYWORD",
+		[SYM_BAD] = "SYM_BAD",
+	};
+	return type_name[type] ?: "UNKNOWN_TYPE";
+}
+
+
+void inspect_symbol(AstNode *node)
+{
+	struct symbol *sym = node->ptr;
+	node->text = g_strdup_printf("%s %s: %s", node->text, symbol_type_name(sym->type),
+				      builtin_typename(sym) ?: show_ident(sym->ident));
+	ast_append_child(node, "ctype.base_type:", sym->ctype.base_type,inspect_symbol);
+
+	switch (sym->namespace) {
+		case NS_PREPROCESSOR:
+			break;
+		default:
+			ast_append_child(node, "arguments:", sym->arguments, inspect_symbol_list);
+			ast_append_child(node, "symbol_list:", sym->symbol_list, inspect_symbol_list);
+			ast_append_child(node, "stmt:", sym->stmt, inspect_statement);
+			break;
+	}
+}
+
+
+void inspect_symbol_list(AstNode *node)
+{
+	inspect_ptr_list(node, "symbol_list", inspect_symbol);
+}
+
+
+static const char *expression_type_name(enum expression_type type)
+{
+	static const char *expression_type_name[] = {
+		[EXPR_VALUE] = "EXPR_VALUE",
+		[EXPR_STRING] = "EXPR_STRING",
+		[EXPR_SYMBOL] = "EXPR_SYMBOL",
+		[EXPR_TYPE] = "EXPR_TYPE",
+		[EXPR_BINOP] = "EXPR_BINOP",
+		[EXPR_ASSIGNMENT] = "EXPR_ASSIGNMENT",
+		[EXPR_LOGICAL] = "EXPR_LOGICAL",
+		[EXPR_DEREF] = "EXPR_DEREF",
+		[EXPR_PREOP] = "EXPR_PREOP",
+		[EXPR_POSTOP] = "EXPR_POSTOP",
+		[EXPR_CAST] = "EXPR_CAST",
+		[EXPR_FORCE_CAST] = "EXPR_FORCE_CAST",
+		[EXPR_IMPLIED_CAST] = "EXPR_IMPLIED_CAST",
+		[EXPR_SIZEOF] = "EXPR_SIZEOF",
+		[EXPR_ALIGNOF] = "EXPR_ALIGNOF",
+		[EXPR_PTRSIZEOF] = "EXPR_PTRSIZEOF",
+		[EXPR_CONDITIONAL] = "EXPR_CONDITIONAL",
+		[EXPR_SELECT] = "EXPR_SELECT",
+		[EXPR_STATEMENT] = "EXPR_STATEMENT",
+		[EXPR_CALL] = "EXPR_CALL",
+		[EXPR_COMMA] = "EXPR_COMMA",
+		[EXPR_COMPARE] = "EXPR_COMPARE",
+		[EXPR_LABEL] = "EXPR_LABEL",
+		[EXPR_INITIALIZER] = "EXPR_INITIALIZER",
+		[EXPR_IDENTIFIER] = "EXPR_IDENTIFIER",
+		[EXPR_INDEX] = "EXPR_INDEX",
+		[EXPR_POS] = "EXPR_POS",
+		[EXPR_FVALUE] = "EXPR_FVALUE",
+		[EXPR_SLICE] = "EXPR_SLICE",
+		[EXPR_OFFSETOF] = "EXPR_OFFSETOF",
+	};
+	return expression_type_name[type] ?: "UNKNOWN_EXPRESSION_TYPE";
+}
+
+void inspect_expression(AstNode *node)
+{
+	struct expression *expr = node->ptr;
+	node->text = g_strdup_printf("%s %s", node->text, expression_type_name(expr->type));
+	switch (expr->type) {
+		case EXPR_STATEMENT:
+			ast_append_child(node, "statement:", expr->statement, inspect_statement);
+			break;
+		case EXPR_BINOP:
+		case EXPR_COMMA:
+		case EXPR_COMPARE:
+		case EXPR_LOGICAL:
+		case EXPR_ASSIGNMENT:
+			ast_append_child(node, "left:", expr->left, inspect_expression);
+			ast_append_child(node, "right:", expr->right, inspect_expression);
+			break;
+
+		case EXPR_CAST:
+		case EXPR_FORCE_CAST:
+		case EXPR_IMPLIED_CAST:
+			ast_append_child(node, "cast_type:", expr->cast_type, inspect_symbol);
+			ast_append_child(node, "cast_expression:", expr->cast_expression, inspect_expression);
+			break;
+
+		case EXPR_PREOP:
+			ast_append_child(node, "unop:", expr->unop, inspect_expression);
+			break;
+		
+		default:
+			break;
+	}
+}
+
+
+
diff --git a/deps/sparse/ast-inspect.h b/deps/sparse/ast-inspect.h
new file mode 100644
index 0000000..6e15c91
--- /dev/null
+++ b/deps/sparse/ast-inspect.h
@@ -0,0 +1,17 @@
+
+#ifndef _AST_INSPECT_H_
+#define _AST_INSPECT_H_
+
+#include "ast-model.h"
+
+void inspect_symbol(AstNode *node);
+void inspect_symbol_list(AstNode *node);
+
+void inspect_statement(AstNode *node);
+void inspect_statement_list(AstNode *node);
+
+void inspect_expression(AstNode *node);
+void inspect_expression_list(AstNode *node);
+
+
+#endif
diff --git a/deps/sparse/ast-model.c b/deps/sparse/ast-model.c
new file mode 100644
index 0000000..704c487
--- /dev/null
+++ b/deps/sparse/ast-model.c
@@ -0,0 +1,468 @@
+/*
+ *   ast-model.c 
+ *
+ *   A custom tree model to simplify viewing of AST objects.
+ *   Modify from the Gtk+ tree view tutorial, custom-list.c
+ *   by Tim-Philipp Mueller < tim at centricular dot net >
+ *
+ *   Copyright (C) 2010 Christopher Li
+ */
+
+
+#include "ast-model.h"
+#include "stdint.h"
+
+/* boring declarations of local functions */
+
+static void ast_init(AstNode *pkg_tree);
+static void ast_class_init(AstNodeClass *klass);
+static void ast_tree_model_init(GtkTreeModelIface *iface);
+static void ast_finalize(GObject *object);
+static GtkTreeModelFlags ast_get_flags(GtkTreeModel *tree_model);
+static gint ast_get_n_columns(GtkTreeModel *tree_model);
+static GType ast_get_column_type(GtkTreeModel *tree_model, gint index);
+static gboolean ast_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
+				  GtkTreePath *path);
+static GtkTreePath *ast_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter); 
+static void ast_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
+			       gint column, GValue *value);
+static gboolean ast_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter);
+static gboolean ast_iter_children(GtkTreeModel *tree_model,
+                                       GtkTreeIter *iter,
+                                       GtkTreeIter *parent);
+static gboolean ast_iter_has_child(GtkTreeModel *tree_model, GtkTreeIter *iter);
+static gint ast_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter);
+static gboolean ast_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
+                                        GtkTreeIter *parent, gint n);
+static gboolean ast_iter_parent(GtkTreeModel *tree_model,
+                                     GtkTreeIter *iter,
+                                     GtkTreeIter *child);
+
+static GObjectClass *parent_class = NULL;  /* GObject stuff - nothing to worry about */
+
+static inline
+void inspect_child_node(AstNode *node)
+{
+	if (node->inspect) {
+		node->inspect(node);
+		node->inspect = NULL;
+	}
+}
+
+
+static inline
+AstNode* ast_nth_child(AstNode *node, int n)
+{
+	if (!node)
+		return NULL;
+
+	inspect_child_node(node);
+
+	if (n >= node->childnodes->len)
+		return FALSE;
+	return g_array_index(node->childnodes, AstNode *, n);
+}
+
+
+static inline
+gboolean ast_set_iter(GtkTreeIter *iter, AstNode *node)
+{
+	iter->user_data = node;
+	iter->user_data2 = iter->user_data3 = NULL;
+	return node != NULL;
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_get_type: here we register our new type and its interfaces
+ *                with the type system. If you want to implement
+ *                additional interfaces like GtkTreeSortable, you
+ *                will need to do it here.
+ *
+ *****************************************************************************/
+
+GType
+ast_get_type (void)
+{
+	static GType ast_type = 0;
+	static const GTypeInfo ast_info = {
+		sizeof (AstNodeClass),
+		NULL,                                         /* base_init */
+		NULL,                                         /* base_finalize */
+		(GClassInitFunc) ast_class_init,
+		NULL,                                         /* class finalize */
+		NULL,                                         /* class_data */
+		sizeof (AstNode),
+		0,                                           /* n_preallocs */
+		(GInstanceInitFunc) ast_init
+	};
+	static const GInterfaceInfo tree_model_info = {
+		(GInterfaceInitFunc) ast_tree_model_init,
+		NULL,
+		NULL
+	};
+
+
+
+	if (ast_type)
+		return ast_type;
+
+	/* Some boilerplate type registration stuff */
+	ast_type = g_type_register_static(G_TYPE_OBJECT, "AstNode",
+						&ast_info, (GTypeFlags)0);
+
+	/* Here we register our GtkTreeModel interface with the type system */
+	g_type_add_interface_static(ast_type, GTK_TYPE_TREE_MODEL, &tree_model_info);
+
+	return ast_type;
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_class_init: more boilerplate GObject/GType stuff.
+ *                  Init callback for the type system,
+ *                  called once when our new class is created.
+ *
+ *****************************************************************************/
+
+static void
+ast_class_init (AstNodeClass *klass)
+{
+	GObjectClass *object_class;
+
+	parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
+	object_class = (GObjectClass*) klass;
+
+	object_class->finalize = ast_finalize;
+}
+
+/*****************************************************************************
+ *
+ *  ast_tree_model_init: init callback for the interface registration
+ *                       in ast_get_type. Here we override
+ *                       the GtkTreeModel interface functions that
+ *                       we implement.
+ *
+ *****************************************************************************/
+
+static void
+ast_tree_model_init (GtkTreeModelIface *iface)
+{
+	iface->get_flags       = ast_get_flags;
+	iface->get_n_columns   = ast_get_n_columns;
+	iface->get_column_type = ast_get_column_type;
+	iface->get_iter        = ast_get_iter;
+	iface->get_path        = ast_get_path;
+	iface->get_value       = ast_get_value;
+	iface->iter_next       = ast_iter_next;
+	iface->iter_children   = ast_iter_children;
+	iface->iter_has_child  = ast_iter_has_child;
+	iface->iter_n_children = ast_iter_n_children;
+	iface->iter_nth_child  = ast_iter_nth_child;
+	iface->iter_parent     = ast_iter_parent;
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_init: this is called every time a new ast node object
+ *            instance is created (we do that in ast_new).
+ *            Initialise the list structure's fields here.
+ *
+ *****************************************************************************/
+
+static void
+ast_init (AstNode *node)
+{
+	node->childnodes = g_array_new(FALSE, TRUE, sizeof(AstNode *));
+	node->stamp    = g_random_int(); /* Random int to check whether iters belong to out model */
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_finalize: this is called just before an ast node is
+ *                destroyed. Free dynamically allocated memory here.
+ *
+ *****************************************************************************/
+
+static void
+ast_finalize (GObject *object)
+{
+	/*  AstNode *node = AST_NODE(object); */
+
+	/* FIXME: free all node memory */
+
+	/* must chain up - finalize parent */
+	(* parent_class->finalize) (object);
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_get_flags: tells the rest of the world whether our tree model
+ *                 has any special characteristics. In our case,
+ *                 we have a list model (instead of a tree), and each
+ *                 tree iter is valid as long as the row in question
+ *                 exists, as it only contains a pointer to our struct.
+ *
+ *****************************************************************************/
+
+static GtkTreeModelFlags
+ast_get_flags(GtkTreeModel *tree_model)
+{
+	return (GTK_TREE_MODEL_ITERS_PERSIST);
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_get_n_columns: tells the rest of the world how many data
+ *                          columns we export via the tree model interface
+ *
+ *****************************************************************************/
+
+static gint
+ast_get_n_columns(GtkTreeModel *tree_model)
+{
+	return 1;
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_get_column_type: tells the rest of the world which type of
+ *                       data an exported model column contains
+ *
+ *****************************************************************************/
+
+static GType
+ast_get_column_type(GtkTreeModel *tree_model,
+                         gint index)
+{
+	return G_TYPE_STRING;
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_get_iter: converts a tree path (physical position) into a
+ *                tree iter structure (the content of the iter
+ *                fields will only be used internally by our model).
+ *                We simply store a pointer to our AstNodeItem
+ *                structure that represents that row in the tree iter.
+ *
+ *****************************************************************************/
+
+static gboolean
+ast_get_iter(GtkTreeModel *tree_model,
+                  GtkTreeIter  *iter,
+                  GtkTreePath  *path)
+{
+	AstNode    *node;
+	gint          *indices, depth;
+	int i;
+
+	node = AST_NODE(tree_model);
+	indices = gtk_tree_path_get_indices(path);
+	depth   = gtk_tree_path_get_depth(path);
+
+	for (i = 0; i < depth; i++)
+		node = ast_nth_child(node, indices[i]);
+
+	return ast_set_iter(iter, node);
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_get_path: converts a tree iter into a tree path (ie. the
+ *                physical position of that row in the list).
+ *
+ *****************************************************************************/
+
+static GtkTreePath *
+ast_get_path(GtkTreeModel *tree_model,
+                  GtkTreeIter  *iter)
+{
+	GtkTreePath  *path;
+	AstNode   *root = AST_NODE(tree_model);
+	AstNode   *node = AST_NODE(iter->user_data);
+
+	path = gtk_tree_path_new();
+	while (node != root) {
+		gtk_tree_path_prepend_index(path, node->index);
+		node = node->parent;
+	}
+	return path;
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_get_value: Returns a row's exported data columns
+ *                 (_get_value is what gtk_tree_model_get uses)
+ *
+ *****************************************************************************/
+
+static void
+ast_get_value(GtkTreeModel *tree_model,
+                   GtkTreeIter  *iter,
+                   gint          column,
+                   GValue       *value)
+{
+	AstNode    *node = iter->user_data;
+
+	g_assert(AST_IS_NODE(tree_model));
+	if (column != 1)
+		return;
+
+	inspect_child_node(node);
+
+	g_value_init(value, G_TYPE_STRING);
+	g_value_set_string(value, node->text);
+	return;
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_iter_next: Takes an iter structure and sets it to point
+ *                 to the next row.
+ *
+ *****************************************************************************/
+
+static gboolean
+ast_iter_next(GtkTreeModel  *tree_model,
+                   GtkTreeIter   *iter)
+{
+	AstNode    *node = iter->user_data;
+	
+	g_assert(AST_IS_NODE (tree_model));
+
+	node = ast_nth_child(node->parent, node->index + 1);
+	return ast_set_iter(iter, node);
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_iter_children: Returns TRUE or FALSE depending on whether
+ *                     the row specified by 'parent' has any children.
+ *                     If it has children, then 'iter' is set to
+ *                     point to the first child. Special case: if
+ *                     'parent' is NULL, then the first top-level
+ *                     row should be returned if it exists.
+ *
+ *****************************************************************************/
+
+static gboolean
+ast_iter_children(GtkTreeModel *tree_model,
+                       GtkTreeIter  *iter,
+                       GtkTreeIter  *parent)
+{
+	return ast_iter_nth_child(tree_model, iter, parent, 0);
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_iter_has_child: Returns TRUE or FALSE depending on whether
+ *                      the row specified by 'iter' has any children.
+ *                      We only have a list and thus no children.
+ *
+ *****************************************************************************/
+
+static gboolean
+ast_iter_has_child (GtkTreeModel *tree_model,
+                         GtkTreeIter  *iter)
+{
+	AstNode    *node = iter->user_data;
+	inspect_child_node(node);
+	return node->childnodes->len > 0;
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_iter_n_children: Returns the number of children the row
+ *                       specified by 'iter' has. This is usually 0,
+ *                       as we only have a list and thus do not have
+ *                       any children to any rows. A special case is
+ *                       when 'iter' is NULL, in which case we need
+ *                       to return the number of top-level node,
+ *                       ie. the number of rows in our list.
+ *
+ *****************************************************************************/
+
+static gint
+ast_iter_n_children (GtkTreeModel *tree_model,
+                          GtkTreeIter  *iter)
+{
+	AstNode  *node = iter->user_data;
+
+	inspect_child_node(node);
+	return node->childnodes->len;
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_iter_nth_child: If the row specified by 'parent' has any
+ *                      children, set 'iter' to the n-th child and
+ *                      return TRUE if it exists, otherwise FALSE.
+ *                      A special case is when 'parent' is NULL, in
+ *                      which case we need to set 'iter' to the n-th
+ *                      row if it exists.
+ *
+ *****************************************************************************/
+
+static gboolean
+ast_iter_nth_child(GtkTreeModel *tree_model,
+                        GtkTreeIter  *iter,
+                        GtkTreeIter  *parent,
+                        gint          n)
+{
+	AstNode    *node = parent ? parent->user_data : (AstNode*) tree_model;
+	GArray *array = node->childnodes;
+	if (n >= array->len)
+		return FALSE;
+	iter->user_data = g_array_index(array, AstNode *, n);
+	return TRUE;
+}
+
+
+/*****************************************************************************
+ *
+ *  ast_iter_parent: Point 'iter' to the parent node of 'child'. As
+ *                   we have a list and thus no children and no
+ *                   parents of children, we can just return FALSE.
+ *
+ *****************************************************************************/
+
+static gboolean
+ast_iter_parent (GtkTreeModel *tree_model,
+                      GtkTreeIter  *iter,
+                      GtkTreeIter  *child)
+{
+	AstNode *node = (AstNode *) child->user_data;
+	iter->user_data = node->parent;
+	return node->parent != NULL;
+}
+
+
+AstNode *
+ast_new (AstNode *parent, int index, const char *text, void *ptr, void (*inspect)(AstNode*))
+{
+	AstNode *node = (AstNode*) g_object_new (AST_TYPE_NODE, NULL);
+	g_assert(node != NULL);
+	node->parent = parent;
+	node->index = index;
+	node->text = text;
+	node->inspect = inspect;
+	node->ptr = ptr;
+	return node;
+}
+
diff --git a/deps/sparse/ast-model.h b/deps/sparse/ast-model.h
new file mode 100644
index 0000000..650053d
--- /dev/null
+++ b/deps/sparse/ast-model.h
@@ -0,0 +1,90 @@
+
+/*
+ * ast-model.h
+ *
+ * Copyright (C) 2010 Christopher Li.
+ *
+ */
+
+#ifndef _ast_model_h_
+#define _ast_model_h_
+
+#include <stdint.h>
+#include <gtk/gtk.h>
+#include "lib.h"
+
+#define AST_TYPE_NODE                  (ast_get_type ())
+#define AST_NODE(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), AST_TYPE_NODE, AstNode))
+#define AST_NODE_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass),  AST_TYPE_NODE, AstNodeClass))
+#define AST_IS_NODE(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), AST_TYPE_NODE))
+#define AST_IS_NODE_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass),  AST_TYPE_NODE))
+#define AST_NODE_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj),  AST_TYPE_NODE, AstNodeClass))
+
+enum
+{
+	AST_COL_RECORD = 0,
+	AST_COL_NAME,
+	AST_N_COLUMNS,
+} ;
+
+
+typedef struct AstNode AstNode;
+typedef struct AstNodeClass AstNodeClass;
+
+
+
+/* AstNode: this structure contains everything we need for our
+ *             model implementation. You can add extra fields to
+ *             this structure, e.g. hashtables to quickly lookup
+ *             rows or whatever else you might need, but it is
+ *             crucial that 'parent' is the first member of the
+ *             structure.                                          */
+
+struct AstNode
+{
+	GObject         base;      /* this MUST be the first member */
+
+	AstNode	*parent;
+	int index;
+	const gchar *text;
+	void (*inspect)(struct AstNode* node);
+	void *ptr;
+	GArray *childnodes;
+	gint stamp;
+};
+
+
+
+/* AstNodeClass: more boilerplate GObject stuff */
+
+struct AstNodeClass
+{
+	GObjectClass base_class;
+};
+
+
+GType ast_get_type(void);
+AstNode* ast_new(AstNode *parent, int index, const char *prefix, void *ptr, void (*expand)(AstNode*));
+
+
+static inline
+AstNode* ast_append_child(AstNode *parent, const char *text,
+			   void *ptr, void (*inspect)(AstNode*))
+{
+	if (ptr) {
+		AstNode *child = ast_new(parent, parent->childnodes->len,
+						text, ptr, inspect);
+		g_array_append_val(parent->childnodes, child);
+		return child;
+	}
+	return NULL;
+}
+
+static inline
+void ast_append_attribute(AstNode *parent, const char *text)
+{
+	AstNode *child = ast_new(parent, parent->childnodes->len, text, NULL, NULL);
+	g_array_append_val(parent->childnodes, child);
+}
+
+#endif /* _ast_h_*/
diff --git a/deps/sparse/ast-view.c b/deps/sparse/ast-view.c
new file mode 100644
index 0000000..c2b3963
--- /dev/null
+++ b/deps/sparse/ast-view.c
@@ -0,0 +1,48 @@
+
+#include <stdlib.h>
+#include "ast-model.h"
+#include "ast-inspect.h"
+
+GtkWidget *
+create_view_and_model (void *ptr)
+{
+	GtkTreeViewColumn   *text;
+	GtkCellRenderer *renderer;
+	AstNode *root;
+	GtkWidget *view;
+
+	root = ast_new(NULL, 0, "", ptr, inspect_symbol_list);
+
+	view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(root));
+
+	g_object_unref(root); /* destroy store automatically with view */
+
+	renderer = gtk_cell_renderer_text_new();
+	text = gtk_tree_view_column_new_with_attributes("Node", renderer,
+						       "text", AST_COL_NAME,
+						       NULL);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(view), text);
+
+	return view;
+}
+
+void
+treeview_main (struct symbol_list *syms)
+{
+	GtkWidget *window, *view, *scrollwin;
+
+	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_default_size (GTK_WINDOW(window), 600, 800);
+	g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
+
+	scrollwin = gtk_scrolled_window_new(NULL,NULL);
+
+	view = create_view_and_model(syms);
+
+	gtk_container_add(GTK_CONTAINER(scrollwin), view);
+	gtk_container_add(GTK_CONTAINER(window), scrollwin);
+
+	gtk_widget_show_all(window);
+
+	gtk_main();
+}
diff --git a/deps/sparse/ast-view.h b/deps/sparse/ast-view.h
new file mode 100644
index 0000000..da8f5f5
--- /dev/null
+++ b/deps/sparse/ast-view.h
@@ -0,0 +1,7 @@
+
+#include <gtk/gtk.h>
+#include "lib.h"
+
+extern void treeview_main(struct symbol_list *syms);
+
+
diff --git a/deps/sparse/bitmap.h b/deps/sparse/bitmap.h
new file mode 100644
index 0000000..4d81ffc
--- /dev/null
+++ b/deps/sparse/bitmap.h
@@ -0,0 +1,51 @@
+#ifndef BITMAP_H
+#define BITMAP_H
+
+#define BITS_IN_LONG	(sizeof(unsigned long)*8)
+#define LONGS(x)	((x + BITS_IN_LONG - 1) & -BITS_IN_LONG)
+
+/* Every bitmap gets its own type */
+#define DECLARE_BITMAP(name, x) unsigned long name[LONGS(x)]
+
+static inline int test_bit(unsigned int nr, unsigned long *bitmap)
+{
+	unsigned long offset = nr / BITS_IN_LONG;
+	unsigned long bit = nr & (BITS_IN_LONG-1);
+	return (bitmap[offset] >> bit) & 1;
+}
+
+static inline void set_bit(unsigned int nr, unsigned long *bitmap)
+{
+	unsigned long offset = nr / BITS_IN_LONG;
+	unsigned long bit = nr & (BITS_IN_LONG-1);
+	bitmap[offset] |= 1UL << bit;
+}
+
+static inline void clear_bit(unsigned int nr, unsigned long *bitmap)
+{
+	unsigned long offset = nr / BITS_IN_LONG;
+	unsigned long bit = nr & (BITS_IN_LONG-1);
+	bitmap[offset] &= ~(1UL << bit);
+}
+
+static inline int test_and_set_bit(unsigned int nr, unsigned long *bitmap)
+{
+	unsigned long offset = nr / BITS_IN_LONG;
+	unsigned long bit = nr & (BITS_IN_LONG-1);
+	unsigned long old = bitmap[offset];
+	unsigned long mask = 1UL << bit;
+	bitmap[offset] = old | mask;
+	return (old & mask) != 0;
+}
+
+static inline int test_and_clear_bit(unsigned int nr, unsigned long *bitmap)
+{
+	unsigned long offset = nr / BITS_IN_LONG;
+	unsigned long bit = nr & (BITS_IN_LONG-1);
+	unsigned long old = bitmap[offset];
+	unsigned long mask = 1UL << bit;
+	bitmap[offset] = old & ~mask;
+	return (old & mask) != 0;
+}
+
+#endif /* BITMAP_H */
diff --git a/deps/sparse/c2xml.c b/deps/sparse/c2xml.c
new file mode 100644
index 0000000..af995b1
--- /dev/null
+++ b/deps/sparse/c2xml.c
@@ -0,0 +1,317 @@
+/*
+ * Sparse c2xml
+ *
+ * Dumps the parse tree as an xml document
+ *
+ * Copyright (C) 2007 Rob Taylor
+ *
+ * Licensed under the Open Software License version 1.1
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include "expression.h"
+#include "parse.h"
+#include "scope.h"
+#include "symbol.h"
+
+static xmlDocPtr doc = NULL;       /* document pointer */
+static xmlNodePtr root_node = NULL;/* root node pointer */
+static int idcount = 0;
+
+static void examine_symbol(struct symbol *sym, xmlNodePtr node);
+
+static xmlAttrPtr newProp(xmlNodePtr node, const char *name, const char *value)
+{
+	return xmlNewProp(node, BAD_CAST name, BAD_CAST value);
+}
+
+static xmlAttrPtr newNumProp(xmlNodePtr node, const char *name, int value)
+{
+	char buf[256];
+	snprintf(buf, 256, "%d", value);
+	return newProp(node, name, buf);
+}
+
+static xmlAttrPtr newIdProp(xmlNodePtr node, const char *name, unsigned int id)
+{
+	char buf[256];
+	snprintf(buf, 256, "_%d", id);
+	return newProp(node, name, buf);
+}
+
+static xmlNodePtr new_sym_node(struct symbol *sym, const char *name, xmlNodePtr parent)
+{
+	xmlNodePtr node;
+	const char *ident = show_ident(sym->ident);
+
+	assert(name != NULL);
+	assert(sym != NULL);
+	assert(parent != NULL);
+
+	node = xmlNewChild(parent, NULL, BAD_CAST "symbol", NULL);
+
+	newProp(node, "type", name);
+
+	newIdProp(node, "id", idcount);
+
+	if (sym->ident && ident)
+		newProp(node, "ident", ident);
+	newProp(node, "file", stream_name(sym->pos.stream));
+
+	newNumProp(node, "start-line", sym->pos.line);
+	newNumProp(node, "start-col", sym->pos.pos);
+
+	if (sym->endpos.type) {
+		newNumProp(node, "end-line", sym->endpos.line);
+		newNumProp(node, "end-col", sym->endpos.pos);
+		if (sym->pos.stream != sym->endpos.stream)
+			newProp(node, "end-file", stream_name(sym->endpos.stream));
+        }
+	sym->aux = node;
+
+	idcount++;
+
+	return node;
+}
+
+static inline void examine_members(struct symbol_list *list, xmlNodePtr node)
+{
+	struct symbol *sym;
+
+	FOR_EACH_PTR(list, sym) {
+		examine_symbol(sym, node);
+	} END_FOR_EACH_PTR(sym);
+}
+
+static void examine_modifiers(struct symbol *sym, xmlNodePtr node)
+{
+	const char *modifiers[] = {
+			"auto",
+			"register",
+			"static",
+			"extern",
+			"const",
+			"volatile",
+			"signed",
+			"unsigned",
+			"char",
+			"short",
+			"long",
+			"long-long",
+			"typedef",
+			NULL,
+			NULL,
+			NULL,
+			NULL,
+			NULL,
+			"inline",
+			"addressable",
+			"nocast",
+			"noderef",
+			"accessed",
+			"toplevel",
+			"label",
+			"assigned",
+			"type-type",
+			"safe",
+			"user-type",
+			"force",
+			"explicitly-signed",
+			"bitwise"};
+
+	int i;
+
+	if (sym->namespace != NS_SYMBOL)
+		return;
+
+	/*iterate over the 32 bit bitfield*/
+	for (i=0; i < 32; i++) {
+		if ((sym->ctype.modifiers & 1<<i) && modifiers[i])
+			newProp(node, modifiers[i], "1");
+	}
+}
+
+static void
+examine_layout(struct symbol *sym, xmlNodePtr node)
+{
+	examine_symbol_type(sym);
+
+	newNumProp(node, "bit-size", sym->bit_size);
+	newNumProp(node, "alignment", sym->ctype.alignment);
+	newNumProp(node, "offset", sym->offset);
+	if (is_bitfield_type(sym)) {
+		newNumProp(node, "bit-offset", sym->bit_offset);
+	}
+}
+
+static void examine_symbol(struct symbol *sym, xmlNodePtr node)
+{
+	xmlNodePtr child = NULL;
+	const char *base;
+	int array_size;
+
+	if (!sym)
+		return;
+	if (sym->aux)		/*already visited */
+		return;
+
+	if (sym->ident && sym->ident->reserved)
+		return;
+
+	child = new_sym_node(sym, get_type_name(sym->type), node);
+	examine_modifiers(sym, child);
+	examine_layout(sym, child);
+
+	if (sym->ctype.base_type) {
+		if ((base = builtin_typename(sym->ctype.base_type)) == NULL) {
+			if (!sym->ctype.base_type->aux) {
+				examine_symbol(sym->ctype.base_type, root_node);
+			}
+			xmlNewProp(child, BAD_CAST "base-type",
+			           xmlGetProp((xmlNodePtr)sym->ctype.base_type->aux, BAD_CAST "id"));
+		} else {
+			newProp(child, "base-type-builtin", base);
+		}
+	}
+	if (sym->array_size) {
+		/* TODO: modify get_expression_value to give error return */
+		array_size = get_expression_value(sym->array_size);
+		newNumProp(child, "array-size", array_size);
+	}
+
+
+	switch (sym->type) {
+	case SYM_STRUCT:
+	case SYM_UNION:
+		examine_members(sym->symbol_list, child);
+		break;
+	case SYM_FN:
+		examine_members(sym->arguments, child);
+		break;
+	case SYM_UNINITIALIZED:
+		newProp(child, "base-type-builtin", builtin_typename(sym));
+		break;
+	}
+	return;
+}
+
+static struct position *get_expansion_end (struct token *token)
+{
+	struct token *p1, *p2;
+
+	for (p1=NULL, p2=NULL;
+	     !eof_token(token);
+	     p2 = p1, p1 = token, token = token->next);
+
+	if (p2)
+		return &(p2->pos);
+	else
+		return NULL;
+}
+
+static void examine_macro(struct symbol *sym, xmlNodePtr node)
+{
+	struct position *pos;
+
+	/* this should probably go in the main codebase*/
+	pos = get_expansion_end(sym->expansion);
+	if (pos)
+		sym->endpos = *pos;
+	else
+		sym->endpos = sym->pos;
+
+	new_sym_node(sym, "macro", node);
+}
+
+static void examine_namespace(struct symbol *sym)
+{
+	if (sym->ident && sym->ident->reserved)
+		return;
+
+	switch(sym->namespace) {
+	case NS_MACRO:
+		examine_macro(sym, root_node);
+		break;
+	case NS_TYPEDEF:
+	case NS_STRUCT:
+	case NS_SYMBOL:
+		examine_symbol(sym, root_node);
+		break;
+	case NS_NONE:
+	case NS_LABEL:
+	case NS_ITERATOR:
+	case NS_UNDEF:
+	case NS_PREPROCESSOR:
+	case NS_KEYWORD:
+		break;
+	default:
+		die("Unrecognised namespace type %d",sym->namespace);
+	}
+
+}
+
+static int get_stream_id (const char *name)
+{
+	int i;
+	for (i=0; i<input_stream_nr; i++) {
+		if (strcmp(name, stream_name(i))==0)
+			return i;
+	}
+	return -1;
+}
+
+static inline void examine_symbol_list(const char *file, struct symbol_list *list)
+{
+	struct symbol *sym;
+	int stream_id = get_stream_id (file);
+
+	if (!list)
+		return;
+	FOR_EACH_PTR(list, sym) {
+		if (sym->pos.stream == stream_id)
+			examine_namespace(sym);
+	} END_FOR_EACH_PTR(sym);
+}
+
+int main(int argc, char **argv)
+{
+	struct string_list *filelist = NULL;
+	struct symbol_list *symlist = NULL;
+	char *file;
+
+	doc = xmlNewDoc(BAD_CAST "1.0");
+	root_node = xmlNewNode(NULL, BAD_CAST "parse");
+	xmlDocSetRootElement(doc, root_node);
+
+/* - A DTD is probably unnecessary for something like this
+
+	dtd = xmlCreateIntSubset(doc, "parse", "http://www.kernel.org/pub/software/devel/sparse/parse.dtd"; NULL, "parse.dtd");
+
+	ns = xmlNewNs (root_node, "http://www.kernel.org/pub/software/devel/sparse/parse.dtd";, NULL);
+
+	xmlSetNs(root_node, ns);
+*/
+	symlist = sparse_initialize(argc, argv, &filelist);
+
+	FOR_EACH_PTR_NOTAG(filelist, file) {
+		examine_symbol_list(file, symlist);
+		sparse_keep_tokens(file);
+		examine_symbol_list(file, file_scope->symbols);
+		examine_symbol_list(file, global_scope->symbols);
+	} END_FOR_EACH_PTR_NOTAG(file);
+
+
+	xmlSaveFormatFileEnc("-", doc, "UTF-8", 1);
+	xmlFreeDoc(doc);
+	xmlCleanupParser();
+
+	return 0;
+}
+
diff --git a/deps/sparse/cgcc b/deps/sparse/cgcc
new file mode 100755
index 0000000..6636cc6
--- /dev/null
+++ b/deps/sparse/cgcc
@@ -0,0 +1,287 @@
+#!/usr/bin/perl -w
+# -----------------------------------------------------------------------------
+
+my $cc = $ENV{'REAL_CC'} || 'cc';
+my $check = $ENV{'CHECK'} || 'sparse';
+
+my $m32 = 0;
+my $m64 = 0;
+my $has_specs = 0;
+my $gendeps = 0;
+my $do_check = 0;
+my $do_compile = 1;
+my $gcc_base_dir;
+my $verbose = 0;
+
+while (@ARGV) {
+    $_ = shift(@ARGV);
+    # Look for a .c file.  We don't want to run the checker on .o or .so files
+    # in the link run.  (This simplistic check knows nothing about options
+    # with arguments, but it seems to do the job.)
+    $do_check = 1 if /^[^-].*\.c$/;
+
+    # Ditto for stdin.
+    $do_check = 1 if $_ eq '-';
+
+    $m32 = 1 if /^-m32$/;
+    $m64 = 1 if /^-m64$/;
+    $gendeps = 1 if /^-M$/;
+
+    if (/^-target=(.*)$/) {
+	$check .= &add_specs ($1);
+	$has_specs = 1;
+	next;
+    }
+
+    if ($_ eq '-no-compile') {
+	$do_compile = 0;
+	next;
+    }
+
+    if (/^-gcc-base-dir$/) {
+        $gcc_base_dir = shift @ARGV;
+        die ("$0: missing argument for -gcc-base-dir option") if !$gcc_base_dir;
+        next;
+    }
+
+    # If someone adds "-E", don't pre-process twice.
+    $do_compile = 0 if $_ eq '-E';
+
+    $verbose = 1 if $_ eq '-v';
+
+    my $this_arg = ' ' . &quote_arg ($_);
+    $cc .= $this_arg unless &check_only_option ($_);
+    $check .= $this_arg;
+}
+
+if ($gendeps) {
+    $do_compile = 1;
+    $do_check = 0;
+}
+
+if ($do_check) {
+    if (!$has_specs) {
+	$check .= &add_specs ('host_arch_specs');
+	$check .= &add_specs ('host_os_specs');
+    }
+
+    $gcc_base_dir = qx($cc -print-file-name=) if !$gcc_base_dir;
+    $check .= " -gcc-base-dir " . $gcc_base_dir if $gcc_base_dir;
+
+    print "$check\n" if $verbose;
+    if ($do_compile) {
+	system ($check);
+    } else {
+	exec ($check);
+    }
+}
+
+if ($do_compile) {
+    print "$cc\n" if $verbose;
+    exec ($cc);
+}
+
+exit 0;
+
+# -----------------------------------------------------------------------------
+# Check if an option is for "check" only.
+
+sub check_only_option {
+    my ($arg) = @_;
+    return 1 if $arg =~ /^-W(no-?)?(default-bitfield-sign|one-bit-signed-bitfield|cast-truncate|bitwise|typesign|context|undef|ptr-subtraction-blows|cast-to-as|decl|transparent-union|address-space|enum-mismatch|do-while|old-initializer|non-pointer-null|paren-string|return-void|designated-init|sparse-all)$/;
+    return 1 if $arg =~ /^-v(no-?)?(entry|dead)$/;
+    return 0;
+}
+
+# -----------------------------------------------------------------------------
+# Simple arg-quoting function.  Just adds backslashes when needed.
+
+sub quote_arg {
+    my ($arg) = @_;
+    return "''" if $arg eq '';
+    return join ('',
+		 map {
+		     m|^[-a-zA-Z0-9._/,=]+$| ? $_ : "\\" . $_;
+		 } (split (//, $arg)));
+}
+
+# -----------------------------------------------------------------------------
+
+sub integer_types {
+    my ($char,@dummy) = @_;
+
+    my %pow2m1 =
+	(8 => '127',
+	 16 => '32767',
+	 32 => '2147483647',
+	 64 => '9223372036854775807',
+	 128 => '170141183460469231731687303715884105727',
+	 );
+    my @types = (['SCHAR',''], ['SHRT',''], ['INT',''], ['LONG','L'], ['LONG_LONG','LL'], ['LONG_LONG_LONG','LLL']);
+
+    my $result = " -D__CHAR_BIT__=$char";
+    while (@types && @_) {
+	my $bits = shift @_;
+	my ($name,$suffix) = @{ shift @types };
+	die "$0: weird number of bits." unless exists $pow2m1{$bits};
+	$result .= " -D__${name}_MAX__=" . $pow2m1{$bits} . $suffix;
+    }
+    return $result;
+}
+
+# -----------------------------------------------------------------------------
+
+sub float_types {
+    my ($has_inf,$has_qnan,$dec_dig,@bitsizes) = @_;
+    my $result = " -D__FLT_RADIX__=2";
+    $result .= " -D__FINITE_MATH_ONLY__=" . ($has_inf || $has_qnan ? '0' : '1');
+    $result .= " -D__DECIMAL_DIG__=$dec_dig";
+
+    my %constants =
+	(24 =>
+	 {
+	     'MIN' => '1.17549435e-38',
+	     'MAX' => '3.40282347e+38',
+	     'EPSILON' => '1.19209290e-7',
+	     'DENORM_MIN' => '1.40129846e-45',
+	 },
+	 53 =>
+	 {
+	     'MIN' => '2.2250738585072014e-308',
+	     'MAX' => '1.7976931348623157e+308',
+	     'EPSILON' => '2.2204460492503131e-16',
+	     'DENORM_MIN' => '4.9406564584124654e-324',
+	 },
+	 64 =>
+	 {
+	     'MIN' => '3.36210314311209350626e-4932',
+	     'MAX' => '1.18973149535723176502e+4932',
+	     'EPSILON' => '1.08420217248550443401e-19',
+	     'DENORM_MIN' => '3.64519953188247460253e-4951',
+	 },
+	 113 =>
+	 {
+	     'MIN' => '3.36210314311209350626267781732175260e-4932',
+	     'MAX' => '1.18973149535723176508575932662800702e+4932',
+	     'EPSILON' => '1.92592994438723585305597794258492732e-34',
+	     'DENORM_MIN' => '6.47517511943802511092443895822764655e-4966',
+	 },
+	 );	     
+
+    my @types = (['FLT','F'], ['DBL',''], ['LDBL','L']);
+    while (@types) {
+	my ($mant_bits,$exp_bits) = @{ shift @bitsizes };
+	my ($name,$suffix) = @{ shift @types };
+
+	my $h = $constants{$mant_bits};
+	die "$0: weird number of mantissa bits." unless $h;
+
+	my $mant_dig = int (($mant_bits - 1) * log (2) / log (10));
+	my $max_exp = 1 << ($exp_bits - 1);
+	my $min_exp = 3 - $max_exp;
+	my $max_10_exp = int ($max_exp * log (2) / log (10));
+	my $min_10_exp = -int (-$min_exp * log (2) / log (10));
+
+	$result .= " -D__${name}_MANT_DIG__=$mant_bits";
+	$result .= " -D__${name}_DIG__=$mant_dig";
+	$result .= " -D__${name}_MIN_EXP__='($min_exp)'";
+	$result .= " -D__${name}_MAX_EXP__=$max_exp";
+	$result .= " -D__${name}_MIN_10_EXP__='($min_10_exp)'";
+	$result .= " -D__${name}_MAX_10_EXP__=$max_10_exp";
+	$result .= " -D__${name}_HAS_INFINITY__=" . ($has_inf ? '1' : '0');
+	$result .= " -D__${name}_HAS_QUIET_NAN__=" . ($has_qnan ? '1' : '0');;
+
+	foreach my $inf (sort keys %$h) {
+	    $result .= " -D__${name}_${inf}__=" . $h->{$inf} . $suffix;
+	}
+    }
+    return $result;
+}
+
+# -----------------------------------------------------------------------------
+
+sub define_size_t {
+    my ($text) = @_;
+    # We have to undef in order to override check's internal definition.
+    return ' -U__SIZE_TYPE__ ' . &quote_arg ("-D__SIZE_TYPE__=$text");
+}
+
+# -----------------------------------------------------------------------------
+
+sub add_specs {
+    my ($spec) = @_;
+    if ($spec eq 'sunos') {
+	return &add_specs ('unix') .
+	    ' -D__sun__=1 -D__sun=1 -Dsun=1' .
+	    ' -D__svr4__=1 -DSVR4=1' .
+	    ' -D__STDC__=0' .
+	    ' -D_REENTRANT' .
+	    ' -D_SOLARIS_THREADS' .
+	    ' -DNULL="((void *)0)"';
+    } elsif ($spec eq 'linux') {
+	return &add_specs ('unix') .
+	    ' -D__linux__=1 -D__linux=1 -Dlinux=linux';
+    } elsif ($spec eq 'openbsd') {
+	return &add_specs ('unix') .
+	    ' -D__OpenBSD__=1';
+    } elsif ($spec eq 'unix') {
+	return ' -Dunix=1 -D__unix=1 -D__unix__=1';
+    } elsif ( $spec =~ /^cygwin/) {
+	return &add_specs ('unix') .
+	    ' -D__CYGWIN__=1 -D__CYGWIN32__=1' .
+	    " -D'_cdecl=__attribute__((__cdecl__))'" .
+	    " -D'__cdecl=__attribute__((__cdecl__))'" .
+	    " -D'_stdcall=__attribute__((__stdcall__))'" .
+	    " -D'__stdcall=__attribute__((__stdcall__))'" .
+	    " -D'_fastcall=__attribute__((__fastcall__))'" .
+	    " -D'__fastcall=__attribute__((__fastcall__))'" .
+	    " -D'__declspec(x)=__attribute__((x))'";
+    } elsif ($spec eq 'i86') {
+	return (' -Di386=1 -D__i386=1 -D__i386__=1' .
+		&integer_types (8, 16, 32, $m64 ? 64 : 32, 64) .
+		&float_types (1, 1, 21, [24,8], [53,11], [64,15]) .
+		&define_size_t ($m64 ? "long unsigned int" : "unsigned int"));
+    } elsif ($spec eq 'sparc') {
+	return (' -Dsparc=1 -D__sparc=1 -D__sparc__=1' .
+		&integer_types (8, 16, 32, $m64 ? 64 : 32, 64) .
+		&float_types (1, 1, 33, [24,8], [53,11], [113,15]) .
+		&define_size_t ($m64 ? "long unsigned int" : "unsigned int"));
+    } elsif ($spec eq 'sparc64') {
+	return (' -Dsparc=1 -D__sparc=1 -D__sparc__=1 -D__sparcv9__=1 -D__sparc64__=1 -D__arch64__=1 -D__LP64__=1' .
+		&integer_types (8, 16, 32, 64, 64, 128) .
+		&float_types (1, 1, 33, [24,8], [53,11], [113,15]) .
+		&define_size_t ("long unsigned int"));
+    } elsif ($spec eq 'x86_64') {
+	return (' -Dx86_64=1 -D__x86_64=1 -D__x86_64__=1' . ($m32 ? '' : ' -D__LP64__=1') .
+		&integer_types (8, 16, 32, $m32 ? 32 : 64, 64, 128) .
+		&float_types (1, 1, 33, [24,8], [53,11], [113,15]) .
+		&define_size_t ($m32 ? "unsigned int" : "long unsigned int"));
+    } elsif ($spec eq 'ppc') {
+	return (' -D__powerpc__=1 -D_BIG_ENDIAN -D_STRING_ARCH_unaligned=1' .
+		&integer_types (8, 16, 32, $m64 ? 64 : 32, 64) .
+		&float_types (1, 1, 21, [24,8], [53,11], [113,15]) .
+		&define_size_t ($m64 ? "long unsigned int" : "unsigned int"));
+    } elsif ($spec eq 'host_os_specs') {
+	my $os = `uname -s`;
+	chomp $os;
+	return &add_specs (lc $os);
+    } elsif ($spec eq 'host_arch_specs') {
+	my $arch = `uname -m`;
+	chomp $arch;
+	if ($arch =~ /^(i.?86|athlon)$/i) {
+	    return &add_specs ('i86');
+	} elsif ($arch =~ /^(sun4u)$/i) {
+	    return &add_specs ('sparc');
+	} elsif ($arch =~ /^(x86_64)$/i) {
+	    return &add_specs ('x86_64');
+	} elsif ($arch =~ /^(ppc)$/i) {
+	    return &add_specs ('ppc');
+	} elsif ($arch =~ /^(sparc64)$/i) {
+	    return &add_specs ('sparc64');
+	}
+    } else {
+	die "$0: invalid specs: $spec\n";
+    }
+}
+
+# -----------------------------------------------------------------------------
diff --git a/deps/sparse/cgcc.1 b/deps/sparse/cgcc.1
new file mode 100644
index 0000000..227c02b
--- /dev/null
+++ b/deps/sparse/cgcc.1
@@ -0,0 +1,37 @@
+.\" cgcc manpage by Josh Triplett
+.TH cgcc "1"
+.
+.SH NAME
+cgcc \- Compiler wrapper to run Sparse after compiling
+.
+.SH SYNOPSIS
+.B cgcc
+[\fISPARSE OPTIONS\fR]... [\fICOMPILER OPTIONS\fR]... [\fIINPUT FILES\fR]...
+.br
+.B make CC=cgcc
+.
+.SH DESCRIPTION
+\fBcgcc\fR provides a wrapper around a C compiler (\fBcc\fR by
+default) which also invokes the Sparse static analysis tool.
+.P
+\fBcgcc\fR accepts all Sparse command-line options, such as warning
+options, and passes all other options through to the compiler.
+.P
+By providing the same interface as the C compiler, \fBcgcc\fR allows
+projects to run Sparse as part of their build without modifying their
+build system, by using \fBcgcc\fR as the compiler.  For many projects,
+setting \fBCC=cgcc\fR on the \fBmake\fR command-line will work.
+.
+.SH ENVIRONMENT
+.TP
+.B REAL_CC
+If set, \fBcgcc\fR will use this as the compiler to invoke, rather
+than the default \fBcc\fR.
+.
+.TP
+.B CHECK
+If set, \fBcgcc\fR will use this as the Sparse program to invoke,
+rather than the default \fBsparse\fR.
+.
+.SH SEE ALSO
+.BR sparse (1)
diff --git a/deps/sparse/compat-bsd.c b/deps/sparse/compat-bsd.c
new file mode 100644
index 0000000..4f3c8c0
--- /dev/null
+++ b/deps/sparse/compat-bsd.c
@@ -0,0 +1,20 @@
+/*
+ * BSD Compatibility functions
+ *
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+
+#include "compat/mmap-blob.c"
+
+long double string_to_ld(const char *nptr, char **endptr)
+{
+	return strtod(nptr, endptr);
+}
diff --git a/deps/sparse/compat-cygwin.c b/deps/sparse/compat-cygwin.c
new file mode 100644
index 0000000..e65b538
--- /dev/null
+++ b/deps/sparse/compat-cygwin.c
@@ -0,0 +1,40 @@
+/*	
+ * Cygwin Compatibility functions	
+ *	
+ *	
+ *  Licensed under the Open Software License version 1.1	
+ */	
+	
+	
+	
+#include <sys/mman.h>	
+#include <stdlib.h>	
+#include <string.h>	
+#include <sys/stat.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+	
+void *blob_alloc(unsigned long size)	
+{	
+	void *ptr;	
+	size = (size + 4095) & ~4095;	
+	ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);	
+	if (ptr == MAP_FAILED)	
+		ptr = NULL;	
+	else	
+		memset(ptr, 0, size);	
+	return ptr;	
+}	
+	
+void blob_free(void *addr, unsigned long size)	
+{	
+	size = (size + 4095) & ~4095;	
+	munmap(addr, size);	
+}	
+	
+long double string_to_ld(const char *nptr, char **endptr) 	
+{	
+	return strtod(nptr, endptr);	
+}
diff --git a/deps/sparse/compat-linux.c b/deps/sparse/compat-linux.c
new file mode 100644
index 0000000..a7a6140
--- /dev/null
+++ b/deps/sparse/compat-linux.c
@@ -0,0 +1,7 @@
+#define _GNU_SOURCE
+
+#include "lib.h"
+#include "allocate.h"
+
+#include "compat/mmap-blob.c"
+#include "compat/strtold.c"
diff --git a/deps/sparse/compat-mingw.c b/deps/sparse/compat-mingw.c
new file mode 100644
index 0000000..c978e04
--- /dev/null
+++ b/deps/sparse/compat-mingw.c
@@ -0,0 +1,37 @@
+/*	
+ * MinGW Compatibility functions	
+ *	
+ *	
+ *  Licensed under the Open Software License version 1.1	
+ */	
+	
+	
+	
+#include <stdarg.h>	
+#include <windef.h>	
+#include <winbase.h>	
+#include <stdlib.h>	
+#include <string.h>	
+	
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+	
+void *blob_alloc(unsigned long size)	
+{	
+	void *ptr;	
+	ptr = malloc(size);	
+	if (ptr != NULL)	
+		memset(ptr, 0, size);	
+	return ptr;	
+}	
+	
+void blob_free(void *addr, unsigned long size)	
+{	
+	free(addr);	
+}	
+	
+long double string_to_ld(const char *nptr, char **endptr) 	
+{	
+	return strtod(nptr, endptr);	
+}
diff --git a/deps/sparse/compat-solaris.c b/deps/sparse/compat-solaris.c
new file mode 100644
index 0000000..7253a89
--- /dev/null
+++ b/deps/sparse/compat-solaris.c
@@ -0,0 +1,33 @@
+#include "lib.h"
+#include "allocate.h"
+
+#include "compat/mmap-blob.c"
+
+#include <floatingpoint.h>
+#include <limits.h>
+#include <errno.h>
+
+long double string_to_ld(const char *str, char **endptr)
+{
+	long double res;
+	decimal_record dr;
+	enum decimal_string_form form;
+	decimal_mode dm;
+	fp_exception_field_type excp;
+	char *echar;
+
+	string_to_decimal ((char **)&str, INT_MAX, 0,
+			   &dr, &form, &echar);
+	if (endptr) *endptr = (char *)str;
+
+	if (form == invalid_form) {
+		errno = EINVAL;
+		return 0.0;
+	}
+
+	dm.rd = fp_nearest;
+	decimal_to_quadruple (&res, &dm, &dr, &excp);
+        if (excp & ((1 << fp_overflow) | (1 << fp_underflow)))
+                errno = ERANGE;
+	return res;
+}
diff --git a/deps/sparse/compat.h b/deps/sparse/compat.h
new file mode 100644
index 0000000..9814ae3
--- /dev/null
+++ b/deps/sparse/compat.h
@@ -0,0 +1,28 @@
+#ifndef COMPAT_H
+#define COMPAT_H
+
+/*
+ * Various systems get these things wrong. So
+ * we create a small compat library for them.
+ *
+ *  - zeroed anonymous mmap
+ *	Missing in MinGW
+ *  - "string to long double" (C99 strtold())
+ *	Missing in Solaris and MinGW
+ */
+struct stream;
+struct stat;
+
+/*
+ * Our "blob" allocator works on chunks that are multiples
+ * of this size (the underlying allocator may be a mmap that
+ * cannot handle smaller chunks, for example, so trying to
+ * allocate blobs that aren't aligned is not going to work).
+ */
+#define CHUNK 32768
+
+void *blob_alloc(unsigned long size);
+void blob_free(void *addr, unsigned long size);
+long double string_to_ld(const char *nptr, char **endptr);
+
+#endif
diff --git a/deps/sparse/compat/mmap-blob.c b/deps/sparse/compat/mmap-blob.c
new file mode 100644
index 0000000..1cab4de
--- /dev/null
+++ b/deps/sparse/compat/mmap-blob.c
@@ -0,0 +1,37 @@
+#include <sys/mman.h>
+#include <sys/types.h>
+
+/*
+ * Allow old BSD naming too, it would be a pity to have to make a
+ * separate file just for this.
+ */
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+/*
+ * Our blob allocator enforces the strict CHUNK size
+ * requirement, as a portability check.
+ */
+void *blob_alloc(unsigned long size)
+{
+	void *ptr;
+
+	if (size & ~CHUNK)
+		die("internal error: bad allocation size (%lu bytes)", size);
+	ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	if (ptr == MAP_FAILED)
+		ptr = NULL;
+	return ptr;
+}
+
+void blob_free(void *addr, unsigned long size)
+{
+	if (!size || (size & ~CHUNK) || ((unsigned long) addr & 512))
+		die("internal error: bad blob free (%lu bytes at %p)", size, addr);
+#ifndef DEBUG
+	munmap(addr, size);
+#else
+	mprotect(addr, size, PROT_NONE);
+#endif
+}
diff --git a/deps/sparse/compat/strtold.c b/deps/sparse/compat/strtold.c
new file mode 100644
index 0000000..1b6ad7d
--- /dev/null
+++ b/deps/sparse/compat/strtold.c
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+
+long double string_to_ld(const char *nptr, char **endptr)
+{
+	return strtold(nptr, endptr);
+}
diff --git a/deps/sparse/compile-i386.c b/deps/sparse/compile-i386.c
new file mode 100644
index 0000000..da3ee49
--- /dev/null
+++ b/deps/sparse/compile-i386.c
@@ -0,0 +1,2391 @@
+/*
+ * sparse/compile-i386.c
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003 Linus Torvalds
+ * Copyright 2003 Jeff Garzik
+ *
+ * Licensed under the Open Software License version 1.1
+ *
+ * x86 backend
+ *
+ * TODO list:
+ * in general, any non-32bit SYM_BASETYPE is unlikely to work.
+ * complex initializers
+ * bitfields
+ * global struct/union variables
+ * addressing structures, and members of structures (as opposed to
+ *     scalars) on the stack.  Requires smarter stack frame allocation.
+ * labels / goto
+ * any function argument that isn't 32 bits (or promoted to such)
+ * inline asm
+ * floating point
+ *
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "scope.h"
+#include "expression.h"
+#include "target.h"
+#include "compile.h"
+#include "bitmap.h"
+
+struct textbuf {
+	unsigned int	len;	/* does NOT include terminating null */
+	char		*text;
+	struct textbuf	*next;
+	struct textbuf	*prev;
+};
+
+struct loop_stack {
+	int		continue_lbl;
+	int		loop_bottom_lbl;
+	struct loop_stack *next;
+};
+
+struct atom;
+struct storage;
+DECLARE_PTR_LIST(str_list, struct atom);
+DECLARE_PTR_LIST(atom_list, struct atom);
+DECLARE_PTR_LIST(storage_list, struct storage);
+
+struct function {
+	int stack_size;
+	int pseudo_nr;
+	struct storage_list *pseudo_list;
+	struct atom_list *atom_list;
+	struct str_list *str_list;
+	struct loop_stack *loop_stack;
+	struct symbol **argv;
+	unsigned int argc;
+	int ret_target;
+};
+
+enum storage_type {
+	STOR_PSEUDO,	/* variable stored on the stack */
+	STOR_ARG,	/* function argument */
+	STOR_SYM,	/* a symbol we can directly ref in the asm */
+	STOR_REG,	/* scratch register */
+	STOR_VALUE,	/* integer constant */
+	STOR_LABEL,	/* label / jump target */
+	STOR_LABELSYM,	/* label generated from symbol's pointer value */
+};
+
+struct reg_info {
+	const char	*name;
+	struct storage	*contains;
+	const unsigned char aliases[12];
+#define own_regno aliases[0]
+};
+
+struct storage {
+	enum storage_type type;
+	unsigned long flags;
+
+	/* STOR_REG */
+	struct reg_info *reg;
+	struct symbol *ctype;
+
+	union {
+		/* STOR_PSEUDO */
+		struct {
+			int pseudo;
+			int offset;
+			int size;
+		};
+		/* STOR_ARG */
+		struct {
+			int idx;
+		};
+		/* STOR_SYM */
+		struct {
+			struct symbol *sym;
+		};
+		/* STOR_VALUE */
+		struct {
+			long long value;
+		};
+		/* STOR_LABEL */
+		struct {
+			int label;
+		};
+		/* STOR_LABELSYM */
+		struct {
+			struct symbol *labelsym;
+		};
+	};
+};
+
+enum {
+	STOR_LABEL_VAL	= (1 << 0),
+	STOR_WANTS_FREE	= (1 << 1),
+};
+
+struct symbol_private {
+	struct storage *addr;
+};
+
+enum atom_type {
+	ATOM_TEXT,
+	ATOM_INSN,
+	ATOM_CSTR,
+};
+
+struct atom {
+	enum atom_type type;
+	union {
+		/* stuff for text */
+		struct {
+			char *text;
+			unsigned int text_len;  /* w/o terminating null */
+		};
+
+		/* stuff for insns */
+		struct {
+			char insn[32];
+			char comment[40];
+			struct storage *op1;
+			struct storage *op2;
+		};
+
+		/* stuff for C strings */
+		struct {
+			struct string *string;
+			int label;
+		};
+	};
+};
+
+
+static struct function *current_func = NULL;
+static struct textbuf *unit_post_text = NULL;
+static const char *current_section;
+
+static void emit_comment(const char * fmt, ...) FORMAT_ATTR(1);
+static void emit_move(struct storage *src, struct storage *dest,
+		      struct symbol *ctype, const char *comment);
+static int type_is_signed(struct symbol *sym);
+static struct storage *x86_address_gen(struct expression *expr);
+static struct storage *x86_symbol_expr(struct symbol *sym);
+static void x86_symbol(struct symbol *sym);
+static struct storage *x86_statement(struct statement *stmt);
+static struct storage *x86_expression(struct expression *expr);
+
+enum registers {
+	NOREG,
+	 AL,  DL,  CL,  BL,  AH,  DH,  CH,  BH,	// 8-bit
+	 AX,  DX,  CX,  BX,  SI,  DI,  BP,  SP,	// 16-bit
+	EAX, EDX, ECX, EBX, ESI, EDI, EBP, ESP,	// 32-bit
+	EAX_EDX, ECX_EBX, ESI_EDI,		// 64-bit
+};
+
+/* This works on regno's, reg_info's and hardreg_storage's */
+#define byte_reg(reg) ((reg) - 16)
+#define highbyte_reg(reg) ((reg)-12)
+#define word_reg(reg) ((reg)-8)
+
+#define REGINFO(nr, str, conflicts...)	[nr] = { .name = str, .aliases = { nr , conflicts } }
+
+static struct reg_info reg_info_table[] = {
+	REGINFO( AL,  "%al", AX, EAX, EAX_EDX),
+	REGINFO( DL,  "%dl", DX, EDX, EAX_EDX),
+	REGINFO( CL,  "%cl", CX, ECX, ECX_EBX),
+	REGINFO( BL,  "%bl", BX, EBX, ECX_EBX),
+	REGINFO( AH,  "%ah", AX, EAX, EAX_EDX),
+	REGINFO( DH,  "%dh", DX, EDX, EAX_EDX),
+	REGINFO( CH,  "%ch", CX, ECX, ECX_EBX),
+	REGINFO( BH,  "%bh", BX, EBX, ECX_EBX),
+	REGINFO( AX,  "%ax", AL, AH, EAX, EAX_EDX),
+	REGINFO( DX,  "%dx", DL, DH, EDX, EAX_EDX),
+	REGINFO( CX,  "%cx", CL, CH, ECX, ECX_EBX),
+	REGINFO( BX,  "%bx", BL, BH, EBX, ECX_EBX),
+	REGINFO( SI,  "%si", ESI, ESI_EDI),
+	REGINFO( DI,  "%di", EDI, ESI_EDI),
+	REGINFO( BP,  "%bp", EBP),
+	REGINFO( SP,  "%sp", ESP),
+	REGINFO(EAX, "%eax", AL, AH, AX, EAX_EDX),
+	REGINFO(EDX, "%edx", DL, DH, DX, EAX_EDX),
+	REGINFO(ECX, "%ecx", CL, CH, CX, ECX_EBX),
+	REGINFO(EBX, "%ebx", BL, BH, BX, ECX_EBX),
+	REGINFO(ESI, "%esi", SI, ESI_EDI),
+	REGINFO(EDI, "%edi", DI, ESI_EDI),
+	REGINFO(EBP, "%ebp", BP),
+	REGINFO(ESP, "%esp", SP),
+	REGINFO(EAX_EDX, "%eax:%edx", AL, AH, AX, EAX, DL, DH, DX, EDX),
+	REGINFO(ECX_EBX, "%ecx:%ebx", CL, CH, CX, ECX, BL, BH, BX, EBX),
+	REGINFO(ESI_EDI, "%esi:%edi", SI, ESI, DI, EDI),
+};
+
+#define REGSTORAGE(nr) [nr] = { .type = STOR_REG, .reg = reg_info_table + (nr) }
+
+static struct storage hardreg_storage_table[] = {
+	REGSTORAGE(AL), REGSTORAGE(DL), REGSTORAGE(CL), REGSTORAGE(BL),
+	REGSTORAGE(AH), REGSTORAGE(DH), REGSTORAGE(CH), REGSTORAGE(BH),
+	REGSTORAGE(AX), REGSTORAGE(DX), REGSTORAGE(CX), REGSTORAGE(BX),
+	REGSTORAGE(SI), REGSTORAGE(DI), REGSTORAGE(BP), REGSTORAGE(SP),
+	REGSTORAGE(EAX), REGSTORAGE(EDX), REGSTORAGE(ECX), REGSTORAGE(EBX),
+	REGSTORAGE(ESI), REGSTORAGE(EDI), REGSTORAGE(EBP), REGSTORAGE(ESP),
+	REGSTORAGE(EAX_EDX), REGSTORAGE(ECX_EBX), REGSTORAGE(ESI_EDI),
+};
+
+#define REG_EAX (&hardreg_storage_table[EAX])
+#define REG_ECX (&hardreg_storage_table[ECX])
+#define REG_EDX (&hardreg_storage_table[EDX])
+#define REG_ESP (&hardreg_storage_table[ESP])
+#define REG_DL	(&hardreg_storage_table[DL])
+#define REG_DX	(&hardreg_storage_table[DX])
+#define REG_AL	(&hardreg_storage_table[AL])
+#define REG_AX	(&hardreg_storage_table[AX])
+
+static DECLARE_BITMAP(regs_in_use, 256);
+
+static inline struct storage * reginfo_reg(struct reg_info *info)
+{
+	return hardreg_storage_table + info->own_regno;
+}
+
+static struct storage * get_hardreg(struct storage *reg, int clear)
+{
+	struct reg_info *info = reg->reg;
+	const unsigned char *aliases;
+	int regno;
+
+	aliases = info->aliases;
+	while ((regno = *aliases++) != NOREG) {
+		if (test_bit(regno, regs_in_use))
+			goto busy;
+		if (clear)
+			reg_info_table[regno].contains = NULL;
+	}
+	set_bit(info->own_regno, regs_in_use);
+	return reg;
+busy:
+	fprintf(stderr, "register %s is busy\n", info->name);
+	if (regno + reg_info_table != info)
+		fprintf(stderr, "  conflicts with %s\n", reg_info_table[regno].name);
+	exit(1);
+}
+
+static void put_reg(struct storage *reg)
+{
+	struct reg_info *info = reg->reg;
+	int regno = info->own_regno;
+
+	if (test_and_clear_bit(regno, regs_in_use))
+		return;
+	fprintf(stderr, "freeing already free'd register %s\n", reg_info_table[regno].name);
+}
+
+struct regclass {
+	const char *name;
+	const unsigned char regs[30];
+};
+
+static struct regclass regclass_8 = { "8-bit", { AL, DL, CL, BL, AH, DH, CH, BH }};
+static struct regclass regclass_16 = { "16-bit", { AX, DX, CX, BX, SI, DI, BP }};
+static struct regclass regclass_32 = { "32-bit", { EAX, EDX, ECX, EBX, ESI, EDI, EBP }};
+static struct regclass regclass_64 = { "64-bit", { EAX_EDX, ECX_EBX, ESI_EDI }};
+
+static struct regclass regclass_32_8 = { "32-bit bytes", { EAX, EDX, ECX, EBX }};
+
+static struct regclass *get_regclass_bits(int bits)
+{
+	switch (bits) {
+	case 8: return &regclass_8;
+	case 16: return &regclass_16;
+	case 64: return &regclass_64;
+	default: return &regclass_32;
+	}
+}
+
+static struct regclass *get_regclass(struct expression *expr)
+{
+	return get_regclass_bits(expr->ctype->bit_size);
+}
+
+static int register_busy(int regno)
+{
+	if (!test_bit(regno, regs_in_use)) {
+		struct reg_info *info = reg_info_table + regno;
+		const unsigned char *regs = info->aliases+1;
+
+		while ((regno = *regs) != NOREG) {
+			regs++;
+			if (test_bit(regno, regs_in_use))
+				goto busy;
+		}
+		return 0;
+	}
+busy:
+	return 1;
+}
+
+static struct storage *get_reg(struct regclass *class)
+{
+	const unsigned char *regs = class->regs;
+	int regno;
+
+	while ((regno = *regs) != NOREG) {
+		regs++;
+		if (register_busy(regno))
+			continue;
+		return get_hardreg(hardreg_storage_table + regno, 1);
+	}
+	fprintf(stderr, "Ran out of %s registers\n", class->name);
+	exit(1);
+}
+
+static struct storage *get_reg_value(struct storage *value, struct regclass *class)
+{
+	struct reg_info *info;
+	struct storage *reg;
+
+	/* Do we already have it somewhere */
+	info = value->reg;
+	if (info && info->contains == value) {
+		emit_comment("already have register %s", info->name);
+		return get_hardreg(hardreg_storage_table + info->own_regno, 0);
+	}
+
+	reg = get_reg(class);
+	emit_move(value, reg, value->ctype, "reload register");
+	info = reg->reg;
+	info->contains = value;
+	value->reg = info;
+	return reg;
+}
+
+static struct storage *temp_from_bits(unsigned int bit_size)
+{
+	return get_reg(get_regclass_bits(bit_size));
+}
+
+static inline unsigned int pseudo_offset(struct storage *s)
+{
+	if (s->type != STOR_PSEUDO)
+		return 123456;	/* intentionally bogus value */
+
+	return s->offset;
+}
+
+static inline unsigned int arg_offset(struct storage *s)
+{
+	if (s->type != STOR_ARG)
+		return 123456;	/* intentionally bogus value */
+
+	/* FIXME: this is wrong wrong wrong */
+	return current_func->stack_size + ((1 + s->idx) * 4);
+}
+
+static const char *pretty_offset(int ofs)
+{
+	static char esp_buf[64];
+
+	if (ofs)
+		sprintf(esp_buf, "%d(%%esp)", ofs);
+	else
+		strcpy(esp_buf, "(%esp)");
+
+	return esp_buf;
+}
+
+static void stor_sym_init(struct symbol *sym)
+{
+	struct storage *stor;
+	struct symbol_private *priv;
+
+	priv = calloc(1, sizeof(*priv) + sizeof(*stor));
+	if (!priv)
+		die("OOM in stor_sym_init");
+
+	stor = (struct storage *) (priv + 1);
+
+	priv->addr = stor;
+	stor->type = STOR_SYM;
+	stor->sym = sym;
+}
+
+static const char *stor_op_name(struct storage *s)
+{
+	static char name[32];
+
+	switch (s->type) {
+	case STOR_PSEUDO:
+		strcpy(name, pretty_offset((int) pseudo_offset(s)));
+		break;
+	case STOR_ARG:
+		strcpy(name, pretty_offset((int) arg_offset(s)));
+		break;
+	case STOR_SYM:
+		strcpy(name, show_ident(s->sym->ident));
+		break;
+	case STOR_REG:
+		strcpy(name, s->reg->name);
+		break;
+	case STOR_VALUE:
+		sprintf(name, "$%Ld", s->value);
+		break;
+	case STOR_LABEL:
+		sprintf(name, "%s.L%d", s->flags & STOR_LABEL_VAL ? "$" : "",
+			s->label);
+		break;
+	case STOR_LABELSYM:
+		sprintf(name, "%s.LS%p", s->flags & STOR_LABEL_VAL ? "$" : "",
+			s->labelsym);
+		break;
+	}
+
+	return name;
+}
+
+static struct atom *new_atom(enum atom_type type)
+{
+	struct atom *atom;
+
+	atom = calloc(1, sizeof(*atom));	/* TODO: chunked alloc */
+	if (!atom)
+		die("nuclear OOM");
+
+	atom->type = type;
+
+	return atom;
+}
+
+static inline void push_cstring(struct function *f, struct string *str,
+				int label)
+{
+	struct atom *atom;
+
+	atom = new_atom(ATOM_CSTR);
+	atom->string = str;
+	atom->label = label;
+
+	add_ptr_list(&f->str_list, atom);	/* note: _not_ atom_list */
+}
+
+static inline void push_atom(struct function *f, struct atom *atom)
+{
+	add_ptr_list(&f->atom_list, atom);
+}
+
+static void push_text_atom(struct function *f, const char *text)
+{
+	struct atom *atom = new_atom(ATOM_TEXT);
+
+	atom->text = strdup(text);
+	atom->text_len = strlen(text);
+
+	push_atom(f, atom);
+}
+
+static struct storage *new_storage(enum storage_type type)
+{
+	struct storage *stor;
+
+	stor = calloc(1, sizeof(*stor));
+	if (!stor)
+		die("OOM in new_storage");
+
+	stor->type = type;
+
+	return stor;
+}
+
+static struct storage *stack_alloc(int n_bytes)
+{
+	struct function *f = current_func;
+	struct storage *stor;
+
+	assert(f != NULL);
+
+	stor = new_storage(STOR_PSEUDO);
+	stor->type = STOR_PSEUDO;
+	stor->pseudo = f->pseudo_nr;
+	stor->offset = f->stack_size; /* FIXME: stack req. natural align */
+	stor->size = n_bytes;
+	f->stack_size += n_bytes;
+	f->pseudo_nr++;
+
+	add_ptr_list(&f->pseudo_list, stor);
+
+	return stor;
+}
+
+static struct storage *new_labelsym(struct symbol *sym)
+{
+	struct storage *stor;
+
+	stor = new_storage(STOR_LABELSYM);
+
+	if (stor) {
+		stor->flags |= STOR_WANTS_FREE;
+		stor->labelsym = sym;
+	}
+
+	return stor;
+}
+
+static struct storage *new_val(long long value)
+{
+	struct storage *stor;
+
+	stor = new_storage(STOR_VALUE);
+
+	if (stor) {
+		stor->flags |= STOR_WANTS_FREE;
+		stor->value = value;
+	}
+
+	return stor;
+}
+
+static int new_label(void)
+{
+	static int label = 0;
+	return ++label;
+}
+
+static void textbuf_push(struct textbuf **buf_p, const char *text)
+{
+	struct textbuf *tmp, *list = *buf_p;
+	unsigned int text_len = strlen(text);
+	unsigned int alloc_len = text_len + 1 + sizeof(*list);
+
+	tmp = calloc(1, alloc_len);
+	if (!tmp)
+		die("OOM on textbuf alloc");
+
+	tmp->text = ((void *) tmp) + sizeof(*tmp);
+	memcpy(tmp->text, text, text_len + 1);
+	tmp->len = text_len;
+
+	/* add to end of list */
+	if (!list) {
+		list = tmp;
+		tmp->prev = tmp;
+	} else {
+		tmp->prev = list->prev;
+		tmp->prev->next = tmp;
+		list->prev = tmp;
+	}
+	tmp->next = list;
+
+	*buf_p = list;
+}
+
+static void textbuf_emit(struct textbuf **buf_p)
+{
+	struct textbuf *tmp, *list = *buf_p;
+
+	while (list) {
+		tmp = list;
+		if (tmp->next == tmp)
+			list = NULL;
+		else {
+			tmp->prev->next = tmp->next;
+			tmp->next->prev = tmp->prev;
+			list = tmp->next;
+		}
+
+		fputs(tmp->text, stdout);
+
+		free(tmp);
+	}
+
+	*buf_p = list;
+}
+
+static void insn(const char *insn, struct storage *op1, struct storage *op2,
+		 const char *comment_in)
+{
+	struct function *f = current_func;
+	struct atom *atom = new_atom(ATOM_INSN);
+
+	assert(insn != NULL);
+
+	strcpy(atom->insn, insn);
+	if (comment_in && (*comment_in))
+		strncpy(atom->comment, comment_in,
+			sizeof(atom->comment) - 1);
+
+	atom->op1 = op1;
+	atom->op2 = op2;
+
+	push_atom(f, atom);
+}
+
+static void emit_comment(const char *fmt, ...)
+{
+	struct function *f = current_func;
+	static char tmpbuf[100] = "\t# ";
+	va_list args;
+	int i;
+
+	va_start(args, fmt);
+	i = vsnprintf(tmpbuf+3, sizeof(tmpbuf)-4, fmt, args);
+	va_end(args);
+	tmpbuf[i+3] = '\n';
+	tmpbuf[i+4] = '\0';
+	push_text_atom(f, tmpbuf);
+}
+
+static void emit_label (int label, const char *comment)
+{
+	struct function *f = current_func;
+	char s[64];
+
+	if (!comment)
+		sprintf(s, ".L%d:\n", label);
+	else
+		sprintf(s, ".L%d:\t\t\t\t\t# %s\n", label, comment);
+
+	push_text_atom(f, s);
+}
+
+static void emit_labelsym (struct symbol *sym, const char *comment)
+{
+	struct function *f = current_func;
+	char s[64];
+
+	if (!comment)
+		sprintf(s, ".LS%p:\n", sym);
+	else
+		sprintf(s, ".LS%p:\t\t\t\t# %s\n", sym, comment);
+
+	push_text_atom(f, s);
+}
+
+void emit_unit_begin(const char *basename)
+{
+	printf("\t.file\t\"%s\"\n", basename);
+}
+
+void emit_unit_end(void)
+{
+	textbuf_emit(&unit_post_text);
+	printf("\t.ident\t\"sparse silly x86 backend (built %s)\"\n", __DATE__);
+}
+
+/* conditionally switch sections */
+static void emit_section(const char *s)
+{
+	if (s == current_section)
+		return;
+	if (current_section && (!strcmp(s, current_section)))
+		return;
+
+	printf("\t%s\n", s);
+	current_section = s;
+}
+
+static void emit_insn_atom(struct function *f, struct atom *atom)
+{
+	char s[128];
+	char comment[64];
+	struct storage *op1 = atom->op1;
+	struct storage *op2 = atom->op2;
+
+	if (atom->comment[0])
+		sprintf(comment, "\t\t# %s", atom->comment);
+	else
+		comment[0] = 0;
+
+	if (atom->op2) {
+		char tmp[16];
+		strcpy(tmp, stor_op_name(op1));
+		sprintf(s, "\t%s\t%s, %s%s\n",
+			atom->insn, tmp, stor_op_name(op2), comment);
+	} else if (atom->op1)
+		sprintf(s, "\t%s\t%s%s%s\n",
+			atom->insn, stor_op_name(op1),
+			comment[0] ? "\t" : "", comment);
+	else
+		sprintf(s, "\t%s\t%s%s\n",
+			atom->insn,
+			comment[0] ? "\t\t" : "", comment);
+
+	write(STDOUT_FILENO, s, strlen(s));
+}
+
+static void emit_atom_list(struct function *f)
+{
+	struct atom *atom;
+
+	FOR_EACH_PTR(f->atom_list, atom) {
+		switch (atom->type) {
+		case ATOM_TEXT: {
+			ssize_t rc = write(STDOUT_FILENO, atom->text,
+					   atom->text_len);
+			(void) rc;	/* FIXME */
+			break;
+		}
+		case ATOM_INSN:
+			emit_insn_atom(f, atom);
+			break;
+		case ATOM_CSTR:
+			assert(0);
+			break;
+		}
+	} END_FOR_EACH_PTR(atom);
+}
+
+static void emit_string_list(struct function *f)
+{
+	struct atom *atom;
+
+	emit_section(".section\t.rodata");
+
+	FOR_EACH_PTR(f->str_list, atom) {
+		/* FIXME: escape " in string */
+		printf(".L%d:\n", atom->label);
+		printf("\t.string\t%s\n", show_string(atom->string));
+
+		free(atom);
+	} END_FOR_EACH_PTR(atom);
+}
+
+static void func_cleanup(struct function *f)
+{
+	struct storage *stor;
+	struct atom *atom;
+
+	FOR_EACH_PTR(f->pseudo_list, stor) {
+		free(stor);
+	} END_FOR_EACH_PTR(stor);
+
+	FOR_EACH_PTR(f->atom_list, atom) {
+		if ((atom->type == ATOM_TEXT) && (atom->text))
+			free(atom->text);
+		if (atom->op1 && (atom->op1->flags & STOR_WANTS_FREE))
+			free(atom->op1);
+		if (atom->op2 && (atom->op2->flags & STOR_WANTS_FREE))
+			free(atom->op2);
+		free(atom);
+	} END_FOR_EACH_PTR(atom);
+
+	free_ptr_list(&f->pseudo_list);
+	free(f);
+}
+
+/* function prologue */
+static void emit_func_pre(struct symbol *sym)
+{
+	struct function *f;
+	struct symbol *arg;
+	unsigned int i, argc = 0, alloc_len;
+	unsigned char *mem;
+	struct symbol_private *privbase;
+	struct storage *storage_base;
+	struct symbol *base_type = sym->ctype.base_type;
+
+	FOR_EACH_PTR(base_type->arguments, arg) {
+		argc++;
+	} END_FOR_EACH_PTR(arg);
+
+	alloc_len =
+		sizeof(*f) +
+		(argc * sizeof(struct symbol *)) +
+		(argc * sizeof(struct symbol_private)) +
+		(argc * sizeof(struct storage));
+	mem = calloc(1, alloc_len);
+	if (!mem)
+		die("OOM on func info");
+
+	f		=  (struct function *) mem;
+	mem		+= sizeof(*f);
+	f->argv		=  (struct symbol **) mem;
+	mem		+= (argc * sizeof(struct symbol *));
+	privbase	=  (struct symbol_private *) mem;
+	mem		+= (argc * sizeof(struct symbol_private));
+	storage_base	=  (struct storage *) mem;
+
+	f->argc = argc;
+	f->ret_target = new_label();
+
+	i = 0;
+	FOR_EACH_PTR(base_type->arguments, arg) {
+		f->argv[i] = arg;
+		arg->aux = &privbase[i];
+		storage_base[i].type = STOR_ARG;
+		storage_base[i].idx = i;
+		privbase[i].addr = &storage_base[i];
+		i++;
+	} END_FOR_EACH_PTR(arg);
+
+	assert(current_func == NULL);
+	current_func = f;
+}
+
+/* function epilogue */
+static void emit_func_post(struct symbol *sym)
+{
+	const char *name = show_ident(sym->ident);
+	struct function *f = current_func;
+	int stack_size = f->stack_size;
+
+	if (f->str_list)
+		emit_string_list(f);
+
+	/* function prologue */
+	emit_section(".text");
+	if ((sym->ctype.modifiers & MOD_STATIC) == 0)
+		printf(".globl %s\n", name);
+	printf("\t.type\t%s, @function\n", name);
+	printf("%s:\n", name);
+
+	if (stack_size) {
+		char pseudo_const[16];
+
+		sprintf(pseudo_const, "$%d", stack_size);
+		printf("\tsubl\t%s, %%esp\n", pseudo_const);
+	}
+
+	/* function epilogue */
+
+	/* jump target for 'return' statements */
+	emit_label(f->ret_target, NULL);
+
+	if (stack_size) {
+		struct storage *val;
+
+		val = new_storage(STOR_VALUE);
+		val->value = (long long) (stack_size);
+		val->flags = STOR_WANTS_FREE;
+
+		insn("addl", val, REG_ESP, NULL);
+	}
+
+	insn("ret", NULL, NULL, NULL);
+
+	/* output everything to stdout */
+	fflush(stdout);		/* paranoia; needed? */
+	emit_atom_list(f);
+
+	/* function footer */
+	name = show_ident(sym->ident);
+	printf("\t.size\t%s, .-%s\n", name, name);
+
+	func_cleanup(f);
+	current_func = NULL;
+}
+
+/* emit object (a.k.a. variable, a.k.a. data) prologue */
+static void emit_object_pre(const char *name, unsigned long modifiers,
+			    unsigned long alignment, unsigned int byte_size)
+{
+	if ((modifiers & MOD_STATIC) == 0)
+		printf(".globl %s\n", name);
+	emit_section(".data");
+	if (alignment)
+		printf("\t.align %lu\n", alignment);
+	printf("\t.type\t%s, @object\n", name);
+	printf("\t.size\t%s, %d\n", name, byte_size);
+	printf("%s:\n", name);
+}
+
+/* emit value (only) for an initializer scalar */
+static void emit_scalar(struct expression *expr, unsigned int bit_size)
+{
+	const char *type;
+	long long ll;
+
+	assert(expr->type == EXPR_VALUE);
+
+	if (expr->value == 0ULL) {
+		printf("\t.zero\t%d\n", bit_size / 8);
+		return;
+	}
+
+	ll = (long long) expr->value;
+
+	switch (bit_size) {
+	case 8:		type = "byte";	ll = (char) ll; break;
+	case 16:	type = "value";	ll = (short) ll; break;
+	case 32:	type = "long";	ll = (int) ll; break;
+	case 64:	type = "quad";	break;
+	default:	type = NULL;	break;
+	}
+
+	assert(type != NULL);
+
+	printf("\t.%s\t%Ld\n", type, ll);
+}
+
+static void emit_global_noinit(const char *name, unsigned long modifiers,
+			       unsigned long alignment, unsigned int byte_size)
+{
+	char s[64];
+
+	if (modifiers & MOD_STATIC) {
+		sprintf(s, "\t.local\t%s\n", name);
+		textbuf_push(&unit_post_text, s);
+	}
+	if (alignment)
+		sprintf(s, "\t.comm\t%s,%d,%lu\n", name, byte_size, alignment);
+	else
+		sprintf(s, "\t.comm\t%s,%d\n", name, byte_size);
+	textbuf_push(&unit_post_text, s);
+}
+
+static int ea_current, ea_last;
+
+static void emit_initializer(struct symbol *sym,
+			     struct expression *expr)
+{
+	int distance = ea_current - ea_last - 1;
+
+	if (distance > 0)
+		printf("\t.zero\t%d\n", (sym->bit_size / 8) * distance);
+
+	if (expr->type == EXPR_VALUE) {
+		struct symbol *base_type = sym->ctype.base_type;
+		assert(base_type != NULL);
+
+		emit_scalar(expr, sym->bit_size / get_expression_value(base_type->array_size));
+		return;
+	}
+	if (expr->type != EXPR_INITIALIZER)
+		return;
+
+	assert(0); /* FIXME */
+}
+
+static int sort_array_cmp(const struct expression *a,
+			  const struct expression *b)
+{
+	int a_ofs = 0, b_ofs = 0;
+
+	if (a->type == EXPR_POS)
+		a_ofs = (int) a->init_offset;
+	if (b->type == EXPR_POS)
+		b_ofs = (int) b->init_offset;
+
+	return a_ofs - b_ofs;
+}
+
+/* move to front-end? */
+static void sort_array(struct expression *expr)
+{
+	struct expression *entry, **list;
+	unsigned int elem, sorted, i;
+
+	elem = 0;
+	FOR_EACH_PTR(expr->expr_list, entry) {
+		elem++;
+	} END_FOR_EACH_PTR(entry);
+
+	if (!elem)
+		return;
+
+	list = malloc(sizeof(entry) * elem);
+	if (!list)
+		die("OOM in sort_array");
+
+	/* this code is no doubt evil and ignores EXPR_INDEX possibly
+	 * to its detriment and other nasty things.  improvements
+	 * welcome.
+	 */
+	i = 0;
+	sorted = 0;
+	FOR_EACH_PTR(expr->expr_list, entry) {
+		if ((entry->type == EXPR_POS) || (entry->type == EXPR_VALUE)) {
+			/* add entry to list[], in sorted order */
+			if (sorted == 0) {
+				list[0] = entry;
+				sorted = 1;
+			} else {
+				for (i = 0; i < sorted; i++)
+					if (sort_array_cmp(entry, list[i]) <= 0)
+						break;
+
+				/* If inserting into the middle of list[]
+				 * instead of appending, we memmove.
+				 * This is ugly, but thankfully
+				 * uncommon.  Input data with tons of
+				 * entries very rarely have explicit
+				 * offsets.  convert to qsort eventually...
+				 */
+				if (i != sorted)
+					memmove(&list[i + 1], &list[i],
+						(sorted - i) * sizeof(entry));
+				list[i] = entry;
+				sorted++;
+			}
+		}
+	} END_FOR_EACH_PTR(entry);
+
+	i = 0;
+	FOR_EACH_PTR(expr->expr_list, entry) {
+		if ((entry->type == EXPR_POS) || (entry->type == EXPR_VALUE))
+			*THIS_ADDRESS(entry) = list[i++];
+	} END_FOR_EACH_PTR(entry);
+
+}
+
+static void emit_array(struct symbol *sym)
+{
+	struct symbol *base_type = sym->ctype.base_type;
+	struct expression *expr = sym->initializer;
+	struct expression *entry;
+
+	assert(base_type != NULL);
+
+	stor_sym_init(sym);
+
+	ea_last = -1;
+
+	emit_object_pre(show_ident(sym->ident), sym->ctype.modifiers,
+		        sym->ctype.alignment,
+			sym->bit_size / 8);
+
+	sort_array(expr);
+
+	FOR_EACH_PTR(expr->expr_list, entry) {
+		if (entry->type == EXPR_VALUE) {
+			ea_current = 0;
+			emit_initializer(sym, entry);
+			ea_last = ea_current;
+		} else if (entry->type == EXPR_POS) {
+			ea_current =
+			    entry->init_offset / (base_type->bit_size / 8);
+			emit_initializer(sym, entry->init_expr);
+			ea_last = ea_current;
+		}
+	} END_FOR_EACH_PTR(entry);
+}
+
+void emit_one_symbol(struct symbol *sym)
+{
+	x86_symbol(sym);
+}
+
+static void emit_copy(struct storage *dest, struct storage *src,
+		      struct symbol *ctype)
+{
+	struct storage *reg = NULL;
+	unsigned int bit_size;
+
+	/* FIXME: Bitfield copy! */
+
+	bit_size = src->size * 8;
+	if (!bit_size)
+		bit_size = 32;
+	if ((src->type == STOR_ARG) && (bit_size < 32))
+		bit_size = 32;
+
+	reg = temp_from_bits(bit_size);
+	emit_move(src, reg, ctype, "begin copy ..");
+
+	bit_size = dest->size * 8;
+	if (!bit_size)
+		bit_size = 32;
+	if ((dest->type == STOR_ARG) && (bit_size < 32))
+		bit_size = 32;
+
+	emit_move(reg, dest, ctype, ".... end copy");
+	put_reg(reg);
+}
+
+static void emit_store(struct expression *dest_expr, struct storage *dest,
+		       struct storage *src, int bits)
+{
+	/* FIXME: Bitfield store! */
+	printf("\tst.%d\t\tv%d,[v%d]\n", bits, src->pseudo, dest->pseudo);
+}
+
+static void emit_scalar_noinit(struct symbol *sym)
+{
+	emit_global_noinit(show_ident(sym->ident),
+			   sym->ctype.modifiers, sym->ctype.alignment,
+			   sym->bit_size / 8);
+	stor_sym_init(sym);
+}
+
+static void emit_array_noinit(struct symbol *sym)
+{
+	emit_global_noinit(show_ident(sym->ident),
+			   sym->ctype.modifiers, sym->ctype.alignment,
+			   get_expression_value(sym->array_size) * (sym->bit_size / 8));
+	stor_sym_init(sym);
+}
+
+static const char *opbits(const char *insn, unsigned int bits)
+{
+	static char opbits_str[32];
+	char c;
+
+	switch (bits) {
+	case 8:	 c = 'b'; break;
+	case 16: c = 'w'; break;
+	case 32: c = 'l'; break;
+	case 64: c = 'q'; break;
+	default: abort(); break;
+	}
+
+	sprintf(opbits_str, "%s%c", insn, c);
+
+	return opbits_str;
+}
+
+static void emit_move(struct storage *src, struct storage *dest,
+		      struct symbol *ctype, const char *comment)
+{
+	unsigned int bits;
+	unsigned int is_signed;
+	unsigned int is_dest = (src->type == STOR_REG);
+	const char *opname;
+
+	if (ctype) {
+		bits = ctype->bit_size;
+		is_signed = type_is_signed(ctype);
+	} else {
+		bits = 32;
+		is_signed = 0;
+	}
+
+	/*
+	 * Are we moving from a register to a register?
+	 * Make the new reg to be the "cache".
+	 */
+	if ((dest->type == STOR_REG) && (src->type == STOR_REG)) {
+		struct storage *backing;
+
+reg_reg_move:
+		if (dest == src)
+			return;
+
+		backing = src->reg->contains;
+		if (backing) {
+			/* Is it still valid? */
+			if (backing->reg != src->reg)
+				backing = NULL;
+			else
+				backing->reg = dest->reg;
+		}
+		dest->reg->contains = backing;
+		insn("mov", src, dest, NULL);
+		return;
+	}
+
+	/*
+	 * Are we moving to a register from a non-reg?
+	 *
+	 * See if we have the non-reg source already cached
+	 * in a register..
+	 */
+	if (dest->type == STOR_REG) {
+		if (src->reg) {
+			struct reg_info *info = src->reg;
+			if (info->contains == src) {
+				src = reginfo_reg(info);
+				goto reg_reg_move;
+			}
+		}
+		dest->reg->contains = src;
+		src->reg = dest->reg;
+	}
+
+	if (src->type == STOR_REG) {
+		/* We could just mark the register dirty here and do lazy store.. */
+		src->reg->contains = dest;
+		dest->reg = src->reg;
+	}
+
+	if ((bits == 8) || (bits == 16)) {
+		if (is_dest)
+			opname = "mov";
+		else
+			opname = is_signed ? "movsx" : "movzx";
+	} else
+		opname = "mov";
+
+	insn(opbits(opname, bits), src, dest, comment);
+}
+
+static struct storage *emit_compare(struct expression *expr)
+{
+	struct storage *left = x86_expression(expr->left);
+	struct storage *right = x86_expression(expr->right);
+	struct storage *reg1, *reg2;
+	struct storage *new, *val;
+	const char *opname = NULL;
+	unsigned int right_bits = expr->right->ctype->bit_size;
+
+	switch(expr->op) {
+	case '<': 		opname = "setl";	break;
+	case '>':		opname = "setg";	break;
+	case SPECIAL_LTE:
+				opname = "setle";	break;
+	case SPECIAL_GTE:
+				opname = "setge";	break;
+	case SPECIAL_EQUAL:	opname = "sete";	break;
+	case SPECIAL_NOTEQUAL:	opname = "setne";	break;
+	case SPECIAL_UNSIGNED_LT:
+				opname = "setb";	break;
+	case SPECIAL_UNSIGNED_GT:
+				opname = "seta";	break;
+	case SPECIAL_UNSIGNED_LTE:
+				opname = "setb";	break;
+	case SPECIAL_UNSIGNED_GTE:
+				opname = "setae";	break;
+	default:
+		assert(0);
+		break;
+	}
+
+	/* init EDX to 0 */
+	val = new_storage(STOR_VALUE);
+	val->flags = STOR_WANTS_FREE;
+
+	reg1 = get_reg(&regclass_32_8);
+	emit_move(val, reg1, NULL, NULL);
+
+	/* move op1 into EAX */
+	reg2 = get_reg_value(left, get_regclass(expr->left));
+
+	/* perform comparison, RHS (op1, right) and LHS (op2, EAX) */
+	insn(opbits("cmp", right_bits), right, reg2, NULL);
+	put_reg(reg2);
+
+	/* store result of operation, 0 or 1, in DL using SETcc */
+	insn(opname, byte_reg(reg1), NULL, NULL);
+
+	/* finally, store the result (DL) in a new pseudo / stack slot */
+	new = stack_alloc(4);
+	emit_move(reg1, new, NULL, "end EXPR_COMPARE");
+	put_reg(reg1);
+
+	return new;
+}
+
+static struct storage *emit_value(struct expression *expr)
+{
+#if 0 /* old and slow way */
+	struct storage *new = stack_alloc(4);
+	struct storage *val;
+
+	val = new_storage(STOR_VALUE);
+	val->value = (long long) expr->value;
+	val->flags = STOR_WANTS_FREE;
+	insn("movl", val, new, NULL);
+
+	return new;
+#else
+	struct storage *val;
+
+	val = new_storage(STOR_VALUE);
+	val->value = (long long) expr->value;
+
+	return val;	/* FIXME: memory leak */
+#endif
+}
+
+static struct storage *emit_divide(struct expression *expr, struct storage *left, struct storage *right)
+{
+	struct storage *eax_edx;
+	struct storage *reg, *new;
+	struct storage *val = new_storage(STOR_VALUE);
+
+	emit_comment("begin DIVIDE");
+	eax_edx = get_hardreg(hardreg_storage_table + EAX_EDX, 1);
+
+	/* init EDX to 0 */
+	val->flags = STOR_WANTS_FREE;
+	emit_move(val, REG_EDX, NULL, NULL);
+
+	new = stack_alloc(expr->ctype->bit_size / 8);
+
+	/* EAX is dividend */
+	emit_move(left, REG_EAX, NULL, NULL);
+
+	reg = get_reg_value(right, &regclass_32);
+
+	/* perform binop */
+	insn("div", reg, REG_EAX, NULL);
+	put_reg(reg);
+
+	reg = REG_EAX;
+	if (expr->op == '%')
+		reg = REG_EDX;
+	emit_move(reg, new, NULL, NULL);
+
+	put_reg(eax_edx);
+	emit_comment("end DIVIDE");
+	return new;
+}
+
+static struct storage *emit_binop(struct expression *expr)
+{
+	struct storage *left = x86_expression(expr->left);
+	struct storage *right = x86_expression(expr->right);
+	struct storage *new;
+	struct storage *dest, *src;
+	const char *opname = NULL;
+	const char *suffix = NULL;
+	char opstr[16];
+	int is_signed;
+
+	/* Divides have special register constraints */
+	if ((expr->op == '/') || (expr->op == '%'))
+		return emit_divide(expr, left, right);
+
+	is_signed = type_is_signed(expr->ctype);
+
+	switch (expr->op) {
+	case '+':
+		opname = "add";
+		break;
+	case '-':
+		opname = "sub";
+		break;
+	case '&':
+		opname = "and";
+		break;
+	case '|':
+		opname = "or";
+		break;
+	case '^':
+		opname = "xor";
+		break;
+	case SPECIAL_LEFTSHIFT:
+		opname = "shl";
+		break;
+	case SPECIAL_RIGHTSHIFT:
+		if (is_signed)
+			opname = "sar";
+		else
+			opname = "shr";
+		break;
+	case '*':
+		if (is_signed)
+			opname = "imul";
+		else
+			opname = "mul";
+		break;
+	case SPECIAL_LOGICAL_AND:
+		warning(expr->pos, "bogus bitwise and for logical op (should use '2*setne + and' or something)");
+		opname = "and";
+		break;
+	case SPECIAL_LOGICAL_OR:
+		warning(expr->pos, "bogus bitwise or for logical op (should use 'or + setne' or something)");
+		opname = "or";
+		break;
+	default:
+		error_die(expr->pos, "unhandled binop '%s'\n", show_special(expr->op));
+		break;
+	}
+
+	dest = get_reg_value(right, &regclass_32);
+	src = get_reg_value(left, &regclass_32);
+	switch (expr->ctype->bit_size) {
+	case 8:
+		suffix = "b";
+		break;
+	case 16:
+		suffix = "w";
+		break;
+	case 32:
+		suffix = "l";
+		break;
+	case 64:
+		suffix = "q";		/* FIXME */
+		break;
+	default:
+		assert(0);
+		break;
+	}
+
+	snprintf(opstr, sizeof(opstr), "%s%s", opname, suffix);
+
+	/* perform binop */
+	insn(opstr, src, dest, NULL);
+	put_reg(src);
+
+	/* store result in new pseudo / stack slot */
+	new = stack_alloc(expr->ctype->bit_size / 8);
+	emit_move(dest, new, NULL, "end EXPR_BINOP");
+
+	put_reg(dest);
+
+	return new;
+}
+
+static int emit_conditional_test(struct storage *val)
+{
+	struct storage *reg;
+	struct storage *target_val;
+	int target_false;
+
+	/* load result into EAX */
+	emit_comment("begin if/conditional");
+	reg = get_reg_value(val, &regclass_32);
+
+	/* compare result with zero */
+	insn("test", reg, reg, NULL);
+	put_reg(reg);
+
+	/* create conditional-failed label to jump to */
+	target_false = new_label();
+	target_val = new_storage(STOR_LABEL);
+	target_val->label = target_false;
+	target_val->flags = STOR_WANTS_FREE;
+	insn("jz", target_val, NULL, NULL);
+
+	return target_false;
+}
+
+static int emit_conditional_end(int target_false)
+{
+	struct storage *cond_end_st;
+	int cond_end;
+
+	/* finished generating code for if-true statement.
+	 * add a jump-to-end jump to avoid falling through
+	 * to the if-false statement code.
+	 */
+	cond_end = new_label();
+	cond_end_st = new_storage(STOR_LABEL);
+	cond_end_st->label = cond_end;
+	cond_end_st->flags = STOR_WANTS_FREE;
+	insn("jmp", cond_end_st, NULL, NULL);
+
+	/* if we have both if-true and if-false statements,
+	 * the failed-conditional case will fall through to here
+	 */
+	emit_label(target_false, NULL);
+
+	return cond_end;
+}
+
+static void emit_if_conditional(struct statement *stmt)
+{
+	struct storage *val;
+	int cond_end;
+
+	/* emit test portion of conditional */
+	val = x86_expression(stmt->if_conditional);
+	cond_end = emit_conditional_test(val);
+
+	/* emit if-true statement */
+	x86_statement(stmt->if_true);
+
+	/* emit if-false statement, if present */
+	if (stmt->if_false) {
+		cond_end = emit_conditional_end(cond_end);
+		x86_statement(stmt->if_false);
+	}
+
+	/* end of conditional; jump target for if-true branch */
+	emit_label(cond_end, "end if");
+}
+
+static struct storage *emit_inc_dec(struct expression *expr, int postop)
+{
+	struct storage *addr = x86_address_gen(expr->unop);
+	struct storage *retval;
+	char opname[16];
+
+	strcpy(opname, opbits(expr->op == SPECIAL_INCREMENT ? "inc" : "dec",
+			      expr->ctype->bit_size));
+
+	if (postop) {
+		struct storage *new = stack_alloc(4);
+
+		emit_copy(new, addr, expr->unop->ctype);
+
+		retval = new;
+	} else
+		retval = addr;
+
+	insn(opname, addr, NULL, NULL);
+
+	return retval;
+}
+
+static struct storage *emit_postop(struct expression *expr)
+{
+	return emit_inc_dec(expr, 1);
+}
+
+static struct storage *emit_return_stmt(struct statement *stmt)
+{
+	struct function *f = current_func;
+	struct expression *expr = stmt->ret_value;
+	struct storage *val = NULL, *jmplbl;
+
+	if (expr && expr->ctype) {
+		val = x86_expression(expr);
+		assert(val != NULL);
+		emit_move(val, REG_EAX, expr->ctype, "return");
+	}
+
+	jmplbl = new_storage(STOR_LABEL);
+	jmplbl->flags |= STOR_WANTS_FREE;
+	jmplbl->label = f->ret_target;
+	insn("jmp", jmplbl, NULL, NULL);
+
+	return val;
+}
+
+static struct storage *emit_conditional_expr(struct expression *expr)
+{
+	struct storage *cond, *true = NULL, *false = NULL;
+	struct storage *new = stack_alloc(expr->ctype->bit_size / 8);
+	int target_false, cond_end;
+
+	/* evaluate conditional */
+	cond = x86_expression(expr->conditional);
+	target_false = emit_conditional_test(cond);
+
+	/* handle if-true part of the expression */
+	true = x86_expression(expr->cond_true);
+
+	emit_copy(new, true, expr->ctype);
+
+	cond_end = emit_conditional_end(target_false);
+
+	/* handle if-false part of the expression */
+	false = x86_expression(expr->cond_false);
+
+	emit_copy(new, false, expr->ctype);
+
+	/* end of conditional; jump target for if-true branch */
+	emit_label(cond_end, "end conditional");
+
+	return new;
+}
+
+static struct storage *emit_select_expr(struct expression *expr)
+{
+	struct storage *cond = x86_expression(expr->conditional);
+	struct storage *true = x86_expression(expr->cond_true);
+	struct storage *false = x86_expression(expr->cond_false);
+	struct storage *reg_cond, *reg_true, *reg_false;
+	struct storage *new = stack_alloc(4);
+
+	emit_comment("begin SELECT");
+	reg_cond = get_reg_value(cond, get_regclass(expr->conditional));
+	reg_true = get_reg_value(true, get_regclass(expr));
+	reg_false = get_reg_value(false, get_regclass(expr));
+
+	/*
+	 * Do the actual select: check the conditional for zero,
+	 * move false over true if zero
+	 */ 
+	insn("test", reg_cond, reg_cond, NULL);
+	insn("cmovz", reg_false, reg_true, NULL);
+
+	/* Store it back */
+	emit_move(reg_true, new, expr->ctype, NULL);
+	put_reg(reg_cond);
+	put_reg(reg_true);
+	put_reg(reg_false);
+	emit_comment("end SELECT");
+	return new;
+}
+
+static struct storage *emit_symbol_expr_init(struct symbol *sym)
+{
+	struct expression *expr = sym->initializer;
+	struct symbol_private *priv = sym->aux;
+
+	if (priv == NULL) {
+		priv = calloc(1, sizeof(*priv));
+		sym->aux = priv;
+
+		if (expr == NULL) {
+			struct storage *new = stack_alloc(4);
+			fprintf(stderr, "FIXME! no value for symbol %s.  creating pseudo %d (stack offset %d)\n",
+				show_ident(sym->ident),
+				new->pseudo, new->pseudo * 4);
+			priv->addr = new;
+		} else {
+			priv->addr = x86_expression(expr);
+		}
+	}
+
+	return priv->addr;
+}
+
+static struct storage *emit_string_expr(struct expression *expr)
+{
+	struct function *f = current_func;
+	int label = new_label();
+	struct storage *new;
+
+	push_cstring(f, expr->string, label);
+
+	new = new_storage(STOR_LABEL);
+	new->label = label;
+	new->flags = STOR_LABEL_VAL | STOR_WANTS_FREE;
+	return new;
+}
+
+static struct storage *emit_cast_expr(struct expression *expr)
+{
+	struct symbol *old_type, *new_type;
+	struct storage *op = x86_expression(expr->cast_expression);
+	int oldbits, newbits;
+	struct storage *new;
+
+	old_type = expr->cast_expression->ctype;
+	new_type = expr->cast_type;
+
+	oldbits = old_type->bit_size;
+	newbits = new_type->bit_size;
+	if (oldbits >= newbits)
+		return op;
+
+	emit_move(op, REG_EAX, old_type, "begin cast ..");
+
+	new = stack_alloc(newbits / 8);
+	emit_move(REG_EAX, new, new_type, ".... end cast");
+
+	return new;
+}
+
+static struct storage *emit_regular_preop(struct expression *expr)
+{
+	struct storage *target = x86_expression(expr->unop);
+	struct storage *val, *new = stack_alloc(4);
+	const char *opname = NULL;
+
+	switch (expr->op) {
+	case '!':
+		val = new_storage(STOR_VALUE);
+		val->flags = STOR_WANTS_FREE;
+		emit_move(val, REG_EDX, NULL, NULL);
+		emit_move(target, REG_EAX, expr->unop->ctype, NULL);
+		insn("test", REG_EAX, REG_EAX, NULL);
+		insn("setz", REG_DL, NULL, NULL);
+		emit_move(REG_EDX, new, expr->unop->ctype, NULL);
+
+		break;
+	case '~':
+		opname = "not";
+	case '-':
+		if (!opname)
+			opname = "neg";
+		emit_move(target, REG_EAX, expr->unop->ctype, NULL);
+		insn(opname, REG_EAX, NULL, NULL);
+		emit_move(REG_EAX, new, expr->unop->ctype, NULL);
+		break;
+	default:
+		assert(0);
+		break;
+	}
+
+	return new;
+}
+
+static void emit_case_statement(struct statement *stmt)
+{
+	emit_labelsym(stmt->case_label, NULL);
+	x86_statement(stmt->case_statement);
+}
+
+static void emit_switch_statement(struct statement *stmt)
+{
+	struct storage *val = x86_expression(stmt->switch_expression);
+	struct symbol *sym, *default_sym = NULL;
+	struct storage *labelsym, *label;
+	int switch_end = 0;
+
+	emit_move(val, REG_EAX, stmt->switch_expression->ctype, "begin case");
+
+	/*
+	 * This is where a _real_ back-end would go through the
+	 * cases to decide whether to use a lookup table or a
+	 * series of comparisons etc
+	 */
+	FOR_EACH_PTR(stmt->switch_case->symbol_list, sym) {
+		struct statement *case_stmt = sym->stmt;
+		struct expression *expr = case_stmt->case_expression;
+		struct expression *to = case_stmt->case_to;
+
+		/* default: */
+		if (!expr)
+			default_sym = sym;
+
+		/* case NNN: */
+		else {
+			struct storage *case_val = new_val(expr->value);
+
+			assert (expr->type == EXPR_VALUE);
+
+			insn("cmpl", case_val, REG_EAX, NULL);
+
+			if (!to) {
+				labelsym = new_labelsym(sym);
+				insn("je", labelsym, NULL, NULL);
+			} else {
+				int next_test;
+
+				label = new_storage(STOR_LABEL);
+				label->flags |= STOR_WANTS_FREE;
+				label->label = next_test = new_label();
+
+				/* FIXME: signed/unsigned */
+				insn("jl", label, NULL, NULL);
+
+				case_val = new_val(to->value);
+				insn("cmpl", case_val, REG_EAX, NULL);
+
+				/* TODO: implement and use refcounting... */
+				label = new_storage(STOR_LABEL);
+				label->flags |= STOR_WANTS_FREE;
+				label->label = next_test;
+
+				/* FIXME: signed/unsigned */
+				insn("jg", label, NULL, NULL);
+
+				labelsym = new_labelsym(sym);
+				insn("jmp", labelsym, NULL, NULL);
+
+				emit_label(next_test, NULL);
+			}
+		}
+	} END_FOR_EACH_PTR(sym);
+
+	if (default_sym) {
+		labelsym = new_labelsym(default_sym);
+		insn("jmp", labelsym, NULL, "default");
+	} else {
+		label = new_storage(STOR_LABEL);
+		label->flags |= STOR_WANTS_FREE;
+		label->label = switch_end = new_label();
+		insn("jmp", label, NULL, "goto end of switch");
+	}
+
+	x86_statement(stmt->switch_statement);
+
+	if (stmt->switch_break->used)
+		emit_labelsym(stmt->switch_break, NULL);
+
+	if (switch_end)
+		emit_label(switch_end, NULL);
+}
+
+static void x86_struct_member(struct symbol *sym)
+{
+	printf("\t%s:%d:%ld at offset %ld.%d", show_ident(sym->ident), sym->bit_size, sym->ctype.alignment, sym->offset, sym->bit_offset);
+	printf("\n");
+}
+
+static void x86_symbol(struct symbol *sym)
+{
+	struct symbol *type;
+
+	if (!sym)
+		return;
+
+	type = sym->ctype.base_type;
+	if (!type)
+		return;
+
+	/*
+	 * Show actual implementation information
+	 */
+	switch (type->type) {
+
+	case SYM_ARRAY:
+		if (sym->initializer)
+			emit_array(sym);
+		else
+			emit_array_noinit(sym);
+		break;
+
+	case SYM_BASETYPE:
+		if (sym->initializer) {
+			emit_object_pre(show_ident(sym->ident),
+					sym->ctype.modifiers,
+				        sym->ctype.alignment,
+					sym->bit_size / 8);
+			emit_scalar(sym->initializer, sym->bit_size);
+			stor_sym_init(sym);
+		} else
+			emit_scalar_noinit(sym);
+		break;
+
+	case SYM_STRUCT:
+	case SYM_UNION: {
+		struct symbol *member;
+
+		printf(" {\n");
+		FOR_EACH_PTR(type->symbol_list, member) {
+			x86_struct_member(member);
+		} END_FOR_EACH_PTR(member);
+		printf("}\n");
+		break;
+	}
+
+	case SYM_FN: {
+		struct statement *stmt = type->stmt;
+		if (stmt) {
+			emit_func_pre(sym);
+			x86_statement(stmt);
+			emit_func_post(sym);
+		}
+		break;
+	}
+
+	default:
+		break;
+	}
+
+	if (sym->initializer && (type->type != SYM_BASETYPE) &&
+	    (type->type != SYM_ARRAY)) {
+		printf(" = \n");
+		x86_expression(sym->initializer);
+	}
+}
+
+static void x86_symbol_init(struct symbol *sym);
+
+static void x86_symbol_decl(struct symbol_list *syms)
+{
+	struct symbol *sym;
+	FOR_EACH_PTR(syms, sym) {
+		x86_symbol_init(sym);
+	} END_FOR_EACH_PTR(sym);
+}
+
+static void loopstk_push(int cont_lbl, int loop_bottom_lbl)
+{
+	struct function *f = current_func;
+	struct loop_stack *ls;
+
+	ls = malloc(sizeof(*ls));
+	ls->continue_lbl = cont_lbl;
+	ls->loop_bottom_lbl = loop_bottom_lbl;
+	ls->next = f->loop_stack;
+	f->loop_stack = ls;
+}
+
+static void loopstk_pop(void)
+{
+	struct function *f = current_func;
+	struct loop_stack *ls;
+
+	assert(f->loop_stack != NULL);
+	ls = f->loop_stack;
+	f->loop_stack = f->loop_stack->next;
+	free(ls);
+}
+
+static int loopstk_break(void)
+{
+	return current_func->loop_stack->loop_bottom_lbl;
+}
+
+static int loopstk_continue(void)
+{
+	return current_func->loop_stack->continue_lbl;
+}
+
+static void emit_loop(struct statement *stmt)
+{
+	struct statement  *pre_statement = stmt->iterator_pre_statement;
+	struct expression *pre_condition = stmt->iterator_pre_condition;
+	struct statement  *statement = stmt->iterator_statement;
+	struct statement  *post_statement = stmt->iterator_post_statement;
+	struct expression *post_condition = stmt->iterator_post_condition;
+	int loop_top = 0, loop_bottom, loop_continue;
+	int have_bottom = 0;
+	struct storage *val;
+
+	loop_bottom = new_label();
+	loop_continue = new_label();
+	loopstk_push(loop_continue, loop_bottom);
+
+	x86_symbol_decl(stmt->iterator_syms);
+	x86_statement(pre_statement);
+	if (!post_condition || post_condition->type != EXPR_VALUE || post_condition->value) {
+		loop_top = new_label();
+		emit_label(loop_top, "loop top");
+	}
+	if (pre_condition) {
+		if (pre_condition->type == EXPR_VALUE) {
+			if (!pre_condition->value) {
+				struct storage *lbv;
+				lbv = new_storage(STOR_LABEL);
+				lbv->label = loop_bottom;
+				lbv->flags = STOR_WANTS_FREE;
+				insn("jmp", lbv, NULL, "go to loop bottom");
+				have_bottom = 1;
+			}
+		} else {
+			struct storage *lbv = new_storage(STOR_LABEL);
+			lbv->label = loop_bottom;
+			lbv->flags = STOR_WANTS_FREE;
+			have_bottom = 1;
+
+			val = x86_expression(pre_condition);
+
+			emit_move(val, REG_EAX, NULL, "loop pre condition");
+			insn("test", REG_EAX, REG_EAX, NULL);
+			insn("jz", lbv, NULL, NULL);
+		}
+	}
+	x86_statement(statement);
+	if (stmt->iterator_continue->used)
+		emit_label(loop_continue, "'continue' iterator");
+	x86_statement(post_statement);
+	if (!post_condition) {
+		struct storage *lbv = new_storage(STOR_LABEL);
+		lbv->label = loop_top;
+		lbv->flags = STOR_WANTS_FREE;
+		insn("jmp", lbv, NULL, "go to loop top");
+	} else if (post_condition->type == EXPR_VALUE) {
+		if (post_condition->value) {
+			struct storage *lbv = new_storage(STOR_LABEL);
+			lbv->label = loop_top;
+			lbv->flags = STOR_WANTS_FREE;
+			insn("jmp", lbv, NULL, "go to loop top");
+		}
+	} else {
+		struct storage *lbv = new_storage(STOR_LABEL);
+		lbv->label = loop_top;
+		lbv->flags = STOR_WANTS_FREE;
+
+		val = x86_expression(post_condition);
+
+		emit_move(val, REG_EAX, NULL, "loop post condition");
+		insn("test", REG_EAX, REG_EAX, NULL);
+		insn("jnz", lbv, NULL, NULL);
+	}
+	if (have_bottom || stmt->iterator_break->used)
+		emit_label(loop_bottom, "loop bottom");
+
+	loopstk_pop();
+}
+
+/*
+ * Print out a statement
+ */
+static struct storage *x86_statement(struct statement *stmt)
+{
+	if (!stmt)
+		return NULL;
+	switch (stmt->type) {
+	default:
+		return NULL;
+	case STMT_RETURN:
+		return emit_return_stmt(stmt);
+	case STMT_DECLARATION:
+		x86_symbol_decl(stmt->declaration);
+		break;
+	case STMT_COMPOUND: {
+		struct statement *s;
+		struct storage *last = NULL;
+
+		FOR_EACH_PTR(stmt->stmts, s) {
+			last = x86_statement(s);
+		} END_FOR_EACH_PTR(s);
+
+		return last;
+	}
+
+	case STMT_EXPRESSION:
+		return x86_expression(stmt->expression);
+	case STMT_IF:
+		emit_if_conditional(stmt);
+		return NULL;
+
+	case STMT_CASE:
+		emit_case_statement(stmt);
+		break;
+	case STMT_SWITCH:
+		emit_switch_statement(stmt);
+		break;
+
+	case STMT_ITERATOR:
+		emit_loop(stmt);
+		break;
+
+	case STMT_NONE:
+		break;
+
+	case STMT_LABEL:
+		printf(".L%p:\n", stmt->label_identifier);
+		x86_statement(stmt->label_statement);
+		break;
+
+	case STMT_GOTO:
+		if (stmt->goto_expression) {
+			struct storage *val = x86_expression(stmt->goto_expression);
+			printf("\tgoto *v%d\n", val->pseudo);
+		} else if (!strcmp("break", show_ident(stmt->goto_label->ident))) {
+			struct storage *lbv = new_storage(STOR_LABEL);
+			lbv->label = loopstk_break();
+			lbv->flags = STOR_WANTS_FREE;
+			insn("jmp", lbv, NULL, "'break'; go to loop bottom");
+		} else if (!strcmp("continue", show_ident(stmt->goto_label->ident))) {
+			struct storage *lbv = new_storage(STOR_LABEL);
+			lbv->label = loopstk_continue();
+			lbv->flags = STOR_WANTS_FREE;
+			insn("jmp", lbv, NULL, "'continue'; go to loop top");
+		} else {
+			struct storage *labelsym = new_labelsym(stmt->goto_label);
+			insn("jmp", labelsym, NULL, NULL);
+		}
+		break;
+	case STMT_ASM:
+		printf("\tasm( .... )\n");
+		break;
+	}
+	return NULL;
+}
+
+static struct storage *x86_call_expression(struct expression *expr)
+{
+	struct function *f = current_func;
+	struct symbol *direct;
+	struct expression *arg, *fn;
+	struct storage *retval, *fncall;
+	int framesize;
+	char s[64];
+
+	if (!expr->ctype) {
+		warning(expr->pos, "\tcall with no type!");
+		return NULL;
+	}
+
+	framesize = 0;
+	FOR_EACH_PTR_REVERSE(expr->args, arg) {
+		struct storage *new = x86_expression(arg);
+		int size = arg->ctype->bit_size;
+
+		/*
+		 * FIXME: i386 SysV ABI dictates that values
+		 * smaller than 32 bits should be placed onto
+		 * the stack as 32-bit objects.  We should not
+		 * blindly do a 32-bit push on objects smaller
+		 * than 32 bits.
+		 */
+		if (size < 32)
+			size = 32;
+		insn("pushl", new, NULL,
+		     !framesize ? "begin function call" : NULL);
+
+		framesize += bits_to_bytes(size);
+	} END_FOR_EACH_PTR_REVERSE(arg);
+
+	fn = expr->fn;
+
+	/* Remove dereference, if any */
+	direct = NULL;
+	if (fn->type == EXPR_PREOP) {
+		if (fn->unop->type == EXPR_SYMBOL) {
+			struct symbol *sym = fn->unop->symbol;
+			if (sym->ctype.base_type->type == SYM_FN)
+				direct = sym;
+		}
+	}
+	if (direct) {
+		struct storage *direct_stor = new_storage(STOR_SYM);
+		direct_stor->flags |= STOR_WANTS_FREE;
+		direct_stor->sym = direct;
+		insn("call", direct_stor, NULL, NULL);
+	} else {
+		fncall = x86_expression(fn);
+		emit_move(fncall, REG_EAX, fn->ctype, NULL);
+
+		strcpy(s, "\tcall\t*%eax\n");
+		push_text_atom(f, s);
+	}
+
+	/* FIXME: pay attention to BITS_IN_POINTER */
+	if (framesize) {
+		struct storage *val = new_storage(STOR_VALUE);
+		val->value = (long long) framesize;
+		val->flags = STOR_WANTS_FREE;
+		insn("addl", val, REG_ESP, NULL);
+	}
+
+	retval = stack_alloc(4);
+	emit_move(REG_EAX, retval, NULL, "end function call");
+
+	return retval;
+}
+
+static struct storage *x86_address_gen(struct expression *expr)
+{
+	struct function *f = current_func;
+	struct storage *addr;
+	struct storage *new;
+	char s[32];
+
+	addr = x86_expression(expr->unop);
+	if (expr->unop->type == EXPR_SYMBOL)
+		return addr;
+
+	emit_move(addr, REG_EAX, NULL, "begin deref ..");
+
+	/* FIXME: operand size */
+	strcpy(s, "\tmovl\t(%eax), %ecx\n");
+	push_text_atom(f, s);
+
+	new = stack_alloc(4);
+	emit_move(REG_ECX, new, NULL, ".... end deref");
+
+	return new;
+}
+
+static struct storage *x86_assignment(struct expression *expr)
+{
+	struct expression *target = expr->left;
+	struct storage *val, *addr;
+
+	if (!expr->ctype)
+		return NULL;
+
+	val = x86_expression(expr->right);
+	addr = x86_address_gen(target);
+
+	switch (val->type) {
+	/* copy, where both operands are memory */
+	case STOR_PSEUDO:
+	case STOR_ARG:
+		emit_copy(addr, val, expr->ctype);
+		break;
+
+	/* copy, one or zero operands are memory */
+	case STOR_REG:
+	case STOR_SYM:
+	case STOR_VALUE:
+	case STOR_LABEL:
+		emit_move(val, addr, expr->left->ctype, NULL);
+		break;
+
+	case STOR_LABELSYM:
+		assert(0);
+		break;
+	}
+	return val;
+}
+
+static int x86_initialization(struct symbol *sym, struct expression *expr)
+{
+	struct storage *val, *addr;
+	int bits;
+
+	if (!expr->ctype)
+		return 0;
+
+	bits = expr->ctype->bit_size;
+	val = x86_expression(expr);
+	addr = x86_symbol_expr(sym);
+	// FIXME! The "target" expression is for bitfield store information.
+	// Leave it NULL, which works fine.
+	emit_store(NULL, addr, val, bits);
+	return 0;
+}
+
+static struct storage *x86_access(struct expression *expr)
+{
+	return x86_address_gen(expr);
+}
+
+static struct storage *x86_preop(struct expression *expr)
+{
+	/*
+	 * '*' is an lvalue access, and is fundamentally different
+	 * from an arithmetic operation. Maybe it should have an
+	 * expression type of its own..
+	 */
+	if (expr->op == '*')
+		return x86_access(expr);
+	if (expr->op == SPECIAL_INCREMENT || expr->op == SPECIAL_DECREMENT)
+		return emit_inc_dec(expr, 0);
+	return emit_regular_preop(expr);
+}
+
+static struct storage *x86_symbol_expr(struct symbol *sym)
+{
+	struct storage *new = stack_alloc(4);
+
+	if (sym->ctype.modifiers & (MOD_TOPLEVEL | MOD_EXTERN | MOD_STATIC)) {
+		printf("\tmovi.%d\t\tv%d,$%s\n", bits_in_pointer, new->pseudo, show_ident(sym->ident));
+		return new;
+	}
+	if (sym->ctype.modifiers & MOD_ADDRESSABLE) {
+		printf("\taddi.%d\t\tv%d,vFP,$%lld\n", bits_in_pointer, new->pseudo, sym->value);
+		return new;
+	}
+	printf("\taddi.%d\t\tv%d,vFP,$offsetof(%s:%p)\n", bits_in_pointer, new->pseudo, show_ident(sym->ident), sym);
+	return new;
+}
+
+static void x86_symbol_init(struct symbol *sym)
+{
+	struct symbol_private *priv = sym->aux;
+	struct expression *expr = sym->initializer;
+	struct storage *new;
+
+	if (expr)
+		new = x86_expression(expr);
+	else
+		new = stack_alloc(sym->bit_size / 8);
+
+	if (!priv) {
+		priv = calloc(1, sizeof(*priv));
+		sym->aux = priv;
+		/* FIXME: leak! we don't free... */
+		/* (well, we don't free symbols either) */
+	}
+
+	priv->addr = new;
+}
+
+static int type_is_signed(struct symbol *sym)
+{
+	if (sym->type == SYM_NODE)
+		sym = sym->ctype.base_type;
+	if (sym->type == SYM_PTR)
+		return 0;
+	return !(sym->ctype.modifiers & MOD_UNSIGNED);
+}
+
+static struct storage *x86_label_expr(struct expression *expr)
+{
+	struct storage *new = stack_alloc(4);
+	printf("\tmovi.%d\t\tv%d,.L%p\n", bits_in_pointer, new->pseudo, expr->label_symbol);
+	return new;
+}
+
+static struct storage *x86_statement_expr(struct expression *expr)
+{
+	return x86_statement(expr->statement);
+}
+
+static int x86_position_expr(struct expression *expr, struct symbol *base)
+{
+	struct storage *new = x86_expression(expr->init_expr);
+	struct symbol *ctype = expr->init_expr->ctype;
+
+	printf("\tinsert v%d at [%d:%d] of %s\n", new->pseudo,
+		expr->init_offset, ctype->bit_offset,
+		show_ident(base->ident));
+	return 0;
+}
+
+static void x86_initializer_expr(struct expression *expr, struct symbol *ctype)
+{
+	struct expression *entry;
+
+	FOR_EACH_PTR(expr->expr_list, entry) {
+		// Nested initializers have their positions already
+		// recursively calculated - just output them too
+		if (entry->type == EXPR_INITIALIZER) {
+			x86_initializer_expr(entry, ctype);
+			continue;
+		}
+
+		// Ignore initializer indexes and identifiers - the
+		// evaluator has taken them into account
+		if (entry->type == EXPR_IDENTIFIER || entry->type == EXPR_INDEX)
+			continue;
+		if (entry->type == EXPR_POS) {
+			x86_position_expr(entry, ctype);
+			continue;
+		}
+		x86_initialization(ctype, entry);
+	} END_FOR_EACH_PTR(entry);
+}
+
+/*
+ * Print out an expression. Return the pseudo that contains the
+ * variable.
+ */
+static struct storage *x86_expression(struct expression *expr)
+{
+	if (!expr)
+		return NULL;
+
+	if (!expr->ctype) {
+		struct position *pos = &expr->pos;
+		printf("\tno type at %s:%d:%d\n",
+			stream_name(pos->stream),
+			pos->line, pos->pos);
+		return NULL;
+	}
+
+	switch (expr->type) {
+	default:
+		return NULL;
+	case EXPR_CALL:
+		return x86_call_expression(expr);
+
+	case EXPR_ASSIGNMENT:
+		return x86_assignment(expr);
+
+	case EXPR_COMPARE:
+		return emit_compare(expr);
+	case EXPR_BINOP:
+	case EXPR_COMMA:
+	case EXPR_LOGICAL:
+		return emit_binop(expr);
+	case EXPR_PREOP:
+		return x86_preop(expr);
+	case EXPR_POSTOP:
+		return emit_postop(expr);
+	case EXPR_SYMBOL:
+		return emit_symbol_expr_init(expr->symbol);
+	case EXPR_DEREF:
+	case EXPR_SIZEOF:
+	case EXPR_ALIGNOF:
+		warning(expr->pos, "invalid expression after evaluation");
+		return NULL;
+	case EXPR_CAST:
+	case EXPR_FORCE_CAST:
+	case EXPR_IMPLIED_CAST:
+		return emit_cast_expr(expr);
+	case EXPR_VALUE:
+		return emit_value(expr);
+	case EXPR_STRING:
+		return emit_string_expr(expr);
+	case EXPR_INITIALIZER:
+		x86_initializer_expr(expr, expr->ctype);
+		return NULL;
+	case EXPR_SELECT:
+		return emit_select_expr(expr);
+	case EXPR_CONDITIONAL:
+		return emit_conditional_expr(expr);
+	case EXPR_STATEMENT:
+		return x86_statement_expr(expr);
+	case EXPR_LABEL:
+		return x86_label_expr(expr);
+
+	// None of these should exist as direct expressions: they are only
+	// valid as sub-expressions of initializers.
+	case EXPR_POS:
+		warning(expr->pos, "unable to show plain initializer position expression");
+		return NULL;
+	case EXPR_IDENTIFIER:
+		warning(expr->pos, "unable to show identifier expression");
+		return NULL;
+	case EXPR_INDEX:
+		warning(expr->pos, "unable to show index expression");
+		return NULL;
+	case EXPR_TYPE:
+		warning(expr->pos, "unable to show type expression");
+		return NULL;
+	case EXPR_FVALUE:
+		warning(expr->pos, "floating point support is not implemented");
+		return NULL;
+	}
+	return NULL;
+}
diff --git a/deps/sparse/compile.c b/deps/sparse/compile.c
new file mode 100644
index 0000000..d405b22
--- /dev/null
+++ b/deps/sparse/compile.c
@@ -0,0 +1,68 @@
+/*
+ * Example trivial client program that uses the sparse library
+ * and x86 backend.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003 Linus Torvalds
+ * Copyright 2003 Jeff Garzik
+ *
+ *  Licensed under the Open Software License version 1.1
+ *
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "expression.h"
+#include "compile.h"
+
+static void clean_up_symbols(struct symbol_list *list)
+{
+	struct symbol *sym;
+
+	FOR_EACH_PTR(list, sym) {
+		expand_symbol(sym);
+		emit_one_symbol(sym);
+	} END_FOR_EACH_PTR(sym);
+}
+
+int main(int argc, char **argv)
+{
+	char *file;
+	struct string_list *filelist = NULL;
+
+	clean_up_symbols(sparse_initialize(argc, argv, &filelist));
+	FOR_EACH_PTR_NOTAG(filelist, file) {
+		struct symbol_list *list;
+		const char *basename = strrchr(file, '/');
+		basename = basename ?  basename+1 : file;
+
+		list = sparse(file);
+
+		// Do type evaluation and simplification
+		emit_unit_begin(basename);
+		clean_up_symbols(list);
+		emit_unit_end();
+	} END_FOR_EACH_PTR_NOTAG(file);
+
+#if 0
+	// And show the allocation statistics
+	show_ident_alloc();
+	show_token_alloc();
+	show_symbol_alloc();
+	show_expression_alloc();
+	show_statement_alloc();
+	show_string_alloc();
+	show_bytes_alloc();
+#endif
+	return 0;
+}
diff --git a/deps/sparse/compile.h b/deps/sparse/compile.h
new file mode 100644
index 0000000..177363a
--- /dev/null
+++ b/deps/sparse/compile.h
@@ -0,0 +1,10 @@
+#ifndef COMPILE_H
+#define COMPILE_H
+
+struct symbol;
+
+extern void emit_one_symbol(struct symbol *);
+extern void emit_unit_begin(const char *);
+extern void emit_unit_end(void);
+
+#endif /* COMPILE_H */
diff --git a/deps/sparse/cse.c b/deps/sparse/cse.c
new file mode 100644
index 0000000..e8fbe34
--- /dev/null
+++ b/deps/sparse/cse.c
@@ -0,0 +1,400 @@
+/*
+ * CSE - walk the linearized instruction flow, and
+ * see if we can simplify it and apply CSE on it.
+ *
+ * Copyright (C) 2004 Linus Torvalds
+ */
+
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <assert.h>
+
+#include "parse.h"
+#include "expression.h"
+#include "linearize.h"
+#include "flow.h"
+
+#define INSN_HASH_SIZE 256
+static struct instruction_list *insn_hash_table[INSN_HASH_SIZE];
+
+int repeat_phase;
+
+static int phi_compare(pseudo_t phi1, pseudo_t phi2)
+{
+	const struct instruction *def1 = phi1->def;
+	const struct instruction *def2 = phi2->def;
+
+	if (def1->src1 != def2->src1)
+		return def1->src1 < def2->src1 ? -1 : 1;
+	if (def1->bb != def2->bb)
+		return def1->bb < def2->bb ? -1 : 1;
+	return 0;
+}
+
+
+static void clean_up_one_instruction(struct basic_block *bb, struct instruction *insn)
+{
+	unsigned long hash;
+
+	if (!insn->bb)
+		return;
+	assert(insn->bb == bb);
+	repeat_phase |= simplify_instruction(insn);
+	hash = (insn->opcode << 3) + (insn->size >> 3);
+	switch (insn->opcode) {
+	case OP_SEL:
+		hash += hashval(insn->src3);
+		/* Fall through */	
+
+	/* Binary arithmetic */
+	case OP_ADD: case OP_SUB:
+	case OP_MULU: case OP_MULS:
+	case OP_DIVU: case OP_DIVS:
+	case OP_MODU: case OP_MODS:
+	case OP_SHL:
+	case OP_LSR: case OP_ASR:
+	case OP_AND: case OP_OR:
+
+	/* Binary logical */
+	case OP_XOR: case OP_AND_BOOL:
+	case OP_OR_BOOL:
+
+	/* Binary comparison */
+	case OP_SET_EQ: case OP_SET_NE:
+	case OP_SET_LE: case OP_SET_GE:
+	case OP_SET_LT: case OP_SET_GT:
+	case OP_SET_B:  case OP_SET_A:
+	case OP_SET_BE: case OP_SET_AE:
+		hash += hashval(insn->src2);
+		/* Fall through */
+	
+	/* Unary */
+	case OP_NOT: case OP_NEG:
+		hash += hashval(insn->src1);
+		break;
+
+	case OP_SETVAL:
+		hash += hashval(insn->val);
+		break;
+
+	case OP_SYMADDR:
+		hash += hashval(insn->symbol);
+		break;
+
+	case OP_CAST:
+	case OP_SCAST:
+	case OP_PTRCAST:
+		/*
+		 * This is crap! Many "orig_types" are the
+		 * same as far as casts go, we should generate
+		 * some kind of "type hash" that is identical
+		 * for identical casts
+		 */
+		hash += hashval(insn->orig_type);
+		hash += hashval(insn->src);
+		break;
+
+	/* Other */
+	case OP_PHI: {
+		pseudo_t phi;
+		FOR_EACH_PTR(insn->phi_list, phi) {
+			struct instruction *def;
+			if (phi == VOID || !phi->def)
+				continue;
+			def = phi->def;
+			hash += hashval(def->src1);
+			hash += hashval(def->bb);
+		} END_FOR_EACH_PTR(phi);
+		break;
+	}
+
+	default:
+		/*
+		 * Nothing to do, don't even bother hashing them,
+		 * we're not going to try to CSE them
+		 */
+		return;
+	}
+	hash += hash >> 16;
+	hash &= INSN_HASH_SIZE-1;
+	add_instruction(insn_hash_table + hash, insn);
+}
+
+static void clean_up_insns(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		struct instruction *insn;
+		FOR_EACH_PTR(bb->insns, insn) {
+			clean_up_one_instruction(bb, insn);
+		} END_FOR_EACH_PTR(insn);
+	} END_FOR_EACH_PTR(bb);
+}
+
+/* Compare two (sorted) phi-lists */
+static int phi_list_compare(struct pseudo_list *l1, struct pseudo_list *l2)
+{
+	pseudo_t phi1, phi2;
+
+	PREPARE_PTR_LIST(l1, phi1);
+	PREPARE_PTR_LIST(l2, phi2);
+	for (;;) {
+		int cmp;
+
+		while (phi1 && (phi1 == VOID || !phi1->def))
+			NEXT_PTR_LIST(phi1);
+		while (phi2 && (phi2 == VOID || !phi2->def))
+			NEXT_PTR_LIST(phi2);
+
+		if (!phi1)
+			return phi2 ? -1 : 0;
+		if (!phi2)
+			return phi1 ? 1 : 0;
+		cmp = phi_compare(phi1, phi2);
+		if (cmp)
+			return cmp;
+		NEXT_PTR_LIST(phi1);
+		NEXT_PTR_LIST(phi2);
+	}
+	/* Not reached, but we need to make the nesting come out right */
+	FINISH_PTR_LIST(phi2);
+	FINISH_PTR_LIST(phi1);
+}
+
+static int insn_compare(const void *_i1, const void *_i2)
+{
+	const struct instruction *i1 = _i1;
+	const struct instruction *i2 = _i2;
+
+	if (i1->opcode != i2->opcode)
+		return i1->opcode < i2->opcode ? -1 : 1;
+
+	switch (i1->opcode) {
+	case OP_SEL:
+		if (i1->src3 != i2->src3)
+			return i1->src3 < i2->src3 ? -1 : 1;
+		/* Fall-through to binops */
+
+	/* Binary arithmetic */
+	case OP_ADD: case OP_SUB:
+	case OP_MULU: case OP_MULS:
+	case OP_DIVU: case OP_DIVS:
+	case OP_MODU: case OP_MODS:
+	case OP_SHL:
+	case OP_LSR: case OP_ASR:
+	case OP_AND: case OP_OR:
+
+	/* Binary logical */
+	case OP_XOR: case OP_AND_BOOL:
+	case OP_OR_BOOL:
+
+	/* Binary comparison */
+	case OP_SET_EQ: case OP_SET_NE:
+	case OP_SET_LE: case OP_SET_GE:
+	case OP_SET_LT: case OP_SET_GT:
+	case OP_SET_B:  case OP_SET_A:
+	case OP_SET_BE: case OP_SET_AE:
+		if (i1->src2 != i2->src2)
+			return i1->src2 < i2->src2 ? -1 : 1;
+		/* Fall through to unops */
+
+	/* Unary */
+	case OP_NOT: case OP_NEG:
+		if (i1->src1 != i2->src1)
+			return i1->src1 < i2->src1 ? -1 : 1;
+		break;
+
+	case OP_SYMADDR:
+		if (i1->symbol != i2->symbol)
+			return i1->symbol < i2->symbol ? -1 : 1;
+		break;
+
+	case OP_SETVAL:
+		if (i1->val != i2->val)
+			return i1->val < i2->val ? -1 : 1;
+		break;
+
+	/* Other */
+	case OP_PHI:
+		return phi_list_compare(i1->phi_list, i2->phi_list);
+
+	case OP_CAST:
+	case OP_SCAST:
+	case OP_PTRCAST:
+		/*
+		 * This is crap! See the comments on hashing.
+		 */
+		if (i1->orig_type != i2->orig_type)
+			return i1->orig_type < i2->orig_type ? -1 : 1;
+		if (i1->src != i2->src)
+			return i1->src < i2->src ? -1 : 1;
+		break;
+
+	default:
+		warning(i1->pos, "bad instruction on hash chain");
+	}
+	if (i1->size != i2->size)
+		return i1->size < i2->size ? -1 : 1;
+	return 0;
+}
+
+static void sort_instruction_list(struct instruction_list **list)
+{
+	sort_list((struct ptr_list **)list , insn_compare);
+}
+
+static struct instruction * cse_one_instruction(struct instruction *insn, struct instruction *def)
+{
+	convert_instruction_target(insn, def->target);
+
+	if (insn->opcode == OP_PHI) {
+		/* Remove the instruction from PHI users */
+		pseudo_t phi;
+		FOR_EACH_PTR(insn->phi_list, phi) {
+			struct pseudo_user *pu;
+			FOR_EACH_PTR(phi->users, pu) {
+				if (pu->insn == insn)
+					DELETE_CURRENT_PTR(pu);
+			} END_FOR_EACH_PTR(pu);
+		} END_FOR_EACH_PTR(phi);
+	}
+
+	insn->opcode = OP_NOP;
+	insn->bb = NULL;
+	repeat_phase |= REPEAT_CSE;
+	return def;
+}
+
+/*
+ * Does "bb1" dominate "bb2"?
+ */
+static int bb_dominates(struct entrypoint *ep, struct basic_block *bb1, struct basic_block *bb2, unsigned long generation)
+{
+	struct basic_block *parent;
+
+	/* Nothing dominates the entrypoint.. */
+	if (bb2 == ep->entry->bb)
+		return 0;
+	FOR_EACH_PTR(bb2->parents, parent) {
+		if (parent == bb1)
+			continue;
+		if (parent->generation == generation)
+			continue;
+		parent->generation = generation;
+		if (!bb_dominates(ep, bb1, parent, generation))
+			return 0;
+	} END_FOR_EACH_PTR(parent);
+	return 1;
+}
+
+static struct basic_block *trivial_common_parent(struct basic_block *bb1, struct basic_block *bb2)
+{
+	struct basic_block *parent;
+
+	if (bb_list_size(bb1->parents) != 1)
+		return NULL;
+	parent = first_basic_block(bb1->parents);
+	if (bb_list_size(bb2->parents) != 1)
+		return NULL;
+	if (first_basic_block(bb2->parents) != parent)
+		return NULL;
+	return parent;
+}
+
+static inline void remove_instruction(struct instruction_list **list, struct instruction *insn, int count)
+{
+	delete_ptr_list_entry((struct ptr_list **)list, insn, count);
+}
+
+static void add_instruction_to_end(struct instruction *insn, struct basic_block *bb)
+{
+	struct instruction *br = delete_last_instruction(&bb->insns);
+	insn->bb = bb;
+	add_instruction(&bb->insns, insn);
+	add_instruction(&bb->insns, br);
+}
+
+static struct instruction * try_to_cse(struct entrypoint *ep, struct instruction *i1, struct instruction *i2)
+{
+	struct basic_block *b1, *b2, *common;
+
+	/*
+	 * OK, i1 and i2 are the same instruction, modulo "target".
+	 * We should now see if we can combine them.
+	 */
+	b1 = i1->bb;
+	b2 = i2->bb;
+
+	/*
+	 * Currently we only handle the uninteresting degenerate case where
+	 * the CSE is inside one basic-block.
+	 */
+	if (b1 == b2) {
+		struct instruction *insn;
+		FOR_EACH_PTR(b1->insns, insn) {
+			if (insn == i1)
+				return cse_one_instruction(i2, i1);
+			if (insn == i2)
+				return cse_one_instruction(i1, i2);
+		} END_FOR_EACH_PTR(insn);
+		warning(b1->pos, "Whaa? unable to find CSE instructions");
+		return i1;
+	}
+	if (bb_dominates(ep, b1, b2, ++bb_generation))
+		return cse_one_instruction(i2, i1);
+
+	if (bb_dominates(ep, b2, b1, ++bb_generation))
+		return cse_one_instruction(i1, i2);
+
+	/* No direct dominance - but we could try to find a common ancestor.. */
+	common = trivial_common_parent(b1, b2);
+	if (common) {
+		i1 = cse_one_instruction(i2, i1);
+		remove_instruction(&b1->insns, i1, 1);
+		add_instruction_to_end(i1, common);
+	}
+
+	return i1;
+}
+
+void cleanup_and_cse(struct entrypoint *ep)
+{
+	int i;
+
+	simplify_memops(ep);
+repeat:
+	repeat_phase = 0;
+	clean_up_insns(ep);
+	for (i = 0; i < INSN_HASH_SIZE; i++) {
+		struct instruction_list **list = insn_hash_table + i;
+		if (*list) {
+			if (instruction_list_size(*list) > 1) {
+				struct instruction *insn, *last;
+
+				sort_instruction_list(list);
+
+				last = NULL;
+				FOR_EACH_PTR(*list, insn) {
+					if (!insn->bb)
+						continue;
+					if (last) {
+						if (!insn_compare(last, insn))
+							insn = try_to_cse(ep, last, insn);
+					}
+					last = insn;
+				} END_FOR_EACH_PTR(insn);
+			}
+			free_ptr_list((struct ptr_list **)list);
+		}
+	}
+
+	if (repeat_phase & REPEAT_SYMBOL_CLEANUP)
+		simplify_memops(ep);
+
+	if (repeat_phase & REPEAT_CSE)
+		goto repeat;
+}
diff --git a/deps/sparse/ctags.c b/deps/sparse/ctags.c
new file mode 100644
index 0000000..7e129a6
--- /dev/null
+++ b/deps/sparse/ctags.c
@@ -0,0 +1,211 @@
+/*
+ * Sparse Ctags
+ *
+ * Ctags generates tags from preprocessing results.
+ *
+ * Copyright (C) 2006 Christopher Li
+ *
+ * Licensed under the Open Software License version 1.1
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "parse.h"
+#include "scope.h"
+
+static struct symbol_list *taglist = NULL;
+
+static void examine_symbol(struct symbol *sym);
+
+#define MAX(_x,_y) ((_x) > (_y) ? (_x) : (_y))
+
+static int cmp_sym(const void *m, const void *n)
+{
+	const struct ident *a = ((const struct symbol *)m)->ident;
+	const struct ident *b = ((const struct symbol *)n)->ident;
+	int ret = strncmp(a->name, b->name, MAX(a->len, b->len));
+	if (!ret) {
+		const struct position a_pos = ((const struct symbol *)m)->pos;
+		const struct position b_pos = ((const struct symbol *)n)->pos;
+
+		ret = strcmp(stream_name(a_pos.stream),
+		             stream_name(b_pos.stream));
+		if (!ret)
+			return a_pos.line < b_pos.line;
+	}
+	return ret;
+}
+
+static void show_tag_header(FILE *fp)
+{
+	fprintf(fp, "!_TAG_FILE_FORMAT\t2\t/extended format; --format=1 will not append ;\" to lines/\n");
+	fprintf(fp, "!_TAG_FILE_SORTED\t0\t/0=unsorted, 1=sorted, 2=foldcase/\n");
+	fprintf(fp, "!_TAG_PROGRAM_AUTHOR\tChristopher Li\t/sparse chrisli org/\n");
+	fprintf(fp, "!_TAG_PROGRAM_NAME\tSparse Ctags\t//\n");
+	fprintf(fp, "!_TAG_PROGRAM_URL\thttp://www.kernel.org/pub/software/devel/sparse/\t/official site/\n");
+	fprintf(fp, "!_TAG_PROGRAM_VERSION\t0.01\t//\n");
+}
+
+static inline void show_symbol_tag(FILE *fp, struct symbol *sym)
+{
+	fprintf(fp, "%s\t%s\t%d;\"\t%c\tfile:\n", show_ident(sym->ident),
+	       stream_name(sym->pos.stream), sym->pos.line, (int)sym->kind);
+}
+
+static void show_tags(struct symbol_list *list)
+{
+	struct symbol *sym;
+	struct ident *ident = NULL;
+	struct position pos = {};
+	static const char *filename;
+	FILE *fp;
+
+	if (!list)
+		return;
+
+	fp = fopen("tags", "w");
+	if (!fp) {
+		perror("open tags file");
+		return;
+	}
+	show_tag_header(fp);
+	FOR_EACH_PTR(list, sym) {
+		if (ident == sym->ident && pos.line == sym->pos.line &&
+		    !strcmp(filename, stream_name(sym->pos.stream)))
+			continue;
+
+		show_symbol_tag(fp, sym);
+		ident = sym->ident;
+		pos = sym->pos;
+		filename = stream_name(sym->pos.stream);
+	} END_FOR_EACH_PTR(sym);
+	fclose(fp);
+}
+
+static inline void add_tag(struct symbol *sym)
+{
+	if (sym->ident && !sym->visited) {
+		sym->visited = 1;
+		add_symbol(&taglist, sym);
+	}
+}
+
+static inline void examine_members(struct symbol_list *list)
+{
+	struct symbol *sym;
+
+	FOR_EACH_PTR(list, sym) {
+		sym->kind = 'm';
+		examine_symbol(sym);
+	} END_FOR_EACH_PTR(sym);
+}
+
+static void examine_symbol(struct symbol *sym)
+{
+	struct symbol *base = sym;
+
+	if (!sym || sym->visited)
+		return;
+	if (sym->ident && sym->ident->reserved)
+		return;
+	if (sym->type == SYM_KEYWORD || sym->type == SYM_PREPROCESSOR)
+		return;
+
+	add_tag(sym);
+	base = sym->ctype.base_type;
+
+	switch (sym->type) {
+	case SYM_NODE:
+		if (base->type == SYM_FN)
+			sym->kind = 'f';
+		examine_symbol(base);
+		break;
+	case SYM_STRUCT:
+		sym->kind = 's';
+		examine_members(sym->symbol_list);
+		break;
+	case SYM_UNION:
+		sym->kind = 'u';
+		examine_members(sym->symbol_list);
+		break;
+	case SYM_ENUM:
+		sym->kind = 'e';
+	case SYM_PTR:
+	case SYM_TYPEOF:
+	case SYM_BITFIELD:
+	case SYM_FN:
+	case SYM_ARRAY:
+		examine_symbol(sym->ctype.base_type);
+		break;
+	case SYM_BASETYPE:
+		break;
+
+	default:
+		die("unknown symbol %s namespace:%d type:%d\n", show_ident(sym->ident),
+		    sym->namespace, sym->type);
+	}
+	if (!sym->kind)
+		sym->kind = 'v';
+	return;
+}
+
+static void examine_namespace(struct symbol *sym)
+{
+	if (sym->visited)
+		return;
+	if (sym->ident && sym->ident->reserved)
+		return;
+
+	switch(sym->namespace) {
+	case NS_KEYWORD:
+	case NS_PREPROCESSOR:
+		return;
+	case NS_LABEL:
+		sym->kind = 'l';
+		break;
+	case NS_MACRO:
+	case NS_UNDEF:
+		sym->kind = 'd';
+		break;
+	case NS_TYPEDEF:
+		sym->kind = 't';
+	case NS_SYMBOL:
+	case NS_STRUCT:
+		examine_symbol(sym);
+		break;
+	default:
+		die("unknown namespace %d symbol:%s type:%d\n", sym->namespace,
+		    show_ident(sym->ident), sym->type);
+	}
+	add_tag(sym);
+}
+
+static inline void examine_symbol_list(struct symbol_list *list)
+{
+	struct symbol *sym;
+
+	if (!list)
+		return;
+	FOR_EACH_PTR(list, sym) {
+		examine_namespace(sym);
+	} END_FOR_EACH_PTR(sym);
+}
+
+int main(int argc, char **argv)
+{
+	struct string_list *filelist = NULL;
+	char *file;
+
+	examine_symbol_list(sparse_initialize(argc, argv, &filelist));
+	FOR_EACH_PTR_NOTAG(filelist, file) {
+		sparse(file);
+		examine_symbol_list(file_scope->symbols);
+	} END_FOR_EACH_PTR_NOTAG(file);
+	examine_symbol_list(global_scope->symbols);
+	sort_list((struct ptr_list **)&taglist, cmp_sym);
+	show_tags(taglist);
+	return 0;
+}
diff --git a/deps/sparse/dissect.c b/deps/sparse/dissect.c
new file mode 100644
index 0000000..61240d7
--- /dev/null
+++ b/deps/sparse/dissect.c
@@ -0,0 +1,578 @@
+/*
+ * sparse/dissect.c
+ *
+ * Started by Oleg Nesterov <oleg tv-sign ru>
+ *
+ * Licensed under the Open Software License version 1.1
+ */
+
+#include "dissect.h"
+
+#define	U_VOID	 0x00
+#define	U_SELF	((1 << U_SHIFT) - 1)
+#define	U_MASK	(U_R_VAL | U_W_VAL | U_R_AOF)
+
+#define	DO_LIST(l__, p__, expr__)		\
+	do {					\
+		typeof(l__->list[0]) p__;	\
+		FOR_EACH_PTR(l__, p__)		\
+			expr__;			\
+		END_FOR_EACH_PTR(p__);		\
+	} while (0)
+
+#define	DO_2_LIST(l1__,l2__, p1__,p2__, expr__)	\
+	do {					\
+		typeof(l1__->list[0]) p1__;	\
+		typeof(l2__->list[0]) p2__;	\
+		PREPARE_PTR_LIST(l1__, p1__);	\
+		FOR_EACH_PTR(l2__, p2__)	\
+			expr__;			\
+			NEXT_PTR_LIST(p1__);	\
+		END_FOR_EACH_PTR(p2__);		\
+		FINISH_PTR_LIST(p1__);		\
+	} while (0)
+
+
+typedef unsigned usage_t;
+
+static struct reporter *reporter;
+static struct symbol *return_type;
+
+static void do_sym_list(struct symbol_list *list);
+
+static struct symbol
+	*base_type(struct symbol *sym),
+	*do_initializer(struct symbol *type, struct expression *expr),
+	*do_expression(usage_t mode, struct expression *expr),
+	*do_statement(usage_t mode, struct statement *stmt);
+
+static inline int is_ptr(struct symbol *type)
+{
+	return type->type == SYM_PTR || type->type == SYM_ARRAY;
+}
+
+static inline usage_t u_rval(usage_t mode)
+{
+	return mode & (U_R_VAL | (U_MASK << U_SHIFT))
+		? U_R_VAL : 0;
+}
+
+static inline usage_t u_addr(usage_t mode)
+{
+	return mode = mode & U_MASK
+		? U_R_AOF | (mode & U_W_AOF) : 0;
+}
+
+static usage_t u_lval(struct symbol *type)
+{
+	int wptr = is_ptr(type) && !(type->ctype.modifiers & MOD_CONST);
+	return wptr || type == &bad_ctype
+		? U_W_AOF | U_R_VAL : U_R_VAL;
+}
+
+static usage_t fix_mode(struct symbol *type, usage_t mode)
+{
+	mode &= (U_SELF | (U_SELF << U_SHIFT));
+
+	switch (type->type) {
+		case SYM_BASETYPE:
+			if (!type->ctype.base_type)
+				break;
+		case SYM_ENUM:
+		case SYM_BITFIELD:
+			if (mode & U_MASK)
+				mode &= U_SELF;
+		default:
+
+		break; case SYM_FN:
+			if (mode & U_R_VAL)
+				mode |= U_R_AOF;
+			mode &= ~(U_R_VAL | U_W_AOF);
+
+		break; case SYM_ARRAY:
+			if (mode & (U_MASK << U_SHIFT))
+				mode >>= U_SHIFT;
+			else if (mode != U_W_VAL)
+				mode = u_addr(mode);
+	}
+
+	if (!(mode & U_R_AOF))
+		mode &= ~U_W_AOF;
+
+	return mode;
+}
+
+static inline struct symbol *no_member(struct ident *name)
+{
+	static struct symbol sym = {
+		.type = SYM_BAD,
+	};
+
+	sym.ctype.base_type = &bad_ctype;
+	sym.ident = name;
+
+	return &sym;
+}
+
+static struct symbol *report_member(mode_t mode, struct position *pos,
+					struct symbol *type, struct symbol *mem)
+{
+	struct symbol *ret = mem->ctype.base_type;
+
+	if (reporter->r_member)
+		reporter->r_member(fix_mode(ret, mode), pos, type, mem);
+
+	return ret;
+}
+
+static void report_implicit(usage_t mode, struct position *pos, struct symbol *type)
+{
+	if (type->type != SYM_STRUCT && type->type != SYM_UNION)
+		return;
+
+	if (!reporter->r_member)
+		return;
+
+	if (type->ident != NULL)
+		reporter->r_member(mode, pos, type, NULL);
+
+	DO_LIST(type->symbol_list, mem,
+		report_implicit(mode, pos, base_type(mem)));
+}
+
+static inline struct symbol *expr_symbol(struct expression *expr)
+{
+	struct symbol *sym = expr->symbol;
+
+	if (!sym) {
+		sym = lookup_symbol(expr->symbol_name, NS_SYMBOL);
+
+		if (!sym) {
+			sym = alloc_symbol(expr->pos, SYM_BAD);
+			bind_symbol(sym, expr->symbol_name, NS_SYMBOL);
+			sym->ctype.modifiers = MOD_EXTERN;
+		}
+	}
+
+	if (!sym->ctype.base_type)
+		sym->ctype.base_type = &bad_ctype;
+
+	return sym;
+}
+
+static struct symbol *report_symbol(usage_t mode, struct expression *expr)
+{
+	struct symbol *sym = expr_symbol(expr);
+	struct symbol *ret = base_type(sym);
+
+	if (0 && ret->type == SYM_ENUM)
+		return report_member(mode, &expr->pos, ret, expr->symbol);
+
+	if (reporter->r_symbol)
+		reporter->r_symbol(fix_mode(ret, mode), &expr->pos, sym);
+
+	return ret;
+}
+
+static inline struct ident *mk_name(struct ident *root, struct ident *node)
+{
+	char name[256];
+
+	snprintf(name, sizeof(name), "%.*s:%.*s",
+			root ? root->len : 0, root ? root->name : "",
+			node ? node->len : 0, node ? node->name : "");
+
+	return built_in_ident(name);
+}
+
+static void examine_sym_node(struct symbol *node, struct ident *root)
+{
+	struct symbol *base;
+	struct ident *name;
+
+	if (node->examined)
+		return;
+
+	node->examined = 1;
+	name = node->ident;
+
+	while ((base = node->ctype.base_type) != NULL)
+		switch (base->type) {
+		case SYM_TYPEOF:
+			node->ctype.base_type =
+				do_expression(U_VOID, base->initializer);
+			break;
+
+		case SYM_ARRAY:
+			do_expression(U_R_VAL, base->array_size);
+		case SYM_PTR: case SYM_FN:
+			node = base;
+			break;
+
+		case SYM_STRUCT: case SYM_UNION: //case SYM_ENUM:
+			if (base->evaluated)
+				return;
+			if (!base->symbol_list)
+				return;
+			base->evaluated = 1;
+
+			if (!base->ident && name)
+				base->ident = mk_name(root, name);
+			if (base->ident && reporter->r_symdef)
+				reporter->r_symdef(base);
+			DO_LIST(base->symbol_list, mem,
+				examine_sym_node(mem, base->ident ?: root));
+		default:
+			return;
+		}
+}
+
+static struct symbol *base_type(struct symbol *sym)
+{
+	if (!sym)
+		return &bad_ctype;
+
+	if (sym->type == SYM_NODE)
+		examine_sym_node(sym, NULL);
+
+	return sym->ctype.base_type	// builtin_fn_type
+		?: &bad_ctype;
+}
+
+static struct symbol *__lookup_member(struct symbol *type, struct ident *name, int *p_addr)
+{
+	struct symbol *node;
+	int addr = 0;
+
+	FOR_EACH_PTR(type->symbol_list, node)
+		if (!name) {
+			if (addr == *p_addr)
+				return node;
+		}
+		else if (node->ident == NULL) {
+			node = __lookup_member(node->ctype.base_type, name, NULL);
+			if (node)
+				goto found;
+		}
+		else if (node->ident == name) {
+found:
+			if (p_addr)
+				*p_addr = addr;
+			return node;
+		}
+		addr++;
+	END_FOR_EACH_PTR(node);
+
+	return NULL;
+}
+
+static struct symbol *lookup_member(struct symbol *type, struct ident *name, int *addr)
+{
+	return __lookup_member(type, name, addr)
+		?: no_member(name);
+}
+
+static struct expression *peek_preop(struct expression *expr, int op)
+{
+	do {
+		if (expr->type != EXPR_PREOP)
+			break;
+		if (expr->op == op)
+			return expr->unop;
+		if (expr->op == '(')
+			expr = expr->unop;
+		else
+			break;
+	} while (expr);
+
+	return NULL;
+}
+
+static struct symbol *do_expression(usage_t mode, struct expression *expr)
+{
+	struct symbol *ret = &int_ctype;
+
+again:
+	if (expr) switch (expr->type) {
+	default:
+		warning(expr->pos, "bad expr->type: %d", expr->type);
+
+	case EXPR_TYPE:		// [struct T]; Why ???
+	case EXPR_VALUE:
+	case EXPR_FVALUE:
+
+	break; case EXPR_LABEL:
+		ret = &label_ctype;
+
+	break; case EXPR_STRING:
+		ret = &string_ctype;
+
+	break; case EXPR_STATEMENT:
+		ret = do_statement(mode, expr->statement);
+
+	break; case EXPR_SIZEOF: case EXPR_ALIGNOF: case EXPR_PTRSIZEOF:
+		do_expression(U_VOID, expr->cast_expression);
+
+	break; case EXPR_COMMA:
+		do_expression(U_VOID, expr->left);
+		ret = do_expression(mode, expr->right);
+
+	break; case EXPR_CAST: case EXPR_FORCE_CAST: //case EXPR_IMPLIED_CAST:
+		ret = base_type(expr->cast_type);
+		do_initializer(ret, expr->cast_expression);
+
+	break; case EXPR_COMPARE: case EXPR_LOGICAL:
+		mode = u_rval(mode);
+		do_expression(mode, expr->left);
+		do_expression(mode, expr->right);
+
+	break; case EXPR_CONDITIONAL: //case EXPR_SELECT:
+		do_expression(expr->cond_true
+					? U_R_VAL : U_R_VAL | mode,
+				expr->conditional);
+		ret = do_expression(mode, expr->cond_true);
+		ret = do_expression(mode, expr->cond_false);
+
+	break; case EXPR_CALL:
+		ret = do_expression(U_R_PTR, expr->fn);
+		if (is_ptr(ret))
+			ret = ret->ctype.base_type;
+		DO_2_LIST(ret->arguments, expr->args, arg, val,
+			do_expression(u_lval(base_type(arg)), val));
+		ret = ret->type == SYM_FN ? base_type(ret)
+			: &bad_ctype;
+
+	break; case EXPR_ASSIGNMENT:
+		mode |= U_W_VAL | U_R_VAL;
+		if (expr->op == '=')
+			mode &= ~U_R_VAL;
+		ret = do_expression(mode, expr->left);
+		report_implicit(mode, &expr->pos, ret);
+		mode = expr->op == '='
+			? u_lval(ret) : U_R_VAL;
+		do_expression(mode, expr->right);
+
+	break; case EXPR_BINOP: {
+		struct symbol *l, *r;
+		mode |= u_rval(mode);
+		l = do_expression(mode, expr->left);
+		r = do_expression(mode, expr->right);
+		if (expr->op != '+' && expr->op != '-')
+			;
+		else if (!is_ptr_type(r))
+			ret = l;
+		else if (!is_ptr_type(l))
+			ret = r;
+	}
+
+	break; case EXPR_PREOP: case EXPR_POSTOP: {
+		struct expression *unop = expr->unop;
+
+		switch (expr->op) {
+		case SPECIAL_INCREMENT:
+		case SPECIAL_DECREMENT:
+			mode |= U_W_VAL | U_R_VAL;
+		default:
+			mode |= u_rval(mode);
+		case '(':
+			ret = do_expression(mode, unop);
+
+		break; case '&':
+			if ((expr = peek_preop(unop, '*')))
+				goto again;
+			ret = alloc_symbol(unop->pos, SYM_PTR);
+			ret->ctype.base_type =
+				do_expression(u_addr(mode), unop);
+
+		break; case '*':
+			if ((expr = peek_preop(unop, '&')))
+				goto again;
+			if (mode & (U_MASK << U_SHIFT))
+				mode |= U_R_VAL;
+			mode <<= U_SHIFT;
+			if (mode & (U_R_AOF << U_SHIFT))
+				mode |= U_R_VAL;
+			if (mode & (U_W_VAL << U_SHIFT))
+				mode |= U_W_AOF;
+			ret = do_expression(mode, unop);
+			ret = is_ptr(ret) ? base_type(ret)
+				: &bad_ctype;
+		}
+	}
+
+	break; case EXPR_DEREF: {
+		struct symbol *p_type;
+		usage_t p_mode;
+
+		p_mode = mode & U_SELF;
+		if (!(mode & U_MASK) && (mode & (U_MASK << U_SHIFT)))
+			p_mode = U_R_VAL;
+		p_type = do_expression(p_mode, expr->deref);
+
+		ret = report_member(mode, &expr->pos, p_type,
+			lookup_member(p_type, expr->member, NULL));
+	}
+
+	break; case EXPR_SYMBOL:
+		ret = report_symbol(mode, expr);
+	}
+
+	return ret;
+}
+
+static void do_asm_xputs(usage_t mode, struct expression_list *xputs)
+{
+	int nr = 0;
+
+	DO_LIST(xputs, expr,
+		if (++nr % 3 == 0)
+			do_expression(U_W_AOF | mode, expr));
+}
+
+static struct symbol *do_statement(usage_t mode, struct statement *stmt)
+{
+	struct symbol *ret = &void_ctype;
+
+	if (stmt) switch (stmt->type) {
+	default:
+		warning(stmt->pos, "bad stmt->type: %d", stmt->type);
+
+	case STMT_NONE:
+	case STMT_RANGE:
+	case STMT_CONTEXT:
+
+	break; case STMT_DECLARATION:
+		do_sym_list(stmt->declaration);
+
+	break; case STMT_EXPRESSION:
+		ret = do_expression(mode, stmt->expression);
+
+	break; case STMT_RETURN:
+		do_expression(u_lval(return_type), stmt->expression);
+
+	break; case STMT_ASM:
+		do_expression(U_R_VAL, stmt->asm_string);
+		do_asm_xputs(U_W_VAL, stmt->asm_outputs);
+		do_asm_xputs(U_R_VAL, stmt->asm_inputs);
+
+	break; case STMT_COMPOUND: {
+		int count;
+
+		count = statement_list_size(stmt->stmts);
+		DO_LIST(stmt->stmts, st,
+			ret = do_statement(--count ? U_VOID : mode, st));
+	}
+
+	break; case STMT_ITERATOR:
+		do_sym_list(stmt->iterator_syms);
+		do_statement(U_VOID, stmt->iterator_pre_statement);
+		do_expression(U_R_VAL, stmt->iterator_pre_condition);
+		do_statement(U_VOID, stmt->iterator_post_statement);
+		do_statement(U_VOID, stmt->iterator_statement);
+		do_expression(U_R_VAL, stmt->iterator_post_condition);
+
+	break; case STMT_IF:
+		do_expression(U_R_VAL, stmt->if_conditional);
+		do_statement(U_VOID, stmt->if_true);
+		do_statement(U_VOID, stmt->if_false);
+
+	break; case STMT_SWITCH:
+		do_expression(U_R_VAL, stmt->switch_expression);
+		do_statement(U_VOID, stmt->switch_statement);
+
+	break; case STMT_CASE:
+		do_expression(U_R_VAL, stmt->case_expression);
+		do_expression(U_R_VAL, stmt->case_to);
+		do_statement(U_VOID, stmt->case_statement);
+
+	break; case STMT_GOTO:
+		do_expression(U_R_PTR, stmt->goto_expression);
+
+	break; case STMT_LABEL:
+		do_statement(mode, stmt->label_statement);
+
+	}
+
+	return ret;
+}
+
+static struct symbol *do_initializer(struct symbol *type, struct expression *expr)
+{
+	struct symbol *m_type;
+	struct expression *m_expr;
+	int m_addr;
+
+	if (expr) switch (expr->type) {
+	default:
+		do_expression(u_lval(type), expr);
+
+	break; case EXPR_INDEX:
+		do_initializer(base_type(type), expr->idx_expression);
+
+	break; case EXPR_INITIALIZER:
+		m_addr = 0;
+		FOR_EACH_PTR(expr->expr_list, m_expr)
+			if (type->type == SYM_ARRAY) {
+				m_type = base_type(type);
+				if (m_expr->type == EXPR_INDEX)
+					m_expr = m_expr->idx_expression;
+			} else {
+				struct position *pos = &m_expr->pos;
+				struct ident *m_name = NULL;
+
+				if (m_expr->type == EXPR_IDENTIFIER) {
+					m_name = m_expr->expr_ident;
+					m_expr = m_expr->ident_expression;
+				}
+
+				m_type = report_member(U_W_VAL, pos, type,
+						lookup_member(type, m_name, &m_addr));
+				if (m_expr->type != EXPR_INITIALIZER)
+					report_implicit(U_W_VAL, pos, m_type);
+			}
+			do_initializer(m_type, m_expr);
+			m_addr++;
+		END_FOR_EACH_PTR(m_expr);
+	}
+
+	return type;
+}
+
+static inline struct symbol *do_symbol(struct symbol *sym)
+{
+	struct symbol *type;
+
+	type = base_type(sym);
+
+	if (reporter->r_symdef)
+		reporter->r_symdef(sym);
+
+	switch (type->type) {
+	default:
+		if (!sym->initializer)
+			break;
+		if (reporter->r_symbol)
+			reporter->r_symbol(U_W_VAL, &sym->pos, sym);
+		do_initializer(type, sym->initializer);
+
+	break; case SYM_FN:
+		do_sym_list(type->arguments);
+		return_type = base_type(type);
+		do_statement(U_VOID, sym->ctype.modifiers & MOD_INLINE
+					? type->inline_stmt
+					: type->stmt);
+	}
+
+	return type;
+}
+
+static void do_sym_list(struct symbol_list *list)
+{
+	DO_LIST(list, sym, do_symbol(sym));
+}
+
+void dissect(struct symbol_list *list, struct reporter *rep)
+{
+	reporter = rep;
+	do_sym_list(list);
+}
diff --git a/deps/sparse/dissect.h b/deps/sparse/dissect.h
new file mode 100644
index 0000000..3b72b89
--- /dev/null
+++ b/deps/sparse/dissect.h
@@ -0,0 +1,40 @@
+#ifndef	DISSECT_H
+#define	DISSECT_H
+
+#include <stdio.h>
+#include "parse.h"
+#include "expression.h"
+
+#define	U_SHIFT		8
+
+#define	U_R_AOF		0x01
+#define	U_W_AOF		0x02
+
+#define	U_R_VAL		0x04
+#define	U_W_VAL		0x08
+
+#define	U_R_PTR		(U_R_VAL << U_SHIFT)
+#define	U_W_PTR		(U_W_VAL << U_SHIFT)
+
+struct reporter
+{
+	void (*r_symdef)(struct symbol *);
+
+	void (*r_symbol)(unsigned, struct position *, struct symbol *);
+	void (*r_member)(unsigned, struct position *, struct symbol *, struct symbol *);
+};
+
+extern void dissect(struct symbol_list *, struct reporter *);
+
+#define	MK_IDENT(s)	({				\
+	static struct {					\
+		struct ident ident;			\
+		char __[sizeof(s)];			\
+	} ident = {{					\
+		.len  = sizeof(s)-1,			\
+		.name = s,				\
+	}};						\
+	&ident.ident;					\
+})
+
+#endif
diff --git a/deps/sparse/evaluate.c b/deps/sparse/evaluate.c
new file mode 100644
index 0000000..bebe968
--- /dev/null
+++ b/deps/sparse/evaluate.c
@@ -0,0 +1,3390 @@
+/*
+ * sparse/evaluate.c
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ *
+ * Evaluate constant expressions.
+ */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "parse.h"
+#include "token.h"
+#include "symbol.h"
+#include "target.h"
+#include "expression.h"
+
+struct symbol *current_fn;
+
+static struct symbol *degenerate(struct expression *expr);
+static struct symbol *evaluate_symbol(struct symbol *sym);
+
+static struct symbol *evaluate_symbol_expression(struct expression *expr)
+{
+	struct expression *addr;
+	struct symbol *sym = expr->symbol;
+	struct symbol *base_type;
+
+	if (!sym) {
+		expression_error(expr, "undefined identifier '%s'", show_ident(expr->symbol_name));
+		return NULL;
+	}
+
+	examine_symbol_type(sym);
+
+	base_type = get_base_type(sym);
+	if (!base_type) {
+		expression_error(expr, "identifier '%s' has no type", show_ident(expr->symbol_name));
+		return NULL;
+	}
+
+	addr = alloc_expression(expr->pos, EXPR_SYMBOL);
+	addr->symbol = sym;
+	addr->symbol_name = expr->symbol_name;
+	addr->ctype = &lazy_ptr_ctype;	/* Lazy evaluation: we need to do a proper job if somebody does &sym */
+	expr->type = EXPR_PREOP;
+	expr->op = '*';
+	expr->unop = addr;
+
+	/* The type of a symbol is the symbol itself! */
+	expr->ctype = sym;
+	return sym;
+}
+
+static struct symbol *evaluate_string(struct expression *expr)
+{
+	struct symbol *sym = alloc_symbol(expr->pos, SYM_NODE);
+	struct symbol *array = alloc_symbol(expr->pos, SYM_ARRAY);
+	struct expression *addr = alloc_expression(expr->pos, EXPR_SYMBOL);
+	struct expression *initstr = alloc_expression(expr->pos, EXPR_STRING);
+	unsigned int length = expr->string->length;
+
+	sym->array_size = alloc_const_expression(expr->pos, length);
+	sym->bit_size = bytes_to_bits(length);
+	sym->ctype.alignment = 1;
+	sym->string = 1;
+	sym->ctype.modifiers = MOD_STATIC;
+	sym->ctype.base_type = array;
+	sym->initializer = initstr;
+
+	initstr->ctype = sym;
+	initstr->string = expr->string;
+
+	array->array_size = sym->array_size;
+	array->bit_size = bytes_to_bits(length);
+	array->ctype.alignment = 1;
+	array->ctype.modifiers = MOD_STATIC;
+	array->ctype.base_type = &char_ctype;
+	
+	addr->symbol = sym;
+	addr->ctype = &lazy_ptr_ctype;
+
+	expr->type = EXPR_PREOP;
+	expr->op = '*';
+	expr->unop = addr;  
+	expr->ctype = sym;
+	return sym;
+}
+
+/* type has come from classify_type and is an integer type */
+static inline struct symbol *integer_promotion(struct symbol *type)
+{
+	struct symbol *orig_type = type;
+	unsigned long mod =  type->ctype.modifiers;
+	int width = type->bit_size;
+
+	/*
+	 * Bitfields always promote to the base type,
+	 * even if the bitfield might be bigger than
+	 * an "int".
+	 */
+	if (type->type == SYM_BITFIELD) {
+		type = type->ctype.base_type;
+		orig_type = type;
+	}
+	mod = type->ctype.modifiers;
+	if (width < bits_in_int)
+		return &int_ctype;
+
+	/* If char/short has as many bits as int, it still gets "promoted" */
+	if (mod & (MOD_CHAR | MOD_SHORT)) {
+		if (mod & MOD_UNSIGNED)
+			return &uint_ctype;
+		return &int_ctype;
+	}
+	return orig_type;
+}
+
+/*
+ * integer part of usual arithmetic conversions:
+ *	integer promotions are applied
+ *	if left and right are identical, we are done
+ *	if signedness is the same, convert one with lower rank
+ *	unless unsigned argument has rank lower than signed one, convert the
+ *	signed one.
+ *	if signed argument is bigger than unsigned one, convert the unsigned.
+ *	otherwise, convert signed.
+ *
+ * Leaving aside the integer promotions, that is equivalent to
+ *	if identical, don't convert
+ *	if left is bigger than right, convert right
+ *	if right is bigger than left, convert right
+ *	otherwise, if signedness is the same, convert one with lower rank
+ *	otherwise convert the signed one.
+ */
+static struct symbol *bigger_int_type(struct symbol *left, struct symbol *right)
+{
+	unsigned long lmod, rmod;
+
+	left = integer_promotion(left);
+	right = integer_promotion(right);
+
+	if (left == right)
+		goto left;
+
+	if (left->bit_size > right->bit_size)
+		goto left;
+
+	if (right->bit_size > left->bit_size)
+		goto right;
+
+	lmod = left->ctype.modifiers;
+	rmod = right->ctype.modifiers;
+	if ((lmod ^ rmod) & MOD_UNSIGNED) {
+		if (lmod & MOD_UNSIGNED)
+			goto left;
+	} else if ((lmod & ~rmod) & (MOD_LONG_ALL))
+		goto left;
+right:
+	left = right;
+left:
+	return left;
+}
+
+static int same_cast_type(struct symbol *orig, struct symbol *new)
+{
+	return orig->bit_size == new->bit_size && orig->bit_offset == new->bit_offset;
+}
+
+static struct symbol *base_type(struct symbol *node, unsigned long *modp, unsigned long *asp)
+{
+	unsigned long mod, as;
+
+	mod = 0; as = 0;
+	while (node) {
+		mod |= node->ctype.modifiers;
+		as |= node->ctype.as;
+		if (node->type == SYM_NODE) {
+			node = node->ctype.base_type;
+			continue;
+		}
+		break;
+	}
+	*modp = mod & ~MOD_IGNORE;
+	*asp = as;
+	return node;
+}
+
+static int is_same_type(struct expression *expr, struct symbol *new)
+{
+	struct symbol *old = expr->ctype;
+	unsigned long oldmod, newmod, oldas, newas;
+
+	old = base_type(old, &oldmod, &oldas);
+	new = base_type(new, &newmod, &newas);
+
+	/* Same base type, same address space? */
+	if (old == new && oldas == newas) {
+		unsigned long difmod;
+
+		/* Check the modifier bits. */
+		difmod = (oldmod ^ newmod) & ~MOD_NOCAST;
+
+		/* Exact same type? */
+		if (!difmod)
+			return 1;
+
+		/*
+		 * Not the same type, but differs only in "const".
+		 * Don't warn about MOD_NOCAST.
+		 */
+		if (difmod == MOD_CONST)
+			return 0;
+	}
+	if ((oldmod | newmod) & MOD_NOCAST) {
+		const char *tofrom = "to/from";
+		if (!(newmod & MOD_NOCAST))
+			tofrom = "from";
+		if (!(oldmod & MOD_NOCAST))
+			tofrom = "to";
+		warning(expr->pos, "implicit cast %s nocast type", tofrom);
+	}
+	return 0;
+}
+
+static void
+warn_for_different_enum_types (struct position pos,
+			       struct symbol *typea,
+			       struct symbol *typeb)
+{
+	if (!Wenum_mismatch)
+		return;
+	if (typea->type == SYM_NODE)
+		typea = typea->ctype.base_type;
+	if (typeb->type == SYM_NODE)
+		typeb = typeb->ctype.base_type;
+
+	if (typea == typeb)
+		return;
+
+	if (typea->type == SYM_ENUM && typeb->type == SYM_ENUM) {
+		warning(pos, "mixing different enum types");
+		info(pos, "    %s versus", show_typename(typea));
+		info(pos, "    %s", show_typename(typeb));
+	}
+}
+
+/*
+ * This gets called for implicit casts in assignments and
+ * integer promotion. We often want to try to move the
+ * cast down, because the ops involved may have been
+ * implicitly cast up, and we can get rid of the casts
+ * early.
+ */
+static struct expression * cast_to(struct expression *old, struct symbol *type)
+{
+	struct expression *expr;
+
+	warn_for_different_enum_types (old->pos, old->ctype, type);
+
+	if (old->ctype != &null_ctype && is_same_type(old, type))
+		return old;
+
+	/*
+	 * See if we can simplify the op. Move the cast down.
+	 */
+	switch (old->type) {
+	case EXPR_PREOP:
+		if (old->ctype->bit_size < type->bit_size)
+			break;
+		if (old->op == '~') {
+			old->ctype = type;
+			old->unop = cast_to(old->unop, type);
+			return old;
+		}
+		break;
+
+	case EXPR_IMPLIED_CAST:
+		warn_for_different_enum_types(old->pos, old->ctype, type);
+
+		if (old->ctype->bit_size >= type->bit_size) {
+			struct expression *orig = old->cast_expression;
+			if (same_cast_type(orig->ctype, type))
+				return orig;
+			if (old->ctype->bit_offset == type->bit_offset) {
+				old->ctype = type;
+				old->cast_type = type;
+				return old;
+			}
+		}
+		break;
+
+	default:
+		/* nothing */;
+	}
+
+	expr = alloc_expression(old->pos, EXPR_IMPLIED_CAST);
+	expr->flags = old->flags;
+	expr->ctype = type;
+	expr->cast_type = type;
+	expr->cast_expression = old;
+	return expr;
+}
+
+enum {
+	TYPE_NUM = 1,
+	TYPE_BITFIELD = 2,
+	TYPE_RESTRICT = 4,
+	TYPE_FLOAT = 8,
+	TYPE_PTR = 16,
+	TYPE_COMPOUND = 32,
+	TYPE_FOULED = 64,
+	TYPE_FN = 128,
+};
+
+static inline int classify_type(struct symbol *type, struct symbol **base)
+{
+	static int type_class[SYM_BAD + 1] = {
+		[SYM_PTR] = TYPE_PTR,
+		[SYM_FN] = TYPE_PTR | TYPE_FN,
+		[SYM_ARRAY] = TYPE_PTR | TYPE_COMPOUND,
+		[SYM_STRUCT] = TYPE_COMPOUND,
+		[SYM_UNION] = TYPE_COMPOUND,
+		[SYM_BITFIELD] = TYPE_NUM | TYPE_BITFIELD,
+		[SYM_RESTRICT] = TYPE_NUM | TYPE_RESTRICT,
+		[SYM_FOULED] = TYPE_NUM | TYPE_RESTRICT | TYPE_FOULED,
+	};
+	if (type->type == SYM_NODE)
+		type = type->ctype.base_type;
+	if (type->type == SYM_TYPEOF) {
+		type = evaluate_expression(type->initializer);
+		if (!type)
+			type = &bad_ctype;
+		else if (type->type == SYM_NODE)
+			type = type->ctype.base_type;
+	}
+	if (type->type == SYM_ENUM)
+		type = type->ctype.base_type;
+	*base = type;
+	if (type->type == SYM_BASETYPE) {
+		if (type->ctype.base_type == &int_type)
+			return TYPE_NUM;
+		if (type->ctype.base_type == &fp_type)
+			return TYPE_NUM | TYPE_FLOAT;
+	}
+	return type_class[type->type];
+}
+
+#define is_int(class) ((class & (TYPE_NUM | TYPE_FLOAT)) == TYPE_NUM)
+
+static inline int is_string_type(struct symbol *type)
+{
+	if (type->type == SYM_NODE)
+		type = type->ctype.base_type;
+	return type->type == SYM_ARRAY && is_byte_type(type->ctype.base_type);
+}
+
+static struct symbol *bad_expr_type(struct expression *expr)
+{
+	sparse_error(expr->pos, "incompatible types for operation (%s)", show_special(expr->op));
+	switch (expr->type) {
+	case EXPR_BINOP:
+	case EXPR_COMPARE:
+		info(expr->pos, "   left side has type %s", show_typename(expr->left->ctype));
+		info(expr->pos, "   right side has type %s", show_typename(expr->right->ctype));
+		break;
+	case EXPR_PREOP:
+	case EXPR_POSTOP:
+		info(expr->pos, "   argument has type %s", show_typename(expr->unop->ctype));
+		break;
+	default:
+		break;
+	}
+
+	expr->flags = 0;
+	return expr->ctype = &bad_ctype;
+}
+
+static int restricted_value(struct expression *v, struct symbol *type)
+{
+	if (v->type != EXPR_VALUE)
+		return 1;
+	if (v->value != 0)
+		return 1;
+	return 0;
+}
+
+static int restricted_binop(int op, struct symbol *type)
+{
+	switch (op) {
+		case '&':
+		case '=':
+		case SPECIAL_AND_ASSIGN:
+		case SPECIAL_OR_ASSIGN:
+		case SPECIAL_XOR_ASSIGN:
+			return 1;	/* unfoul */
+		case '|':
+		case '^':
+		case '?':
+			return 2;	/* keep fouled */
+		case SPECIAL_EQUAL:
+		case SPECIAL_NOTEQUAL:
+			return 3;	/* warn if fouled */
+		default:
+			return 0;	/* warn */
+	}
+}
+
+static int restricted_unop(int op, struct symbol **type)
+{
+	if (op == '~') {
+		if ((*type)->bit_size < bits_in_int)
+			*type = befoul(*type);
+		return 0;
+	} if (op == '+')
+		return 0;
+	return 1;
+}
+
+/* type should be SYM_FOULED */
+static inline struct symbol *unfoul(struct symbol *type)
+{
+	return type->ctype.base_type;
+}
+
+static struct symbol *restricted_binop_type(int op,
+					struct expression *left,
+					struct expression *right,
+					int lclass, int rclass,
+					struct symbol *ltype,
+					struct symbol *rtype)
+{
+	struct symbol *ctype = NULL;
+	if (lclass & TYPE_RESTRICT) {
+		if (rclass & TYPE_RESTRICT) {
+			if (ltype == rtype) {
+				ctype = ltype;
+			} else if (lclass & TYPE_FOULED) {
+				if (unfoul(ltype) == rtype)
+					ctype = ltype;
+			} else if (rclass & TYPE_FOULED) {
+				if (unfoul(rtype) == ltype)
+					ctype = rtype;
+			}
+		} else {
+			if (!restricted_value(right, ltype))
+				ctype = ltype;
+		}
+	} else if (!restricted_value(left, rtype))
+		ctype = rtype;
+
+	if (ctype) {
+		switch (restricted_binop(op, ctype)) {
+		case 1:
+			if ((lclass ^ rclass) & TYPE_FOULED)
+				ctype = unfoul(ctype);
+			break;
+		case 3:
+			if (!(lclass & rclass & TYPE_FOULED))
+				break;
+		case 0:
+			ctype = NULL;
+		default:
+			break;
+		}
+	}
+
+	return ctype;
+}
+
+static inline void unrestrict(struct expression *expr,
+			      int class, struct symbol **ctype)
+{
+	if (class & TYPE_RESTRICT) {
+		if (class & TYPE_FOULED)
+			*ctype = unfoul(*ctype);
+		warning(expr->pos, "%s degrades to integer",
+			show_typename(*ctype));
+		*ctype = (*ctype)->ctype.base_type; /* get to arithmetic type */
+	}
+}
+
+static struct symbol *usual_conversions(int op,
+					struct expression *left,
+					struct expression *right,
+					int lclass, int rclass,
+					struct symbol *ltype,
+					struct symbol *rtype)
+{
+	struct symbol *ctype;
+
+	warn_for_different_enum_types(right->pos, left->ctype, right->ctype);
+
+	if ((lclass | rclass) & TYPE_RESTRICT)
+		goto Restr;
+
+Normal:
+	if (!(lclass & TYPE_FLOAT)) {
+		if (!(rclass & TYPE_FLOAT))
+			return bigger_int_type(ltype, rtype);
+		else
+			return rtype;
+	} else if (rclass & TYPE_FLOAT) {
+		unsigned long lmod = ltype->ctype.modifiers;
+		unsigned long rmod = rtype->ctype.modifiers;
+		if (rmod & ~lmod & (MOD_LONG_ALL))
+			return rtype;
+		else
+			return ltype;
+	} else
+		return ltype;
+
+Restr:
+	ctype = restricted_binop_type(op, left, right,
+				      lclass, rclass, ltype, rtype);
+	if (ctype)
+		return ctype;
+
+	unrestrict(left, lclass, &ltype);
+	unrestrict(right, rclass, &rtype);
+
+	goto Normal;
+}
+
+static inline int lvalue_expression(struct expression *expr)
+{
+	return expr->type == EXPR_PREOP && expr->op == '*';
+}
+
+static struct symbol *evaluate_ptr_add(struct expression *expr, struct symbol *itype)
+{
+	struct expression *index = expr->right;
+	struct symbol *ctype, *base;
+	int multiply;
+
+	classify_type(degenerate(expr->left), &ctype);
+	base = examine_pointer_target(ctype);
+
+	if (!base) {
+		expression_error(expr, "missing type information");
+		return NULL;
+	}
+	if (is_function(base)) {
+		expression_error(expr, "arithmetics on pointers to functions");
+		return NULL;
+	}
+
+	/* Get the size of whatever the pointer points to */
+	multiply = is_void_type(base) ? 1 : bits_to_bytes(base->bit_size);
+
+	if (ctype == &null_ctype)
+		ctype = &ptr_ctype;
+	expr->ctype = ctype;
+
+	if (multiply == 1 && itype->bit_size >= bits_in_pointer)
+		return ctype;
+
+	if (index->type == EXPR_VALUE) {
+		struct expression *val = alloc_expression(expr->pos, EXPR_VALUE);
+		unsigned long long v = index->value, mask;
+		mask = 1ULL << (itype->bit_size - 1);
+		if (v & mask)
+			v |= -mask;
+		else
+			v &= mask - 1;
+		v *= multiply;
+		mask = 1ULL << (bits_in_pointer - 1);
+		v &= mask | (mask - 1);
+		val->value = v;
+		val->ctype = ssize_t_ctype;
+		expr->right = val;
+		return ctype;
+	}
+
+	if (itype->bit_size < bits_in_pointer)
+		index = cast_to(index, ssize_t_ctype);
+
+	if (multiply > 1) {
+		struct expression *val = alloc_expression(expr->pos, EXPR_VALUE);
+		struct expression *mul = alloc_expression(expr->pos, EXPR_BINOP);
+
+		val->ctype = ssize_t_ctype;
+		val->value = multiply;
+
+		mul->op = '*';
+		mul->ctype = ssize_t_ctype;
+		mul->left = index;
+		mul->right = val;
+		index = mul;
+	}
+
+	expr->right = index;
+	return ctype;
+}
+
+static void examine_fn_arguments(struct symbol *fn);
+
+#define MOD_IGN (MOD_VOLATILE | MOD_CONST)
+
+const char *type_difference(struct ctype *c1, struct ctype *c2,
+	unsigned long mod1, unsigned long mod2)
+{
+	unsigned long as1 = c1->as, as2 = c2->as;
+	struct symbol *t1 = c1->base_type;
+	struct symbol *t2 = c2->base_type;
+	int move1 = 1, move2 = 1;
+	mod1 |= c1->modifiers;
+	mod2 |= c2->modifiers;
+	for (;;) {
+		unsigned long diff;
+		int type;
+		struct symbol *base1 = t1->ctype.base_type;
+		struct symbol *base2 = t2->ctype.base_type;
+
+		/*
+		 * FIXME! Collect alignment and context too here!
+		 */
+		if (move1) {
+			if (t1 && t1->type != SYM_PTR) {
+				mod1 |= t1->ctype.modifiers;
+				as1 |= t1->ctype.as;
+			}
+			move1 = 0;
+		}
+
+		if (move2) {
+			if (t2 && t2->type != SYM_PTR) {
+				mod2 |= t2->ctype.modifiers;
+				as2 |= t2->ctype.as;
+			}
+			move2 = 0;
+		}
+
+		if (t1 == t2)
+			break;
+		if (!t1 || !t2)
+			return "different types";
+
+		if (t1->type == SYM_NODE || t1->type == SYM_ENUM) {
+			t1 = base1;
+			move1 = 1;
+			if (!t1)
+				return "bad types";
+			continue;
+		}
+
+		if (t2->type == SYM_NODE || t2->type == SYM_ENUM) {
+			t2 = base2;
+			move2 = 1;
+			if (!t2)
+				return "bad types";
+			continue;
+		}
+
+		move1 = move2 = 1;
+		type = t1->type;
+		if (type != t2->type)
+			return "different base types";
+
+		switch (type) {
+		default:
+			sparse_error(t1->pos,
+				     "internal error: bad type in derived(%d)",
+				     type);
+			return "bad types";
+		case SYM_RESTRICT:
+			return "different base types";
+		case SYM_UNION:
+		case SYM_STRUCT:
+			/* allow definition of incomplete structs and unions */
+			if (t1->ident == t2->ident)
+			  return NULL;
+			return "different base types";
+		case SYM_ARRAY:
+			/* XXX: we ought to compare sizes */
+			break;
+		case SYM_PTR:
+			if (as1 != as2)
+				return "different address spaces";
+			/* MOD_SPECIFIER is due to idiocy in parse.c */
+			if ((mod1 ^ mod2) & ~MOD_IGNORE & ~MOD_SPECIFIER)
+				return "different modifiers";
+			/* we could be lazier here */
+			base1 = examine_pointer_target(t1);
+			base2 = examine_pointer_target(t2);
+			mod1 = t1->ctype.modifiers;
+			as1 = t1->ctype.as;
+			mod2 = t2->ctype.modifiers;
+			as2 = t2->ctype.as;
+			break;
+		case SYM_FN: {
+			struct symbol *arg1, *arg2;
+			int i;
+
+			if (as1 != as2)
+				return "different address spaces";
+			if ((mod1 ^ mod2) & ~MOD_IGNORE & ~MOD_SIGNEDNESS)
+				return "different modifiers";
+			mod1 = t1->ctype.modifiers;
+			as1 = t1->ctype.as;
+			mod2 = t2->ctype.modifiers;
+			as2 = t2->ctype.as;
+
+			if (base1->variadic != base2->variadic)
+				return "incompatible variadic arguments";
+			examine_fn_arguments(t1);
+			examine_fn_arguments(t2);
+			PREPARE_PTR_LIST(t1->arguments, arg1);
+			PREPARE_PTR_LIST(t2->arguments, arg2);
+			i = 1;
+			for (;;) {
+				const char *diffstr;
+				if (!arg1 && !arg2)
+					break;
+				if (!arg1 || !arg2)
+					return "different argument counts";
+				diffstr = type_difference(&arg1->ctype,
+							  &arg2->ctype,
+							  MOD_IGN, MOD_IGN);
+				if (diffstr) {
+					static char argdiff[80];
+					sprintf(argdiff, "incompatible argument %d (%s)", i, diffstr);
+					return argdiff;
+				}
+				NEXT_PTR_LIST(arg1);
+				NEXT_PTR_LIST(arg2);
+				i++;
+			}
+			FINISH_PTR_LIST(arg2);
+			FINISH_PTR_LIST(arg1);
+			break;
+		}
+		case SYM_BASETYPE:
+			if (as1 != as2)
+				return "different address spaces";
+			if (base1 != base2)
+				return "different base types";
+			diff = (mod1 ^ mod2) & ~MOD_IGNORE;
+			if (!diff)
+				return NULL;
+			if (diff & MOD_SIZE)
+				return "different type sizes";
+			else if (diff & ~MOD_SIGNEDNESS)
+				return "different modifiers";
+			else
+				return "different signedness";
+		}
+		t1 = base1;
+		t2 = base2;
+	}
+	if (as1 != as2)
+		return "different address spaces";
+	if ((mod1 ^ mod2) & ~MOD_IGNORE & ~MOD_SIGNEDNESS)
+		return "different modifiers";
+	return NULL;
+}
+
+static void bad_null(struct expression *expr)
+{
+	if (Wnon_pointer_null)
+		warning(expr->pos, "Using plain integer as NULL pointer");
+}
+
+static unsigned long target_qualifiers(struct symbol *type)
+{
+	unsigned long mod = type->ctype.modifiers & MOD_IGN;
+	if (type->ctype.base_type && type->ctype.base_type->type == SYM_ARRAY)
+		mod = 0;
+	return mod;
+}
+
+static struct symbol *evaluate_ptr_sub(struct expression *expr)
+{
+	const char *typediff;
+	struct symbol *ltype, *rtype;
+	struct expression *l = expr->left;
+	struct expression *r = expr->right;
+	struct symbol *lbase;
+
+	classify_type(degenerate(l), &ltype);
+	classify_type(degenerate(r), &rtype);
+
+	lbase = examine_pointer_target(ltype);
+	examine_pointer_target(rtype);
+	typediff = type_difference(&ltype->ctype, &rtype->ctype,
+				   target_qualifiers(rtype),
+				   target_qualifiers(ltype));
+	if (typediff)
+		expression_error(expr, "subtraction of different types can't work (%s)", typediff);
+
+	if (is_function(lbase)) {
+		expression_error(expr, "subtraction of functions? Share your drugs");
+		return NULL;
+	}
+
+	expr->ctype = ssize_t_ctype;
+	if (lbase->bit_size > bits_in_char) {
+		struct expression *sub = alloc_expression(expr->pos, EXPR_BINOP);
+		struct expression *div = expr;
+		struct expression *val = alloc_expression(expr->pos, EXPR_VALUE);
+		unsigned long value = bits_to_bytes(lbase->bit_size);
+
+		val->ctype = size_t_ctype;
+		val->value = value;
+
+		if (value & (value-1)) {
+			if (Wptr_subtraction_blows)
+				warning(expr->pos, "potentially expensive pointer subtraction");
+		}
+
+		sub->op = '-';
+		sub->ctype = ssize_t_ctype;
+		sub->left = l;
+		sub->right = r;
+
+		div->op = '/';
+		div->left = sub;
+		div->right = val;
+	}
+		
+	return ssize_t_ctype;
+}
+
+#define is_safe_type(type) ((type)->ctype.modifiers & MOD_SAFE)
+
+static struct symbol *evaluate_conditional(struct expression *expr, int iterator)
+{
+	struct symbol *ctype;
+
+	if (!expr)
+		return NULL;
+
+	if (!iterator && expr->type == EXPR_ASSIGNMENT && expr->op == '=')
+		warning(expr->pos, "assignment expression in conditional");
+
+	ctype = evaluate_expression(expr);
+	if (ctype) {
+		if (is_safe_type(ctype))
+			warning(expr->pos, "testing a 'safe expression'");
+	}
+
+	return ctype;
+}
+
+static struct symbol *evaluate_logical(struct expression *expr)
+{
+	if (!evaluate_conditional(expr->left, 0))
+		return NULL;
+	if (!evaluate_conditional(expr->right, 0))
+		return NULL;
+
+	expr->ctype = &bool_ctype;
+	if (expr->flags) {
+		if (!(expr->left->flags & expr->right->flags & Int_const_expr))
+			expr->flags = 0;
+	}
+	return &bool_ctype;
+}
+
+static struct symbol *evaluate_binop(struct expression *expr)
+{
+	struct symbol *ltype, *rtype, *ctype;
+	int lclass = classify_type(expr->left->ctype, &ltype);
+	int rclass = classify_type(expr->right->ctype, &rtype);
+	int op = expr->op;
+
+	if (expr->flags) {
+		if (!(expr->left->flags & expr->right->flags & Int_const_expr))
+			expr->flags = 0;
+	}
+
+	/* number op number */
+	if (lclass & rclass & TYPE_NUM) {
+		if ((lclass | rclass) & TYPE_FLOAT) {
+			switch (op) {
+			case '+': case '-': case '*': case '/':
+				break;
+			default:
+				return bad_expr_type(expr);
+			}
+		}
+
+		if (op == SPECIAL_LEFTSHIFT || op == SPECIAL_RIGHTSHIFT) {
+			// shifts do integer promotions, but that's it.
+			unrestrict(expr->left, lclass, &ltype);
+			unrestrict(expr->right, rclass, &rtype);
+			ctype = ltype = integer_promotion(ltype);
+			rtype = integer_promotion(rtype);
+		} else {
+			// The rest do usual conversions
+			const unsigned left_not  = expr->left->type == EXPR_PREOP
+			                           && expr->left->op == '!';
+			const unsigned right_not = expr->right->type == EXPR_PREOP
+			                           && expr->right->op == '!';
+			if ((op == '&' || op == '|') && (left_not || right_not))
+				warning(expr->pos, "dubious: %sx %c %sy",
+				        left_not ? "!" : "",
+					op,
+					right_not ? "!" : "");
+
+			ltype = usual_conversions(op, expr->left, expr->right,
+						  lclass, rclass, ltype, rtype);
+			ctype = rtype = ltype;
+		}
+
+		expr->left = cast_to(expr->left, ltype);
+		expr->right = cast_to(expr->right, rtype);
+		expr->ctype = ctype;
+		return ctype;
+	}
+
+	/* pointer (+|-) integer */
+	if (lclass & TYPE_PTR && is_int(rclass) && (op == '+' || op == '-')) {
+		unrestrict(expr->right, rclass, &rtype);
+		return evaluate_ptr_add(expr, rtype);
+	}
+
+	/* integer + pointer */
+	if (rclass & TYPE_PTR && is_int(lclass) && op == '+') {
+		struct expression *index = expr->left;
+		unrestrict(index, lclass, &ltype);
+		expr->left = expr->right;
+		expr->right = index;
+		return evaluate_ptr_add(expr, ltype);
+	}
+
+	/* pointer - pointer */
+	if (lclass & rclass & TYPE_PTR && expr->op == '-')
+		return evaluate_ptr_sub(expr);
+
+	return bad_expr_type(expr);
+}
+
+static struct symbol *evaluate_comma(struct expression *expr)
+{
+	expr->ctype = degenerate(expr->right);
+	if (expr->ctype == &null_ctype)
+		expr->ctype = &ptr_ctype;
+	expr->flags &= expr->left->flags & expr->right->flags;
+	return expr->ctype;
+}
+
+static int modify_for_unsigned(int op)
+{
+	if (op == '<')
+		op = SPECIAL_UNSIGNED_LT;
+	else if (op == '>')
+		op = SPECIAL_UNSIGNED_GT;
+	else if (op == SPECIAL_LTE)
+		op = SPECIAL_UNSIGNED_LTE;
+	else if (op == SPECIAL_GTE)
+		op = SPECIAL_UNSIGNED_GTE;
+	return op;
+}
+
+static inline int is_null_pointer_constant(struct expression *e)
+{
+	if (e->ctype == &null_ctype)
+		return 1;
+	if (!(e->flags & Int_const_expr))
+		return 0;
+	return is_zero_constant(e) ? 2 : 0;
+}
+
+static struct symbol *evaluate_compare(struct expression *expr)
+{
+	struct expression *left = expr->left, *right = expr->right;
+	struct symbol *ltype, *rtype, *lbase, *rbase;
+	int lclass = classify_type(degenerate(left), &ltype);
+	int rclass = classify_type(degenerate(right), &rtype);
+	struct symbol *ctype;
+	const char *typediff;
+
+	if (expr->flags) {
+		if (!(expr->left->flags & expr->right->flags & Int_const_expr))
+			expr->flags = 0;
+	}
+
+	/* Type types? */
+	if (is_type_type(ltype) && is_type_type(rtype))
+		goto OK;
+
+	if (is_safe_type(left->ctype) || is_safe_type(right->ctype))
+		warning(expr->pos, "testing a 'safe expression'");
+
+	/* number on number */
+	if (lclass & rclass & TYPE_NUM) {
+		ctype = usual_conversions(expr->op, expr->left, expr->right,
+					  lclass, rclass, ltype, rtype);
+		expr->left = cast_to(expr->left, ctype);
+		expr->right = cast_to(expr->right, ctype);
+		if (ctype->ctype.modifiers & MOD_UNSIGNED)
+			expr->op = modify_for_unsigned(expr->op);
+		goto OK;
+	}
+
+	/* at least one must be a pointer */
+	if (!((lclass | rclass) & TYPE_PTR))
+		return bad_expr_type(expr);
+
+	/* equality comparisons can be with null pointer constants */
+	if (expr->op == SPECIAL_EQUAL || expr->op == SPECIAL_NOTEQUAL) {
+		int is_null1 = is_null_pointer_constant(left);
+		int is_null2 = is_null_pointer_constant(right);
+		if (is_null1 == 2)
+			bad_null(left);
+		if (is_null2 == 2)
+			bad_null(right);
+		if (is_null1 && is_null2) {
+			int positive = expr->op == SPECIAL_EQUAL;
+			expr->type = EXPR_VALUE;
+			expr->value = positive;
+			goto OK;
+		}
+		if (is_null1 && (rclass & TYPE_PTR)) {
+			left = cast_to(left, rtype);
+			goto OK;
+		}
+		if (is_null2 && (lclass & TYPE_PTR)) {
+			right = cast_to(right, ltype);
+			goto OK;
+		}
+	}
+	/* both should be pointers */
+	if (!(lclass & rclass & TYPE_PTR))
+		return bad_expr_type(expr);
+	expr->op = modify_for_unsigned(expr->op);
+
+	lbase = examine_pointer_target(ltype);
+	rbase = examine_pointer_target(rtype);
+
+	/* they also have special treatment for pointers to void */
+	if (expr->op == SPECIAL_EQUAL || expr->op == SPECIAL_NOTEQUAL) {
+		if (ltype->ctype.as == rtype->ctype.as) {
+			if (lbase == &void_ctype) {
+				right = cast_to(right, ltype);
+				goto OK;
+			}
+			if (rbase == &void_ctype) {
+				left = cast_to(left, rtype);
+				goto OK;
+			}
+		}
+	}
+
+	typediff = type_difference(&ltype->ctype, &rtype->ctype,
+				   target_qualifiers(rtype),
+				   target_qualifiers(ltype));
+	if (!typediff)
+		goto OK;
+
+	expression_error(expr, "incompatible types in comparison expression (%s)", typediff);
+	return NULL;
+
+OK:
+	expr->ctype = &bool_ctype;
+	return &bool_ctype;
+}
+
+/*
+ * NOTE! The degenerate case of "x ? : y", where we don't
+ * have a true case, this will possibly promote "x" to the
+ * same type as "y", and thus _change_ the conditional
+ * test in the expression. But since promotion is "safe"
+ * for testing, that's OK.
+ */
+static struct symbol *evaluate_conditional_expression(struct expression *expr)
+{
+	struct expression **true;
+	struct symbol *ctype, *ltype, *rtype, *lbase, *rbase;
+	int lclass, rclass;
+	const char * typediff;
+	int qual;
+
+	if (!evaluate_conditional(expr->conditional, 0))
+		return NULL;
+	if (!evaluate_expression(expr->cond_false))
+		return NULL;
+
+	ctype = degenerate(expr->conditional);
+	rtype = degenerate(expr->cond_false);
+
+	true = &expr->conditional;
+	ltype = ctype;
+	if (expr->cond_true) {
+		if (!evaluate_expression(expr->cond_true))
+			return NULL;
+		ltype = degenerate(expr->cond_true);
+		true = &expr->cond_true;
+	}
+
+	if (expr->flags) {
+		int flags = expr->conditional->flags & Int_const_expr;
+		flags &= (*true)->flags & expr->cond_false->flags;
+		if (!flags)
+			expr->flags = 0;
+	}
+
+	lclass = classify_type(ltype, &ltype);
+	rclass = classify_type(rtype, &rtype);
+	if (lclass & rclass & TYPE_NUM) {
+		ctype = usual_conversions('?', *true, expr->cond_false,
+					  lclass, rclass, ltype, rtype);
+		*true = cast_to(*true, ctype);
+		expr->cond_false = cast_to(expr->cond_false, ctype);
+		goto out;
+	}
+
+	if ((lclass | rclass) & TYPE_PTR) {
+		int is_null1 = is_null_pointer_constant(*true);
+		int is_null2 = is_null_pointer_constant(expr->cond_false);
+
+		if (is_null1 && is_null2) {
+			*true = cast_to(*true, &ptr_ctype);
+			expr->cond_false = cast_to(expr->cond_false, &ptr_ctype);
+			ctype = &ptr_ctype;
+			goto out;
+		}
+		if (is_null1 && (rclass & TYPE_PTR)) {
+			if (is_null1 == 2)
+				bad_null(*true);
+			*true = cast_to(*true, rtype);
+			ctype = rtype;
+			goto out;
+		}
+		if (is_null2 && (lclass & TYPE_PTR)) {
+			if (is_null2 == 2)
+				bad_null(expr->cond_false);
+			expr->cond_false = cast_to(expr->cond_false, ltype);
+			ctype = ltype;
+			goto out;
+		}
+		if (!(lclass & rclass & TYPE_PTR)) {
+			typediff = "different types";
+			goto Err;
+		}
+		/* OK, it's pointer on pointer */
+		if (ltype->ctype.as != rtype->ctype.as) {
+			typediff = "different address spaces";
+			goto Err;
+		}
+
+		/* need to be lazier here */
+		lbase = examine_pointer_target(ltype);
+		rbase = examine_pointer_target(rtype);
+		qual = target_qualifiers(ltype) | target_qualifiers(rtype);
+
+		if (lbase == &void_ctype) {
+			/* XXX: pointers to function should warn here */
+			ctype = ltype;
+			goto Qual;
+
+		}
+		if (rbase == &void_ctype) {
+			/* XXX: pointers to function should warn here */
+			ctype = rtype;
+			goto Qual;
+		}
+		/* XXX: that should be pointer to composite */
+		ctype = ltype;
+		typediff = type_difference(&ltype->ctype, &rtype->ctype,
+					   qual, qual);
+		if (!typediff)
+			goto Qual;
+		goto Err;
+	}
+
+	/* void on void, struct on same struct, union on same union */
+	if (ltype == rtype) {
+		ctype = ltype;
+		goto out;
+	}
+	typediff = "different base types";
+
+Err:
+	expression_error(expr, "incompatible types in conditional expression (%s)", typediff);
+	return NULL;
+
+out:
+	expr->ctype = ctype;
+	return ctype;
+
+Qual:
+	if (qual & ~ctype->ctype.modifiers) {
+		struct symbol *sym = alloc_symbol(ctype->pos, SYM_PTR);
+		*sym = *ctype;
+		sym->ctype.modifiers |= qual;
+		ctype = sym;
+	}
+	*true = cast_to(*true, ctype);
+	expr->cond_false = cast_to(expr->cond_false, ctype);
+	goto out;
+}
+
+/* FP assignments can not do modulo or bit operations */
+static int compatible_float_op(int op)
+{
+	return	op == SPECIAL_ADD_ASSIGN ||
+		op == SPECIAL_SUB_ASSIGN ||
+		op == SPECIAL_MUL_ASSIGN ||
+		op == SPECIAL_DIV_ASSIGN;
+}
+
+static int evaluate_assign_op(struct expression *expr)
+{
+	struct symbol *target = expr->left->ctype;
+	struct symbol *source = expr->right->ctype;
+	struct symbol *t, *s;
+	int tclass = classify_type(target, &t);
+	int sclass = classify_type(source, &s);
+	int op = expr->op;
+
+	if (tclass & sclass & TYPE_NUM) {
+		if (tclass & TYPE_FLOAT && !compatible_float_op(op)) {
+			expression_error(expr, "invalid assignment");
+			return 0;
+		}
+		if (tclass & TYPE_RESTRICT) {
+			if (!restricted_binop(op, t)) {
+				warning(expr->pos, "bad assignment (%s) to %s",
+					show_special(op), show_typename(t));
+				expr->right = cast_to(expr->right, target);
+				return 0;
+			}
+			/* allowed assignments unfoul */
+			if (sclass & TYPE_FOULED && unfoul(s) == t)
+				goto Cast;
+			if (!restricted_value(expr->right, t))
+				return 1;
+		} else if (!(sclass & TYPE_RESTRICT))
+			goto Cast;
+		/* source and target would better be identical restricted */
+		if (t == s)
+			return 1;
+		warning(expr->pos, "invalid assignment: %s", show_special(op));
+		info(expr->pos, "   left side has type %s", show_typename(t));
+		info(expr->pos, "   right side has type %s", show_typename(s));
+		expr->right = cast_to(expr->right, target);
+		return 0;
+	}
+	if (tclass == TYPE_PTR && is_int(sclass)) {
+		if (op == SPECIAL_ADD_ASSIGN || op == SPECIAL_SUB_ASSIGN) {
+			unrestrict(expr->right, sclass, &s);
+			evaluate_ptr_add(expr, s);
+			return 1;
+		}
+		expression_error(expr, "invalid pointer assignment");
+		return 0;
+	}
+
+	expression_error(expr, "invalid assignment");
+	return 0;
+
+Cast:
+	expr->right = cast_to(expr->right, target);
+	return 1;
+}
+
+static int whitelist_pointers(struct symbol *t1, struct symbol *t2)
+{
+	if (t1 == t2)
+		return 0;	/* yes, 0 - we don't want a cast_to here */
+	if (t1 == &void_ctype)
+		return 1;
+	if (t2 == &void_ctype)
+		return 1;
+	if (classify_type(t1, &t1) != TYPE_NUM)
+		return 0;
+	if (classify_type(t2, &t2) != TYPE_NUM)
+		return 0;
+	if (t1 == t2)
+		return 1;
+	if (t1->ctype.modifiers & t2->ctype.modifiers & MOD_CHAR)
+		return 1;
+	if ((t1->ctype.modifiers ^ t2->ctype.modifiers) & MOD_SIZE)
+		return 0;
+	return !Wtypesign;
+}
+
+static int compatible_assignment_types(struct expression *expr, struct symbol *target,
+	struct expression **rp, const char *where)
+{
+	const char *typediff;
+	struct symbol *source = degenerate(*rp);
+	struct symbol *t, *s;
+	int tclass = classify_type(target, &t);
+	int sclass = classify_type(source, &s);
+
+	if (tclass & sclass & TYPE_NUM) {
+		if (tclass & TYPE_RESTRICT) {
+			/* allowed assignments unfoul */
+			if (sclass & TYPE_FOULED && unfoul(s) == t)
+				goto Cast;
+			if (!restricted_value(*rp, target))
+				return 1;
+			if (s == t)
+				return 1;
+		} else if (!(sclass & TYPE_RESTRICT))
+			goto Cast;
+		typediff = "different base types";
+		goto Err;
+	}
+
+	if (tclass == TYPE_PTR) {
+		unsigned long mod1, mod2;
+		struct symbol *b1, *b2;
+		// NULL pointer is always OK
+		int is_null = is_null_pointer_constant(*rp);
+		if (is_null) {
+			if (is_null == 2)
+				bad_null(*rp);
+			goto Cast;
+		}
+		if (!(sclass & TYPE_PTR)) {
+			typediff = "different base types";
+			goto Err;
+		}
+		b1 = examine_pointer_target(t);
+		b2 = examine_pointer_target(s);
+		mod1 = target_qualifiers(t);
+		mod2 = target_qualifiers(s);
+		if (whitelist_pointers(b1, b2)) {
+			/*
+			 * assignments to/from void * are OK, provided that
+			 * we do not remove qualifiers from pointed to [C]
+			 * or mix address spaces [sparse].
+			 */
+			if (t->ctype.as != s->ctype.as) {
+				typediff = "different address spaces";
+				goto Err;
+			}
+			if (mod2 & ~mod1) {
+				typediff = "different modifiers";
+				goto Err;
+			}
+			goto Cast;
+		}
+		/* It's OK if the target is more volatile or const than the source */
+		typediff = type_difference(&t->ctype, &s->ctype, 0, mod1);
+		if (typediff)
+			goto Err;
+		return 1;
+	}
+
+	if ((tclass & TYPE_COMPOUND) && s == t)
+		return 1;
+
+	if (tclass & TYPE_NUM) {
+		/* XXX: need to turn into comparison with NULL */
+		if (t == &bool_ctype && (sclass & TYPE_PTR))
+			goto Cast;
+		typediff = "different base types";
+		goto Err;
+	}
+	typediff = "invalid types";
+
+Err:
+	warning(expr->pos, "incorrect type in %s (%s)", where, typediff);
+	info(expr->pos, "   expected %s", show_typename(target));
+	info(expr->pos, "   got %s", show_typename(source));
+	*rp = cast_to(*rp, target);
+	return 0;
+Cast:
+	*rp = cast_to(*rp, target);
+	return 1;
+}
+
+static void mark_assigned(struct expression *expr)
+{
+	struct symbol *sym;
+
+	if (!expr)
+		return;
+	switch (expr->type) {
+	case EXPR_SYMBOL:
+		sym = expr->symbol;
+		if (!sym)
+			return;
+		if (sym->type != SYM_NODE)
+			return;
+		sym->ctype.modifiers |= MOD_ASSIGNED;
+		return;
+
+	case EXPR_BINOP:
+		mark_assigned(expr->left);
+		mark_assigned(expr->right);
+		return;
+	case EXPR_CAST:
+	case EXPR_FORCE_CAST:
+		mark_assigned(expr->cast_expression);
+		return;
+	case EXPR_SLICE:
+		mark_assigned(expr->base);
+		return;
+	default:
+		/* Hmm? */
+		return;
+	}
+}
+
+static void evaluate_assign_to(struct expression *left, struct symbol *type)
+{
+	if (type->ctype.modifiers & MOD_CONST)
+		expression_error(left, "assignment to const expression");
+
+	/* We know left is an lvalue, so it's a "preop-*" */
+	mark_assigned(left->unop);
+}
+
+static struct symbol *evaluate_assignment(struct expression *expr)
+{
+	struct expression *left = expr->left;
+	struct expression *where = expr;
+	struct symbol *ltype;
+
+	if (!lvalue_expression(left)) {
+		expression_error(expr, "not an lvalue");
+		return NULL;
+	}
+
+	ltype = left->ctype;
+
+	if (expr->op != '=') {
+		if (!evaluate_assign_op(expr))
+			return NULL;
+	} else {
+		if (!compatible_assignment_types(where, ltype, &expr->right, "assignment"))
+			return NULL;
+	}
+
+	evaluate_assign_to(left, ltype);
+
+	expr->ctype = ltype;
+	return ltype;
+}
+
+static void examine_fn_arguments(struct symbol *fn)
+{
+	struct symbol *s;
+
+	FOR_EACH_PTR(fn->arguments, s) {
+		struct symbol *arg = evaluate_symbol(s);
+		/* Array/function arguments silently degenerate into pointers */
+		if (arg) {
+			struct symbol *ptr;
+			switch(arg->type) {
+			case SYM_ARRAY:
+			case SYM_FN:
+				ptr = alloc_symbol(s->pos, SYM_PTR);
+				if (arg->type == SYM_ARRAY)
+					ptr->ctype = arg->ctype;
+				else
+					ptr->ctype.base_type = arg;
+				ptr->ctype.as |= s->ctype.as;
+				ptr->ctype.modifiers |= s->ctype.modifiers & MOD_PTRINHERIT;
+
+				s->ctype.base_type = ptr;
+				s->ctype.as = 0;
+				s->ctype.modifiers &= ~MOD_PTRINHERIT;
+				s->bit_size = 0;
+				s->examined = 0;
+				examine_symbol_type(s);
+				break;
+			default:
+				/* nothing */
+				break;
+			}
+		}
+	} END_FOR_EACH_PTR(s);
+}
+
+static struct symbol *convert_to_as_mod(struct symbol *sym, int as, int mod)
+{
+	/* Take the modifiers of the pointer, and apply them to the member */
+	mod |= sym->ctype.modifiers;
+	if (sym->ctype.as != as || sym->ctype.modifiers != mod) {
+		struct symbol *newsym = alloc_symbol(sym->pos, SYM_NODE);
+		*newsym = *sym;
+		newsym->ctype.as = as;
+		newsym->ctype.modifiers = mod;
+		sym = newsym;
+	}
+	return sym;
+}
+
+static struct symbol *create_pointer(struct expression *expr, struct symbol *sym, int degenerate)
+{
+	struct symbol *node = alloc_symbol(expr->pos, SYM_NODE);
+	struct symbol *ptr = alloc_symbol(expr->pos, SYM_PTR);
+
+	node->ctype.base_type = ptr;
+	ptr->bit_size = bits_in_pointer;
+	ptr->ctype.alignment = pointer_alignment;
+
+	node->bit_size = bits_in_pointer;
+	node->ctype.alignment = pointer_alignment;
+
+	access_symbol(sym);
+	if (sym->ctype.modifiers & MOD_REGISTER) {
+		warning(expr->pos, "taking address of 'register' variable '%s'", show_ident(sym->ident));
+		sym->ctype.modifiers &= ~MOD_REGISTER;
+	}
+	if (sym->type == SYM_NODE) {
+		ptr->ctype.as |= sym->ctype.as;
+		ptr->ctype.modifiers |= sym->ctype.modifiers & MOD_PTRINHERIT;
+		sym = sym->ctype.base_type;
+	}
+	if (degenerate && sym->type == SYM_ARRAY) {
+		ptr->ctype.as |= sym->ctype.as;
+		ptr->ctype.modifiers |= sym->ctype.modifiers & MOD_PTRINHERIT;
+		sym = sym->ctype.base_type;
+	}
+	ptr->ctype.base_type = sym;
+
+	return node;
+}
+
+/* Arrays degenerate into pointers on pointer arithmetic */
+static struct symbol *degenerate(struct expression *expr)
+{
+	struct symbol *ctype, *base;
+
+	if (!expr)
+		return NULL;
+	ctype = expr->ctype;
+	if (!ctype)
+		return NULL;
+	base = examine_symbol_type(ctype);
+	if (ctype->type == SYM_NODE)
+		base = ctype->ctype.base_type;
+	/*
+	 * Arrays degenerate into pointers to the entries, while
+	 * functions degenerate into pointers to themselves.
+	 * If array was part of non-lvalue compound, we create a copy
+	 * of that compound first and then act as if we were dealing with
+	 * the corresponding field in there.
+	 */
+	switch (base->type) {
+	case SYM_ARRAY:
+		if (expr->type == EXPR_SLICE) {
+			struct symbol *a = alloc_symbol(expr->pos, SYM_NODE);
+			struct expression *e0, *e1, *e2, *e3, *e4;
+
+			a->ctype.base_type = expr->base->ctype;
+			a->bit_size = expr->base->ctype->bit_size;
+			a->array_size = expr->base->ctype->array_size;
+
+			e0 = alloc_expression(expr->pos, EXPR_SYMBOL);
+			e0->symbol = a;
+			e0->ctype = &lazy_ptr_ctype;
+
+			e1 = alloc_expression(expr->pos, EXPR_PREOP);
+			e1->unop = e0;
+			e1->op = '*';
+			e1->ctype = expr->base->ctype;	/* XXX */
+
+			e2 = alloc_expression(expr->pos, EXPR_ASSIGNMENT);
+			e2->left = e1;
+			e2->right = expr->base;
+			e2->op = '=';
+			e2->ctype = expr->base->ctype;
+
+			if (expr->r_bitpos) {
+				e3 = alloc_expression(expr->pos, EXPR_BINOP);
+				e3->op = '+';
+				e3->left = e0;
+				e3->right = alloc_const_expression(expr->pos,
+							bits_to_bytes(expr->r_bitpos));
+				e3->ctype = &lazy_ptr_ctype;
+			} else {
+				e3 = e0;
+			}
+
+			e4 = alloc_expression(expr->pos, EXPR_COMMA);
+			e4->left = e2;
+			e4->right = e3;
+			e4->ctype = &lazy_ptr_ctype;
+
+			expr->unop = e4;
+			expr->type = EXPR_PREOP;
+			expr->op = '*';
+		}
+	case SYM_FN:
+		if (expr->op != '*' || expr->type != EXPR_PREOP) {
+			expression_error(expr, "strange non-value function or array");
+			return &bad_ctype;
+		}
+		*expr = *expr->unop;
+		ctype = create_pointer(expr, ctype, 1);
+		expr->ctype = ctype;
+	default:
+		/* nothing */;
+	}
+	return ctype;
+}
+
+static struct symbol *evaluate_addressof(struct expression *expr)
+{
+	struct expression *op = expr->unop;
+	struct symbol *ctype;
+
+	if (op->op != '*' || op->type != EXPR_PREOP) {
+		expression_error(expr, "not addressable");
+		return NULL;
+	}
+	ctype = op->ctype;
+	*expr = *op->unop;
+	expr->flags = 0;
+
+	if (expr->type == EXPR_SYMBOL) {
+		struct symbol *sym = expr->symbol;
+		sym->ctype.modifiers |= MOD_ADDRESSABLE;
+	}
+
+	/*
+	 * symbol expression evaluation is lazy about the type
+	 * of the sub-expression, so we may have to generate
+	 * the type here if so..
+	 */
+	if (expr->ctype == &lazy_ptr_ctype) {
+		ctype = create_pointer(expr, ctype, 0);
+		expr->ctype = ctype;
+	}
+	return expr->ctype;
+}
+
+
+static struct symbol *evaluate_dereference(struct expression *expr)
+{
+	struct expression *op = expr->unop;
+	struct symbol *ctype = op->ctype, *node, *target;
+
+	/* Simplify: *&(expr) => (expr) */
+	if (op->type == EXPR_PREOP && op->op == '&') {
+		*expr = *op->unop;
+		expr->flags = 0;
+		return expr->ctype;
+	}
+
+	/* Dereferencing a node drops all the node information. */
+	if (ctype->type == SYM_NODE)
+		ctype = ctype->ctype.base_type;
+
+	node = alloc_symbol(expr->pos, SYM_NODE);
+	target = ctype->ctype.base_type;
+
+	switch (ctype->type) {
+	default:
+		expression_error(expr, "cannot dereference this type");
+		return NULL;
+	case SYM_PTR:
+		node->ctype.modifiers = target->ctype.modifiers & MOD_SPECIFIER;
+		merge_type(node, ctype);
+		break;
+
+	case SYM_ARRAY:
+		if (!lvalue_expression(op)) {
+			expression_error(op, "non-lvalue array??");
+			return NULL;
+		}
+
+		/* Do the implied "addressof" on the array */
+		*op = *op->unop;
+
+		/*
+		 * When an array is dereferenced, we need to pick
+		 * up the attributes of the original node too..
+		 */
+		merge_type(node, op->ctype);
+		merge_type(node, ctype);
+		break;
+	}
+
+	node->bit_size = target->bit_size;
+	node->array_size = target->array_size;
+
+	expr->ctype = node;
+	return node;
+}
+
+/*
+ * Unary post-ops: x++ and x--
+ */
+static struct symbol *evaluate_postop(struct expression *expr)
+{
+	struct expression *op = expr->unop;
+	struct symbol *ctype = op->ctype;
+	int class = classify_type(op->ctype, &ctype);
+	int multiply = 0;
+
+	if (!lvalue_expression(expr->unop)) {
+		expression_error(expr, "need lvalue expression for ++/--");
+		return NULL;
+	}
+
+	if ((class & TYPE_RESTRICT) && restricted_unop(expr->op, &ctype))
+		return bad_expr_type(expr);
+
+	if (class & TYPE_NUM) {
+		multiply = 1;
+	} else if (class == TYPE_PTR) {
+		struct symbol *target = examine_pointer_target(ctype);
+		if (!is_function(target))
+			multiply = bits_to_bytes(target->bit_size);
+	}
+
+	if (multiply) {
+		evaluate_assign_to(op, op->ctype);
+		expr->op_value = multiply;
+		expr->ctype = ctype;
+		return ctype;
+	}
+
+	expression_error(expr, "bad argument type for ++/--");
+	return NULL;
+}
+
+static struct symbol *evaluate_sign(struct expression *expr)
+{
+	struct symbol *ctype = expr->unop->ctype;
+	int class = classify_type(ctype, &ctype);
+	if (expr->flags && !(expr->unop->flags & Int_const_expr))
+		expr->flags = 0;
+	/* should be an arithmetic type */
+	if (!(class & TYPE_NUM))
+		return bad_expr_type(expr);
+	if (!(class & (TYPE_FLOAT|TYPE_RESTRICT))) {
+		struct symbol *rtype = integer_promotion(ctype);
+		expr->unop = cast_to(expr->unop, rtype);
+		ctype = rtype;
+	} else if ((class & TYPE_FLOAT) && expr->op != '~') {
+		/* no conversions needed */
+	} else if ((class & TYPE_RESTRICT) && !restricted_unop(expr->op, &ctype)) {
+		/* no conversions needed */
+	} else {
+		return bad_expr_type(expr);
+	}
+	if (expr->op == '+')
+		*expr = *expr->unop;
+	expr->ctype = ctype;
+	return ctype;
+}
+
+static struct symbol *evaluate_preop(struct expression *expr)
+{
+	struct symbol *ctype = expr->unop->ctype;
+
+	switch (expr->op) {
+	case '(':
+		*expr = *expr->unop;
+		return ctype;
+
+	case '+':
+	case '-':
+	case '~':
+		return evaluate_sign(expr);
+
+	case '*':
+		return evaluate_dereference(expr);
+
+	case '&':
+		return evaluate_addressof(expr);
+
+	case SPECIAL_INCREMENT:
+	case SPECIAL_DECREMENT:
+		/*
+		 * From a type evaluation standpoint the preops are
+		 * the same as the postops
+		 */
+		return evaluate_postop(expr);
+
+	case '!':
+		if (expr->flags && !(expr->unop->flags & Int_const_expr))
+			expr->flags = 0;
+		if (is_safe_type(ctype))
+			warning(expr->pos, "testing a 'safe expression'");
+		if (is_float_type(ctype)) {
+			struct expression *arg = expr->unop;
+			expr->type = EXPR_BINOP;
+			expr->op = SPECIAL_EQUAL;
+			expr->left = arg;
+			expr->right = alloc_expression(expr->pos, EXPR_FVALUE);
+			expr->right->ctype = ctype;
+			expr->right->fvalue = 0;
+		} else if (is_fouled_type(ctype)) {
+			warning(expr->pos, "%s degrades to integer",
+				show_typename(ctype->ctype.base_type));
+		}
+		ctype = &bool_ctype;
+		break;
+
+	default:
+		break;
+	}
+	expr->ctype = ctype;
+	return &bool_ctype;
+}
+
+static struct symbol *find_identifier(struct ident *ident, struct symbol_list *_list, int *offset)
+{
+	struct ptr_list *head = (struct ptr_list *)_list;
+	struct ptr_list *list = head;
+
+	if (!head)
+		return NULL;
+	do {
+		int i;
+		for (i = 0; i < list->nr; i++) {
+			struct symbol *sym = (struct symbol *) list->list[i];
+			if (sym->ident) {
+				if (sym->ident != ident)
+					continue;
+				*offset = sym->offset;
+				return sym;
+			} else {
+				struct symbol *ctype = sym->ctype.base_type;
+				struct symbol *sub;
+				if (!ctype)
+					continue;
+				if (ctype->type != SYM_UNION && ctype->type != SYM_STRUCT)
+					continue;
+				sub = find_identifier(ident, ctype->symbol_list, offset);
+				if (!sub)
+					continue;
+				*offset += sym->offset;
+				return sub;
+			}	
+		}
+	} while ((list = list->next) != head);
+	return NULL;
+}
+
+static struct expression *evaluate_offset(struct expression *expr, unsigned long offset)
+{
+	struct expression *add;
+
+	/*
+	 * Create a new add-expression
+	 *
+	 * NOTE! Even if we just add zero, we need a new node
+	 * for the member pointer, since it has a different
+	 * type than the original pointer. We could make that
+	 * be just a cast, but the fact is, a node is a node,
+	 * so we might as well just do the "add zero" here.
+	 */
+	add = alloc_expression(expr->pos, EXPR_BINOP);
+	add->op = '+';
+	add->left = expr;
+	add->right = alloc_expression(expr->pos, EXPR_VALUE);
+	add->right->ctype = &int_ctype;
+	add->right->value = offset;
+
+	/*
+	 * The ctype of the pointer will be lazily evaluated if
+	 * we ever take the address of this member dereference..
+	 */
+	add->ctype = &lazy_ptr_ctype;
+	return add;
+}
+
+/* structure/union dereference */
+static struct symbol *evaluate_member_dereference(struct expression *expr)
+{
+	int offset;
+	struct symbol *ctype, *member;
+	struct expression *deref = expr->deref, *add;
+	struct ident *ident = expr->member;
+	unsigned int mod;
+	int address_space;
+
+	if (!evaluate_expression(deref))
+		return NULL;
+	if (!ident) {
+		expression_error(expr, "bad member name");
+		return NULL;
+	}
+
+	ctype = deref->ctype;
+	examine_symbol_type(ctype);
+	address_space = ctype->ctype.as;
+	mod = ctype->ctype.modifiers;
+	if (ctype->type == SYM_NODE) {
+		ctype = ctype->ctype.base_type;
+		address_space |= ctype->ctype.as;
+		mod |= ctype->ctype.modifiers;
+	}
+	if (!ctype || (ctype->type != SYM_STRUCT && ctype->type != SYM_UNION)) {
+		expression_error(expr, "expected structure or union");
+		return NULL;
+	}
+	offset = 0;
+	member = find_identifier(ident, ctype->symbol_list, &offset);
+	if (!member) {
+		const char *type = ctype->type == SYM_STRUCT ? "struct" : "union";
+		const char *name = "<unnamed>";
+		int namelen = 9;
+		if (ctype->ident) {
+			name = ctype->ident->name;
+			namelen = ctype->ident->len;
+		}
+		if (ctype->symbol_list)
+			expression_error(expr, "no member '%s' in %s %.*s",
+				show_ident(ident), type, namelen, name);
+		else
+			expression_error(expr, "using member '%s' in "
+				"incomplete %s %.*s", show_ident(ident),
+				type, namelen, name);
+		return NULL;
+	}
+
+	/*
+	 * The member needs to take on the address space and modifiers of
+	 * the "parent" type.
+	 */
+	member = convert_to_as_mod(member, address_space, mod);
+	ctype = get_base_type(member);
+
+	if (!lvalue_expression(deref)) {
+		if (deref->type != EXPR_SLICE) {
+			expr->base = deref;
+			expr->r_bitpos = 0;
+		} else {
+			expr->base = deref->base;
+			expr->r_bitpos = deref->r_bitpos;
+		}
+		expr->r_bitpos += bytes_to_bits(offset);
+		expr->type = EXPR_SLICE;
+		expr->r_nrbits = member->bit_size;
+		expr->r_bitpos += member->bit_offset;
+		expr->ctype = member;
+		return member;
+	}
+
+	deref = deref->unop;
+	expr->deref = deref;
+
+	add = evaluate_offset(deref, offset);
+	expr->type = EXPR_PREOP;
+	expr->op = '*';
+	expr->unop = add;
+
+	expr->ctype = member;
+	return member;
+}
+
+static int is_promoted(struct expression *expr)
+{
+	while (1) {
+		switch (expr->type) {
+		case EXPR_BINOP:
+		case EXPR_SELECT:
+		case EXPR_CONDITIONAL:
+			return 1;
+		case EXPR_COMMA:
+			expr = expr->right;
+			continue;
+		case EXPR_PREOP:
+			switch (expr->op) {
+			case '(':
+				expr = expr->unop;
+				continue;
+			case '+':
+			case '-':
+			case '~':
+				return 1;
+			default:
+				return 0;
+			}
+		default:
+			return 0;
+		}
+	}
+}
+
+
+static struct symbol *evaluate_cast(struct expression *);
+
+static struct symbol *evaluate_type_information(struct expression *expr)
+{
+	struct symbol *sym = expr->cast_type;
+	if (!sym) {
+		sym = evaluate_expression(expr->cast_expression);
+		if (!sym)
+			return NULL;
+		/*
+		 * Expressions of restricted types will possibly get
+		 * promoted - check that here
+		 */
+		if (is_restricted_type(sym)) {
+			if (sym->bit_size < bits_in_int && is_promoted(expr))
+				sym = &int_ctype;
+		} else if (is_fouled_type(sym)) {
+			sym = &int_ctype;
+		}
+	}
+	examine_symbol_type(sym);
+	if (is_bitfield_type(sym)) {
+		expression_error(expr, "trying to examine bitfield type");
+		return NULL;
+	}
+	return sym;
+}
+
+static struct symbol *evaluate_sizeof(struct expression *expr)
+{
+	struct symbol *type;
+	int size;
+
+	type = evaluate_type_information(expr);
+	if (!type)
+		return NULL;
+
+	size = type->bit_size;
+
+	if (size < 0 && is_void_type(type)) {
+		warning(expr->pos, "expression using sizeof(void)");
+		size = bits_in_char;
+	}
+
+	if (size == 1 && is_bool_type(type)) {
+		warning(expr->pos, "expression using sizeof bool");
+		size = bits_in_char;
+	}
+
+	if (is_function(type->ctype.base_type)) {
+		warning(expr->pos, "expression using sizeof on a function");
+		size = bits_in_char;
+	}
+
+	if ((size < 0) || (size & (bits_in_char - 1)))
+		expression_error(expr, "cannot size expression");
+
+	expr->type = EXPR_VALUE;
+	expr->value = bits_to_bytes(size);
+	expr->taint = 0;
+	expr->ctype = size_t_ctype;
+	return size_t_ctype;
+}
+
+static struct symbol *evaluate_ptrsizeof(struct expression *expr)
+{
+	struct symbol *type;
+	int size;
+
+	type = evaluate_type_information(expr);
+	if (!type)
+		return NULL;
+
+	if (type->type == SYM_NODE)
+		type = type->ctype.base_type;
+	if (!type)
+		return NULL;
+	switch (type->type) {
+	case SYM_ARRAY:
+		break;
+	case SYM_PTR:
+		type = get_base_type(type);
+		if (type)
+			break;
+	default:
+		expression_error(expr, "expected pointer expression");
+		return NULL;
+	}
+	size = type->bit_size;
+	if (size & (bits_in_char-1))
+		size = 0;
+	expr->type = EXPR_VALUE;
+	expr->value = bits_to_bytes(size);
+	expr->taint = 0;
+	expr->ctype = size_t_ctype;
+	return size_t_ctype;
+}
+
+static struct symbol *evaluate_alignof(struct expression *expr)
+{
+	struct symbol *type;
+
+	type = evaluate_type_information(expr);
+	if (!type)
+		return NULL;
+
+	expr->type = EXPR_VALUE;
+	expr->value = type->ctype.alignment;
+	expr->taint = 0;
+	expr->ctype = size_t_ctype;
+	return size_t_ctype;
+}
+
+static int evaluate_arguments(struct symbol *f, struct symbol *fn, struct expression_list *head)
+{
+	struct expression *expr;
+	struct symbol_list *argument_types = fn->arguments;
+	struct symbol *argtype;
+	int i = 1;
+
+	PREPARE_PTR_LIST(argument_types, argtype);
+	FOR_EACH_PTR (head, expr) {
+		struct expression **p = THIS_ADDRESS(expr);
+		struct symbol *ctype, *target;
+		ctype = evaluate_expression(expr);
+
+		if (!ctype)
+			return 0;
+
+		target = argtype;
+		if (!target) {
+			struct symbol *type;
+			int class = classify_type(ctype, &type);
+			if (is_int(class)) {
+				*p = cast_to(expr, integer_promotion(type));
+			} else if (class & TYPE_FLOAT) {
+				unsigned long mod = type->ctype.modifiers;
+				if (!(mod & (MOD_LONG_ALL)))
+					*p = cast_to(expr, &double_ctype);
+			} else if (class & TYPE_PTR) {
+				if (expr->ctype == &null_ctype)
+					*p = cast_to(expr, &ptr_ctype);
+				else
+					degenerate(expr);
+			}
+		} else {
+			static char where[30];
+			examine_symbol_type(target);
+			sprintf(where, "argument %d", i);
+			compatible_assignment_types(expr, target, p, where);
+		}
+
+		i++;
+		NEXT_PTR_LIST(argtype);
+	} END_FOR_EACH_PTR(expr);
+	FINISH_PTR_LIST(argtype);
+	return 1;
+}
+
+static struct symbol *find_struct_ident(struct symbol *ctype, struct ident *ident)
+{
+	struct symbol *sym;
+
+	FOR_EACH_PTR(ctype->symbol_list, sym) {
+		if (sym->ident == ident)
+			return sym;
+	} END_FOR_EACH_PTR(sym);
+	return NULL;
+}
+
+static void convert_index(struct expression *e)
+{
+	struct expression *child = e->idx_expression;
+	unsigned from = e->idx_from;
+	unsigned to = e->idx_to + 1;
+	e->type = EXPR_POS;
+	e->init_offset = from * bits_to_bytes(e->ctype->bit_size);
+	e->init_nr = to - from;
+	e->init_expr = child;
+}
+
+static void convert_ident(struct expression *e)
+{
+	struct expression *child = e->ident_expression;
+	struct symbol *sym = e->field;
+	e->type = EXPR_POS;
+	e->init_offset = sym->offset;
+	e->init_nr = 1;
+	e->init_expr = child;
+}
+
+static void convert_designators(struct expression *e)
+{
+	while (e) {
+		if (e->type == EXPR_INDEX)
+			convert_index(e);
+		else if (e->type == EXPR_IDENTIFIER)
+			convert_ident(e);
+		else
+			break;
+		e = e->init_expr;
+	}
+}
+
+static void excess(struct expression *e, const char *s)
+{
+	warning(e->pos, "excessive elements in %s initializer", s);
+}
+
+/*
+ * implicit designator for the first element
+ */
+static struct expression *first_subobject(struct symbol *ctype, int class,
+					  struct expression **v)
+{
+	struct expression *e = *v, *new;
+
+	if (ctype->type == SYM_NODE)
+		ctype = ctype->ctype.base_type;
+
+	if (class & TYPE_PTR) { /* array */
+		if (!ctype->bit_size)
+			return NULL;
+		new = alloc_expression(e->pos, EXPR_INDEX);
+		new->idx_expression = e;
+		new->ctype = ctype->ctype.base_type;
+	} else  {
+		struct symbol *field, *p;
+		PREPARE_PTR_LIST(ctype->symbol_list, p);
+		while (p && !p->ident && is_bitfield_type(p))
+			NEXT_PTR_LIST(p);
+		field = p;
+		FINISH_PTR_LIST(p);
+		if (!field)
+			return NULL;
+		new = alloc_expression(e->pos, EXPR_IDENTIFIER);
+		new->ident_expression = e;
+		new->field = new->ctype = field;
+	}
+	*v = new;
+	return new;
+}
+
+/*
+ * sanity-check explicit designators; return the innermost one or NULL
+ * in case of error.  Assign types.
+ */
+static struct expression *check_designators(struct expression *e,
+					    struct symbol *ctype)
+{
+	struct expression *last = NULL;
+	const char *err;
+	while (1) {
+		if (ctype->type == SYM_NODE)
+			ctype = ctype->ctype.base_type;
+		if (e->type == EXPR_INDEX) {
+			struct symbol *type;
+			if (ctype->type != SYM_ARRAY) {
+				err = "array index in non-array";
+				break;
+			}
+			type = ctype->ctype.base_type;
+			if (ctype->bit_size >= 0 && type->bit_size >= 0) {
+				unsigned offset = e->idx_to * type->bit_size;
+				if (offset >= ctype->bit_size) {
+					err = "index out of bounds in";
+					break;
+				}
+			}
+			e->ctype = ctype = type;
+			ctype = type;
+			last = e;
+			if (!e->idx_expression) {
+				err = "invalid";
+				break;
+			}
+			e = e->idx_expression;
+		} else if (e->type == EXPR_IDENTIFIER) {
+			if (ctype->type != SYM_STRUCT && ctype->type != SYM_UNION) {
+				err = "field name not in struct or union";
+				break;
+			}
+			ctype = find_struct_ident(ctype, e->expr_ident);
+			if (!ctype) {
+				err = "unknown field name in";
+				break;
+			}
+			e->field = e->ctype = ctype;
+			last = e;
+			if (!e->ident_expression) {
+				err = "invalid";
+				break;
+			}
+			e = e->ident_expression;
+		} else if (e->type == EXPR_POS) {
+			err = "internal front-end error: EXPR_POS in";
+			break;
+		} else
+			return last;
+	}
+	expression_error(e, "%s initializer", err);
+	return NULL;
+}
+
+/*
+ * choose the next subobject to initialize.
+ *
+ * Get designators for next element, switch old ones to EXPR_POS.
+ * Return the resulting expression or NULL if we'd run out of subobjects.
+ * The innermost designator is returned in *v.  Designators in old
+ * are assumed to be already sanity-checked.
+ */
+static struct expression *next_designators(struct expression *old,
+			     struct symbol *ctype,
+			     struct expression *e, struct expression **v)
+{
+	struct expression *new = NULL;
+
+	if (!old)
+		return NULL;
+	if (old->type == EXPR_INDEX) {
+		struct expression *copy;
+		unsigned n;
+
+		copy = next_designators(old->idx_expression,
+					old->ctype, e, v);
+		if (!copy) {
+			n = old->idx_to + 1;
+			if (n * old->ctype->bit_size == ctype->bit_size) {
+				convert_index(old);
+				return NULL;
+			}
+			copy = e;
+			*v = new = alloc_expression(e->pos, EXPR_INDEX);
+		} else {
+			n = old->idx_to;
+			new = alloc_expression(e->pos, EXPR_INDEX);
+		}
+
+		new->idx_from = new->idx_to = n;
+		new->idx_expression = copy;
+		new->ctype = old->ctype;
+		convert_index(old);
+	} else if (old->type == EXPR_IDENTIFIER) {
+		struct expression *copy;
+		struct symbol *field;
+
+		copy = next_designators(old->ident_expression,
+					old->ctype, e, v);
+		if (!copy) {
+			field = old->field->next_subobject;
+			if (!field) {
+				convert_ident(old);
+				return NULL;
+			}
+			copy = e;
+			*v = new = alloc_expression(e->pos, EXPR_IDENTIFIER);
+		} else {
+			field = old->field;
+			new = alloc_expression(e->pos, EXPR_IDENTIFIER);
+		}
+
+		new->field = field;
+		new->expr_ident = field->ident;
+		new->ident_expression = copy;
+		new->ctype = field;
+		convert_ident(old);
+	}
+	return new;
+}
+
+static int handle_simple_initializer(struct expression **ep, int nested,
+				     int class, struct symbol *ctype);
+
+/*
+ * deal with traversing subobjects [6.7.8(17,18,20)]
+ */
+static void handle_list_initializer(struct expression *expr,
+				    int class, struct symbol *ctype)
+{
+	struct expression *e, *last = NULL, *top = NULL, *next;
+	int jumped = 0;
+
+	FOR_EACH_PTR(expr->expr_list, e) {
+		struct expression **v;
+		struct symbol *type;
+		int lclass;
+
+		if (e->type != EXPR_INDEX && e->type != EXPR_IDENTIFIER) {
+			struct symbol *struct_sym;
+			if (!top) {
+				top = e;
+				last = first_subobject(ctype, class, &top);
+			} else {
+				last = next_designators(last, ctype, e, &top);
+			}
+			if (!last) {
+				excess(e, class & TYPE_PTR ? "array" :
+							"struct or union");
+				DELETE_CURRENT_PTR(e);
+				continue;
+			}
+			struct_sym = ctype->type == SYM_NODE ? ctype->ctype.base_type : ctype;
+			if (Wdesignated_init && struct_sym->designated_init)
+				warning(e->pos, "%s%.*s%spositional init of field in %s %s, declared with attribute designated_init",
+					ctype->ident ? "in initializer for " : "",
+					ctype->ident ? ctype->ident->len : 0,
+					ctype->ident ? ctype->ident->name : "",
+					ctype->ident ? ": " : "",
+					get_type_name(struct_sym->type),
+					show_ident(struct_sym->ident));
+			if (jumped) {
+				warning(e->pos, "advancing past deep designator");
+				jumped = 0;
+			}
+			REPLACE_CURRENT_PTR(e, last);
+		} else {
+			next = check_designators(e, ctype);
+			if (!next) {
+				DELETE_CURRENT_PTR(e);
+				continue;
+			}
+			top = next;
+			/* deeper than one designator? */
+			jumped = top != e;
+			convert_designators(last);
+			last = e;
+		}
+
+found:
+		lclass = classify_type(top->ctype, &type);
+		if (top->type == EXPR_INDEX)
+			v = &top->idx_expression;
+		else
+			v = &top->ident_expression;
+
+		if (handle_simple_initializer(v, 1, lclass, top->ctype))
+			continue;
+
+		if (!(lclass & TYPE_COMPOUND)) {
+			warning(e->pos, "bogus scalar initializer");
+			DELETE_CURRENT_PTR(e);
+			continue;
+		}
+
+		next = first_subobject(type, lclass, v);
+		if (next) {
+			warning(e->pos, "missing braces around initializer");
+			top = next;
+			goto found;
+		}
+
+		DELETE_CURRENT_PTR(e);
+		excess(e, lclass & TYPE_PTR ? "array" : "struct or union");
+
+	} END_FOR_EACH_PTR(e);
+
+	convert_designators(last);
+	expr->ctype = ctype;
+}
+
+static int is_string_literal(struct expression **v)
+{
+	struct expression *e = *v;
+	while (e && e->type == EXPR_PREOP && e->op == '(')
+		e = e->unop;
+	if (!e || e->type != EXPR_STRING)
+		return 0;
+	if (e != *v && Wparen_string)
+		warning(e->pos,
+			"array initialized from parenthesized string constant");
+	*v = e;
+	return 1;
+}
+
+/*
+ * We want a normal expression, possibly in one layer of braces.  Warn
+ * if the latter happens inside a list (it's legal, but likely to be
+ * an effect of screwup).  In case of anything not legal, we are definitely
+ * having an effect of screwup, so just fail and let the caller warn.
+ */
+static struct expression *handle_scalar(struct expression *e, int nested)
+{
+	struct expression *v = NULL, *p;
+	int count = 0;
+
+	/* normal case */
+	if (e->type != EXPR_INITIALIZER)
+		return e;
+
+	FOR_EACH_PTR(e->expr_list, p) {
+		if (!v)
+			v = p;
+		count++;
+	} END_FOR_EACH_PTR(p);
+	if (count != 1)
+		return NULL;
+	switch(v->type) {
+	case EXPR_INITIALIZER:
+	case EXPR_INDEX:
+	case EXPR_IDENTIFIER:
+		return NULL;
+	default:
+		break;
+	}
+	if (nested)
+		warning(e->pos, "braces around scalar initializer");
+	return v;
+}
+
+/*
+ * deal with the cases that don't care about subobjects:
+ * scalar <- assignment expression, possibly in braces [6.7.8(11)]
+ * character array <- string literal, possibly in braces [6.7.8(14)]
+ * struct or union <- assignment expression of compatible type [6.7.8(13)]
+ * compound type <- initializer list in braces [6.7.8(16)]
+ * The last one punts to handle_list_initializer() which, in turn will call
+ * us for individual elements of the list.
+ *
+ * We do not handle 6.7.8(15) (wide char array <- wide string literal) for
+ * the lack of support of wide char stuff in general.
+ *
+ * One note: we need to take care not to evaluate a string literal until
+ * we know that we *will* handle it right here.  Otherwise we would screw
+ * the cases like struct { struct {char s[10]; ...} ...} initialized with
+ * { "string", ...} - we need to preserve that string literal recognizable
+ * until we dig into the inner struct.
+ */
+static int handle_simple_initializer(struct expression **ep, int nested,
+				     int class, struct symbol *ctype)
+{
+	int is_string = is_string_type(ctype);
+	struct expression *e = *ep, *p;
+	struct symbol *type;
+
+	if (!e)
+		return 0;
+
+	/* scalar */
+	if (!(class & TYPE_COMPOUND)) {
+		e = handle_scalar(e, nested);
+		if (!e)
+			return 0;
+		*ep = e;
+		if (!evaluate_expression(e))
+			return 1;
+		compatible_assignment_types(e, ctype, ep, "initializer");
+		return 1;
+	}
+
+	/*
+	 * sublist; either a string, or we dig in; the latter will deal with
+	 * pathologies, so we don't need anything fancy here.
+	 */
+	if (e->type == EXPR_INITIALIZER) {
+		if (is_string) {
+			struct expression *v = NULL;
+			int count = 0;
+
+			FOR_EACH_PTR(e->expr_list, p) {
+				if (!v)
+					v = p;
+				count++;
+			} END_FOR_EACH_PTR(p);
+			if (count == 1 && is_string_literal(&v)) {
+				*ep = e = v;
+				goto String;
+			}
+		}
+		handle_list_initializer(e, class, ctype);
+		return 1;
+	}
+
+	/* string */
+	if (is_string_literal(&e)) {
+		/* either we are doing array of char, or we'll have to dig in */
+		if (is_string) {
+			*ep = e;
+			goto String;
+		}
+		return 0;
+	}
+	/* struct or union can be initialized by compatible */
+	if (class != TYPE_COMPOUND)
+		return 0;
+	type = evaluate_expression(e);
+	if (!type)
+		return 0;
+	if (ctype->type == SYM_NODE)
+		ctype = ctype->ctype.base_type;
+	if (type->type == SYM_NODE)
+		type = type->ctype.base_type;
+	if (ctype == type)
+		return 1;
+	return 0;
+
+String:
+	p = alloc_expression(e->pos, EXPR_STRING);
+	*p = *e;
+	type = evaluate_expression(p);
+	if (ctype->bit_size != -1 &&
+	    ctype->bit_size + bits_in_char < type->bit_size) {
+		warning(e->pos,
+			"too long initializer-string for array of char");
+	}
+	*ep = p;
+	return 1;
+}
+
+static void evaluate_initializer(struct symbol *ctype, struct expression **ep)
+{
+	struct symbol *type;
+	int class = classify_type(ctype, &type);
+	if (!handle_simple_initializer(ep, 0, class, ctype))
+		expression_error(*ep, "invalid initializer");
+}
+
+static struct symbol *evaluate_cast(struct expression *expr)
+{
+	struct expression *target = expr->cast_expression;
+	struct symbol *ctype;
+	struct symbol *t1, *t2;
+	int class1, class2;
+	int as1 = 0, as2 = 0;
+
+	if (!target)
+		return NULL;
+
+	/*
+	 * Special case: a cast can be followed by an
+	 * initializer, in which case we need to pass
+	 * the type value down to that initializer rather
+	 * than trying to evaluate it as an expression
+	 *
+	 * A more complex case is when the initializer is
+	 * dereferenced as part of a post-fix expression.
+	 * We need to produce an expression that can be dereferenced.
+	 */
+	if (target->type == EXPR_INITIALIZER) {
+		struct symbol *sym = expr->cast_type;
+		struct expression *addr = alloc_expression(expr->pos, EXPR_SYMBOL);
+
+		sym->initializer = target;
+		evaluate_symbol(sym);
+
+		addr->ctype = &lazy_ptr_ctype;	/* Lazy eval */
+		addr->symbol = sym;
+
+		expr->type = EXPR_PREOP;
+		expr->op = '*';
+		expr->unop = addr;
+		expr->ctype = sym;
+
+		return sym;
+	}
+
+	ctype = examine_symbol_type(expr->cast_type);
+	expr->ctype = ctype;
+	expr->cast_type = ctype;
+
+	evaluate_expression(target);
+	degenerate(target);
+
+	class1 = classify_type(ctype, &t1);
+
+	/* cast to non-integer type -> not an integer constant expression */
+	if (!is_int(class1))
+		expr->flags = 0;
+	/* if argument turns out to be not an integer constant expression *and*
+	   it was not a floating literal to start with -> too bad */
+	else if (expr->flags == Int_const_expr &&
+		!(target->flags & Int_const_expr))
+		expr->flags = 0;
+	/*
+	 * You can always throw a value away by casting to
+	 * "void" - that's an implicit "force". Note that
+	 * the same is _not_ true of "void *".
+	 */
+	if (t1 == &void_ctype)
+		goto out;
+
+	if (class1 & (TYPE_COMPOUND | TYPE_FN))
+		warning(expr->pos, "cast to non-scalar");
+
+	t2 = target->ctype;
+	if (!t2) {
+		expression_error(expr, "cast from unknown type");
+		goto out;
+	}
+	class2 = classify_type(t2, &t2);
+
+	if (class2 & TYPE_COMPOUND)
+		warning(expr->pos, "cast from non-scalar");
+
+	if (expr->type == EXPR_FORCE_CAST)
+		goto out;
+
+	/* allowed cast unfouls */
+	if (class2 & TYPE_FOULED)
+		t2 = unfoul(t2);
+
+	if (t1 != t2) {
+		if (class1 & TYPE_RESTRICT)
+			warning(expr->pos, "cast to %s",
+				show_typename(t1));
+		if (class2 & TYPE_RESTRICT)
+			warning(expr->pos, "cast from %s",
+				show_typename(t2));
+	}
+
+	if (t1 == &ulong_ctype)
+		as1 = -1;
+	else if (class1 == TYPE_PTR) {
+		examine_pointer_target(t1);
+		as1 = t1->ctype.as;
+	}
+
+	if (t2 == &ulong_ctype)
+		as2 = -1;
+	else if (class2 == TYPE_PTR) {
+		examine_pointer_target(t2);
+		as2 = t2->ctype.as;
+	}
+
+	if (!as1 && as2 > 0)
+		warning(expr->pos, "cast removes address space of expression");
+	if (as1 > 0 && as2 > 0 && as1 != as2)
+		warning(expr->pos, "cast between address spaces (<asn:%d>-><asn:%d>)", as2, as1);
+	if (as1 > 0 && !as2 &&
+	    !is_null_pointer_constant(target) && Wcast_to_as)
+		warning(expr->pos,
+			"cast adds address space to expression (<asn:%d>)", as1);
+
+	if (!(t1->ctype.modifiers & MOD_PTRINHERIT) && class1 == TYPE_PTR &&
+	    !as1 && (target->flags & Int_const_expr)) {
+		if (t1->ctype.base_type == &void_ctype) {
+			if (is_zero_constant(target)) {
+				/* NULL */
+				expr->type = EXPR_VALUE;
+				expr->ctype = &null_ctype;
+				expr->value = 0;
+				return ctype;
+			}
+		}
+	}
+out:
+	return ctype;
+}
+
+/*
+ * Evaluate a call expression with a symbol. This
+ * should expand inline functions, and evaluate
+ * builtins.
+ */
+static int evaluate_symbol_call(struct expression *expr)
+{
+	struct expression *fn = expr->fn;
+	struct symbol *ctype = fn->ctype;
+
+	if (fn->type != EXPR_PREOP)
+		return 0;
+
+	if (ctype->op && ctype->op->evaluate)
+		return ctype->op->evaluate(expr);
+
+	if (ctype->ctype.modifiers & MOD_INLINE) {
+		int ret;
+		struct symbol *curr = current_fn;
+
+		if (ctype->definition)
+			ctype = ctype->definition;
+
+		current_fn = ctype->ctype.base_type;
+
+		ret = inline_function(expr, ctype);
+
+		/* restore the old function */
+		current_fn = curr;
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct symbol *evaluate_call(struct expression *expr)
+{
+	int args, fnargs;
+	struct symbol *ctype, *sym;
+	struct expression *fn = expr->fn;
+	struct expression_list *arglist = expr->args;
+
+	if (!evaluate_expression(fn))
+		return NULL;
+	sym = ctype = fn->ctype;
+	if (ctype->type == SYM_NODE)
+		ctype = ctype->ctype.base_type;
+	if (ctype->type == SYM_PTR)
+		ctype = get_base_type(ctype);
+
+	if (ctype->type != SYM_FN) {
+		struct expression *arg;
+		expression_error(expr, "not a function %s",
+			     show_ident(sym->ident));
+		/* do typechecking in arguments */
+		FOR_EACH_PTR (arglist, arg) {
+			evaluate_expression(arg);
+		} END_FOR_EACH_PTR(arg);
+		return NULL;
+	}
+
+	examine_fn_arguments(ctype);
+        if (sym->type == SYM_NODE && fn->type == EXPR_PREOP &&
+	    sym->op && sym->op->args) {
+		if (!sym->op->args(expr))
+			return NULL;
+	} else {
+		if (!evaluate_arguments(sym, ctype, arglist))
+			return NULL;
+		args = expression_list_size(expr->args);
+		fnargs = symbol_list_size(ctype->arguments);
+		if (args < fnargs)
+			expression_error(expr,
+				     "not enough arguments for function %s",
+				     show_ident(sym->ident));
+		if (args > fnargs && !ctype->variadic)
+			expression_error(expr,
+				     "too many arguments for function %s",
+				     show_ident(sym->ident));
+	}
+	if (sym->type == SYM_NODE) {
+		if (evaluate_symbol_call(expr))
+			return expr->ctype;
+	}
+	expr->ctype = ctype->ctype.base_type;
+	return expr->ctype;
+}
+
+static struct symbol *evaluate_offsetof(struct expression *expr)
+{
+	struct expression *e = expr->down;
+	struct symbol *ctype = expr->in;
+	int class;
+
+	if (expr->op == '.') {
+		struct symbol *field;
+		int offset = 0;
+		if (!ctype) {
+			expression_error(expr, "expected structure or union");
+			return NULL;
+		}
+		examine_symbol_type(ctype);
+		class = classify_type(ctype, &ctype);
+		if (class != TYPE_COMPOUND) {
+			expression_error(expr, "expected structure or union");
+			return NULL;
+		}
+
+		field = find_identifier(expr->ident, ctype->symbol_list, &offset);
+		if (!field) {
+			expression_error(expr, "unknown member");
+			return NULL;
+		}
+		ctype = field;
+		expr->type = EXPR_VALUE;
+		expr->flags = Int_const_expr;
+		expr->value = offset;
+		expr->taint = 0;
+		expr->ctype = size_t_ctype;
+	} else {
+		if (!ctype) {
+			expression_error(expr, "expected structure or union");
+			return NULL;
+		}
+		examine_symbol_type(ctype);
+		class = classify_type(ctype, &ctype);
+		if (class != (TYPE_COMPOUND | TYPE_PTR)) {
+			expression_error(expr, "expected array");
+			return NULL;
+		}
+		ctype = ctype->ctype.base_type;
+		if (!expr->index) {
+			expr->type = EXPR_VALUE;
+			expr->flags = Int_const_expr;
+			expr->value = 0;
+			expr->taint = 0;
+			expr->ctype = size_t_ctype;
+		} else {
+			struct expression *idx = expr->index, *m;
+			struct symbol *i_type = evaluate_expression(idx);
+			int i_class = classify_type(i_type, &i_type);
+			if (!is_int(i_class)) {
+				expression_error(expr, "non-integer index");
+				return NULL;
+			}
+			unrestrict(idx, i_class, &i_type);
+			idx = cast_to(idx, size_t_ctype);
+			m = alloc_const_expression(expr->pos,
+						   bits_to_bytes(ctype->bit_size));
+			m->ctype = size_t_ctype;
+			m->flags = Int_const_expr;
+			expr->type = EXPR_BINOP;
+			expr->left = idx;
+			expr->right = m;
+			expr->op = '*';
+			expr->ctype = size_t_ctype;
+			expr->flags = m->flags & idx->flags & Int_const_expr;
+		}
+	}
+	if (e) {
+		struct expression *copy = __alloc_expression(0);
+		*copy = *expr;
+		if (e->type == EXPR_OFFSETOF)
+			e->in = ctype;
+		if (!evaluate_expression(e))
+			return NULL;
+		expr->type = EXPR_BINOP;
+		expr->flags = e->flags & copy->flags & Int_const_expr;
+		expr->op = '+';
+		expr->ctype = size_t_ctype;
+		expr->left = copy;
+		expr->right = e;
+	}
+	return size_t_ctype;
+}
+
+struct symbol *evaluate_expression(struct expression *expr)
+{
+	if (!expr)
+		return NULL;
+	if (expr->ctype)
+		return expr->ctype;
+
+	switch (expr->type) {
+	case EXPR_VALUE:
+	case EXPR_FVALUE:
+		expression_error(expr, "value expression without a type");
+		return NULL;
+	case EXPR_STRING:
+		return evaluate_string(expr);
+	case EXPR_SYMBOL:
+		return evaluate_symbol_expression(expr);
+	case EXPR_BINOP:
+		if (!evaluate_expression(expr->left))
+			return NULL;
+		if (!evaluate_expression(expr->right))
+			return NULL;
+		return evaluate_binop(expr);
+	case EXPR_LOGICAL:
+		return evaluate_logical(expr);
+	case EXPR_COMMA:
+		evaluate_expression(expr->left);
+		if (!evaluate_expression(expr->right))
+			return NULL;
+		return evaluate_comma(expr);
+	case EXPR_COMPARE:
+		if (!evaluate_expression(expr->left))
+			return NULL;
+		if (!evaluate_expression(expr->right))
+			return NULL;
+		return evaluate_compare(expr);
+	case EXPR_ASSIGNMENT:
+		if (!evaluate_expression(expr->left))
+			return NULL;
+		if (!evaluate_expression(expr->right))
+			return NULL;
+		return evaluate_assignment(expr);
+	case EXPR_PREOP:
+		if (!evaluate_expression(expr->unop))
+			return NULL;
+		return evaluate_preop(expr);
+	case EXPR_POSTOP:
+		if (!evaluate_expression(expr->unop))
+			return NULL;
+		return evaluate_postop(expr);
+	case EXPR_CAST:
+	case EXPR_FORCE_CAST:
+	case EXPR_IMPLIED_CAST:
+		return evaluate_cast(expr);
+	case EXPR_SIZEOF:
+		return evaluate_sizeof(expr);
+	case EXPR_PTRSIZEOF:
+		return evaluate_ptrsizeof(expr);
+	case EXPR_ALIGNOF:
+		return evaluate_alignof(expr);
+	case EXPR_DEREF:
+		return evaluate_member_dereference(expr);
+	case EXPR_CALL:
+		return evaluate_call(expr);
+	case EXPR_SELECT:
+	case EXPR_CONDITIONAL:
+		return evaluate_conditional_expression(expr);
+	case EXPR_STATEMENT:
+		expr->ctype = evaluate_statement(expr->statement);
+		return expr->ctype;
+
+	case EXPR_LABEL:
+		expr->ctype = &ptr_ctype;
+		return &ptr_ctype;
+
+	case EXPR_TYPE:
+		/* Evaluate the type of the symbol .. */
+		evaluate_symbol(expr->symbol);
+		/* .. but the type of the _expression_ is a "type" */
+		expr->ctype = &type_ctype;
+		return &type_ctype;
+
+	case EXPR_OFFSETOF:
+		return evaluate_offsetof(expr);
+
+	/* These can not exist as stand-alone expressions */
+	case EXPR_INITIALIZER:
+	case EXPR_IDENTIFIER:
+	case EXPR_INDEX:
+	case EXPR_POS:
+		expression_error(expr, "internal front-end error: initializer in expression");
+		return NULL;
+	case EXPR_SLICE:
+		expression_error(expr, "internal front-end error: SLICE re-evaluated");
+		return NULL;
+	}
+	return NULL;
+}
+
+static void check_duplicates(struct symbol *sym)
+{
+	int declared = 0;
+	struct symbol *next = sym;
+
+	while ((next = next->same_symbol) != NULL) {
+		const char *typediff;
+		evaluate_symbol(next);
+		declared++;
+		typediff = type_difference(&sym->ctype, &next->ctype, 0, 0);
+		if (typediff) {
+			sparse_error(sym->pos, "symbol '%s' redeclared with different type (originally declared at %s:%d) - %s",
+				show_ident(sym->ident),
+				stream_name(next->pos.stream), next->pos.line, typediff);
+			return;
+		}
+	}
+	if (!declared) {
+		unsigned long mod = sym->ctype.modifiers;
+		if (mod & (MOD_STATIC | MOD_REGISTER))
+			return;
+		if (!(mod & MOD_TOPLEVEL))
+			return;
+		if (!Wdecl)
+			return;
+		if (sym->ident == &main_ident)
+			return;
+		warning(sym->pos, "symbol '%s' was not declared. Should it be static?", show_ident(sym->ident));
+	}
+}
+
+static struct symbol *evaluate_symbol(struct symbol *sym)
+{
+	struct symbol *base_type;
+
+	if (!sym)
+		return sym;
+	if (sym->evaluated)
+		return sym;
+	sym->evaluated = 1;
+
+	sym = examine_symbol_type(sym);
+	base_type = get_base_type(sym);
+	if (!base_type)
+		return NULL;
+
+	/* Evaluate the initializers */
+	if (sym->initializer)
+		evaluate_initializer(sym, &sym->initializer);
+
+	/* And finally, evaluate the body of the symbol too */
+	if (base_type->type == SYM_FN) {
+		struct symbol *curr = current_fn;
+
+		if (sym->definition && sym->definition != sym)
+			return evaluate_symbol(sym->definition);
+
+		current_fn = base_type;
+
+		examine_fn_arguments(base_type);
+		if (!base_type->stmt && base_type->inline_stmt)
+			uninline(sym);
+		if (base_type->stmt)
+			evaluate_statement(base_type->stmt);
+
+		current_fn = curr;
+	}
+
+	return base_type;
+}
+
+void evaluate_symbol_list(struct symbol_list *list)
+{
+	struct symbol *sym;
+
+	FOR_EACH_PTR(list, sym) {
+		evaluate_symbol(sym);
+		check_duplicates(sym);
+	} END_FOR_EACH_PTR(sym);
+}
+
+static struct symbol *evaluate_return_expression(struct statement *stmt)
+{
+	struct expression *expr = stmt->expression;
+	struct symbol *fntype;
+
+	evaluate_expression(expr);
+	fntype = current_fn->ctype.base_type;
+	if (!fntype || fntype == &void_ctype) {
+		if (expr && expr->ctype != &void_ctype)
+			expression_error(expr, "return expression in %s function", fntype?"void":"typeless");
+		if (expr && Wreturn_void)
+			warning(stmt->pos, "returning void-valued expression");
+		return NULL;
+	}
+
+	if (!expr) {
+		sparse_error(stmt->pos, "return with no return value");
+		return NULL;
+	}
+	if (!expr->ctype)
+		return NULL;
+	compatible_assignment_types(expr, fntype, &stmt->expression, "return expression");
+	return NULL;
+}
+
+static void evaluate_if_statement(struct statement *stmt)
+{
+	if (!stmt->if_conditional)
+		return;
+
+	evaluate_conditional(stmt->if_conditional, 0);
+	evaluate_statement(stmt->if_true);
+	evaluate_statement(stmt->if_false);
+}
+
+static void evaluate_iterator(struct statement *stmt)
+{
+	evaluate_symbol_list(stmt->iterator_syms);
+	evaluate_conditional(stmt->iterator_pre_condition, 1);
+	evaluate_conditional(stmt->iterator_post_condition,1);
+	evaluate_statement(stmt->iterator_pre_statement);
+	evaluate_statement(stmt->iterator_statement);
+	evaluate_statement(stmt->iterator_post_statement);
+}
+
+static void verify_output_constraint(struct expression *expr, const char *constraint)
+{
+	switch (*constraint) {
+	case '=':	/* Assignment */
+	case '+':	/* Update */
+		break;
+	default:
+		expression_error(expr, "output constraint is not an assignment constraint (\"%s\")", constraint);
+	}
+}
+
+static void verify_input_constraint(struct expression *expr, const char *constraint)
+{
+	switch (*constraint) {
+	case '=':	/* Assignment */
+	case '+':	/* Update */
+		expression_error(expr, "input constraint with assignment (\"%s\")", constraint);
+	}
+}
+
+static void evaluate_asm_statement(struct statement *stmt)
+{
+	struct expression *expr;
+	struct symbol *sym;
+	int state;
+
+	expr = stmt->asm_string;
+	if (!expr || expr->type != EXPR_STRING) {
+		sparse_error(stmt->pos, "need constant string for inline asm");
+		return;
+	}
+
+	state = 0;
+	FOR_EACH_PTR(stmt->asm_outputs, expr) {
+		switch (state) {
+		case 0: /* Identifier */
+			state = 1;
+			continue;
+
+		case 1: /* Constraint */
+			state = 2;
+			if (!expr || expr->type != EXPR_STRING) {
+				sparse_error(expr ? expr->pos : stmt->pos, "asm output constraint is not a string");
+				*THIS_ADDRESS(expr) = NULL;
+				continue;
+			}
+			verify_output_constraint(expr, expr->string->data);
+			continue;
+
+		case 2: /* Expression */
+			state = 0;
+			if (!evaluate_expression(expr))
+				return;
+			if (!lvalue_expression(expr))
+				warning(expr->pos, "asm output is not an lvalue");
+			evaluate_assign_to(expr, expr->ctype);
+			continue;
+		}
+	} END_FOR_EACH_PTR(expr);
+
+	state = 0;
+	FOR_EACH_PTR(stmt->asm_inputs, expr) {
+		switch (state) {
+		case 0: /* Identifier */
+			state = 1;
+			continue;
+
+		case 1:	/* Constraint */
+			state = 2;
+			if (!expr || expr->type != EXPR_STRING) {
+				sparse_error(expr ? expr->pos : stmt->pos, "asm input constraint is not a string");
+				*THIS_ADDRESS(expr) = NULL;
+				continue;
+			}
+			verify_input_constraint(expr, expr->string->data);
+			continue;
+
+		case 2: /* Expression */
+			state = 0;
+			if (!evaluate_expression(expr))
+				return;
+			continue;
+		}
+	} END_FOR_EACH_PTR(expr);
+
+	FOR_EACH_PTR(stmt->asm_clobbers, expr) {
+		if (!expr) {
+			sparse_error(stmt->pos, "bad asm clobbers");
+			return;
+		}
+		if (expr->type == EXPR_STRING)
+			continue;
+		expression_error(expr, "asm clobber is not a string");
+	} END_FOR_EACH_PTR(expr);
+
+	FOR_EACH_PTR(stmt->asm_labels, sym) {
+		if (!sym || sym->type != SYM_LABEL) {
+			sparse_error(stmt->pos, "bad asm label");
+			return;
+		}
+	} END_FOR_EACH_PTR(sym);
+}
+
+static void evaluate_case_statement(struct statement *stmt)
+{
+	evaluate_expression(stmt->case_expression);
+	evaluate_expression(stmt->case_to);
+	evaluate_statement(stmt->case_statement);
+}
+
+static void check_case_type(struct expression *switch_expr,
+			    struct expression *case_expr,
+			    struct expression **enumcase)
+{
+	struct symbol *switch_type, *case_type;
+	int sclass, cclass;
+
+	if (!case_expr)
+		return;
+
+	switch_type = switch_expr->ctype;
+	case_type = evaluate_expression(case_expr);
+
+	if (!switch_type || !case_type)
+		goto Bad;
+	if (enumcase) {
+		if (*enumcase)
+			warn_for_different_enum_types(case_expr->pos, case_type, (*enumcase)->ctype);
+		else if (is_enum_type(case_type))
+			*enumcase = case_expr;
+	}
+
+	sclass = classify_type(switch_type, &switch_type);
+	cclass = classify_type(case_type, &case_type);
+
+	/* both should be arithmetic */
+	if (!(sclass & cclass & TYPE_NUM))
+		goto Bad;
+
+	/* neither should be floating */
+	if ((sclass | cclass) & TYPE_FLOAT)
+		goto Bad;
+
+	/* if neither is restricted, we are OK */
+	if (!((sclass | cclass) & TYPE_RESTRICT))
+		return;
+
+	if (!restricted_binop_type(SPECIAL_EQUAL, case_expr, switch_expr,
+				   cclass, sclass, case_type, switch_type)) {
+		unrestrict(case_expr, cclass, &case_type);
+		unrestrict(switch_expr, sclass, &switch_type);
+	}
+	return;
+
+Bad:
+	expression_error(case_expr, "incompatible types for 'case' statement");
+}
+
+static void evaluate_switch_statement(struct statement *stmt)
+{
+	struct symbol *sym;
+	struct expression *enumcase = NULL;
+	struct expression **enumcase_holder = &enumcase;
+	struct expression *sel = stmt->switch_expression;
+
+	evaluate_expression(sel);
+	evaluate_statement(stmt->switch_statement);
+	if (!sel)
+		return;
+	if (sel->ctype && is_enum_type(sel->ctype))
+		enumcase_holder = NULL; /* Only check cases against switch */
+
+	FOR_EACH_PTR(stmt->switch_case->symbol_list, sym) {
+		struct statement *case_stmt = sym->stmt;
+		check_case_type(sel, case_stmt->case_expression, enumcase_holder);
+		check_case_type(sel, case_stmt->case_to, enumcase_holder);
+	} END_FOR_EACH_PTR(sym);
+}
+
+struct symbol *evaluate_statement(struct statement *stmt)
+{
+	if (!stmt)
+		return NULL;
+
+	switch (stmt->type) {
+	case STMT_DECLARATION: {
+		struct symbol *s;
+		FOR_EACH_PTR(stmt->declaration, s) {
+			evaluate_symbol(s);
+		} END_FOR_EACH_PTR(s);
+		return NULL;
+	}
+
+	case STMT_RETURN:
+		return evaluate_return_expression(stmt);
+
+	case STMT_EXPRESSION:
+		if (!evaluate_expression(stmt->expression))
+			return NULL;
+		if (stmt->expression->ctype == &null_ctype)
+			stmt->expression = cast_to(stmt->expression, &ptr_ctype);
+		return degenerate(stmt->expression);
+
+	case STMT_COMPOUND: {
+		struct statement *s;
+		struct symbol *type = NULL;
+
+		/* Evaluate the return symbol in the compound statement */
+		evaluate_symbol(stmt->ret);
+
+		/*
+		 * Then, evaluate each statement, making the type of the
+		 * compound statement be the type of the last statement
+		 */
+		type = evaluate_statement(stmt->args);
+		FOR_EACH_PTR(stmt->stmts, s) {
+			type = evaluate_statement(s);
+		} END_FOR_EACH_PTR(s);
+		if (!type)
+			type = &void_ctype;
+		return type;
+	}
+	case STMT_IF:
+		evaluate_if_statement(stmt);
+		return NULL;
+	case STMT_ITERATOR:
+		evaluate_iterator(stmt);
+		return NULL;
+	case STMT_SWITCH:
+		evaluate_switch_statement(stmt);
+		return NULL;
+	case STMT_CASE:
+		evaluate_case_statement(stmt);
+		return NULL;
+	case STMT_LABEL:
+		return evaluate_statement(stmt->label_statement);
+	case STMT_GOTO:
+		evaluate_expression(stmt->goto_expression);
+		return NULL;
+	case STMT_NONE:
+		break;
+	case STMT_ASM:
+		evaluate_asm_statement(stmt);
+		return NULL;
+	case STMT_CONTEXT:
+		evaluate_expression(stmt->expression);
+		return NULL;
+	case STMT_RANGE:
+		evaluate_expression(stmt->range_expression);
+		evaluate_expression(stmt->range_low);
+		evaluate_expression(stmt->range_high);
+		return NULL;
+	}
+	return NULL;
+}
diff --git a/deps/sparse/example.c b/deps/sparse/example.c
new file mode 100644
index 0000000..24444c6
--- /dev/null
+++ b/deps/sparse/example.c
@@ -0,0 +1,1955 @@
+/*
+ * Example of how to write a compiler with sparse
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+
+#include "symbol.h"
+#include "expression.h"
+#include "linearize.h"
+#include "flow.h"
+#include "storage.h"
+#include "target.h"
+
+static const char *opcodes[] = {
+	[OP_BADOP] = "bad_op",
+
+	/* Fn entrypoint */
+	[OP_ENTRY] = "<entry-point>",
+
+	/* Terminator */
+	[OP_RET] = "ret",
+	[OP_BR] = "br",
+	[OP_SWITCH] = "switch",
+	[OP_INVOKE] = "invoke",
+	[OP_COMPUTEDGOTO] = "jmp *",
+	[OP_UNWIND] = "unwind",
+	
+	/* Binary */
+	[OP_ADD] = "add",
+	[OP_SUB] = "sub",
+	[OP_MULU] = "mulu",
+	[OP_MULS] = "muls",
+	[OP_DIVU] = "divu",
+	[OP_DIVS] = "divs",
+	[OP_MODU] = "modu",
+	[OP_MODS] = "mods",
+	[OP_SHL] = "shl",
+	[OP_LSR] = "lsr",
+	[OP_ASR] = "asr",
+	
+	/* Logical */
+	[OP_AND] = "and",
+	[OP_OR] = "or",
+	[OP_XOR] = "xor",
+	[OP_AND_BOOL] = "and-bool",
+	[OP_OR_BOOL] = "or-bool",
+
+	/* Binary comparison */
+	[OP_SET_EQ] = "seteq",
+	[OP_SET_NE] = "setne",
+	[OP_SET_LE] = "setle",
+	[OP_SET_GE] = "setge",
+	[OP_SET_LT] = "setlt",
+	[OP_SET_GT] = "setgt",
+	[OP_SET_B] = "setb",
+	[OP_SET_A] = "seta",
+	[OP_SET_BE] = "setbe",
+	[OP_SET_AE] = "setae",
+
+	/* Uni */
+	[OP_NOT] = "not",
+	[OP_NEG] = "neg",
+
+	/* Special three-input */
+	[OP_SEL] = "select",
+	
+	/* Memory */
+	[OP_MALLOC] = "malloc",
+	[OP_FREE] = "free",
+	[OP_ALLOCA] = "alloca",
+	[OP_LOAD] = "load",
+	[OP_STORE] = "store",
+	[OP_SETVAL] = "set",
+	[OP_GET_ELEMENT_PTR] = "getelem",
+
+	/* Other */
+	[OP_PHI] = "phi",
+	[OP_PHISOURCE] = "phisrc",
+	[OP_COPY] = "copy",
+	[OP_CAST] = "cast",
+	[OP_SCAST] = "scast",
+	[OP_FPCAST] = "fpcast",
+	[OP_PTRCAST] = "ptrcast",
+	[OP_CALL] = "call",
+	[OP_VANEXT] = "va_next",
+	[OP_VAARG] = "va_arg",
+	[OP_SLICE] = "slice",
+	[OP_SNOP] = "snop",
+	[OP_LNOP] = "lnop",
+	[OP_NOP] = "nop",
+	[OP_DEATHNOTE] = "dead",
+	[OP_ASM] = "asm",
+
+	/* Sparse tagging (line numbers, context, whatever) */
+	[OP_CONTEXT] = "context",
+};
+
+static int last_reg, stack_offset;
+
+struct hardreg {
+	const char *name;
+	struct pseudo_list *contains;
+	unsigned busy:16,
+		 dead:8,
+		 used:1;
+};
+
+#define TAG_DEAD 1
+#define TAG_DIRTY 2
+
+/* Our "switch" generation is very very stupid. */
+#define SWITCH_REG (1)
+
+static void output_bb(struct basic_block *bb, unsigned long generation);
+
+/*
+ * We only know about the caller-clobbered registers
+ * right now.
+ */
+static struct hardreg hardregs[] = {
+	{ .name = "%eax" },
+	{ .name = "%edx" },
+	{ .name = "%ecx" },
+	{ .name = "%ebx" },
+	{ .name = "%esi" },
+	{ .name = "%edi" },
+
+	{ .name = "%ebp" },
+	{ .name = "%esp" },
+};
+#define REGNO 6
+#define REG_EBP 6
+#define REG_ESP 7
+
+struct bb_state {
+	struct position pos;
+	struct storage_hash_list *inputs;
+	struct storage_hash_list *outputs;
+	struct storage_hash_list *internal;
+
+	/* CC cache.. */
+	int cc_opcode, cc_dead;
+	pseudo_t cc_target;
+};
+
+enum optype {
+	OP_UNDEF,
+	OP_REG,
+	OP_VAL,
+	OP_MEM,
+	OP_ADDR,
+};
+
+struct operand {
+	enum optype type;
+	int size;
+	union {
+		struct hardreg *reg;
+		long long value;
+		struct /* OP_MEM and OP_ADDR */ {
+			unsigned int offset;
+			unsigned int scale;
+			struct symbol *sym;
+			struct hardreg *base;
+			struct hardreg *index;
+		};
+	};
+};
+
+static const char *show_op(struct bb_state *state, struct operand *op)
+{
+	static char buf[256][4];
+	static int bufnr;
+	char *p, *ret;
+	int nr;
+
+	nr = (bufnr + 1) & 3;
+	bufnr = nr;
+	ret = p = buf[nr];
+
+	switch (op->type) {
+	case OP_UNDEF:
+		return "undef";
+	case OP_REG:
+		return op->reg->name;
+	case OP_VAL:
+		sprintf(p, "$%lld", op->value);
+		break;
+	case OP_MEM:
+	case OP_ADDR:
+		if (op->offset)
+			p += sprintf(p, "%d", op->offset);
+		if (op->sym)
+			p += sprintf(p, "%s%s",
+				op->offset ? "+" : "",
+				show_ident(op->sym->ident));
+		if (op->base || op->index) {
+			p += sprintf(p, "(%s%s%s",
+				op->base ? op->base->name : "",
+				(op->base && op->index) ? "," : "",
+				op->index ? op->index->name : "");
+			if (op->scale > 1)
+				p += sprintf(p, ",%d", op->scale);
+			*p++ = ')';
+			*p = '\0';
+		}
+		break;
+	}
+	return ret;
+}
+
+static struct storage_hash *find_storage_hash(pseudo_t pseudo, struct storage_hash_list *list)
+{
+	struct storage_hash *entry;
+	FOR_EACH_PTR(list, entry) {
+		if (entry->pseudo == pseudo)
+			return entry;
+	} END_FOR_EACH_PTR(entry);
+	return NULL;
+}
+
+static struct storage_hash *find_or_create_hash(pseudo_t pseudo, struct storage_hash_list **listp)
+{
+	struct storage_hash *entry;
+
+	entry = find_storage_hash(pseudo, *listp);
+	if (!entry) {
+		entry = alloc_storage_hash(alloc_storage());
+		entry->pseudo = pseudo;
+		add_ptr_list(listp, entry);
+	}
+	return entry;
+}
+
+/* Eventually we should just build it up in memory */
+static void FORMAT_ATTR(2) output_line(struct bb_state *state, const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	vprintf(fmt, args);
+	va_end(args);
+}
+
+static void FORMAT_ATTR(2) output_label(struct bb_state *state, const char *fmt, ...)
+{
+	static char buffer[512];
+	va_list args;
+
+	va_start(args, fmt);
+	vsnprintf(buffer, sizeof(buffer), fmt, args);
+	va_end(args);
+
+	output_line(state, "%s:\n", buffer);
+}
+
+static void FORMAT_ATTR(2) output_insn(struct bb_state *state, const char *fmt, ...)
+{
+	static char buffer[512];
+	va_list args;
+
+	va_start(args, fmt);
+	vsnprintf(buffer, sizeof(buffer), fmt, args);
+	va_end(args);
+
+	output_line(state, "\t%s\n", buffer);
+}
+
+#define output_insn(state, fmt, arg...) \
+	output_insn(state, fmt "\t\t# %s" , ## arg , __FUNCTION__)
+
+static void FORMAT_ATTR(2) output_comment(struct bb_state *state, const char *fmt, ...)
+{
+	static char buffer[512];
+	va_list args;
+
+	if (!verbose)
+		return;
+	va_start(args, fmt);
+	vsnprintf(buffer, sizeof(buffer), fmt, args);
+	va_end(args);
+
+	output_line(state, "\t# %s\n", buffer);
+}
+
+static const char *show_memop(struct storage *storage)
+{
+	static char buffer[1000];
+
+	if (!storage)
+		return "undef";
+	switch (storage->type) {
+	case REG_FRAME:
+		sprintf(buffer, "%d(FP)", storage->offset);
+		break;
+	case REG_STACK:
+		sprintf(buffer, "%d(SP)", storage->offset);
+		break;
+	case REG_REG:
+		return hardregs[storage->regno].name;
+	default:
+		return show_storage(storage);
+	}
+	return buffer;
+}
+
+static int alloc_stack_offset(int size)
+{
+	int ret = stack_offset;
+	stack_offset = ret + size;
+	return ret;
+}
+
+static void alloc_stack(struct bb_state *state, struct storage *storage)
+{
+	storage->type = REG_STACK;
+	storage->offset = alloc_stack_offset(4);
+}
+
+/*
+ * Can we re-generate the pseudo, so that we don't need to
+ * flush it to memory? We can regenerate:
+ *  - immediates and symbol addresses
+ *  - pseudos we got as input in non-registers
+ *  - pseudos we've already saved off earlier..
+ */
+static int can_regenerate(struct bb_state *state, pseudo_t pseudo)
+{
+	struct storage_hash *in;
+
+	switch (pseudo->type) {
+	case PSEUDO_VAL:
+	case PSEUDO_SYM:
+		return 1;
+
+	default:
+		in = find_storage_hash(pseudo, state->inputs);
+		if (in && in->storage->type != REG_REG)
+			return 1;
+		in = find_storage_hash(pseudo, state->internal);
+		if (in)
+			return 1;
+	}
+	return 0;
+}
+
+static void flush_one_pseudo(struct bb_state *state, struct hardreg *hardreg, pseudo_t pseudo)
+{
+	struct storage_hash *out;
+	struct storage *storage;
+
+	if (can_regenerate(state, pseudo))
+		return;
+
+	output_comment(state, "flushing %s from %s", show_pseudo(pseudo), hardreg->name);
+	out = find_storage_hash(pseudo, state->internal);
+	if (!out) {
+		out = find_storage_hash(pseudo, state->outputs);
+		if (!out)
+			out = find_or_create_hash(pseudo, &state->internal);
+	}
+	storage = out->storage;
+	switch (storage->type) {
+	default:
+		/*
+		 * Aieee - the next user wants it in a register, but we
+		 * need to flush it to memory in between. Which means that
+		 * we need to allocate an internal one, dammit..
+		 */
+		out = find_or_create_hash(pseudo, &state->internal);
+		storage = out->storage;
+		/* Fall through */
+	case REG_UDEF:
+		alloc_stack(state, storage);
+		/* Fall through */
+	case REG_STACK:
+		output_insn(state, "movl %s,%s", hardreg->name, show_memop(storage));
+		break;
+	}
+}
+
+/* Flush a hardreg out to the storage it has.. */
+static void flush_reg(struct bb_state *state, struct hardreg *reg)
+{
+	pseudo_t pseudo;
+
+	if (reg->busy)
+		output_comment(state, "reg %s flushed while busy is %d!", reg->name, reg->busy);
+	if (!reg->contains)
+		return;
+	reg->dead = 0;
+	reg->used = 1;
+	FOR_EACH_PTR(reg->contains, pseudo) {
+		if (CURRENT_TAG(pseudo) & TAG_DEAD)
+			continue;
+		if (!(CURRENT_TAG(pseudo) & TAG_DIRTY))
+			continue;
+		flush_one_pseudo(state, reg, pseudo);
+	} END_FOR_EACH_PTR(pseudo);
+	free_ptr_list(&reg->contains);
+}
+
+static struct storage_hash *find_pseudo_storage(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
+{
+	struct storage_hash *src;
+
+	src = find_storage_hash(pseudo, state->internal);
+	if (!src) {
+		src = find_storage_hash(pseudo, state->inputs);
+		if (!src) {
+			src = find_storage_hash(pseudo, state->outputs);
+			/* Undefined? Screw it! */
+			if (!src)
+				return NULL;
+
+			/*
+			 * If we found output storage, it had better be local stack
+			 * that we flushed to earlier..
+			 */
+			if (src->storage->type != REG_STACK)
+				return NULL;
+		}
+	}
+
+	/*
+	 * Incoming pseudo with out any pre-set storage allocation?
+	 * We can make up our own, and obviously prefer to get it
+	 * in the register we already selected (if it hasn't been
+	 * used yet).
+	 */
+	if (src->storage->type == REG_UDEF) {
+		if (reg && !reg->used) {
+			src->storage->type = REG_REG;
+			src->storage->regno = reg - hardregs;
+			return NULL;
+		}
+		alloc_stack(state, src->storage);
+	}
+	return src;
+}
+
+static void mark_reg_dead(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
+{
+	pseudo_t p;
+
+	FOR_EACH_PTR(reg->contains, p) {
+		if (p != pseudo)
+			continue;
+		if (CURRENT_TAG(p) & TAG_DEAD)
+			continue;
+		output_comment(state, "marking pseudo %s in reg %s dead", show_pseudo(pseudo), reg->name);
+		TAG_CURRENT(p, TAG_DEAD);
+		reg->dead++;
+	} END_FOR_EACH_PTR(p);
+}
+
+static void add_pseudo_reg(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
+{
+	output_comment(state, "added pseudo %s to reg %s", show_pseudo(pseudo), reg->name);
+	add_ptr_list_tag(&reg->contains, pseudo, TAG_DIRTY);
+}
+
+static struct hardreg *preferred_reg(struct bb_state *state, pseudo_t target)
+{
+	struct storage_hash *dst;
+
+	dst = find_storage_hash(target, state->outputs);
+	if (dst) {
+		struct storage *storage = dst->storage;
+		if (storage->type == REG_REG)
+			return hardregs + storage->regno;
+	}
+	return NULL;
+}
+
+static struct hardreg *empty_reg(struct bb_state *state)
+{
+	int i;
+	struct hardreg *reg = hardregs;
+
+	for (i = 0; i < REGNO; i++, reg++) {
+		if (!reg->contains)
+			return reg;
+	}
+	return NULL;
+}
+
+static struct hardreg *target_reg(struct bb_state *state, pseudo_t pseudo, pseudo_t target)
+{
+	int i;
+	int unable_to_find_reg = 0;
+	struct hardreg *reg;
+
+	/* First, see if we have a preferred target register.. */
+	reg = preferred_reg(state, target);
+	if (reg && !reg->contains)
+		goto found;
+
+	reg = empty_reg(state);
+	if (reg)
+		goto found;
+
+	i = last_reg;
+	do {
+		i++;
+		if (i >= REGNO)
+			i = 0;
+		reg = hardregs + i;
+		if (!reg->busy) {
+			flush_reg(state, reg);
+			last_reg = i;
+			goto found;
+		}
+	} while (i != last_reg);
+	assert(unable_to_find_reg);
+
+found:
+	add_pseudo_reg(state, pseudo, reg);
+	return reg;
+}
+
+static struct hardreg *find_in_reg(struct bb_state *state, pseudo_t pseudo)
+{
+	int i;
+	struct hardreg *reg;
+
+	for (i = 0; i < REGNO; i++) {
+		pseudo_t p;
+
+		reg = hardregs + i;
+		FOR_EACH_PTR(reg->contains, p) {
+			if (p == pseudo) {
+				last_reg = i;
+				output_comment(state, "found pseudo %s in reg %s (busy=%d)", show_pseudo(pseudo), reg->name, reg->busy);
+				return reg;
+			}
+		} END_FOR_EACH_PTR(p);
+	}
+	return NULL;
+}
+
+static void flush_pseudo(struct bb_state *state, pseudo_t pseudo, struct storage *storage)
+{
+	struct hardreg *reg = find_in_reg(state, pseudo);
+
+	if (reg)
+		flush_reg(state, reg);
+}
+
+static void flush_cc_cache_to_reg(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
+{
+	int opcode = state->cc_opcode;
+
+	state->cc_opcode = 0;
+	state->cc_target = NULL;
+	output_insn(state, "%s %s", opcodes[opcode], reg->name);
+}
+
+static void flush_cc_cache(struct bb_state *state)
+{
+	pseudo_t pseudo = state->cc_target;
+
+	if (pseudo) {
+		struct hardreg *dst;
+
+		state->cc_target = NULL;
+
+		if (!state->cc_dead) {
+			dst = target_reg(state, pseudo, pseudo);
+			flush_cc_cache_to_reg(state, pseudo, dst);
+		}
+	}
+}
+
+static void add_cc_cache(struct bb_state *state, int opcode, pseudo_t pseudo)
+{
+	assert(!state->cc_target);
+	state->cc_target = pseudo;
+	state->cc_opcode = opcode;
+	state->cc_dead = 0;
+	output_comment(state, "caching %s", opcodes[opcode]);
+}
+
+/* Fill a hardreg with the pseudo it has */
+static struct hardreg *fill_reg(struct bb_state *state, struct hardreg *hardreg, pseudo_t pseudo)
+{
+	struct storage_hash *src;
+	struct instruction *def;
+
+	if (state->cc_target == pseudo) {
+		flush_cc_cache_to_reg(state, pseudo, hardreg);
+		return hardreg;
+	}
+
+	switch (pseudo->type) {
+	case PSEUDO_VAL:
+		output_insn(state, "movl $%lld,%s", pseudo->value, hardreg->name);
+		break;
+	case PSEUDO_SYM:
+		src = find_pseudo_storage(state, pseudo, NULL);
+		/* Static thing? */
+		if (!src) {
+			output_insn(state, "movl $<%s>,%s", show_pseudo(pseudo), hardreg->name);
+			break;
+		}
+		switch (src->storage->type) {
+		case REG_REG:
+			/* Aiaiaiaiaii! Need to flush it to temporary memory */
+			src = find_or_create_hash(pseudo, &state->internal);
+			/* Fall through */
+		default:
+			alloc_stack(state, src->storage);
+			/* Fall through */
+		case REG_STACK:
+		case REG_FRAME:
+			flush_pseudo(state, pseudo, src->storage);
+			output_insn(state, "leal %s,%s", show_memop(src->storage), hardreg->name);
+			break;
+		}
+		break;
+	case PSEUDO_ARG:
+	case PSEUDO_REG:
+		def = pseudo->def;
+		if (def && def->opcode == OP_SETVAL) {
+			output_insn(state, "movl $<%s>,%s", show_pseudo(def->target), hardreg->name);
+			break;
+		}
+		src = find_pseudo_storage(state, pseudo, hardreg);
+		if (!src)
+			break;
+		if (src->flags & TAG_DEAD)
+			mark_reg_dead(state, pseudo, hardreg);
+		output_insn(state, "mov.%d %s,%s", 32, show_memop(src->storage), hardreg->name);
+		break;
+	default:
+		output_insn(state, "reload %s from %s", hardreg->name, show_pseudo(pseudo));
+		break;
+	}
+	return hardreg;
+}
+
+static struct hardreg *getreg(struct bb_state *state, pseudo_t pseudo, pseudo_t target)
+{
+	struct hardreg *reg;
+
+	reg = find_in_reg(state, pseudo);
+	if (reg)
+		return reg;
+	reg = target_reg(state, pseudo, target);
+	return fill_reg(state, reg, pseudo);
+}
+
+static void move_reg(struct bb_state *state, struct hardreg *src, struct hardreg *dst)
+{
+	output_insn(state, "movl %s,%s", src->name, dst->name);
+}
+
+static struct hardreg *copy_reg(struct bb_state *state, struct hardreg *src, pseudo_t target)
+{
+	int i;
+	struct hardreg *reg;
+
+	/* If the container has been killed off, just re-use it */
+	if (!src->contains)
+		return src;
+
+	/* If "src" only has one user, and the contents are dead, we can re-use it */
+	if (src->busy == 1 && src->dead == 1)
+		return src;
+
+	reg = preferred_reg(state, target);
+	if (reg && !reg->contains) {
+		output_comment(state, "copying %s to preferred target %s", show_pseudo(target), reg->name);
+		move_reg(state, src, reg);
+		return reg;
+	}
+
+	for (i = 0; i < REGNO; i++) {
+		reg = hardregs + i;
+		if (!reg->contains) {
+			output_comment(state, "copying %s to %s", show_pseudo(target), reg->name);
+			output_insn(state, "movl %s,%s", src->name, reg->name);
+			return reg;
+		}
+	}
+
+	flush_reg(state, src);
+	return src;
+}
+
+static void put_operand(struct bb_state *state, struct operand *op)
+{
+	switch (op->type) {
+	case OP_REG:
+		op->reg->busy--;
+		break;
+	case OP_ADDR:
+	case OP_MEM:
+		if (op->base)
+			op->base->busy--;
+		if (op->index)
+			op->index->busy--;
+		break;
+	default:
+		break;
+	}
+}
+
+static struct operand *alloc_op(void)
+{
+	struct operand *op = malloc(sizeof(*op));
+	memset(op, 0, sizeof(*op));
+	return op;
+}
+
+static struct operand *get_register_operand(struct bb_state *state, pseudo_t pseudo, pseudo_t target)
+{
+	struct operand *op = alloc_op();
+	op->type = OP_REG;
+	op->reg = getreg(state, pseudo, target);
+	op->reg->busy++;
+	return op;
+}
+
+static int get_sym_frame_offset(struct bb_state *state, pseudo_t pseudo)
+{
+	int offset = pseudo->nr;
+	if (offset < 0) {
+		offset = alloc_stack_offset(4);
+		pseudo->nr = offset;
+	}
+	return offset;
+}
+
+static struct operand *get_generic_operand(struct bb_state *state, pseudo_t pseudo)
+{
+	struct hardreg *reg;
+	struct storage *src;
+	struct storage_hash *hash;
+	struct operand *op = malloc(sizeof(*op));
+
+	memset(op, 0, sizeof(*op));
+	switch (pseudo->type) {
+	case PSEUDO_VAL:
+		op->type = OP_VAL;
+		op->value = pseudo->value;
+		break;
+
+	case PSEUDO_SYM: {
+		struct symbol *sym = pseudo->sym;
+		op->type = OP_ADDR;
+		if (sym->ctype.modifiers & MOD_NONLOCAL) {
+			op->sym = sym;
+			break;
+		}
+		op->base = hardregs + REG_EBP;
+		op->offset = get_sym_frame_offset(state, pseudo);
+		break;
+	}
+
+	default:
+		reg = find_in_reg(state, pseudo);
+		if (reg) {
+			op->type = OP_REG;
+			op->reg = reg;
+			reg->busy++;
+			break;
+		}
+		hash = find_pseudo_storage(state, pseudo, NULL);
+		if (!hash)
+			break;
+		src = hash->storage;
+		switch (src->type) {
+		case REG_REG:
+			op->type = OP_REG;
+			op->reg = hardregs + src->regno;
+			op->reg->busy++;
+			break;
+		case REG_FRAME:
+			op->type = OP_MEM;
+			op->offset = src->offset;
+			op->base = hardregs + REG_EBP;
+			break;
+		case REG_STACK:
+			op->type = OP_MEM;
+			op->offset = src->offset;
+			op->base = hardregs + REG_ESP;
+			break;
+		default:
+			break;
+		}
+	}
+	return op;
+}
+
+/* Callers should be made to use the proper "operand" formats */
+static const char *generic(struct bb_state *state, pseudo_t pseudo)
+{
+	struct hardreg *reg;
+	struct operand *op = get_generic_operand(state, pseudo);
+	static char buf[100];
+	const char *str;
+
+	switch (op->type) {
+	case OP_ADDR:
+		if (!op->offset && op->base && !op->sym)
+			return op->base->name;
+		if (op->sym && !op->base) {
+			int len = sprintf(buf, "$ %s", show_op(state, op));
+			if (op->offset)
+				sprintf(buf + len, " + %d", op->offset);
+			return buf;
+		}
+		str = show_op(state, op);
+		put_operand(state, op);
+		reg = target_reg(state, pseudo, NULL);
+		output_insn(state, "lea %s,%s", show_op(state, op), reg->name);
+		return reg->name;		
+
+	default:
+		str = show_op(state, op);
+	}
+	put_operand(state, op);
+	return str;
+}
+
+static struct operand *get_address_operand(struct bb_state *state, struct instruction *memop)
+{
+	struct hardreg *base;
+	struct operand *op = get_generic_operand(state, memop->src);
+
+	switch (op->type) {
+	case OP_ADDR:
+		op->offset += memop->offset;
+		break;
+	default:
+		put_operand(state, op);
+		base = getreg(state, memop->src, NULL);
+		op->type = OP_ADDR;
+		op->base = base;
+		base->busy++;
+		op->offset = memop->offset;
+		op->sym = NULL;
+	}
+	return op;
+}
+
+static const char *address(struct bb_state *state, struct instruction *memop)
+{
+	struct operand *op = get_address_operand(state, memop);
+	const char *str = show_op(state, op);
+	put_operand(state, op);
+	return str;
+}
+
+static const char *reg_or_imm(struct bb_state *state, pseudo_t pseudo)
+{
+	switch(pseudo->type) {
+	case PSEUDO_VAL:
+		return show_pseudo(pseudo);
+	default:
+		return getreg(state, pseudo, NULL)->name;
+	}
+}
+
+static void kill_dead_reg(struct hardreg *reg)
+{
+	if (reg->dead) {
+		pseudo_t p;
+		
+		FOR_EACH_PTR(reg->contains, p) {
+			if (CURRENT_TAG(p) & TAG_DEAD) {
+				DELETE_CURRENT_PTR(p);
+				reg->dead--;
+			}
+		} END_FOR_EACH_PTR(p);
+		PACK_PTR_LIST(&reg->contains);
+		assert(!reg->dead);
+	}
+}
+
+static struct hardreg *target_copy_reg(struct bb_state *state, struct hardreg *src, pseudo_t target)
+{
+	kill_dead_reg(src);
+	return copy_reg(state, src, target);
+}
+
+static void do_binop(struct bb_state *state, struct instruction *insn, pseudo_t val1, pseudo_t val2)
+{
+	const char *op = opcodes[insn->opcode];
+	struct operand *src = get_register_operand(state, val1, insn->target);
+	struct operand *src2 = get_generic_operand(state, val2);
+	struct hardreg *dst;
+
+	dst = target_copy_reg(state, src->reg, insn->target);
+	output_insn(state, "%s.%d %s,%s", op, insn->size, show_op(state, src2), dst->name);
+	put_operand(state, src);
+	put_operand(state, src2);
+	add_pseudo_reg(state, insn->target, dst);
+}
+
+static void generate_binop(struct bb_state *state, struct instruction *insn)
+{
+	flush_cc_cache(state);
+	do_binop(state, insn, insn->src1, insn->src2);
+}
+
+static int is_dead_reg(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
+{
+	pseudo_t p;
+	FOR_EACH_PTR(reg->contains, p) {
+		if (p == pseudo)
+			return CURRENT_TAG(p) & TAG_DEAD;
+	} END_FOR_EACH_PTR(p);
+	return 0;
+}
+
+/*
+ * Commutative binops are much more flexible, since we can switch the
+ * sources around to satisfy the target register, or to avoid having
+ * to load one of them into a register..
+ */
+static void generate_commutative_binop(struct bb_state *state, struct instruction *insn)
+{
+	pseudo_t src1, src2;
+	struct hardreg *reg1, *reg2;
+
+	flush_cc_cache(state);
+	src1 = insn->src1;
+	src2 = insn->src2;
+	reg2 = find_in_reg(state, src2);
+	if (!reg2)
+		goto dont_switch;
+	reg1 = find_in_reg(state, src1);
+	if (!reg1)
+		goto do_switch;
+	if (!is_dead_reg(state, src2, reg2))
+		goto dont_switch;
+	if (!is_dead_reg(state, src1, reg1))
+		goto do_switch;
+
+	/* Both are dead. Is one preferable? */
+	if (reg2 != preferred_reg(state, insn->target))
+		goto dont_switch;
+
+do_switch:
+	src1 = src2;
+	src2 = insn->src1;
+dont_switch:
+	do_binop(state, insn, src1, src2);
+}
+
+/*
+ * This marks a pseudo dead. It still stays on the hardreg list (the hardreg
+ * still has its value), but it's scheduled to be killed after the next
+ * "sequence point" when we call "kill_read_pseudos()"
+ */
+static void mark_pseudo_dead(struct bb_state *state, pseudo_t pseudo)
+{
+	int i;
+	struct storage_hash *src;
+
+	if (state->cc_target == pseudo)
+		state->cc_dead = 1;
+	src = find_pseudo_storage(state, pseudo, NULL);
+	if (src)
+		src->flags |= TAG_DEAD;
+	for (i = 0; i < REGNO; i++)
+		mark_reg_dead(state, pseudo, hardregs + i);
+}
+
+static void kill_dead_pseudos(struct bb_state *state)
+{
+	int i;
+
+	for (i = 0; i < REGNO; i++) {
+		kill_dead_reg(hardregs + i);
+	}
+}
+
+static void generate_store(struct instruction *insn, struct bb_state *state)
+{
+	output_insn(state, "mov.%d %s,%s", insn->size, reg_or_imm(state, insn->target), address(state, insn));
+}
+
+static void generate_load(struct instruction *insn, struct bb_state *state)
+{
+	const char *input = address(state, insn);
+	struct hardreg *dst;
+
+	kill_dead_pseudos(state);
+	dst = target_reg(state, insn->target, NULL);
+	output_insn(state, "mov.%d %s,%s", insn->size, input, dst->name);
+}
+
+static void kill_pseudo(struct bb_state *state, pseudo_t pseudo)
+{
+	int i;
+	struct hardreg *reg;
+
+	output_comment(state, "killing pseudo %s", show_pseudo(pseudo));
+	for (i = 0; i < REGNO; i++) {
+		pseudo_t p;
+
+		reg = hardregs + i;
+		FOR_EACH_PTR(reg->contains, p) {
+			if (p != pseudo)
+				continue;
+			if (CURRENT_TAG(p) & TAG_DEAD)
+				reg->dead--;
+			output_comment(state, "removing pseudo %s from reg %s", 
+				show_pseudo(pseudo), reg->name);
+			DELETE_CURRENT_PTR(p);
+		} END_FOR_EACH_PTR(p);
+		PACK_PTR_LIST(&reg->contains);
+	}
+}
+
+static void generate_copy(struct bb_state *state, struct instruction *insn)
+{
+	struct hardreg *src = getreg(state, insn->src, insn->target);
+	kill_pseudo(state, insn->target);
+	add_pseudo_reg(state, insn->target, src);
+}
+
+static void generate_cast(struct bb_state *state, struct instruction *insn)
+{
+	struct hardreg *src = getreg(state, insn->src, insn->target);
+	struct hardreg *dst;
+	unsigned int old = insn->orig_type ? insn->orig_type->bit_size : 0;
+	unsigned int new = insn->size;
+
+	/*
+	 * Cast to smaller type? Ignore the high bits, we
+	 * just keep both pseudos in the same register.
+	 */
+	if (old >= new) {
+		add_pseudo_reg(state, insn->target, src);
+		return;
+	}
+
+	dst = target_copy_reg(state, src, insn->target);
+
+	if (insn->orig_type && (insn->orig_type->ctype.modifiers & MOD_SIGNED)) {
+		output_insn(state, "sext.%d.%d %s", old, new, dst->name);
+	} else {
+		unsigned long long mask;
+		mask = ~(~0ULL << old);
+		mask &= ~(~0ULL << new);
+		output_insn(state, "andl.%d $%#llx,%s", insn->size, mask, dst->name);
+	}
+	add_pseudo_reg(state, insn->target, dst);
+}
+
+static void generate_output_storage(struct bb_state *state);
+
+static const char *conditional[] = {
+	[OP_SET_EQ] = "e",
+	[OP_SET_NE] = "ne",
+	[OP_SET_LE] = "le",
+	[OP_SET_GE] = "ge",
+	[OP_SET_LT] = "lt",
+	[OP_SET_GT] = "gt",
+	[OP_SET_B] = "b",
+	[OP_SET_A] = "a",
+	[OP_SET_BE] = "be",
+	[OP_SET_AE] = "ae"
+};
+	
+
+static void generate_branch(struct bb_state *state, struct instruction *br)
+{
+	const char *cond = "XXX";
+	struct basic_block *target;
+
+	if (br->cond) {
+		if (state->cc_target == br->cond) {
+			cond = conditional[state->cc_opcode];
+		} else {
+			struct hardreg *reg = getreg(state, br->cond, NULL);
+			output_insn(state, "testl %s,%s", reg->name, reg->name);
+			cond = "ne";
+		}
+	}
+	generate_output_storage(state);
+	target = br->bb_true;
+	if (br->cond) {
+		output_insn(state, "j%s .L%p", cond, target);
+		target = br->bb_false;
+	}
+	output_insn(state, "jmp .L%p", target);
+}
+
+/* We've made sure that there is a dummy reg live for the output */
+static void generate_switch(struct bb_state *state, struct instruction *insn)
+{
+	struct hardreg *reg = hardregs + SWITCH_REG;
+
+	generate_output_storage(state);
+	output_insn(state, "switch on %s", reg->name);
+	output_insn(state, "unimplemented: %s", show_instruction(insn));
+}
+
+static void generate_ret(struct bb_state *state, struct instruction *ret)
+{
+	if (ret->src && ret->src != VOID) {
+		struct hardreg *wants = hardregs+0;
+		struct hardreg *reg = getreg(state, ret->src, NULL);
+		if (reg != wants)
+			output_insn(state, "movl %s,%s", reg->name, wants->name);
+	}
+	output_insn(state, "ret");
+}
+
+/*
+ * Fake "call" linearization just as a taster..
+ */
+static void generate_call(struct bb_state *state, struct instruction *insn)
+{
+	int offset = 0;
+	pseudo_t arg;
+
+	FOR_EACH_PTR(insn->arguments, arg) {
+		output_insn(state, "pushl %s", generic(state, arg));
+		offset += 4;
+	} END_FOR_EACH_PTR(arg);
+	flush_reg(state, hardregs+0);
+	flush_reg(state, hardregs+1);
+	flush_reg(state, hardregs+2);
+	output_insn(state, "call %s", show_pseudo(insn->func));
+	if (offset)
+		output_insn(state, "addl $%d,%%esp", offset);
+	if (insn->target && insn->target != VOID)
+		add_pseudo_reg(state, insn->target, hardregs+0);
+}
+
+static void generate_select(struct bb_state *state, struct instruction *insn)
+{
+	const char *cond;
+	struct hardreg *src1, *src2, *dst;
+
+	src1 = getreg(state, insn->src2, NULL);
+	dst = copy_reg(state, src1, insn->target);
+	add_pseudo_reg(state, insn->target, dst);
+	src2 = getreg(state, insn->src3, insn->target);
+
+	if (state->cc_target == insn->src1) {
+		cond = conditional[state->cc_opcode];
+	} else {
+		struct hardreg *reg = getreg(state, insn->src1, NULL);
+		output_insn(state, "testl %s,%s", reg->name, reg->name);
+		cond = "ne";
+	}
+
+	output_insn(state, "sel%s %s,%s", cond, src2->name, dst->name);
+}
+
+struct asm_arg {
+	const struct ident *name;
+	const char *value;
+	pseudo_t pseudo;
+	struct hardreg *reg;
+};
+
+static void replace_asm_arg(char **dst_p, struct asm_arg *arg)
+{
+	char *dst = *dst_p;
+	int len = strlen(arg->value);
+
+	memcpy(dst, arg->value, len);
+	*dst_p = dst + len;
+}
+
+static void replace_asm_percent(const char **src_p, char **dst_p, struct asm_arg *args, int nr)
+{
+	const char *src = *src_p;
+	char c;
+	int index;
+
+	c = *src++;
+	switch (c) {
+	case '0' ... '9':
+		index = c - '0';
+		if (index < nr)
+			replace_asm_arg(dst_p, args+index);
+		break;
+	}	
+	*src_p = src;
+	return;
+}
+
+static void replace_asm_named(const char **src_p, char **dst_p, struct asm_arg *args, int nr)
+{
+	const char *src = *src_p;
+	const char *end = src;
+
+	for(;;) {
+		char c = *end++;
+		if (!c)
+			return;
+		if (c == ']') {
+			int i;
+
+			*src_p = end;
+			for (i = 0; i < nr; i++) {
+				const struct ident *ident = args[i].name;
+				int len;
+				if (!ident)
+					continue;
+				len = ident->len;
+				if (memcmp(src, ident->name, len))
+					continue;
+				replace_asm_arg(dst_p, args+i);
+				return;
+			}
+		}
+	}
+}
+
+static const char *replace_asm_args(const char *str, struct asm_arg *args, int nr)
+{
+	static char buffer[1000];
+	char *p = buffer;
+
+	for (;;) {
+		char c = *str;
+		*p = c;
+		if (!c)
+			return buffer;
+		str++;
+		switch (c) {
+		case '%':
+			if (*str == '%') {
+				str++;
+				p++;
+				continue;
+			}
+			replace_asm_percent(&str, &p, args, nr);
+			continue;
+		case '[':
+			replace_asm_named(&str, &p, args, nr);
+			continue;
+		default:
+			break;
+		}
+		p++;
+	}
+}
+
+#define MAX_ASM_ARG (50)
+static struct asm_arg asm_arguments[MAX_ASM_ARG];
+
+static struct asm_arg *generate_asm_inputs(struct bb_state *state, struct asm_constraint_list *list, struct asm_arg *arg)
+{
+	struct asm_constraint *entry;
+
+	FOR_EACH_PTR(list, entry) {
+		const char *constraint = entry->constraint;
+		pseudo_t pseudo = entry->pseudo;
+		struct hardreg *reg, *orig;
+		const char *string;
+		int index;
+
+		string = "undef";
+		switch (*constraint) {
+		case 'r':
+			string = getreg(state, pseudo, NULL)->name;
+			break;
+		case '0' ... '9':
+			index = *constraint - '0';
+			reg = asm_arguments[index].reg;
+			orig = find_in_reg(state, pseudo);
+			if (orig)
+				move_reg(state, orig, reg);
+			else
+				fill_reg(state, reg, pseudo);
+			string = reg->name;
+			break;
+		default:
+			string = generic(state, pseudo);
+			break;
+		}
+
+		output_insn(state, "# asm input \"%s\": %s : %s", constraint, show_pseudo(pseudo), string);
+
+		arg->name = entry->ident;
+		arg->value = string;
+		arg->pseudo = NULL;
+		arg->reg = NULL;
+		arg++;
+	} END_FOR_EACH_PTR(entry);
+	return arg;
+}
+
+static struct asm_arg *generate_asm_outputs(struct bb_state *state, struct asm_constraint_list *list, struct asm_arg *arg)
+{
+	struct asm_constraint *entry;
+
+	FOR_EACH_PTR(list, entry) {
+		const char *constraint = entry->constraint;
+		pseudo_t pseudo = entry->pseudo;
+		struct hardreg *reg;
+		const char *string;
+
+		while (*constraint == '=' || *constraint == '+')
+			constraint++;
+
+		string = "undef";
+		switch (*constraint) {
+		case 'r':
+		default:
+			reg = target_reg(state, pseudo, NULL);
+			arg->pseudo = pseudo;
+			arg->reg = reg;
+			string = reg->name;
+			break;
+		}
+
+		output_insn(state, "# asm output \"%s\": %s : %s", constraint, show_pseudo(pseudo), string);
+
+		arg->name = entry->ident;
+		arg->value = string;
+		arg++;
+	} END_FOR_EACH_PTR(entry);
+	return arg;
+}
+
+static void generate_asm(struct bb_state *state, struct instruction *insn)
+{
+	const char *str = insn->string;
+
+	if (insn->asm_rules->outputs || insn->asm_rules->inputs) {
+		struct asm_arg *arg;
+
+		arg = generate_asm_outputs(state, insn->asm_rules->outputs, asm_arguments);
+		arg = generate_asm_inputs(state, insn->asm_rules->inputs, arg);
+		str = replace_asm_args(str, asm_arguments, arg - asm_arguments);
+	}
+	output_insn(state, "%s", str);
+}
+
+static void generate_compare(struct bb_state *state, struct instruction *insn)
+{
+	struct hardreg *src;
+	const char *src2;
+	int opcode;
+
+	flush_cc_cache(state);
+	opcode = insn->opcode;
+
+	/*
+	 * We should try to switch these around if necessary,
+	 * and update the opcode to match..
+	 */
+	src = getreg(state, insn->src1, insn->target);
+	src2 = generic(state, insn->src2);
+
+	output_insn(state, "cmp.%d %s,%s", insn->size, src2, src->name);
+
+	add_cc_cache(state, opcode, insn->target);
+}
+
+static void generate_one_insn(struct instruction *insn, struct bb_state *state)
+{
+	if (verbose)
+		output_comment(state, "%s", show_instruction(insn));
+
+	switch (insn->opcode) {
+	case OP_ENTRY: {
+		struct symbol *sym = insn->bb->ep->name;
+		const char *name = show_ident(sym->ident);
+		if (sym->ctype.modifiers & MOD_STATIC)
+			printf("\n\n%s:\n", name);
+		else
+			printf("\n\n.globl %s\n%s:\n", name, name);
+		break;
+	}
+
+	/*
+	 * OP_SETVAL likewise doesn't actually generate any
+	 * code. On use, the "def" of the pseudo will be
+	 * looked up.
+	 */
+	case OP_SETVAL:
+		break;
+
+	case OP_STORE:
+		generate_store(insn, state);
+		break;
+
+	case OP_LOAD:
+		generate_load(insn, state);
+		break;
+
+	case OP_DEATHNOTE:
+		mark_pseudo_dead(state, insn->target);
+		return;
+
+	case OP_COPY:
+		generate_copy(state, insn);
+		break;
+
+	case OP_ADD: case OP_MULU: case OP_MULS:
+	case OP_AND: case OP_OR: case OP_XOR:
+	case OP_AND_BOOL: case OP_OR_BOOL:
+		generate_commutative_binop(state, insn);
+		break;
+
+	case OP_SUB: case OP_DIVU: case OP_DIVS:
+	case OP_MODU: case OP_MODS:
+	case OP_SHL: case OP_LSR: case OP_ASR:
+ 		generate_binop(state, insn);
+		break;
+
+	case OP_BINCMP ... OP_BINCMP_END:
+		generate_compare(state, insn);
+		break;
+
+	case OP_CAST: case OP_SCAST: case OP_FPCAST: case OP_PTRCAST:
+		generate_cast(state, insn);
+		break;
+
+	case OP_SEL:
+		generate_select(state, insn);
+		break;
+
+	case OP_BR:
+		generate_branch(state, insn);
+		break;
+
+	case OP_SWITCH:
+		generate_switch(state, insn);
+		break;
+
+	case OP_CALL:
+		generate_call(state, insn);
+		break;
+
+	case OP_RET:
+		generate_ret(state, insn);
+		break;
+
+	case OP_ASM:
+		generate_asm(state, insn);
+		break;
+
+	case OP_PHI:
+	case OP_PHISOURCE:
+	default:
+		output_insn(state, "unimplemented: %s", show_instruction(insn));
+		break;
+	}
+	kill_dead_pseudos(state);
+}
+
+#define VERY_BUSY 1000
+#define REG_FIXED 2000
+
+static void write_reg_to_storage(struct bb_state *state, struct hardreg *reg, pseudo_t pseudo, struct storage *storage)
+{
+	int i;
+	struct hardreg *out;
+
+	switch (storage->type) {
+	case REG_REG:
+		out = hardregs + storage->regno;
+		if (reg == out)
+			return;
+		output_insn(state, "movl %s,%s", reg->name, out->name);
+		return;
+	case REG_UDEF:
+		if (reg->busy < VERY_BUSY) {
+			storage->type = REG_REG;
+			storage->regno = reg - hardregs;
+			reg->busy = REG_FIXED;
+			return;
+		}
+
+		/* Try to find a non-busy register.. */
+		for (i = 0; i < REGNO; i++) {
+			out = hardregs + i;
+			if (out->contains)
+				continue;
+			output_insn(state, "movl %s,%s", reg->name, out->name);
+			storage->type = REG_REG;
+			storage->regno = i;
+			out->busy = REG_FIXED;
+			return;
+		}
+
+		/* Fall back on stack allocation ... */
+		alloc_stack(state, storage);
+		/* Fall through */
+	default:
+		output_insn(state, "movl %s,%s", reg->name, show_memop(storage));
+		return;
+	}
+}
+
+static void write_val_to_storage(struct bb_state *state, pseudo_t src, struct storage *storage)
+{
+	struct hardreg *out;
+
+	switch (storage->type) {
+	case REG_UDEF:
+		alloc_stack(state, storage);
+	default:
+		output_insn(state, "movl %s,%s", show_pseudo(src), show_memop(storage));
+		break;
+	case REG_REG:
+		out = hardregs + storage->regno;
+		output_insn(state, "movl %s,%s", show_pseudo(src), out->name);
+	}
+}
+
+static void fill_output(struct bb_state *state, pseudo_t pseudo, struct storage *out)
+{
+	int i;
+	struct storage_hash *in;
+	struct instruction *def;
+
+	/* Is that pseudo a constant value? */
+	switch (pseudo->type) {
+	case PSEUDO_VAL:
+		write_val_to_storage(state, pseudo, out);
+		return;
+	case PSEUDO_REG:
+		def = pseudo->def;
+		if (def && def->opcode == OP_SETVAL) {
+			write_val_to_storage(state, pseudo, out);
+			return;
+		}
+	default:
+		break;
+	}
+
+	/* See if we have that pseudo in a register.. */
+	for (i = 0; i < REGNO; i++) {
+		struct hardreg *reg = hardregs + i;
+		pseudo_t p;
+
+		FOR_EACH_PTR(reg->contains, p) {
+			if (p == pseudo) {
+				write_reg_to_storage(state, reg, pseudo, out);
+				return;
+			}
+		} END_FOR_EACH_PTR(p);
+	}
+
+	/* Do we have it in another storage? */
+	in = find_storage_hash(pseudo, state->internal);
+	if (!in) {
+		in = find_storage_hash(pseudo, state->inputs);
+		/* Undefined? */
+		if (!in)
+			return;
+	}
+	switch (out->type) {
+	case REG_UDEF:
+		*out = *in->storage;
+		break;
+	case REG_REG:
+		output_insn(state, "movl %s,%s", show_memop(in->storage), hardregs[out->regno].name);
+		break;
+	default:
+		if (out == in->storage)
+			break;
+		if ((out->type == in->storage->type) && (out->regno == in->storage->regno))
+			break;
+		output_insn(state, "movl %s,%s", show_memop(in->storage), show_memop(out));
+		break;
+	}
+	return;
+}
+
+static int final_pseudo_flush(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
+{
+	struct storage_hash *hash;
+	struct storage *out;
+	struct hardreg *dst;
+
+	/*
+	 * Since this pseudo is live at exit, we'd better have output 
+	 * storage for it..
+	 */
+	hash = find_storage_hash(pseudo, state->outputs);
+	if (!hash)
+		return 1;
+	out = hash->storage;
+
+	/* If the output is in a register, try to get it there.. */
+	if (out->type == REG_REG) {
+		dst = hardregs + out->regno;
+		/*
+		 * Two good cases: nobody is using the right register,
+		 * or we've already set it aside for output..
+		 */
+		if (!dst->contains || dst->busy > VERY_BUSY)
+			goto copy_to_dst;
+
+		/* Aiee. Try to keep it in a register.. */
+		dst = empty_reg(state);
+		if (dst)
+			goto copy_to_dst;
+
+		return 0;
+	}
+
+	/* If the output is undefined, let's see if we can put it in a register.. */
+	if (out->type == REG_UDEF) {
+		dst = empty_reg(state);
+		if (dst) {
+			out->type = REG_REG;
+			out->regno = dst - hardregs;
+			goto copy_to_dst;
+		}
+		/* Uhhuh. Not so good. No empty registers right now */
+		return 0;
+	}
+
+	/* If we know we need to flush it, just do so already .. */
+	output_insn(state, "movl %s,%s", reg->name, show_memop(out));
+	return 1;
+
+copy_to_dst:
+	if (reg == dst)
+		return 1;
+	output_insn(state, "movl %s,%s", reg->name, dst->name);
+	add_pseudo_reg(state, pseudo, dst);
+	return 1;
+}
+
+/*
+ * This tries to make sure that we put all the pseudos that are
+ * live on exit into the proper storage
+ */
+static void generate_output_storage(struct bb_state *state)
+{
+	struct storage_hash *entry;
+
+	/* Go through the fixed outputs, making sure we have those regs free */
+	FOR_EACH_PTR(state->outputs, entry) {
+		struct storage *out = entry->storage;
+		if (out->type == REG_REG) {
+			struct hardreg *reg = hardregs + out->regno;
+			pseudo_t p;
+			int flushme = 0;
+
+			reg->busy = REG_FIXED;
+			FOR_EACH_PTR(reg->contains, p) {
+				if (p == entry->pseudo) {
+					flushme = -100;
+					continue;
+				}
+				if (CURRENT_TAG(p) & TAG_DEAD)
+					continue;
+
+				/* Try to write back the pseudo to where it should go ... */
+				if (final_pseudo_flush(state, p, reg)) {
+					DELETE_CURRENT_PTR(p);
+					continue;
+				}
+				flushme++;
+			} END_FOR_EACH_PTR(p);
+			PACK_PTR_LIST(&reg->contains);
+			if (flushme > 0)
+				flush_reg(state, reg);
+		}
+	} END_FOR_EACH_PTR(entry);
+
+	FOR_EACH_PTR(state->outputs, entry) {
+		fill_output(state, entry->pseudo, entry->storage);
+	} END_FOR_EACH_PTR(entry);
+}
+
+static void generate(struct basic_block *bb, struct bb_state *state)
+{
+	int i;
+	struct storage_hash *entry;
+	struct instruction *insn;
+
+	for (i = 0; i < REGNO; i++) {
+		free_ptr_list(&hardregs[i].contains);
+		hardregs[i].busy = 0;
+		hardregs[i].dead = 0;
+		hardregs[i].used = 0;
+	}
+
+	FOR_EACH_PTR(state->inputs, entry) {
+		struct storage *storage = entry->storage;
+		const char *name = show_storage(storage);
+		output_comment(state, "incoming %s in %s", show_pseudo(entry->pseudo), name);
+		if (storage->type == REG_REG) {
+			int regno = storage->regno;
+			add_pseudo_reg(state, entry->pseudo, hardregs + regno);
+			name = hardregs[regno].name;
+		}
+	} END_FOR_EACH_PTR(entry);
+
+	output_label(state, ".L%p", bb);
+	FOR_EACH_PTR(bb->insns, insn) {
+		if (!insn->bb)
+			continue;
+		generate_one_insn(insn, state);
+	} END_FOR_EACH_PTR(insn);
+
+	if (verbose) {
+		output_comment(state, "--- in ---");
+		FOR_EACH_PTR(state->inputs, entry) {
+			output_comment(state, "%s <- %s", show_pseudo(entry->pseudo), show_storage(entry->storage));
+		} END_FOR_EACH_PTR(entry);
+		output_comment(state, "--- spill ---");
+		FOR_EACH_PTR(state->internal, entry) {
+			output_comment(state, "%s <-> %s", show_pseudo(entry->pseudo), show_storage(entry->storage));
+		} END_FOR_EACH_PTR(entry);
+		output_comment(state, "--- out ---");
+		FOR_EACH_PTR(state->outputs, entry) {
+			output_comment(state, "%s -> %s", show_pseudo(entry->pseudo), show_storage(entry->storage));
+		} END_FOR_EACH_PTR(entry);
+	}
+	printf("\n");
+}
+
+static void generate_list(struct basic_block_list *list, unsigned long generation)
+{
+	struct basic_block *bb;
+	FOR_EACH_PTR(list, bb) {
+		if (bb->generation == generation)
+			continue;
+		output_bb(bb, generation);
+	} END_FOR_EACH_PTR(bb);
+}
+
+/*
+ * Mark all the output registers of all the parents
+ * as being "used" - this does not mean that we cannot
+ * re-use them, but it means that we cannot ask the
+ * parents to pass in another pseudo in one of those
+ * registers that it already uses for another child.
+ */
+static void mark_used_registers(struct basic_block *bb, struct bb_state *state)
+{
+	struct basic_block *parent;
+
+	FOR_EACH_PTR(bb->parents, parent) {
+		struct storage_hash_list *outputs = gather_storage(parent, STOR_OUT);
+		struct storage_hash *entry;
+
+		FOR_EACH_PTR(outputs, entry) {
+			struct storage *s = entry->storage;
+			if (s->type == REG_REG) {
+				struct hardreg *reg = hardregs + s->regno;
+				reg->used = 1;
+			}
+		} END_FOR_EACH_PTR(entry);
+	} END_FOR_EACH_PTR(parent);
+}
+
+static void output_bb(struct basic_block *bb, unsigned long generation)
+{
+	struct bb_state state;
+
+	bb->generation = generation;
+
+	/* Make sure all parents have been generated first */
+	generate_list(bb->parents, generation);
+
+	state.pos = bb->pos;
+	state.inputs = gather_storage(bb, STOR_IN);
+	state.outputs = gather_storage(bb, STOR_OUT);
+	state.internal = NULL;
+	state.cc_opcode = 0;
+	state.cc_target = NULL;
+
+	/* Mark incoming registers used */
+	mark_used_registers(bb, &state);
+
+	generate(bb, &state);
+
+	free_ptr_list(&state.inputs);
+	free_ptr_list(&state.outputs);
+
+	/* Generate all children... */
+	generate_list(bb->children, generation);
+}
+
+/*
+ * We should set up argument sources here..
+ *
+ * Things like "first three arguments in registers" etc
+ * are all for this place.
+ *
+ * On x86, we default to stack, unless it's a static
+ * function that doesn't have its address taken.
+ *
+ * I should implement the -mregparm=X cmd line option.
+ */
+static void set_up_arch_entry(struct entrypoint *ep, struct instruction *entry)
+{
+	pseudo_t arg;
+	struct symbol *sym, *argtype;
+	int i, offset, regparm;
+
+	sym = ep->name;
+	regparm = 0;
+	if (!(sym->ctype.modifiers & MOD_ADDRESSABLE))
+		regparm = 3;
+	sym = sym->ctype.base_type;
+	i = 0;
+	offset = 0;
+	PREPARE_PTR_LIST(sym->arguments, argtype);
+	FOR_EACH_PTR(entry->arg_list, arg) {
+		struct storage *in = lookup_storage(entry->bb, arg, STOR_IN);
+		if (!in) {
+			in = alloc_storage();
+			add_storage(in, entry->bb, arg, STOR_IN);
+		}
+		if (i < regparm) {
+			in->type = REG_REG;
+			in->regno = i;
+		} else {
+			int bits = argtype ? argtype->bit_size : 0;
+
+			if (bits < bits_in_int)
+				bits = bits_in_int;
+
+			in->type = REG_FRAME;
+			in->offset = offset;
+			
+			offset += bits_to_bytes(bits);
+		}
+		i++;
+		NEXT_PTR_LIST(argtype);
+	} END_FOR_EACH_PTR(arg);
+	FINISH_PTR_LIST(argtype);
+}
+
+/*
+ * Set up storage information for "return"
+ *
+ * Not strictly necessary, since the code generator will
+ * certainly move the return value to the right register,
+ * but it can help register allocation if the allocator
+ * sees that the target register is going to return in %eax.
+ */
+static void set_up_arch_exit(struct basic_block *bb, struct instruction *ret)
+{
+	pseudo_t pseudo = ret->src;
+
+	if (pseudo && pseudo != VOID) {
+		struct storage *out = lookup_storage(bb, pseudo, STOR_OUT);
+		if (!out) {
+			out = alloc_storage();
+			add_storage(out, bb, pseudo, STOR_OUT);
+		}
+		out->type = REG_REG;
+		out->regno = 0;
+	}
+}
+
+/*
+ * Set up dummy/silly output storage information for a switch
+ * instruction. We need to make sure that a register is available
+ * when we generate code for switch, so force that by creating
+ * a dummy output rule.
+ */
+static void set_up_arch_switch(struct basic_block *bb, struct instruction *insn)
+{
+	pseudo_t pseudo = insn->cond;
+	struct storage *out = lookup_storage(bb, pseudo, STOR_OUT);
+	if (!out) {
+		out = alloc_storage();
+		add_storage(out, bb, pseudo, STOR_OUT);
+	}
+	out->type = REG_REG;
+	out->regno = SWITCH_REG;
+}
+
+static void arch_set_up_storage(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+
+	/* Argument storage etc.. */
+	set_up_arch_entry(ep, ep->entry);
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		struct instruction *insn = last_instruction(bb->insns);
+		if (!insn)
+			continue;
+		switch (insn->opcode) {
+		case OP_RET:
+			set_up_arch_exit(bb, insn);
+			break;
+		case OP_SWITCH:
+			set_up_arch_switch(bb, insn);
+			break;
+		default:
+			/* nothing */;
+		}
+	} END_FOR_EACH_PTR(bb);
+}
+
+static void output(struct entrypoint *ep)
+{
+	unsigned long generation = ++bb_generation;
+
+	last_reg = -1;
+	stack_offset = 0;
+
+	/* Get rid of SSA form (phinodes etc) */
+	unssa(ep);
+
+	/* Set up initial inter-bb storage links */
+	set_up_storage(ep);
+
+	/* Architecture-specific storage rules.. */
+	arch_set_up_storage(ep);
+
+	/* Show the results ... */
+	output_bb(ep->entry->bb, generation);
+
+	/* Clear the storage hashes for the next function.. */
+	free_storage();
+}
+
+static int compile(struct symbol_list *list)
+{
+	struct symbol *sym;
+	FOR_EACH_PTR(list, sym) {
+		struct entrypoint *ep;
+		expand_symbol(sym);
+		ep = linearize_symbol(sym);
+		if (ep)
+			output(ep);
+	} END_FOR_EACH_PTR(sym);
+	
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	struct string_list *filelist = NULL;
+	char *file;
+
+	compile(sparse_initialize(argc, argv, &filelist));
+	dbg_dead = 1;
+	FOR_EACH_PTR_NOTAG(filelist, file) {
+		compile(sparse(file));
+	} END_FOR_EACH_PTR_NOTAG(file);
+	return 0;
+}
+
diff --git a/deps/sparse/expand.c b/deps/sparse/expand.c
new file mode 100644
index 0000000..63a9075
--- /dev/null
+++ b/deps/sparse/expand.c
@@ -0,0 +1,1247 @@
+/*
+ * sparse/expand.c
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ *
+ * expand constant expressions.
+ */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "parse.h"
+#include "token.h"
+#include "symbol.h"
+#include "target.h"
+#include "expression.h"
+
+/* Random cost numbers */
+#define SIDE_EFFECTS 10000	/* The expression has side effects */
+#define UNSAFE 100		/* The expression may be "infinitely costly" due to exceptions */
+#define SELECT_COST 20		/* Cut-off for turning a conditional into a select */
+#define BRANCH_COST 10		/* Cost of a conditional branch */
+
+static int expand_expression(struct expression *);
+static int expand_statement(struct statement *);
+static int conservative;
+
+static int expand_symbol_expression(struct expression *expr)
+{
+	struct symbol *sym = expr->symbol;
+
+	if (sym == &zero_int) {
+		if (Wundef)
+			warning(expr->pos, "undefined preprocessor identifier '%s'", show_ident(expr->symbol_name));
+		expr->type = EXPR_VALUE;
+		expr->value = 0;
+		expr->taint = 0;
+		return 0;
+	}
+	/* The cost of a symbol expression is lower for on-stack symbols */
+	return (sym->ctype.modifiers & (MOD_STATIC | MOD_EXTERN)) ? 2 : 1;
+}
+
+static long long get_longlong(struct expression *expr)
+{
+	int no_expand = expr->ctype->ctype.modifiers & MOD_UNSIGNED;
+	long long mask = 1ULL << (expr->ctype->bit_size - 1);
+	long long value = expr->value;
+	long long ormask, andmask;
+
+	if (!(value & mask))
+		no_expand = 1;
+	andmask = mask | (mask-1);
+	ormask = ~andmask;
+	if (no_expand)
+		ormask = 0;
+	return (value & andmask) | ormask;
+}
+
+void cast_value(struct expression *expr, struct symbol *newtype,
+		struct expression *old, struct symbol *oldtype)
+{
+	int old_size = oldtype->bit_size;
+	int new_size = newtype->bit_size;
+	long long value, mask, signmask;
+	long long oldmask, oldsignmask, dropped;
+
+	if (newtype->ctype.base_type == &fp_type ||
+	    oldtype->ctype.base_type == &fp_type)
+		goto Float;
+
+	// For pointers and integers, we can just move the value around
+	expr->type = EXPR_VALUE;
+	expr->taint = old->taint;
+	if (old_size == new_size) {
+		expr->value = old->value;
+		return;
+	}
+
+	// expand it to the full "long long" value
+	value = get_longlong(old);
+
+Int:
+	// Truncate it to the new size
+	signmask = 1ULL << (new_size-1);
+	mask = signmask | (signmask-1);
+	expr->value = value & mask;
+
+	// Stop here unless checking for truncation
+	if (!Wcast_truncate || conservative)
+		return;
+	
+	// Check if we dropped any bits..
+	oldsignmask = 1ULL << (old_size-1);
+	oldmask = oldsignmask | (oldsignmask-1);
+	dropped = oldmask & ~mask;
+
+	// OK if the bits were (and still are) purely sign bits
+	if (value & dropped) {
+		if (!(value & oldsignmask) || !(value & signmask) || (value & dropped) != dropped)
+			warning(old->pos, "cast truncates bits from constant value (%llx becomes %llx)",
+				value & oldmask,
+				value & mask);
+	}
+	return;
+
+Float:
+	if (!is_float_type(newtype)) {
+		value = (long long)old->fvalue;
+		expr->type = EXPR_VALUE;
+		expr->taint = 0;
+		goto Int;
+	}
+
+	if (!is_float_type(oldtype))
+		expr->fvalue = (long double)get_longlong(old);
+	else
+		expr->fvalue = old->fvalue;
+
+	if (!(newtype->ctype.modifiers & MOD_LONGLONG) && \
+	    !(newtype->ctype.modifiers & MOD_LONGLONGLONG)) {
+		if ((newtype->ctype.modifiers & MOD_LONG))
+			expr->fvalue = (double)expr->fvalue;
+		else
+			expr->fvalue = (float)expr->fvalue;
+	}
+	expr->type = EXPR_FVALUE;
+}
+
+static int check_shift_count(struct expression *expr, struct symbol *ctype, unsigned int count)
+{
+	warning(expr->pos, "shift too big (%u) for type %s", count, show_typename(ctype));
+	count &= ctype->bit_size-1;
+	return count;
+}
+
+/*
+ * CAREFUL! We need to get the size and sign of the
+ * result right!
+ */
+#define CONVERT(op,s)	(((op)<<1)+(s))
+#define SIGNED(op)	CONVERT(op, 1)
+#define UNSIGNED(op)	CONVERT(op, 0)
+static int simplify_int_binop(struct expression *expr, struct symbol *ctype)
+{
+	struct expression *left = expr->left, *right = expr->right;
+	unsigned long long v, l, r, mask;
+	signed long long sl, sr;
+	int is_signed;
+
+	if (right->type != EXPR_VALUE)
+		return 0;
+	r = right->value;
+	if (expr->op == SPECIAL_LEFTSHIFT || expr->op == SPECIAL_RIGHTSHIFT) {
+		if (r >= ctype->bit_size) {
+			if (conservative)
+				return 0;
+			r = check_shift_count(expr, ctype, r);
+			right->value = r;
+		}
+	}
+	if (left->type != EXPR_VALUE)
+		return 0;
+	l = left->value; r = right->value;
+	is_signed = !(ctype->ctype.modifiers & MOD_UNSIGNED);
+	mask = 1ULL << (ctype->bit_size-1);
+	sl = l; sr = r;
+	if (is_signed && (sl & mask))
+		sl |= ~(mask-1);
+	if (is_signed && (sr & mask))
+		sr |= ~(mask-1);
+	
+	switch (CONVERT(expr->op,is_signed)) {
+	case SIGNED('+'):
+	case UNSIGNED('+'):
+		v = l + r;
+		break;
+
+	case SIGNED('-'):
+	case UNSIGNED('-'):
+		v = l - r;
+		break;
+
+	case SIGNED('&'):
+	case UNSIGNED('&'):
+		v = l & r;
+		break;
+
+	case SIGNED('|'):
+	case UNSIGNED('|'):
+		v = l | r;
+		break;
+
+	case SIGNED('^'):
+	case UNSIGNED('^'):
+		v = l ^ r;
+		break;
+
+	case SIGNED('*'):
+		v = sl * sr;
+		break;
+
+	case UNSIGNED('*'):
+		v = l * r;
+		break;
+
+	case SIGNED('/'):
+		if (!r)
+			goto Div;
+		if (l == mask && sr == -1)
+			goto Overflow;
+		v = sl / sr;
+		break;
+
+	case UNSIGNED('/'):
+		if (!r) goto Div;
+		v = l / r; 
+		break;
+
+	case SIGNED('%'):
+		if (!r)
+			goto Div;
+		v = sl % sr;
+		break;
+
+	case UNSIGNED('%'):
+		if (!r) goto Div;
+		v = l % r;
+		break;
+
+	case SIGNED(SPECIAL_LEFTSHIFT):
+	case UNSIGNED(SPECIAL_LEFTSHIFT):
+		v = l << r;
+		break; 
+
+	case SIGNED(SPECIAL_RIGHTSHIFT):
+		v = sl >> r;
+		break;
+
+	case UNSIGNED(SPECIAL_RIGHTSHIFT):
+		v = l >> r;
+		break;
+
+	default:
+		return 0;
+	}
+	mask = mask | (mask-1);
+	expr->value = v & mask;
+	expr->type = EXPR_VALUE;
+	expr->taint = left->taint | right->taint;
+	return 1;
+Div:
+	if (!conservative)
+		warning(expr->pos, "division by zero");
+	return 0;
+Overflow:
+	if (!conservative)
+		warning(expr->pos, "constant integer operation overflow");
+	return 0;
+}
+
+static int simplify_cmp_binop(struct expression *expr, struct symbol *ctype)
+{
+	struct expression *left = expr->left, *right = expr->right;
+	unsigned long long l, r, mask;
+	signed long long sl, sr;
+
+	if (left->type != EXPR_VALUE || right->type != EXPR_VALUE)
+		return 0;
+	l = left->value; r = right->value;
+	mask = 1ULL << (ctype->bit_size-1);
+	sl = l; sr = r;
+	if (sl & mask)
+		sl |= ~(mask-1);
+	if (sr & mask)
+		sr |= ~(mask-1);
+	switch (expr->op) {
+	case '<':		expr->value = sl < sr; break;
+	case '>':		expr->value = sl > sr; break;
+	case SPECIAL_LTE:	expr->value = sl <= sr; break;
+	case SPECIAL_GTE:	expr->value = sl >= sr; break;
+	case SPECIAL_EQUAL:	expr->value = l == r; break;
+	case SPECIAL_NOTEQUAL:	expr->value = l != r; break;
+	case SPECIAL_UNSIGNED_LT:expr->value = l < r; break;
+	case SPECIAL_UNSIGNED_GT:expr->value = l > r; break;
+	case SPECIAL_UNSIGNED_LTE:expr->value = l <= r; break;
+	case SPECIAL_UNSIGNED_GTE:expr->value = l >= r; break;
+	}
+	expr->type = EXPR_VALUE;
+	expr->taint = left->taint | right->taint;
+	return 1;
+}
+
+static int simplify_float_binop(struct expression *expr)
+{
+	struct expression *left = expr->left, *right = expr->right;
+	unsigned long mod = expr->ctype->ctype.modifiers;
+	long double l, r, res;
+
+	if (left->type != EXPR_FVALUE || right->type != EXPR_FVALUE)
+		return 0;
+
+	l = left->fvalue;
+	r = right->fvalue;
+
+	if (mod & MOD_LONGLONG) {
+		switch (expr->op) {
+		case '+':	res = l + r; break;
+		case '-':	res = l - r; break;
+		case '*':	res = l * r; break;
+		case '/':	if (!r) goto Div;
+				res = l / r; break;
+		default: return 0;
+		}
+	} else if (mod & MOD_LONG) {
+		switch (expr->op) {
+		case '+':	res = (double) l + (double) r; break;
+		case '-':	res = (double) l - (double) r; break;
+		case '*':	res = (double) l * (double) r; break;
+		case '/':	if (!r) goto Div;
+				res = (double) l / (double) r; break;
+		default: return 0;
+		}
+	} else {
+		switch (expr->op) {
+		case '+':	res = (float)l + (float)r; break;
+		case '-':	res = (float)l - (float)r; break;
+		case '*':	res = (float)l * (float)r; break;
+		case '/':	if (!r) goto Div;
+				res = (float)l / (float)r; break;
+		default: return 0;
+		}
+	}
+	expr->type = EXPR_FVALUE;
+	expr->fvalue = res;
+	return 1;
+Div:
+	if (!conservative)
+		warning(expr->pos, "division by zero");
+	return 0;
+}
+
+static int simplify_float_cmp(struct expression *expr, struct symbol *ctype)
+{
+	struct expression *left = expr->left, *right = expr->right;
+	long double l, r;
+
+	if (left->type != EXPR_FVALUE || right->type != EXPR_FVALUE)
+		return 0;
+
+	l = left->fvalue;
+	r = right->fvalue;
+	switch (expr->op) {
+	case '<':		expr->value = l < r; break;
+	case '>':		expr->value = l > r; break;
+	case SPECIAL_LTE:	expr->value = l <= r; break;
+	case SPECIAL_GTE:	expr->value = l >= r; break;
+	case SPECIAL_EQUAL:	expr->value = l == r; break;
+	case SPECIAL_NOTEQUAL:	expr->value = l != r; break;
+	}
+	expr->type = EXPR_VALUE;
+	expr->taint = 0;
+	return 1;
+}
+
+static int expand_binop(struct expression *expr)
+{
+	int cost;
+
+	cost = expand_expression(expr->left);
+	cost += expand_expression(expr->right);
+	if (simplify_int_binop(expr, expr->ctype))
+		return 0;
+	if (simplify_float_binop(expr))
+		return 0;
+	return cost + 1;
+}
+
+static int expand_logical(struct expression *expr)
+{
+	struct expression *left = expr->left;
+	struct expression *right;
+	int cost, rcost;
+
+	/* Do immediate short-circuiting ... */
+	cost = expand_expression(left);
+	if (left->type == EXPR_VALUE) {
+		if (expr->op == SPECIAL_LOGICAL_AND) {
+			if (!left->value) {
+				expr->type = EXPR_VALUE;
+				expr->value = 0;
+				expr->taint = left->taint;
+				return 0;
+			}
+		} else {
+			if (left->value) {
+				expr->type = EXPR_VALUE;
+				expr->value = 1;
+				expr->taint = left->taint;
+				return 0;
+			}
+		}
+	}
+
+	right = expr->right;
+	rcost = expand_expression(right);
+	if (left->type == EXPR_VALUE && right->type == EXPR_VALUE) {
+		/*
+		 * We know the left value doesn't matter, since
+		 * otherwise we would have short-circuited it..
+		 */
+		expr->type = EXPR_VALUE;
+		expr->value = right->value != 0;
+		expr->taint = left->taint | right->taint;
+		return 0;
+	}
+
+	/*
+	 * If the right side is safe and cheaper than a branch,
+	 * just avoid the branch and turn it into a regular binop
+	 * style SAFELOGICAL.
+	 */
+	if (rcost < BRANCH_COST) {
+		expr->type = EXPR_BINOP;
+		rcost -= BRANCH_COST - 1;
+	}
+
+	return cost + BRANCH_COST + rcost;
+}
+
+static int expand_comma(struct expression *expr)
+{
+	int cost;
+
+	cost = expand_expression(expr->left);
+	cost += expand_expression(expr->right);
+	if (expr->left->type == EXPR_VALUE || expr->left->type == EXPR_FVALUE) {
+		unsigned flags = expr->flags;
+		unsigned taint;
+		taint = expr->left->type == EXPR_VALUE ? expr->left->taint : 0;
+		*expr = *expr->right;
+		expr->flags = flags;
+		if (expr->type == EXPR_VALUE)
+			expr->taint |= Taint_comma | taint;
+	}
+	return cost;
+}
+
+#define MOD_IGN (MOD_VOLATILE | MOD_CONST)
+
+static int compare_types(int op, struct symbol *left, struct symbol *right)
+{
+	struct ctype c1 = {.base_type = left};
+	struct ctype c2 = {.base_type = right};
+	switch (op) {
+	case SPECIAL_EQUAL:
+		return !type_difference(&c1, &c2, MOD_IGN, MOD_IGN);
+	case SPECIAL_NOTEQUAL:
+		return type_difference(&c1, &c2, MOD_IGN, MOD_IGN) != NULL;
+	case '<':
+		return left->bit_size < right->bit_size;
+	case '>':
+		return left->bit_size > right->bit_size;
+	case SPECIAL_LTE:
+		return left->bit_size <= right->bit_size;
+	case SPECIAL_GTE:
+		return left->bit_size >= right->bit_size;
+	}
+	return 0;
+}
+
+static int expand_compare(struct expression *expr)
+{
+	struct expression *left = expr->left, *right = expr->right;
+	int cost;
+
+	cost = expand_expression(left);
+	cost += expand_expression(right);
+
+	if (left && right) {
+		/* Type comparison? */
+		if (left->type == EXPR_TYPE && right->type == EXPR_TYPE) {
+			int op = expr->op;
+			expr->type = EXPR_VALUE;
+			expr->value = compare_types(op, left->symbol, right->symbol);
+			expr->taint = 0;
+			return 0;
+		}
+		if (simplify_cmp_binop(expr, left->ctype))
+			return 0;
+		if (simplify_float_cmp(expr, left->ctype))
+			return 0;
+	}
+	return cost + 1;
+}
+
+static int expand_conditional(struct expression *expr)
+{
+	struct expression *cond = expr->conditional;
+	struct expression *true = expr->cond_true;
+	struct expression *false = expr->cond_false;
+	int cost, cond_cost;
+
+	cond_cost = expand_expression(cond);
+	if (cond->type == EXPR_VALUE) {
+		unsigned flags = expr->flags;
+		if (!cond->value)
+			true = false;
+		if (!true)
+			true = cond;
+		cost = expand_expression(true);
+		*expr = *true;
+		expr->flags = flags;
+		if (expr->type == EXPR_VALUE)
+			expr->taint |= cond->taint;
+		return cost;
+	}
+
+	cost = expand_expression(true);
+	cost += expand_expression(false);
+
+	if (cost < SELECT_COST) {
+		expr->type = EXPR_SELECT;
+		cost -= BRANCH_COST - 1;
+	}
+
+	return cost + cond_cost + BRANCH_COST;
+}
+		
+static int expand_assignment(struct expression *expr)
+{
+	expand_expression(expr->left);
+	expand_expression(expr->right);
+	return SIDE_EFFECTS;
+}
+
+static int expand_addressof(struct expression *expr)
+{
+	return expand_expression(expr->unop);
+}
+
+/*
+ * Look up a trustable initializer value at the requested offset.
+ *
+ * Return NULL if no such value can be found or statically trusted.
+ *
+ * FIXME!! We should check that the size is right!
+ */
+static struct expression *constant_symbol_value(struct symbol *sym, int offset)
+{
+	struct expression *value;
+
+	if (sym->ctype.modifiers & (MOD_ASSIGNED | MOD_ADDRESSABLE))
+		return NULL;
+	value = sym->initializer;
+	if (!value)
+		return NULL;
+	if (value->type == EXPR_INITIALIZER) {
+		struct expression *entry;
+		FOR_EACH_PTR(value->expr_list, entry) {
+			if (entry->type != EXPR_POS) {
+				if (offset)
+					continue;
+				return entry;
+			}
+			if (entry->init_offset < offset)
+				continue;
+			if (entry->init_offset > offset)
+				return NULL;
+			return entry->init_expr;
+		} END_FOR_EACH_PTR(entry);
+		return NULL;
+	}
+	return value;
+}
+
+static int expand_dereference(struct expression *expr)
+{
+	struct expression *unop = expr->unop;
+	unsigned int offset;
+
+	expand_expression(unop);
+
+	/*
+	 * NOTE! We get a bogus warning right now for some special
+	 * cases: apparently I've screwed up the optimization of
+	 * a zero-offset dereference, and the ctype is wrong.
+	 *
+	 * Leave the warning in anyway, since this is also a good
+	 * test for me to get the type evaluation right..
+	 */
+	if (expr->ctype->ctype.modifiers & MOD_NODEREF)
+		warning(unop->pos, "dereference of noderef expression");
+
+	/*
+	 * Is it "symbol" or "symbol + offset"?
+	 */
+	offset = 0;
+	if (unop->type == EXPR_BINOP && unop->op == '+') {
+		struct expression *right = unop->right;
+		if (right->type == EXPR_VALUE) {
+			offset = right->value;
+			unop = unop->left;
+		}
+	}
+
+	if (unop->type == EXPR_SYMBOL) {
+		struct symbol *sym = unop->symbol;
+		struct expression *value = constant_symbol_value(sym, offset);
+
+		/* Const symbol with a constant initializer? */
+		if (value) {
+			/* FIXME! We should check that the size is right! */
+			if (value->type == EXPR_VALUE) {
+				expr->type = EXPR_VALUE;
+				expr->value = value->value;
+				expr->taint = 0;
+				return 0;
+			} else if (value->type == EXPR_FVALUE) {
+				expr->type = EXPR_FVALUE;
+				expr->fvalue = value->fvalue;
+				return 0;
+			}
+		}
+
+		/* Direct symbol dereference? Cheap and safe */
+		return (sym->ctype.modifiers & (MOD_STATIC | MOD_EXTERN)) ? 2 : 1;
+	}
+
+	return UNSAFE;
+}
+
+static int simplify_preop(struct expression *expr)
+{
+	struct expression *op = expr->unop;
+	unsigned long long v, mask;
+
+	if (op->type != EXPR_VALUE)
+		return 0;
+
+	mask = 1ULL << (expr->ctype->bit_size-1);
+	v = op->value;
+	switch (expr->op) {
+	case '+': break;
+	case '-':
+		if (v == mask && !(expr->ctype->ctype.modifiers & MOD_UNSIGNED))
+			goto Overflow;
+		v = -v;
+		break;
+	case '!': v = !v; break;
+	case '~': v = ~v; break;
+	default: return 0;
+	}
+	mask = mask | (mask-1);
+	expr->value = v & mask;
+	expr->type = EXPR_VALUE;
+	expr->taint = op->taint;
+	return 1;
+
+Overflow:
+	if (!conservative)
+		warning(expr->pos, "constant integer operation overflow");
+	return 0;
+}
+
+static int simplify_float_preop(struct expression *expr)
+{
+	struct expression *op = expr->unop;
+	long double v;
+
+	if (op->type != EXPR_FVALUE)
+		return 0;
+	v = op->fvalue;
+	switch (expr->op) {
+	case '+': break;
+	case '-': v = -v; break;
+	default: return 0;
+	}
+	expr->fvalue = v;
+	expr->type = EXPR_FVALUE;
+	return 1;
+}
+
+/*
+ * Unary post-ops: x++ and x--
+ */
+static int expand_postop(struct expression *expr)
+{
+	expand_expression(expr->unop);
+	return SIDE_EFFECTS;
+}
+
+static int expand_preop(struct expression *expr)
+{
+	int cost;
+
+	switch (expr->op) {
+	case '*':
+		return expand_dereference(expr);
+
+	case '&':
+		return expand_addressof(expr);
+
+	case SPECIAL_INCREMENT:
+	case SPECIAL_DECREMENT:
+		/*
+		 * From a type evaluation standpoint the preops are
+		 * the same as the postops
+		 */
+		return expand_postop(expr);
+
+	default:
+		break;
+	}
+	cost = expand_expression(expr->unop);
+
+	if (simplify_preop(expr))
+		return 0;
+	if (simplify_float_preop(expr))
+		return 0;
+	return cost + 1;
+}
+
+static int expand_arguments(struct expression_list *head)
+{
+	int cost = 0;
+	struct expression *expr;
+
+	FOR_EACH_PTR (head, expr) {
+		cost += expand_expression(expr);
+	} END_FOR_EACH_PTR(expr);
+	return cost;
+}
+
+static int expand_cast(struct expression *expr)
+{
+	int cost;
+	struct expression *target = expr->cast_expression;
+
+	cost = expand_expression(target);
+
+	/* Simplify normal integer casts.. */
+	if (target->type == EXPR_VALUE || target->type == EXPR_FVALUE) {
+		cast_value(expr, expr->ctype, target, target->ctype);
+		return 0;
+	}
+	return cost + 1;
+}
+
+/* The arguments are constant if the cost of all of them is zero */
+int expand_constant_p(struct expression *expr, int cost)
+{
+	expr->type = EXPR_VALUE;
+	expr->value = !cost;
+	expr->taint = 0;
+	return 0;
+}
+
+/* The arguments are safe, if their cost is less than SIDE_EFFECTS */
+int expand_safe_p(struct expression *expr, int cost)
+{
+	expr->type = EXPR_VALUE;
+	expr->value = (cost < SIDE_EFFECTS);
+	expr->taint = 0;
+	return 0;
+}
+
+/*
+ * expand a call expression with a symbol. This
+ * should expand builtins.
+ */
+static int expand_symbol_call(struct expression *expr, int cost)
+{
+	struct expression *fn = expr->fn;
+	struct symbol *ctype = fn->ctype;
+
+	if (fn->type != EXPR_PREOP)
+		return SIDE_EFFECTS;
+
+	if (ctype->op && ctype->op->expand)
+		return ctype->op->expand(expr, cost);
+
+	if (ctype->ctype.modifiers & MOD_PURE)
+		return 0;
+
+	return SIDE_EFFECTS;
+}
+
+static int expand_call(struct expression *expr)
+{
+	int cost;
+	struct symbol *sym;
+	struct expression *fn = expr->fn;
+
+	cost = expand_arguments(expr->args);
+	sym = fn->ctype;
+	if (!sym) {
+		expression_error(expr, "function has no type");
+		return SIDE_EFFECTS;
+	}
+	if (sym->type == SYM_NODE)
+		return expand_symbol_call(expr, cost);
+
+	return SIDE_EFFECTS;
+}
+
+static int expand_expression_list(struct expression_list *list)
+{
+	int cost = 0;
+	struct expression *expr;
+
+	FOR_EACH_PTR(list, expr) {
+		cost += expand_expression(expr);
+	} END_FOR_EACH_PTR(expr);
+	return cost;
+}
+
+/* 
+ * We can simplify nested position expressions if
+ * this is a simple (single) positional expression.
+ */
+static int expand_pos_expression(struct expression *expr)
+{
+	struct expression *nested = expr->init_expr;
+	unsigned long offset = expr->init_offset;
+	int nr = expr->init_nr;
+
+	if (nr == 1) {
+		switch (nested->type) {
+		case EXPR_POS:
+			offset += nested->init_offset;
+			*expr = *nested;
+			expr->init_offset = offset;
+			nested = expr;
+			break;
+
+		case EXPR_INITIALIZER: {
+			struct expression *reuse = nested, *entry;
+			*expr = *nested;
+			FOR_EACH_PTR(expr->expr_list, entry) {
+				if (entry->type == EXPR_POS) {
+					entry->init_offset += offset;
+				} else {
+					if (!reuse) {
+						/*
+						 * This happens rarely, but it can happen
+						 * with bitfields that are all at offset
+						 * zero..
+						 */
+						reuse = alloc_expression(entry->pos, EXPR_POS);
+					}
+					reuse->type = EXPR_POS;
+					reuse->ctype = entry->ctype;
+					reuse->init_offset = offset;
+					reuse->init_nr = 1;
+					reuse->init_expr = entry;
+					REPLACE_CURRENT_PTR(entry, reuse);
+					reuse = NULL;
+				}
+			} END_FOR_EACH_PTR(entry);
+			nested = expr;
+			break;
+		}
+
+		default:
+			break;
+		}
+	}
+	return expand_expression(nested);
+}
+
+static unsigned long bit_offset(const struct expression *expr)
+{
+	unsigned long offset = 0;
+	while (expr->type == EXPR_POS) {
+		offset += bytes_to_bits(expr->init_offset);
+		expr = expr->init_expr;
+	}
+	if (expr && expr->ctype)
+		offset += expr->ctype->bit_offset;
+	return offset;
+}
+
+static int compare_expressions(const void *_a, const void *_b)
+{
+	const struct expression *a = _a;
+	const struct expression *b = _b;
+	unsigned long a_pos = bit_offset(a);
+	unsigned long b_pos = bit_offset(b);
+
+	return (a_pos < b_pos) ? -1 : (a_pos == b_pos) ? 0 : 1;
+}
+
+static void sort_expression_list(struct expression_list **list)
+{
+	sort_list((struct ptr_list **)list, compare_expressions);
+}
+
+static void verify_nonoverlapping(struct expression_list **list)
+{
+	struct expression *a = NULL;
+	struct expression *b;
+
+	FOR_EACH_PTR(*list, b) {
+		if (!b->ctype || !b->ctype->bit_size)
+			continue;
+		if (a && bit_offset(a) == bit_offset(b)) {
+			warning(a->pos, "Initializer entry defined twice");
+			info(b->pos, "  also defined here");
+			return;
+		}
+		a = b;
+	} END_FOR_EACH_PTR(b);
+}
+
+static int expand_expression(struct expression *expr)
+{
+	if (!expr)
+		return 0;
+	if (!expr->ctype || expr->ctype == &bad_ctype)
+		return UNSAFE;
+
+	switch (expr->type) {
+	case EXPR_VALUE:
+	case EXPR_FVALUE:
+	case EXPR_STRING:
+		return 0;
+	case EXPR_TYPE:
+	case EXPR_SYMBOL:
+		return expand_symbol_expression(expr);
+	case EXPR_BINOP:
+		return expand_binop(expr);
+
+	case EXPR_LOGICAL:
+		return expand_logical(expr);
+
+	case EXPR_COMMA:
+		return expand_comma(expr);
+
+	case EXPR_COMPARE:
+		return expand_compare(expr);
+
+	case EXPR_ASSIGNMENT:
+		return expand_assignment(expr);
+
+	case EXPR_PREOP:
+		return expand_preop(expr);
+
+	case EXPR_POSTOP:
+		return expand_postop(expr);
+
+	case EXPR_CAST:
+	case EXPR_FORCE_CAST:
+	case EXPR_IMPLIED_CAST:
+		return expand_cast(expr);
+
+	case EXPR_CALL:
+		return expand_call(expr);
+
+	case EXPR_DEREF:
+		warning(expr->pos, "we should not have an EXPR_DEREF left at expansion time");
+		return UNSAFE;
+
+	case EXPR_SELECT:
+	case EXPR_CONDITIONAL:
+		return expand_conditional(expr);
+
+	case EXPR_STATEMENT: {
+		struct statement *stmt = expr->statement;
+		int cost = expand_statement(stmt);
+
+		if (stmt->type == STMT_EXPRESSION && stmt->expression)
+			*expr = *stmt->expression;
+		return cost;
+	}
+
+	case EXPR_LABEL:
+		return 0;
+
+	case EXPR_INITIALIZER:
+		sort_expression_list(&expr->expr_list);
+		verify_nonoverlapping(&expr->expr_list);
+		return expand_expression_list(expr->expr_list);
+
+	case EXPR_IDENTIFIER:
+		return UNSAFE;
+
+	case EXPR_INDEX:
+		return UNSAFE;
+
+	case EXPR_SLICE:
+		return expand_expression(expr->base) + 1;
+
+	case EXPR_POS:
+		return expand_pos_expression(expr);
+
+	case EXPR_SIZEOF:
+	case EXPR_PTRSIZEOF:
+	case EXPR_ALIGNOF:
+	case EXPR_OFFSETOF:
+		expression_error(expr, "internal front-end error: sizeof in expansion?");
+		return UNSAFE;
+	}
+	return SIDE_EFFECTS;
+}
+
+static void expand_const_expression(struct expression *expr, const char *where)
+{
+	if (expr) {
+		expand_expression(expr);
+		if (expr->type != EXPR_VALUE)
+			expression_error(expr, "Expected constant expression in %s", where);
+	}
+}
+
+int expand_symbol(struct symbol *sym)
+{
+	int retval;
+	struct symbol *base_type;
+
+	if (!sym)
+		return 0;
+	base_type = sym->ctype.base_type;
+	if (!base_type)
+		return 0;
+
+	retval = expand_expression(sym->initializer);
+	/* expand the body of the symbol */
+	if (base_type->type == SYM_FN) {
+		if (base_type->stmt)
+			expand_statement(base_type->stmt);
+	}
+	return retval;
+}
+
+static void expand_return_expression(struct statement *stmt)
+{
+	expand_expression(stmt->expression);
+}
+
+static int expand_if_statement(struct statement *stmt)
+{
+	struct expression *expr = stmt->if_conditional;
+
+	if (!expr || !expr->ctype || expr->ctype == &bad_ctype)
+		return UNSAFE;
+
+	expand_expression(expr);
+
+/* This is only valid if nobody jumps into the "dead" side */
+#if 0
+	/* Simplify constant conditionals without even evaluating the false side */
+	if (expr->type == EXPR_VALUE) {
+		struct statement *simple;
+		simple = expr->value ? stmt->if_true : stmt->if_false;
+
+		/* Nothing? */
+		if (!simple) {
+			stmt->type = STMT_NONE;
+			return 0;
+		}
+		expand_statement(simple);
+		*stmt = *simple;
+		return SIDE_EFFECTS;
+	}
+#endif
+	expand_statement(stmt->if_true);
+	expand_statement(stmt->if_false);
+	return SIDE_EFFECTS;
+}
+
+/*
+ * Expanding a compound statement is really just
+ * about adding up the costs of each individual
+ * statement.
+ *
+ * We also collapse a simple compound statement:
+ * this would trigger for simple inline functions,
+ * except we would have to check the "return"
+ * symbol usage. Next time.
+ */
+static int expand_compound(struct statement *stmt)
+{
+	struct statement *s, *last;
+	int cost, statements;
+
+	if (stmt->ret)
+		expand_symbol(stmt->ret);
+
+	last = stmt->args;
+	cost = expand_statement(last);
+	statements = last != NULL;
+	FOR_EACH_PTR(stmt->stmts, s) {
+		statements++;
+		last = s;
+		cost += expand_statement(s);
+	} END_FOR_EACH_PTR(s);
+
+	if (statements == 1 && !stmt->ret)
+		*stmt = *last;
+
+	return cost;
+}
+
+static int expand_statement(struct statement *stmt)
+{
+	if (!stmt)
+		return 0;
+
+	switch (stmt->type) {
+	case STMT_DECLARATION: {
+		struct symbol *sym;
+		FOR_EACH_PTR(stmt->declaration, sym) {
+			expand_symbol(sym);
+		} END_FOR_EACH_PTR(sym);
+		return SIDE_EFFECTS;
+	}
+
+	case STMT_RETURN:
+		expand_return_expression(stmt);
+		return SIDE_EFFECTS;
+
+	case STMT_EXPRESSION:
+		return expand_expression(stmt->expression);
+
+	case STMT_COMPOUND:
+		return expand_compound(stmt);
+
+	case STMT_IF:
+		return expand_if_statement(stmt);
+
+	case STMT_ITERATOR:
+		expand_expression(stmt->iterator_pre_condition);
+		expand_expression(stmt->iterator_post_condition);
+		expand_statement(stmt->iterator_pre_statement);
+		expand_statement(stmt->iterator_statement);
+		expand_statement(stmt->iterator_post_statement);
+		return SIDE_EFFECTS;
+
+	case STMT_SWITCH:
+		expand_expression(stmt->switch_expression);
+		expand_statement(stmt->switch_statement);
+		return SIDE_EFFECTS;
+
+	case STMT_CASE:
+		expand_const_expression(stmt->case_expression, "case statement");
+		expand_const_expression(stmt->case_to, "case statement");
+		expand_statement(stmt->case_statement);
+		return SIDE_EFFECTS;
+
+	case STMT_LABEL:
+		expand_statement(stmt->label_statement);
+		return SIDE_EFFECTS;
+
+	case STMT_GOTO:
+		expand_expression(stmt->goto_expression);
+		return SIDE_EFFECTS;
+
+	case STMT_NONE:
+		break;
+	case STMT_ASM:
+		/* FIXME! Do the asm parameter evaluation! */
+		break;
+	case STMT_CONTEXT:
+		expand_expression(stmt->expression);
+		break;
+	case STMT_RANGE:
+		expand_expression(stmt->range_expression);
+		expand_expression(stmt->range_low);
+		expand_expression(stmt->range_high);
+		break;
+	}
+	return SIDE_EFFECTS;
+}
+
+static inline int bad_integer_constant_expression(struct expression *expr)
+{
+	if (!(expr->flags & Int_const_expr))
+		return 1;
+	if (expr->taint & Taint_comma)
+		return 1;
+	return 0;
+}
+
+static long long __get_expression_value(struct expression *expr, int strict)
+{
+	long long value, mask;
+	struct symbol *ctype;
+
+	if (!expr)
+		return 0;
+	ctype = evaluate_expression(expr);
+	if (!ctype) {
+		expression_error(expr, "bad constant expression type");
+		return 0;
+	}
+	expand_expression(expr);
+	if (expr->type != EXPR_VALUE) {
+		expression_error(expr, "bad constant expression");
+		return 0;
+	}
+	if (strict && bad_integer_constant_expression(expr)) {
+		expression_error(expr, "bad integer constant expression");
+		return 0;
+	}
+
+	value = expr->value;
+	mask = 1ULL << (ctype->bit_size-1);
+
+	if (value & mask) {
+		while (ctype->type != SYM_BASETYPE)
+			ctype = ctype->ctype.base_type;
+		if (!(ctype->ctype.modifiers & MOD_UNSIGNED))
+			value = value | mask | ~(mask-1);
+	}
+	return value;
+}
+
+long long get_expression_value(struct expression *expr)
+{
+	return __get_expression_value(expr, 0);
+}
+
+long long const_expression_value(struct expression *expr)
+{
+	return __get_expression_value(expr, 1);
+}
+
+int is_zero_constant(struct expression *expr)
+{
+	const int saved = conservative;
+	conservative = 1;
+	expand_expression(expr);
+	conservative = saved;
+	return expr->type == EXPR_VALUE && !expr->value;
+}
diff --git a/deps/sparse/expression.c b/deps/sparse/expression.c
new file mode 100644
index 0000000..0ae3a60
--- /dev/null
+++ b/deps/sparse/expression.c
@@ -0,0 +1,951 @@
+/*
+ * sparse/expression.c
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ *
+ * This is the expression parsing part of parsing C.
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "scope.h"
+#include "expression.h"
+#include "target.h"
+
+static int match_oplist(int op, ...)
+{
+	va_list args;
+	int nextop;
+
+	va_start(args, op);
+	do {
+		nextop = va_arg(args, int);
+	} while (nextop != 0 && nextop != op);
+	va_end(args);
+
+	return nextop != 0;
+}
+
+static struct token *comma_expression(struct token *, struct expression **);
+
+struct token *parens_expression(struct token *token, struct expression **expr, const char *where)
+{
+	token = expect(token, '(', where);
+	if (match_op(token, '{')) {
+		struct expression *e = alloc_expression(token->pos, EXPR_STATEMENT);
+		struct statement *stmt = alloc_statement(token->pos, STMT_COMPOUND);
+		*expr = e;
+		e->statement = stmt;
+		start_symbol_scope();
+		token = compound_statement(token->next, stmt);
+		end_symbol_scope();
+		token = expect(token, '}', "at end of statement expression");
+	} else
+		token = parse_expression(token, expr);
+	return expect(token, ')', where);
+}
+
+/*
+ * Handle __func__, __FUNCTION__ and __PRETTY_FUNCTION__ token
+ * conversion
+ */
+static int convert_one_fn_token(struct token *token)
+{
+	struct symbol *sym = current_fn;
+
+	if (sym) {
+		struct ident *ident = sym->ident;
+		if (ident) {
+			int len = ident->len;
+			struct string *string;
+
+			string = __alloc_string(len+1);
+			memcpy(string->data, ident->name, len);
+			string->data[len] = 0;
+			string->length = len+1;
+			token_type(token) = TOKEN_STRING;
+			token->string = string;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int convert_function(struct token *next)
+{
+	int retval = 0;
+	for (;;) {
+		struct token *token = next;
+		next = next->next;
+		switch (token_type(token)) {
+		case TOKEN_STRING:
+			continue;
+		case TOKEN_IDENT:
+			if (token->ident == &__func___ident ||
+			    token->ident == &__FUNCTION___ident ||
+			    token->ident == &__PRETTY_FUNCTION___ident) {
+				if (!convert_one_fn_token(token))
+					break;
+				retval = 1;
+				continue;
+			}
+		/* Fall through */
+		default:
+			break;
+		}
+		break;
+	}
+	return retval;
+}
+
+static struct token *parse_type(struct token *token, struct expression **tree)
+{
+	struct symbol *sym;
+	*tree = alloc_expression(token->pos, EXPR_TYPE);
+	(*tree)->flags = Int_const_expr; /* sic */
+	token = typename(token, &sym, NULL);
+	if (sym->ident)
+		sparse_error(token->pos,
+			     "type expression should not include identifier "
+			     "\"%s\"", sym->ident->name);
+	(*tree)->symbol = sym;
+	return token;
+}
+
+static struct token *builtin_types_compatible_p_expr(struct token *token,
+						     struct expression **tree)
+{
+	struct expression *expr = alloc_expression(
+		token->pos, EXPR_COMPARE);
+	expr->flags = Int_const_expr;
+	expr->op = SPECIAL_EQUAL;
+	token = token->next;
+	if (!match_op(token, '('))
+		return expect(token, '(',
+			      "after __builtin_types_compatible_p");
+	token = token->next;
+	token = parse_type(token, &expr->left);
+	if (!match_op(token, ','))
+		return expect(token, ',',
+			      "in __builtin_types_compatible_p");
+	token = token->next;
+	token = parse_type(token, &expr->right);
+	if (!match_op(token, ')'))
+		return expect(token, ')',
+			      "at end of __builtin_types_compatible_p");
+	token = token->next;
+	
+	*tree = expr;
+	return token;
+}
+
+static struct token *builtin_offsetof_expr(struct token *token,
+					   struct expression **tree)
+{
+	struct expression *expr = NULL;
+	struct expression **p = &expr;
+	struct symbol *sym;
+	int op = '.';
+
+	token = token->next;
+	if (!match_op(token, '('))
+		return expect(token, '(', "after __builtin_offset");
+
+	token = token->next;
+	token = typename(token, &sym, NULL);
+	if (sym->ident)
+		sparse_error(token->pos,
+			     "type expression should not include identifier "
+			     "\"%s\"", sym->ident->name);
+
+	if (!match_op(token, ','))
+		return expect(token, ',', "in __builtin_offset");
+
+	while (1) {
+		struct expression *e;
+		switch (op) {
+		case ')':
+			expr->in = sym;
+			*tree = expr;
+		default:
+			return expect(token, ')', "at end of __builtin_offset");
+		case SPECIAL_DEREFERENCE:
+			e = alloc_expression(token->pos, EXPR_OFFSETOF);
+			e->flags = Int_const_expr;
+			e->op = '[';
+			*p = e;
+			p = &e->down;
+			/* fall through */
+		case '.':
+			token = token->next;
+			e = alloc_expression(token->pos, EXPR_OFFSETOF);
+			e->flags = Int_const_expr;
+			e->op = '.';
+			if (token_type(token) != TOKEN_IDENT) {
+				sparse_error(token->pos, "Expected member name");
+				return token;
+			}
+			e->ident = token->ident;
+			token = token->next;
+			break;
+		case '[':
+			token = token->next;
+			e = alloc_expression(token->pos, EXPR_OFFSETOF);
+			e->flags = Int_const_expr;
+			e->op = '[';
+			token = parse_expression(token, &e->index);
+			token = expect(token, ']',
+					"at end of array dereference");
+			if (!e->index)
+				return token;
+		}
+		*p = e;
+		p = &e->down;
+		op = token_type(token) == TOKEN_SPECIAL ? token->special : 0;
+	}
+}
+
+static struct token *string_expression(struct token *token, struct expression *expr)
+{
+	struct string *string = token->string;
+	struct token *next = token->next;
+	int stringtype = token_type(token);
+
+	convert_function(token);
+
+	if (token_type(next) == stringtype) {
+		int totlen = string->length-1;
+		char *data;
+
+		do {
+			totlen += next->string->length-1;
+			next = next->next;
+		} while (token_type(next) == stringtype);
+
+		if (totlen > MAX_STRING) {
+			warning(token->pos, "trying to concatenate %d-character string (%d bytes max)", totlen, MAX_STRING);
+			totlen = MAX_STRING;
+		}
+
+		string = __alloc_string(totlen+1);
+		string->length = totlen+1;
+		data = string->data;
+		next = token;
+		do {
+			struct string *s = next->string;
+			int len = s->length-1;
+
+			if (len > totlen)
+				len = totlen;
+			totlen -= len;
+
+			next = next->next;
+			memcpy(data, s->data, len);
+			data += len;
+		} while (token_type(next) == stringtype);
+		*data = '\0';
+	}
+	expr->string = string;
+	return next;
+}
+
+#ifndef ULLONG_MAX
+#define ULLONG_MAX (~0ULL)
+#endif
+
+static unsigned long long parse_num(const char *nptr, char **end)
+{
+	if (nptr[0] == '0' && tolower(nptr[1]) == 'b')
+		return strtoull(&nptr[2], end, 2);
+	return strtoull(nptr, end, 0);
+}
+
+static void get_number_value(struct expression *expr, struct token *token)
+{
+	const char *str = token->number;
+	unsigned long long value;
+	char *end;
+	int size = 0, want_unsigned = 0;
+	int overflow = 0, do_warn = 0;
+	int try_unsigned = 1;
+	int bits;
+
+	errno = 0;
+	value = parse_num(str, &end);
+	if (end == str)
+		goto Float;
+	if (value == ULLONG_MAX && errno == ERANGE)
+		overflow = 1;
+	while (1) {
+		char c = *end++;
+		if (!c) {
+			break;
+		} else if (c == 'u' || c == 'U') {
+			if (want_unsigned)
+				goto Enoint;
+			want_unsigned = 1;
+		} else if (c == 'l' || c == 'L') {
+			if (size)
+				goto Enoint;
+			size = 1;
+			if (*end == c) {
+				size = 2;
+				end++;
+			}
+		} else
+			goto Float;
+	}
+	if (overflow)
+		goto Eoverflow;
+	/* OK, it's a valid integer */
+	/* decimals can be unsigned only if directly specified as such */
+	if (str[0] != '0' && !want_unsigned)
+		try_unsigned = 0;
+	if (!size) {
+		bits = bits_in_int - 1;
+		if (!(value & (~1ULL << bits))) {
+			if (!(value & (1ULL << bits))) {
+				goto got_it;
+			} else if (try_unsigned) {
+				want_unsigned = 1;
+				goto got_it;
+			}
+		}
+		size = 1;
+		do_warn = 1;
+	}
+	if (size < 2) {
+		bits = bits_in_long - 1;
+		if (!(value & (~1ULL << bits))) {
+			if (!(value & (1ULL << bits))) {
+				goto got_it;
+			} else if (try_unsigned) {
+				want_unsigned = 1;
+				goto got_it;
+			}
+			do_warn |= 2;
+		}
+		size = 2;
+		do_warn |= 1;
+	}
+	bits = bits_in_longlong - 1;
+	if (value & (~1ULL << bits))
+		goto Eoverflow;
+	if (!(value & (1ULL << bits)))
+		goto got_it;
+	if (!try_unsigned)
+		warning(expr->pos, "decimal constant %s is too big for long long",
+			show_token(token));
+	want_unsigned = 1;
+got_it:
+	if (do_warn)
+		warning(expr->pos, "constant %s is so big it is%s%s%s",
+			show_token(token),
+			want_unsigned ? " unsigned":"",
+			size > 0 ? " long":"",
+			size > 1 ? " long":"");
+	if (do_warn & 2)
+		warning(expr->pos,
+			"decimal constant %s is between LONG_MAX and ULONG_MAX."
+			" For C99 that means long long, C90 compilers are very "
+			"likely to produce unsigned long (and a warning) here",
+			show_token(token));
+        expr->type = EXPR_VALUE;
+	expr->flags = Int_const_expr;
+        expr->ctype = ctype_integer(size, want_unsigned);
+        expr->value = value;
+	return;
+Eoverflow:
+	error_die(expr->pos, "constant %s is too big even for unsigned long long",
+			show_token(token));
+	return;
+Float:
+	expr->fvalue = string_to_ld(str, &end);
+	if (str == end)
+		goto Enoint;
+
+	if (*end && end[1])
+		goto Enoint;
+
+	if (*end == 'f' || *end == 'F')
+		expr->ctype = &float_ctype;
+	else if (*end == 'l' || *end == 'L')
+		expr->ctype = &ldouble_ctype;
+	else if (!*end)
+		expr->ctype = &double_ctype;
+	else
+		goto Enoint;
+
+	expr->flags = Float_literal;
+	expr->type = EXPR_FVALUE;
+	return;
+
+Enoint:
+	error_die(expr->pos, "constant %s is not a valid number", show_token(token));
+}
+
+struct token *primary_expression(struct token *token, struct expression **tree)
+{
+	struct expression *expr = NULL;
+
+	switch (token_type(token)) {
+	case TOKEN_CHAR:
+	case TOKEN_WIDE_CHAR:
+		expr = alloc_expression(token->pos, EXPR_VALUE);   
+		expr->flags = Int_const_expr;
+		expr->ctype = token_type(token) == TOKEN_CHAR ? &int_ctype : &long_ctype;
+		expr->value = (unsigned char) token->character;
+		token = token->next;
+		break;
+
+	case TOKEN_NUMBER:
+		expr = alloc_expression(token->pos, EXPR_VALUE);
+		get_number_value(expr, token); /* will see if it's an integer */
+		token = token->next;
+		break;
+
+	case TOKEN_ZERO_IDENT: {
+		expr = alloc_expression(token->pos, EXPR_SYMBOL);
+		expr->flags = Int_const_expr;
+		expr->ctype = &int_ctype;
+		expr->symbol = &zero_int;
+		expr->symbol_name = token->ident;
+		token = token->next;
+		break;
+	}
+
+	case TOKEN_IDENT: {
+		struct symbol *sym = lookup_symbol(token->ident, NS_SYMBOL | NS_TYPEDEF);
+		struct token *next = token->next;
+
+		if (!sym) {
+			if (convert_function(token))
+				goto handle_string;
+			if (token->ident == &__builtin_types_compatible_p_ident) {
+				token = builtin_types_compatible_p_expr(token, &expr);
+				break;
+			}
+			if (token->ident == &__builtin_offsetof_ident) {
+				token = builtin_offsetof_expr(token, &expr);
+				break;
+			}
+		} else if (sym->enum_member) {
+			expr = alloc_expression(token->pos, EXPR_VALUE);
+			*expr = *sym->initializer;
+			/* we want the right position reported, thus the copy */
+			expr->pos = token->pos;
+			expr->flags = Int_const_expr;
+			token = next;
+			break;
+		}
+
+		expr = alloc_expression(token->pos, EXPR_SYMBOL);
+
+		/*
+		 * We support types as real first-class citizens, with type
+		 * comparisons etc:
+		 *
+		 *	if (typeof(a) == int) ..
+		 */
+		if (sym && sym->namespace == NS_TYPEDEF) {
+			sparse_error(token->pos, "typename in expression");
+			sym = NULL;
+		}
+		expr->symbol_name = token->ident;
+		expr->symbol = sym;
+		token = next;
+		break;
+	}
+
+	case TOKEN_STRING:
+	case TOKEN_WIDE_STRING: {
+	handle_string:
+		expr = alloc_expression(token->pos, EXPR_STRING);
+		expr->wide = token_type(token) == TOKEN_WIDE_STRING;
+		token = string_expression(token, expr);
+		break;
+	}
+
+	case TOKEN_SPECIAL:
+		if (token->special == '(') {
+			expr = alloc_expression(token->pos, EXPR_PREOP);
+			expr->op = '(';
+			token = parens_expression(token, &expr->unop, "in expression");
+			if (expr->unop)
+				expr->flags = expr->unop->flags;
+			break;
+		}
+		if (token->special == '[' && lookup_type(token->next)) {
+			expr = alloc_expression(token->pos, EXPR_TYPE);
+			expr->flags = Int_const_expr; /* sic */
+			token = typename(token->next, &expr->symbol, NULL);
+			token = expect(token, ']', "in type expression");
+			break;
+		}
+			
+	default:
+		;
+	}
+	*tree = expr;
+	return token;
+}
+
+static struct token *expression_list(struct token *token, struct expression_list **list)
+{
+	while (!match_op(token, ')')) {
+		struct expression *expr = NULL;
+		token = assignment_expression(token, &expr);
+		if (!expr)
+			break;
+		add_expression(list, expr);
+		if (!match_op(token, ','))
+			break;
+		token = token->next;
+	}
+	return token;
+}
+
+/*
+ * extend to deal with the ambiguous C grammar for parsing
+ * a cast expressions followed by an initializer.
+ */
+static struct token *postfix_expression(struct token *token, struct expression **tree, struct expression *cast_init_expr)
+{
+	struct expression *expr = cast_init_expr;
+
+	if (!expr)
+		token = primary_expression(token, &expr);
+
+	while (expr && token_type(token) == TOKEN_SPECIAL) {
+		switch (token->special) {
+		case '[': {			/* Array dereference */
+			struct expression *deref = alloc_expression(token->pos, EXPR_PREOP);
+			struct expression *add = alloc_expression(token->pos, EXPR_BINOP);
+
+			deref->op = '*';
+			deref->unop = add;
+
+			add->op = '+';
+			add->left = expr;
+			token = parse_expression(token->next, &add->right);
+			token = expect(token, ']', "at end of array dereference");
+			expr = deref;
+			continue;
+		}
+		case SPECIAL_INCREMENT:		/* Post-increment */
+		case SPECIAL_DECREMENT:	{	/* Post-decrement */
+			struct expression *post = alloc_expression(token->pos, EXPR_POSTOP);
+			post->op = token->special;
+			post->unop = expr;
+			expr = post;
+			token = token->next;
+			continue;
+		}
+		case SPECIAL_DEREFERENCE: {	/* Structure pointer member dereference */
+			/* "x->y" is just shorthand for "(*x).y" */
+			struct expression *inner = alloc_expression(token->pos, EXPR_PREOP);
+			inner->op = '*';
+			inner->unop = expr;
+			expr = inner;
+		}
+		/* Fall through!! */
+		case '.': {			/* Structure member dereference */
+			struct expression *deref = alloc_expression(token->pos, EXPR_DEREF);
+			deref->op = '.';
+			deref->deref = expr;
+			token = token->next;
+			if (token_type(token) != TOKEN_IDENT) {
+				sparse_error(token->pos, "Expected member name");
+				break;
+			}
+			deref->member = token->ident;
+			token = token->next;
+			expr = deref;
+			continue;
+		}
+
+		case '(': {			/* Function call */
+			struct expression *call = alloc_expression(token->pos, EXPR_CALL);
+			call->op = '(';
+			call->fn = expr;
+			token = expression_list(token->next, &call->args);
+			token = expect(token, ')', "in function call");
+			expr = call;
+			continue;
+		}
+
+		default:
+			break;
+		}
+		break;
+	}
+	*tree = expr;
+	return token;
+}
+
+static struct token *cast_expression(struct token *token, struct expression **tree);
+static struct token *unary_expression(struct token *token, struct expression **tree);
+
+static struct token *type_info_expression(struct token *token,
+	struct expression **tree, int type)
+{
+	struct expression *expr = alloc_expression(token->pos, type);
+	struct token *p;
+
+	*tree = expr;
+	expr->flags = Int_const_expr; /* XXX: VLA support will need that changed */
+	token = token->next;
+	if (!match_op(token, '(') || !lookup_type(token->next))
+		return unary_expression(token, &expr->cast_expression);
+	p = token;
+	token = typename(token->next, &expr->cast_type, NULL);
+
+	if (!match_op(token, ')')) {
+		static const char * error[] = {
+			[EXPR_SIZEOF] = "at end of sizeof",
+			[EXPR_ALIGNOF] = "at end of __alignof__",
+			[EXPR_PTRSIZEOF] = "at end of __sizeof_ptr__"
+		};
+		return expect(token, ')', error[type]);
+	}
+
+	token = token->next;
+	/*
+	 * C99 ambiguity: the typename might have been the beginning
+	 * of a typed initializer expression..
+	 */
+	if (match_op(token, '{')) {
+		struct expression *cast = alloc_expression(p->pos, EXPR_CAST);
+		cast->cast_type = expr->cast_type;
+		expr->cast_type = NULL;
+		expr->cast_expression = cast;
+		token = initializer(&cast->cast_expression, token);
+		token = postfix_expression(token, &expr->cast_expression, cast);
+	}
+	return token;
+}
+
+static struct token *unary_expression(struct token *token, struct expression **tree)
+{
+	if (token_type(token) == TOKEN_IDENT) {
+		struct ident *ident = token->ident;
+		if (ident->reserved) {
+			static const struct {
+				struct ident *id;
+				int type;
+			} type_information[] = {
+				{ &sizeof_ident, EXPR_SIZEOF },
+				{ &__alignof___ident, EXPR_ALIGNOF },
+				{ &__alignof_ident, EXPR_ALIGNOF },
+				{ &__sizeof_ptr___ident, EXPR_PTRSIZEOF },
+			};
+			int i;
+			for (i = 0; i < 3; i++) {
+				if (ident == type_information[i].id)
+					return type_info_expression(token, tree, type_information[i].type);
+			}
+		}
+	}
+
+	if (token_type(token) == TOKEN_SPECIAL) {
+		if (match_oplist(token->special,
+		    SPECIAL_INCREMENT, SPECIAL_DECREMENT,
+		    '&', '*', 0)) {
+		    	struct expression *unop;
+			struct expression *unary;
+			struct token *next;
+
+			next = cast_expression(token->next, &unop);
+			if (!unop) {
+				sparse_error(token->pos, "Syntax error in unary expression");
+				*tree = NULL;
+				return next;
+			}
+			unary = alloc_expression(token->pos, EXPR_PREOP);
+			unary->op = token->special;
+			unary->unop = unop;
+			*tree = unary;
+			return next;
+		}
+		/* possibly constant ones */
+		if (match_oplist(token->special, '+', '-', '~', '!', 0)) {
+		    	struct expression *unop;
+			struct expression *unary;
+			struct token *next;
+
+			next = cast_expression(token->next, &unop);
+			if (!unop) {
+				sparse_error(token->pos, "Syntax error in unary expression");
+				*tree = NULL;
+				return next;
+			}
+			unary = alloc_expression(token->pos, EXPR_PREOP);
+			unary->op = token->special;
+			unary->unop = unop;
+			unary->flags = unop->flags & Int_const_expr;
+			*tree = unary;
+			return next;
+		}
+		/* Gcc extension: &&label gives the address of a label */
+		if (match_op(token, SPECIAL_LOGICAL_AND) &&
+		    token_type(token->next) == TOKEN_IDENT) {
+			struct expression *label = alloc_expression(token->pos, EXPR_LABEL);
+			struct symbol *sym = label_symbol(token->next);
+			if (!(sym->ctype.modifiers & MOD_ADDRESSABLE)) {
+				sym->ctype.modifiers |= MOD_ADDRESSABLE;
+				add_symbol(&function_computed_target_list, sym);
+			}
+			label->label_symbol = sym;
+			*tree = label;
+			return token->next->next;
+		}
+						
+	}
+			
+	return postfix_expression(token, tree, NULL);
+}
+
+/*
+ * Ambiguity: a '(' can be either a cast-expression or
+ * a primary-expression depending on whether it is followed
+ * by a type or not. 
+ *
+ * additional ambiguity: a "cast expression" followed by
+ * an initializer is really a postfix-expression.
+ */
+static struct token *cast_expression(struct token *token, struct expression **tree)
+{
+	if (match_op(token, '(')) {
+		struct token *next = token->next;
+		if (lookup_type(next)) {
+			struct expression *cast = alloc_expression(next->pos, EXPR_CAST);
+			struct expression *v;
+			struct symbol *sym;
+			int is_force;
+
+			token = typename(next, &sym, &is_force);
+			cast->cast_type = sym;
+			token = expect(token, ')', "at end of cast operator");
+			if (match_op(token, '{')) {
+				if (is_force)
+					warning(sym->pos,
+						"[force] in compound literal");
+				token = initializer(&cast->cast_expression, token);
+				return postfix_expression(token, tree, cast);
+			}
+			*tree = cast;
+			if (is_force)
+				cast->type = EXPR_FORCE_CAST;
+			token = cast_expression(token, &v);
+			if (!v)
+				return token;
+			cast->cast_expression = v;
+			if (v->flags & Int_const_expr)
+				cast->flags = Int_const_expr;
+			else if (v->flags & Float_literal) /* and _not_ int */
+				cast->flags = Int_const_expr | Float_literal;
+			return token;
+		}
+	}
+	return unary_expression(token, tree);
+}
+
+/*
+ * Generic left-to-right binop parsing
+ *
+ * This _really_ needs to be inlined, because that makes the inner
+ * function call statically deterministic rather than a totally
+ * unpredictable indirect call. But gcc-3 is so "clever" that it
+ * doesn't do so by default even when you tell it to inline it.
+ *
+ * Making it a macro avoids the inlining problem, and also means
+ * that we can pass in the op-comparison as an expression rather
+ * than create a data structure for it.
+ */
+
+#define LR_BINOP_EXPRESSION(__token, tree, type, inner, compare)	\
+	struct expression *left = NULL;					\
+	struct token * next = inner(__token, &left);			\
+									\
+	if (left) {							\
+		while (token_type(next) == TOKEN_SPECIAL) {		\
+			struct expression *top, *right = NULL;		\
+			int op = next->special;				\
+									\
+			if (!(compare))					\
+				goto out;				\
+			top = alloc_expression(next->pos, type);	\
+			next = inner(next->next, &right);		\
+			if (!right) {					\
+				sparse_error(next->pos, "No right hand side of '%s'-expression", show_special(op));	\
+				break;					\
+			}						\
+			top->flags = left->flags & right->flags		\
+						& Int_const_expr;	\
+			top->op = op;					\
+			top->left = left;				\
+			top->right = right;				\
+			left = top;					\
+		}							\
+	}								\
+out:									\
+	*tree = left;							\
+	return next;							\
+
+static struct token *multiplicative_expression(struct token *token, struct expression **tree)
+{
+	LR_BINOP_EXPRESSION(
+		token, tree, EXPR_BINOP, cast_expression,
+		(op == '*') || (op == '/') || (op == '%')
+	);
+}
+
+static struct token *additive_expression(struct token *token, struct expression **tree)
+{
+	LR_BINOP_EXPRESSION(
+		token, tree, EXPR_BINOP, multiplicative_expression,
+		(op == '+') || (op == '-')
+	);
+}
+
+static struct token *shift_expression(struct token *token, struct expression **tree)
+{
+	LR_BINOP_EXPRESSION(
+		token, tree, EXPR_BINOP, additive_expression,
+		(op == SPECIAL_LEFTSHIFT) || (op == SPECIAL_RIGHTSHIFT)
+	);
+}
+
+static struct token *relational_expression(struct token *token, struct expression **tree)
+{
+	LR_BINOP_EXPRESSION(
+		token, tree, EXPR_COMPARE, shift_expression,
+		(op == '<') || (op == '>') ||
+		(op == SPECIAL_LTE) || (op == SPECIAL_GTE)
+	);
+}
+
+static struct token *equality_expression(struct token *token, struct expression **tree)
+{
+	LR_BINOP_EXPRESSION(
+		token, tree, EXPR_COMPARE, relational_expression,
+		(op == SPECIAL_EQUAL) || (op == SPECIAL_NOTEQUAL)
+	);
+}
+
+static struct token *bitwise_and_expression(struct token *token, struct expression **tree)
+{
+	LR_BINOP_EXPRESSION(
+		token, tree, EXPR_BINOP, equality_expression,
+		(op == '&')
+	);
+}
+
+static struct token *bitwise_xor_expression(struct token *token, struct expression **tree)
+{
+	LR_BINOP_EXPRESSION(
+		token, tree, EXPR_BINOP, bitwise_and_expression,
+		(op == '^')
+	);
+}
+
+static struct token *bitwise_or_expression(struct token *token, struct expression **tree)
+{
+	LR_BINOP_EXPRESSION(
+		token, tree, EXPR_BINOP, bitwise_xor_expression,
+		(op == '|')
+	);
+}
+
+static struct token *logical_and_expression(struct token *token, struct expression **tree)
+{
+	LR_BINOP_EXPRESSION(
+		token, tree, EXPR_LOGICAL, bitwise_or_expression,
+		(op == SPECIAL_LOGICAL_AND)
+	);
+}
+
+static struct token *logical_or_expression(struct token *token, struct expression **tree)
+{
+	LR_BINOP_EXPRESSION(
+		token, tree, EXPR_LOGICAL, logical_and_expression,
+		(op == SPECIAL_LOGICAL_OR)
+	);
+}
+
+struct token *conditional_expression(struct token *token, struct expression **tree)
+{
+	token = logical_or_expression(token, tree);
+	if (*tree && match_op(token, '?')) {
+		struct expression *expr = alloc_expression(token->pos, EXPR_CONDITIONAL);
+		expr->op = token->special;
+		expr->left = *tree;
+		*tree = expr;
+		token = parse_expression(token->next, &expr->cond_true);
+		token = expect(token, ':', "in conditional expression");
+		token = conditional_expression(token, &expr->cond_false);
+		if (expr->left && expr->cond_false) {
+			int is_const = expr->left->flags &
+					expr->cond_false->flags &
+					Int_const_expr;
+			if (expr->cond_true)
+				is_const &= expr->cond_true->flags;
+			expr->flags = is_const;
+		}
+	}
+	return token;
+}
+
+struct token *assignment_expression(struct token *token, struct expression **tree)
+{
+	token = conditional_expression(token, tree);
+	if (*tree && token_type(token) == TOKEN_SPECIAL) {
+		static const int assignments[] = {
+			'=',
+			SPECIAL_ADD_ASSIGN, SPECIAL_SUB_ASSIGN,
+			SPECIAL_MUL_ASSIGN, SPECIAL_DIV_ASSIGN,
+			SPECIAL_MOD_ASSIGN, SPECIAL_SHL_ASSIGN,
+			SPECIAL_SHR_ASSIGN, SPECIAL_AND_ASSIGN,
+			SPECIAL_OR_ASSIGN,  SPECIAL_XOR_ASSIGN };
+		int i, op = token->special;
+		for (i = 0; i < ARRAY_SIZE(assignments); i++)
+			if (assignments[i] == op) {
+				struct expression * expr = alloc_expression(token->pos, EXPR_ASSIGNMENT);
+				expr->left = *tree;
+				expr->op = op;
+				*tree = expr;
+				return assignment_expression(token->next, &expr->right);
+			}
+	}
+	return token;
+}
+
+static struct token *comma_expression(struct token *token, struct expression **tree)
+{
+	LR_BINOP_EXPRESSION(
+		token, tree, EXPR_COMMA, assignment_expression,
+		(op == ',')
+	);
+}
+
+struct token *parse_expression(struct token *token, struct expression **tree)
+{
+	return comma_expression(token,tree);
+}
+
+
diff --git a/deps/sparse/expression.h b/deps/sparse/expression.h
new file mode 100644
index 0000000..9778de8
--- /dev/null
+++ b/deps/sparse/expression.h
@@ -0,0 +1,222 @@
+#ifndef EXPRESSION_H
+#define EXPRESSION_H
+/*
+ * sparse/expression.h
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ *
+ * Declarations and helper functions for expression parsing.
+ */
+
+#include "allocate.h"
+#include "lib.h"
+#include "symbol.h"
+
+struct expression_list;
+
+enum expression_type {
+	EXPR_VALUE = 1,
+	EXPR_STRING,
+	EXPR_SYMBOL,
+	EXPR_TYPE,
+	EXPR_BINOP,
+	EXPR_ASSIGNMENT,
+	EXPR_LOGICAL,
+	EXPR_DEREF,
+	EXPR_PREOP,
+	EXPR_POSTOP,
+	EXPR_CAST,
+	EXPR_FORCE_CAST,
+	EXPR_IMPLIED_CAST,
+	EXPR_SIZEOF,
+	EXPR_ALIGNOF,
+	EXPR_PTRSIZEOF,
+	EXPR_CONDITIONAL,
+	EXPR_SELECT,		// a "safe" conditional expression
+	EXPR_STATEMENT,
+	EXPR_CALL,
+	EXPR_COMMA,
+	EXPR_COMPARE,
+	EXPR_LABEL,
+	EXPR_INITIALIZER,	// initializer list
+	EXPR_IDENTIFIER,	// identifier in initializer
+	EXPR_INDEX,		// index in initializer
+	EXPR_POS,		// position in initializer
+	EXPR_FVALUE,
+	EXPR_SLICE,
+	EXPR_OFFSETOF,
+};
+
+enum {
+	Int_const_expr = 1,
+	Float_literal = 2,
+}; /* for expr->flags */
+
+enum {
+	Taint_comma = 1,
+}; /* for expr->taint */
+
+struct expression {
+	enum expression_type type:8;
+	unsigned flags:8;
+	int op;
+	struct position pos;
+	struct symbol *ctype;
+	union {
+		// EXPR_VALUE
+		struct {
+			unsigned long long value;
+			unsigned taint;
+		};
+
+		// EXPR_FVALUE
+		long double fvalue;
+
+		// EXPR_STRING
+		struct {
+			int wide;
+			struct string *string;
+		};
+
+		// EXPR_UNOP, EXPR_PREOP and EXPR_POSTOP
+		struct /* unop */ {
+			struct expression *unop;
+			unsigned long op_value;
+		};
+
+		// EXPR_SYMBOL, EXPR_TYPE
+		struct /* symbol_arg */ {
+			struct symbol *symbol;
+			struct ident *symbol_name;
+		};
+
+		// EXPR_STATEMENT
+		struct statement *statement;
+
+		// EXPR_BINOP, EXPR_COMMA, EXPR_COMPARE, EXPR_LOGICAL and EXPR_ASSIGNMENT
+		struct /* binop_arg */ {
+			struct expression *left, *right;
+		};
+		// EXPR_DEREF
+		struct /* deref_arg */ {
+			struct expression *deref;
+			struct ident *member;
+		};
+		// EXPR_SLICE
+		struct /* slice */ {
+			struct expression *base;
+			unsigned r_bitpos, r_nrbits;
+		};
+		// EXPR_CAST and EXPR_SIZEOF
+		struct /* cast_arg */ {
+			struct symbol *cast_type;
+			struct expression *cast_expression;
+		};
+		// EXPR_CONDITIONAL
+		// EXPR_SELECT
+		struct /* conditional_expr */ {
+			struct expression *conditional, *cond_true, *cond_false;
+		};
+		// EXPR_CALL
+		struct /* call_expr */ {
+			struct expression *fn;
+			struct expression_list *args;
+		};
+		// EXPR_LABEL
+		struct /* label_expr */ {
+			struct symbol *label_symbol;
+		};
+		// EXPR_INITIALIZER
+		struct expression_list *expr_list;
+		// EXPR_IDENTIFIER
+		struct /* ident_expr */ {
+			struct ident *expr_ident;
+			struct symbol *field;
+			struct expression *ident_expression;
+		};
+		// EXPR_INDEX
+		struct /* index_expr */ {
+			unsigned int idx_from, idx_to;
+			struct expression *idx_expression;
+		};
+		// EXPR_POS
+		struct /* initpos_expr */ {
+			unsigned int init_offset, init_nr;
+			struct expression *init_expr;
+		};
+		// EXPR_OFFSETOF
+		struct {
+			struct symbol *in;
+			struct expression *down;
+			union {
+				struct ident *ident;
+				struct expression *index;
+			};
+		};
+	};
+};
+
+/* Constant expression values */
+int is_zero_constant(struct expression *);
+long long get_expression_value(struct expression *);
+long long const_expression_value(struct expression *);
+
+/* Expression parsing */
+struct token *parse_expression(struct token *token, struct expression **tree);
+struct token *conditional_expression(struct token *token, struct expression **tree);
+struct token *primary_expression(struct token *token, struct expression **tree);
+struct token *parens_expression(struct token *token, struct expression **expr, const char *where);
+struct token *assignment_expression(struct token *token, struct expression **tree);
+
+extern void evaluate_symbol_list(struct symbol_list *list);
+extern struct symbol *evaluate_statement(struct statement *stmt);
+extern struct symbol *evaluate_expression(struct expression *);
+
+extern int expand_symbol(struct symbol *);
+
+static inline struct expression *alloc_expression(struct position pos, int type)
+{
+	struct expression *expr = __alloc_expression(0);
+	expr->type = type;
+	expr->pos = pos;
+	return expr;
+}
+
+static inline struct expression *alloc_const_expression(struct position pos, int value)
+{
+	struct expression *expr = __alloc_expression(0);
+	expr->type = EXPR_VALUE;
+	expr->pos = pos;
+	expr->value = value;
+	expr->ctype = &int_ctype;
+	return expr;
+}
+
+/* Type name parsing */
+struct token *typename(struct token *, struct symbol **, int *);
+
+static inline int lookup_type(struct token *token)
+{
+	if (token->pos.type == TOKEN_IDENT) {
+		struct symbol *sym = lookup_symbol(token->ident, NS_SYMBOL | NS_TYPEDEF);
+		return sym && (sym->namespace & NS_TYPEDEF);
+	}
+	return 0;
+}
+
+/* Statement parsing */
+struct statement *alloc_statement(struct position pos, int type);
+struct token *initializer(struct expression **tree, struct token *token);
+struct token *compound_statement(struct token *, struct statement *);
+
+/* The preprocessor calls this 'constant_expression()' */
+#define constant_expression(token,tree) conditional_expression(token, tree)
+
+/* Cast folding of constant values.. */
+void cast_value(struct expression *expr, struct symbol *newtype,
+	struct expression *old, struct symbol *oldtype);
+
+#endif
diff --git a/deps/sparse/flow.c b/deps/sparse/flow.c
new file mode 100644
index 0000000..7db9548
--- /dev/null
+++ b/deps/sparse/flow.c
@@ -0,0 +1,1006 @@
+/*
+ * Flow - walk the linearized flowgraph, simplifying it as we
+ * go along.
+ *
+ * Copyright (C) 2004 Linus Torvalds
+ */
+
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <assert.h>
+
+#include "parse.h"
+#include "expression.h"
+#include "linearize.h"
+#include "flow.h"
+#include "target.h"
+
+unsigned long bb_generation;
+
+/*
+ * Dammit, if we have a phi-node followed by a conditional
+ * branch on that phi-node, we should damn well be able to
+ * do something about the source. Maybe.
+ */
+static int rewrite_branch(struct basic_block *bb,
+	struct basic_block **ptr,
+	struct basic_block *old,
+	struct basic_block *new)
+{
+	if (*ptr != old || new == old)
+		return 0;
+
+	/* We might find new if-conversions or non-dominating CSEs */
+	repeat_phase |= REPEAT_CSE;
+	*ptr = new;
+	replace_bb_in_list(&bb->children, old, new, 1);
+	remove_bb_from_list(&old->parents, bb, 1);
+	add_bb(&new->parents, bb);
+	return 1;
+}
+
+/*
+ * Return the known truth value of a pseudo, or -1 if
+ * it's not known.
+ */
+static int pseudo_truth_value(pseudo_t pseudo)
+{
+	switch (pseudo->type) {
+	case PSEUDO_VAL:
+		return !!pseudo->value;
+
+	case PSEUDO_REG: {
+		struct instruction *insn = pseudo->def;
+
+		/* A symbol address is always considered true.. */
+		if (insn->opcode == OP_SYMADDR && insn->target == pseudo)
+			return 1;
+	}
+		/* Fall through */
+	default:
+		return -1;
+	}
+}
+
+/*
+ * Does a basic block depend on the pseudos that "src" defines?
+ */
+static int bb_depends_on(struct basic_block *target, struct basic_block *src)
+{
+	pseudo_t pseudo;
+
+	FOR_EACH_PTR(src->defines, pseudo) {
+		if (pseudo_in_list(target->needs, pseudo))
+			return 1;
+	} END_FOR_EACH_PTR(pseudo);
+	return 0;
+}
+
+/*
+ * When we reach here, we have:
+ *  - a basic block that ends in a conditional branch and
+ *    that has no side effects apart from the pseudos it
+ *    may change.
+ *  - the phi-node that the conditional branch depends on
+ *  - full pseudo liveness information
+ *
+ * We need to check if any of the _sources_ of the phi-node
+ * may be constant, and not actually need this block at all.
+ */
+static int try_to_simplify_bb(struct basic_block *bb, struct instruction *first, struct instruction *second)
+{
+	int changed = 0;
+	pseudo_t phi;
+
+	FOR_EACH_PTR(first->phi_list, phi) {
+		struct instruction *def = phi->def;
+		struct basic_block *source, *target;
+		pseudo_t pseudo;
+		struct instruction *br;
+		int true;
+
+		if (!def)
+			continue;
+		source = def->bb;
+		pseudo = def->src1;
+		if (!pseudo || !source)
+			continue;
+		br = last_instruction(source->insns);
+		if (!br)
+			continue;
+		if (br->opcode != OP_BR)
+			continue;
+		true = pseudo_truth_value(pseudo);
+		if (true < 0)
+			continue;
+		target = true ? second->bb_true : second->bb_false;
+		if (bb_depends_on(target, bb))
+			continue;
+		changed |= rewrite_branch(source, &br->bb_true, bb, target);
+		changed |= rewrite_branch(source, &br->bb_false, bb, target);
+	} END_FOR_EACH_PTR(phi);
+	return changed;
+}
+
+static int bb_has_side_effects(struct basic_block *bb)
+{
+	struct instruction *insn;
+	FOR_EACH_PTR(bb->insns, insn) {
+		switch (insn->opcode) {
+		case OP_CALL:
+			/* FIXME! This should take "const" etc into account */
+			return 1;
+
+		case OP_STORE:
+		case OP_CONTEXT:
+			return 1;
+
+		case OP_ASM:
+			/* FIXME! This should take "volatile" etc into account */
+			return 1;
+
+		default:
+			continue;
+		}
+	} END_FOR_EACH_PTR(insn);
+	return 0;
+}
+
+static int simplify_phi_branch(struct basic_block *bb, struct instruction *br)
+{
+	pseudo_t cond = br->cond;
+	struct instruction *def;
+
+	if (cond->type != PSEUDO_REG)
+		return 0;
+	def = cond->def;
+	if (def->bb != bb || def->opcode != OP_PHI)
+		return 0;
+	if (bb_has_side_effects(bb))
+		return 0;
+	return try_to_simplify_bb(bb, def, br);
+}
+
+static int simplify_branch_branch(struct basic_block *bb, struct instruction *br,
+	struct basic_block **target_p, int true)
+{
+	struct basic_block *target = *target_p, *final;
+	struct instruction *insn;
+	int retval;
+
+	if (target == bb)
+		return 0;
+	insn = last_instruction(target->insns);
+	if (!insn || insn->opcode != OP_BR || insn->cond != br->cond)
+		return 0;
+	/*
+	 * Ahhah! We've found a branch to a branch on the same conditional!
+	 * Now we just need to see if we can rewrite the branch..
+	 */
+	retval = 0;
+	final = true ? insn->bb_true : insn->bb_false;
+	if (bb_has_side_effects(target))
+		goto try_to_rewrite_target;
+	if (bb_depends_on(final, target))
+		goto try_to_rewrite_target;
+	return rewrite_branch(bb, target_p, target, final);
+
+try_to_rewrite_target:
+	/*
+	 * If we're the only parent, at least we can rewrite the
+	 * now-known second branch.
+	 */
+	if (bb_list_size(target->parents) != 1)
+		return retval;
+	insert_branch(target, insn, final);
+	kill_instruction(insn);
+	return 1;
+}
+
+static int simplify_one_branch(struct basic_block *bb, struct instruction *br)
+{
+	if (simplify_phi_branch(bb, br))
+		return 1;
+	return simplify_branch_branch(bb, br, &br->bb_true, 1) |
+	       simplify_branch_branch(bb, br, &br->bb_false, 0);
+}
+
+static int simplify_branch_nodes(struct entrypoint *ep)
+{
+	int changed = 0;
+	struct basic_block *bb;
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		struct instruction *br = last_instruction(bb->insns);
+
+		if (!br || br->opcode != OP_BR || !br->bb_false)
+			continue;
+		changed |= simplify_one_branch(bb, br);
+	} END_FOR_EACH_PTR(bb);
+	return changed;
+}
+
+/*
+ * This is called late - when we have intra-bb liveness information..
+ */
+int simplify_flow(struct entrypoint *ep)
+{
+	return simplify_branch_nodes(ep);
+}
+
+static inline void concat_user_list(struct pseudo_user_list *src, struct pseudo_user_list **dst)
+{
+	concat_ptr_list((struct ptr_list *)src, (struct ptr_list **)dst);
+}
+
+void convert_instruction_target(struct instruction *insn, pseudo_t src)
+{
+	pseudo_t target;
+	struct pseudo_user *pu;
+	/*
+	 * Go through the "insn->users" list and replace them all..
+	 */
+	target = insn->target;
+	if (target == src)
+		return;
+	FOR_EACH_PTR(target->users, pu) {
+		if (*pu->userp != VOID) {
+			assert(*pu->userp == target);
+			*pu->userp = src;
+		}
+	} END_FOR_EACH_PTR(pu);
+	concat_user_list(target->users, &src->users);
+	target->users = NULL;
+}
+
+void convert_load_instruction(struct instruction *insn, pseudo_t src)
+{
+	convert_instruction_target(insn, src);
+	/* Turn the load into a no-op */
+	insn->opcode = OP_LNOP;
+	insn->bb = NULL;
+}
+
+static int overlapping_memop(struct instruction *a, struct instruction *b)
+{
+	unsigned int a_start = bytes_to_bits(a->offset);
+	unsigned int b_start = bytes_to_bits(b->offset);
+	unsigned int a_size = a->size;
+	unsigned int b_size = b->size;
+
+	if (a_size + a_start <= b_start)
+		return 0;
+	if (b_size + b_start <= a_start)
+		return 0;
+	return 1;
+}
+
+static inline int same_memop(struct instruction *a, struct instruction *b)
+{
+	return	a->offset == b->offset && a->size == b->size;
+}
+
+/*
+ * Return 1 if "dom" dominates the access to "pseudo"
+ * in "insn".
+ *
+ * Return 0 if it doesn't, and -1 if you don't know.
+ */
+int dominates(pseudo_t pseudo, struct instruction *insn, struct instruction *dom, int local)
+{
+	int opcode = dom->opcode;
+
+	if (opcode == OP_CALL || opcode == OP_ENTRY)
+		return local ? 0 : -1;
+	if (opcode != OP_LOAD && opcode != OP_STORE)
+		return 0;
+	if (dom->src != pseudo) {
+		if (local)
+			return 0;
+		/* We don't think two explicitly different symbols ever alias */
+		if (dom->src->type == PSEUDO_SYM)
+			return 0;
+		/* We could try to do some alias analysis here */
+		return -1;
+	}
+	if (!same_memop(insn, dom)) {
+		if (dom->opcode == OP_LOAD)
+			return 0;
+		if (!overlapping_memop(insn, dom))
+			return 0;
+		return -1;
+	}
+	return 1;
+}
+
+static int find_dominating_parents(pseudo_t pseudo, struct instruction *insn,
+	struct basic_block *bb, unsigned long generation, struct pseudo_list **dominators,
+	int local, int loads)
+{
+	struct basic_block *parent;
+
+	if (!bb->parents)
+		return !!local;
+
+	if (bb_list_size(bb->parents) > 1)
+		loads = 0;
+	FOR_EACH_PTR(bb->parents, parent) {
+		struct instruction *one;
+		struct instruction *br;
+		pseudo_t phi;
+
+		FOR_EACH_PTR_REVERSE(parent->insns, one) {
+			int dominance;
+			if (one == insn)
+				goto no_dominance;
+			dominance = dominates(pseudo, insn, one, local);
+			if (dominance < 0) {
+				if (one->opcode == OP_LOAD)
+					continue;
+				return 0;
+			}
+			if (!dominance)
+				continue;
+			if (one->opcode == OP_LOAD && !loads)
+				continue;
+			goto found_dominator;
+		} END_FOR_EACH_PTR_REVERSE(one);
+no_dominance:
+		if (parent->generation == generation)
+			continue;
+		parent->generation = generation;
+
+		if (!find_dominating_parents(pseudo, insn, parent, generation, dominators, local, loads))
+			return 0;
+		continue;
+
+found_dominator:
+		br = delete_last_instruction(&parent->insns);
+		phi = alloc_phi(parent, one->target, one->size);
+		phi->ident = phi->ident ? : pseudo->ident;
+		add_instruction(&parent->insns, br);
+		use_pseudo(insn, phi, add_pseudo(dominators, phi));
+	} END_FOR_EACH_PTR(parent);
+	return 1;
+}		
+
+/*
+ * We should probably sort the phi list just to make it easier to compare
+ * later for equality. 
+ */
+void rewrite_load_instruction(struct instruction *insn, struct pseudo_list *dominators)
+{
+	pseudo_t new, phi;
+
+	/*
+	 * Check for somewhat common case of duplicate
+	 * phi nodes.
+	 */
+	new = first_pseudo(dominators)->def->src1;
+	FOR_EACH_PTR(dominators, phi) {
+		if (new != phi->def->src1)
+			goto complex_phi;
+		new->ident = new->ident ? : phi->ident;
+	} END_FOR_EACH_PTR(phi);
+
+	/*
+	 * All the same pseudo - mark the phi-nodes unused
+	 * and convert the load into a LNOP and replace the
+	 * pseudo.
+	 */
+	FOR_EACH_PTR(dominators, phi) {
+		phi->def->bb = NULL;
+	} END_FOR_EACH_PTR(phi);
+	convert_load_instruction(insn, new);
+	return;
+
+complex_phi:
+	/* We leave symbol pseudos with a bogus usage list here */
+	if (insn->src->type != PSEUDO_SYM)
+		kill_use(&insn->src);
+	insn->opcode = OP_PHI;
+	insn->phi_list = dominators;
+}
+
+static int find_dominating_stores(pseudo_t pseudo, struct instruction *insn,
+	unsigned long generation, int local)
+{
+	struct basic_block *bb = insn->bb;
+	struct instruction *one, *dom = NULL;
+	struct pseudo_list *dominators;
+	int partial;
+
+	/* Unreachable load? Undo it */
+	if (!bb) {
+		insn->opcode = OP_LNOP;
+		return 1;
+	}
+
+	partial = 0;
+	FOR_EACH_PTR(bb->insns, one) {
+		int dominance;
+		if (one == insn)
+			goto found;
+		dominance = dominates(pseudo, insn, one, local);
+		if (dominance < 0) {
+			/* Ignore partial load dominators */
+			if (one->opcode == OP_LOAD)
+				continue;
+			dom = NULL;
+			partial = 1;
+			continue;
+		}
+		if (!dominance)
+			continue;
+		dom = one;
+		partial = 0;
+	} END_FOR_EACH_PTR(one);
+	/* Whaa? */
+	warning(pseudo->sym->pos, "unable to find symbol read");
+	return 0;
+found:
+	if (partial)
+		return 0;
+
+	if (dom) {
+		convert_load_instruction(insn, dom->target);
+		return 1;
+	}
+
+	/* OK, go find the parents */
+	bb->generation = generation;
+
+	dominators = NULL;
+	if (!find_dominating_parents(pseudo, insn, bb, generation, &dominators, local, 1))
+		return 0;
+
+	/* This happens with initial assignments to structures etc.. */
+	if (!dominators) {
+		if (!local)
+			return 0;
+		check_access(insn);
+		convert_load_instruction(insn, value_pseudo(0));
+		return 1;
+	}
+
+	/*
+	 * If we find just one dominating instruction, we
+	 * can turn it into a direct thing. Otherwise we'll
+	 * have to turn the load into a phi-node of the
+	 * dominators.
+	 */
+	rewrite_load_instruction(insn, dominators);
+	return 1;
+}
+
+static void kill_store(struct instruction *insn)
+{
+	if (insn) {
+		insn->bb = NULL;
+		insn->opcode = OP_SNOP;
+		kill_use(&insn->target);
+	}
+}
+
+/* Kill a pseudo that is dead on exit from the bb */
+static void kill_dead_stores(pseudo_t pseudo, unsigned long generation, struct basic_block *bb, int local)
+{
+	struct instruction *insn;
+	struct basic_block *parent;
+
+	if (bb->generation == generation)
+		return;
+	bb->generation = generation;
+	FOR_EACH_PTR_REVERSE(bb->insns, insn) {
+		int opcode = insn->opcode;
+
+		if (opcode != OP_LOAD && opcode != OP_STORE) {
+			if (local)
+				continue;
+			if (opcode == OP_CALL)
+				return;
+			continue;
+		}
+		if (insn->src == pseudo) {
+			if (opcode == OP_LOAD)
+				return;
+			kill_store(insn);
+			continue;
+		}
+		if (local)
+			continue;
+		if (insn->src->type != PSEUDO_SYM)
+			return;
+	} END_FOR_EACH_PTR_REVERSE(insn);
+
+	FOR_EACH_PTR(bb->parents, parent) {
+		struct basic_block *child;
+		FOR_EACH_PTR(parent->children, child) {
+			if (child && child != bb)
+				return;
+		} END_FOR_EACH_PTR(child);
+		kill_dead_stores(pseudo, generation, parent, local);
+	} END_FOR_EACH_PTR(parent);
+}
+
+/*
+ * This should see if the "insn" trivially dominates some previous store, and kill the
+ * store if unnecessary.
+ */
+static void kill_dominated_stores(pseudo_t pseudo, struct instruction *insn, 
+	unsigned long generation, struct basic_block *bb, int local, int found)
+{
+	struct instruction *one;
+	struct basic_block *parent;
+
+	/* Unreachable store? Undo it */
+	if (!bb) {
+		kill_store(insn);
+		return;
+	}
+	if (bb->generation == generation)
+		return;
+	bb->generation = generation;
+	FOR_EACH_PTR_REVERSE(bb->insns, one) {
+		int dominance;
+		if (!found) {
+			if (one != insn)
+				continue;
+			found = 1;
+			continue;
+		}
+		dominance = dominates(pseudo, insn, one, local);
+		if (!dominance)
+			continue;
+		if (dominance < 0)
+			return;
+		if (one->opcode == OP_LOAD)
+			return;
+		kill_store(one);
+	} END_FOR_EACH_PTR_REVERSE(one);
+
+	if (!found) {
+		warning(bb->pos, "Unable to find instruction");
+		return;
+	}
+
+	FOR_EACH_PTR(bb->parents, parent) {
+		struct basic_block *child;
+		FOR_EACH_PTR(parent->children, child) {
+			if (child && child != bb)
+				return;
+		} END_FOR_EACH_PTR(child);
+		kill_dominated_stores(pseudo, insn, generation, parent, local, found);
+	} END_FOR_EACH_PTR(parent);
+}
+
+void check_access(struct instruction *insn)
+{
+	pseudo_t pseudo = insn->src;
+
+	if (insn->bb && pseudo->type == PSEUDO_SYM) {
+		int offset = insn->offset, bit = bytes_to_bits(offset) + insn->size;
+		struct symbol *sym = pseudo->sym;
+
+		if (sym->bit_size > 0 && (offset < 0 || bit > sym->bit_size))
+			warning(insn->pos, "invalid access %s '%s' (%d %d)",
+				offset < 0 ? "below" : "past the end of",
+				show_ident(sym->ident), offset,
+				bits_to_bytes(sym->bit_size));
+	}
+}
+
+static void simplify_one_symbol(struct entrypoint *ep, struct symbol *sym)
+{
+	pseudo_t pseudo, src;
+	struct pseudo_user *pu;
+	struct instruction *def;
+	unsigned long mod;
+	int all, stores, complex;
+
+	/* Never used as a symbol? */
+	pseudo = sym->pseudo;
+	if (!pseudo)
+		return;
+
+	/* We don't do coverage analysis of volatiles.. */
+	if (sym->ctype.modifiers & MOD_VOLATILE)
+		return;
+
+	/* ..and symbols with external visibility need more care */
+	mod = sym->ctype.modifiers & (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE);
+	if (mod)
+		goto external_visibility;
+
+	def = NULL;
+	stores = 0;
+	complex = 0;
+	FOR_EACH_PTR(pseudo->users, pu) {
+		/* We know that the symbol-pseudo use is the "src" in the instruction */
+		struct instruction *insn = pu->insn;
+
+		switch (insn->opcode) {
+		case OP_STORE:
+			stores++;
+			def = insn;
+			break;
+		case OP_LOAD:
+			break;
+		case OP_SYMADDR:
+			if (!insn->bb)
+				continue;
+			mod |= MOD_ADDRESSABLE;
+			goto external_visibility;
+		case OP_NOP:
+		case OP_SNOP:
+		case OP_LNOP:
+		case OP_PHI:
+			continue;
+		default:
+			warning(sym->pos, "symbol '%s' pseudo used in unexpected way", show_ident(sym->ident));
+		}
+		complex |= insn->offset;
+	} END_FOR_EACH_PTR(pu);
+
+	if (complex)
+		goto complex_def;
+	if (stores > 1)
+		goto multi_def;
+
+	/*
+	 * Goodie, we have a single store (if even that) in the whole
+	 * thing. Replace all loads with moves from the pseudo,
+	 * replace the store with a def.
+	 */
+	src = VOID;
+	if (def)
+		src = def->target;
+
+	FOR_EACH_PTR(pseudo->users, pu) {
+		struct instruction *insn = pu->insn;
+		if (insn->opcode == OP_LOAD) {
+			check_access(insn);
+			convert_load_instruction(insn, src);
+		}
+	} END_FOR_EACH_PTR(pu);
+
+	/* Turn the store into a no-op */
+	kill_store(def);
+	return;
+
+multi_def:
+complex_def:
+external_visibility:
+	all = 1;
+	FOR_EACH_PTR_REVERSE(pseudo->users, pu) {
+		struct instruction *insn = pu->insn;
+		if (insn->opcode == OP_LOAD)
+			all &= find_dominating_stores(pseudo, insn, ++bb_generation, !mod);
+	} END_FOR_EACH_PTR_REVERSE(pu);
+
+	/* If we converted all the loads, remove the stores. They are dead */
+	if (all && !mod) {
+		FOR_EACH_PTR(pseudo->users, pu) {
+			struct instruction *insn = pu->insn;
+			if (insn->opcode == OP_STORE)
+				kill_store(insn);
+		} END_FOR_EACH_PTR(pu);
+	} else {
+		/*
+		 * If we couldn't take the shortcut, see if we can at least kill some
+		 * of them..
+		 */
+		FOR_EACH_PTR(pseudo->users, pu) {
+			struct instruction *insn = pu->insn;
+			if (insn->opcode == OP_STORE)
+				kill_dominated_stores(pseudo, insn, ++bb_generation, insn->bb, !mod, 0);
+		} END_FOR_EACH_PTR(pu);
+
+		if (!(mod & (MOD_NONLOCAL | MOD_STATIC))) {
+			struct basic_block *bb;
+			FOR_EACH_PTR(ep->bbs, bb) {
+				if (!bb->children)
+					kill_dead_stores(pseudo, ++bb_generation, bb, !mod);
+			} END_FOR_EACH_PTR(bb);
+		}
+	}
+			
+	return;
+}
+
+void simplify_symbol_usage(struct entrypoint *ep)
+{
+	pseudo_t pseudo;
+
+	FOR_EACH_PTR(ep->accesses, pseudo) {
+		simplify_one_symbol(ep, pseudo->sym);
+	} END_FOR_EACH_PTR(pseudo);
+}
+
+static void mark_bb_reachable(struct basic_block *bb, unsigned long generation)
+{
+	struct basic_block *child;
+
+	if (bb->generation == generation)
+		return;
+	bb->generation = generation;
+	FOR_EACH_PTR(bb->children, child) {
+		mark_bb_reachable(child, generation);
+	} END_FOR_EACH_PTR(child);
+}
+
+static void kill_defs(struct instruction *insn)
+{
+	pseudo_t target = insn->target;
+
+	if (!has_use_list(target))
+		return;
+	if (target->def != insn)
+		return;
+
+	convert_instruction_target(insn, VOID);
+}
+
+void kill_bb(struct basic_block *bb)
+{
+	struct instruction *insn;
+	struct basic_block *child, *parent;
+
+	FOR_EACH_PTR(bb->insns, insn) {
+		kill_instruction(insn);
+		kill_defs(insn);
+		/*
+		 * We kill unreachable instructions even if they
+		 * otherwise aren't "killable" (e.g. volatile loads)
+		 */
+		insn->bb = NULL;
+	} END_FOR_EACH_PTR(insn);
+	bb->insns = NULL;
+
+	FOR_EACH_PTR(bb->children, child) {
+		remove_bb_from_list(&child->parents, bb, 0);
+	} END_FOR_EACH_PTR(child);
+	bb->children = NULL;
+
+	FOR_EACH_PTR(bb->parents, parent) {
+		remove_bb_from_list(&parent->children, bb, 0);
+	} END_FOR_EACH_PTR(parent);
+	bb->parents = NULL;
+}
+
+void kill_unreachable_bbs(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+	unsigned long generation = ++bb_generation;
+
+	mark_bb_reachable(ep->entry->bb, generation);
+	FOR_EACH_PTR(ep->bbs, bb) {
+		if (bb->generation == generation)
+			continue;
+		/* Mark it as being dead */
+		kill_bb(bb);
+		bb->ep = NULL;
+		DELETE_CURRENT_PTR(bb);
+	} END_FOR_EACH_PTR(bb);
+	PACK_PTR_LIST(&ep->bbs);
+}
+
+static int rewrite_parent_branch(struct basic_block *bb, struct basic_block *old, struct basic_block *new)
+{
+	int changed = 0;
+	struct instruction *insn = last_instruction(bb->insns);
+
+	if (!insn)
+		return 0;
+
+	/* Infinite loops: let's not "optimize" them.. */
+	if (old == new)
+		return 0;
+
+	switch (insn->opcode) {
+	case OP_BR:
+		changed |= rewrite_branch(bb, &insn->bb_true, old, new);
+		changed |= rewrite_branch(bb, &insn->bb_false, old, new);
+		assert(changed);
+		return changed;
+	case OP_SWITCH: {
+		struct multijmp *jmp;
+		FOR_EACH_PTR(insn->multijmp_list, jmp) {
+			changed |= rewrite_branch(bb, &jmp->target, old, new);
+		} END_FOR_EACH_PTR(jmp);
+		assert(changed);
+		return changed;
+	}
+	default:
+		return 0;
+	}
+}
+
+static struct basic_block * rewrite_branch_bb(struct basic_block *bb, struct instruction *br)
+{
+	struct basic_block *parent;
+	struct basic_block *target = br->bb_true;
+	struct basic_block *false = br->bb_false;
+
+	if (target && false) {
+		pseudo_t cond = br->cond;
+		if (cond->type != PSEUDO_VAL)
+			return NULL;
+		target = cond->value ? target : false;
+	}
+
+	/*
+	 * We can't do FOR_EACH_PTR() here, because the parent list
+	 * may change when we rewrite the parent.
+	 */
+	while ((parent = first_basic_block(bb->parents)) != NULL) {
+		if (!rewrite_parent_branch(parent, bb, target))
+			return NULL;
+	}
+	return target;
+}
+
+static void vrfy_bb_in_list(struct basic_block *bb, struct basic_block_list *list)
+{
+	if (bb) {
+		struct basic_block *tmp;
+		int no_bb_in_list = 0;
+
+		FOR_EACH_PTR(list, tmp) {
+			if (bb == tmp)
+				return;
+		} END_FOR_EACH_PTR(tmp);
+		assert(no_bb_in_list);
+	}
+}
+
+static void vrfy_parents(struct basic_block *bb)
+{
+	struct basic_block *tmp;
+	FOR_EACH_PTR(bb->parents, tmp) {
+		vrfy_bb_in_list(bb, tmp->children);
+	} END_FOR_EACH_PTR(tmp);
+}
+
+static void vrfy_children(struct basic_block *bb)
+{
+	struct basic_block *tmp;
+	struct instruction *br = last_instruction(bb->insns);
+
+	if (!br) {
+		assert(!bb->children);
+		return;
+	}
+	switch (br->opcode) {
+		struct multijmp *jmp;
+	case OP_BR:
+		vrfy_bb_in_list(br->bb_true, bb->children);
+		vrfy_bb_in_list(br->bb_false, bb->children);
+		break;
+	case OP_SWITCH:
+	case OP_COMPUTEDGOTO:
+		FOR_EACH_PTR(br->multijmp_list, jmp) {
+			vrfy_bb_in_list(jmp->target, bb->children);
+		} END_FOR_EACH_PTR(jmp);
+		break;
+	default:
+		break;
+	}
+		
+	FOR_EACH_PTR(bb->children, tmp) {
+		vrfy_bb_in_list(bb, tmp->parents);
+	} END_FOR_EACH_PTR(tmp);
+}
+
+static void vrfy_bb_flow(struct basic_block *bb)
+{
+	vrfy_children(bb);
+	vrfy_parents(bb);
+}
+
+void vrfy_flow(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+	struct basic_block *entry = ep->entry->bb;
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		if (bb == entry)
+			entry = NULL;
+		vrfy_bb_flow(bb);
+	} END_FOR_EACH_PTR(bb);
+	assert(!entry);
+}
+
+void pack_basic_blocks(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+
+	/* See if we can merge a bb into another one.. */
+	FOR_EACH_PTR(ep->bbs, bb) {
+		struct instruction *first, *insn;
+		struct basic_block *parent, *child, *last;
+
+		if (!bb_reachable(bb))
+			continue;
+
+		/*
+		 * Just a branch?
+		 */
+		FOR_EACH_PTR(bb->insns, first) {
+			if (!first->bb)
+				continue;
+			switch (first->opcode) {
+			case OP_NOP: case OP_LNOP: case OP_SNOP:
+				continue;
+			case OP_BR: {
+				struct basic_block *replace;
+				replace = rewrite_branch_bb(bb, first);
+				if (replace) {
+					kill_bb(bb);
+					goto no_merge;
+				}
+			}
+			/* fallthrough */
+			default:
+				goto out;
+			}
+		} END_FOR_EACH_PTR(first);
+
+out:
+		/*
+		 * See if we only have one parent..
+		 */
+		last = NULL;
+		FOR_EACH_PTR(bb->parents, parent) {
+			if (last) {
+				if (last != parent)
+					goto no_merge;
+				continue;
+			}
+			last = parent;
+		} END_FOR_EACH_PTR(parent);
+
+		parent = last;
+		if (!parent || parent == bb)
+			continue;
+
+		/*
+		 * Goodie. See if the parent can merge..
+		 */
+		FOR_EACH_PTR(parent->children, child) {
+			if (child != bb)
+				goto no_merge;
+		} END_FOR_EACH_PTR(child);
+
+		/*
+		 * Merge the two.
+		 */
+		repeat_phase |= REPEAT_CSE;
+
+		parent->children = bb->children;
+		bb->children = NULL;
+		bb->parents = NULL;
+
+		FOR_EACH_PTR(parent->children, child) {
+			replace_bb_in_list(&child->parents, bb, parent, 0);
+		} END_FOR_EACH_PTR(child);
+
+		kill_instruction(delete_last_instruction(&parent->insns));
+		FOR_EACH_PTR(bb->insns, insn) {
+			if (insn->bb) {
+				assert(insn->bb == bb);
+				insn->bb = parent;
+			}
+			add_instruction(&parent->insns, insn);
+		} END_FOR_EACH_PTR(insn);
+		bb->insns = NULL;
+
+	no_merge:
+		/* nothing to do */;
+	} END_FOR_EACH_PTR(bb);
+}
+
+
diff --git a/deps/sparse/flow.h b/deps/sparse/flow.h
new file mode 100644
index 0000000..370aadd
--- /dev/null
+++ b/deps/sparse/flow.h
@@ -0,0 +1,42 @@
+#ifndef FLOW_H
+#define FLOW_H
+
+#include "lib.h"
+
+extern unsigned long bb_generation;
+
+#define REPEAT_CSE		1
+#define REPEAT_SYMBOL_CLEANUP	2
+
+struct entrypoint;
+struct instruction;
+
+extern int simplify_flow(struct entrypoint *ep);
+
+extern void simplify_symbol_usage(struct entrypoint *ep);
+extern void simplify_memops(struct entrypoint *ep);
+extern void pack_basic_blocks(struct entrypoint *ep);
+
+extern void convert_instruction_target(struct instruction *insn, pseudo_t src);
+extern void cleanup_and_cse(struct entrypoint *ep);
+extern int simplify_instruction(struct instruction *);
+
+extern void kill_bb(struct basic_block *);
+extern void kill_use(pseudo_t *);
+extern void kill_instruction(struct instruction *);
+extern void kill_unreachable_bbs(struct entrypoint *ep);
+
+void check_access(struct instruction *insn);
+void convert_load_instruction(struct instruction *, pseudo_t);
+void rewrite_load_instruction(struct instruction *, struct pseudo_list *);
+int dominates(pseudo_t pseudo, struct instruction *insn, struct instruction *dom, int local);
+
+extern void clear_liveness(struct entrypoint *ep);
+extern void track_pseudo_liveness(struct entrypoint *ep);
+extern void track_pseudo_death(struct entrypoint *ep);
+extern void track_phi_uses(struct instruction *insn);
+
+extern void vrfy_flow(struct entrypoint *ep);
+extern int pseudo_in_list(struct pseudo_list *list, pseudo_t pseudo);
+
+#endif
diff --git a/deps/sparse/gdbhelpers b/deps/sparse/gdbhelpers
new file mode 100644
index 0000000..8634786
--- /dev/null
+++ b/deps/sparse/gdbhelpers
@@ -0,0 +1,307 @@
+
+# Don't forget to rebuild sparse with uncommented debug options
+# in the Makefile. Also, gcc 3 is known to screw up with the
+# cpp macros in the debugging info.
+
+
+# If a gdb_show_* function is running at a non-zero recursion
+# level, only a short summary is shown, preventing further
+# recursion. Also note that gdb has only one, global, scope
+# for variables, so we need to be careful with recursions.
+
+
+set $showing_token = 0
+set $showing_ident = 0
+set $showing_symbol = 0
+
+set $ntabs = 0
+
+define gdb_tabs
+	set $tmp = $ntabs
+	while ($tmp--)
+		printf "\t"
+	end
+end
+
+
+# Ptr list handling
+define ptr_entry
+	set $ptr = $arg0
+	set $index = $arg1
+	set $entry = &($arg2)
+
+	set *($entry) = (void *) (~3UL & (unsigned long)$ptr->list[$index])
+end
+
+
+# Ptr list looping skeleton
+define gdb_ptr_list_for_each
+
+	set $my_head = (struct ptr_list *) $arg0
+	set $my_list = $my_head
+
+
+	if ($my_head)
+		while (1)
+			set $my_nr = 0
+			while ($my_nr < $my_list->nr)
+
+				# Put your iterator code here
+				set $my_nr++
+			end
+
+			if (($my_list = $my_list->next) == $my_head)
+				loop_break
+			end
+		end
+	end
+end
+
+# Show symbols in a symbol_list. Non-recursive
+define gdb_ptr_list_for_each_show_symbol
+
+	set $my_head = (struct ptr_list *) $arg0
+	set $my_list = $my_head
+
+
+	if ($my_head)
+		while (1)
+			set $my_nr = 0
+			while ($my_nr < ($my_list)->nr)
+				set $my_symbol = (struct symbol *) ((~3UL) & (unsigned long)($my_list)->list[$my_nr])
+				gdb_show_symbol($my_symbol)
+
+				set $my_nr++
+			end
+
+			set $my_list = ($my_list)->next
+			if ($my_list == $my_head)
+				loop_break
+			end
+		end
+	end
+end
+
+
+#define gdb_show_statement
+
+
+# Recursive
+define gdb_show_ctype
+	printf "modifiers: "
+	if ($arg0->modifiers & MOD_AUTO)
+		printf "MOD_AUTO "
+	end
+	if ($arg0->modifiers & MOD_REGISTER)
+		printf "MOD_REGISTER "
+	end
+	if ($arg0->modifiers & MOD_STATIC)
+		printf "MOD_STATIC "
+	end
+	if ($arg0->modifiers & MOD_EXTERN)
+		printf "MOD_EXTERN "
+	end
+	if ($arg0->modifiers & MOD_CONST)
+		printf "MOD_CONST "
+	end
+	if ($arg0->modifiers & MOD_VOLATILE)
+		printf "MOD_VOLATILE "
+	end
+	if ($arg0->modifiers & MOD_SIGNED)
+		printf "MOD_SIGNED "
+	end
+	if ($arg0->modifiers & MOD_UNSIGNED)
+		printf "MOD_UNSIGNED "
+	end
+	if ($arg0->modifiers & MOD_CHAR)
+		printf "MOD_CHAR "
+	end
+	if ($arg0->modifiers & MOD_SHORT)
+		printf "MOD_SHORT "
+	end
+	if ($arg0->modifiers & MOD_LONG)
+		printf "MOD_LONG "
+	end
+	if ($arg0->modifiers & MOD_LONGLONG)
+		printf "MOD_LONGLONG "
+	end
+	if ($arg0->modifiers & MOD_LONGLONGLONG)
+		printf "MOD_LONGLONGLONG "
+	end
+	if ($arg0->modifiers & MOD_TYPEDEF)
+		printf "MOD_TYPEDEF "
+	end
+	if ($arg0->modifiers & MOD_INLINE)
+		printf "MOD_INLINE "
+	end
+	if ($arg0->modifiers & MOD_ADDRESSABLE)
+		printf "MOD_ADDRESSABLE "
+	end
+	if ($arg0->modifiers & MOD_NOCAST)
+		printf "MOD_NOCAST "
+	end
+	if ($arg0->modifiers & MOD_NODEREF)
+		printf "MOD_NODEREF "
+	end
+	if ($arg0->modifiers & MOD_ACCESSED)
+		printf "MOD_ACCESSED "
+	end
+	if ($arg0->modifiers & MOD_TOPLEVEL)
+		printf "MOD_TOPLEVEL "
+	end
+	if ($arg0->modifiers & MOD_ASSIGNED)
+		printf "MOD_ASSIGNED "
+	end
+	if ($arg0->modifiers & MOD_TYPE)
+		printf "MOD_TYPE "
+	end
+	if ($arg0->modifiers & MOD_SAFE)
+		printf "MOD_SAFE "
+	end
+	if ($arg0->modifiers & MOD_USERTYPE)
+		printf "MOD_USERTYPE "
+	end
+	if ($arg0->modifiers & MOD_EXPLICITLY_SIGNED)
+		printf "MOD_EXPLICITLY_SIGNED"
+	end
+	if ($arg0->modifiers & MOD_BITWISE)
+		printf "MOD_BITWISE "
+	end
+	if (!$arg0->modifiers)
+		printf "0"
+	end
+
+	printf ", alignment = %d", $arg0->alignment
+	if ($arg0->as)
+		printf ", address_space = %d", $arg0->as
+	end
+	printf "\n"
+
+
+	set $ntabs++
+	if ($arg0->base_type)
+		gdb_tabs
+		printf "base_type = "
+		gdb_show_symbol($arg0->base_type)
+	end
+
+	set $ntabs--
+
+
+end
+
+define gdb_show_symbol
+	printf "(%x) type = ", $arg0
+	output $arg0->type
+	printf ", namespace = "
+	output $arg0->namespace
+	if ($arg0->ident)
+		printf ", ident = %s\n", show_ident($arg0->ident)
+	else
+		printf ", ident = NULL\n"
+	end
+
+#	print "zz"
+
+	gdb_tabs
+	printf "dump:\n"
+	call show_symbol($arg0)
+
+	set $ntabs++
+	if ($arg0->namespace == NS_SYMBOL)
+		gdb_tabs
+		printf "ctype = "
+		gdb_show_ctype(&($arg0->ctype))
+	end
+	set $ntabs--
+end
+
+
+# non-recursive
+define gdb_show_symbols_next_id
+	set $sym = $arg0
+	printf "{\n"
+	set $ntabs++
+	while ($sym)
+		gdb_tabs
+		printf "symbol = "
+		gdb_show_symbol($sym)
+		set $sym = $sym->next_id
+	end
+	set $ntabs--
+	gdb_tabs
+	printf "}\n"
+end
+
+define gdb_show_ident
+	if ($arg0)
+		printf "(%p) '%s'\n", $arg0, show_ident($arg0)
+	else
+		printf "NULL\n"
+	end
+
+	if (! $showing_ident)
+		set $showing_ident = 1
+		set $ntabs++
+
+		set $ident = $arg0
+
+		if ($ident->symbols)
+			gdb_tabs
+			printf "symbols = "
+			gdb_show_symbols_next_id($ident->symbols)
+		end
+
+		set $ntabs--
+		set $showing_ident = 0
+	end
+end
+
+define gdb_show_token
+	printf "%p: '%s', type = ", $arg0, show_token($arg0)
+	output (enum token_type) ($arg0)->pos.type
+	printf "\n"
+
+	if (! $showing_token)
+		set $showing_token = 1
+		set $ntabs++
+
+		set $token = $arg0
+
+		if ($token->pos.type == TOKEN_IDENT)
+			gdb_tabs
+			printf "ident = "
+			gdb_show_ident $token.ident
+		end
+
+		if ($token->pos.type == TOKEN_MACRO_ARGUMENT)
+			gdb_tabs
+			printf "argnum = %d\n", $token->argnum
+		end
+
+		if ($token->pos.type == TOKEN_SPECIAL)
+			gdb_tabs
+			printf "special = \"%s\"\n", show_special($token.special)
+		end
+
+		set $ntabs--
+		set $showing_token = 0
+	end
+end
+
+# non-recursive
+define gdb_show_tokens
+	set $t = $arg0
+	printf "{\n"
+	set $ntabs++
+	while ($t != &eof_token_entry)
+		gdb_tabs
+		printf "token = "
+		gdb_show_token($t)
+		set $t = ($t)->next
+	end
+	set $ntabs--
+	gdb_tabs
+	printf "}\n"
+end
+
diff --git a/deps/sparse/graph.c b/deps/sparse/graph.c
new file mode 100644
index 0000000..3633783
--- /dev/null
+++ b/deps/sparse/graph.c
@@ -0,0 +1,186 @@
+/* Copyright  International Business Machines Corp., 2006
+ *              Adelard LLP, 2007
+ *
+ * Author: Josh Triplett <josh freedesktop org>
+ *         Dan Sheridan <djs adelard com>
+ *
+ * Licensed under the Open Software License version 1.1
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "expression.h"
+#include "linearize.h"
+
+
+/* Draw the subgraph for a given entrypoint. Includes details of loads
+ * and stores for globals, and marks return bbs */
+static void graph_ep(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+	struct instruction *insn;
+
+	const char *fname, *sname;
+
+	fname = show_ident(ep->name->ident);
+	sname = stream_name(ep->entry->bb->pos.stream);
+
+	printf("subgraph cluster%p {\n"
+	       "    color=blue;\n"
+	       "    label=<<TABLE BORDER=\"0\" CELLBORDER=\"0\">\n"
+	       "             <TR><TD>%s</TD></TR>\n"
+	       "             <TR><TD><FONT POINT-SIZE=\"21\">%s()</FONT></TD></TR>\n"
+	       "           </TABLE>>;\n"
+	       "    file=\"%s\";\n"
+	       "    fun=\"%s\";\n"
+	       "    ep=bb%p;\n",
+	       ep, sname, fname, sname, fname, ep->entry->bb);
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		struct basic_block *child;
+		int ret = 0;
+		const char * s = ", ls=\"[";
+
+		/* Node for the bb */
+		printf("    bb%p [shape=ellipse,label=%d,line=%d,col=%d",
+		       bb, bb->pos.line, bb->pos.line, bb->pos.pos);
+
+
+		/* List loads and stores */
+		FOR_EACH_PTR(bb->insns, insn) {
+			switch(insn->opcode) {
+			case OP_STORE:
+				if (insn->symbol->type == PSEUDO_SYM) {
+				  printf("%s store(%s)", s, show_ident(insn->symbol->sym->ident));
+				  s = ",";
+				}
+				break;
+
+			case OP_LOAD:
+				if (insn->symbol->type == PSEUDO_SYM) {
+				  printf("%s load(%s)", s, show_ident(insn->symbol->sym->ident));
+				  s = ",";
+				}
+				break;
+
+			case OP_RET:
+				ret = 1;
+				break;
+
+			}
+		} END_FOR_EACH_PTR(insn);
+		if (s[1] == 0)
+			printf("]\"");
+		if (ret)
+			printf(",op=ret");
+		printf("];\n");
+
+		/* Edges between bbs; lower weight for upward edges */
+		FOR_EACH_PTR(bb->children, child) {
+			printf("    bb%p -> bb%p [op=br, %s];\n", bb, child,
+			       (bb->pos.line > child->pos.line) ? "weight=5" : "weight=10");
+		} END_FOR_EACH_PTR(child);
+	} END_FOR_EACH_PTR(bb);
+
+	printf("}\n");
+}
+
+
+/* Insert edges for intra- or inter-file calls, depending on the value
+ * of internal. Bold edges are used for calls with destinations;
+ * dashed for calls to external functions */
+static void graph_calls(struct entrypoint *ep, int internal)
+{
+	struct basic_block *bb;
+	struct instruction *insn;
+
+	show_ident(ep->name->ident);
+	stream_name(ep->entry->bb->pos.stream);
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		if (!bb)
+			continue;
+		if (!bb->parents && !bb->children && !bb->insns && verbose < 2)
+			continue;
+
+		FOR_EACH_PTR(bb->insns, insn) {
+			if (insn->opcode == OP_CALL &&
+			    internal == !(insn->func->sym->ctype.modifiers & MOD_EXTERN)) {
+
+				/* Find the symbol for the callee's definition */
+				struct symbol * sym;
+				if (insn->func->type == PSEUDO_SYM) {
+					for (sym = insn->func->sym->ident->symbols;
+					     sym; sym = sym->next_id) {
+						if (sym->namespace & NS_SYMBOL && sym->ep)
+							break;
+					}
+
+					if (sym)
+						printf("bb%p -> bb%p"
+						       "[label=%d,line=%d,col=%d,op=call,style=bold,weight=30];\n",
+						       bb, sym->ep->entry->bb,
+						       insn->pos.line, insn->pos.line, insn->pos.pos);
+					else
+						printf("bb%p -> \"%s\" "
+						       "[label=%d,line=%d,col=%d,op=extern,style=dashed];\n",
+						       bb, show_pseudo(insn->func),
+						       insn->pos.line, insn->pos.line, insn->pos.pos);
+				}
+			}
+		} END_FOR_EACH_PTR(insn);
+	} END_FOR_EACH_PTR(bb);
+}
+
+int main(int argc, char **argv)
+{
+	struct string_list *filelist = NULL;
+	char *file;
+	struct symbol *sym;
+
+	struct symbol_list *fsyms, *all_syms=NULL;
+
+	printf("digraph call_graph {\n");
+	fsyms = sparse_initialize(argc, argv, &filelist);
+	concat_symbol_list(fsyms, &all_syms);
+
+	/* Linearize all symbols, graph internal basic block
+	 * structures and intra-file calls */
+	FOR_EACH_PTR_NOTAG(filelist, file) {
+
+		fsyms = sparse(file);
+		concat_symbol_list(fsyms, &all_syms);
+
+		FOR_EACH_PTR(fsyms, sym) {
+			expand_symbol(sym);
+			linearize_symbol(sym);
+		} END_FOR_EACH_PTR(sym);
+
+		FOR_EACH_PTR(fsyms, sym) {
+			if (sym->ep) {
+				graph_ep(sym->ep);
+				graph_calls(sym->ep, 1);
+			}
+		} END_FOR_EACH_PTR_NOTAG(sym);
+
+	} END_FOR_EACH_PTR_NOTAG(file);
+
+	/* Graph inter-file calls */
+	FOR_EACH_PTR(all_syms, sym) {
+		if (sym->ep)
+			graph_calls(sym->ep, 0);
+	} END_FOR_EACH_PTR_NOTAG(sym);
+
+	printf("}\n");
+	return 0;
+}
diff --git a/deps/sparse/gvpr/return-paths b/deps/sparse/gvpr/return-paths
new file mode 100755
index 0000000..5815fc7
--- /dev/null
+++ b/deps/sparse/gvpr/return-paths
@@ -0,0 +1,107 @@
+#!/usr/bin/gvpr -f
+// Split call sites into call site and return site nodes and add
+// return edges
+//
+// Run with graph ... | return-paths
+
+BEGIN {
+	// Find the immediate parent subgraph of this object
+	graph_t find_owner(obj_t o, graph_t g)
+	{
+		graph_t g1;
+		for (g1 = fstsubg(g); g1; g1 = nxtsubg(g1))
+			if(isIn(g1,o)) return g1;
+		return NULL;
+	}
+}
+
+BEG_G {
+	node_t calls[]; // Crude hash table for tracking who calls what
+	graph_t g,g2;
+	edge_t e,e2;
+	string idx;
+	node_t n, n2;
+	int i;
+
+	$tvtype = TV_en;
+}
+
+// Each call edge which hasn't already been seen
+E [op == "call" && tail.split != 1] {
+	int offset=0;
+
+	// Clear the label of this call
+	label = "";
+	g = find_owner(tail, $G);
+
+	// Consider each outgoing call. Split the node accordingly
+	n = tail;
+	for (e = fstout(tail); e; e = nxtout(e)) {
+		if (e.op == "call") {
+
+			// Split node
+			n2 = node(g, sprintf("%s%s%d", tail.name, "x", offset++));
+			copyA(tail, n2);
+			n2.line = e.line;
+			n2.label = e.line;
+			n2.col = e.col;
+			n2.split = 1;
+			n2.op = "target";
+
+			// Record this call
+			g2 = find_owner(e.head, $G);
+			i = 0;
+			while(calls[sprintf("%s%d", g2.name, ++i)]) {
+			}
+			calls[sprintf("%s%d", g2.name, i)] = n2;
+
+			// Connect original to split
+			e2 = edge(n, n2, "call");
+			e2.style = "dotted";
+			e2.weight = 50;
+
+			// Replace this outedge
+			if (n != tail) {
+				e2 = edge(n, e.head, "transformed-call");
+				copyA(e,e2);
+				e2.label = "";
+				delete($G,e);
+			}
+
+			// Record where we were
+			n = n2;
+		}
+	}
+
+	// Consider the outgoing control flow: move down to the bottom of
+	// the call sequence nodes
+	for (e = fstout(tail); e; e = nxtout(e)) {
+		if (e.op == "br") {
+			// Replace this outedge
+			e2 = edge(n,e.head,"transformed");
+			copyA(e,e2);
+			delete($G,e);
+		}
+	}
+}
+
+// Each return node: add edges back to the caller
+N [op == "ret"] {
+	for (g = fstsubg($G); g; g = nxtsubg(g)) {
+		if(isIn(g,$)) {
+			i = 0;
+			while(calls[sprintf("%s%d", g.name, ++i)]) {
+				e = edge($, calls[sprintf("%s%d", g.name, i)], "return");
+				e.style = "dotted";
+				e.op = "ret";
+				e.line = e.tail.line;
+				e.weight = 5;
+			}
+		}
+	}
+}
+
+
+END_G {
+	write($);
+}
diff --git a/deps/sparse/gvpr/subg-fwd b/deps/sparse/gvpr/subg-fwd
new file mode 100755
index 0000000..c407213
--- /dev/null
+++ b/deps/sparse/gvpr/subg-fwd
@@ -0,0 +1,79 @@
+#!/usr/bin/gvpr -f
+// Compute the forward partition of the chosen function
+//
+// Run with graph ... | return-paths | subg-fwd -a functionname
+//       or graph ... | subg-fwd
+
+
+BEGIN {
+	// Find the immediate parent subgraph of this object
+	graph_t find_owner(obj_t o, graph_t g)
+	{
+		graph_t g1;
+		for (g1 = fstsubg(g); g1; g1 = nxtsubg(g1))
+			if(isIn(g1,o)) return g1;
+		return NULL;
+	}
+}
+
+BEG_G {
+	graph_t sg = subg ($, sprintf("incoming-%s", ARGV[0]));
+	graph_t returns = graph("return-edges", ""); // Temporary graph to hold return edges
+	graph_t target, g, g2;
+	node_t n;
+	edge_t e;
+	int i;
+
+	$tvtype = TV_fwd;
+
+	// find the ep corresponding to ARG[0]
+	for (g = fstsubg($G); g; g = nxtsubg(g)) {
+		if(g.fun == ARGV[0]) {
+			n = node($,g.ep);
+			$tvroot = n;
+			n.style = "filled";
+			target = g;
+			break;
+		}
+	}
+	if(!target) {
+		printf(2, "Function %s not found\n", ARGV[0]);
+		exit(1);
+	}
+}
+
+// Preserve external functions
+E [op == "extern"] {
+	subnode (sg, head);
+}
+
+// Move unused return edges into a separate graph so they don't get followed
+N [op == "ret"] {
+	for (e = fstout($); e; e = nxtout(e))
+		if (e.op == "ret" && !isIn(sg, e.head)) {
+			clone(returns, e);
+			delete($G, e);
+		}
+}
+
+// Recover elided return edge for this target node
+N [op == "target" && indegree == 1] {
+	n = copy(returns, $);
+	e = fstin(n); // each target node can only have one return edge
+	e = edge(copy(sg, e.tail), $, "recovered"); // clone should work here, but doesn't
+	copyA(fstin(n), e);
+}
+
+// Copy relevant nodes
+N {
+	$tvroot = NULL;
+
+	g = find_owner($, $G);
+	if(g && g != sg)
+		subnode (copy(sg, g), $);
+}
+
+END_G {
+	induce(sg);
+	write(sg);
+}
diff --git a/deps/sparse/gvpr/subg-rev b/deps/sparse/gvpr/subg-rev
new file mode 100755
index 0000000..cd9bddd
--- /dev/null
+++ b/deps/sparse/gvpr/subg-rev
@@ -0,0 +1,101 @@
+#!/usr/bin/gvpr -f
+// Compute the reverse partition of the chosen function
+//
+// Run with graph ... | return-paths | subg-rev -a functionname
+
+
+BEGIN {
+	// Find the immediate parent subgraph of this object
+	graph_t find_owner(obj_t o, graph_t g)
+	{
+		graph_t g1;
+		for (g1 = fstsubg(g); g1; g1 = nxtsubg(g1))
+			if(isIn(g1,o)) return g1;
+		return NULL;
+	}
+}
+
+BEG_G {
+	graph_t calls[]; // Crude hash table for tracking who calls what
+	graph_t sg = subg ($, "reachable");
+	graph_t target, g, g2;
+	edge_t e;
+	int i;
+
+	$tvtype = TV_rev;
+
+	// find the ep corresponding to ARG[0]
+	for (g = fstsubg($G); g; g = nxtsubg(g)) {
+		if(g.fun == ARGV[0]) {
+			node_t n;
+			n = node($,g.ep);
+			$tvroot = n;
+			n.style = "filled";
+			target = g;
+			break;
+		}
+	}
+	if(!target) {
+		printf(2, "Function %s not found\n", ARGV[0]);
+		exit(1);
+	}
+
+	// Add the incoming call edges to the allowed call list
+	i = 0;
+	for(e = fstin(n); e; e = nxtin(e)) {
+		if (e.op = "call") {
+			g2 = find_owner(e.tail, $G);
+			calls[sprintf("%s%d", g2.name, ++i)] = g;
+		}
+	}
+}
+
+
+E [op == "ret"] {
+
+	// This is a return edge. Allow the corresponding call
+	g = find_owner(head, $G);
+	i = 0;
+	while(calls[sprintf("%s%d", g.name, ++i)]) {
+	}
+	calls[sprintf("%s%d", g.name, i)] = find_owner(tail, $G);
+	g2 = find_owner(tail, $G);
+}
+
+
+N [split == 1] {
+
+	// Ignore returns back to the target function
+	for (e = fstin($); e; e = nxtin(e))
+		if (e.op == "ret" && isIn(target,e.tail))
+			delete($G,e);
+}
+
+N {
+	$tvroot = NULL;
+
+	for (e = fstin($); e; e = nxtin(e)) {
+		if (e.op == "call") {
+			int found = 0;
+			g = find_owner(e.tail, $G);
+			i = 0;
+			while(calls[sprintf("%s%d", g.name, ++i)]) {
+				if (isIn(calls[sprintf("%s%d", g.name, i)],e.head))
+					found = 1;
+			}
+			g2 = find_owner(e.head, $G);
+			if (!found) delete($G, e);
+		}
+	}
+
+	for (g = fstsubg($G); g; g = nxtsubg(g)) {
+		if(isIn(g,$) && g != sg) {
+			subnode (copy(sg, g), $);
+		}
+	}
+}
+
+END_G {
+	induce(sg);
+	write(sg);
+}
diff --git a/deps/sparse/ident-list.h b/deps/sparse/ident-list.h
new file mode 100644
index 0000000..b12d172
--- /dev/null
+++ b/deps/sparse/ident-list.h
@@ -0,0 +1,117 @@
+
+#define IDENT(n) __IDENT(n## _ident, #n, 0)
+#define IDENT_RESERVED(n) __IDENT(n## _ident, #n, 1)
+
+/* Basic C reserved words.. */
+IDENT_RESERVED(sizeof);
+IDENT_RESERVED(if);
+IDENT_RESERVED(else);
+IDENT_RESERVED(return);
+IDENT_RESERVED(switch);
+IDENT_RESERVED(case);
+IDENT_RESERVED(default);
+IDENT_RESERVED(break);
+IDENT_RESERVED(continue);
+IDENT_RESERVED(for);
+IDENT_RESERVED(while);
+IDENT_RESERVED(do);
+IDENT_RESERVED(goto);
+
+/* C typenames. They get marked as reserved when initialized */
+IDENT(struct);
+IDENT(union);
+IDENT(enum);
+IDENT(__attribute); IDENT(__attribute__);
+IDENT(volatile); IDENT(__volatile); IDENT(__volatile__);
+IDENT(double);
+
+/* Special case for L'\t' */
+IDENT(L);
+
+/* Extended gcc identifiers */
+IDENT(asm); IDENT_RESERVED(__asm); IDENT_RESERVED(__asm__);
+IDENT(alignof); IDENT_RESERVED(__alignof); IDENT_RESERVED(__alignof__); 
+IDENT_RESERVED(__sizeof_ptr__);
+IDENT_RESERVED(__builtin_types_compatible_p);
+IDENT_RESERVED(__builtin_offsetof);
+IDENT_RESERVED(__label__);
+
+/* Attribute names */
+IDENT(packed); IDENT(__packed__);
+IDENT(aligned); IDENT(__aligned__);
+IDENT(nocast);
+IDENT(noderef);
+IDENT(safe);
+IDENT(force);
+IDENT(address_space);
+IDENT(context);
+IDENT(mode); IDENT(__mode__);
+IDENT(QI); IDENT(__QI__);
+IDENT(HI); IDENT(__HI__);
+IDENT(SI); IDENT(__SI__);
+IDENT(DI); IDENT(__DI__);
+IDENT(word); IDENT(__word__);
+IDENT(format); IDENT(__format__);
+IDENT(section); IDENT(__section__);
+IDENT(unused); IDENT(__unused__);
+IDENT(const); IDENT(__const); IDENT(__const__);
+IDENT(used); IDENT(__used__);
+IDENT(warn_unused_result); IDENT(__warn_unused_result__);
+IDENT(noinline); IDENT(__noinline__);
+IDENT(deprecated); IDENT(__deprecated__);
+IDENT(noreturn); IDENT(__noreturn__);
+IDENT(regparm); IDENT(__regparm__);
+IDENT(weak); IDENT(__weak__);
+IDENT(no_instrument_function); IDENT(__no_instrument_function__);
+IDENT(sentinel); IDENT(__sentinel__);
+IDENT(alias); IDENT(__alias__);
+IDENT(pure); IDENT(__pure__);
+IDENT(always_inline); IDENT(__always_inline__);
+IDENT(syscall_linkage); IDENT(__syscall_linkage__);
+IDENT(visibility); IDENT(__visibility__);
+IDENT(bitwise); IDENT(__bitwise__);
+IDENT(model); IDENT(__model__);
+IDENT(format_arg); IDENT(__format_arg__);
+IDENT(nothrow); IDENT(__nothrow); IDENT(__nothrow__);
+IDENT(__transparent_union__);
+IDENT(malloc);
+IDENT(__malloc__);
+IDENT(nonnull); IDENT(__nonnull); IDENT(__nonnull__);
+IDENT(constructor); IDENT(__constructor__);
+IDENT(destructor); IDENT(__destructor__);
+IDENT(cold); IDENT(__cold__);
+IDENT(hot); IDENT(__hot__);
+IDENT(cdecl); IDENT(__cdecl__);
+IDENT(stdcall); IDENT(__stdcall__);
+IDENT(fastcall); IDENT(__fastcall__);
+IDENT(dllimport); IDENT(__dllimport__);
+IDENT(dllexport); IDENT(__dllexport__);
+IDENT(restrict); IDENT(__restrict);
+IDENT(artificial); IDENT(__artificial__);
+
+/* Preprocessor idents.  Direct use of __IDENT avoids mentioning the keyword
+ * itself by name, preventing these tokens from expanding when compiling
+ * sparse. */
+IDENT(defined);
+__IDENT(pragma_ident, "__pragma__", 0);
+__IDENT(__VA_ARGS___ident, "__VA_ARGS__", 0);
+__IDENT(__LINE___ident, "__LINE__", 0);
+__IDENT(__FILE___ident, "__FILE__", 0);
+__IDENT(__DATE___ident, "__DATE__", 0);
+__IDENT(__TIME___ident, "__TIME__", 0);
+__IDENT(__func___ident, "__func__", 0);
+__IDENT(__FUNCTION___ident, "__FUNCTION__", 0);
+__IDENT(__PRETTY_FUNCTION___ident, "__PRETTY_FUNCTION__", 0);
+
+/* Sparse commands */
+IDENT_RESERVED(__context__);
+IDENT_RESERVED(__range__);
+
+/* Magic function names we recognize */
+IDENT(memset); IDENT(memcpy);
+IDENT(copy_to_user); IDENT(copy_from_user);
+IDENT(main);
+
+#undef __IDENT
+#undef IDENT
+#undef IDENT_RESERVED
diff --git a/deps/sparse/inline.c b/deps/sparse/inline.c
new file mode 100644
index 0000000..9ed4570
--- /dev/null
+++ b/deps/sparse/inline.c
@@ -0,0 +1,569 @@
+/*
+ * Sparse - a semantic source parser.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "expression.h"
+
+static struct expression * dup_expression(struct expression *expr)
+{
+	struct expression *dup = alloc_expression(expr->pos, expr->type);
+	*dup = *expr;
+	return dup;
+}
+
+static struct statement * dup_statement(struct statement *stmt)
+{
+	struct statement *dup = alloc_statement(stmt->pos, stmt->type);
+	*dup = *stmt;
+	return dup;
+}
+
+static struct symbol *copy_symbol(struct position pos, struct symbol *sym)
+{
+	if (!sym)
+		return sym;
+	if (sym->ctype.modifiers & (MOD_STATIC | MOD_EXTERN | MOD_TOPLEVEL | MOD_INLINE))
+		return sym;
+	if (!sym->replace) {
+		warning(pos, "unreplaced symbol '%s'", show_ident(sym->ident));
+		return sym;
+	}
+	return sym->replace;
+}
+
+static struct symbol_list *copy_symbol_list(struct symbol_list *src)
+{
+	struct symbol_list *dst = NULL;
+	struct symbol *sym;
+
+	FOR_EACH_PTR(src, sym) {
+		struct symbol *newsym = copy_symbol(sym->pos, sym);
+		add_symbol(&dst, newsym);
+	} END_FOR_EACH_PTR(sym);
+	return dst;
+}
+
+static struct expression * copy_expression(struct expression *expr)
+{
+	if (!expr)
+		return NULL;
+
+	switch (expr->type) {
+	/*
+	 * EXPR_SYMBOL is the interesting case, we may need to replace the
+	 * symbol to the new copy.
+	 */
+	case EXPR_SYMBOL: {
+		struct symbol *sym = copy_symbol(expr->pos, expr->symbol);
+		if (sym == expr->symbol)
+			break;
+		expr = dup_expression(expr);
+		expr->symbol = sym;
+		break;
+	}
+
+	/* Atomics, never change, just return the expression directly */
+	case EXPR_VALUE:
+	case EXPR_STRING:
+	case EXPR_FVALUE:
+	case EXPR_TYPE:
+		break;
+
+	/* Unops: check if the subexpression is unique */
+	case EXPR_PREOP:
+	case EXPR_POSTOP: {
+		struct expression *unop = copy_expression(expr->unop);
+		if (expr->unop == unop)
+			break;
+		expr = dup_expression(expr);
+		expr->unop = unop;
+		break;
+	}
+
+	case EXPR_SLICE: {
+		struct expression *base = copy_expression(expr->base);
+		expr = dup_expression(expr);
+		expr->base = base;
+		break;
+	}
+
+	/* Binops: copy left/right expressions */
+	case EXPR_BINOP:
+	case EXPR_COMMA:
+	case EXPR_COMPARE:
+	case EXPR_LOGICAL: {
+		struct expression *left = copy_expression(expr->left);
+		struct expression *right = copy_expression(expr->right);
+		if (left == expr->left && right == expr->right)
+			break;
+		expr = dup_expression(expr);
+		expr->left = left;
+		expr->right = right;
+		break;
+	}
+
+	case EXPR_ASSIGNMENT: {
+		struct expression *left = copy_expression(expr->left);
+		struct expression *right = copy_expression(expr->right);
+		if (expr->op == '=' && left == expr->left && right == expr->right)
+			break;
+		expr = dup_expression(expr);
+		expr->left = left;
+		expr->right = right;
+		break;
+	}
+
+	/* Dereference */
+	case EXPR_DEREF: {
+		struct expression *deref = copy_expression(expr->deref);
+		expr = dup_expression(expr);
+		expr->deref = deref;
+		break;
+	}
+
+	/* Cast/sizeof/__alignof__ */
+	case EXPR_CAST:
+		if (expr->cast_expression->type == EXPR_INITIALIZER) {
+			struct expression *cast = expr->cast_expression;
+			struct symbol *sym = expr->cast_type;
+			expr = dup_expression(expr);
+			expr->cast_expression = copy_expression(cast);
+			expr->cast_type = alloc_symbol(sym->pos, sym->type);
+			*expr->cast_type = *sym;
+			break;
+		}
+	case EXPR_FORCE_CAST:
+	case EXPR_IMPLIED_CAST:
+	case EXPR_SIZEOF: 
+	case EXPR_PTRSIZEOF:
+	case EXPR_ALIGNOF: {
+		struct expression *cast = copy_expression(expr->cast_expression);
+		if (cast == expr->cast_expression)
+			break;
+		expr = dup_expression(expr);
+		expr->cast_expression = cast;
+		break;
+	}
+
+	/* Conditional expression */
+	case EXPR_SELECT:
+	case EXPR_CONDITIONAL: {
+		struct expression *cond = copy_expression(expr->conditional);
+		struct expression *true = copy_expression(expr->cond_true);
+		struct expression *false = copy_expression(expr->cond_false);
+		if (cond == expr->conditional && true == expr->cond_true && false == expr->cond_false)
+			break;
+		expr = dup_expression(expr);
+		expr->conditional = cond;
+		expr->cond_true = true;
+		expr->cond_false = false;
+		break;
+	}
+
+	/* Statement expression */
+	case EXPR_STATEMENT: {
+		struct statement *stmt = alloc_statement(expr->pos, STMT_COMPOUND);
+		copy_statement(expr->statement, stmt);
+		expr = dup_expression(expr);
+		expr->statement = stmt;
+		break;
+	}
+
+	/* Call expression */
+	case EXPR_CALL: {
+		struct expression *fn = copy_expression(expr->fn);
+		struct expression_list *list = expr->args;
+		struct expression *arg;
+
+		expr = dup_expression(expr);
+		expr->fn = fn;
+		expr->args = NULL;
+		FOR_EACH_PTR(list, arg) {
+			add_expression(&expr->args, copy_expression(arg));
+		} END_FOR_EACH_PTR(arg);
+		break;
+	}
+
+	/* Initializer list statement */
+	case EXPR_INITIALIZER: {
+		struct expression_list *list = expr->expr_list;
+		struct expression *entry;
+		expr = dup_expression(expr);
+		expr->expr_list = NULL;
+		FOR_EACH_PTR(list, entry) {
+			add_expression(&expr->expr_list, copy_expression(entry));
+		} END_FOR_EACH_PTR(entry);
+		break;
+	}
+
+	/* Label in inline function - hmm. */
+	case EXPR_LABEL: {
+		struct symbol *label_symbol = copy_symbol(expr->pos, expr->label_symbol);
+		expr = dup_expression(expr);
+		expr->label_symbol = label_symbol;
+		break;
+	}
+
+	case EXPR_INDEX: {
+		struct expression *sub_expr = copy_expression(expr->idx_expression);
+		expr = dup_expression(expr);
+		expr->idx_expression = sub_expr;
+		break;
+	}
+		
+	case EXPR_IDENTIFIER: {
+		struct expression *sub_expr = copy_expression(expr->ident_expression);
+		expr = dup_expression(expr);
+		expr->ident_expression = sub_expr;
+		break;
+	}
+
+	/* Position in initializer.. */
+	case EXPR_POS: {
+		struct expression *val = copy_expression(expr->init_expr);
+		expr = dup_expression(expr);
+		expr->init_expr = val;
+		break;
+	}
+	case EXPR_OFFSETOF: {
+		struct expression *val = copy_expression(expr->down);
+		if (expr->op == '.') {
+			if (expr->down != val) {
+				expr = dup_expression(expr);
+				expr->down = val;
+			}
+		} else {
+			struct expression *idx = copy_expression(expr->index);
+			if (expr->down != val || expr->index != idx) {
+				expr = dup_expression(expr);
+				expr->down = val;
+				expr->index = idx;
+			}
+		}
+		break;
+	}
+	default:
+		warning(expr->pos, "trying to copy expression type %d", expr->type);
+	}
+	return expr;
+}
+
+static struct expression_list *copy_asm_constraints(struct expression_list *in)
+{
+	struct expression_list *out = NULL;
+	struct expression *expr;
+	int state = 0;
+
+	FOR_EACH_PTR(in, expr) {
+		switch (state) {
+		case 0: /* identifier */
+		case 1: /* constraint */
+			state++;
+			add_expression(&out, expr);
+			continue;
+		case 2: /* expression */
+			state = 0;
+			add_expression(&out, copy_expression(expr));
+			continue;
+		}
+	} END_FOR_EACH_PTR(expr);
+	return out;
+}
+
+static void set_replace(struct symbol *old, struct symbol *new)
+{
+	new->replace = old;
+	old->replace = new;
+}
+
+static void unset_replace(struct symbol *sym)
+{
+	struct symbol *r = sym->replace;
+	if (!r) {
+		warning(sym->pos, "symbol '%s' not replaced?", show_ident(sym->ident));
+		return;
+	}
+	r->replace = NULL;
+	sym->replace = NULL;
+}
+
+static void unset_replace_list(struct symbol_list *list)
+{
+	struct symbol *sym;
+	FOR_EACH_PTR(list, sym) {
+		unset_replace(sym);
+	} END_FOR_EACH_PTR(sym);
+}
+
+static struct statement *copy_one_statement(struct statement *stmt)
+{
+	if (!stmt)
+		return NULL;
+	switch(stmt->type) {
+	case STMT_NONE:
+		break;
+	case STMT_DECLARATION: {
+		struct symbol *sym;
+		struct statement *newstmt = dup_statement(stmt);
+		newstmt->declaration = NULL;
+		FOR_EACH_PTR(stmt->declaration, sym) {
+			struct symbol *newsym = copy_symbol(stmt->pos, sym);
+			if (newsym != sym)
+				newsym->initializer = copy_expression(sym->initializer);
+			add_symbol(&newstmt->declaration, newsym);
+		} END_FOR_EACH_PTR(sym);
+		stmt = newstmt;
+		break;
+	}
+	case STMT_CONTEXT:
+	case STMT_EXPRESSION: {
+		struct expression *expr = copy_expression(stmt->expression);
+		if (expr == stmt->expression)
+			break;
+		stmt = dup_statement(stmt);
+		stmt->expression = expr;
+		break;
+	}
+	case STMT_RANGE: {
+		struct expression *expr = copy_expression(stmt->range_expression);
+		if (expr == stmt->expression)
+			break;
+		stmt = dup_statement(stmt);
+		stmt->range_expression = expr;
+		break;
+	}
+	case STMT_COMPOUND: {
+		struct statement *new = alloc_statement(stmt->pos, STMT_COMPOUND);
+		copy_statement(stmt, new);
+		stmt = new;
+		break;
+	}
+	case STMT_IF: {
+		struct expression *cond = stmt->if_conditional;
+		struct statement *true = stmt->if_true;
+		struct statement *false = stmt->if_false;
+
+		cond = copy_expression(cond);
+		true = copy_one_statement(true);
+		false = copy_one_statement(false);
+		if (stmt->if_conditional == cond &&
+		    stmt->if_true == true &&
+		    stmt->if_false == false)
+			break;
+		stmt = dup_statement(stmt);
+		stmt->if_conditional = cond;
+		stmt->if_true = true;
+		stmt->if_false = false;
+		break;
+	}
+	case STMT_RETURN: {
+		struct expression *retval = copy_expression(stmt->ret_value);
+		struct symbol *sym = copy_symbol(stmt->pos, stmt->ret_target);
+
+		stmt = dup_statement(stmt);
+		stmt->ret_value = retval;
+		stmt->ret_target = sym;
+		break;
+	}
+	case STMT_CASE: {
+		stmt = dup_statement(stmt);
+		stmt->case_label = copy_symbol(stmt->pos, stmt->case_label);
+		stmt->case_label->stmt = stmt;
+		stmt->case_expression = copy_expression(stmt->case_expression);
+		stmt->case_to = copy_expression(stmt->case_to);
+		stmt->case_statement = copy_one_statement(stmt->case_statement);
+		break;
+	}
+	case STMT_SWITCH: {
+		struct symbol *switch_break = copy_symbol(stmt->pos, stmt->switch_break);
+		struct symbol *switch_case = copy_symbol(stmt->pos, stmt->switch_case);
+		struct expression *expr = copy_expression(stmt->switch_expression);
+		struct statement *switch_stmt = copy_one_statement(stmt->switch_statement);
+
+		stmt = dup_statement(stmt);
+		switch_case->symbol_list = copy_symbol_list(switch_case->symbol_list);
+		stmt->switch_break = switch_break;
+		stmt->switch_case = switch_case;
+		stmt->switch_expression = expr;
+		stmt->switch_statement = switch_stmt;
+		break;		
+	}
+	case STMT_ITERATOR: {
+		stmt = dup_statement(stmt);
+		stmt->iterator_break = copy_symbol(stmt->pos, stmt->iterator_break);
+		stmt->iterator_continue = copy_symbol(stmt->pos, stmt->iterator_continue);
+		stmt->iterator_syms = copy_symbol_list(stmt->iterator_syms);
+
+		stmt->iterator_pre_statement = copy_one_statement(stmt->iterator_pre_statement);
+		stmt->iterator_pre_condition = copy_expression(stmt->iterator_pre_condition);
+
+		stmt->iterator_statement = copy_one_statement(stmt->iterator_statement);
+
+		stmt->iterator_post_statement = copy_one_statement(stmt->iterator_post_statement);
+		stmt->iterator_post_condition = copy_expression(stmt->iterator_post_condition);
+		break;
+	}
+	case STMT_LABEL: {
+		stmt = dup_statement(stmt);
+		stmt->label_identifier = copy_symbol(stmt->pos, stmt->label_identifier);
+		stmt->label_statement = copy_one_statement(stmt->label_statement);
+		break;
+	}
+	case STMT_GOTO: {
+		stmt = dup_statement(stmt);
+		stmt->goto_label = copy_symbol(stmt->pos, stmt->goto_label);
+		stmt->goto_expression = copy_expression(stmt->goto_expression);
+		stmt->target_list = copy_symbol_list(stmt->target_list);
+		break;
+	}
+	case STMT_ASM: {
+		stmt = dup_statement(stmt);
+		stmt->asm_inputs = copy_asm_constraints(stmt->asm_inputs);
+		stmt->asm_outputs = copy_asm_constraints(stmt->asm_outputs);
+		/* no need to dup "clobbers", since they are all constant strings */
+		break;
+	}
+	default:
+		warning(stmt->pos, "trying to copy statement type %d", stmt->type);
+		break;
+	}
+	return stmt;
+}
+
+/*
+ * Copy a statement tree from 'src' to 'dst', where both
+ * source and destination are of type STMT_COMPOUND.
+ *
+ * We do this for the tree-level inliner.
+ *
+ * This doesn't do the symbol replacement right: it's not
+ * re-entrant.
+ */
+void copy_statement(struct statement *src, struct statement *dst)
+{
+	struct statement *stmt;
+
+	FOR_EACH_PTR(src->stmts, stmt) {
+		add_statement(&dst->stmts, copy_one_statement(stmt));
+	} END_FOR_EACH_PTR(stmt);
+	dst->args = copy_one_statement(src->args);
+	dst->ret = copy_symbol(src->pos, src->ret);
+	dst->inline_fn = src->inline_fn;
+}
+
+static struct symbol *create_copy_symbol(struct symbol *orig)
+{
+	struct symbol *sym = orig;
+	if (orig) {
+		sym = alloc_symbol(orig->pos, orig->type);
+		*sym = *orig;
+		sym->bb_target = NULL;
+		sym->pseudo = NULL;
+		set_replace(orig, sym);
+		orig = sym;
+	}
+	return orig;
+}
+
+static struct symbol_list *create_symbol_list(struct symbol_list *src)
+{
+	struct symbol_list *dst = NULL;
+	struct symbol *sym;
+
+	FOR_EACH_PTR(src, sym) {
+		struct symbol *newsym = create_copy_symbol(sym);
+		add_symbol(&dst, newsym);
+	} END_FOR_EACH_PTR(sym);
+	return dst;
+}
+
+int inline_function(struct expression *expr, struct symbol *sym)
+{
+	struct symbol_list * fn_symbol_list;
+	struct symbol *fn = sym->ctype.base_type;
+	struct expression_list *arg_list = expr->args;
+	struct statement *stmt = alloc_statement(expr->pos, STMT_COMPOUND);
+	struct symbol_list *name_list, *arg_decl;
+	struct symbol *name;
+	struct expression *arg;
+
+	if (!fn->inline_stmt) {
+		sparse_error(fn->pos, "marked inline, but without a definition");
+		return 0;
+	}
+	if (fn->expanding)
+		return 0;
+
+	fn->expanding = 1;
+
+	name_list = fn->arguments;
+
+	expr->type = EXPR_STATEMENT;
+	expr->statement = stmt;
+	expr->ctype = fn->ctype.base_type;
+
+	fn_symbol_list = create_symbol_list(sym->inline_symbol_list);
+
+	arg_decl = NULL;
+	PREPARE_PTR_LIST(name_list, name);
+	FOR_EACH_PTR(arg_list, arg) {
+		struct symbol *a = alloc_symbol(arg->pos, SYM_NODE);
+
+		a->ctype.base_type = arg->ctype;
+		if (name) {
+			*a = *name;
+			set_replace(name, a);
+			add_symbol(&fn_symbol_list, a);
+		}
+		a->initializer = arg;
+		add_symbol(&arg_decl, a);
+
+		NEXT_PTR_LIST(name);
+	} END_FOR_EACH_PTR(arg);
+	FINISH_PTR_LIST(name);
+
+	copy_statement(fn->inline_stmt, stmt);
+
+	if (arg_decl) {
+		struct statement *decl = alloc_statement(expr->pos, STMT_DECLARATION);
+		decl->declaration = arg_decl;
+		stmt->args = decl;
+	}
+	stmt->inline_fn = sym;
+
+	unset_replace_list(fn_symbol_list);
+
+	evaluate_statement(stmt);
+
+	fn->expanding = 0;
+	return 1;
+}
+
+void uninline(struct symbol *sym)
+{
+	struct symbol *fn = sym->ctype.base_type;
+	struct symbol_list *arg_list = fn->arguments;
+	struct symbol *p;
+
+	sym->symbol_list = create_symbol_list(sym->inline_symbol_list);
+	FOR_EACH_PTR(arg_list, p) {
+		p->replace = p;
+	} END_FOR_EACH_PTR(p);
+	fn->stmt = alloc_statement(fn->pos, STMT_COMPOUND);
+	copy_statement(fn->inline_stmt, fn->stmt);
+	unset_replace_list(sym->symbol_list);
+	unset_replace_list(arg_list);
+}
diff --git a/deps/sparse/lib.c b/deps/sparse/lib.c
new file mode 100644
index 0000000..396e9f1
--- /dev/null
+++ b/deps/sparse/lib.c
@@ -0,0 +1,991 @@
+/*
+ * 'sparse' library helper routines.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <sys/types.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "expression.h"
+#include "scope.h"
+#include "linearize.h"
+#include "target.h"
+
+int verbose, optimize, optimize_size, preprocessing;
+int die_if_error = 0;
+
+#ifndef __GNUC__
+# define __GNUC__ 2
+# define __GNUC_MINOR__ 95
+# define __GNUC_PATCHLEVEL__ 0
+#endif
+
+int gcc_major = __GNUC__;
+int gcc_minor = __GNUC_MINOR__;
+int gcc_patchlevel = __GNUC_PATCHLEVEL__;
+
+static const char *gcc_base_dir = GCC_BASE;
+
+struct token *skip_to(struct token *token, int op)
+{
+	while (!match_op(token, op) && !eof_token(token))
+		token = token->next;
+	return token;
+}
+
+struct token *expect(struct token *token, int op, const char *where)
+{
+	if (!match_op(token, op)) {
+		static struct token bad_token;
+		if (token != &bad_token) {
+			bad_token.next = token;
+			sparse_error(token->pos, "Expected %s %s", show_special(op), where);
+			sparse_error(token->pos, "got %s", show_token(token));
+		}
+		if (op == ';')
+			return skip_to(token, op);
+		return &bad_token;
+	}
+	return token->next;
+}
+
+unsigned int hexval(unsigned int c)
+{
+	int retval = 256;
+	switch (c) {
+	case '0'...'9':
+		retval = c - '0';
+		break;
+	case 'a'...'f':
+		retval = c - 'a' + 10;
+		break;
+	case 'A'...'F':
+		retval = c - 'A' + 10;
+		break;
+	}
+	return retval;
+}
+
+static void do_warn(const char *type, struct position pos, const char * fmt, va_list args)
+{
+	static char buffer[512];
+	const char *name;
+
+	vsprintf(buffer, fmt, args);	
+	name = stream_name(pos.stream);
+		
+	fprintf(stderr, "%s:%d:%d: %s%s\n",
+		name, pos.line, pos.pos, type, buffer);
+}
+
+static int max_warnings = 100;
+static int show_info = 1;
+
+void info(struct position pos, const char * fmt, ...)
+{
+	va_list args;
+
+	if (!show_info)
+		return;
+	va_start(args, fmt);
+	do_warn("", pos, fmt, args);
+	va_end(args);
+}
+
+void warning(struct position pos, const char * fmt, ...)
+{
+	va_list args;
+
+	if (!max_warnings) {
+		show_info = 0;
+		return;
+	}
+
+	if (!--max_warnings) {
+		show_info = 0;
+		fmt = "too many warnings";
+	}
+
+	va_start(args, fmt);
+	do_warn("warning: ", pos, fmt, args);
+	va_end(args);
+}	
+
+static void do_error(struct position pos, const char * fmt, va_list args)
+{
+	static int errors = 0;
+        die_if_error = 1;
+	show_info = 1;
+	/* Shut up warnings after an error */
+	max_warnings = 0;
+	if (errors > 100) {
+		static int once = 0;
+		show_info = 0;
+		if (once)
+			return;
+		fmt = "too many errors";
+		once = 1;
+	}
+
+	do_warn("error: ", pos, fmt, args);
+	errors++;
+}	
+
+void sparse_error(struct position pos, const char * fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	do_error(pos, fmt, args);
+	va_end(args);
+}
+
+void expression_error(struct expression *expr, const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	do_error(expr->pos, fmt, args);
+	va_end(args);
+	expr->ctype = &bad_ctype;
+}
+
+void error_die(struct position pos, const char * fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	do_warn("error: ", pos, fmt, args);
+	va_end(args);
+	exit(1);
+}
+
+void die(const char *fmt, ...)
+{
+	va_list args;
+	static char buffer[512];
+
+	va_start(args, fmt);
+	vsnprintf(buffer, sizeof(buffer), fmt, args);
+	va_end(args);
+
+	fprintf(stderr, "%s\n", buffer);
+	exit(1);
+}
+
+static struct token *pre_buffer_begin = NULL;
+static struct token *pre_buffer_end = NULL;
+
+int Waddress_space = 1;
+int Wbitwise = 0;
+int Wcast_to_as = 0;
+int Wcast_truncate = 1;
+int Wcontext = 1;
+int Wdecl = 1;
+int Wdefault_bitfield_sign = 0;
+int Wdesignated_init = 1;
+int Wdo_while = 0;
+int Wenum_mismatch = 1;
+int Wnon_pointer_null = 1;
+int Wold_initializer = 1;
+int Wone_bit_signed_bitfield = 1;
+int Wparen_string = 0;
+int Wptr_subtraction_blows = 0;
+int Wreturn_void = 0;
+int Wshadow = 0;
+int Wtransparent_union = 0;
+int Wtypesign = 0;
+int Wundef = 0;
+int Wuninitialized = 1;
+int Wdeclarationafterstatement = -1;
+
+int dbg_entry = 0;
+int dbg_dead = 0;
+
+int preprocess_only;
+
+static enum { STANDARD_C89,
+              STANDARD_C94,
+              STANDARD_C99,
+              STANDARD_GNU89,
+              STANDARD_GNU99, } standard = STANDARD_GNU89;
+
+#define CMDLINE_INCLUDE 20
+int cmdline_include_nr = 0;
+struct cmdline_include cmdline_include[CMDLINE_INCLUDE];
+
+
+void add_pre_buffer(const char *fmt, ...)
+{
+	va_list args;
+	unsigned int size;
+	struct token *begin, *end;
+	char buffer[4096];
+
+	va_start(args, fmt);
+	size = vsnprintf(buffer, sizeof(buffer), fmt, args);
+	va_end(args);
+	begin = tokenize_buffer(buffer, size, &end);
+	if (!pre_buffer_begin)
+		pre_buffer_begin = begin;
+	if (pre_buffer_end)
+		pre_buffer_end->next = begin;
+	pre_buffer_end = end;
+}
+
+static char **handle_switch_D(char *arg, char **next)
+{
+	const char *name = arg + 1;
+	const char *value = "1";
+
+	if (!*name || isspace(*name))
+		die("argument to `-D' is missing");
+
+	for (;;) {
+		char c;
+		c = *++arg;
+		if (!c)
+			break;
+		if (isspace((unsigned char)c) || c == '=') {
+			*arg = '\0';
+			value = arg + 1;
+			break;
+		}
+	}
+	add_pre_buffer("#define %s %s\n", name, value);
+	return next;
+}
+
+static char **handle_switch_E(char *arg, char **next)
+{
+	if (arg[1] == '\0')
+		preprocess_only = 1;
+	return next;
+}
+
+static char **handle_switch_I(char *arg, char **next)
+{
+	char *path = arg+1;
+
+	switch (arg[1]) {
+	case '-':
+		add_pre_buffer("#split_include\n");
+		break;
+
+	case '\0':	/* Plain "-I" */
+		path = *++next;
+		if (!path)
+			die("missing argument for -I option");
+		/* Fall through */
+	default:
+		add_pre_buffer("#add_include \"%s/\"\n", path);
+	}
+	return next;
+}
+
+static void add_cmdline_include(char *filename)
+{
+	int fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		perror(filename);
+		return;
+	}
+	if (cmdline_include_nr >= CMDLINE_INCLUDE)
+		die("too many include files for %s\n", filename);
+	cmdline_include[cmdline_include_nr].filename = filename;
+	cmdline_include[cmdline_include_nr].fd = fd;
+	cmdline_include_nr++;
+}
+
+static char **handle_switch_i(char *arg, char **next)
+{
+	if (*next && !strcmp(arg, "include"))
+		add_cmdline_include(*++next);
+	else if (*next && !strcmp(arg, "imacros"))
+		add_cmdline_include(*++next);
+	else if (*next && !strcmp(arg, "isystem")) {
+		char *path = *++next;
+		if (!path)
+			die("missing argument for -isystem option");
+		add_pre_buffer("#add_isystem \"%s/\"\n", path);
+	} else if (*next && !strcmp(arg, "idirafter")) {
+		char *path = *++next;
+		if (!path)
+			die("missing argument for -idirafter option");
+		add_pre_buffer("#add_dirafter \"%s/\"\n", path);
+	}
+	return next;
+}
+
+static char **handle_switch_M(char *arg, char **next)
+{
+	if (!strcmp(arg, "MF") || !strcmp(arg,"MQ") || !strcmp(arg,"MT")) {
+		if (!*next)
+			die("missing argument for -%s option", arg);
+		return next + 1;
+	}
+	return next;
+}
+
+static char **handle_switch_m(char *arg, char **next)
+{
+	if (!strcmp(arg, "m64")) {
+		bits_in_long = 64;
+		max_int_alignment = 8;
+		bits_in_pointer = 64;
+		pointer_alignment = 8;
+		size_t_ctype = &ulong_ctype;
+		ssize_t_ctype = &long_ctype;
+	} else if (!strcmp(arg, "msize-long")) {
+		size_t_ctype = &ulong_ctype;
+		ssize_t_ctype = &long_ctype;
+	}
+	return next;
+}
+
+static char **handle_switch_o(char *arg, char **next)
+{
+	if (!strcmp (arg, "o")) {       // "-o foo"
+		if (!*++next)
+			die("argument to '-o' is missing");
+	}
+	// else "-ofoo"
+
+	return next;
+}
+
+static const struct warning {
+	const char *name;
+	int *flag;
+} warnings[] = {
+	{ "address-space", &Waddress_space },
+	{ "bitwise", &Wbitwise },
+	{ "cast-to-as", &Wcast_to_as },
+	{ "cast-truncate", &Wcast_truncate },
+	{ "context", &Wcontext },
+	{ "decl", &Wdecl },
+	{ "default-bitfield-sign", &Wdefault_bitfield_sign },
+	{ "designated-init", &Wdesignated_init },
+	{ "do-while", &Wdo_while },
+	{ "enum-mismatch", &Wenum_mismatch },
+	{ "non-pointer-null", &Wnon_pointer_null },
+	{ "old-initializer", &Wold_initializer },
+	{ "one-bit-signed-bitfield", &Wone_bit_signed_bitfield },
+	{ "paren-string", &Wparen_string },
+	{ "ptr-subtraction-blows", &Wptr_subtraction_blows },
+	{ "return-void", &Wreturn_void },
+	{ "shadow", &Wshadow },
+	{ "transparent-union", &Wtransparent_union },
+	{ "typesign", &Wtypesign },
+	{ "undef", &Wundef },
+	{ "uninitialized", &Wuninitialized },
+	{ "declaration-after-statement", &Wdeclarationafterstatement },
+};
+
+enum {
+	WARNING_OFF,
+	WARNING_ON,
+	WARNING_FORCE_OFF
+};
+
+
+static char **handle_onoff_switch(char *arg, char **next, const struct warning warnings[], int n)
+{
+	int flag = WARNING_ON;
+	char *p = arg + 1;
+	unsigned i;
+
+	if (!strcmp(p, "sparse-all")) {
+		for (i = 0; i < n; i++) {
+			if (*warnings[i].flag != WARNING_FORCE_OFF)
+				*warnings[i].flag = WARNING_ON;
+		}
+	}
+
+	// Prefixes "no" and "no-" mean to turn warning off.
+	if (p[0] == 'n' && p[1] == 'o') {
+		p += 2;
+		if (p[0] == '-')
+			p++;
+		flag = WARNING_FORCE_OFF;
+	}
+
+	for (i = 0; i < n; i++) {
+		if (!strcmp(p,warnings[i].name)) {
+			*warnings[i].flag = flag;
+			return next;
+		}
+	}
+
+	// Unknown.
+	return NULL;
+}
+
+static char **handle_switch_W(char *arg, char **next)
+{
+	char ** ret = handle_onoff_switch(arg, next, warnings, ARRAY_SIZE(warnings));
+	if (ret)
+		return ret;
+
+	// Unknown.
+	return next;
+}
+
+static struct warning debugs[] = {
+	{ "entry", &dbg_entry},
+	{ "dead", &dbg_dead},
+};
+
+
+static char **handle_switch_v(char *arg, char **next)
+{
+	char ** ret = handle_onoff_switch(arg, next, debugs, ARRAY_SIZE(debugs));
+	if (ret)
+		return ret;
+
+	// Unknown.
+	do {
+		verbose++;
+	} while (*++arg == 'v');
+	return next;
+}
+
+
+static void handle_onoff_switch_finalize(const struct warning warnings[], int n)
+{
+	unsigned i;
+
+	for (i = 0; i < n; i++) {
+		if (*warnings[i].flag == WARNING_FORCE_OFF)
+			*warnings[i].flag = WARNING_OFF;
+	}
+}
+
+static void handle_switch_W_finalize(void)
+{
+	handle_onoff_switch_finalize(warnings, ARRAY_SIZE(warnings));
+
+	/* default Wdeclarationafterstatement based on the C dialect */
+	if (-1 == Wdeclarationafterstatement)
+	{
+		switch (standard)
+		{
+			case STANDARD_C89:
+			case STANDARD_C94:
+				Wdeclarationafterstatement = 1;
+				break;
+
+			case STANDARD_C99:
+			case STANDARD_GNU89:
+			case STANDARD_GNU99:
+				Wdeclarationafterstatement = 0;
+				break;
+
+			default:
+				assert (0);
+		}
+
+	}
+}
+
+static void handle_switch_v_finalize(void)
+{
+	handle_onoff_switch_finalize(debugs, ARRAY_SIZE(debugs));
+}
+
+static char **handle_switch_U(char *arg, char **next)
+{
+	const char *name = arg + 1;
+	add_pre_buffer ("#undef %s\n", name);
+	return next;
+}
+
+static char **handle_switch_O(char *arg, char **next)
+{
+	int level = 1;
+	if (arg[1] >= '0' && arg[1] <= '9')
+		level = arg[1] - '0';
+	optimize = level;
+	optimize_size = arg[1] == 's';
+	return next;
+}
+
+static char **handle_switch_ftabstop(char *arg, char **next)
+{
+	char *end;
+	unsigned long val;
+
+	if (*arg == '\0')
+		die("error: missing argument to \"-ftabstop=\"");
+
+	/* we silently ignore silly values */
+	val = strtoul(arg, &end, 10);
+	if (*end == '\0' && 1 <= val && val <= 100)
+		tabstop = val;
+
+	return next;
+}
+
+static char **handle_switch_f(char *arg, char **next)
+{
+	arg++;
+
+	if (!strncmp(arg, "tabstop=", 8))
+		return handle_switch_ftabstop(arg+8, next);
+
+	/* handle switches w/ arguments above, boolean and only boolean below */
+
+	if (!strncmp(arg, "no-", 3)) {
+		arg += 3;
+	}
+	/* handle switch here.. */
+	return next;
+}
+
+static char **handle_switch_G(char *arg, char **next)
+{
+	if (!strcmp (arg, "G") && *next)
+		return next + 1; // "-G 0"
+	else
+		return next;     // "-G0" or (bogus) terminal "-G"
+}
+
+static char **handle_switch_a(char *arg, char **next)
+{
+	if (!strcmp (arg, "ansi"))
+		standard = STANDARD_C89;
+
+	return next;
+}
+
+static char **handle_switch_s(char *arg, char **next)
+{
+	if (!strncmp (arg, "std=", 4))
+	{
+		arg += 4;
+
+		if (!strcmp (arg, "c89") ||
+		    !strcmp (arg, "iso9899:1990"))
+			standard = STANDARD_C89;
+
+		else if (!strcmp (arg, "iso9899:199409"))
+			standard = STANDARD_C94;
+
+		else if (!strcmp (arg, "c99") ||
+			 !strcmp (arg, "c9x") ||
+			 !strcmp (arg, "iso9899:1999") ||
+			 !strcmp (arg, "iso9899:199x"))
+			standard = STANDARD_C99;
+
+		else if (!strcmp (arg, "gnu89"))
+			standard = STANDARD_GNU89;
+
+		else if (!strcmp (arg, "gnu99") || !strcmp (arg, "gnu9x"))
+			standard = STANDARD_GNU99;
+
+		else
+			die ("Unsupported C dialect");
+	}
+
+	return next;
+}
+
+static char **handle_nostdinc(char *arg, char **next)
+{
+	add_pre_buffer("#nostdinc\n");
+	return next;
+}
+
+static char **handle_base_dir(char *arg, char **next)
+{
+	gcc_base_dir = *++next;
+	if (!gcc_base_dir)
+		die("missing argument for -gcc-base-dir option");
+	return next;
+}
+
+struct switches {
+	const char *name;
+	char **(*fn)(char *, char **);
+};
+
+static char **handle_switch(char *arg, char **next)
+{
+	static struct switches cmd[] = {
+		{ "nostdinc", handle_nostdinc },
+		{ "gcc-base-dir", handle_base_dir},
+		{ NULL, NULL }
+	};
+	struct switches *s;
+
+	switch (*arg) {
+	case 'D': return handle_switch_D(arg, next);
+	case 'E': return handle_switch_E(arg, next);
+	case 'I': return handle_switch_I(arg, next);
+	case 'i': return handle_switch_i(arg, next);
+	case 'M': return handle_switch_M(arg, next);
+	case 'm': return handle_switch_m(arg, next);
+	case 'o': return handle_switch_o(arg, next);
+	case 'U': return handle_switch_U(arg, next);
+	case 'v': return handle_switch_v(arg, next);
+	case 'W': return handle_switch_W(arg, next);
+	case 'O': return handle_switch_O(arg, next);
+	case 'f': return handle_switch_f(arg, next);
+	case 'G': return handle_switch_G(arg, next);
+	case 'a': return handle_switch_a(arg, next);
+	case 's': return handle_switch_s(arg, next);
+	default:
+		break;
+	}
+
+	s = cmd;
+	while (s->name) {
+		if (!strcmp(s->name, arg))
+			return s->fn(arg, next);
+		s++;
+	}
+
+	/*
+	 * Ignore unknown command line options:
+	 * they're probably gcc switches
+	 */
+	return next;
+}
+
+void declare_builtin_functions(void)
+{
+	/* Gaah. gcc knows tons of builtin <string.h> functions */
+	add_pre_buffer("extern void *__builtin_memcpy(void *, const void *, __SIZE_TYPE__);\n");
+	add_pre_buffer("extern void *__builtin_mempcpy(void *, const void *, __SIZE_TYPE__);\n");
+	add_pre_buffer("extern void *__builtin_memset(void *, int, __SIZE_TYPE__);\n");
+	add_pre_buffer("extern int __builtin_memcmp(const void *, const void *, __SIZE_TYPE__);\n");
+	add_pre_buffer("extern char *__builtin_strcat(char *, const char *);\n");
+	add_pre_buffer("extern char *__builtin_strncat(char *, const char *, __SIZE_TYPE__);\n");
+	add_pre_buffer("extern int __builtin_strcmp(const char *, const char *);\n");
+	add_pre_buffer("extern char *__builtin_strchr(const char *, int);\n");
+	add_pre_buffer("extern char *__builtin_strcpy(char *, const char *);\n");
+	add_pre_buffer("extern char *__builtin_strncpy(char *, const char *, __SIZE_TYPE__);\n");
+	add_pre_buffer("extern __SIZE_TYPE__ __builtin_strspn(const char *, const char *);\n");
+	add_pre_buffer("extern __SIZE_TYPE__ __builtin_strcspn(const char *, const char *);\n");
+	add_pre_buffer("extern char * __builtin_strpbrk(const char *, const char *);\n");
+	add_pre_buffer("extern __SIZE_TYPE__ __builtin_strlen(const char *);\n");
+
+	/* And bitwise operations.. */
+	add_pre_buffer("extern int __builtin_clz(int);\n");
+	add_pre_buffer("extern int __builtin_clzl(long);\n");
+	add_pre_buffer("extern int __builtin_clzll(long long);\n");
+	add_pre_buffer("extern int __builtin_ctz(int);\n");
+	add_pre_buffer("extern int __builtin_ctzl(long);\n");
+	add_pre_buffer("extern int __builtin_ctzll(long long);\n");
+	add_pre_buffer("extern int __builtin_ffs(int);\n");
+	add_pre_buffer("extern int __builtin_ffsl(long);\n");
+	add_pre_buffer("extern int __builtin_ffsll(long long);\n");
+	add_pre_buffer("extern int __builtin_popcount(unsigned int);\n");
+	add_pre_buffer("extern int __builtin_popcountl(unsigned long);\n");
+	add_pre_buffer("extern int __builtin_popcountll(unsigned long long);\n");
+
+	/* And some random ones.. */
+	add_pre_buffer("extern void *__builtin_return_address(unsigned int);\n");
+	add_pre_buffer("extern void *__builtin_extract_return_addr(void *);\n");
+	add_pre_buffer("extern void *__builtin_frame_address(unsigned int);\n");
+	add_pre_buffer("extern void __builtin_trap(void);\n");
+	add_pre_buffer("extern void *__builtin_alloca(__SIZE_TYPE__);\n");
+	add_pre_buffer("extern void __builtin_prefetch (const void *, ...);\n");
+	add_pre_buffer("extern long __builtin_alpha_extbl(long, long);\n");
+	add_pre_buffer("extern long __builtin_alpha_extwl(long, long);\n");
+	add_pre_buffer("extern long __builtin_alpha_insbl(long, long);\n");
+	add_pre_buffer("extern long __builtin_alpha_inswl(long, long);\n");
+	add_pre_buffer("extern long __builtin_alpha_insql(long, long);\n");
+	add_pre_buffer("extern long __builtin_alpha_inslh(long, long);\n");
+	add_pre_buffer("extern long __builtin_alpha_cmpbge(long, long);\n");
+	add_pre_buffer("extern long __builtin_labs(long);\n");
+	add_pre_buffer("extern double __builtin_fabs(double);\n");
+
+	/* Add Blackfin-specific stuff */
+	add_pre_buffer(
+		"#ifdef __bfin__\n"
+		"extern void __builtin_bfin_csync(void);\n"
+		"extern void __builtin_bfin_ssync(void);\n"
+		"extern int __builtin_bfin_norm_fr1x32(int);\n"
+		"#endif\n"
+	);
+
+	/* And some floating point stuff.. */
+	add_pre_buffer("extern int __builtin_isgreater(float, float);\n");
+	add_pre_buffer("extern int __builtin_isgreaterequal(float, float);\n");
+	add_pre_buffer("extern int __builtin_isless(float, float);\n");
+	add_pre_buffer("extern int __builtin_islessequal(float, float);\n");
+	add_pre_buffer("extern int __builtin_islessgreater(float, float);\n");
+	add_pre_buffer("extern int __builtin_isunordered(float, float);\n");
+
+	/* And some __FORTIFY_SOURCE ones.. */
+	add_pre_buffer ("extern __SIZE_TYPE__ __builtin_object_size(void *, int);\n");
+	add_pre_buffer ("extern void * __builtin___memcpy_chk(void *, const void *, __SIZE_TYPE__, __SIZE_TYPE__);\n");
+	add_pre_buffer ("extern void * __builtin___memmove_chk(void *, const void *, __SIZE_TYPE__, __SIZE_TYPE__);\n");
+	add_pre_buffer ("extern void * __builtin___mempcpy_chk(void *, const void *, __SIZE_TYPE__, __SIZE_TYPE__);\n");
+	add_pre_buffer ("extern void * __builtin___memset_chk(void *, int, __SIZE_TYPE__, __SIZE_TYPE__);\n");
+	add_pre_buffer ("extern int __builtin___sprintf_chk(char *, int, __SIZE_TYPE__, const char *, ...);\n");
+	add_pre_buffer ("extern int __builtin___snprintf_chk(char *, __SIZE_TYPE__, int , __SIZE_TYPE__, const char *, ...);\n");
+	add_pre_buffer ("extern char * __builtin___stpcpy_chk(char *, const char *, __SIZE_TYPE__);\n");
+	add_pre_buffer ("extern char * __builtin___strcat_chk(char *, const char *, __SIZE_TYPE__);\n");
+	add_pre_buffer ("extern char * __builtin___strcpy_chk(char *, const char *, __SIZE_TYPE__);\n");
+	add_pre_buffer ("extern char * __builtin___strncat_chk(char *, const char *, __SIZE_TYPE__, __SIZE_TYPE__);\n");
+	add_pre_buffer ("extern char * __builtin___strncpy_chk(char *, const char *, __SIZE_TYPE__, __SIZE_TYPE__);\n");
+	add_pre_buffer ("extern int __builtin___vsprintf_chk(char *, int, __SIZE_TYPE__, const char *, __builtin_va_list);\n");
+	add_pre_buffer ("extern int __builtin___vsnprintf_chk(char *, __SIZE_TYPE__, int, __SIZE_TYPE__, const char *, __builtin_va_list ap);\n");
+	add_pre_buffer ("extern void __builtin_unreachable(void);\n");
+}
+
+void create_builtin_stream(void)
+{
+	add_pre_buffer("#weak_define __GNUC__ %d\n", gcc_major);
+	add_pre_buffer("#weak_define __GNUC_MINOR__ %d\n", gcc_minor);
+	add_pre_buffer("#weak_define __GNUC_PATCHLEVEL__ %d\n", gcc_patchlevel);
+
+	/* We add compiler headers path here because we have to parse
+	 * the arguments to get it, falling back to default. */
+	add_pre_buffer("#add_system \"%s/include\"\n", gcc_base_dir);
+	add_pre_buffer("#add_system \"%s/include-fixed\"\n", gcc_base_dir);
+
+	add_pre_buffer("#define __extension__\n");
+	add_pre_buffer("#define __pragma__\n");
+
+	// gcc defines __SIZE_TYPE__ to be size_t.  For linux/i86 and
+	// solaris/sparc that is really "unsigned int" and for linux/x86_64
+	// it is "long unsigned int".  In either case we can probably
+	// get away with this.  We need the #weak_define as cgcc will define
+	// the right __SIZE_TYPE__.
+	if (size_t_ctype == &ulong_ctype)
+		add_pre_buffer("#weak_define __SIZE_TYPE__ long unsigned int\n");
+	else
+		add_pre_buffer("#weak_define __SIZE_TYPE__ unsigned int\n");
+	add_pre_buffer("#weak_define __STDC__ 1\n");
+
+	switch (standard)
+	{
+		case STANDARD_C89:
+			add_pre_buffer("#weak_define __STRICT_ANSI__\n");
+			break;
+
+		case STANDARD_C94:
+			add_pre_buffer("#weak_define __STDC_VERSION__ 199409L\n");
+			add_pre_buffer("#weak_define __STRICT_ANSI__\n");
+			break;
+
+		case STANDARD_C99:
+			add_pre_buffer("#weak_define __STDC_VERSION__ 199901L\n");
+			add_pre_buffer("#weak_define __STRICT_ANSI__\n");
+			break;
+
+		case STANDARD_GNU89:
+			break;
+
+		case STANDARD_GNU99:
+			add_pre_buffer("#weak_define __STDC_VERSION__ 199901L\n");
+			break;
+
+		default:
+			assert (0);
+	}
+
+	add_pre_buffer("#define __builtin_stdarg_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n");
+	add_pre_buffer("#define __builtin_va_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n");
+	add_pre_buffer("#define __builtin_ms_va_start(a,b) ((a) = (__builtin_ms_va_list)(&(b)))\n");
+	add_pre_buffer("#define __builtin_va_arg(arg,type)  ({ type __va_arg_ret = *(type *)(arg); arg += sizeof(type); __va_arg_ret; })\n");
+	add_pre_buffer("#define __builtin_va_alist (*(void *)0)\n");
+	add_pre_buffer("#define __builtin_va_arg_incr(x) ((x) + 1)\n");
+	add_pre_buffer("#define __builtin_va_copy(dest, src) ({ dest = src; (void)0; })\n");
+	add_pre_buffer("#define __builtin_va_end(arg)\n");
+	add_pre_buffer("#define __builtin_ms_va_end(arg)\n");
+
+	/* FIXME! We need to do these as special magic macros at expansion time! */
+	add_pre_buffer("#define __BASE_FILE__ \"base_file.c\"\n");
+
+	if (optimize)
+		add_pre_buffer("#define __OPTIMIZE__ 1\n");
+	if (optimize_size)
+		add_pre_buffer("#define __OPTIMIZE_SIZE__ 1\n");
+
+	/* GCC defines these for limits.h */
+	add_pre_buffer("#weak_define __SHRT_MAX__ " STRINGIFY(__SHRT_MAX__) "\n");
+	add_pre_buffer("#weak_define __SCHAR_MAX__ " STRINGIFY(__SCHAR_MAX__) "\n");
+	add_pre_buffer("#weak_define __INT_MAX__ " STRINGIFY(__INT_MAX__) "\n");
+	add_pre_buffer("#weak_define __LONG_MAX__ " STRINGIFY(__LONG_MAX__) "\n");
+	add_pre_buffer("#weak_define __LONG_LONG_MAX__ " STRINGIFY(__LONG_LONG_MAX__) "\n");
+	add_pre_buffer("#weak_define __WCHAR_MAX__ " STRINGIFY(__WCHAR_MAX__) "\n");
+}
+
+static struct symbol_list *sparse_tokenstream(struct token *token)
+{
+	// Preprocess the stream
+	token = preprocess(token);
+
+	if (preprocess_only) {
+		while (!eof_token(token)) {
+			int prec = 1;
+			struct token *next = token->next;
+			const char *separator = "";
+			if (next->pos.whitespace)
+				separator = " ";
+			if (next->pos.newline) {
+				separator = "\n\t\t\t\t\t";
+				prec = next->pos.pos;
+				if (prec > 4)
+					prec = 4;
+			}
+			printf("%s%.*s", show_token(token), prec, separator);
+			token = next;
+		}
+		putchar('\n');
+
+		return NULL;
+	}
+
+	// Parse the resulting C code
+	while (!eof_token(token))
+		token = external_declaration(token, &translation_unit_used_list);
+	return translation_unit_used_list;
+}
+
+static struct symbol_list *sparse_file(const char *filename)
+{
+	int fd;
+	struct token *token;
+
+	if (strcmp (filename, "-") == 0) {
+		fd = 0;
+	} else {
+		fd = open(filename, O_RDONLY);
+		if (fd < 0)
+			die("No such file: %s", filename);
+	}
+
+	// Tokenize the input stream
+	token = tokenize(filename, fd, NULL, includepath);
+	close(fd);
+
+	return sparse_tokenstream(token);
+}
+
+/*
+ * This handles the "-include" directive etc: we're in global
+ * scope, and all types/macros etc will affect all the following
+ * files.
+ *
+ * NOTE NOTE NOTE! "#undef" of anything in this stage will
+ * affect all subsequent files too, i.e. we can have non-local
+ * behaviour between files!
+ */
+static struct symbol_list *sparse_initial(void)
+{
+	struct token *token;
+	int i;
+
+	// Prepend any "include" file to the stream.
+	// We're in global scope, it will affect all files!
+	token = NULL;
+	for (i = cmdline_include_nr - 1; i >= 0; i--)
+		token = tokenize(cmdline_include[i].filename, cmdline_include[i].fd,
+				 token, includepath);
+
+	// Prepend the initial built-in stream
+	if (token)
+		pre_buffer_end->next = token;
+	return sparse_tokenstream(pre_buffer_begin);
+}
+
+struct symbol_list *sparse_initialize(int argc, char **argv, struct string_list **filelist)
+{
+	char **args;
+	struct symbol_list *list;
+
+	// Initialize symbol stream first, so that we can add defines etc
+	init_symbols();
+
+	args = argv;
+	for (;;) {
+		char *arg = *++args;
+		if (!arg)
+			break;
+
+		if (arg[0] == '-' && arg[1]) {
+			args = handle_switch(arg+1, args);
+			continue;
+		}
+		add_ptr_list_notag(filelist, arg);
+	}
+	handle_switch_W_finalize();
+	handle_switch_v_finalize();
+
+	list = NULL;
+	if (!ptr_list_empty(filelist)) {
+		// Initialize type system
+		init_ctype();
+
+		create_builtin_stream();
+		add_pre_buffer("#define __CHECKER__ 1\n");
+		if (!preprocess_only)
+			declare_builtin_functions();
+
+		list = sparse_initial();
+
+		/*
+		 * Protect the initial token allocations, since
+		 * they need to survive all the others
+		 */
+		protect_token_alloc();
+	}
+	return list;
+}
+
+struct symbol_list * sparse_keep_tokens(char *filename)
+{
+	struct symbol_list *res;
+
+	/* Clear previous symbol list */
+	translation_unit_used_list = NULL;
+
+	new_file_scope();
+	res = sparse_file(filename);
+
+	/* And return it */
+	return res;
+}
+
+
+struct symbol_list * __sparse(char *filename)
+{
+	struct symbol_list *res;
+
+	res = sparse_keep_tokens(filename);
+
+	/* Drop the tokens for this file after parsing */
+	clear_token_alloc();
+
+	/* And return it */
+	return res;
+}
+
+struct symbol_list * sparse(char *filename)
+{
+	struct symbol_list *res = __sparse(filename);
+
+	/* Evaluate the complete symbol list */
+	evaluate_symbol_list(res);
+
+	return res;
+}
diff --git a/deps/sparse/lib.h b/deps/sparse/lib.h
new file mode 100644
index 0000000..2cea252
--- /dev/null
+++ b/deps/sparse/lib.h
@@ -0,0 +1,226 @@
+#ifndef LIB_H
+#define LIB_H
+
+#include <stdlib.h>
+#include <stddef.h>
+
+/*
+ * Basic helper routine descriptions for 'sparse'.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003 Linus Torvalds
+ *               2004 Christopher Li
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+
+#include "compat.h"
+#include "ptrlist.h"
+
+#define DO_STRINGIFY(x) #x
+#define STRINGIFY(x) DO_STRINGIFY(x)
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+#endif
+
+extern int verbose, optimize, optimize_size, preprocessing;
+extern int die_if_error;
+extern int repeat_phase, merge_phi_sources;
+extern int gcc_major, gcc_minor, gcc_patchlevel;
+
+extern unsigned int hexval(unsigned int c);
+
+struct position {
+	unsigned int type:6,
+		     stream:14,
+		     newline:1,
+		     whitespace:1,
+		     pos:10;
+	unsigned int line:31,
+		     noexpand:1;
+};
+
+struct cmdline_include {
+	char *filename;
+	int fd;
+};
+
+extern struct cmdline_include cmdline_include[];
+extern int cmdline_include_nr;
+
+
+struct ident;
+struct token;
+struct symbol;
+struct statement;
+struct expression;
+struct basic_block;
+struct entrypoint;
+struct instruction;
+struct multijmp;
+struct pseudo;
+
+DECLARE_PTR_LIST(symbol_list, struct symbol);
+DECLARE_PTR_LIST(statement_list, struct statement);
+DECLARE_PTR_LIST(expression_list, struct expression);
+DECLARE_PTR_LIST(basic_block_list, struct basic_block);
+DECLARE_PTR_LIST(instruction_list, struct instruction);
+DECLARE_PTR_LIST(multijmp_list, struct multijmp);
+DECLARE_PTR_LIST(pseudo_list, struct pseudo);
+DECLARE_PTR_LIST(string_list, char);
+
+typedef struct pseudo *pseudo_t;
+
+struct token *skip_to(struct token *, int);
+struct token *expect(struct token *, int, const char *);
+#ifdef __GNUC__
+#define FORMAT_ATTR(pos) __attribute__ ((__format__ (__printf__, pos, pos+1)))
+#define NORETURN_ATTR __attribute__ ((__noreturn__))
+#define SENTINEL_ATTR __attribute__ ((__sentinel__))
+#else
+#define FORMAT_ATTR(pos)
+#define NORETURN_ATTR
+#define SENTINEL_ATTR
+#endif
+extern void die(const char *, ...) FORMAT_ATTR(1) NORETURN_ATTR;
+extern void info(struct position, const char *, ...) FORMAT_ATTR(2);
+extern void warning(struct position, const char *, ...) FORMAT_ATTR(2);
+extern void sparse_error(struct position, const char *, ...) FORMAT_ATTR(2);
+extern void error_die(struct position, const char *, ...) FORMAT_ATTR(2) NORETURN_ATTR;
+extern void expression_error(struct expression *, const char *, ...) FORMAT_ATTR(2);
+
+extern void add_pre_buffer(const char *fmt, ...) FORMAT_ATTR(1);
+
+extern int preprocess_only;
+
+extern int Waddress_space;
+extern int Wbitwise;
+extern int Wcast_to_as;
+extern int Wcast_truncate;
+extern int Wcontext;
+extern int Wdecl;
+extern int Wdefault_bitfield_sign;
+extern int Wdesignated_init;
+extern int Wdo_while;
+extern int Wenum_mismatch;
+extern int Wnon_pointer_null;
+extern int Wold_initializer;
+extern int Wone_bit_signed_bitfield;
+extern int Wparen_string;
+extern int Wptr_subtraction_blows;
+extern int Wreturn_void;
+extern int Wshadow;
+extern int Wtransparent_union;
+extern int Wtypesign;
+extern int Wundef;
+extern int Wuninitialized;
+extern int Wdeclarationafterstatement;
+
+extern int dbg_entry;
+extern int dbg_dead;
+
+extern void declare_builtin_functions(void);
+extern void create_builtin_stream(void);
+extern struct symbol_list *sparse_initialize(int argc, char **argv, struct string_list **files);
+extern struct symbol_list *__sparse(char *filename);
+extern struct symbol_list *sparse_keep_tokens(char *filename);
+extern struct symbol_list *sparse(char *filename);
+
+static inline int symbol_list_size(struct symbol_list *list)
+{
+	return ptr_list_size((struct ptr_list *)(list));
+}
+
+static inline int statement_list_size(struct statement_list *list)
+{
+	return ptr_list_size((struct ptr_list *)(list));
+}
+
+static inline int expression_list_size(struct expression_list *list)
+{
+	return ptr_list_size((struct ptr_list *)(list));
+}
+
+static inline int instruction_list_size(struct instruction_list *list)
+{
+	return ptr_list_size((struct ptr_list *)(list));
+}
+
+static inline int pseudo_list_size(struct pseudo_list *list)
+{
+	return ptr_list_size((struct ptr_list *)(list));
+}
+
+static inline int bb_list_size(struct basic_block_list *list)
+{
+	return ptr_list_size((struct ptr_list *)(list));
+}
+
+static inline void free_instruction_list(struct instruction_list **head)
+{
+	free_ptr_list((struct ptr_list **)head);
+}
+
+static inline struct instruction * delete_last_instruction(struct instruction_list **head)
+{
+	return undo_ptr_list_last((struct ptr_list **)head);
+}
+
+static inline struct basic_block * delete_last_basic_block(struct basic_block_list **head)
+{
+	return delete_ptr_list_last((struct ptr_list **)head);
+}
+
+static inline struct basic_block *first_basic_block(struct basic_block_list *head)
+{
+	return first_ptr_list((struct ptr_list *)head);
+}
+static inline struct instruction *last_instruction(struct instruction_list *head)
+{
+	return last_ptr_list((struct ptr_list *)head);
+}
+
+static inline struct instruction *first_instruction(struct instruction_list *head)
+{
+	return first_ptr_list((struct ptr_list *)head);
+}
+
+static inline pseudo_t first_pseudo(struct pseudo_list *head)
+{
+	return first_ptr_list((struct ptr_list *)head);
+}
+
+static inline void concat_symbol_list(struct symbol_list *from, struct symbol_list **to)
+{
+	concat_ptr_list((struct ptr_list *)from, (struct ptr_list **)to);
+}
+
+static inline void concat_basic_block_list(struct basic_block_list *from, struct basic_block_list **to)
+{
+	concat_ptr_list((struct ptr_list *)from, (struct ptr_list **)to);
+}
+
+static inline void concat_instruction_list(struct instruction_list *from, struct instruction_list **to)
+{
+	concat_ptr_list((struct ptr_list *)from, (struct ptr_list **)to);
+}
+
+static inline void add_symbol(struct symbol_list **list, struct symbol *sym)
+{
+	add_ptr_list(list, sym);
+}
+
+static inline void add_statement(struct statement_list **list, struct statement *stmt)
+{
+	add_ptr_list(list, stmt);
+}
+
+static inline void add_expression(struct expression_list **list, struct expression *expr)
+{
+	add_ptr_list(list, expr);
+}
+
+#define hashval(x) ((unsigned long)(x))
+
+#endif
diff --git a/deps/sparse/linearize.c b/deps/sparse/linearize.c
new file mode 100644
index 0000000..3209727
--- /dev/null
+++ b/deps/sparse/linearize.c
@@ -0,0 +1,2214 @@
+/*
+ * Linearize - walk the statement tree (but _not_ the expressions)
+ * to generate a linear version of it and the basic blocks. 
+ *
+ * NOTE! We're not interested in the actual sub-expressions yet,
+ * even though they can generate conditional branches and
+ * subroutine calls. That's all "local" behaviour.
+ *
+ * Copyright (C) 2004 Linus Torvalds
+ * Copyright (C) 2004 Christopher Li
+ */
+
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "parse.h"
+#include "expression.h"
+#include "linearize.h"
+#include "flow.h"
+#include "target.h"
+
+pseudo_t linearize_statement(struct entrypoint *ep, struct statement *stmt);
+pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr);
+
+static pseudo_t add_binary_op(struct entrypoint *ep, struct symbol *ctype, int op, pseudo_t left, pseudo_t right);
+static pseudo_t add_setval(struct entrypoint *ep, struct symbol *ctype, struct expression *val);
+static pseudo_t linearize_one_symbol(struct entrypoint *ep, struct symbol *sym);
+
+struct access_data;
+static pseudo_t add_load(struct entrypoint *ep, struct access_data *);
+static pseudo_t linearize_initializer(struct entrypoint *ep, struct expression *initializer, struct access_data *);
+
+struct pseudo void_pseudo = {};
+
+static struct position current_pos;
+
+ALLOCATOR(pseudo_user, "pseudo_user");
+
+static struct instruction *alloc_instruction(int opcode, int size)
+{
+	struct instruction * insn = __alloc_instruction(0);
+	insn->opcode = opcode;
+	insn->size = size;
+	insn->pos = current_pos;
+	return insn;
+}
+
+static inline int type_size(struct symbol *type)
+{
+	return type ? type->bit_size > 0 ? type->bit_size : 0 : 0;
+}
+
+static struct instruction *alloc_typed_instruction(int opcode, struct symbol *type)
+{
+	struct instruction *insn = alloc_instruction(opcode, type_size(type));
+	insn->type = type;
+	return insn;
+}
+
+static struct entrypoint *alloc_entrypoint(void)
+{
+	return __alloc_entrypoint(0);
+}
+
+static struct basic_block *alloc_basic_block(struct entrypoint *ep, struct position pos)
+{
+	struct basic_block *bb = __alloc_basic_block(0);
+	bb->context = -1;
+	bb->pos = pos;
+	bb->ep = ep;
+	return bb;
+}
+
+static struct multijmp *alloc_multijmp(struct basic_block *target, int begin, int end)
+{
+	struct multijmp *multijmp = __alloc_multijmp(0);
+	multijmp->target = target;
+	multijmp->begin = begin;
+	multijmp->end = end;
+	return multijmp;
+}
+
+static inline int regno(pseudo_t n)
+{
+	int retval = -1;
+	if (n && n->type == PSEUDO_REG)
+		retval = n->nr;
+	return retval;
+}
+
+const char *show_pseudo(pseudo_t pseudo)
+{
+	static int n;
+	static char buffer[4][64];
+	char *buf;
+	int i;
+
+	if (!pseudo)
+		return "no pseudo";
+	if (pseudo == VOID)
+		return "VOID";
+	buf = buffer[3 & ++n];
+	switch(pseudo->type) {
+	case PSEUDO_SYM: {
+		struct symbol *sym = pseudo->sym;
+		struct expression *expr;
+
+		if (sym->bb_target) {
+			snprintf(buf, 64, ".L%p", sym->bb_target);
+			break;
+		}
+		if (sym->ident) {
+			snprintf(buf, 64, "%s", show_ident(sym->ident));
+			break;
+		}
+		expr = sym->initializer;
+		snprintf(buf, 64, "<anon symbol:%p>", sym);
+		if (expr) {
+			switch (expr->type) {
+			case EXPR_VALUE:
+				snprintf(buf, 64, "<symbol value: %lld>", expr->value);
+				break;
+			case EXPR_STRING:
+				return show_string(expr->string);
+			default:
+				break;
+			}
+		}
+		break;
+	}
+	case PSEUDO_REG:
+		i = snprintf(buf, 64, "%%r%d", pseudo->nr);
+		if (pseudo->ident)
+			sprintf(buf+i, "(%s)", show_ident(pseudo->ident));
+		break;
+	case PSEUDO_VAL: {
+		long long value = pseudo->value;
+		if (value > 1000 || value < -1000)
+			snprintf(buf, 64, "$%#llx", value);
+		else
+			snprintf(buf, 64, "$%lld", value);
+		break;
+	}
+	case PSEUDO_ARG:
+		snprintf(buf, 64, "%%arg%d", pseudo->nr);
+		break;
+	case PSEUDO_PHI:
+		i = snprintf(buf, 64, "%%phi%d", pseudo->nr);
+		if (pseudo->ident)
+			sprintf(buf+i, "(%s)", show_ident(pseudo->ident));
+		break;
+	default:
+		snprintf(buf, 64, "<bad pseudo type %d>", pseudo->type);
+	}
+	return buf;
+}
+
+static const char *opcodes[] = {
+	[OP_BADOP] = "bad_op",
+
+	/* Fn entrypoint */
+	[OP_ENTRY] = "<entry-point>",
+
+	/* Terminator */
+	[OP_RET] = "ret",
+	[OP_BR] = "br",
+	[OP_SWITCH] = "switch",
+	[OP_INVOKE] = "invoke",
+	[OP_COMPUTEDGOTO] = "jmp *",
+	[OP_UNWIND] = "unwind",
+	
+	/* Binary */
+	[OP_ADD] = "add",
+	[OP_SUB] = "sub",
+	[OP_MULU] = "mulu",
+	[OP_MULS] = "muls",
+	[OP_DIVU] = "divu",
+	[OP_DIVS] = "divs",
+	[OP_MODU] = "modu",
+	[OP_MODS] = "mods",
+	[OP_SHL] = "shl",
+	[OP_LSR] = "lsr",
+	[OP_ASR] = "asr",
+	
+	/* Logical */
+	[OP_AND] = "and",
+	[OP_OR] = "or",
+	[OP_XOR] = "xor",
+	[OP_AND_BOOL] = "and-bool",
+	[OP_OR_BOOL] = "or-bool",
+
+	/* Binary comparison */
+	[OP_SET_EQ] = "seteq",
+	[OP_SET_NE] = "setne",
+	[OP_SET_LE] = "setle",
+	[OP_SET_GE] = "setge",
+	[OP_SET_LT] = "setlt",
+	[OP_SET_GT] = "setgt",
+	[OP_SET_B] = "setb",
+	[OP_SET_A] = "seta",
+	[OP_SET_BE] = "setbe",
+	[OP_SET_AE] = "setae",
+
+	/* Uni */
+	[OP_NOT] = "not",
+	[OP_NEG] = "neg",
+
+	/* Special three-input */
+	[OP_SEL] = "select",
+	
+	/* Memory */
+	[OP_MALLOC] = "malloc",
+	[OP_FREE] = "free",
+	[OP_ALLOCA] = "alloca",
+	[OP_LOAD] = "load",
+	[OP_STORE] = "store",
+	[OP_SETVAL] = "set",
+	[OP_SYMADDR] = "symaddr",
+	[OP_GET_ELEMENT_PTR] = "getelem",
+
+	/* Other */
+	[OP_PHI] = "phi",
+	[OP_PHISOURCE] = "phisrc",
+	[OP_CAST] = "cast",
+	[OP_SCAST] = "scast",
+	[OP_FPCAST] = "fpcast",
+	[OP_PTRCAST] = "ptrcast",
+	[OP_INLINED_CALL] = "# call",
+	[OP_CALL] = "call",
+	[OP_VANEXT] = "va_next",
+	[OP_VAARG] = "va_arg",
+	[OP_SLICE] = "slice",
+	[OP_SNOP] = "snop",
+	[OP_LNOP] = "lnop",
+	[OP_NOP] = "nop",
+	[OP_DEATHNOTE] = "dead",
+	[OP_ASM] = "asm",
+
+	/* Sparse tagging (line numbers, context, whatever) */
+	[OP_CONTEXT] = "context",
+	[OP_RANGE] = "range-check",
+
+	[OP_COPY] = "copy",
+};
+
+static char *show_asm_constraints(char *buf, const char *sep, struct asm_constraint_list *list)
+{
+	struct asm_constraint *entry;
+
+	FOR_EACH_PTR(list, entry) {
+		buf += sprintf(buf, "%s\"%s\"", sep, entry->constraint);
+		if (entry->pseudo)
+			buf += sprintf(buf, " (%s)", show_pseudo(entry->pseudo));
+		if (entry->ident)
+			buf += sprintf(buf, " [%s]", show_ident(entry->ident));
+		sep = ", ";		
+	} END_FOR_EACH_PTR(entry);
+	return buf;
+}
+
+static char *show_asm(char *buf, struct instruction *insn)
+{
+	struct asm_rules *rules = insn->asm_rules;
+
+	buf += sprintf(buf, "\"%s\"", insn->string);
+	buf = show_asm_constraints(buf, "\n\t\tout: ", rules->outputs);
+	buf = show_asm_constraints(buf, "\n\t\tin: ", rules->inputs);
+	buf = show_asm_constraints(buf, "\n\t\tclobber: ", rules->clobbers);
+	return buf;
+}
+
+const char *show_instruction(struct instruction *insn)
+{
+	int opcode = insn->opcode;
+	static char buffer[4096];
+	char *buf;
+
+	buf = buffer;
+	if (!insn->bb)
+		buf += sprintf(buf, "# ");
+
+	if (opcode < ARRAY_SIZE(opcodes)) {
+		const char *op = opcodes[opcode];
+		if (!op)
+			buf += sprintf(buf, "opcode:%d", opcode);
+		else
+			buf += sprintf(buf, "%s", op);
+		if (insn->size)
+			buf += sprintf(buf, ".%d", insn->size);
+		memset(buf, ' ', 20);
+		buf++;
+	}
+
+	if (buf < buffer + 12)
+		buf = buffer + 12;
+	switch (opcode) {
+	case OP_RET:
+		if (insn->src && insn->src != VOID)
+			buf += sprintf(buf, "%s", show_pseudo(insn->src));
+		break;
+	case OP_BR:
+		if (insn->bb_true && insn->bb_false) {
+			buf += sprintf(buf, "%s, .L%p, .L%p", show_pseudo(insn->cond), insn->bb_true, insn->bb_false);
+			break;
+		}
+		buf += sprintf(buf, ".L%p", insn->bb_true ? insn->bb_true : insn->bb_false);
+		break;
+
+	case OP_SYMADDR: {
+		struct symbol *sym = insn->symbol->sym;
+		buf += sprintf(buf, "%s <- ", show_pseudo(insn->target));
+
+		if (sym->bb_target) {
+			buf += sprintf(buf, ".L%p", sym->bb_target);
+			break;
+		}
+		if (sym->ident) {
+			buf += sprintf(buf, "%s", show_ident(sym->ident));
+			break;
+		}
+		buf += sprintf(buf, "<anon symbol:%p>", sym);
+		break;
+	}
+		
+	case OP_SETVAL: {
+		struct expression *expr = insn->val;
+		buf += sprintf(buf, "%s <- ", show_pseudo(insn->target));
+
+		if (!expr) {
+			buf += sprintf(buf, "%s", "<none>");
+			break;
+		}
+			
+		switch (expr->type) {
+		case EXPR_VALUE:
+			buf += sprintf(buf, "%lld", expr->value);
+			break;
+		case EXPR_FVALUE:
+			buf += sprintf(buf, "%Lf", expr->fvalue);
+			break;
+		case EXPR_STRING:
+			buf += sprintf(buf, "%.40s", show_string(expr->string));
+			break;
+		case EXPR_SYMBOL:
+			buf += sprintf(buf, "%s", show_ident(expr->symbol->ident));
+			break;
+		case EXPR_LABEL:
+			buf += sprintf(buf, ".L%p", expr->symbol->bb_target);
+			break;
+		default:
+			buf += sprintf(buf, "SETVAL EXPR TYPE %d", expr->type);
+		}
+		break;
+	}
+	case OP_SWITCH: {
+		struct multijmp *jmp;
+		buf += sprintf(buf, "%s", show_pseudo(insn->target));
+		FOR_EACH_PTR(insn->multijmp_list, jmp) {
+			if (jmp->begin == jmp->end)
+				buf += sprintf(buf, ", %d -> .L%p", jmp->begin, jmp->target);
+			else if (jmp->begin < jmp->end)
+				buf += sprintf(buf, ", %d ... %d -> .L%p", jmp->begin, jmp->end, jmp->target);
+			else
+				buf += sprintf(buf, ", default -> .L%p", jmp->target);
+		} END_FOR_EACH_PTR(jmp);
+		break;
+	}
+	case OP_COMPUTEDGOTO: {
+		struct multijmp *jmp;
+		buf += sprintf(buf, "%s", show_pseudo(insn->target));
+		FOR_EACH_PTR(insn->multijmp_list, jmp) {
+			buf += sprintf(buf, ", .L%p", jmp->target);
+		} END_FOR_EACH_PTR(jmp);
+		break;
+	}
+
+	case OP_PHISOURCE: {
+		struct instruction *phi;
+		buf += sprintf(buf, "%s <- %s    ", show_pseudo(insn->target), show_pseudo(insn->phi_src));
+		FOR_EACH_PTR(insn->phi_users, phi) {
+			buf += sprintf(buf, " (%s)", show_pseudo(phi->target));
+		} END_FOR_EACH_PTR(phi);
+		break;
+	}
+
+	case OP_PHI: {
+		pseudo_t phi;
+		const char *s = " <-";
+		buf += sprintf(buf, "%s", show_pseudo(insn->target));
+		FOR_EACH_PTR(insn->phi_list, phi) {
+			buf += sprintf(buf, "%s %s", s, show_pseudo(phi));
+			s = ",";
+		} END_FOR_EACH_PTR(phi);
+		break;
+	}	
+	case OP_LOAD: case OP_LNOP:
+		buf += sprintf(buf, "%s <- %d[%s]", show_pseudo(insn->target), insn->offset, show_pseudo(insn->src));
+		break;
+	case OP_STORE: case OP_SNOP:
+		buf += sprintf(buf, "%s -> %d[%s]", show_pseudo(insn->target), insn->offset, show_pseudo(insn->src));
+		break;
+	case OP_INLINED_CALL:
+	case OP_CALL: {
+		struct pseudo *arg;
+		if (insn->target && insn->target != VOID)
+			buf += sprintf(buf, "%s <- ", show_pseudo(insn->target));
+		buf += sprintf(buf, "%s", show_pseudo(insn->func));
+		FOR_EACH_PTR(insn->arguments, arg) {
+			buf += sprintf(buf, ", %s", show_pseudo(arg));
+		} END_FOR_EACH_PTR(arg);
+		break;
+	}
+	case OP_CAST:
+	case OP_SCAST:
+	case OP_FPCAST:
+	case OP_PTRCAST:
+		buf += sprintf(buf, "%s <- (%d) %s",
+			show_pseudo(insn->target),
+			type_size(insn->orig_type),
+			show_pseudo(insn->src));
+		break;
+	case OP_BINARY ... OP_BINARY_END:
+	case OP_BINCMP ... OP_BINCMP_END:
+		buf += sprintf(buf, "%s <- %s, %s", show_pseudo(insn->target), show_pseudo(insn->src1), show_pseudo(insn->src2));
+		break;
+
+	case OP_SEL:
+		buf += sprintf(buf, "%s <- %s, %s, %s", show_pseudo(insn->target),
+			show_pseudo(insn->src1), show_pseudo(insn->src2), show_pseudo(insn->src3));
+		break;
+
+	case OP_SLICE:
+		buf += sprintf(buf, "%s <- %s, %d, %d", show_pseudo(insn->target), show_pseudo(insn->base), insn->from, insn->len);
+		break;
+
+	case OP_NOT: case OP_NEG:
+		buf += sprintf(buf, "%s <- %s", show_pseudo(insn->target), show_pseudo(insn->src1));
+		break;
+
+	case OP_CONTEXT:
+		buf += sprintf(buf, "%s%d", insn->check ? "check: " : "", insn->increment);
+		break;
+	case OP_RANGE:
+		buf += sprintf(buf, "%s between %s..%s", show_pseudo(insn->src1), show_pseudo(insn->src2), show_pseudo(insn->src3));
+		break;
+	case OP_NOP:
+		buf += sprintf(buf, "%s <- %s", show_pseudo(insn->target), show_pseudo(insn->src1));
+		break;
+	case OP_DEATHNOTE:
+		buf += sprintf(buf, "%s", show_pseudo(insn->target));
+		break;
+	case OP_ASM:
+		buf = show_asm(buf, insn);
+		break;
+	case OP_COPY:
+		buf += sprintf(buf, "%s <- %s", show_pseudo(insn->target), show_pseudo(insn->src));
+		break;
+	default:
+		break;
+	}
+
+	if (buf >= buffer + sizeof(buffer))
+		die("instruction buffer overflowed %td\n", buf - buffer);
+	do { --buf; } while (*buf == ' ');
+	*++buf = 0;
+	return buffer;
+}
+
+void show_bb(struct basic_block *bb)
+{
+	struct instruction *insn;
+
+	printf(".L%p:\n", bb);
+	if (verbose) {
+		pseudo_t needs, defines;
+		printf("%s:%d\n", stream_name(bb->pos.stream), bb->pos.line);
+
+		FOR_EACH_PTR(bb->needs, needs) {
+			struct instruction *def = needs->def;
+			if (def->opcode != OP_PHI) {
+				printf("  **uses %s (from .L%p)**\n", show_pseudo(needs), def->bb);
+			} else {
+				pseudo_t phi;
+				const char *sep = " ";
+				printf("  **uses %s (from", show_pseudo(needs));
+				FOR_EACH_PTR(def->phi_list, phi) {
+					if (phi == VOID)
+						continue;
+					printf("%s(%s:.L%p)", sep, show_pseudo(phi), phi->def->bb);
+					sep = ", ";
+				} END_FOR_EACH_PTR(phi);		
+				printf(")**\n");
+			}
+		} END_FOR_EACH_PTR(needs);
+
+		FOR_EACH_PTR(bb->defines, defines) {
+			printf("  **defines %s **\n", show_pseudo(defines));
+		} END_FOR_EACH_PTR(defines);
+
+		if (bb->parents) {
+			struct basic_block *from;
+			FOR_EACH_PTR(bb->parents, from) {
+				printf("  **from %p (%s:%d:%d)**\n", from,
+					stream_name(from->pos.stream), from->pos.line, from->pos.pos);
+			} END_FOR_EACH_PTR(from);
+		}
+
+		if (bb->children) {
+			struct basic_block *to;
+			FOR_EACH_PTR(bb->children, to) {
+				printf("  **to %p (%s:%d:%d)**\n", to,
+					stream_name(to->pos.stream), to->pos.line, to->pos.pos);
+			} END_FOR_EACH_PTR(to);
+		}
+	}
+
+	FOR_EACH_PTR(bb->insns, insn) {
+		if (!insn->bb && verbose < 2)
+			continue;
+		printf("\t%s\n", show_instruction(insn));
+	} END_FOR_EACH_PTR(insn);
+	if (!bb_terminated(bb))
+		printf("\tEND\n");
+}
+
+static void show_symbol_usage(pseudo_t pseudo)
+{
+	struct pseudo_user *pu;
+
+	if (pseudo) {
+		FOR_EACH_PTR(pseudo->users, pu) {
+			printf("\t%s\n", show_instruction(pu->insn));
+		} END_FOR_EACH_PTR(pu);
+	}
+}
+
+void show_entry(struct entrypoint *ep)
+{
+	struct symbol *sym;
+	struct basic_block *bb;
+
+	printf("%s:\n", show_ident(ep->name->ident));
+
+	if (verbose) {
+		printf("ep %p: %s\n", ep, show_ident(ep->name->ident));
+
+		FOR_EACH_PTR(ep->syms, sym) {
+			if (!sym->pseudo)
+				continue;
+			if (!sym->pseudo->users)
+				continue;
+			printf("   sym: %p %s\n", sym, show_ident(sym->ident));
+			if (sym->ctype.modifiers & (MOD_EXTERN | MOD_STATIC | MOD_ADDRESSABLE))
+				printf("\texternal visibility\n");
+			show_symbol_usage(sym->pseudo);
+		} END_FOR_EACH_PTR(sym);
+
+		printf("\n");
+	}
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		if (!bb)
+			continue;
+		if (!bb->parents && !bb->children && !bb->insns && verbose < 2)
+			continue;
+		show_bb(bb);
+		printf("\n");
+	} END_FOR_EACH_PTR(bb);
+
+	printf("\n");
+}
+
+static void bind_label(struct symbol *label, struct basic_block *bb, struct position pos)
+{
+	if (label->bb_target)
+		warning(pos, "label '%s' already bound", show_ident(label->ident));
+	label->bb_target = bb;
+}
+
+static struct basic_block * get_bound_block(struct entrypoint *ep, struct symbol *label)
+{
+	struct basic_block *bb = label->bb_target;
+
+	if (!bb) {
+		bb = alloc_basic_block(ep, label->pos);
+		label->bb_target = bb;
+	}
+	return bb;
+}
+
+static void finish_block(struct entrypoint *ep)
+{
+	struct basic_block *src = ep->active;
+	if (bb_reachable(src))
+		ep->active = NULL;
+}
+
+static void add_goto(struct entrypoint *ep, struct basic_block *dst)
+{
+	struct basic_block *src = ep->active;
+	if (bb_reachable(src)) {
+		struct instruction *br = alloc_instruction(OP_BR, 0);
+		br->bb_true = dst;
+		add_bb(&dst->parents, src);
+		add_bb(&src->children, dst);
+		br->bb = src;
+		add_instruction(&src->insns, br);
+		ep->active = NULL;
+	}
+}
+
+static void add_one_insn(struct entrypoint *ep, struct instruction *insn)
+{
+	struct basic_block *bb = ep->active;    
+
+	if (bb_reachable(bb)) {
+		insn->bb = bb;
+		add_instruction(&bb->insns, insn);
+	}
+}
+
+static void set_activeblock(struct entrypoint *ep, struct basic_block *bb)
+{
+	if (!bb_terminated(ep->active))
+		add_goto(ep, bb);
+
+	ep->active = bb;
+	if (bb_reachable(bb))
+		add_bb(&ep->bbs, bb);
+}
+
+static void remove_parent(struct basic_block *child, struct basic_block *parent)
+{
+	remove_bb_from_list(&child->parents, parent, 1);
+	if (!child->parents)
+		kill_bb(child);
+}
+
+/* Change a "switch" into a branch */
+void insert_branch(struct basic_block *bb, struct instruction *jmp, struct basic_block *target)
+{
+	struct instruction *br, *old;
+	struct basic_block *child;
+
+	/* Remove the switch */
+	old = delete_last_instruction(&bb->insns);
+	assert(old == jmp);
+
+	br = alloc_instruction(OP_BR, 0);
+	br->bb = bb;
+	br->bb_true = target;
+	add_instruction(&bb->insns, br);
+
+	FOR_EACH_PTR(bb->children, child) {
+		if (child == target) {
+			target = NULL;	/* Trigger just once */
+			continue;
+		}
+		DELETE_CURRENT_PTR(child);
+		remove_parent(child, bb);
+	} END_FOR_EACH_PTR(child);
+	PACK_PTR_LIST(&bb->children);
+}
+	
+
+void insert_select(struct basic_block *bb, struct instruction *br, struct instruction *phi_node, pseudo_t if_true, pseudo_t if_false)
+{
+	pseudo_t target;
+	struct instruction *select;
+
+	/* Remove the 'br' */
+	delete_last_instruction(&bb->insns);
+
+	select = alloc_instruction(OP_SEL, phi_node->size);
+	select->bb = bb;
+
+	assert(br->cond);
+	use_pseudo(select, br->cond, &select->src1);
+
+	target = phi_node->target;
+	assert(target->def == phi_node);
+	select->target = target;
+	target->def = select;
+
+	use_pseudo(select, if_true, &select->src2);
+	use_pseudo(select, if_false, &select->src3);
+
+	add_instruction(&bb->insns, select);
+	add_instruction(&bb->insns, br);
+}
+
+static inline int bb_empty(struct basic_block *bb)
+{
+	return !bb->insns;
+}
+
+/* Add a label to the currently active block, return new active block */
+static struct basic_block * add_label(struct entrypoint *ep, struct symbol *label)
+{
+	struct basic_block *bb = label->bb_target;
+
+	if (bb) {
+		set_activeblock(ep, bb);
+		return bb;
+	}
+	bb = ep->active;
+	if (!bb_reachable(bb) || !bb_empty(bb)) {
+		bb = alloc_basic_block(ep, label->pos);
+		set_activeblock(ep, bb);
+	}
+	label->bb_target = bb;
+	return bb;
+}
+
+static void add_branch(struct entrypoint *ep, struct expression *expr, pseudo_t cond, struct basic_block *bb_true, struct basic_block *bb_false)
+{
+	struct basic_block *bb = ep->active;
+	struct instruction *br;
+
+	if (bb_reachable(bb)) {
+       		br = alloc_instruction(OP_BR, 0);
+		use_pseudo(br, cond, &br->cond);
+		br->bb_true = bb_true;
+		br->bb_false = bb_false;
+		add_bb(&bb_true->parents, bb);
+		add_bb(&bb_false->parents, bb);
+		add_bb(&bb->children, bb_true);
+		add_bb(&bb->children, bb_false);
+		add_one_insn(ep, br);
+	}
+}
+
+/* Dummy pseudo allocator */
+pseudo_t alloc_pseudo(struct instruction *def)
+{
+	static int nr = 0;
+	struct pseudo * pseudo = __alloc_pseudo(0);
+	pseudo->type = PSEUDO_REG;
+	pseudo->nr = ++nr;
+	pseudo->def = def;
+	return pseudo;
+}
+
+static void clear_symbol_pseudos(struct entrypoint *ep)
+{
+	pseudo_t pseudo;
+
+	FOR_EACH_PTR(ep->accesses, pseudo) {
+		pseudo->sym->pseudo = NULL;
+	} END_FOR_EACH_PTR(pseudo);
+}
+
+static pseudo_t symbol_pseudo(struct entrypoint *ep, struct symbol *sym)
+{
+	pseudo_t pseudo;
+
+	if (!sym)
+		return VOID;
+
+	pseudo = sym->pseudo;
+	if (!pseudo) {
+		pseudo = __alloc_pseudo(0);
+		pseudo->nr = -1;
+		pseudo->type = PSEUDO_SYM;
+		pseudo->sym = sym;
+		pseudo->ident = sym->ident;
+		sym->pseudo = pseudo;
+		add_pseudo(&ep->accesses, pseudo);
+	}
+	/* Symbol pseudos have neither nr, usage nor def */
+	return pseudo;
+}
+
+pseudo_t value_pseudo(long long val)
+{
+#define MAX_VAL_HASH 64
+	static struct pseudo_list *prev[MAX_VAL_HASH];
+	int hash = val & (MAX_VAL_HASH-1);
+	struct pseudo_list **list = prev + hash;
+	pseudo_t pseudo;
+
+	FOR_EACH_PTR(*list, pseudo) {
+		if (pseudo->value == val)
+			return pseudo;
+	} END_FOR_EACH_PTR(pseudo);
+
+	pseudo = __alloc_pseudo(0);
+	pseudo->type = PSEUDO_VAL;
+	pseudo->value = val;
+	add_pseudo(list, pseudo);
+
+	/* Value pseudos have neither nr, usage nor def */
+	return pseudo;
+}
+
+static pseudo_t argument_pseudo(struct entrypoint *ep, int nr)
+{
+	pseudo_t pseudo = __alloc_pseudo(0);
+	struct instruction *entry = ep->entry;
+
+	pseudo->type = PSEUDO_ARG;
+	pseudo->nr = nr;
+	pseudo->def = entry;
+	add_pseudo(&entry->arg_list, pseudo);
+
+	/* Argument pseudos have neither usage nor def */
+	return pseudo;
+}
+
+pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, int size)
+{
+	struct instruction *insn = alloc_instruction(OP_PHISOURCE, size);
+	pseudo_t phi = __alloc_pseudo(0);
+	static int nr = 0;
+
+	phi->type = PSEUDO_PHI;
+	phi->nr = ++nr;
+	phi->def = insn;
+
+	use_pseudo(insn, pseudo, &insn->phi_src);
+	insn->bb = source;
+	insn->target = phi;
+	add_instruction(&source->insns, insn);
+	return phi;
+}
+
+/*
+ * We carry the "access_data" structure around for any accesses,
+ * which simplifies things a lot. It contains all the access
+ * information in one place.
+ */
+struct access_data {
+	struct symbol *result_type;	// result ctype
+	struct symbol *source_type;	// source ctype
+	pseudo_t address;		// pseudo containing address ..
+	pseudo_t origval;		// pseudo for original value ..
+	unsigned int offset, alignment;	// byte offset
+	unsigned int bit_size, bit_offset; // which bits
+	struct position pos;
+};
+
+static void finish_address_gen(struct entrypoint *ep, struct access_data *ad)
+{
+}
+
+static int linearize_simple_address(struct entrypoint *ep,
+	struct expression *addr,
+	struct access_data *ad)
+{
+	if (addr->type == EXPR_SYMBOL) {
+		linearize_one_symbol(ep, addr->symbol);
+		ad->address = symbol_pseudo(ep, addr->symbol);
+		return 1;
+	}
+	if (addr->type == EXPR_BINOP) {
+		if (addr->right->type == EXPR_VALUE) {
+			if (addr->op == '+') {
+				ad->offset += get_expression_value(addr->right);
+				return linearize_simple_address(ep, addr->left, ad);
+			}
+		}
+	}
+	ad->address = linearize_expression(ep, addr);
+	return 1;
+}
+
+static struct symbol *base_type(struct symbol *sym)
+{
+	struct symbol *base = sym;
+
+	if (sym) {
+		if (sym->type == SYM_NODE)
+			base = base->ctype.base_type;
+		if (base->type == SYM_BITFIELD)
+			return base->ctype.base_type;
+	}
+	return sym;
+}
+
+static int linearize_address_gen(struct entrypoint *ep,
+	struct expression *expr,
+	struct access_data *ad)
+{
+	struct symbol *ctype = expr->ctype;
+
+	if (!ctype)
+		return 0;
+	ad->pos = expr->pos;
+	ad->result_type = ctype;
+	ad->source_type = base_type(ctype);
+	ad->bit_size = ctype->bit_size;
+	ad->alignment = ctype->ctype.alignment;
+	ad->bit_offset = ctype->bit_offset;
+	if (expr->type == EXPR_PREOP && expr->op == '*')
+		return linearize_simple_address(ep, expr->unop, ad);
+
+	warning(expr->pos, "generating address of non-lvalue (%d)", expr->type);
+	return 0;
+}
+
+static pseudo_t add_load(struct entrypoint *ep, struct access_data *ad)
+{
+	struct instruction *insn;
+	pseudo_t new;
+
+	new = ad->origval;
+	if (0 && new)
+		return new;
+
+	insn = alloc_typed_instruction(OP_LOAD, ad->source_type);
+	new = alloc_pseudo(insn);
+	ad->origval = new;
+
+	insn->target = new;
+	insn->offset = ad->offset;
+	use_pseudo(insn, ad->address, &insn->src);
+	add_one_insn(ep, insn);
+	return new;
+}
+
+static void add_store(struct entrypoint *ep, struct access_data *ad, pseudo_t value)
+{
+	struct basic_block *bb = ep->active;
+
+	if (bb_reachable(bb)) {
+		struct instruction *store = alloc_typed_instruction(OP_STORE, ad->source_type);
+		store->offset = ad->offset;
+		use_pseudo(store, value, &store->target);
+		use_pseudo(store, ad->address, &store->src);
+		add_one_insn(ep, store);
+	}
+}
+
+static pseudo_t linearize_store_gen(struct entrypoint *ep,
+		pseudo_t value,
+		struct access_data *ad)
+{
+	pseudo_t store = value;
+
+	if (type_size(ad->source_type) != type_size(ad->result_type)) {
+		pseudo_t orig = add_load(ep, ad);
+		int shift = ad->bit_offset;
+		unsigned long long mask = (1ULL << ad->bit_size)-1;
+
+		if (shift) {
+			store = add_binary_op(ep, ad->source_type, OP_SHL, value, value_pseudo(shift));
+			mask <<= shift;
+		}
+		orig = add_binary_op(ep, ad->source_type, OP_AND, orig, value_pseudo(~mask));
+		store = add_binary_op(ep, ad->source_type, OP_OR, orig, store);
+	}
+	add_store(ep, ad, store);
+	return value;
+}
+
+static pseudo_t add_binary_op(struct entrypoint *ep, struct symbol *ctype, int op, pseudo_t left, pseudo_t right)
+{
+	struct instruction *insn = alloc_typed_instruction(op, ctype);
+	pseudo_t target = alloc_pseudo(insn);
+	insn->target = target;
+	use_pseudo(insn, left, &insn->src1);
+	use_pseudo(insn, right, &insn->src2);
+	add_one_insn(ep, insn);
+	return target;
+}
+
+static pseudo_t add_setval(struct entrypoint *ep, struct symbol *ctype, struct expression *val)
+{
+	struct instruction *insn = alloc_typed_instruction(OP_SETVAL, ctype);
+	pseudo_t target = alloc_pseudo(insn);
+	insn->target = target;
+	insn->val = val;
+	add_one_insn(ep, insn);
+	return target;
+}
+
+static pseudo_t add_symbol_address(struct entrypoint *ep, struct symbol *sym)
+{
+	struct instruction *insn = alloc_instruction(OP_SYMADDR, bits_in_pointer);
+	pseudo_t target = alloc_pseudo(insn);
+
+	insn->target = target;
+	use_pseudo(insn, symbol_pseudo(ep, sym), &insn->symbol);
+	add_one_insn(ep, insn);
+	return target;
+}
+
+static pseudo_t linearize_load_gen(struct entrypoint *ep, struct access_data *ad)
+{
+	pseudo_t new = add_load(ep, ad);
+
+	if (ad->bit_offset) {
+		pseudo_t shift = value_pseudo(ad->bit_offset);
+		pseudo_t newval = add_binary_op(ep, ad->source_type, OP_LSR, new, shift);
+		new = newval;
+	}
+		
+	return new;
+}
+
+static pseudo_t linearize_access(struct entrypoint *ep, struct expression *expr)
+{
+	struct access_data ad = { NULL, };
+	pseudo_t value;
+
+	if (!linearize_address_gen(ep, expr, &ad))
+		return VOID;
+	value = linearize_load_gen(ep, &ad);
+	finish_address_gen(ep, &ad);
+	return value;
+}
+
+/* FIXME: FP */
+static pseudo_t linearize_inc_dec(struct entrypoint *ep, struct expression *expr, int postop)
+{
+	struct access_data ad = { NULL, };
+		pseudo_t old, new, one;
+	int op = expr->op == SPECIAL_INCREMENT ? OP_ADD : OP_SUB;
+
+	if (!linearize_address_gen(ep, expr->unop, &ad))
+		return VOID;
+
+	old = linearize_load_gen(ep, &ad);
+	one = value_pseudo(expr->op_value);
+	new = add_binary_op(ep, expr->ctype, op, old, one);
+	linearize_store_gen(ep, new, &ad);
+	finish_address_gen(ep, &ad);
+	return postop ? old : new;
+}
+
+static pseudo_t add_uniop(struct entrypoint *ep, struct expression *expr, int op, pseudo_t src)
+{
+	struct instruction *insn = alloc_typed_instruction(op, expr->ctype);
+	pseudo_t new = alloc_pseudo(insn);
+
+	insn->target = new;
+	use_pseudo(insn, src, &insn->src1);
+	add_one_insn(ep, insn);
+	return new;
+}
+
+static pseudo_t linearize_slice(struct entrypoint *ep, struct expression *expr)
+{
+	pseudo_t pre = linearize_expression(ep, expr->base);
+	struct instruction *insn = alloc_typed_instruction(OP_SLICE, expr->ctype);
+	pseudo_t new = alloc_pseudo(insn);
+
+	insn->target = new;
+	insn->from = expr->r_bitpos;
+	insn->len = expr->r_nrbits;
+	use_pseudo(insn, pre, &insn->base);
+	add_one_insn(ep, insn);
+	return new;
+}
+
+static pseudo_t linearize_regular_preop(struct entrypoint *ep, struct expression *expr)
+{
+	pseudo_t pre = linearize_expression(ep, expr->unop);
+	switch (expr->op) {
+	case '+':
+		return pre;
+	case '!': {
+		pseudo_t zero = value_pseudo(0);
+		return add_binary_op(ep, expr->unop->ctype, OP_SET_EQ, pre, zero);
+	}
+	case '~':
+		return add_uniop(ep, expr, OP_NOT, pre);
+	case '-':
+		return add_uniop(ep, expr, OP_NEG, pre);
+	}
+	return VOID;
+}
+
+static pseudo_t linearize_preop(struct entrypoint *ep, struct expression *expr)
+{
+	/*
+	 * '*' is an lvalue access, and is fundamentally different
+	 * from an arithmetic operation. Maybe it should have an
+	 * expression type of its own..
+	 */
+	if (expr->op == '*')
+		return linearize_access(ep, expr);
+	if (expr->op == SPECIAL_INCREMENT || expr->op == SPECIAL_DECREMENT)
+		return linearize_inc_dec(ep, expr, 0);
+	return linearize_regular_preop(ep, expr);
+}
+
+static pseudo_t linearize_postop(struct entrypoint *ep, struct expression *expr)
+{
+	return linearize_inc_dec(ep, expr, 1);
+}	
+
+/*
+ * Casts to pointers are "less safe" than other casts, since
+ * they imply type-unsafe accesses. "void *" is a special
+ * case, since you can't access through it anyway without another
+ * cast.
+ */
+static struct instruction *alloc_cast_instruction(struct symbol *src, struct symbol *ctype)
+{
+	int opcode = OP_CAST;
+	struct symbol *base = src;
+
+	if (base->ctype.modifiers & MOD_SIGNED)
+		opcode = OP_SCAST;
+	if (base->type == SYM_NODE)
+		base = base->ctype.base_type;
+	if (base->type == SYM_PTR) {
+		base = base->ctype.base_type;
+		if (base != &void_ctype)
+			opcode = OP_PTRCAST;
+	}
+	if (base->ctype.base_type == &fp_type)
+		opcode = OP_FPCAST;
+	return alloc_typed_instruction(opcode, ctype);
+}
+
+static pseudo_t cast_pseudo(struct entrypoint *ep, pseudo_t src, struct symbol *from, struct symbol *to)
+{
+	pseudo_t result;
+	struct instruction *insn;
+
+	if (src == VOID)
+		return VOID;
+	if (!from || !to)
+		return VOID;
+	if (from->bit_size < 0 || to->bit_size < 0)
+		return VOID;
+	insn = alloc_cast_instruction(from, to);
+	result = alloc_pseudo(insn);
+	insn->target = result;
+	insn->orig_type = from;
+	use_pseudo(insn, src, &insn->src);
+	add_one_insn(ep, insn);
+	return result;
+}
+
+static int opcode_sign(int opcode, struct symbol *ctype)
+{
+	if (ctype && (ctype->ctype.modifiers & MOD_SIGNED)) {
+		switch(opcode) {
+		case OP_MULU: case OP_DIVU: case OP_MODU: case OP_LSR:
+			opcode++;
+		}
+	}
+	return opcode;
+}
+
+static pseudo_t linearize_assignment(struct entrypoint *ep, struct expression *expr)
+{
+	struct access_data ad = { NULL, };
+	struct expression *target = expr->left;
+	struct expression *src = expr->right;
+	pseudo_t value;
+
+	value = linearize_expression(ep, src);
+	if (!target || !linearize_address_gen(ep, target, &ad))
+		return value;
+	if (expr->op != '=') {
+		pseudo_t oldvalue = linearize_load_gen(ep, &ad);
+		pseudo_t dst;
+		static const int op_trans[] = {
+			[SPECIAL_ADD_ASSIGN - SPECIAL_BASE] = OP_ADD,
+			[SPECIAL_SUB_ASSIGN - SPECIAL_BASE] = OP_SUB,
+			[SPECIAL_MUL_ASSIGN - SPECIAL_BASE] = OP_MULU,
+			[SPECIAL_DIV_ASSIGN - SPECIAL_BASE] = OP_DIVU,
+			[SPECIAL_MOD_ASSIGN - SPECIAL_BASE] = OP_MODU,
+			[SPECIAL_SHL_ASSIGN - SPECIAL_BASE] = OP_SHL,
+			[SPECIAL_SHR_ASSIGN - SPECIAL_BASE] = OP_LSR,
+			[SPECIAL_AND_ASSIGN - SPECIAL_BASE] = OP_AND,
+			[SPECIAL_OR_ASSIGN  - SPECIAL_BASE] = OP_OR,
+			[SPECIAL_XOR_ASSIGN - SPECIAL_BASE] = OP_XOR
+		};
+		int opcode;
+
+		if (!src)
+			return VOID;
+
+		oldvalue = cast_pseudo(ep, oldvalue, src->ctype, expr->ctype);
+		opcode = opcode_sign(op_trans[expr->op - SPECIAL_BASE], src->ctype);
+		dst = add_binary_op(ep, src->ctype, opcode, oldvalue, value);
+		value = cast_pseudo(ep, dst, expr->ctype, src->ctype);
+	}
+	value = linearize_store_gen(ep, value, &ad);
+	finish_address_gen(ep, &ad);
+	return value;
+}
+
+static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expression *expr)
+{
+	struct expression *arg, *fn;
+	struct instruction *insn = alloc_typed_instruction(OP_CALL, expr->ctype);
+	pseudo_t retval, call;
+	struct ctype *ctype = NULL;
+	struct context *context;
+
+	if (!expr->ctype) {
+		warning(expr->pos, "call with no type!");
+		return VOID;
+	}
+
+	FOR_EACH_PTR(expr->args, arg) {
+		pseudo_t new = linearize_expression(ep, arg);
+		use_pseudo(insn, new, add_pseudo(&insn->arguments, new));
+	} END_FOR_EACH_PTR(arg);
+
+	fn = expr->fn;
+
+	if (fn->ctype)
+		ctype = &fn->ctype->ctype;
+
+	if (fn->type == EXPR_PREOP) {
+		if (fn->unop->type == EXPR_SYMBOL) {
+			struct symbol *sym = fn->unop->symbol;
+			if (sym->ctype.base_type->type == SYM_FN)
+				fn = fn->unop;
+		}
+	}
+	if (fn->type == EXPR_SYMBOL) {
+		call = symbol_pseudo(ep, fn->symbol);
+	} else {
+		call = linearize_expression(ep, fn);
+	}
+	use_pseudo(insn, call, &insn->func);
+	retval = VOID;
+	if (expr->ctype != &void_ctype)
+		retval = alloc_pseudo(insn);
+	insn->target = retval;
+	add_one_insn(ep, insn);
+
+	if (ctype) {
+		FOR_EACH_PTR(ctype->contexts, context) {
+			int in = context->in;
+			int out = context->out;
+			int check = 0;
+			int context_diff;
+			if (in < 0) {
+				check = 1;
+				in = 0;
+			}
+			if (out < 0) {
+				check = 0;
+				out = 0;
+			}
+			context_diff = out - in;
+			if (check || context_diff) {
+				insn = alloc_instruction(OP_CONTEXT, 0);
+				insn->increment = context_diff;
+				insn->check = check;
+				insn->context_expr = context->context;
+				add_one_insn(ep, insn);
+			}
+		} END_FOR_EACH_PTR(context);
+	}
+
+	return retval;
+}
+
+static pseudo_t linearize_binop(struct entrypoint *ep, struct expression *expr)
+{
+	pseudo_t src1, src2, dst;
+	static const int opcode[] = {
+		['+'] = OP_ADD, ['-'] = OP_SUB,
+		['*'] = OP_MULU, ['/'] = OP_DIVU,
+		['%'] = OP_MODU, ['&'] = OP_AND,
+		['|'] = OP_OR,  ['^'] = OP_XOR,
+		[SPECIAL_LEFTSHIFT] = OP_SHL,
+		[SPECIAL_RIGHTSHIFT] = OP_LSR,
+		[SPECIAL_LOGICAL_AND] = OP_AND_BOOL,
+		[SPECIAL_LOGICAL_OR] = OP_OR_BOOL,
+	};
+	int op;
+
+	src1 = linearize_expression(ep, expr->left);
+	src2 = linearize_expression(ep, expr->right);
+	op = opcode_sign(opcode[expr->op], expr->ctype);
+	dst = add_binary_op(ep, expr->ctype, op, src1, src2);
+	return dst;
+}
+
+static pseudo_t linearize_logical_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false);
+
+pseudo_t linearize_cond_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false);
+
+static pseudo_t linearize_select(struct entrypoint *ep, struct expression *expr)
+{
+	pseudo_t cond, true, false, res;
+	struct instruction *insn;
+
+	true = linearize_expression(ep, expr->cond_true);
+	false = linearize_expression(ep, expr->cond_false);
+	cond = linearize_expression(ep, expr->conditional);
+
+	insn = alloc_typed_instruction(OP_SEL, expr->ctype);
+	if (!expr->cond_true)
+		true = cond;
+	use_pseudo(insn, cond, &insn->src1);
+	use_pseudo(insn, true, &insn->src2);
+	use_pseudo(insn, false, &insn->src3);
+
+	res = alloc_pseudo(insn);
+	insn->target = res;
+	add_one_insn(ep, insn);
+	return res;
+}
+
+static pseudo_t add_join_conditional(struct entrypoint *ep, struct expression *expr,
+				     pseudo_t phi1, pseudo_t phi2)
+{
+	pseudo_t target;
+	struct instruction *phi_node;
+
+	if (phi1 == VOID)
+		return phi2;
+	if (phi2 == VOID)
+		return phi1;
+
+	phi_node = alloc_typed_instruction(OP_PHI, expr->ctype);
+	use_pseudo(phi_node, phi1, add_pseudo(&phi_node->phi_list, phi1));
+	use_pseudo(phi_node, phi2, add_pseudo(&phi_node->phi_list, phi2));
+	phi_node->target = target = alloc_pseudo(phi_node);
+	add_one_insn(ep, phi_node);
+	return target;
+}	
+
+static pseudo_t linearize_short_conditional(struct entrypoint *ep, struct expression *expr,
+					    struct expression *cond,
+					    struct expression *expr_false)
+{
+	pseudo_t src1, src2;
+	struct basic_block *bb_false;
+	struct basic_block *merge = alloc_basic_block(ep, expr->pos);
+	pseudo_t phi1, phi2;
+	int size = type_size(expr->ctype);
+
+	if (!expr_false || !ep->active)
+		return VOID;
+
+	bb_false = alloc_basic_block(ep, expr_false->pos);
+	src1 = linearize_expression(ep, cond);
+	phi1 = alloc_phi(ep->active, src1, size);
+	add_branch(ep, expr, src1, merge, bb_false);
+
+	set_activeblock(ep, bb_false);
+	src2 = linearize_expression(ep, expr_false);
+	phi2 = alloc_phi(ep->active, src2, size);
+	set_activeblock(ep, merge);
+
+	return add_join_conditional(ep, expr, phi1, phi2);
+}
+
+static pseudo_t linearize_conditional(struct entrypoint *ep, struct expression *expr,
+				      struct expression *cond,
+				      struct expression *expr_true,
+				      struct expression *expr_false)
+{
+	pseudo_t src1, src2;
+	pseudo_t phi1, phi2;
+	struct basic_block *bb_true, *bb_false, *merge;
+	int size = type_size(expr->ctype);
+
+	if (!cond || !expr_true || !expr_false || !ep->active)
+		return VOID;
+	bb_true = alloc_basic_block(ep, expr_true->pos);
+	bb_false = alloc_basic_block(ep, expr_false->pos);
+	merge = alloc_basic_block(ep, expr->pos);
+
+	linearize_cond_branch(ep, cond, bb_true, bb_false);
+
+	set_activeblock(ep, bb_true);
+	src1 = linearize_expression(ep, expr_true);
+	phi1 = alloc_phi(ep->active, src1, size);
+	add_goto(ep, merge); 
+
+	set_activeblock(ep, bb_false);
+	src2 = linearize_expression(ep, expr_false);
+	phi2 = alloc_phi(ep->active, src2, size);
+	set_activeblock(ep, merge);
+
+	return add_join_conditional(ep, expr, phi1, phi2);
+}
+
+static pseudo_t linearize_logical(struct entrypoint *ep, struct expression *expr)
+{
+	struct expression *shortcut;
+
+	shortcut = alloc_const_expression(expr->pos, expr->op == SPECIAL_LOGICAL_OR);
+	shortcut->ctype = expr->ctype;
+	if (expr->op == SPECIAL_LOGICAL_OR)
+		return linearize_conditional(ep, expr, expr->left, shortcut, expr->right);
+	return linearize_conditional(ep, expr, expr->left, expr->right, shortcut);
+}
+
+static pseudo_t linearize_compare(struct entrypoint *ep, struct expression *expr)
+{
+	static const int cmpop[] = {
+		['>'] = OP_SET_GT, ['<'] = OP_SET_LT,
+		[SPECIAL_EQUAL] = OP_SET_EQ,
+		[SPECIAL_NOTEQUAL] = OP_SET_NE,
+		[SPECIAL_GTE] = OP_SET_GE,
+		[SPECIAL_LTE] = OP_SET_LE,
+		[SPECIAL_UNSIGNED_LT] = OP_SET_B,
+		[SPECIAL_UNSIGNED_GT] = OP_SET_A,
+		[SPECIAL_UNSIGNED_LTE] = OP_SET_BE,
+		[SPECIAL_UNSIGNED_GTE] = OP_SET_AE,
+	};
+
+	pseudo_t src1 = linearize_expression(ep, expr->left);
+	pseudo_t src2 = linearize_expression(ep, expr->right);
+	pseudo_t dst = add_binary_op(ep, expr->left->ctype, cmpop[expr->op], src1, src2);
+	return dst;
+}
+
+
+pseudo_t linearize_cond_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false)
+{
+	pseudo_t cond;
+
+	if (!expr || !bb_reachable(ep->active))
+		return VOID;
+
+	switch (expr->type) {
+
+	case EXPR_STRING:
+	case EXPR_VALUE:
+		add_goto(ep, expr->value ? bb_true : bb_false);
+		return VOID;
+
+	case EXPR_FVALUE:
+		add_goto(ep, expr->fvalue ? bb_true : bb_false);
+		return VOID;
+		
+	case EXPR_LOGICAL:
+		linearize_logical_branch(ep, expr, bb_true, bb_false);
+		return VOID;
+
+	case EXPR_COMPARE:
+		cond = linearize_compare(ep, expr);
+		add_branch(ep, expr, cond, bb_true, bb_false);
+		break;
+		
+	case EXPR_PREOP:
+		if (expr->op == '!')
+			return linearize_cond_branch(ep, expr->unop, bb_false, bb_true);
+		/* fall through */
+	default: {
+		cond = linearize_expression(ep, expr);
+		add_branch(ep, expr, cond, bb_true, bb_false);
+
+		return VOID;
+	}
+	}
+	return VOID;
+}
+
+
+	
+static pseudo_t linearize_logical_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false)
+{
+	struct basic_block *next = alloc_basic_block(ep, expr->pos);
+
+	if (expr->op == SPECIAL_LOGICAL_OR)
+		linearize_cond_branch(ep, expr->left, bb_true, next);
+	else
+		linearize_cond_branch(ep, expr->left, next, bb_false);
+	set_activeblock(ep, next);
+	linearize_cond_branch(ep, expr->right, bb_true, bb_false);
+	return VOID;
+}
+
+static pseudo_t linearize_cast(struct entrypoint *ep, struct expression *expr)
+{
+	pseudo_t src;
+	struct expression *orig = expr->cast_expression;
+
+	if (!orig)
+		return VOID;
+
+	src = linearize_expression(ep, orig);
+	return cast_pseudo(ep, src, orig->ctype, expr->ctype);
+}
+
+static pseudo_t linearize_position(struct entrypoint *ep, struct expression *pos, struct access_data *ad)
+{
+	struct expression *init_expr = pos->init_expr;
+
+	ad->offset = pos->init_offset;
+	ad->source_type = base_type(init_expr->ctype);
+	ad->result_type = init_expr->ctype;
+	return linearize_initializer(ep, init_expr, ad);
+}
+
+static pseudo_t linearize_initializer(struct entrypoint *ep, struct expression *initializer, struct access_data *ad)
+{
+	switch (initializer->type) {
+	case EXPR_INITIALIZER: {
+		struct expression *expr;
+		FOR_EACH_PTR(initializer->expr_list, expr) {
+			linearize_initializer(ep, expr, ad);
+		} END_FOR_EACH_PTR(expr);
+		break;
+	}
+	case EXPR_POS:
+		linearize_position(ep, initializer, ad);
+		break;
+	default: {
+		pseudo_t value = linearize_expression(ep, initializer);
+		ad->source_type = base_type(initializer->ctype);
+		ad->result_type = initializer->ctype;
+		linearize_store_gen(ep, value, ad);
+		return value;
+	}
+	}
+
+	return VOID;
+}
+
+static void linearize_argument(struct entrypoint *ep, struct symbol *arg, int nr)
+{
+	struct access_data ad = { NULL, };
+
+	ad.source_type = arg;
+	ad.result_type = arg;
+	ad.address = symbol_pseudo(ep, arg);
+	linearize_store_gen(ep, argument_pseudo(ep, nr), &ad);
+	finish_address_gen(ep, &ad);
+}
+
+pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr)
+{
+	if (!expr)
+		return VOID;
+
+	current_pos = expr->pos;
+	switch (expr->type) {
+	case EXPR_SYMBOL:
+		linearize_one_symbol(ep, expr->symbol);
+		return add_symbol_address(ep, expr->symbol);
+
+	case EXPR_VALUE:
+		return value_pseudo(expr->value);
+
+	case EXPR_STRING: case EXPR_FVALUE: case EXPR_LABEL:
+		return add_setval(ep, expr->ctype, expr);
+
+	case EXPR_STATEMENT:
+		return linearize_statement(ep, expr->statement);
+
+	case EXPR_CALL:
+		return linearize_call_expression(ep, expr);
+
+	case EXPR_BINOP:
+		return linearize_binop(ep, expr);
+
+	case EXPR_LOGICAL:
+		return linearize_logical(ep, expr);
+
+	case EXPR_COMPARE:
+		return  linearize_compare(ep, expr);
+
+	case EXPR_SELECT:
+		return	linearize_select(ep, expr);
+
+	case EXPR_CONDITIONAL:
+		if (!expr->cond_true)
+			return linearize_short_conditional(ep, expr, expr->conditional, expr->cond_false);
+
+		return  linearize_conditional(ep, expr, expr->conditional,
+					      expr->cond_true, expr->cond_false);
+
+	case EXPR_COMMA:
+		linearize_expression(ep, expr->left);
+		return linearize_expression(ep, expr->right);
+
+	case EXPR_ASSIGNMENT:
+		return linearize_assignment(ep, expr);
+
+	case EXPR_PREOP:
+		return linearize_preop(ep, expr);
+
+	case EXPR_POSTOP:
+		return linearize_postop(ep, expr);
+
+	case EXPR_CAST:
+	case EXPR_FORCE_CAST:
+	case EXPR_IMPLIED_CAST:
+		return linearize_cast(ep, expr);
+	
+	case EXPR_SLICE:
+		return linearize_slice(ep, expr);
+
+	case EXPR_INITIALIZER:
+	case EXPR_POS:
+		warning(expr->pos, "unexpected initializer expression (%d %d)", expr->type, expr->op);
+		return VOID;
+	default: 
+		warning(expr->pos, "unknown expression (%d %d)", expr->type, expr->op);
+		return VOID;
+	}
+	return VOID;
+}
+
+static pseudo_t linearize_one_symbol(struct entrypoint *ep, struct symbol *sym)
+{
+	struct access_data ad = { NULL, };
+	pseudo_t value;
+
+	if (!sym || !sym->initializer || sym->initialized)
+		return VOID;
+
+	/* We need to output these puppies some day too.. */
+	if (sym->ctype.modifiers & (MOD_STATIC | MOD_TOPLEVEL))
+		return VOID;
+
+	sym->initialized = 1;
+	ad.address = symbol_pseudo(ep, sym);
+	value = linearize_initializer(ep, sym->initializer, &ad);
+	finish_address_gen(ep, &ad);
+	return value;
+}
+
+static pseudo_t linearize_compound_statement(struct entrypoint *ep, struct statement *stmt)
+{
+	pseudo_t pseudo;
+	struct statement *s;
+	struct symbol *ret = stmt->ret;
+
+	pseudo = VOID;
+	FOR_EACH_PTR(stmt->stmts, s) {
+		pseudo = linearize_statement(ep, s);
+	} END_FOR_EACH_PTR(s);
+
+	if (ret) {
+		struct basic_block *bb = add_label(ep, ret);
+		struct instruction *phi_node = first_instruction(bb->insns);
+
+		if (!phi_node)
+			return pseudo;
+
+		if (pseudo_list_size(phi_node->phi_list)==1) {
+			pseudo = first_pseudo(phi_node->phi_list);
+			assert(pseudo->type == PSEUDO_PHI);
+			return pseudo->def->src1;
+		}
+		return phi_node->target;
+	}
+
+	return pseudo;
+}
+
+static pseudo_t linearize_inlined_call(struct entrypoint *ep, struct statement *stmt)
+{
+	struct instruction *insn = alloc_instruction(OP_INLINED_CALL, 0);
+	struct statement *args = stmt->args;
+	struct basic_block *bb;
+	pseudo_t pseudo;
+
+	if (args) {
+		struct symbol *sym;
+
+		concat_symbol_list(args->declaration, &ep->syms);
+		FOR_EACH_PTR(args->declaration, sym) {
+			pseudo_t value = linearize_one_symbol(ep, sym);
+			use_pseudo(insn, value, add_pseudo(&insn->arguments, value));
+		} END_FOR_EACH_PTR(sym);
+	}
+
+	insn->target = pseudo = linearize_compound_statement(ep, stmt);
+	use_pseudo(insn, symbol_pseudo(ep, stmt->inline_fn), &insn->func);
+	bb = ep->active;
+	if (bb && !bb->insns)
+		bb->pos = stmt->pos;
+	add_one_insn(ep, insn);
+	return pseudo;
+}
+
+static pseudo_t linearize_context(struct entrypoint *ep, struct statement *stmt)
+{
+	struct instruction *insn = alloc_instruction(OP_CONTEXT, 0);
+	struct expression *expr = stmt->expression;
+	int value = 0;
+
+	if (expr->type == EXPR_VALUE)
+		value = expr->value;
+
+	insn->increment = value;
+	insn->context_expr = stmt->context;
+	add_one_insn(ep, insn);
+	return VOID;
+}
+
+static pseudo_t linearize_range(struct entrypoint *ep, struct statement *stmt)
+{
+	struct instruction *insn = alloc_instruction(OP_RANGE, 0);
+
+	use_pseudo(insn, linearize_expression(ep, stmt->range_expression), &insn->src1);
+	use_pseudo(insn, linearize_expression(ep, stmt->range_low), &insn->src2);
+	use_pseudo(insn, linearize_expression(ep, stmt->range_high), &insn->src3);
+	add_one_insn(ep, insn);
+	return VOID;
+}
+
+ALLOCATOR(asm_rules, "asm rules");
+ALLOCATOR(asm_constraint, "asm constraints");
+
+static void add_asm_input(struct entrypoint *ep, struct instruction *insn, struct expression *expr,
+	const char *constraint, const struct ident *ident)
+{
+	pseudo_t pseudo = linearize_expression(ep, expr);
+	struct asm_constraint *rule = __alloc_asm_constraint(0);
+
+	rule->ident = ident;
+	rule->constraint = constraint;
+	use_pseudo(insn, pseudo, &rule->pseudo);
+	add_ptr_list(&insn->asm_rules->inputs, rule);
+}
+
+static void add_asm_output(struct entrypoint *ep, struct instruction *insn, struct expression *expr,
+	const char *constraint, const struct ident *ident)
+{
+	struct access_data ad = { NULL, };
+	pseudo_t pseudo = alloc_pseudo(insn);
+	struct asm_constraint *rule;
+
+	if (!expr || !linearize_address_gen(ep, expr, &ad))
+		return;
+	linearize_store_gen(ep, pseudo, &ad);
+	finish_address_gen(ep, &ad);
+	rule = __alloc_asm_constraint(0);
+	rule->ident = ident;
+	rule->constraint = constraint;
+	use_pseudo(insn, pseudo, &rule->pseudo);
+	add_ptr_list(&insn->asm_rules->outputs, rule);
+}
+
+static pseudo_t linearize_asm_statement(struct entrypoint *ep, struct statement *stmt)
+{
+	int state;
+	struct expression *expr;
+	struct instruction *insn;
+	struct asm_rules *rules;
+	const char *constraint;
+	struct ident *ident;
+
+	insn = alloc_instruction(OP_ASM, 0);
+	expr = stmt->asm_string;
+	if (!expr || expr->type != EXPR_STRING) {
+		warning(stmt->pos, "expected string in inline asm");
+		return VOID;
+	}
+	insn->string = expr->string->data;
+
+	rules = __alloc_asm_rules(0);
+	insn->asm_rules = rules;
+
+	/* Gather the inputs.. */
+	state = 0;
+	ident = NULL;
+	constraint = NULL;
+	FOR_EACH_PTR(stmt->asm_inputs, expr) {
+		switch (state) {
+		case 0:	/* Identifier */
+			state = 1;
+			ident = (struct ident *)expr;
+			continue;
+
+		case 1:	/* Constraint */
+			state = 2;
+			constraint = expr ? expr->string->data : "";
+			continue;
+
+		case 2:	/* Expression */
+			state = 0;
+			add_asm_input(ep, insn, expr, constraint, ident);
+		}
+	} END_FOR_EACH_PTR(expr);
+
+	add_one_insn(ep, insn);
+
+	/* Assign the outputs */
+	state = 0;
+	ident = NULL;
+	constraint = NULL;
+	FOR_EACH_PTR(stmt->asm_outputs, expr) {
+		switch (state) {
+		case 0:	/* Identifier */
+			state = 1;
+			ident = (struct ident *)expr;
+			continue;
+
+		case 1:	/* Constraint */
+			state = 2;
+			constraint = expr ? expr->string->data : "";
+			continue;
+
+		case 2:
+			state = 0;
+			add_asm_output(ep, insn, expr, constraint, ident);
+		}
+	} END_FOR_EACH_PTR(expr);
+
+	return VOID;
+}
+
+static int multijmp_cmp(const void *_a, const void *_b)
+{
+	const struct multijmp *a = _a;
+	const struct multijmp *b = _b;
+
+	// "default" case?
+	if (a->begin > a->end) {
+		if (b->begin > b->end)
+			return 0;
+		return 1;
+	}
+	if (b->begin > b->end)
+		return -1;
+	if (a->begin == b->begin) {
+		if (a->end == b->end)
+			return 0;
+		return (a->end < b->end) ? -1 : 1;
+	}
+	return a->begin < b->begin ? -1 : 1;
+}
+
+static void sort_switch_cases(struct instruction *insn)
+{
+	sort_list((struct ptr_list **)&insn->multijmp_list, multijmp_cmp);
+}
+
+static pseudo_t linearize_declaration(struct entrypoint *ep, struct statement *stmt)
+{
+	struct symbol *sym;
+
+	concat_symbol_list(stmt->declaration, &ep->syms);
+
+	FOR_EACH_PTR(stmt->declaration, sym) {
+		linearize_one_symbol(ep, sym);
+	} END_FOR_EACH_PTR(sym);
+	return VOID;
+}
+
+static pseudo_t linearize_return(struct entrypoint *ep, struct statement *stmt)
+{
+	struct expression *expr = stmt->expression;
+	struct basic_block *bb_return = get_bound_block(ep, stmt->ret_target);
+	struct basic_block *active;
+	pseudo_t src = linearize_expression(ep, expr);
+	active = ep->active;
+	if (active && src != &void_pseudo) {
+		struct instruction *phi_node = first_instruction(bb_return->insns);
+		pseudo_t phi;
+		if (!phi_node) {
+			phi_node = alloc_typed_instruction(OP_PHI, expr->ctype);
+			phi_node->target = alloc_pseudo(phi_node);
+			phi_node->bb = bb_return;
+			add_instruction(&bb_return->insns, phi_node);
+		}
+		phi = alloc_phi(active, src, type_size(expr->ctype));
+		phi->ident = &return_ident;
+		use_pseudo(phi_node, phi, add_pseudo(&phi_node->phi_list, phi));
+	}
+	add_goto(ep, bb_return);
+	return VOID;
+}
+
+static pseudo_t linearize_switch(struct entrypoint *ep, struct statement *stmt)
+{
+	struct symbol *sym;
+	struct instruction *switch_ins;
+	struct basic_block *switch_end = alloc_basic_block(ep, stmt->pos);
+	struct basic_block *active, *default_case;
+	struct multijmp *jmp;
+	pseudo_t pseudo;
+
+	pseudo = linearize_expression(ep, stmt->switch_expression);
+
+	active = ep->active;
+	if (!bb_reachable(active))
+		return VOID;
+
+	switch_ins = alloc_instruction(OP_SWITCH, 0);
+	use_pseudo(switch_ins, pseudo, &switch_ins->cond);
+	add_one_insn(ep, switch_ins);
+	finish_block(ep);
+
+	default_case = NULL;
+	FOR_EACH_PTR(stmt->switch_case->symbol_list, sym) {
+		struct statement *case_stmt = sym->stmt;
+		struct basic_block *bb_case = get_bound_block(ep, sym);
+
+		if (!case_stmt->case_expression) {
+			default_case = bb_case;
+			continue;
+		} else {
+			int begin, end;
+
+			begin = end = case_stmt->case_expression->value;
+			if (case_stmt->case_to)
+				end = case_stmt->case_to->value;
+			if (begin > end)
+				jmp = alloc_multijmp(bb_case, end, begin);
+			else
+				jmp = alloc_multijmp(bb_case, begin, end);
+
+		}
+		add_multijmp(&switch_ins->multijmp_list, jmp);
+		add_bb(&bb_case->parents, active);
+		add_bb(&active->children, bb_case);
+	} END_FOR_EACH_PTR(sym);
+
+	bind_label(stmt->switch_break, switch_end, stmt->pos);
+
+	/* And linearize the actual statement */
+	linearize_statement(ep, stmt->switch_statement);
+	set_activeblock(ep, switch_end);
+
+	if (!default_case)
+		default_case = switch_end;
+
+	jmp = alloc_multijmp(default_case, 1, 0);
+	add_multijmp(&switch_ins->multijmp_list, jmp);
+	add_bb(&default_case->parents, active);
+	add_bb(&active->children, default_case);
+	sort_switch_cases(switch_ins);
+
+	return VOID;
+}
+
+static pseudo_t linearize_iterator(struct entrypoint *ep, struct statement *stmt)
+{
+	struct statement  *pre_statement = stmt->iterator_pre_statement;
+	struct expression *pre_condition = stmt->iterator_pre_condition;
+	struct statement  *statement = stmt->iterator_statement;
+	struct statement  *post_statement = stmt->iterator_post_statement;
+	struct expression *post_condition = stmt->iterator_post_condition;
+	struct basic_block *loop_top, *loop_body, *loop_continue, *loop_end;
+
+	concat_symbol_list(stmt->iterator_syms, &ep->syms);
+	linearize_statement(ep, pre_statement);
+
+	loop_body = loop_top = alloc_basic_block(ep, stmt->pos);
+	loop_continue = alloc_basic_block(ep, stmt->pos);
+	loop_end = alloc_basic_block(ep, stmt->pos);
+
+	/* An empty post-condition means that it's the same as the pre-condition */
+	if (!post_condition) {
+		loop_top = alloc_basic_block(ep, stmt->pos);
+		set_activeblock(ep, loop_top);
+	}
+
+	if (pre_condition)
+			linearize_cond_branch(ep, pre_condition, loop_body, loop_end);
+
+	bind_label(stmt->iterator_continue, loop_continue, stmt->pos);
+	bind_label(stmt->iterator_break, loop_end, stmt->pos);
+
+	set_activeblock(ep, loop_body);
+	linearize_statement(ep, statement);
+	add_goto(ep, loop_continue);
+
+	set_activeblock(ep, loop_continue);
+	linearize_statement(ep, post_statement);
+	if (!post_condition)
+		add_goto(ep, loop_top);
+	else
+		linearize_cond_branch(ep, post_condition, loop_top, loop_end);
+	set_activeblock(ep, loop_end);
+
+	return VOID;
+}
+
+pseudo_t linearize_statement(struct entrypoint *ep, struct statement *stmt)
+{
+	struct basic_block *bb;
+
+	if (!stmt)
+		return VOID;
+
+	bb = ep->active;
+	if (bb && !bb->insns)
+		bb->pos = stmt->pos;
+	current_pos = stmt->pos;
+
+	switch (stmt->type) {
+	case STMT_NONE:
+		break;
+
+	case STMT_DECLARATION:
+		return linearize_declaration(ep, stmt);
+
+	case STMT_CONTEXT:
+		return linearize_context(ep, stmt);
+
+	case STMT_RANGE:
+		return linearize_range(ep, stmt);
+
+	case STMT_EXPRESSION:
+		return linearize_expression(ep, stmt->expression);
+
+	case STMT_ASM:
+		return linearize_asm_statement(ep, stmt);
+
+	case STMT_RETURN:
+		return linearize_return(ep, stmt);
+
+	case STMT_CASE: {
+		add_label(ep, stmt->case_label);
+		linearize_statement(ep, stmt->case_statement);
+		break;
+	}
+
+	case STMT_LABEL: {
+		struct symbol *label = stmt->label_identifier;
+
+		if (label->used) {
+			add_label(ep, label);
+			linearize_statement(ep, stmt->label_statement);
+		}
+		break;
+	}
+
+	case STMT_GOTO: {
+		struct symbol *sym;
+		struct expression *expr;
+		struct instruction *goto_ins;
+		struct basic_block *active;
+		pseudo_t pseudo;
+
+		active = ep->active;
+		if (!bb_reachable(active))
+			break;
+
+		if (stmt->goto_label) {
+			add_goto(ep, get_bound_block(ep, stmt->goto_label));
+			break;
+		}
+
+		expr = stmt->goto_expression;
+		if (!expr)
+			break;
+
+		/* This can happen as part of simplification */
+		if (expr->type == EXPR_LABEL) {
+			add_goto(ep, get_bound_block(ep, expr->label_symbol));
+			break;
+		}
+
+		pseudo = linearize_expression(ep, expr);
+		goto_ins = alloc_instruction(OP_COMPUTEDGOTO, 0);
+		use_pseudo(goto_ins, pseudo, &goto_ins->target);
+		add_one_insn(ep, goto_ins);
+
+		FOR_EACH_PTR(stmt->target_list, sym) {
+			struct basic_block *bb_computed = get_bound_block(ep, sym);
+			struct multijmp *jmp = alloc_multijmp(bb_computed, 1, 0);
+			add_multijmp(&goto_ins->multijmp_list, jmp);
+			add_bb(&bb_computed->parents, ep->active);
+			add_bb(&active->children, bb_computed);
+		} END_FOR_EACH_PTR(sym);
+
+		finish_block(ep);
+		break;
+	}
+
+	case STMT_COMPOUND:
+		if (stmt->inline_fn)
+			return linearize_inlined_call(ep, stmt);
+		return linearize_compound_statement(ep, stmt);
+
+	/*
+	 * This could take 'likely/unlikely' into account, and
+	 * switch the arms around appropriately..
+	 */
+	case STMT_IF: {
+		struct basic_block *bb_true, *bb_false, *endif;
+ 		struct expression *cond = stmt->if_conditional;
+
+		bb_true = alloc_basic_block(ep, stmt->pos);
+		bb_false = endif = alloc_basic_block(ep, stmt->pos);
+
+ 		linearize_cond_branch(ep, cond, bb_true, bb_false);
+
+		set_activeblock(ep, bb_true);
+ 		linearize_statement(ep, stmt->if_true);
+ 
+ 		if (stmt->if_false) {
+			endif = alloc_basic_block(ep, stmt->pos);
+			add_goto(ep, endif);
+			set_activeblock(ep, bb_false);
+ 			linearize_statement(ep, stmt->if_false);
+		}
+		set_activeblock(ep, endif);
+		break;
+	}
+
+	case STMT_SWITCH:
+		return linearize_switch(ep, stmt);
+
+	case STMT_ITERATOR:
+		return linearize_iterator(ep, stmt);
+
+	default:
+		break;
+	}
+	return VOID;
+}
+
+static struct entrypoint *linearize_fn(struct symbol *sym, struct symbol *base_type)
+{
+	struct entrypoint *ep;
+	struct basic_block *bb;
+	struct symbol *arg;
+	struct instruction *entry;
+	pseudo_t result;
+	int i;
+
+	if (!base_type->stmt)
+		return NULL;
+
+	ep = alloc_entrypoint();
+	bb = alloc_basic_block(ep, sym->pos);
+	
+	ep->name = sym;
+	sym->ep = ep;
+	set_activeblock(ep, bb);
+
+	entry = alloc_instruction(OP_ENTRY, 0);
+	add_one_insn(ep, entry);
+	ep->entry = entry;
+
+	concat_symbol_list(base_type->arguments, &ep->syms);
+
+	/* FIXME!! We should do something else about varargs.. */
+	i = 0;
+	FOR_EACH_PTR(base_type->arguments, arg) {
+		linearize_argument(ep, arg, ++i);
+	} END_FOR_EACH_PTR(arg);
+
+	result = linearize_statement(ep, base_type->stmt);
+	if (bb_reachable(ep->active) && !bb_terminated(ep->active)) {
+		struct symbol *ret_type = base_type->ctype.base_type;
+		struct instruction *insn = alloc_typed_instruction(OP_RET, ret_type);
+
+		if (type_size(ret_type) > 0)
+			use_pseudo(insn, result, &insn->src);
+		add_one_insn(ep, insn);
+	}
+
+	/*
+	 * Do trivial flow simplification - branches to
+	 * branches, kill dead basicblocks etc
+	 */
+	kill_unreachable_bbs(ep);
+
+	/*
+	 * Turn symbols into pseudos
+	 */
+	simplify_symbol_usage(ep);
+
+repeat:
+	/*
+	 * Remove trivial instructions, and try to CSE
+	 * the rest.
+	 */
+	do {
+		cleanup_and_cse(ep);
+		pack_basic_blocks(ep);
+	} while (repeat_phase & REPEAT_CSE);
+
+	kill_unreachable_bbs(ep);
+	vrfy_flow(ep);
+
+	/* Cleanup */
+	clear_symbol_pseudos(ep);
+
+	/* And track pseudo register usage */
+	track_pseudo_liveness(ep);
+
+	/*
+	 * Some flow optimizations can only effectively
+	 * be done when we've done liveness analysis. But
+	 * if they trigger, we need to start all over
+	 * again
+	 */
+	if (simplify_flow(ep)) {
+		clear_liveness(ep);
+		goto repeat;
+	}
+
+	/* Finally, add deathnotes to pseudos now that we have them */
+	if (dbg_dead)
+		track_pseudo_death(ep);
+
+	return ep;
+}
+
+struct entrypoint *linearize_symbol(struct symbol *sym)
+{
+	struct symbol *base_type;
+
+	if (!sym)
+		return NULL;
+	current_pos = sym->pos;
+	base_type = sym->ctype.base_type;
+	if (!base_type)
+		return NULL;
+	if (base_type->type == SYM_FN)
+		return linearize_fn(sym, base_type);
+	return NULL;
+}
diff --git a/deps/sparse/linearize.h b/deps/sparse/linearize.h
new file mode 100644
index 0000000..50b3601
--- /dev/null
+++ b/deps/sparse/linearize.h
@@ -0,0 +1,346 @@
+#ifndef LINEARIZE_H
+#define LINEARIZE_H
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+
+struct instruction;
+DECLARE_PTR_LIST(pseudo_ptr_list, pseudo_t);
+
+struct pseudo_user {
+	struct instruction *insn;
+	pseudo_t *userp;
+};
+
+DECLARE_ALLOCATOR(pseudo_user);
+DECLARE_PTR_LIST(pseudo_user_list, struct pseudo_user);
+
+
+enum pseudo_type {
+	PSEUDO_VOID,
+	PSEUDO_REG,
+	PSEUDO_SYM,
+	PSEUDO_VAL,
+	PSEUDO_ARG,
+	PSEUDO_PHI,
+};
+
+struct pseudo {
+	int nr;
+	enum pseudo_type type;
+	struct pseudo_user_list *users;
+	struct ident *ident;
+	union {
+		struct symbol *sym;
+		struct instruction *def;
+		long long value;
+	};
+};
+
+extern struct pseudo void_pseudo;
+
+#define VOID (&void_pseudo)
+
+struct multijmp {
+	struct basic_block *target;
+	int begin, end;
+};
+
+struct asm_constraint {
+	pseudo_t pseudo;
+	const char *constraint;
+	const struct ident *ident;
+};
+
+DECLARE_ALLOCATOR(asm_constraint);
+DECLARE_PTR_LIST(asm_constraint_list, struct asm_constraint);
+
+struct asm_rules {
+	struct asm_constraint_list *inputs;
+	struct asm_constraint_list *outputs;
+	struct asm_constraint_list *clobbers;
+};
+
+DECLARE_ALLOCATOR(asm_rules);
+
+struct instruction {
+	unsigned opcode:8,
+		 size:24;
+	struct basic_block *bb;
+	struct position pos;
+	struct symbol *type;
+	union {
+		pseudo_t target;
+		pseudo_t cond;		/* for branch and switch */
+	};
+	union {
+		struct /* entrypoint */ {
+			struct pseudo_list *arg_list;
+		};
+		struct /* branch */ {
+			struct basic_block *bb_true, *bb_false;
+		};
+		struct /* switch */ {
+			struct multijmp_list *multijmp_list;
+		};
+		struct /* phi_node */ {
+			struct pseudo_list *phi_list;
+		};
+		struct /* phi source */ {
+			pseudo_t phi_src;
+			struct instruction_list *phi_users;
+		};
+		struct /* unops */ {
+			pseudo_t src;
+			struct symbol *orig_type;	/* casts */
+			unsigned int offset;		/* memops */
+		};
+		struct /* binops and sel */ {
+			pseudo_t src1, src2, src3;
+		};
+		struct /* slice */ {
+			pseudo_t base;
+			unsigned from, len;
+		};
+		struct /* multijump */ {
+			int begin, end;
+		};
+		struct /* setval */ {
+			pseudo_t symbol;		/* Subtle: same offset as "src" !! */
+			struct expression *val;
+		};
+		struct /* call */ {
+			pseudo_t func;
+			struct pseudo_list *arguments;
+		};
+		struct /* context */ {
+			int increment;
+			int check;
+			struct expression *context_expr;
+		};
+		struct /* asm */ {
+			const char *string;
+			struct asm_rules *asm_rules;
+		};
+	};
+};
+
+enum opcode {
+	OP_BADOP,
+
+	/* Entry */
+	OP_ENTRY,
+
+	/* Terminator */
+	OP_TERMINATOR,
+	OP_RET = OP_TERMINATOR,
+	OP_BR,
+	OP_SWITCH,
+	OP_INVOKE,
+	OP_COMPUTEDGOTO,
+	OP_UNWIND,
+	OP_TERMINATOR_END = OP_UNWIND,
+	
+	/* Binary */
+	OP_BINARY,
+	OP_ADD = OP_BINARY,
+	OP_SUB,
+	OP_MULU, OP_MULS,
+	OP_DIVU, OP_DIVS,
+	OP_MODU, OP_MODS,
+	OP_SHL,
+	OP_LSR, OP_ASR,
+	
+	/* Logical */
+	OP_AND,
+	OP_OR,
+	OP_XOR,
+	OP_AND_BOOL,
+	OP_OR_BOOL,
+	OP_BINARY_END = OP_OR_BOOL,
+
+	/* Binary comparison */
+	OP_BINCMP,
+	OP_SET_EQ = OP_BINCMP,
+	OP_SET_NE,
+	OP_SET_LE,
+	OP_SET_GE,
+	OP_SET_LT,
+	OP_SET_GT,
+	OP_SET_B,
+	OP_SET_A,
+	OP_SET_BE,
+	OP_SET_AE,
+	OP_BINCMP_END = OP_SET_AE,
+
+	/* Uni */
+	OP_NOT,
+	OP_NEG,
+
+	/* Select - three input values */
+	OP_SEL,
+	
+	/* Memory */
+	OP_MALLOC,
+	OP_FREE,
+	OP_ALLOCA,
+	OP_LOAD,
+	OP_STORE,
+	OP_SETVAL,
+	OP_SYMADDR,
+	OP_GET_ELEMENT_PTR,
+
+	/* Other */
+	OP_PHI,
+	OP_PHISOURCE,
+	OP_CAST,
+	OP_SCAST,
+	OP_FPCAST,
+	OP_PTRCAST,
+	OP_INLINED_CALL,
+	OP_CALL,
+	OP_VANEXT,
+	OP_VAARG,
+	OP_SLICE,
+	OP_SNOP,
+	OP_LNOP,
+	OP_NOP,
+	OP_DEATHNOTE,
+	OP_ASM,
+
+	/* Sparse tagging (line numbers, context, whatever) */
+	OP_CONTEXT,
+	OP_RANGE,
+
+	/* Needed to translate SSA back to normal form */
+	OP_COPY,
+};
+
+struct basic_block_list;
+struct instruction_list;
+
+struct basic_block {
+	struct position pos;
+	unsigned long generation;
+	int context;
+	struct entrypoint *ep;
+	struct basic_block_list *parents; /* sources */
+	struct basic_block_list *children; /* destinations */
+	struct instruction_list *insns;	/* Linear list of instructions */
+	struct pseudo_list *needs, *defines;
+};
+
+static inline int is_branch_goto(struct instruction *br)
+{
+	return br && br->opcode==OP_BR && (!br->bb_true || !br->bb_false);
+}
+
+static inline void add_bb(struct basic_block_list **list, struct basic_block *bb)
+{
+	add_ptr_list(list, bb);
+}
+
+static inline void add_instruction(struct instruction_list **list, struct instruction *insn)
+{
+	add_ptr_list(list, insn);
+}
+
+static inline void add_multijmp(struct multijmp_list **list, struct multijmp *multijmp)
+{
+	add_ptr_list(list, multijmp);
+}
+
+static inline pseudo_t *add_pseudo(struct pseudo_list **list, pseudo_t pseudo)
+{
+	return add_ptr_list(list, pseudo);
+}
+
+static inline int remove_pseudo(struct pseudo_list **list, pseudo_t pseudo)
+{
+	return delete_ptr_list_entry((struct ptr_list **)list, pseudo, 0) != 0;
+}
+
+static inline int bb_terminated(struct basic_block *bb)
+{
+	struct instruction *insn;
+	if (!bb)
+		return 0;
+	insn = last_instruction(bb->insns);
+	return insn && insn->opcode >= OP_TERMINATOR
+	            && insn->opcode <= OP_TERMINATOR_END;
+}
+
+static inline int bb_reachable(struct basic_block *bb)
+{
+	return bb != NULL;
+}
+
+static inline void add_pseudo_ptr(pseudo_t *ptr, struct pseudo_ptr_list **list)
+{
+	add_ptr_list(list, ptr);
+}
+
+static inline void add_pseudo_user_ptr(struct pseudo_user *user, struct pseudo_user_list **list)
+{
+	add_ptr_list(list, user);
+}
+
+static inline int has_use_list(pseudo_t p)
+{
+	return (p && p->type != PSEUDO_VOID && p->type != PSEUDO_VAL);
+}
+
+static inline struct pseudo_user *alloc_pseudo_user(struct instruction *insn, pseudo_t *pp)
+{
+	struct pseudo_user *user = __alloc_pseudo_user(0);
+	user->userp = pp;
+	user->insn = insn;
+	return user;
+}
+
+static inline void use_pseudo(struct instruction *insn, pseudo_t p, pseudo_t *pp)
+{
+	*pp = p;
+	if (has_use_list(p))
+		add_pseudo_user_ptr(alloc_pseudo_user(insn, pp), &p->users);
+}
+
+static inline void remove_bb_from_list(struct basic_block_list **list, struct basic_block *entry, int count)
+{
+	delete_ptr_list_entry((struct ptr_list **)list, entry, count);
+}
+
+static inline void replace_bb_in_list(struct basic_block_list **list,
+	struct basic_block *old, struct basic_block *new, int count)
+{
+	replace_ptr_list_entry((struct ptr_list **)list, old, new, count);
+}
+
+struct entrypoint {
+	struct symbol *name;
+	struct symbol_list *syms;
+	struct pseudo_list *accesses;
+	struct basic_block_list *bbs;
+	struct basic_block *active;
+	struct instruction *entry;
+};
+
+extern void insert_select(struct basic_block *bb, struct instruction *br, struct instruction *phi, pseudo_t if_true, pseudo_t if_false);
+extern void insert_branch(struct basic_block *bb, struct instruction *br, struct basic_block *target);
+
+pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, int size);
+pseudo_t alloc_pseudo(struct instruction *def);
+pseudo_t value_pseudo(long long val);
+
+struct entrypoint *linearize_symbol(struct symbol *sym);
+int unssa(struct entrypoint *ep);
+void show_entry(struct entrypoint *ep);
+const char *show_pseudo(pseudo_t pseudo);
+void show_bb(struct basic_block *bb);
+const char *show_instruction(struct instruction *insn);
+
+#endif /* LINEARIZE_H */
+
diff --git a/deps/sparse/liveness.c b/deps/sparse/liveness.c
new file mode 100644
index 0000000..eeff0f7
--- /dev/null
+++ b/deps/sparse/liveness.c
@@ -0,0 +1,362 @@
+/*
+ * Register - track pseudo usage, maybe eventually try to do register
+ * allocation.
+ *
+ * Copyright (C) 2004 Linus Torvalds
+ */
+
+#include <assert.h>
+
+#include "parse.h"
+#include "expression.h"
+#include "linearize.h"
+#include "flow.h"
+
+static void phi_defines(struct instruction * phi_node, pseudo_t target,
+	void (*defines)(struct basic_block *, struct instruction *, pseudo_t))
+{
+	pseudo_t phi;
+	FOR_EACH_PTR(phi_node->phi_list, phi) {
+		struct instruction *def;
+		if (phi == VOID)
+			continue;
+		def = phi->def;
+		if (!def || !def->bb)
+			continue;
+		if (def->opcode == OP_PHI) {
+			phi_defines(def, target, defines);
+			continue;
+		}
+		defines(def->bb, phi->def, target);
+	} END_FOR_EACH_PTR(phi);
+}
+
+static void asm_liveness(struct basic_block *bb, struct instruction *insn,
+	void (*def)(struct basic_block *, struct instruction *, pseudo_t),
+	void (*use)(struct basic_block *, struct instruction *, pseudo_t))
+{
+	struct asm_constraint *entry;
+
+	FOR_EACH_PTR(insn->asm_rules->inputs, entry) {
+		use(bb, insn, entry->pseudo);
+	} END_FOR_EACH_PTR(entry);
+		
+	FOR_EACH_PTR(insn->asm_rules->outputs, entry) {
+		def(bb, insn, entry->pseudo);
+	} END_FOR_EACH_PTR(entry);
+}
+
+static void track_instruction_usage(struct basic_block *bb, struct instruction *insn,
+	void (*def)(struct basic_block *, struct instruction *, pseudo_t),
+	void (*use)(struct basic_block *, struct instruction *, pseudo_t))
+{
+	pseudo_t pseudo;
+
+	#define USES(x)		use(bb, insn, insn->x)
+	#define DEFINES(x)	def(bb, insn, insn->x)
+
+	switch (insn->opcode) {
+	case OP_RET:
+		USES(src);
+		break;
+
+	case OP_BR: case OP_SWITCH:
+		USES(cond);
+		break;
+
+	case OP_COMPUTEDGOTO:
+		USES(target);
+		break;
+	
+	/* Binary */
+	case OP_BINARY ... OP_BINARY_END:
+	case OP_BINCMP ... OP_BINCMP_END:
+		USES(src1); USES(src2); DEFINES(target);
+		break;
+
+	/* Uni */
+	case OP_NOT: case OP_NEG:
+		USES(src1); DEFINES(target);
+		break;
+
+	case OP_SEL:
+		USES(src1); USES(src2); USES(src3); DEFINES(target);
+		break;
+	
+	/* Memory */
+	case OP_LOAD:
+		USES(src); DEFINES(target);
+		break;
+
+	case OP_STORE:
+		USES(src); USES(target);
+		break;
+
+	case OP_SETVAL:
+		DEFINES(target);
+		break;
+
+	case OP_SYMADDR:
+		USES(symbol); DEFINES(target);
+		break;
+
+	/* Other */
+	case OP_PHI:
+		/* Phi-nodes are "backwards" nodes. Their def doesn't matter */
+		phi_defines(insn, insn->target, def);
+		break;
+
+	case OP_PHISOURCE:
+		/*
+		 * We don't care about the phi-source define, they get set
+		 * up and expanded by the OP_PHI
+		 */
+		USES(phi_src);
+		break;
+
+	case OP_CAST:
+	case OP_SCAST:
+	case OP_FPCAST:
+	case OP_PTRCAST:
+		USES(src); DEFINES(target);
+		break;
+
+	case OP_CALL:
+		USES(func);
+		if (insn->target != VOID)
+			DEFINES(target);
+		FOR_EACH_PTR(insn->arguments, pseudo) {
+			use(bb, insn, pseudo);
+		} END_FOR_EACH_PTR(pseudo);
+		break;
+
+	case OP_SLICE:
+		USES(base); DEFINES(target);
+		break;
+
+	case OP_ASM:
+		asm_liveness(bb, insn, def, use);
+		break;
+
+	case OP_RANGE:
+		USES(src1); USES(src2); USES(src3);
+		break;
+
+	case OP_BADOP:
+	case OP_INVOKE:
+	case OP_UNWIND:
+	case OP_MALLOC:
+	case OP_FREE:
+	case OP_ALLOCA:
+	case OP_GET_ELEMENT_PTR:
+	case OP_VANEXT:
+	case OP_VAARG:
+	case OP_SNOP:
+	case OP_LNOP:
+	case OP_NOP:
+	case OP_CONTEXT:
+		break;
+	}
+}
+
+int pseudo_in_list(struct pseudo_list *list, pseudo_t pseudo)
+{
+	pseudo_t old;
+	FOR_EACH_PTR(list,old) {
+		if (old == pseudo)
+			return 1;
+	} END_FOR_EACH_PTR(old);   
+	return 0;
+}
+
+static int liveness_changed;
+
+static void add_pseudo_exclusive(struct pseudo_list **list, pseudo_t pseudo)
+{
+	if (!pseudo_in_list(*list, pseudo)) {
+		liveness_changed = 1;
+		add_pseudo(list, pseudo);
+	}
+}
+
+static inline int trackable_pseudo(pseudo_t pseudo)
+{
+	return pseudo && (pseudo->type == PSEUDO_REG || pseudo->type == PSEUDO_ARG);
+}
+
+static void insn_uses(struct basic_block *bb, struct instruction *insn, pseudo_t pseudo)
+{
+	if (trackable_pseudo(pseudo)) {
+		struct instruction *def = pseudo->def;
+		if (pseudo->type != PSEUDO_REG || def->bb != bb || def->opcode == OP_PHI)
+			add_pseudo_exclusive(&bb->needs, pseudo);
+	}
+}
+
+static void insn_defines(struct basic_block *bb, struct instruction *insn, pseudo_t pseudo)
+{
+	assert(trackable_pseudo(pseudo));
+	add_pseudo(&bb->defines, pseudo);
+}
+
+static void track_bb_liveness(struct basic_block *bb)
+{
+	pseudo_t needs;
+
+	FOR_EACH_PTR(bb->needs, needs) {
+		struct basic_block *parent;
+		FOR_EACH_PTR(bb->parents, parent) {
+			if (!pseudo_in_list(parent->defines, needs)) {
+				add_pseudo_exclusive(&parent->needs, needs);
+			}
+		} END_FOR_EACH_PTR(parent);
+	} END_FOR_EACH_PTR(needs);
+}
+
+/*
+ * We need to clear the liveness information if we 
+ * are going to re-run it.
+ */
+void clear_liveness(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		free_ptr_list(&bb->needs);
+		free_ptr_list(&bb->defines);
+	} END_FOR_EACH_PTR(bb);
+}
+
+/*
+ * Track inter-bb pseudo liveness. The intra-bb case
+ * is purely local information.
+ */
+void track_pseudo_liveness(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+
+	/* Add all the bb pseudo usage */
+	FOR_EACH_PTR(ep->bbs, bb) {
+		struct instruction *insn;
+		FOR_EACH_PTR(bb->insns, insn) {
+			if (!insn->bb)
+				continue;
+			assert(insn->bb == bb);
+			track_instruction_usage(bb, insn, insn_defines, insn_uses);
+		} END_FOR_EACH_PTR(insn);
+	} END_FOR_EACH_PTR(bb);
+
+	/* Calculate liveness.. */
+	do {
+		liveness_changed = 0;
+		FOR_EACH_PTR_REVERSE(ep->bbs, bb) {
+			track_bb_liveness(bb);
+		} END_FOR_EACH_PTR_REVERSE(bb);
+	} while (liveness_changed);
+
+	/* Remove the pseudos from the "defines" list that are used internally */
+	FOR_EACH_PTR(ep->bbs, bb) {
+		pseudo_t def;
+		FOR_EACH_PTR(bb->defines, def) {
+			struct basic_block *child;
+			FOR_EACH_PTR(bb->children, child) {
+				if (pseudo_in_list(child->needs, def))
+					goto is_used;
+			} END_FOR_EACH_PTR(child);
+			DELETE_CURRENT_PTR(def);
+is_used:
+		;
+		} END_FOR_EACH_PTR(def);
+		PACK_PTR_LIST(&bb->defines);
+	} END_FOR_EACH_PTR(bb);
+}
+
+static void merge_pseudo_list(struct pseudo_list *src, struct pseudo_list **dest)
+{
+	pseudo_t pseudo;
+	FOR_EACH_PTR(src, pseudo) {
+		add_pseudo_exclusive(dest, pseudo);
+	} END_FOR_EACH_PTR(pseudo);
+}
+
+void track_phi_uses(struct instruction *insn)
+{
+	pseudo_t phi;
+	FOR_EACH_PTR(insn->phi_list, phi) {
+		struct instruction *def;
+		if (phi == VOID || !phi->def)
+			continue;
+		def = phi->def;
+		assert(def->opcode == OP_PHISOURCE);
+		add_ptr_list(&def->phi_users, insn);
+	} END_FOR_EACH_PTR(phi);
+}
+
+static void track_bb_phi_uses(struct basic_block *bb)
+{
+	struct instruction *insn;
+	FOR_EACH_PTR(bb->insns, insn) {
+		if (insn->bb && insn->opcode == OP_PHI)
+			track_phi_uses(insn);
+	} END_FOR_EACH_PTR(insn);
+}
+
+static struct pseudo_list **live_list;
+static struct pseudo_list *dead_list;
+
+static void death_def(struct basic_block *bb, struct instruction *insn, pseudo_t pseudo)
+{
+}
+
+static void death_use(struct basic_block *bb, struct instruction *insn, pseudo_t pseudo)
+{
+	if (trackable_pseudo(pseudo) && !pseudo_in_list(*live_list, pseudo)) {
+		add_pseudo(&dead_list, pseudo);
+		add_pseudo(live_list, pseudo);
+	}
+}
+
+static void track_pseudo_death_bb(struct basic_block *bb)
+{
+	struct pseudo_list *live = NULL;
+	struct basic_block *child;
+	struct instruction *insn;
+
+	FOR_EACH_PTR(bb->children, child) {
+		merge_pseudo_list(child->needs, &live);
+	} END_FOR_EACH_PTR(child);
+
+	live_list = &live;
+	FOR_EACH_PTR_REVERSE(bb->insns, insn) {
+		if (!insn->bb)
+			continue;
+
+		dead_list = NULL;
+		track_instruction_usage(bb, insn, death_def, death_use);
+		if (dead_list) {
+			pseudo_t dead;
+			FOR_EACH_PTR(dead_list, dead) {
+				struct instruction *deathnote = __alloc_instruction(0);
+				deathnote->bb = bb;
+				deathnote->opcode = OP_DEATHNOTE;
+				deathnote->target = dead;
+				INSERT_CURRENT(deathnote, insn);
+			} END_FOR_EACH_PTR(dead);
+			free_ptr_list(&dead_list);
+		}
+	} END_FOR_EACH_PTR_REVERSE(insn);
+	free_ptr_list(&live);
+}
+
+void track_pseudo_death(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		track_bb_phi_uses(bb);
+	} END_FOR_EACH_PTR(bb);
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		track_pseudo_death_bb(bb);
+	} END_FOR_EACH_PTR(bb);
+}
diff --git a/deps/sparse/memops.c b/deps/sparse/memops.c
new file mode 100644
index 0000000..45bd340
--- /dev/null
+++ b/deps/sparse/memops.c
@@ -0,0 +1,196 @@
+/*
+ * memops - try to combine memory ops.
+ *
+ * Copyright (C) 2004 Linus Torvalds
+ */
+
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <assert.h>
+
+#include "parse.h"
+#include "expression.h"
+#include "linearize.h"
+#include "flow.h"
+
+static int find_dominating_parents(pseudo_t pseudo, struct instruction *insn,
+	struct basic_block *bb, unsigned long generation, struct pseudo_list **dominators,
+	int local, int loads)
+{
+	struct basic_block *parent;
+
+	if (bb_list_size(bb->parents) > 1)
+		loads = 0;
+	FOR_EACH_PTR(bb->parents, parent) {
+		struct instruction *one;
+		struct instruction *br;
+		pseudo_t phi;
+
+		FOR_EACH_PTR_REVERSE(parent->insns, one) {
+			int dominance;
+			if (one == insn)
+				goto no_dominance;
+			dominance = dominates(pseudo, insn, one, local);
+			if (dominance < 0) {
+				if (one->opcode == OP_LOAD)
+					continue;
+				return 0;
+			}
+			if (!dominance)
+				continue;
+			if (one->opcode == OP_LOAD && !loads)
+				continue;
+			goto found_dominator;
+		} END_FOR_EACH_PTR_REVERSE(one);
+no_dominance:
+		if (parent->generation == generation)
+			continue;
+		parent->generation = generation;
+
+		if (!find_dominating_parents(pseudo, insn, parent, generation, dominators, local, loads))
+			return 0;
+		continue;
+
+found_dominator:
+		br = delete_last_instruction(&parent->insns);
+		phi = alloc_phi(parent, one->target, one->size);
+		phi->ident = phi->ident ? : one->target->ident;
+		add_instruction(&parent->insns, br);
+		use_pseudo(insn, phi, add_pseudo(dominators, phi));
+	} END_FOR_EACH_PTR(parent);
+	return 1;
+}		
+
+static int address_taken(pseudo_t pseudo)
+{
+	struct pseudo_user *pu;
+	FOR_EACH_PTR(pseudo->users, pu) {
+		struct instruction *insn = pu->insn;
+		if (insn->bb && (insn->opcode != OP_LOAD && insn->opcode != OP_STORE))
+			return 1;
+	} END_FOR_EACH_PTR(pu);
+	return 0;
+}
+
+static int local_pseudo(pseudo_t pseudo)
+{
+	return pseudo->type == PSEUDO_SYM
+		&& !(pseudo->sym->ctype.modifiers & (MOD_STATIC | MOD_NONLOCAL))
+		&& !address_taken(pseudo);
+}
+
+static void simplify_loads(struct basic_block *bb)
+{
+	struct instruction *insn;
+
+	FOR_EACH_PTR_REVERSE(bb->insns, insn) {
+		if (!insn->bb)
+			continue;
+		if (insn->opcode == OP_LOAD) {
+			struct instruction *dom;
+			pseudo_t pseudo = insn->src;
+			int local = local_pseudo(pseudo);
+			struct pseudo_list *dominators;
+			unsigned long generation;
+
+			/* Check for illegal offsets.. */
+			check_access(insn);
+
+			RECURSE_PTR_REVERSE(insn, dom) {
+				int dominance;
+				if (!dom->bb)
+					continue;
+				dominance = dominates(pseudo, insn, dom, local);
+				if (dominance) {
+					/* possible partial dominance? */
+					if (dominance < 0)  {
+						if (dom->opcode == OP_LOAD)
+							continue;
+						goto next_load;
+					}
+					/* Yeehaa! Found one! */
+					convert_load_instruction(insn, dom->target);
+					goto next_load;
+				}
+			} END_FOR_EACH_PTR_REVERSE(dom);
+
+			/* OK, go find the parents */
+			generation = ++bb_generation;
+			bb->generation = generation;
+			dominators = NULL;
+			if (find_dominating_parents(pseudo, insn, bb, generation, &dominators, local, 1)) {
+				/* This happens with initial assignments to structures etc.. */
+				if (!dominators) {
+					if (local) {
+						assert(pseudo->type != PSEUDO_ARG);
+						convert_load_instruction(insn, value_pseudo(0));
+					}
+					goto next_load;
+				}
+				rewrite_load_instruction(insn, dominators);
+			}
+		}
+next_load:
+		/* Do the next one */;
+	} END_FOR_EACH_PTR_REVERSE(insn);
+}
+
+static void kill_store(struct instruction *insn)
+{
+	if (insn) {
+		insn->bb = NULL;
+		insn->opcode = OP_SNOP;
+		kill_use(&insn->target);
+	}
+}
+
+static void kill_dominated_stores(struct basic_block *bb)
+{
+	struct instruction *insn;
+
+	FOR_EACH_PTR_REVERSE(bb->insns, insn) {
+		if (!insn->bb)
+			continue;
+		if (insn->opcode == OP_STORE) {
+			struct instruction *dom;
+			pseudo_t pseudo = insn->src;
+			int local = local_pseudo(pseudo);
+
+			RECURSE_PTR_REVERSE(insn, dom) {
+				int dominance;
+				if (!dom->bb)
+					continue;
+				dominance = dominates(pseudo, insn, dom, local);
+				if (dominance) {
+					/* possible partial dominance? */
+					if (dominance < 0)
+						goto next_store;
+					if (dom->opcode == OP_LOAD)
+						goto next_store;
+					/* Yeehaa! Found one! */
+					kill_store(dom);
+				}
+			} END_FOR_EACH_PTR_REVERSE(dom);
+
+			/* OK, we should check the parents now */
+		}
+next_store:
+		/* Do the next one */;
+	} END_FOR_EACH_PTR_REVERSE(insn);
+}
+
+void simplify_memops(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+
+	FOR_EACH_PTR_REVERSE(ep->bbs, bb) {
+		simplify_loads(bb);
+	} END_FOR_EACH_PTR_REVERSE(bb);
+
+	FOR_EACH_PTR_REVERSE(ep->bbs, bb) {
+		kill_dominated_stores(bb);
+	} END_FOR_EACH_PTR_REVERSE(bb);
+}
diff --git a/deps/sparse/obfuscate.c b/deps/sparse/obfuscate.c
new file mode 100644
index 0000000..1015510
--- /dev/null
+++ b/deps/sparse/obfuscate.c
@@ -0,0 +1,60 @@
+/*
+ * Example trivial client program that uses the sparse library
+ * to tokenize, preprocess and parse a C file, and prints out
+ * the results.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "expression.h"
+#include "linearize.h"
+
+static void emit_entrypoint(struct entrypoint *ep)
+{
+	
+}
+
+static void emit_symbol(struct symbol *sym)
+{
+	struct entrypoint *ep;
+	ep = linearize_symbol(sym);
+	if (ep)
+		emit_entrypoint(ep);
+}
+
+static void emit_symbol_list(struct symbol_list *list)
+{
+	struct symbol *sym;
+
+	FOR_EACH_PTR(list, sym) {
+		expand_symbol(sym);
+		emit_symbol(sym);
+	} END_FOR_EACH_PTR(sym);
+}
+
+int main(int argc, char **argv)
+{
+	struct string_list *filelist = NULL;
+	char *file;
+
+	emit_symbol_list(sparse_initialize(argc, argv, &filelist));
+	FOR_EACH_PTR_NOTAG(filelist, file) {
+		emit_symbol_list(sparse(file));
+	} END_FOR_EACH_PTR_NOTAG(file);
+	return 0;
+}
diff --git a/deps/sparse/parse.c b/deps/sparse/parse.c
new file mode 100644
index 0000000..bd42180
--- /dev/null
+++ b/deps/sparse/parse.c
@@ -0,0 +1,2785 @@
+/*
+ * Stupid C parser, version 1e-6.
+ *
+ * Let's see how hard this is to do.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ * Copyright (C) 2004 Christopher Li
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "scope.h"
+#include "expression.h"
+#include "target.h"
+
+static struct symbol_list **function_symbol_list;
+struct symbol_list *function_computed_target_list;
+struct statement_list *function_computed_goto_list;
+
+static struct token *statement(struct token *token, struct statement **tree);
+static struct token *handle_attributes(struct token *token, struct decl_state *ctx, unsigned int keywords);
+
+typedef struct token *declarator_t(struct token *, struct decl_state *);
+static declarator_t
+	struct_specifier, union_specifier, enum_specifier,
+	attribute_specifier, typeof_specifier, parse_asm_declarator,
+	typedef_specifier, inline_specifier, auto_specifier,
+	register_specifier, static_specifier, extern_specifier,
+	thread_specifier, const_qualifier, volatile_qualifier;
+
+static struct token *parse_if_statement(struct token *token, struct statement *stmt);
+static struct token *parse_return_statement(struct token *token, struct statement *stmt);
+static struct token *parse_loop_iterator(struct token *token, struct statement *stmt);
+static struct token *parse_default_statement(struct token *token, struct statement *stmt);
+static struct token *parse_case_statement(struct token *token, struct statement *stmt);
+static struct token *parse_switch_statement(struct token *token, struct statement *stmt);
+static struct token *parse_for_statement(struct token *token, struct statement *stmt);
+static struct token *parse_while_statement(struct token *token, struct statement *stmt);
+static struct token *parse_do_statement(struct token *token, struct statement *stmt);
+static struct token *parse_goto_statement(struct token *token, struct statement *stmt);
+static struct token *parse_context_statement(struct token *token, struct statement *stmt);
+static struct token *parse_range_statement(struct token *token, struct statement *stmt);
+static struct token *parse_asm_statement(struct token *token, struct statement *stmt);
+static struct token *toplevel_asm_declaration(struct token *token, struct symbol_list **list);
+
+typedef struct token *attr_t(struct token *, struct symbol *,
+			     struct decl_state *);
+
+static attr_t
+	attribute_packed, attribute_aligned, attribute_modifier,
+	attribute_address_space, attribute_context,
+	attribute_designated_init,
+	attribute_transparent_union, ignore_attribute,
+	attribute_mode, attribute_force;
+
+typedef struct symbol *to_mode_t(struct symbol *);
+
+static to_mode_t
+	to_QI_mode, to_HI_mode, to_SI_mode, to_DI_mode, to_TI_mode, to_word_mode;
+
+enum {
+	Set_T = 1,
+	Set_S = 2,
+	Set_Char = 4,
+	Set_Int = 8,
+	Set_Double = 16,
+	Set_Float = 32,
+	Set_Signed = 64,
+	Set_Unsigned = 128,
+	Set_Short = 256,
+	Set_Long = 512,
+	Set_Vlong = 1024,
+	Set_Any = Set_T | Set_Short | Set_Long | Set_Signed | Set_Unsigned
+};
+
+enum {
+	CInt = 0, CSInt, CUInt, CReal, CChar, CSChar, CUChar
+};
+
+enum {
+	SNone = 0, STypedef, SAuto, SRegister, SExtern, SStatic, SForced
+};
+
+static struct symbol_op typedef_op = {
+	.type = KW_MODIFIER,
+	.declarator = typedef_specifier,
+};
+
+static struct symbol_op inline_op = {
+	.type = KW_MODIFIER,
+	.declarator = inline_specifier,
+};
+
+static struct symbol_op auto_op = {
+	.type = KW_MODIFIER,
+	.declarator = auto_specifier,
+};
+
+static struct symbol_op register_op = {
+	.type = KW_MODIFIER,
+	.declarator = register_specifier,
+};
+
+static struct symbol_op static_op = {
+	.type = KW_MODIFIER,
+	.declarator = static_specifier,
+};
+
+static struct symbol_op extern_op = {
+	.type = KW_MODIFIER,
+	.declarator = extern_specifier,
+};
+
+static struct symbol_op thread_op = {
+	.type = KW_MODIFIER,
+	.declarator = thread_specifier,
+};
+
+static struct symbol_op const_op = {
+	.type = KW_QUALIFIER,
+	.declarator = const_qualifier,
+};
+
+static struct symbol_op volatile_op = {
+	.type = KW_QUALIFIER,
+	.declarator = volatile_qualifier,
+};
+
+static struct symbol_op restrict_op = {
+	.type = KW_QUALIFIER,
+};
+
+static struct symbol_op typeof_op = {
+	.type = KW_SPECIFIER,
+	.declarator = typeof_specifier,
+	.test = Set_Any,
+	.set = Set_S|Set_T,
+};
+
+static struct symbol_op attribute_op = {
+	.type = KW_ATTRIBUTE,
+	.declarator = attribute_specifier,
+};
+
+static struct symbol_op struct_op = {
+	.type = KW_SPECIFIER,
+	.declarator = struct_specifier,
+	.test = Set_Any,
+	.set = Set_S|Set_T,
+};
+
+static struct symbol_op union_op = {
+	.type = KW_SPECIFIER,
+	.declarator = union_specifier,
+	.test = Set_Any,
+	.set = Set_S|Set_T,
+};
+
+static struct symbol_op enum_op = {
+	.type = KW_SPECIFIER,
+	.declarator = enum_specifier,
+	.test = Set_Any,
+	.set = Set_S|Set_T,
+};
+
+static struct symbol_op spec_op = {
+	.type = KW_SPECIFIER | KW_EXACT,
+	.test = Set_Any,
+	.set = Set_S|Set_T,
+};
+
+static struct symbol_op char_op = {
+	.type = KW_SPECIFIER,
+	.test = Set_T|Set_Long|Set_Short,
+	.set = Set_T|Set_Char,
+	.class = CChar,
+};
+
+static struct symbol_op int_op = {
+	.type = KW_SPECIFIER,
+	.test = Set_T,
+	.set = Set_T|Set_Int,
+};
+
+static struct symbol_op double_op = {
+	.type = KW_SPECIFIER,
+	.test = Set_T|Set_Signed|Set_Unsigned|Set_Short|Set_Vlong,
+	.set = Set_T|Set_Double,
+	.class = CReal,
+};
+
+static struct symbol_op float_op = {
+	.type = KW_SPECIFIER | KW_SHORT,
+	.test = Set_T|Set_Signed|Set_Unsigned|Set_Short|Set_Long,
+	.set = Set_T|Set_Float,
+	.class = CReal,
+};
+
+static struct symbol_op short_op = {
+	.type = KW_SPECIFIER | KW_SHORT,
+	.test = Set_S|Set_Char|Set_Float|Set_Double|Set_Long|Set_Short,
+	.set = Set_Short,
+};
+
+static struct symbol_op signed_op = {
+	.type = KW_SPECIFIER,
+	.test = Set_S|Set_Float|Set_Double|Set_Signed|Set_Unsigned,
+	.set = Set_Signed,
+	.class = CSInt,
+};
+
+static struct symbol_op unsigned_op = {
+	.type = KW_SPECIFIER,
+	.test = Set_S|Set_Float|Set_Double|Set_Signed|Set_Unsigned,
+	.set = Set_Unsigned,
+	.class = CUInt,
+};
+
+static struct symbol_op long_op = {
+	.type = KW_SPECIFIER | KW_LONG,
+	.test = Set_S|Set_Char|Set_Float|Set_Short|Set_Vlong,
+	.set = Set_Long,
+};
+
+static struct symbol_op if_op = {
+	.statement = parse_if_statement,
+};
+
+static struct symbol_op return_op = {
+	.statement = parse_return_statement,
+};
+
+static struct symbol_op loop_iter_op = {
+	.statement = parse_loop_iterator,
+};
+
+static struct symbol_op default_op = {
+	.statement = parse_default_statement,
+};
+
+static struct symbol_op case_op = {
+	.statement = parse_case_statement,
+};
+
+static struct symbol_op switch_op = {
+	.statement = parse_switch_statement,
+};
+
+static struct symbol_op for_op = {
+	.statement = parse_for_statement,
+};
+
+static struct symbol_op while_op = {
+	.statement = parse_while_statement,
+};
+
+static struct symbol_op do_op = {
+	.statement = parse_do_statement,
+};
+
+static struct symbol_op goto_op = {
+	.statement = parse_goto_statement,
+};
+
+static struct symbol_op __context___op = {
+	.statement = parse_context_statement,
+};
+
+static struct symbol_op range_op = {
+	.statement = parse_range_statement,
+};
+
+static struct symbol_op asm_op = {
+	.type = KW_ASM,
+	.declarator = parse_asm_declarator,
+	.statement = parse_asm_statement,
+	.toplevel = toplevel_asm_declaration,
+};
+
+static struct symbol_op packed_op = {
+	.attribute = attribute_packed,
+};
+
+static struct symbol_op aligned_op = {
+	.attribute = attribute_aligned,
+};
+
+static struct symbol_op attr_mod_op = {
+	.attribute = attribute_modifier,
+};
+
+static struct symbol_op attr_force_op = {
+	.attribute = attribute_force,
+};
+
+static struct symbol_op address_space_op = {
+	.attribute = attribute_address_space,
+};
+
+static struct symbol_op mode_op = {
+	.attribute = attribute_mode,
+};
+
+static struct symbol_op context_op = {
+	.attribute = attribute_context,
+};
+
+static struct symbol_op designated_init_op = {
+	.attribute = attribute_designated_init,
+};
+
+static struct symbol_op transparent_union_op = {
+	.attribute = attribute_transparent_union,
+};
+
+static struct symbol_op ignore_attr_op = {
+	.attribute = ignore_attribute,
+};
+
+static struct symbol_op mode_QI_op = {
+	.type = KW_MODE,
+	.to_mode = to_QI_mode
+};
+
+static struct symbol_op mode_HI_op = {
+	.type = KW_MODE,
+	.to_mode = to_HI_mode
+};
+
+static struct symbol_op mode_SI_op = {
+	.type = KW_MODE,
+	.to_mode = to_SI_mode
+};
+
+static struct symbol_op mode_DI_op = {
+	.type = KW_MODE,
+	.to_mode = to_DI_mode
+};
+
+static struct symbol_op mode_TI_op = {
+	.type = KW_MODE,
+	.to_mode = to_TI_mode
+};
+
+static struct symbol_op mode_word_op = {
+	.type = KW_MODE,
+	.to_mode = to_word_mode
+};
+
+static struct init_keyword {
+	const char *name;
+	enum namespace ns;
+	unsigned long modifiers;
+	struct symbol_op *op;
+	struct symbol *type;
+} keyword_table[] = {
+	/* Type qualifiers */
+	{ "const",	NS_TYPEDEF, .op = &const_op },
+	{ "__const",	NS_TYPEDEF, .op = &const_op },
+	{ "__const__",	NS_TYPEDEF, .op = &const_op },
+	{ "volatile",	NS_TYPEDEF, .op = &volatile_op },
+	{ "__volatile",		NS_TYPEDEF, .op = &volatile_op },
+	{ "__volatile__", 	NS_TYPEDEF, .op = &volatile_op },
+
+	/* Typedef.. */
+	{ "typedef",	NS_TYPEDEF, .op = &typedef_op },
+
+	/* Type specifiers */
+	{ "void",	NS_TYPEDEF, .type = &void_ctype, .op = &spec_op},
+	{ "char",	NS_TYPEDEF, .op = &char_op },
+	{ "short",	NS_TYPEDEF, .op = &short_op },
+	{ "int",	NS_TYPEDEF, .op = &int_op },
+	{ "long",	NS_TYPEDEF, .op = &long_op },
+	{ "float",	NS_TYPEDEF, .op = &float_op },
+	{ "double",	NS_TYPEDEF, .op = &double_op },
+	{ "signed",	NS_TYPEDEF, .op = &signed_op },
+	{ "__signed",	NS_TYPEDEF, .op = &signed_op },
+	{ "__signed__",	NS_TYPEDEF, .op = &signed_op },
+	{ "unsigned",	NS_TYPEDEF, .op = &unsigned_op },
+	{ "_Bool",	NS_TYPEDEF, .type = &bool_ctype, .op = &spec_op },
+
+	/* Predeclared types */
+	{ "__builtin_va_list", NS_TYPEDEF, .type = &ptr_ctype, .op = &spec_op },
+	{ "__builtin_ms_va_list", NS_TYPEDEF, .type = &ptr_ctype, .op = &spec_op },
+	{ "__int128_t",	NS_TYPEDEF, .type = &lllong_ctype, .op = &spec_op },
+	{ "__uint128_t",NS_TYPEDEF, .type = &ulllong_ctype, .op = &spec_op },
+
+	/* Extended types */
+	{ "typeof", 	NS_TYPEDEF, .op = &typeof_op },
+	{ "__typeof", 	NS_TYPEDEF, .op = &typeof_op },
+	{ "__typeof__",	NS_TYPEDEF, .op = &typeof_op },
+
+	{ "__attribute",   NS_TYPEDEF, .op = &attribute_op },
+	{ "__attribute__", NS_TYPEDEF, .op = &attribute_op },
+
+	{ "struct",	NS_TYPEDEF, .op = &struct_op },
+	{ "union", 	NS_TYPEDEF, .op = &union_op },
+	{ "enum", 	NS_TYPEDEF, .op = &enum_op },
+
+	{ "inline",	NS_TYPEDEF, .op = &inline_op },
+	{ "__inline",	NS_TYPEDEF, .op = &inline_op },
+	{ "__inline__",	NS_TYPEDEF, .op = &inline_op },
+
+	/* Ignored for now.. */
+	{ "restrict",	NS_TYPEDEF, .op = &restrict_op},
+	{ "__restrict",	NS_TYPEDEF, .op = &restrict_op},
+
+	/* Storage class */
+	{ "auto",	NS_TYPEDEF, .op = &auto_op },
+	{ "register",	NS_TYPEDEF, .op = &register_op },
+	{ "static",	NS_TYPEDEF, .op = &static_op },
+	{ "extern",	NS_TYPEDEF, .op = &extern_op },
+	{ "__thread",	NS_TYPEDEF, .op = &thread_op },
+
+	/* Statement */
+	{ "if",		NS_KEYWORD, .op = &if_op },
+	{ "return",	NS_KEYWORD, .op = &return_op },
+	{ "break",	NS_KEYWORD, .op = &loop_iter_op },
+	{ "continue",	NS_KEYWORD, .op = &loop_iter_op },
+	{ "default",	NS_KEYWORD, .op = &default_op },
+	{ "case",	NS_KEYWORD, .op = &case_op },
+	{ "switch",	NS_KEYWORD, .op = &switch_op },
+	{ "for",	NS_KEYWORD, .op = &for_op },
+	{ "while",	NS_KEYWORD, .op = &while_op },
+	{ "do",		NS_KEYWORD, .op = &do_op },
+	{ "goto",	NS_KEYWORD, .op = &goto_op },
+	{ "__context__",NS_KEYWORD, .op = &__context___op },
+	{ "__range__",	NS_KEYWORD, .op = &range_op },
+	{ "asm",	NS_KEYWORD, .op = &asm_op },
+	{ "__asm",	NS_KEYWORD, .op = &asm_op },
+	{ "__asm__",	NS_KEYWORD, .op = &asm_op },
+
+	/* Attribute */
+	{ "packed",	NS_KEYWORD, .op = &packed_op },
+	{ "__packed__",	NS_KEYWORD, .op = &packed_op },
+	{ "aligned",	NS_KEYWORD, .op = &aligned_op },
+	{ "__aligned__",NS_KEYWORD, .op = &aligned_op },
+	{ "nocast",	NS_KEYWORD,	MOD_NOCAST,	.op = &attr_mod_op },
+	{ "noderef",	NS_KEYWORD,	MOD_NODEREF,	.op = &attr_mod_op },
+	{ "safe",	NS_KEYWORD,	MOD_SAFE, 	.op = &attr_mod_op },
+	{ "force",	NS_KEYWORD,	.op = &attr_force_op },
+	{ "bitwise",	NS_KEYWORD,	MOD_BITWISE,	.op = &attr_mod_op },
+	{ "__bitwise__",NS_KEYWORD,	MOD_BITWISE,	.op = &attr_mod_op },
+	{ "address_space",NS_KEYWORD,	.op = &address_space_op },
+	{ "mode",	NS_KEYWORD,	.op = &mode_op },
+	{ "context",	NS_KEYWORD,	.op = &context_op },
+	{ "designated_init",	NS_KEYWORD,	.op = &designated_init_op },
+	{ "__transparent_union__",	NS_KEYWORD,	.op = &transparent_union_op },
+	{ "noreturn",	NS_KEYWORD,	MOD_NORETURN,	.op = &attr_mod_op },
+	{ "__noreturn__",	NS_KEYWORD,	MOD_NORETURN,	.op = &attr_mod_op },
+	{ "pure",	NS_KEYWORD,	MOD_PURE,	.op = &attr_mod_op },
+	{"__pure__",	NS_KEYWORD,	MOD_PURE,	.op = &attr_mod_op },
+	{"const",	NS_KEYWORD,	MOD_PURE,	.op = &attr_mod_op },
+	{"__const",	NS_KEYWORD,	MOD_PURE,	.op = &attr_mod_op },
+	{"__const__",	NS_KEYWORD,	MOD_PURE,	.op = &attr_mod_op },
+
+	{ "__mode__",	NS_KEYWORD,	.op = &mode_op },
+	{ "QI",		NS_KEYWORD,	MOD_CHAR,	.op = &mode_QI_op },
+	{ "__QI__",	NS_KEYWORD,	MOD_CHAR,	.op = &mode_QI_op },
+	{ "HI",		NS_KEYWORD,	MOD_SHORT,	.op = &mode_HI_op },
+	{ "__HI__",	NS_KEYWORD,	MOD_SHORT,	.op = &mode_HI_op },
+	{ "SI",		NS_KEYWORD,			.op = &mode_SI_op },
+	{ "__SI__",	NS_KEYWORD,			.op = &mode_SI_op },
+	{ "DI",		NS_KEYWORD,	MOD_LONGLONG,	.op = &mode_DI_op },
+	{ "__DI__",	NS_KEYWORD,	MOD_LONGLONG,	.op = &mode_DI_op },
+	{ "TI",		NS_KEYWORD,	MOD_LONGLONGLONG,	.op = &mode_TI_op },
+	{ "__TI__",	NS_KEYWORD,	MOD_LONGLONGLONG,	.op = &mode_TI_op },
+	{ "word",	NS_KEYWORD,	MOD_LONG,	.op = &mode_word_op },
+	{ "__word__",	NS_KEYWORD,	MOD_LONG,	.op = &mode_word_op },
+};
+
+const char *ignored_attributes[] = {
+	"alias",
+	"__alias__",
+	"alloc_size",
+	"__alloc_size__",
+	"always_inline",
+	"__always_inline__",
+	"artificial",
+	"__artificial__",
+	"bounded",
+	"__bounded__",
+	"cdecl",
+	"__cdecl__",
+	"cold",
+	"__cold__",
+	"constructor",
+	"__constructor__",
+	"deprecated",
+	"__deprecated__",
+	"destructor",
+	"__destructor__",
+	"dllexport",
+	"__dllexport__",
+	"dllimport",
+	"__dllimport__",
+	"externally_visible",
+	"__externally_visible__",
+	"fastcall",
+	"__fastcall__",
+	"format",
+	"__format__",
+	"format_arg",
+	"__format_arg__",
+	"hot",
+	"__hot__",
+	"l1_text",
+	"__l1_text__",
+	"l1_data",
+	"__l1_data__",
+	"l2",
+	"__l2__",
+	"may_alias",
+	"__may_alias__",
+	"malloc",
+	"__malloc__",
+	"may_alias",
+	"__may_alias__",
+	"model",
+	"__model__",
+	"ms_abi",
+	"__ms_abi__",
+	"ms_hook_prologue",
+	"__ms_hook_prologue__",
+	"naked",
+	"__naked__",
+	"no_instrument_function",
+	"__no_instrument_function__",
+	"noinline",
+	"__noinline__",
+	"nonnull",
+	"__nonnull",
+	"__nonnull__",
+	"nothrow",
+	"__nothrow",
+	"__nothrow__",
+	"regparm",
+	"__regparm__",
+	"section",
+	"__section__",
+	"sentinel",
+	"__sentinel__",
+	"signal",
+	"__signal__",
+	"stdcall",
+	"__stdcall__",
+	"syscall_linkage",
+	"__syscall_linkage__",
+	"sysv_abi",
+	"__sysv_abi__",
+	"unused",
+	"__unused__",
+	"used",
+	"__used__",
+	"vector_size",
+	"visibility",
+	"__visibility__",
+	"warn_unused_result",
+	"__warn_unused_result__",
+	"warning",
+	"__warning__",
+	"weak",
+	"__weak__",
+};
+
+
+void init_parser(int stream)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(keyword_table); i++) {
+		struct init_keyword *ptr = keyword_table + i;
+		struct symbol *sym = create_symbol(stream, ptr->name, SYM_KEYWORD, ptr->ns);
+		sym->ident->keyword = 1;
+		if (ptr->ns == NS_TYPEDEF)
+			sym->ident->reserved = 1;
+		sym->ctype.modifiers = ptr->modifiers;
+		sym->ctype.base_type = ptr->type;
+		sym->op = ptr->op;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(ignored_attributes); i++) {
+		const char * name = ignored_attributes[i];
+		struct symbol *sym = create_symbol(stream, name, SYM_KEYWORD,
+						   NS_KEYWORD);
+		sym->ident->keyword = 1;
+		sym->op = &ignore_attr_op;
+	}
+}
+
+
+// Add a symbol to the list of function-local symbols
+static void fn_local_symbol(struct symbol *sym)
+{
+	if (function_symbol_list)
+		add_symbol(function_symbol_list, sym);
+}
+
+static int SENTINEL_ATTR match_idents(struct token *token, ...)
+{
+	va_list args;
+	struct ident * next;
+
+	if (token_type(token) != TOKEN_IDENT)
+		return 0;
+
+	va_start(args, token);
+	do {
+		next = va_arg(args, struct ident *);
+	} while (next && token->ident != next);
+	va_end(args);
+
+	return next && token->ident == next;
+}
+
+
+struct statement *alloc_statement(struct position pos, int type)
+{
+	struct statement *stmt = __alloc_statement(0);
+	stmt->type = type;
+	stmt->pos = pos;
+	return stmt;
+}
+
+static struct token *struct_declaration_list(struct token *token, struct symbol_list **list);
+
+static void apply_modifiers(struct position pos, struct decl_state *ctx)
+{
+	struct symbol *ctype;
+	if (!ctx->mode)
+		return;
+	ctype = ctx->mode->to_mode(ctx->ctype.base_type);
+	if (!ctype)
+		sparse_error(pos, "don't know how to apply mode to %s",
+				show_typename(ctx->ctype.base_type));
+	else
+		ctx->ctype.base_type = ctype;
+	
+}
+
+static struct symbol * alloc_indirect_symbol(struct position pos, struct ctype *ctype, int type)
+{
+	struct symbol *sym = alloc_symbol(pos, type);
+
+	sym->ctype.base_type = ctype->base_type;
+	sym->ctype.modifiers = ctype->modifiers;
+
+	ctype->base_type = sym;
+	ctype->modifiers = 0;
+	return sym;
+}
+
+/*
+ * NOTE! NS_LABEL is not just a different namespace,
+ * it also ends up using function scope instead of the
+ * regular symbol scope.
+ */
+struct symbol *label_symbol(struct token *token)
+{
+	struct symbol *sym = lookup_symbol(token->ident, NS_LABEL);
+	if (!sym) {
+		sym = alloc_symbol(token->pos, SYM_LABEL);
+		bind_symbol(sym, token->ident, NS_LABEL);
+		fn_local_symbol(sym);
+	}
+	return sym;
+}
+
+static struct token *struct_union_enum_specifier(enum type type,
+	struct token *token, struct decl_state *ctx,
+	struct token *(*parse)(struct token *, struct symbol *))
+{
+	struct symbol *sym;
+	struct position *repos;
+
+	token = handle_attributes(token, ctx, KW_ATTRIBUTE);
+	if (token_type(token) == TOKEN_IDENT) {
+		sym = lookup_symbol(token->ident, NS_STRUCT);
+		if (!sym ||
+		    (is_outer_scope(sym->scope) &&
+		     (match_op(token->next,';') || match_op(token->next,'{')))) {
+			// Either a new symbol, or else an out-of-scope
+			// symbol being redefined.
+			sym = alloc_symbol(token->pos, type);
+			bind_symbol(sym, token->ident, NS_STRUCT);
+		}
+		if (sym->type != type)
+			error_die(token->pos, "invalid tag applied to %s", show_typename (sym));
+		ctx->ctype.base_type = sym;
+		repos = &token->pos;
+		token = token->next;
+		if (match_op(token, '{')) {
+			// The following test is actually wrong for empty
+			// structs, but (1) they are not C99, (2) gcc does
+			// the same thing, and (3) it's easier.
+			if (sym->symbol_list)
+				error_die(token->pos, "redefinition of %s", show_typename (sym));
+			sym->pos = *repos;
+			token = parse(token->next, sym);
+			token = expect(token, '}', "at end of struct-union-enum-specifier");
+
+			// Mark the structure as needing re-examination
+			sym->examined = 0;
+			sym->endpos = token->pos;
+		}
+		return token;
+	}
+
+	// private struct/union/enum type
+	if (!match_op(token, '{')) {
+		sparse_error(token->pos, "expected declaration");
+		ctx->ctype.base_type = &bad_ctype;
+		return token;
+	}
+
+	sym = alloc_symbol(token->pos, type);
+	token = parse(token->next, sym);
+	ctx->ctype.base_type = sym;
+	token =  expect(token, '}', "at end of specifier");
+	sym->endpos = token->pos;
+
+	return token;
+}
+
+static struct token *parse_struct_declaration(struct token *token, struct symbol *sym)
+{
+	struct symbol *field, *last = NULL;
+	struct token *res;
+	res = struct_declaration_list(token, &sym->symbol_list);
+	FOR_EACH_PTR(sym->symbol_list, field) {
+		if (!field->ident) {
+			struct symbol *base = field->ctype.base_type;
+			if (base && base->type == SYM_BITFIELD)
+				continue;
+		}
+		if (last)
+			last->next_subobject = field;
+		last = field;
+	} END_FOR_EACH_PTR(field);
+	return res;
+}
+
+static struct token *parse_union_declaration(struct token *token, struct symbol *sym)
+{
+	return struct_declaration_list(token, &sym->symbol_list);
+}
+
+static struct token *struct_specifier(struct token *token, struct decl_state *ctx)
+{
+	return struct_union_enum_specifier(SYM_STRUCT, token, ctx, parse_struct_declaration);
+}
+
+static struct token *union_specifier(struct token *token, struct decl_state *ctx)
+{
+	return struct_union_enum_specifier(SYM_UNION, token, ctx, parse_union_declaration);
+}
+
+
+typedef struct {
+	int x;
+	unsigned long long y;
+} Num;
+
+static void upper_boundary(Num *n, Num *v)
+{
+	if (n->x > v->x)
+		return;
+	if (n->x < v->x) {
+		*n = *v;
+		return;
+	}
+	if (n->y < v->y)
+		n->y = v->y;
+}
+
+static void lower_boundary(Num *n, Num *v)
+{
+	if (n->x < v->x)
+		return;
+	if (n->x > v->x) {
+		*n = *v;
+		return;
+	}
+	if (n->y > v->y)
+		n->y = v->y;
+}
+
+static int type_is_ok(struct symbol *type, Num *upper, Num *lower)
+{
+	int shift = type->bit_size;
+	int is_unsigned = type->ctype.modifiers & MOD_UNSIGNED;
+
+	if (!is_unsigned)
+		shift--;
+	if (upper->x == 0 && upper->y >> shift)
+		return 0;
+	if (lower->x == 0 || (!is_unsigned && (~lower->y >> shift) == 0))
+		return 1;
+	return 0;
+}
+
+static struct symbol *bigger_enum_type(struct symbol *s1, struct symbol *s2)
+{
+	if (s1->bit_size < s2->bit_size) {
+		s1 = s2;
+	} else if (s1->bit_size == s2->bit_size) {
+		if (s2->ctype.modifiers & MOD_UNSIGNED)
+			s1 = s2;
+	}
+	if (s1->bit_size < bits_in_int)
+		return &int_ctype;
+	return s1;
+}
+
+static void cast_enum_list(struct symbol_list *list, struct symbol *base_type)
+{
+	struct symbol *sym;
+
+	FOR_EACH_PTR(list, sym) {
+		struct expression *expr = sym->initializer;
+		struct symbol *ctype;
+		if (expr->type != EXPR_VALUE)
+			continue;
+		ctype = expr->ctype;
+		if (ctype->bit_size == base_type->bit_size)
+			continue;
+		cast_value(expr, base_type, expr, ctype);
+	} END_FOR_EACH_PTR(sym);
+}
+
+static struct token *parse_enum_declaration(struct token *token, struct symbol *parent)
+{
+	unsigned long long lastval = 0;
+	struct symbol *ctype = NULL, *base_type = NULL;
+	Num upper = {-1, 0}, lower = {1, 0};
+
+	parent->examined = 1;
+	parent->ctype.base_type = &int_ctype;
+	while (token_type(token) == TOKEN_IDENT) {
+		struct expression *expr = NULL;
+		struct token *next = token->next;
+		struct symbol *sym;
+
+		if (match_op(next, '=')) {
+			next = constant_expression(next->next, &expr);
+			lastval = get_expression_value(expr);
+			ctype = &void_ctype;
+			if (expr && expr->ctype)
+				ctype = expr->ctype;
+		} else if (!ctype) {
+			ctype = &int_ctype;
+		} else if (is_int_type(ctype)) {
+			lastval++;
+		} else {
+			error_die(token->pos, "can't increment the last enum member");
+		}
+
+		if (!expr) {
+			expr = alloc_expression(token->pos, EXPR_VALUE);
+			expr->value = lastval;
+			expr->ctype = ctype;
+		}
+
+		sym = alloc_symbol(token->pos, SYM_NODE);
+		bind_symbol(sym, token->ident, NS_SYMBOL);
+		sym->ctype.modifiers &= ~MOD_ADDRESSABLE;
+		sym->initializer = expr;
+		sym->enum_member = 1;
+		sym->ctype.base_type = parent;
+		add_ptr_list(&parent->symbol_list, sym);
+
+		if (base_type != &bad_ctype) {
+			if (ctype->type == SYM_NODE)
+				ctype = ctype->ctype.base_type;
+			if (ctype->type == SYM_ENUM) {
+				if (ctype == parent)
+					ctype = base_type;
+				else 
+					ctype = ctype->ctype.base_type;
+			}
+			/*
+			 * base_type rules:
+			 *  - if all enums are of the same type, then
+			 *    the base_type is that type (two first
+			 *    cases)
+			 *  - if enums are of different types, they
+			 *    all have to be integer types, and the
+			 *    base type is at least "int_ctype".
+			 *  - otherwise the base_type is "bad_ctype".
+			 */
+			if (!base_type) {
+				base_type = ctype;
+			} else if (ctype == base_type) {
+				/* nothing */
+			} else if (is_int_type(base_type) && is_int_type(ctype)) {
+				base_type = bigger_enum_type(base_type, ctype);
+			} else
+				base_type = &bad_ctype;
+			parent->ctype.base_type = base_type;
+		}
+		if (is_int_type(base_type)) {
+			Num v = {.y = lastval};
+			if (ctype->ctype.modifiers & MOD_UNSIGNED)
+				v.x = 0;
+			else if ((long long)lastval >= 0)
+				v.x = 0;
+			else
+				v.x = -1;
+			upper_boundary(&upper, &v);
+			lower_boundary(&lower, &v);
+		}
+		token = next;
+
+		sym->endpos = token->pos;
+
+		if (!match_op(token, ','))
+			break;
+		token = token->next;
+	}
+	if (!base_type) {
+		sparse_error(token->pos, "bad enum definition");
+		base_type = &bad_ctype;
+	}
+	else if (!is_int_type(base_type))
+		base_type = base_type;
+	else if (type_is_ok(base_type, &upper, &lower))
+		base_type = base_type;
+	else if (type_is_ok(&int_ctype, &upper, &lower))
+		base_type = &int_ctype;
+	else if (type_is_ok(&uint_ctype, &upper, &lower))
+		base_type = &uint_ctype;
+	else if (type_is_ok(&long_ctype, &upper, &lower))
+		base_type = &long_ctype;
+	else if (type_is_ok(&ulong_ctype, &upper, &lower))
+		base_type = &ulong_ctype;
+	else if (type_is_ok(&llong_ctype, &upper, &lower))
+		base_type = &llong_ctype;
+	else if (type_is_ok(&ullong_ctype, &upper, &lower))
+		base_type = &ullong_ctype;
+	else
+		base_type = &bad_ctype;
+	parent->ctype.base_type = base_type;
+	parent->ctype.modifiers |= (base_type->ctype.modifiers & MOD_UNSIGNED);
+	parent->examined = 0;
+
+	cast_enum_list(parent->symbol_list, base_type);
+
+	return token;
+}
+
+static struct token *enum_specifier(struct token *token, struct decl_state *ctx)
+{
+	struct token *ret = struct_union_enum_specifier(SYM_ENUM, token, ctx, parse_enum_declaration);
+	struct ctype *ctype = &ctx->ctype.base_type->ctype;
+
+	if (!ctype->base_type)
+		ctype->base_type = &incomplete_ctype;
+
+	return ret;
+}
+
+static void apply_ctype(struct position pos, struct ctype *thistype, struct ctype *ctype);
+
+static struct token *typeof_specifier(struct token *token, struct decl_state *ctx)
+{
+	struct symbol *sym;
+
+	if (!match_op(token, '(')) {
+		sparse_error(token->pos, "expected '(' after typeof");
+		return token;
+	}
+	if (lookup_type(token->next)) {
+		token = typename(token->next, &sym, NULL);
+		ctx->ctype.base_type = sym->ctype.base_type;
+		apply_ctype(token->pos, &sym->ctype, &ctx->ctype);
+	} else {
+		struct symbol *typeof_sym = alloc_symbol(token->pos, SYM_TYPEOF);
+		token = parse_expression(token->next, &typeof_sym->initializer);
+
+		typeof_sym->endpos = token->pos;
+		if (!typeof_sym->initializer) {
+			sparse_error(token->pos, "expected expression after the '(' token");
+			typeof_sym = &bad_ctype;
+		}
+		ctx->ctype.base_type = typeof_sym;
+	}
+	return expect(token, ')', "after typeof");
+}
+
+static struct token *ignore_attribute(struct token *token, struct symbol *attr, struct decl_state *ctx)
+{
+	struct expression *expr = NULL;
+	if (match_op(token, '('))
+		token = parens_expression(token, &expr, "in attribute");
+	return token;
+}
+
+static struct token *attribute_packed(struct token *token, struct symbol *attr, struct decl_state *ctx)
+{
+	if (!ctx->ctype.alignment)
+		ctx->ctype.alignment = 1;
+	return token;
+}
+
+static struct token *attribute_aligned(struct token *token, struct symbol *attr, struct decl_state *ctx)
+{
+	int alignment = max_alignment;
+	struct expression *expr = NULL;
+
+	if (match_op(token, '(')) {
+		token = parens_expression(token, &expr, "in attribute");
+		if (expr)
+			alignment = const_expression_value(expr);
+	}
+	if (alignment & (alignment-1)) {
+		warning(token->pos, "I don't like non-power-of-2 alignments");
+		return token;
+	} else if (alignment > ctx->ctype.alignment)
+		ctx->ctype.alignment = alignment;
+	return token;
+}
+
+static void apply_qualifier(struct position *pos, struct ctype *ctx, unsigned long qual)
+{
+	if (ctx->modifiers & qual)
+		warning(*pos, "duplicate %s", modifier_string(qual));
+	ctx->modifiers |= qual;
+}
+
+static struct token *attribute_modifier(struct token *token, struct symbol *attr, struct decl_state *ctx)
+{
+	apply_qualifier(&token->pos, &ctx->ctype, attr->ctype.modifiers);
+	return token;
+}
+
+static struct token *attribute_address_space(struct token *token, struct symbol *attr, struct decl_state *ctx)
+{
+	struct expression *expr = NULL;
+	int as;
+	token = expect(token, '(', "after address_space attribute");
+	token = conditional_expression(token, &expr);
+	if (expr) {
+		as = const_expression_value(expr);
+		if (Waddress_space && as)
+			ctx->ctype.as = as;
+	}
+	token = expect(token, ')', "after address_space attribute");
+	return token;
+}
+
+static struct symbol *to_QI_mode(struct symbol *ctype)
+{
+	if (ctype->ctype.base_type != &int_type)
+		return NULL;
+	if (ctype == &char_ctype)
+		return ctype;
+	return ctype->ctype.modifiers & MOD_UNSIGNED ? &uchar_ctype
+						     : &schar_ctype;
+}
+
+static struct symbol *to_HI_mode(struct symbol *ctype)
+{
+	if (ctype->ctype.base_type != &int_type)
+		return NULL;
+	return ctype->ctype.modifiers & MOD_UNSIGNED ? &ushort_ctype
+						     : &sshort_ctype;
+}
+
+static struct symbol *to_SI_mode(struct symbol *ctype)
+{
+	if (ctype->ctype.base_type != &int_type)
+		return NULL;
+	return ctype->ctype.modifiers & MOD_UNSIGNED ? &uint_ctype
+						     : &sint_ctype;
+}
+
+static struct symbol *to_DI_mode(struct symbol *ctype)
+{
+	if (ctype->ctype.base_type != &int_type)
+		return NULL;
+	return ctype->ctype.modifiers & MOD_UNSIGNED ? &ullong_ctype
+						     : &sllong_ctype;
+}
+
+static struct symbol *to_TI_mode(struct symbol *ctype)
+{
+	if (ctype->ctype.base_type != &int_type)
+		return NULL;
+	return ctype->ctype.modifiers & MOD_UNSIGNED ? &ulllong_ctype
+						     : &slllong_ctype;
+}
+
+static struct symbol *to_word_mode(struct symbol *ctype)
+{
+	if (ctype->ctype.base_type != &int_type)
+		return NULL;
+	return ctype->ctype.modifiers & MOD_UNSIGNED ? &ulong_ctype
+						     : &slong_ctype;
+}
+
+static struct token *attribute_mode(struct token *token, struct symbol *attr, struct decl_state *ctx)
+{
+	token = expect(token, '(', "after mode attribute");
+	if (token_type(token) == TOKEN_IDENT) {
+		struct symbol *mode = lookup_keyword(token->ident, NS_KEYWORD);
+		if (mode && mode->op->type == KW_MODE)
+			ctx->mode = mode->op;
+		else
+			sparse_error(token->pos, "unknown mode attribute %s\n", show_ident(token->ident));
+		token = token->next;
+	} else
+		sparse_error(token->pos, "expect attribute mode symbol\n");
+	token = expect(token, ')', "after mode attribute");
+	return token;
+}
+
+static struct token *attribute_context(struct token *token, struct symbol *attr, struct decl_state *ctx)
+{
+	struct context *context = alloc_context();
+	struct expression *args[3];
+	int argc = 0;
+
+	token = expect(token, '(', "after context attribute");
+	while (!match_op(token, ')')) {
+		struct expression *expr = NULL;
+		token = conditional_expression(token, &expr);
+		if (!expr)
+			break;
+		if (argc < 3)
+			args[argc++] = expr;
+		if (!match_op(token, ','))
+			break;
+		token = token->next;
+	}
+
+	switch(argc) {
+	case 0:
+		sparse_error(token->pos, "expected context input/output values");
+		break;
+	case 1:
+		context->in = get_expression_value(args[0]);
+		break;
+	case 2:
+		context->in = get_expression_value(args[0]);
+		context->out = get_expression_value(args[1]);
+		break;
+	case 3:
+		context->context = args[0];
+		context->in = get_expression_value(args[1]);
+		context->out = get_expression_value(args[2]);
+		break;
+	}
+
+	if (argc)
+		add_ptr_list(&ctx->ctype.contexts, context);
+
+	token = expect(token, ')', "after context attribute");
+	return token;
+}
+
+static struct token *attribute_designated_init(struct token *token, struct symbol *attr, struct decl_state *ctx)
+{
+	if (ctx->ctype.base_type && ctx->ctype.base_type->type == SYM_STRUCT)
+		ctx->ctype.base_type->designated_init = 1;
+	else
+		warning(token->pos, "attribute designated_init applied to non-structure type");
+	return token;
+}
+
+static struct token *attribute_transparent_union(struct token *token, struct symbol *attr, struct decl_state *ctx)
+{
+	if (Wtransparent_union)
+		warning(token->pos, "ignoring attribute __transparent_union__");
+	return token;
+}
+
+static struct token *recover_unknown_attribute(struct token *token)
+{
+	struct expression *expr = NULL;
+
+	sparse_error(token->pos, "attribute '%s': unknown attribute", show_ident(token->ident));
+	token = token->next;
+	if (match_op(token, '('))
+		token = parens_expression(token, &expr, "in attribute");
+	return token;
+}
+
+static struct token *attribute_specifier(struct token *token, struct decl_state *ctx)
+{
+	token = expect(token, '(', "after attribute");
+	token = expect(token, '(', "after attribute");
+
+	for (;;) {
+		struct ident *attribute_name;
+		struct symbol *attr;
+
+		if (eof_token(token))
+			break;
+		if (match_op(token, ';'))
+			break;
+		if (token_type(token) != TOKEN_IDENT)
+			break;
+		attribute_name = token->ident;
+		attr = lookup_keyword(attribute_name, NS_KEYWORD);
+		if (attr && attr->op->attribute)
+			token = attr->op->attribute(token->next, attr, ctx);
+		else
+			token = recover_unknown_attribute(token);
+
+		if (!match_op(token, ','))
+			break;
+		token = token->next;
+	}
+
+	token = expect(token, ')', "after attribute");
+	token = expect(token, ')', "after attribute");
+	return token;
+}
+
+static const char *storage_class[] = 
+{
+	[STypedef] = "typedef",
+	[SAuto] = "auto",
+	[SExtern] = "extern",
+	[SStatic] = "static",
+	[SRegister] = "register",
+	[SForced] = "[force]"
+};
+
+static unsigned long storage_modifiers(struct decl_state *ctx)
+{
+	static unsigned long mod[] = 
+	{
+		[SAuto] = MOD_AUTO,
+		[SExtern] = MOD_EXTERN,
+		[SStatic] = MOD_STATIC,
+		[SRegister] = MOD_REGISTER
+	};
+	return mod[ctx->storage_class] | (ctx->is_inline ? MOD_INLINE : 0)
+		| (ctx->is_tls ? MOD_TLS : 0);
+}
+
+static void set_storage_class(struct position *pos, struct decl_state *ctx, int class)
+{
+	/* __thread can be used alone, or with extern or static */
+	if (ctx->is_tls && (class != SStatic && class != SExtern)) {
+		sparse_error(*pos, "__thread can only be used alone, or with "
+				"extern or static");
+		return;
+	}
+
+	if (!ctx->storage_class) {
+		ctx->storage_class = class;
+		return;
+	}
+	if (ctx->storage_class == class)
+		sparse_error(*pos, "duplicate %s", storage_class[class]);
+	else
+		sparse_error(*pos, "multiple storage classes");
+}
+
+static struct token *typedef_specifier(struct token *next, struct decl_state *ctx)
+{
+	set_storage_class(&next->pos, ctx, STypedef);
+	return next;
+}
+
+static struct token *auto_specifier(struct token *next, struct decl_state *ctx)
+{
+	set_storage_class(&next->pos, ctx, SAuto);
+	return next;
+}
+
+static struct token *register_specifier(struct token *next, struct decl_state *ctx)
+{
+	set_storage_class(&next->pos, ctx, SRegister);
+	return next;
+}
+
+static struct token *static_specifier(struct token *next, struct decl_state *ctx)
+{
+	set_storage_class(&next->pos, ctx, SStatic);
+	return next;
+}
+
+static struct token *extern_specifier(struct token *next, struct decl_state *ctx)
+{
+	set_storage_class(&next->pos, ctx, SExtern);
+	return next;
+}
+
+static struct token *thread_specifier(struct token *next, struct decl_state *ctx)
+{
+	/* This GCC extension can be used alone, or with extern or static */
+	if (!ctx->storage_class || ctx->storage_class == SStatic
+			|| ctx->storage_class == SExtern) {
+		ctx->is_tls = 1;
+	} else {
+		sparse_error(next->pos, "__thread can only be used alone, or "
+				"with extern or static");
+	}
+
+	return next;
+}
+
+static struct token *attribute_force(struct token *token, struct symbol *attr, struct decl_state *ctx)
+{
+	set_storage_class(&token->pos, ctx, SForced);
+	return token;
+}
+
+static struct token *inline_specifier(struct token *next, struct decl_state *ctx)
+{
+	ctx->is_inline = 1;
+	return next;
+}
+
+static struct token *const_qualifier(struct token *next, struct decl_state *ctx)
+{
+	apply_qualifier(&next->pos, &ctx->ctype, MOD_CONST);
+	return next;
+}
+
+static struct token *volatile_qualifier(struct token *next, struct decl_state *ctx)
+{
+	apply_qualifier(&next->pos, &ctx->ctype, MOD_VOLATILE);
+	return next;
+}
+
+static void apply_ctype(struct position pos, struct ctype *thistype, struct ctype *ctype)
+{
+	unsigned long mod = thistype->modifiers;
+
+	if (mod)
+		apply_qualifier(&pos, ctype, mod);
+
+	/* Context */
+	concat_ptr_list((struct ptr_list *)thistype->contexts,
+	                (struct ptr_list **)&ctype->contexts);
+
+	/* Alignment */
+	if (thistype->alignment > ctype->alignment)
+		ctype->alignment = thistype->alignment;
+
+	/* Address space */
+	if (thistype->as)
+		ctype->as = thistype->as;
+}
+
+static void specifier_conflict(struct position pos, int what, struct ident *new)
+{
+	const char *old;
+	if (what & (Set_S | Set_T))
+		goto Catch_all;
+	if (what & Set_Char)
+		old = "char";
+	else if (what & Set_Double)
+		old = "double";
+	else if (what & Set_Float)
+		old = "float";
+	else if (what & Set_Signed)
+		old = "signed";
+	else if (what & Set_Unsigned)
+		old = "unsigned";
+	else if (what & Set_Short)
+		old = "short";
+	else if (what & Set_Long)
+		old = "long";
+	else
+		old = "long long";
+	sparse_error(pos, "impossible combination of type specifiers: %s %s",
+			old, show_ident(new));
+	return;
+
+Catch_all:
+	sparse_error(pos, "two or more data types in declaration specifiers");
+}
+
+static struct symbol * const int_types[] =
+	{&short_ctype, &int_ctype, &long_ctype, &llong_ctype};
+static struct symbol * const signed_types[] =
+	{&sshort_ctype, &sint_ctype, &slong_ctype, &sllong_ctype,
+	 &slllong_ctype};
+static struct symbol * const unsigned_types[] =
+	{&ushort_ctype, &uint_ctype, &ulong_ctype, &ullong_ctype,
+	 &ulllong_ctype};
+static struct symbol * const real_types[] =
+	{&float_ctype, &double_ctype, &ldouble_ctype};
+static struct symbol * const char_types[] =
+	{&char_ctype, &schar_ctype, &uchar_ctype};
+static struct symbol * const * const types[] = {
+	int_types + 1, signed_types + 1, unsigned_types + 1,
+	real_types + 1, char_types, char_types + 1, char_types + 2
+};
+
+struct symbol *ctype_integer(int size, int want_unsigned)
+{
+	return types[want_unsigned ? CUInt : CInt][size];
+}
+
+static struct token *handle_qualifiers(struct token *t, struct decl_state *ctx)
+{
+	while (token_type(t) == TOKEN_IDENT) {
+		struct symbol *s = lookup_symbol(t->ident, NS_TYPEDEF);
+		if (!s)
+			break;
+		if (s->type != SYM_KEYWORD)
+			break;
+		if (!(s->op->type & (KW_ATTRIBUTE | KW_QUALIFIER)))
+			break;
+		t = t->next;
+		if (s->op->declarator)
+			t = s->op->declarator(t, ctx);
+	}
+	return t;
+}
+
+static struct token *declaration_specifiers(struct token *token, struct decl_state *ctx)
+{
+	int seen = 0;
+	int class = CInt;
+	int size = 0;
+
+	while (token_type(token) == TOKEN_IDENT) {
+		struct symbol *s = lookup_symbol(token->ident,
+						 NS_TYPEDEF | NS_SYMBOL);
+		if (!s || !(s->namespace & NS_TYPEDEF))
+			break;
+		if (s->type != SYM_KEYWORD) {
+			if (seen & Set_Any)
+				break;
+			seen |= Set_S | Set_T;
+			ctx->ctype.base_type = s->ctype.base_type;
+			apply_ctype(token->pos, &s->ctype, &ctx->ctype);
+			token = token->next;
+			continue;
+		}
+		if (s->op->type & KW_SPECIFIER) {
+			if (seen & s->op->test) {
+				specifier_conflict(token->pos,
+						   seen & s->op->test,
+						   token->ident);
+				break;
+			}
+			seen |= s->op->set;
+			class += s->op->class;
+			if (s->op->type & KW_SHORT) {
+				size = -1;
+			} else if (s->op->type & KW_LONG && size++) {
+				if (class == CReal) {
+					specifier_conflict(token->pos,
+							   Set_Vlong,
+							   &double_ident);
+					break;
+				}
+				seen |= Set_Vlong;
+			}
+		}
+		token = token->next;
+		if (s->op->declarator)
+			token = s->op->declarator(token, ctx);
+		if (s->op->type & KW_EXACT) {
+			ctx->ctype.base_type = s->ctype.base_type;
+			ctx->ctype.modifiers |= s->ctype.modifiers;
+		}
+	}
+
+	if (!(seen & Set_S)) {	/* not set explicitly? */
+		struct symbol *base = &incomplete_ctype;
+		if (seen & Set_Any)
+			base = types[class][size];
+		ctx->ctype.base_type = base;
+	}
+
+	if (ctx->ctype.modifiers & MOD_BITWISE) {
+		struct symbol *type;
+		ctx->ctype.modifiers &= ~MOD_BITWISE;
+		if (!is_int_type(ctx->ctype.base_type)) {
+			sparse_error(token->pos, "invalid modifier");
+			return token;
+		}
+		type = alloc_symbol(token->pos, SYM_BASETYPE);
+		*type = *ctx->ctype.base_type;
+		type->ctype.modifiers &= ~MOD_SPECIFIER;
+		type->ctype.base_type = ctx->ctype.base_type;
+		type->type = SYM_RESTRICT;
+		ctx->ctype.base_type = type;
+		create_fouled(type);
+	}
+	return token;
+}
+
+static struct token *abstract_array_declarator(struct token *token, struct symbol *sym)
+{
+	struct expression *expr = NULL;
+
+	if (match_idents(token, &restrict_ident, &__restrict_ident, NULL))
+		token = token->next;
+	token = parse_expression(token, &expr);
+	sym->array_size = expr;
+	return token;
+}
+
+static struct token *parameter_type_list(struct token *, struct symbol *);
+static struct token *identifier_list(struct token *, struct symbol *);
+static struct token *declarator(struct token *token, struct decl_state *ctx);
+
+static struct token *skip_attribute(struct token *token)
+{
+	token = token->next;
+	if (match_op(token, '(')) {
+		int depth = 1;
+		token = token->next;
+		while (depth && !eof_token(token)) {
+			if (token_type(token) == TOKEN_SPECIAL) {
+				if (token->special == '(')
+					depth++;
+				else if (token->special == ')')
+					depth--;
+			}
+			token = token->next;
+		}
+	}
+	return token;
+}
+
+static struct token *skip_attributes(struct token *token)
+{
+	struct symbol *keyword;
+	for (;;) {
+		if (token_type(token) != TOKEN_IDENT)
+			break;
+		keyword = lookup_keyword(token->ident, NS_KEYWORD | NS_TYPEDEF);
+		if (!keyword || keyword->type != SYM_KEYWORD)
+			break;
+		if (!(keyword->op->type & KW_ATTRIBUTE))
+			break;
+		token = expect(token->next, '(', "after attribute");
+		token = expect(token, '(', "after attribute");
+		for (;;) {
+			if (eof_token(token))
+				break;
+			if (match_op(token, ';'))
+				break;
+			if (token_type(token) != TOKEN_IDENT)
+				break;
+			token = skip_attribute(token);
+			if (!match_op(token, ','))
+				break;
+			token = token->next;
+		}
+		token = expect(token, ')', "after attribute");
+		token = expect(token, ')', "after attribute");
+	}
+	return token;
+}
+
+static struct token *handle_attributes(struct token *token, struct decl_state *ctx, unsigned int keywords)
+{
+	struct symbol *keyword;
+	for (;;) {
+		if (token_type(token) != TOKEN_IDENT)
+			break;
+		keyword = lookup_keyword(token->ident, NS_KEYWORD | NS_TYPEDEF);
+		if (!keyword || keyword->type != SYM_KEYWORD)
+			break;
+		if (!(keyword->op->type & keywords))
+			break;
+		token = keyword->op->declarator(token->next, ctx);
+		keywords &= KW_ATTRIBUTE;
+	}
+	return token;
+}
+
+static int is_nested(struct token *token, struct token **p,
+		    int prefer_abstract)
+{
+	/*
+	 * This can be either a parameter list or a grouping.
+	 * For the direct (non-abstract) case, we know if must be
+	 * a parameter list if we already saw the identifier.
+	 * For the abstract case, we know if must be a parameter
+	 * list if it is empty or starts with a type.
+	 */
+	struct token *next = token->next;
+
+	*p = next = skip_attributes(next);
+
+	if (token_type(next) == TOKEN_IDENT) {
+		if (lookup_type(next))
+			return !prefer_abstract;
+		return 1;
+	}
+
+	if (match_op(next, ')') || match_op(next, SPECIAL_ELLIPSIS))
+		return 0;
+
+	return 1;
+}
+
+enum kind {
+	Empty, K_R, Proto, Bad_Func,
+};
+
+static enum kind which_func(struct token *token,
+			    struct ident **n,
+			    int prefer_abstract)
+{
+	struct token *next = token->next;
+
+	if (token_type(next) == TOKEN_IDENT) {
+		if (lookup_type(next))
+			return Proto;
+		/* identifier list not in definition; complain */
+		if (prefer_abstract)
+			warning(token->pos,
+				"identifier list not in definition");
+		return K_R;
+	}
+
+	if (token_type(next) != TOKEN_SPECIAL)
+		return Bad_Func;
+
+	if (next->special == ')') {
+		/* don't complain about those */
+		if (!n || match_op(next->next, ';'))
+			return Empty;
+		warning(next->pos,
+			"non-ANSI function declaration of function '%s'",
+			show_ident(*n));
+		return Empty;
+	}
+
+	if (next->special == SPECIAL_ELLIPSIS) {
+		warning(next->pos,
+			"variadic functions must have one named argument");
+		return Proto;
+	}
+
+	return Bad_Func;
+}
+
+static struct token *direct_declarator(struct token *token, struct decl_state *ctx)
+{
+	struct ctype *ctype = &ctx->ctype;
+	struct token *next;
+	struct ident **p = ctx->ident;
+
+	if (ctx->ident && token_type(token) == TOKEN_IDENT) {
+		*ctx->ident = token->ident;
+		token = token->next;
+	} else if (match_op(token, '(') &&
+	    is_nested(token, &next, ctx->prefer_abstract)) {
+		struct symbol *base_type = ctype->base_type;
+		if (token->next != next)
+			next = handle_attributes(token->next, ctx,
+						  KW_ATTRIBUTE);
+		token = declarator(next, ctx);
+		token = expect(token, ')', "in nested declarator");
+		while (ctype->base_type != base_type)
+			ctype = &ctype->base_type->ctype;
+		p = NULL;
+	}
+
+	if (match_op(token, '(')) {
+		enum kind kind = which_func(token, p, ctx->prefer_abstract);
+		struct symbol *fn;
+		fn = alloc_indirect_symbol(token->pos, ctype, SYM_FN);
+		token = token->next;
+		if (kind == K_R)
+			token = identifier_list(token, fn);
+		else if (kind == Proto)
+			token = parameter_type_list(token, fn);
+		token = expect(token, ')', "in function declarator");
+		fn->endpos = token->pos;
+		return token;
+	}
+
+	while (match_op(token, '[')) {
+		struct symbol *array;
+		array = alloc_indirect_symbol(token->pos, ctype, SYM_ARRAY);
+		token = abstract_array_declarator(token->next, array);
+		token = expect(token, ']', "in abstract_array_declarator");
+		array->endpos = token->pos;
+		ctype = &array->ctype;
+	}
+	return token;
+}
+
+static struct token *pointer(struct token *token, struct decl_state *ctx)
+{
+	while (match_op(token,'*')) {
+		struct symbol *ptr = alloc_symbol(token->pos, SYM_PTR);
+		ptr->ctype.modifiers = ctx->ctype.modifiers;
+		ptr->ctype.base_type = ctx->ctype.base_type;
+		ptr->ctype.as = ctx->ctype.as;
+		ptr->ctype.contexts = ctx->ctype.contexts;
+		ctx->ctype.modifiers = 0;
+		ctx->ctype.base_type = ptr;
+		ctx->ctype.as = 0;
+		ctx->ctype.contexts = NULL;
+		ctx->ctype.alignment = 0;
+
+		token = handle_qualifiers(token->next, ctx);
+		ctx->ctype.base_type->endpos = token->pos;
+	}
+	return token;
+}
+
+static struct token *declarator(struct token *token, struct decl_state *ctx)
+{
+	token = pointer(token, ctx);
+	return direct_declarator(token, ctx);
+}
+
+static struct token *handle_bitfield(struct token *token, struct decl_state *ctx)
+{
+	struct ctype *ctype = &ctx->ctype;
+	struct expression *expr;
+	struct symbol *bitfield;
+	long long width;
+
+	if (ctype->base_type != &int_type && !is_int_type(ctype->base_type)) {
+		sparse_error(token->pos, "invalid bitfield specifier for type %s.",
+			show_typename(ctype->base_type));
+		// Parse this to recover gracefully.
+		return conditional_expression(token->next, &expr);
+	}
+
+	bitfield = alloc_indirect_symbol(token->pos, ctype, SYM_BITFIELD);
+	token = conditional_expression(token->next, &expr);
+	width = const_expression_value(expr);
+	bitfield->bit_size = width;
+
+	if (width < 0 || width > INT_MAX) {
+		sparse_error(token->pos, "invalid bitfield width, %lld.", width);
+		width = -1;
+	} else if (*ctx->ident && width == 0) {
+		sparse_error(token->pos, "invalid named zero-width bitfield `%s'",
+		     show_ident(*ctx->ident));
+		width = -1;
+	} else if (*ctx->ident) {
+		struct symbol *base_type = bitfield->ctype.base_type;
+		struct symbol *bitfield_type = base_type == &int_type ? bitfield : base_type;
+		int is_signed = !(bitfield_type->ctype.modifiers & MOD_UNSIGNED);
+		if (Wone_bit_signed_bitfield && width == 1 && is_signed) {
+			// Valid values are either {-1;0} or {0}, depending on integer
+			// representation.  The latter makes for very efficient code...
+			sparse_error(token->pos, "dubious one-bit signed bitfield");
+		}
+		if (Wdefault_bitfield_sign &&
+		    bitfield_type->type != SYM_ENUM &&
+		    !(bitfield_type->ctype.modifiers & MOD_EXPLICITLY_SIGNED) &&
+		    is_signed) {
+			// The sign of bitfields is unspecified by default.
+			warning(token->pos, "dubious bitfield without explicit `signed' or `unsigned'");
+		}
+	}
+	bitfield->bit_size = width;
+	bitfield->endpos = token->pos;
+	return token;
+}
+
+static struct token *declaration_list(struct token *token, struct symbol_list **list)
+{
+	struct decl_state ctx = {.prefer_abstract = 0};
+	struct ctype saved;
+	unsigned long mod;
+
+	token = declaration_specifiers(token, &ctx);
+	mod = storage_modifiers(&ctx);
+	saved = ctx.ctype;
+	for (;;) {
+		struct symbol *decl = alloc_symbol(token->pos, SYM_NODE);
+		ctx.ident = &decl->ident;
+
+		token = declarator(token, &ctx);
+		if (match_op(token, ':'))
+			token = handle_bitfield(token, &ctx);
+
+		token = handle_attributes(token, &ctx, KW_ATTRIBUTE);
+		apply_modifiers(token->pos, &ctx);
+
+		decl->ctype = ctx.ctype;
+		decl->ctype.modifiers |= mod;
+		decl->endpos = token->pos;
+		add_symbol(list, decl);
+		if (!match_op(token, ','))
+			break;
+		token = token->next;
+		ctx.ctype = saved;
+	}
+	return token;
+}
+
+static struct token *struct_declaration_list(struct token *token, struct symbol_list **list)
+{
+	while (!match_op(token, '}')) {
+		if (!match_op(token, ';'))
+			token = declaration_list(token, list);
+		if (!match_op(token, ';')) {
+			sparse_error(token->pos, "expected ; at end of declaration");
+			break;
+		}
+		token = token->next;
+	}
+	return token;
+}
+
+static struct token *parameter_declaration(struct token *token, struct symbol *sym)
+{
+	struct decl_state ctx = {.prefer_abstract = 1};
+
+	token = declaration_specifiers(token, &ctx);
+	ctx.ident = &sym->ident;
+	token = declarator(token, &ctx);
+	token = handle_attributes(token, &ctx, KW_ATTRIBUTE);
+	apply_modifiers(token->pos, &ctx);
+	sym->ctype = ctx.ctype;
+	sym->ctype.modifiers |= storage_modifiers(&ctx);
+	sym->endpos = token->pos;
+	return token;
+}
+
+struct token *typename(struct token *token, struct symbol **p, int *forced)
+{
+	struct decl_state ctx = {.prefer_abstract = 1};
+	int class;
+	struct symbol *sym = alloc_symbol(token->pos, SYM_NODE);
+	*p = sym;
+	token = declaration_specifiers(token, &ctx);
+	token = declarator(token, &ctx);
+	apply_modifiers(token->pos, &ctx);
+	sym->ctype = ctx.ctype;
+	sym->endpos = token->pos;
+	class = ctx.storage_class;
+	if (forced) {
+		*forced = 0;
+		if (class == SForced) {
+			*forced = 1;
+			class = 0;
+		}
+	}
+	if (class)
+		warning(sym->pos, "storage class in typename (%s %s)",
+			storage_class[class], show_typename(sym));
+	return token;
+}
+
+static struct token *expression_statement(struct token *token, struct expression **tree)
+{
+	token = parse_expression(token, tree);
+	return expect(token, ';', "at end of statement");
+}
+
+static struct token *parse_asm_operands(struct token *token, struct statement *stmt,
+	struct expression_list **inout)
+{
+	struct expression *expr;
+
+	/* Allow empty operands */
+	if (match_op(token->next, ':') || match_op(token->next, ')'))
+		return token->next;
+	do {
+		struct ident *ident = NULL;
+		if (match_op(token->next, '[') &&
+		    token_type(token->next->next) == TOKEN_IDENT &&
+		    match_op(token->next->next->next, ']')) {
+			ident = token->next->next->ident;
+			token = token->next->next->next;
+		}
+		add_expression(inout, (struct expression *)ident); /* UGGLEE!!! */
+		token = primary_expression(token->next, &expr);
+		add_expression(inout, expr);
+		token = parens_expression(token, &expr, "in asm parameter");
+		add_expression(inout, expr);
+	} while (match_op(token, ','));
+	return token;
+}
+
+static struct token *parse_asm_clobbers(struct token *token, struct statement *stmt,
+	struct expression_list **clobbers)
+{
+	struct expression *expr;
+
+	do {
+		token = primary_expression(token->next, &expr);
+		if (expr)
+			add_expression(clobbers, expr);
+	} while (match_op(token, ','));
+	return token;
+}
+
+static struct token *parse_asm_labels(struct token *token, struct statement *stmt,
+		        struct symbol_list **labels)
+{
+	struct symbol *label;
+
+	do {
+		token = token->next; /* skip ':' and ',' */
+		if (token_type(token) != TOKEN_IDENT)
+			return token;
+		label = label_symbol(token);
+		add_symbol(labels, label);
+		token = token->next;
+	} while (match_op(token, ','));
+	return token;
+}
+
+static struct token *parse_asm_statement(struct token *token, struct statement *stmt)
+{
+	int is_goto = 0;
+
+	token = token->next;
+	stmt->type = STMT_ASM;
+	if (match_idents(token, &__volatile___ident, &__volatile_ident, &volatile_ident, NULL)) {
+		token = token->next;
+	}
+	if (token_type(token) == TOKEN_IDENT && token->ident == &goto_ident) {
+		is_goto = 1;
+		token = token->next;
+	}
+	token = expect(token, '(', "after asm");
+	token = parse_expression(token, &stmt->asm_string);
+	if (match_op(token, ':'))
+		token = parse_asm_operands(token, stmt, &stmt->asm_outputs);
+	if (match_op(token, ':'))
+		token = parse_asm_operands(token, stmt, &stmt->asm_inputs);
+	if (match_op(token, ':'))
+		token = parse_asm_clobbers(token, stmt, &stmt->asm_clobbers);
+	if (is_goto && match_op(token, ':'))
+		token = parse_asm_labels(token, stmt, &stmt->asm_labels);
+	token = expect(token, ')', "after asm");
+	return expect(token, ';', "at end of asm-statement");
+}
+
+static struct token *parse_asm_declarator(struct token *token, struct decl_state *ctx)
+{
+	struct expression *expr;
+	token = expect(token, '(', "after asm");
+	token = parse_expression(token->next, &expr);
+	token = expect(token, ')', "after asm");
+	return token;
+}
+
+/* Make a statement out of an expression */
+static struct statement *make_statement(struct expression *expr)
+{
+	struct statement *stmt;
+
+	if (!expr)
+		return NULL;
+	stmt = alloc_statement(expr->pos, STMT_EXPRESSION);
+	stmt->expression = expr;
+	return stmt;
+}
+
+/*
+ * All iterators have two symbols associated with them:
+ * the "continue" and "break" symbols, which are targets
+ * for continue and break statements respectively.
+ *
+ * They are in a special name-space, but they follow
+ * all the normal visibility rules, so nested iterators
+ * automatically work right.
+ */
+static void start_iterator(struct statement *stmt)
+{
+	struct symbol *cont, *brk;
+
+	start_symbol_scope();
+	cont = alloc_symbol(stmt->pos, SYM_NODE);
+	bind_symbol(cont, &continue_ident, NS_ITERATOR);
+	brk = alloc_symbol(stmt->pos, SYM_NODE);
+	bind_symbol(brk, &break_ident, NS_ITERATOR);
+
+	stmt->type = STMT_ITERATOR;
+	stmt->iterator_break = brk;
+	stmt->iterator_continue = cont;
+	fn_local_symbol(brk);
+	fn_local_symbol(cont);
+}
+
+static void end_iterator(struct statement *stmt)
+{
+	end_symbol_scope();
+}
+
+static struct statement *start_function(struct symbol *sym)
+{
+	struct symbol *ret;
+	struct statement *stmt = alloc_statement(sym->pos, STMT_COMPOUND);
+
+	start_function_scope();
+	ret = alloc_symbol(sym->pos, SYM_NODE);
+	ret->ctype = sym->ctype.base_type->ctype;
+	ret->ctype.modifiers &= ~(MOD_STORAGE | MOD_CONST | MOD_VOLATILE | MOD_TLS | MOD_INLINE | MOD_ADDRESSABLE | MOD_NOCAST | MOD_NODEREF | MOD_ACCESSED | MOD_TOPLEVEL);
+	ret->ctype.modifiers |= (MOD_AUTO | MOD_REGISTER);
+	bind_symbol(ret, &return_ident, NS_ITERATOR);
+	stmt->ret = ret;
+	fn_local_symbol(ret);
+
+	// Currently parsed symbol for __func__/__FUNCTION__/__PRETTY_FUNCTION__
+	current_fn = sym;
+
+	return stmt;
+}
+
+static void end_function(struct symbol *sym)
+{
+	current_fn = NULL;
+	end_function_scope();
+}
+
+/*
+ * A "switch()" statement, like an iterator, has a
+ * the "break" symbol associated with it. It works
+ * exactly like the iterator break - it's the target
+ * for any break-statements in scope, and means that
+ * "break" handling doesn't even need to know whether
+ * it's breaking out of an iterator or a switch.
+ *
+ * In addition, the "case" symbol is a marker for the
+ * case/default statements to find the switch statement
+ * that they are associated with.
+ */
+static void start_switch(struct statement *stmt)
+{
+	struct symbol *brk, *switch_case;
+
+	start_symbol_scope();
+	brk = alloc_symbol(stmt->pos, SYM_NODE);
+	bind_symbol(brk, &break_ident, NS_ITERATOR);
+
+	switch_case = alloc_symbol(stmt->pos, SYM_NODE);
+	bind_symbol(switch_case, &case_ident, NS_ITERATOR);
+	switch_case->stmt = stmt;
+
+	stmt->type = STMT_SWITCH;
+	stmt->switch_break = brk;
+	stmt->switch_case = switch_case;
+
+	fn_local_symbol(brk);
+	fn_local_symbol(switch_case);
+}
+
+static void end_switch(struct statement *stmt)
+{
+	if (!stmt->switch_case->symbol_list)
+		warning(stmt->pos, "switch with no cases");
+	end_symbol_scope();
+}
+
+static void add_case_statement(struct statement *stmt)
+{
+	struct symbol *target = lookup_symbol(&case_ident, NS_ITERATOR);
+	struct symbol *sym;
+
+	if (!target) {
+		sparse_error(stmt->pos, "not in switch scope");
+		stmt->type = STMT_NONE;
+		return;
+	}
+	sym = alloc_symbol(stmt->pos, SYM_NODE);
+	add_symbol(&target->symbol_list, sym);
+	sym->stmt = stmt;
+	stmt->case_label = sym;
+	fn_local_symbol(sym);
+}
+
+static struct token *parse_return_statement(struct token *token, struct statement *stmt)
+{
+	struct symbol *target = lookup_symbol(&return_ident, NS_ITERATOR);
+
+	if (!target)
+		error_die(token->pos, "internal error: return without a function target");
+	stmt->type = STMT_RETURN;
+	stmt->ret_target = target;
+	return expression_statement(token->next, &stmt->ret_value);
+}
+
+static struct token *parse_for_statement(struct token *token, struct statement *stmt)
+{
+	struct symbol_list *syms;
+	struct expression *e1, *e2, *e3;
+	struct statement *iterator;
+
+	start_iterator(stmt);
+	token = expect(token->next, '(', "after 'for'");
+
+	syms = NULL;
+	e1 = NULL;
+	/* C99 variable declaration? */
+	if (lookup_type(token)) {
+		token = external_declaration(token, &syms);
+	} else {
+		token = parse_expression(token, &e1);
+		token = expect(token, ';', "in 'for'");
+	}
+	token = parse_expression(token, &e2);
+	token = expect(token, ';', "in 'for'");
+	token = parse_expression(token, &e3);
+	token = expect(token, ')', "in 'for'");
+	token = statement(token, &iterator);
+
+	stmt->iterator_syms = syms;
+	stmt->iterator_pre_statement = make_statement(e1);
+	stmt->iterator_pre_condition = e2;
+	stmt->iterator_post_statement = make_statement(e3);
+	stmt->iterator_post_condition = NULL;
+	stmt->iterator_statement = iterator;
+	end_iterator(stmt);
+
+	return token;
+}
+
+static struct token *parse_while_statement(struct token *token, struct statement *stmt)
+{
+	struct expression *expr;
+	struct statement *iterator;
+
+	start_iterator(stmt);
+	token = parens_expression(token->next, &expr, "after 'while'");
+	token = statement(token, &iterator);
+
+	stmt->iterator_pre_condition = expr;
+	stmt->iterator_post_condition = NULL;
+	stmt->iterator_statement = iterator;
+	end_iterator(stmt);
+
+	return token;
+}
+
+static struct token *parse_do_statement(struct token *token, struct statement *stmt)
+{
+	struct expression *expr;
+	struct statement *iterator;
+
+	start_iterator(stmt);
+	token = statement(token->next, &iterator);
+	if (token_type(token) == TOKEN_IDENT && token->ident == &while_ident)
+		token = token->next;
+	else
+		sparse_error(token->pos, "expected 'while' after 'do'");
+	token = parens_expression(token, &expr, "after 'do-while'");
+
+	stmt->iterator_post_condition = expr;
+	stmt->iterator_statement = iterator;
+	end_iterator(stmt);
+
+	if (iterator && iterator->type != STMT_COMPOUND && Wdo_while)
+		warning(iterator->pos, "do-while statement is not a compound statement");
+
+	return expect(token, ';', "after statement");
+}
+
+static struct token *parse_if_statement(struct token *token, struct statement *stmt)
+{
+	stmt->type = STMT_IF;
+	token = parens_expression(token->next, &stmt->if_conditional, "after if");
+	token = statement(token, &stmt->if_true);
+	if (token_type(token) != TOKEN_IDENT)
+		return token;
+	if (token->ident != &else_ident)
+		return token;
+	return statement(token->next, &stmt->if_false);
+}
+
+static inline struct token *case_statement(struct token *token, struct statement *stmt)
+{
+	stmt->type = STMT_CASE;
+	token = expect(token, ':', "after default/case");
+	add_case_statement(stmt);
+	return statement(token, &stmt->case_statement);
+}
+
+static struct token *parse_case_statement(struct token *token, struct statement *stmt)
+{
+	token = parse_expression(token->next, &stmt->case_expression);
+	if (match_op(token, SPECIAL_ELLIPSIS))
+		token = parse_expression(token->next, &stmt->case_to);
+	return case_statement(token, stmt);
+}
+
+static struct token *parse_default_statement(struct token *token, struct statement *stmt)
+{
+	return case_statement(token->next, stmt);
+}
+
+static struct token *parse_loop_iterator(struct token *token, struct statement *stmt)
+{
+	struct symbol *target = lookup_symbol(token->ident, NS_ITERATOR);
+	stmt->type = STMT_GOTO;
+	stmt->goto_label = target;
+	if (!target)
+		sparse_error(stmt->pos, "break/continue not in iterator scope");
+	return expect(token->next, ';', "at end of statement");
+}
+
+static struct token *parse_switch_statement(struct token *token, struct statement *stmt)
+{
+	stmt->type = STMT_SWITCH;
+	start_switch(stmt);
+	token = parens_expression(token->next, &stmt->switch_expression, "after 'switch'");
+	token = statement(token, &stmt->switch_statement);
+	end_switch(stmt);
+	return token;
+}
+
+static struct token *parse_goto_statement(struct token *token, struct statement *stmt)
+{
+	stmt->type = STMT_GOTO;
+	token = token->next;
+	if (match_op(token, '*')) {
+		token = parse_expression(token->next, &stmt->goto_expression);
+		add_statement(&function_computed_goto_list, stmt);
+	} else if (token_type(token) == TOKEN_IDENT) {
+		stmt->goto_label = label_symbol(token);
+		token = token->next;
+	} else {
+		sparse_error(token->pos, "Expected identifier or goto expression");
+	}
+	return expect(token, ';', "at end of statement");
+}
+
+static struct token *parse_context_statement(struct token *token, struct statement *stmt)
+{
+	stmt->type = STMT_CONTEXT;
+	token = parse_expression(token->next, &stmt->expression);
+	if (stmt->expression->type == EXPR_PREOP
+	    && stmt->expression->op == '('
+	    && stmt->expression->unop->type == EXPR_COMMA) {
+		struct expression *expr;
+		expr = stmt->expression->unop;
+		stmt->context = expr->left;
+		stmt->expression = expr->right;
+	}
+	return expect(token, ';', "at end of statement");
+}
+
+static struct token *parse_range_statement(struct token *token, struct statement *stmt)
+{
+	stmt->type = STMT_RANGE;
+	token = assignment_expression(token->next, &stmt->range_expression);
+	token = expect(token, ',', "after range expression");
+	token = assignment_expression(token, &stmt->range_low);
+	token = expect(token, ',', "after low range");
+	token = assignment_expression(token, &stmt->range_high);
+	return expect(token, ';', "after range statement");
+}
+
+static struct token *statement(struct token *token, struct statement **tree)
+{
+	struct statement *stmt = alloc_statement(token->pos, STMT_NONE);
+
+	*tree = stmt;
+	if (token_type(token) == TOKEN_IDENT) {
+		struct symbol *s = lookup_keyword(token->ident, NS_KEYWORD);
+		if (s && s->op->statement)
+			return s->op->statement(token, stmt);
+
+		if (match_op(token->next, ':')) {
+			stmt->type = STMT_LABEL;
+			stmt->label_identifier = label_symbol(token);
+			token = skip_attributes(token->next->next);
+			return statement(token, &stmt->label_statement);
+		}
+	}
+
+	if (match_op(token, '{')) {
+		stmt->type = STMT_COMPOUND;
+		start_symbol_scope();
+		token = compound_statement(token->next, stmt);
+		end_symbol_scope();
+		
+		return expect(token, '}', "at end of compound statement");
+	}
+			
+	stmt->type = STMT_EXPRESSION;
+	return expression_statement(token, &stmt->expression);
+}
+
+/* gcc extension - __label__ ident-list; in the beginning of compound stmt */
+static struct token *label_statement(struct token *token)
+{
+	while (token_type(token) == TOKEN_IDENT) {
+		struct symbol *sym = alloc_symbol(token->pos, SYM_LABEL);
+		/* it's block-scope, but we want label namespace */
+		bind_symbol(sym, token->ident, NS_SYMBOL);
+		sym->namespace = NS_LABEL;
+		fn_local_symbol(sym);
+		token = token->next;
+		if (!match_op(token, ','))
+			break;
+		token = token->next;
+	}
+	return expect(token, ';', "at end of label declaration");
+}
+
+static struct token * statement_list(struct token *token, struct statement_list **list)
+{
+	int seen_statement = 0;
+	while (token_type(token) == TOKEN_IDENT &&
+	       token->ident == &__label___ident)
+		token = label_statement(token->next);
+	for (;;) {
+		struct statement * stmt;
+		if (eof_token(token))
+			break;
+		if (match_op(token, '}'))
+			break;
+		if (lookup_type(token)) {
+			if (seen_statement) {
+				warning(token->pos, "mixing declarations and code");
+				seen_statement = 0;
+			}
+			stmt = alloc_statement(token->pos, STMT_DECLARATION);
+			token = external_declaration(token, &stmt->declaration);
+		} else {
+			seen_statement = Wdeclarationafterstatement;
+			token = statement(token, &stmt);
+		}
+		add_statement(list, stmt);
+	}
+	return token;
+}
+
+static struct token *identifier_list(struct token *token, struct symbol *fn)
+{
+	struct symbol_list **list = &fn->arguments;
+	for (;;) {
+		struct symbol *sym = alloc_symbol(token->pos, SYM_NODE);
+		sym->ident = token->ident;
+		token = token->next;
+		sym->endpos = token->pos;
+		sym->ctype.base_type = &incomplete_ctype;
+		add_symbol(list, sym);
+		if (!match_op(token, ',') ||
+		    token_type(token->next) != TOKEN_IDENT ||
+		    lookup_type(token->next))
+			break;
+		token = token->next;
+	}
+	return token;
+}
+
+static struct token *parameter_type_list(struct token *token, struct symbol *fn)
+{
+	struct symbol_list **list = &fn->arguments;
+
+	for (;;) {
+		struct symbol *sym;
+
+		if (match_op(token, SPECIAL_ELLIPSIS)) {
+			fn->variadic = 1;
+			token = token->next;
+			break;
+		}
+
+		sym = alloc_symbol(token->pos, SYM_NODE);
+		token = parameter_declaration(token, sym);
+		if (sym->ctype.base_type == &void_ctype) {
+			/* Special case: (void) */
+			if (!*list && !sym->ident)
+				break;
+			warning(token->pos, "void parameter");
+		}
+		add_symbol(list, sym);
+		if (!match_op(token, ','))
+			break;
+		token = token->next;
+	}
+	return token;
+}
+
+struct token *compound_statement(struct token *token, struct statement *stmt)
+{
+	token = statement_list(token, &stmt->stmts);
+	return token;
+}
+
+static struct expression *identifier_expression(struct token *token)
+{
+	struct expression *expr = alloc_expression(token->pos, EXPR_IDENTIFIER);
+	expr->expr_ident = token->ident;
+	return expr;
+}
+
+static struct expression *index_expression(struct expression *from, struct expression *to)
+{
+	int idx_from, idx_to;
+	struct expression *expr = alloc_expression(from->pos, EXPR_INDEX);
+
+	idx_from = const_expression_value(from);
+	idx_to = idx_from;
+	if (to) {
+		idx_to = const_expression_value(to);
+		if (idx_to < idx_from || idx_from < 0)
+			warning(from->pos, "nonsense array initializer index range");
+	}
+	expr->idx_from = idx_from;
+	expr->idx_to = idx_to;
+	return expr;
+}
+
+static struct token *single_initializer(struct expression **ep, struct token *token)
+{
+	int expect_equal = 0;
+	struct token *next = token->next;
+	struct expression **tail = ep;
+	int nested;
+
+	*ep = NULL; 
+
+	if ((token_type(token) == TOKEN_IDENT) && match_op(next, ':')) {
+		struct expression *expr = identifier_expression(token);
+		if (Wold_initializer)
+			warning(token->pos, "obsolete struct initializer, use C99 syntax");
+		token = initializer(&expr->ident_expression, next->next);
+		if (expr->ident_expression)
+			*ep = expr;
+		return token;
+	}
+
+	for (tail = ep, nested = 0; ; nested++, next = token->next) {
+		if (match_op(token, '.') && (token_type(next) == TOKEN_IDENT)) {
+			struct expression *expr = identifier_expression(next);
+			*tail = expr;
+			tail = &expr->ident_expression;
+			expect_equal = 1;
+			token = next->next;
+		} else if (match_op(token, '[')) {
+			struct expression *from = NULL, *to = NULL, *expr;
+			token = constant_expression(token->next, &from);
+			if (!from) {
+				sparse_error(token->pos, "Expected constant expression");
+				break;
+			}
+			if (match_op(token, SPECIAL_ELLIPSIS))
+				token = constant_expression(token->next, &to);
+			expr = index_expression(from, to);
+			*tail = expr;
+			tail = &expr->idx_expression;
+			token = expect(token, ']', "at end of initializer index");
+			if (nested)
+				expect_equal = 1;
+		} else {
+			break;
+		}
+	}
+	if (nested && !expect_equal) {
+		if (!match_op(token, '='))
+			warning(token->pos, "obsolete array initializer, use C99 syntax");
+		else
+			expect_equal = 1;
+	}
+	if (expect_equal)
+		token = expect(token, '=', "at end of initializer index");
+
+	token = initializer(tail, token);
+	if (!*tail)
+		*ep = NULL;
+	return token;
+}
+
+static struct token *initializer_list(struct expression_list **list, struct token *token)
+{
+	struct expression *expr;
+
+	for (;;) {
+		token = single_initializer(&expr, token);
+		if (!expr)
+			break;
+		add_expression(list, expr);
+		if (!match_op(token, ','))
+			break;
+		token = token->next;
+	}
+	return token;
+}
+
+struct token *initializer(struct expression **tree, struct token *token)
+{
+	if (match_op(token, '{')) {
+		struct expression *expr = alloc_expression(token->pos, EXPR_INITIALIZER);
+		*tree = expr;
+		token = initializer_list(&expr->expr_list, token->next);
+		return expect(token, '}', "at end of initializer");
+	}
+	return assignment_expression(token, tree);
+}
+
+static void declare_argument(struct symbol *sym, struct symbol *fn)
+{
+	if (!sym->ident) {
+		sparse_error(sym->pos, "no identifier for function argument");
+		return;
+	}
+	bind_symbol(sym, sym->ident, NS_SYMBOL);
+}
+
+static struct token *parse_function_body(struct token *token, struct symbol *decl,
+	struct symbol_list **list)
+{
+	struct symbol_list **old_symbol_list;
+	struct symbol *base_type = decl->ctype.base_type;
+	struct statement *stmt, **p;
+	struct symbol *prev;
+	struct symbol *arg;
+
+	old_symbol_list = function_symbol_list;
+	if (decl->ctype.modifiers & MOD_INLINE) {
+		function_symbol_list = &decl->inline_symbol_list;
+		p = &base_type->inline_stmt;
+	} else {
+		function_symbol_list = &decl->symbol_list;
+		p = &base_type->stmt;
+	}
+	function_computed_target_list = NULL;
+	function_computed_goto_list = NULL;
+
+	if (decl->ctype.modifiers & MOD_EXTERN) {
+		if (!(decl->ctype.modifiers & MOD_INLINE))
+			warning(decl->pos, "function '%s' with external linkage has definition", show_ident(decl->ident));
+	}
+	if (!(decl->ctype.modifiers & MOD_STATIC))
+		decl->ctype.modifiers |= MOD_EXTERN;
+
+	stmt = start_function(decl);
+
+	*p = stmt;
+	FOR_EACH_PTR (base_type->arguments, arg) {
+		declare_argument(arg, base_type);
+	} END_FOR_EACH_PTR(arg);
+
+	token = compound_statement(token->next, stmt);
+
+	end_function(decl);
+	if (!(decl->ctype.modifiers & MOD_INLINE))
+		add_symbol(list, decl);
+	check_declaration(decl);
+	decl->definition = decl;
+	prev = decl->same_symbol;
+	if (prev && prev->definition) {
+		warning(decl->pos, "multiple definitions for function '%s'",
+			show_ident(decl->ident));
+		info(prev->definition->pos, " the previous one is here");
+	} else {
+		while (prev) {
+			prev->definition = decl;
+			prev = prev->same_symbol;
+		}
+	}
+	function_symbol_list = old_symbol_list;
+	if (function_computed_goto_list) {
+		if (!function_computed_target_list)
+			warning(decl->pos, "function '%s' has computed goto but no targets?", show_ident(decl->ident));
+		else {
+			FOR_EACH_PTR(function_computed_goto_list, stmt) {
+				stmt->target_list = function_computed_target_list;
+			} END_FOR_EACH_PTR(stmt);
+		}
+	}
+	return expect(token, '}', "at end of function");
+}
+
+static void promote_k_r_types(struct symbol *arg)
+{
+	struct symbol *base = arg->ctype.base_type;
+	if (base && base->ctype.base_type == &int_type && (base->ctype.modifiers & (MOD_CHAR | MOD_SHORT))) {
+		arg->ctype.base_type = &int_ctype;
+	}
+}
+
+static void apply_k_r_types(struct symbol_list *argtypes, struct symbol *fn)
+{
+	struct symbol_list *real_args = fn->ctype.base_type->arguments;
+	struct symbol *arg;
+
+	FOR_EACH_PTR(real_args, arg) {
+		struct symbol *type;
+
+		/* This is quadratic in the number of arguments. We _really_ don't care */
+		FOR_EACH_PTR(argtypes, type) {
+			if (type->ident == arg->ident)
+				goto match;
+		} END_FOR_EACH_PTR(type);
+		sparse_error(arg->pos, "missing type declaration for parameter '%s'", show_ident(arg->ident));
+		continue;
+match:
+		type->used = 1;
+		/* "char" and "short" promote to "int" */
+		promote_k_r_types(type);
+
+		arg->ctype = type->ctype;
+	} END_FOR_EACH_PTR(arg);
+
+	FOR_EACH_PTR(argtypes, arg) {
+		if (!arg->used)
+			warning(arg->pos, "nonsensical parameter declaration '%s'", show_ident(arg->ident));
+	} END_FOR_EACH_PTR(arg);
+
+}
+
+static struct token *parse_k_r_arguments(struct token *token, struct symbol *decl,
+	struct symbol_list **list)
+{
+	struct symbol_list *args = NULL;
+
+	warning(token->pos, "non-ANSI definition of function '%s'", show_ident(decl->ident));
+	do {
+		token = declaration_list(token, &args);
+		if (!match_op(token, ';')) {
+			sparse_error(token->pos, "expected ';' at end of parameter declaration");
+			break;
+		}
+		token = token->next;
+	} while (lookup_type(token));
+
+	apply_k_r_types(args, decl);
+
+	if (!match_op(token, '{')) {
+		sparse_error(token->pos, "expected function body");
+		return token;
+	}
+	return parse_function_body(token, decl, list);
+}
+
+static struct token *toplevel_asm_declaration(struct token *token, struct symbol_list **list)
+{
+	struct symbol *anon = alloc_symbol(token->pos, SYM_NODE);
+	struct symbol *fn = alloc_symbol(token->pos, SYM_FN);
+	struct statement *stmt;
+
+	anon->ctype.base_type = fn;
+	stmt = alloc_statement(token->pos, STMT_NONE);
+	fn->stmt = stmt;
+
+	token = parse_asm_statement(token, stmt);
+
+	add_symbol(list, anon);
+	return token;
+}
+
+struct token *external_declaration(struct token *token, struct symbol_list **list)
+{
+	struct ident *ident = NULL;
+	struct symbol *decl;
+	struct decl_state ctx = { .ident = &ident };
+	struct ctype saved;
+	struct symbol *base_type;
+	unsigned long mod;
+	int is_typedef;
+
+	/* Top-level inline asm? */
+	if (token_type(token) == TOKEN_IDENT) {
+		struct symbol *s = lookup_keyword(token->ident, NS_KEYWORD);
+		if (s && s->op->toplevel)
+			return s->op->toplevel(token, list);
+	}
+
+	/* Parse declaration-specifiers, if any */
+	token = declaration_specifiers(token, &ctx);
+	mod = storage_modifiers(&ctx);
+	decl = alloc_symbol(token->pos, SYM_NODE);
+	/* Just a type declaration? */
+	if (match_op(token, ';')) {
+		apply_modifiers(token->pos, &ctx);
+		return token->next;
+	}
+
+	saved = ctx.ctype;
+	token = declarator(token, &ctx);
+	token = handle_attributes(token, &ctx, KW_ATTRIBUTE | KW_ASM);
+	apply_modifiers(token->pos, &ctx);
+
+	decl->ctype = ctx.ctype;
+	decl->ctype.modifiers |= mod;
+	decl->endpos = token->pos;
+
+	/* Just a type declaration? */
+	if (!ident) {
+		warning(token->pos, "missing identifier in declaration");
+		return expect(token, ';', "at the end of type declaration");
+	}
+
+	/* type define declaration? */
+	is_typedef = ctx.storage_class == STypedef;
+
+	/* Typedefs don't have meaningful storage */
+	if (is_typedef)
+		decl->ctype.modifiers |= MOD_USERTYPE;
+
+	bind_symbol(decl, ident, is_typedef ? NS_TYPEDEF: NS_SYMBOL);
+
+	base_type = decl->ctype.base_type;
+
+	if (is_typedef) {
+		if (base_type && !base_type->ident) {
+			switch (base_type->type) {
+			case SYM_STRUCT:
+			case SYM_UNION:
+			case SYM_ENUM:
+			case SYM_RESTRICT:
+				base_type->ident = ident;
+			}
+		}
+	} else if (base_type && base_type->type == SYM_FN) {
+		/* K&R argument declaration? */
+		if (lookup_type(token))
+			return parse_k_r_arguments(token, decl, list);
+		if (match_op(token, '{'))
+			return parse_function_body(token, decl, list);
+
+		if (!(decl->ctype.modifiers & MOD_STATIC))
+			decl->ctype.modifiers |= MOD_EXTERN;
+	} else if (base_type == &void_ctype && !(decl->ctype.modifiers & MOD_EXTERN)) {
+		sparse_error(token->pos, "void declaration");
+	}
+
+	for (;;) {
+		if (!is_typedef && match_op(token, '=')) {
+			if (decl->ctype.modifiers & MOD_EXTERN) {
+				warning(decl->pos, "symbol with external linkage has initializer");
+				decl->ctype.modifiers &= ~MOD_EXTERN;
+			}
+			token = initializer(&decl->initializer, token->next);
+		}
+		if (!is_typedef) {
+			if (!(decl->ctype.modifiers & (MOD_EXTERN | MOD_INLINE))) {
+				add_symbol(list, decl);
+				fn_local_symbol(decl);
+			}
+		}
+		check_declaration(decl);
+		if (decl->same_symbol)
+			decl->definition = decl->same_symbol->definition;
+
+		if (!match_op(token, ','))
+			break;
+
+		token = token->next;
+		ident = NULL;
+		decl = alloc_symbol(token->pos, SYM_NODE);
+		ctx.ctype = saved;
+		token = handle_attributes(token, &ctx, KW_ATTRIBUTE);
+		token = declarator(token, &ctx);
+		token = handle_attributes(token, &ctx, KW_ATTRIBUTE | KW_ASM);
+		apply_modifiers(token->pos, &ctx);
+		decl->ctype = ctx.ctype;
+		decl->ctype.modifiers |= mod;
+		decl->endpos = token->pos;
+		if (!ident) {
+			sparse_error(token->pos, "expected identifier name in type definition");
+			return token;
+		}
+
+		if (is_typedef)
+			decl->ctype.modifiers |= MOD_USERTYPE;
+
+		bind_symbol(decl, ident, is_typedef ? NS_TYPEDEF: NS_SYMBOL);
+
+		/* Function declarations are automatically extern unless specifically static */
+		base_type = decl->ctype.base_type;
+		if (!is_typedef && base_type && base_type->type == SYM_FN) {
+			if (!(decl->ctype.modifiers & MOD_STATIC))
+				decl->ctype.modifiers |= MOD_EXTERN;
+		}
+	}
+	return expect(token, ';', "at end of declaration");
+}
diff --git a/deps/sparse/parse.dtd b/deps/sparse/parse.dtd
new file mode 100644
index 0000000..cbf95ec
--- /dev/null
+++ b/deps/sparse/parse.dtd
@@ -0,0 +1,51 @@
+<!ELEMENT parse (symbol+) >
+
+<!ELEMENT symbol (symbol*) >
+
+<!ATTLIST symbol type (uninitialized|preprocessor|basetype|node|pointer|function|array|struct|union|enum|typedef|typeof|member|bitfield|label|restrict|fouled|keyword|bad) #REQUIRED
+                 id ID #REQUIRED
+		 file CDATA #REQUIRED
+		 start-line CDATA #REQUIRED
+		 start-col CDATA #REQUIRED
+		 end-line CDATA #IMPLIED
+		 end-col CDATA #IMPLIED
+		 end-file CDATA #IMPLIED
+
+		 ident CDATA #IMPLIED
+		 base-type IDREF #IMPLIED
+		 base-type-builtin (char|signed char|unsigned char|short|signed short|unsigned short|int|signed int|unsigned int|signed long|long|unsigned long|long long|signed long long|unsigned long long|void|bool|string|float|double|long double|incomplete type|abstract int|abstract fp|label type|bad type) #IMPLIED
+
+		 array-size CDATA #IMPLIED
+
+		 bit-size CDATA #IMPLIED
+		 alignment CDATA #IMPLIED
+		 offset CDATA #IMPLIED
+		 bit-offset CDATA #IMPLIED
+
+		 auto (0|1) #IMPLIED
+		 register (0|1) #IMPLIED
+		 static (0|1) #IMPLIED
+		 extern (0|1) #IMPLIED
+		 const (0|1) #IMPLIED
+		 volatile (0|1) #IMPLIED
+		 signed (0|1) #IMPLIED
+		 unsigned (0|1) #IMPLIED
+		 char (0|1) #IMPLIED
+		 short (0|1) #IMPLIED
+		 long (0|1) #IMPLIED
+		 long-long (0|1) #IMPLIED
+		 typedef (0|1) #IMPLIED
+		 inline (0|1) #IMPLIED
+		 addressable (0|1) #IMPLIED
+		 nocast (0|1) #IMPLIED
+		 noderef (0|1) #IMPLIED
+		 accessed (0|1) #IMPLIED
+		 toplevel (0|1) #IMPLIED
+		 label (0|1) #IMPLIED
+		 assigned (0|1) #IMPLIED
+		 type-type (0|1) #IMPLIED
+		 safe (0|1) #IMPLIED
+		 usertype (0|1) #IMPLIED
+		 force (0|1) #IMPLIED
+		 explicitly-signed (0|1) #IMPLIED
+		 bitwise (0|1) #IMPLIED >
diff --git a/deps/sparse/parse.h b/deps/sparse/parse.h
new file mode 100644
index 0000000..b26bd03
--- /dev/null
+++ b/deps/sparse/parse.h
@@ -0,0 +1,134 @@
+#ifndef PARSE_H
+#define PARSE_H
+/*
+ * Basic parsing data structures. Statements and symbols.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+
+#include "symbol.h"
+
+enum statement_type {
+	STMT_NONE,
+	STMT_DECLARATION,
+	STMT_EXPRESSION,
+	STMT_COMPOUND,
+	STMT_IF,
+	STMT_RETURN,
+	STMT_CASE,
+	STMT_SWITCH,
+	STMT_ITERATOR,
+	STMT_LABEL,
+	STMT_GOTO,
+	STMT_ASM,
+	STMT_CONTEXT,
+	STMT_RANGE,
+};
+
+struct statement {
+	enum statement_type type;
+	struct position pos;
+	union {
+		struct /* declaration */ {
+			struct symbol_list *declaration;
+		};
+		struct {
+			struct expression *expression;
+			struct expression *context;
+		};
+		struct /* return_statement */ {
+			struct expression *ret_value;
+			struct symbol *ret_target;
+		};
+		struct /* if_statement */ {
+			struct expression *if_conditional;
+			struct statement *if_true;
+			struct statement *if_false;
+		};
+		struct /* compound_struct */ {
+			struct statement_list *stmts;
+			struct symbol *ret;
+			struct symbol *inline_fn;
+			struct statement *args;
+		};
+		struct /* labeled_struct */ {
+			struct symbol *label_identifier;
+			struct statement *label_statement;
+		};
+		struct /* case_struct */ {
+			struct expression *case_expression;
+			struct expression *case_to;
+			struct statement *case_statement;
+			struct symbol *case_label;
+		};
+		struct /* switch_struct */ {
+			struct expression *switch_expression;
+			struct statement *switch_statement;
+			struct symbol *switch_break, *switch_case;
+		};
+		struct /* iterator_struct */ {
+			struct symbol *iterator_break;
+			struct symbol *iterator_continue;
+			struct symbol_list *iterator_syms;
+			struct statement  *iterator_pre_statement;
+			struct expression *iterator_pre_condition;
+
+			struct statement  *iterator_statement;
+
+			struct statement  *iterator_post_statement;
+			struct expression *iterator_post_condition;
+		};
+		struct /* goto_struct */ {
+			struct symbol *goto_label;
+
+			/* computed gotos have these: */
+			struct expression *goto_expression;
+			struct symbol_list *target_list;
+		};
+		struct /* goto_bb */ {
+			struct expression *bb_conditional;
+			struct symbol *bb_target;
+		};
+		struct /* multijmp */ {
+			struct expression *multi_from;
+			struct expression *multi_to;
+			struct symbol *multi_target;
+		};
+		struct /* asm */ {
+			struct expression *asm_string;
+			struct expression_list *asm_outputs;
+			struct expression_list *asm_inputs;
+			struct expression_list *asm_clobbers;
+			struct symbol_list *asm_labels;
+		};
+		struct /* range */ {
+			struct expression *range_expression;
+			struct expression *range_low;
+			struct expression *range_high;
+		};
+	};
+};
+
+extern struct symbol_list *function_computed_target_list;
+extern struct statement_list *function_computed_goto_list;
+
+extern struct token *parse_expression(struct token *, struct expression **);
+extern struct symbol *label_symbol(struct token *token);
+
+extern int show_statement(struct statement *);
+extern void show_statement_list(struct statement_list *, const char *);
+extern int show_expression(struct expression *);
+
+extern struct token *external_declaration(struct token *token, struct symbol_list **list);
+
+extern struct symbol *ctype_integer(int size, int want_unsigned);
+
+extern void copy_statement(struct statement *src, struct statement *dst);
+extern int inline_function(struct expression *expr, struct symbol *sym);
+extern void uninline(struct symbol *sym);
+extern void init_parser(int);
+
+#endif /* PARSE_H */
diff --git a/deps/sparse/pre-process.c b/deps/sparse/pre-process.c
new file mode 100644
index 0000000..8a16f8b
--- /dev/null
+++ b/deps/sparse/pre-process.c
@@ -0,0 +1,1845 @@
+/*
+ * Do C preprocessing, based on a token list gathered by
+ * the tokenizer.
+ *
+ * This may not be the smartest preprocessor on the planet.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <time.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "parse.h"
+#include "token.h"
+#include "symbol.h"
+#include "expression.h"
+#include "scope.h"
+
+static int false_nesting = 0;
+
+#define INCLUDEPATHS 300
+const char *includepath[INCLUDEPATHS+1] = {
+	"",
+	"/usr/include",
+	"/usr/local/include",
+	NULL
+};
+
+static const char **quote_includepath = includepath;
+static const char **angle_includepath = includepath + 1;
+static const char **isys_includepath   = includepath + 1;
+static const char **sys_includepath   = includepath + 1;
+static const char **dirafter_includepath = includepath + 3;
+
+#define dirty_stream(stream)				\
+	do {						\
+		if (!stream->dirty) {			\
+			stream->dirty = 1;		\
+			if (!stream->ifndef)		\
+				stream->protect = NULL;	\
+		}					\
+	} while(0)
+
+#define end_group(stream)					\
+	do {							\
+		if (stream->ifndef == stream->top_if) {		\
+			stream->ifndef = NULL;			\
+			if (!stream->dirty)			\
+				stream->protect = NULL;		\
+			else if (stream->protect)		\
+				stream->dirty = 0;		\
+		}						\
+	} while(0)
+
+#define nesting_error(stream)		\
+	do {				\
+		stream->dirty = 1;	\
+		stream->ifndef = NULL;	\
+		stream->protect = NULL;	\
+	} while(0)
+
+static struct token *alloc_token(struct position *pos)
+{
+	struct token *token = __alloc_token(0);
+
+	token->pos.stream = pos->stream;
+	token->pos.line = pos->line;
+	token->pos.pos = pos->pos;
+	token->pos.whitespace = 1;
+	return token;
+}
+
+static const char *show_token_sequence(struct token *token);
+
+/* Expand symbol 'sym' at '*list' */
+static int expand(struct token **, struct symbol *);
+
+static void replace_with_string(struct token *token, const char *str)
+{
+	int size = strlen(str) + 1;
+	struct string *s = __alloc_string(size);
+
+	s->length = size;
+	memcpy(s->data, str, size);
+	token_type(token) = TOKEN_STRING;
+	token->string = s;
+}
+
+static void replace_with_integer(struct token *token, unsigned int val)
+{
+	char *buf = __alloc_bytes(11);
+	sprintf(buf, "%u", val);
+	token_type(token) = TOKEN_NUMBER;
+	token->number = buf;
+}
+
+static struct symbol *lookup_macro(struct ident *ident)
+{
+	struct symbol *sym = lookup_symbol(ident, NS_MACRO | NS_UNDEF);
+	if (sym && sym->namespace != NS_MACRO)
+		sym = NULL;
+	return sym;
+}
+
+static int token_defined(struct token *token)
+{
+	if (token_type(token) == TOKEN_IDENT) {
+		struct symbol *sym = lookup_macro(token->ident);
+		if (sym) {
+			sym->used_in = file_scope;
+			return 1;
+		}
+		return 0;
+	}
+
+	sparse_error(token->pos, "expected preprocessor identifier");
+	return 0;
+}
+
+static void replace_with_defined(struct token *token)
+{
+	static const char *string[] = { "0", "1" };
+	int defined = token_defined(token);
+
+	token_type(token) = TOKEN_NUMBER;
+	token->number = string[defined];
+}
+
+static int expand_one_symbol(struct token **list)
+{
+	struct token *token = *list;
+	struct symbol *sym;
+	static char buffer[12]; /* __DATE__: 3 + ' ' + 2 + ' ' + 4 + '\0' */
+	static time_t t = 0;
+
+	if (token->pos.noexpand)
+		return 1;
+
+	sym = lookup_macro(token->ident);
+	if (sym) {
+		sym->used_in = file_scope;
+		return expand(list, sym);
+	}
+	if (token->ident == &__LINE___ident) {
+		replace_with_integer(token, token->pos.line);
+	} else if (token->ident == &__FILE___ident) {
+		replace_with_string(token, stream_name(token->pos.stream));
+	} else if (token->ident == &__DATE___ident) {
+		if (!t)
+			time(&t);
+		strftime(buffer, 12, "%b %e %Y", localtime(&t));
+		replace_with_string(token, buffer);
+	} else if (token->ident == &__TIME___ident) {
+		if (!t)
+			time(&t);
+		strftime(buffer, 9, "%T", localtime(&t));
+		replace_with_string(token, buffer);
+	}
+	return 1;
+}
+
+static inline struct token *scan_next(struct token **where)
+{
+	struct token *token = *where;
+	if (token_type(token) != TOKEN_UNTAINT)
+		return token;
+	do {
+		token->ident->tainted = 0;
+		token = token->next;
+	} while (token_type(token) == TOKEN_UNTAINT);
+	*where = token;
+	return token;
+}
+
+static void expand_list(struct token **list)
+{
+	struct token *next;
+	while (!eof_token(next = scan_next(list))) {
+		if (token_type(next) != TOKEN_IDENT || expand_one_symbol(list))
+			list = &next->next;
+	}
+}
+
+static void preprocessor_line(struct stream *stream, struct token **line);
+
+static struct token *collect_arg(struct token *prev, int vararg, struct position *pos)
+{
+	struct stream *stream = input_streams + prev->pos.stream;
+	struct token **p = &prev->next;
+	struct token *next;
+	int nesting = 0;
+
+	while (!eof_token(next = scan_next(p))) {
+		if (next->pos.newline && match_op(next, '#')) {
+			if (!next->pos.noexpand) {
+				sparse_error(next->pos,
+					     "directive in argument list");
+				preprocessor_line(stream, p);
+				__free_token(next);	/* Free the '#' token */
+				continue;
+			}
+		}
+		switch (token_type(next)) {
+		case TOKEN_STREAMEND:
+		case TOKEN_STREAMBEGIN:
+			*p = &eof_token_entry;
+			return next;
+		}
+		if (false_nesting) {
+			*p = next->next;
+			__free_token(next);
+			continue;
+		}
+		if (match_op(next, '(')) {
+			nesting++;
+		} else if (match_op(next, ')')) {
+			if (!nesting--)
+				break;
+		} else if (match_op(next, ',') && !nesting && !vararg) {
+			break;
+		}
+		next->pos.stream = pos->stream;
+		next->pos.line = pos->line;
+		next->pos.pos = pos->pos;
+		p = &next->next;
+	}
+	*p = &eof_token_entry;
+	return next;
+}
+
+/*
+ * We store arglist as <counter> [arg1] <number of uses for arg1> ... eof
+ */
+
+struct arg {
+	struct token *arg;
+	struct token *expanded;
+	struct token *str;
+	int n_normal;
+	int n_quoted;
+	int n_str;
+};
+
+static int collect_arguments(struct token *start, struct token *arglist, struct arg *args, struct token *what)
+{
+	int wanted = arglist->count.normal;
+	struct token *next = NULL;
+	int count = 0;
+
+	arglist = arglist->next;	/* skip counter */
+
+	if (!wanted) {
+		next = collect_arg(start, 0, &what->pos);
+		if (eof_token(next))
+			goto Eclosing;
+		if (!eof_token(start->next) || !match_op(next, ')')) {
+			count++;
+			goto Emany;
+		}
+	} else {
+		for (count = 0; count < wanted; count++) {
+			struct argcount *p = &arglist->next->count;
+			next = collect_arg(start, p->vararg, &what->pos);
+			arglist = arglist->next->next;
+			if (eof_token(next))
+				goto Eclosing;
+			args[count].arg = start->next;
+			args[count].n_normal = p->normal;
+			args[count].n_quoted = p->quoted;
+			args[count].n_str = p->str;
+			if (match_op(next, ')')) {
+				count++;
+				break;
+			}
+			start = next;
+		}
+		if (count == wanted && !match_op(next, ')'))
+			goto Emany;
+		if (count == wanted - 1) {
+			struct argcount *p = &arglist->next->count;
+			if (!p->vararg)
+				goto Efew;
+			args[count].arg = NULL;
+			args[count].n_normal = p->normal;
+			args[count].n_quoted = p->quoted;
+			args[count].n_str = p->str;
+		}
+		if (count < wanted - 1)
+			goto Efew;
+	}
+	what->next = next->next;
+	return 1;
+
+Efew:
+	sparse_error(what->pos, "macro \"%s\" requires %d arguments, but only %d given",
+		show_token(what), wanted, count);
+	goto out;
+Emany:
+	while (match_op(next, ',')) {
+		next = collect_arg(next, 0, &what->pos);
+		count++;
+	}
+	if (eof_token(next))
+		goto Eclosing;
+	sparse_error(what->pos, "macro \"%s\" passed %d arguments, but takes just %d",
+		show_token(what), count, wanted);
+	goto out;
+Eclosing:
+	sparse_error(what->pos, "unterminated argument list invoking macro \"%s\"",
+		show_token(what));
+out:
+	what->next = next->next;
+	return 0;
+}
+
+static struct token *dup_list(struct token *list)
+{
+	struct token *res = NULL;
+	struct token **p = &res;
+
+	while (!eof_token(list)) {
+		struct token *newtok = __alloc_token(0);
+		*newtok = *list;
+		*p = newtok;
+		p = &newtok->next;
+		list = list->next;
+	}
+	return res;
+}
+
+static struct token *stringify(struct token *arg)
+{
+	const char *s = show_token_sequence(arg);
+	int size = strlen(s)+1;
+	struct token *token = __alloc_token(0);
+	struct string *string = __alloc_string(size);
+
+	memcpy(string->data, s, size);
+	string->length = size;
+	token->pos = arg->pos;
+	token_type(token) = TOKEN_STRING;
+	token->string = string;
+	token->next = &eof_token_entry;
+	return token;
+}
+
+static void expand_arguments(int count, struct arg *args)
+{
+	int i;
+	for (i = 0; i < count; i++) {
+		struct token *arg = args[i].arg;
+		if (!arg)
+			arg = &eof_token_entry;
+		if (args[i].n_str)
+			args[i].str = stringify(arg);
+		if (args[i].n_normal) {
+			if (!args[i].n_quoted) {
+				args[i].expanded = arg;
+				args[i].arg = NULL;
+			} else if (eof_token(arg)) {
+				args[i].expanded = arg;
+			} else {
+				args[i].expanded = dup_list(arg);
+			}
+			expand_list(&args[i].expanded);
+		}
+	}
+}
+
+/*
+ * Possibly valid combinations:
+ *  - ident + ident -> ident
+ *  - ident + number -> ident unless number contains '.', '+' or '-'.
+ *  - number + number -> number
+ *  - number + ident -> number
+ *  - number + '.' -> number
+ *  - number + '+' or '-' -> number, if number used to end on [eEpP].
+ *  - '.' + number -> number, if number used to start with a digit.
+ *  - special + special -> either special or an error.
+ */
+static enum token_type combine(struct token *left, struct token *right, char *p)
+{
+	int len;
+	enum token_type t1 = token_type(left), t2 = token_type(right);
+
+	if (t1 != TOKEN_IDENT && t1 != TOKEN_NUMBER && t1 != TOKEN_SPECIAL)
+		return TOKEN_ERROR;
+
+	if (t2 != TOKEN_IDENT && t2 != TOKEN_NUMBER && t2 != TOKEN_SPECIAL)
+		return TOKEN_ERROR;
+
+	strcpy(p, show_token(left));
+	strcat(p, show_token(right));
+	len = strlen(p);
+
+	if (len >= 256)
+		return TOKEN_ERROR;
+
+	if (t1 == TOKEN_IDENT) {
+		if (t2 == TOKEN_SPECIAL)
+			return TOKEN_ERROR;
+		if (t2 == TOKEN_NUMBER && strpbrk(p, "+-."))
+			return TOKEN_ERROR;
+		return TOKEN_IDENT;
+	}
+
+	if (t1 == TOKEN_NUMBER) {
+		if (t2 == TOKEN_SPECIAL) {
+			switch (right->special) {
+			case '.':
+				break;
+			case '+': case '-':
+				if (strchr("eEpP", p[len - 2]))
+					break;
+			default:
+				return TOKEN_ERROR;
+			}
+		}
+		return TOKEN_NUMBER;
+	}
+
+	if (p[0] == '.' && isdigit((unsigned char)p[1]))
+		return TOKEN_NUMBER;
+
+	return TOKEN_SPECIAL;
+}
+
+static int merge(struct token *left, struct token *right)
+{
+	static char buffer[512];
+	int n;
+
+	switch (combine(left, right, buffer)) {
+	case TOKEN_IDENT:
+		left->ident = built_in_ident(buffer);
+		left->pos.noexpand = 0;
+		return 1;
+
+	case TOKEN_NUMBER: {
+		char *number = __alloc_bytes(strlen(buffer) + 1);
+		memcpy(number, buffer, strlen(buffer) + 1);
+		token_type(left) = TOKEN_NUMBER;	/* could be . + num */
+		left->number = number;
+		return 1;
+	}
+
+	case TOKEN_SPECIAL:
+		if (buffer[2] && buffer[3])
+			break;
+		for (n = SPECIAL_BASE; n < SPECIAL_ARG_SEPARATOR; n++) {
+			if (!memcmp(buffer, combinations[n-SPECIAL_BASE], 3)) {
+				left->special = n;
+				return 1;
+			}
+		}
+	default:
+		;
+	}
+	sparse_error(left->pos, "'##' failed: concatenation is not a valid token");
+	return 0;
+}
+
+static struct token *dup_token(struct token *token, struct position *streampos, struct position *pos)
+{
+	struct token *alloc = alloc_token(streampos);
+	token_type(alloc) = token_type(token);
+	alloc->pos.newline = pos->newline;
+	alloc->pos.whitespace = pos->whitespace;
+	alloc->number = token->number;
+	alloc->pos.noexpand = token->pos.noexpand;
+	return alloc;	
+}
+
+static struct token **copy(struct token **where, struct token *list, int *count)
+{
+	int need_copy = --*count;
+	while (!eof_token(list)) {
+		struct token *token;
+		if (need_copy)
+			token = dup_token(list, &list->pos, &list->pos);
+		else
+			token = list;
+		if (token_type(token) == TOKEN_IDENT && token->ident->tainted)
+			token->pos.noexpand = 1;
+		*where = token;
+		where = &token->next;
+		list = list->next;
+	}
+	*where = &eof_token_entry;
+	return where;
+}
+
+static struct token **substitute(struct token **list, struct token *body, struct arg *args)
+{
+	struct token *token = *list;
+	struct position *base_pos = &token->pos;
+	struct position *pos = base_pos;
+	int *count;
+	enum {Normal, Placeholder, Concat} state = Normal;
+
+	for (; !eof_token(body); body = body->next, pos = &body->pos) {
+		struct token *added, *arg;
+		struct token **tail;
+
+		switch (token_type(body)) {
+		case TOKEN_GNU_KLUDGE:
+			/*
+			 * GNU kludge: if we had <comma>##<vararg>, behaviour
+			 * depends on whether we had enough arguments to have
+			 * a vararg.  If we did, ## is just ignored.  Otherwise
+			 * both , and ## are ignored.  Comma should come from
+			 * the body of macro and not be an argument of earlier
+			 * concatenation.
+			 */
+			if (!args[body->next->argnum].arg)
+				continue;
+			added = dup_token(body, base_pos, pos);
+			token_type(added) = TOKEN_SPECIAL;
+			tail = &added->next;
+			break;
+
+		case TOKEN_STR_ARGUMENT:
+			arg = args[body->argnum].str;
+			count = &args[body->argnum].n_str;
+			goto copy_arg;
+
+		case TOKEN_QUOTED_ARGUMENT:
+			arg = args[body->argnum].arg;
+			count = &args[body->argnum].n_quoted;
+			if (!arg || eof_token(arg)) {
+				if (state == Concat)
+					state = Normal;
+				else
+					state = Placeholder;
+				continue;
+			}
+			goto copy_arg;
+
+		case TOKEN_MACRO_ARGUMENT:
+			arg = args[body->argnum].expanded;
+			count = &args[body->argnum].n_normal;
+			if (eof_token(arg)) {
+				state = Normal;
+				continue;
+			}
+		copy_arg:
+			tail = copy(&added, arg, count);
+			added->pos.newline = pos->newline;
+			added->pos.whitespace = pos->whitespace;
+			break;
+
+		case TOKEN_CONCAT:
+			if (state == Placeholder)
+				state = Normal;
+			else
+				state = Concat;
+			continue;
+
+		case TOKEN_IDENT:
+			added = dup_token(body, base_pos, pos);
+			if (added->ident->tainted)
+				added->pos.noexpand = 1;
+			tail = &added->next;
+			break;
+
+		default:
+			added = dup_token(body, base_pos, pos);
+			tail = &added->next;
+			break;
+		}
+
+		/*
+		 * if we got to doing real concatenation, we already have
+		 * added something into the list, so containing_token() is OK.
+		 */
+		if (state == Concat && merge(containing_token(list), added)) {
+			*list = added->next;
+			if (tail != &added->next)
+				list = tail;
+		} else {
+			*list = added;
+			list = tail;
+		}
+		state = Normal;
+	}
+	*list = &eof_token_entry;
+	return list;
+}
+
+static int expand(struct token **list, struct symbol *sym)
+{
+	struct token *last;
+	struct token *token = *list;
+	struct ident *expanding = token->ident;
+	struct token **tail;
+	int nargs = sym->arglist ? sym->arglist->count.normal : 0;
+	struct arg args[nargs];
+
+	if (expanding->tainted) {
+		token->pos.noexpand = 1;
+		return 1;
+	}
+
+	if (sym->arglist) {
+		if (!match_op(scan_next(&token->next), '('))
+			return 1;
+		if (!collect_arguments(token->next, sym->arglist, args, token))
+			return 1;
+		expand_arguments(nargs, args);
+	}
+
+	expanding->tainted = 1;
+
+	last = token->next;
+	tail = substitute(list, sym->expansion, args);
+	*tail = last;
+
+	return 0;
+}
+
+static const char *token_name_sequence(struct token *token, int endop, struct token *start)
+{
+	static char buffer[256];
+	char *ptr = buffer;
+
+	while (!eof_token(token) && !match_op(token, endop)) {
+		int len;
+		const char *val = token->string->data;
+		if (token_type(token) != TOKEN_STRING)
+			val = show_token(token);
+		len = strlen(val);
+		memcpy(ptr, val, len);
+		ptr += len;
+		token = token->next;
+	}
+	*ptr = 0;
+	if (endop && !match_op(token, endop))
+		sparse_error(start->pos, "expected '>' at end of filename");
+	return buffer;
+}
+
+static int already_tokenized(const char *path)
+{
+	int stream, next;
+
+	for (stream = *hash_stream(path); stream >= 0 ; stream = next) {
+		struct stream *s = input_streams + stream;
+
+		next = s->next_stream;
+		if (s->constant != CONSTANT_FILE_YES)
+			continue;
+		if (strcmp(path, s->name))
+			continue;
+		if (s->protect && !lookup_macro(s->protect))
+			continue;
+		return 1;
+	}
+	return 0;
+}
+
+/* Handle include of header files.
+ * The relevant options are made compatible with gcc. The only options that
+ * are not supported is -withprefix and friends.
+ *
+ * Three set of include paths are known:
+ * quote_includepath:	Path to search when using #include "file.h"
+ * angle_includepath:	Paths to search when using #include <file.h>
+ * isys_includepath:	Paths specified with -isystem, come before the
+ *			built-in system include paths. Gcc would suppress
+ *			warnings from system headers. Here we separate
+ *			them from the angle_ ones to keep search ordering.
+ *
+ * sys_includepath:	Built-in include paths.
+ * dirafter_includepath Paths added with -dirafter.
+ *
+ * The above is implemented as one array with pointers
+ *                         +--------------+
+ * quote_includepath --->  |              |
+ *                         +--------------+
+ *                         |              |
+ *                         +--------------+
+ * angle_includepath --->  |              |
+ *                         +--------------+
+ * isys_includepath  --->  |              |
+ *                         +--------------+
+ * sys_includepath   --->  |              |
+ *                         +--------------+
+ * dirafter_includepath -> |              |
+ *                         +--------------+
+ *
+ * -I dir insert dir just before isys_includepath and move the rest
+ * -I- makes all dirs specified with -I before to quote dirs only and
+ *   angle_includepath is set equal to isys_includepath.
+ * -nostdinc removes all sys dirs by storing NULL in entry pointed
+ *   to by * sys_includepath. Note that this will reset all dirs built-in
+ *   and added before -nostdinc by -isystem and -idirafter.
+ * -isystem dir adds dir where isys_includepath points adding this dir as
+ *   first systemdir
+ * -idirafter dir adds dir to the end of the list
+ */
+
+static void set_stream_include_path(struct stream *stream)
+{
+	const char *path = stream->path;
+	if (!path) {
+		const char *p = strrchr(stream->name, '/');
+		path = "";
+		if (p) {
+			int len = p - stream->name + 1;
+			char *m = malloc(len+1);
+			/* This includes the final "/" */
+			memcpy(m, stream->name, len);
+			m[len] = 0;
+			path = m;
+		}
+		stream->path = path;
+	}
+	includepath[0] = path;
+}
+
+static int try_include(const char *path, const char *filename, int flen, struct token **where, const char **next_path)
+{
+	int fd;
+	int plen = strlen(path);
+	static char fullname[PATH_MAX];
+
+	memcpy(fullname, path, plen);
+	if (plen && path[plen-1] != '/') {
+		fullname[plen] = '/';
+		plen++;
+	}
+	memcpy(fullname+plen, filename, flen);
+	if (already_tokenized(fullname))
+		return 1;
+	fd = open(fullname, O_RDONLY);
+	if (fd >= 0) {
+		char * streamname = __alloc_bytes(plen + flen);
+		memcpy(streamname, fullname, plen + flen);
+		*where = tokenize(streamname, fd, *where, next_path);
+		close(fd);
+		return 1;
+	}
+	return 0;
+}
+
+static int do_include_path(const char **pptr, struct token **list, struct token *token, const char *filename, int flen)
+{
+	const char *path;
+
+	while ((path = *pptr++) != NULL) {
+		if (!try_include(path, filename, flen, list, pptr))
+			continue;
+		return 1;
+	}
+	return 0;
+}
+
+static void do_include(int local, struct stream *stream, struct token **list, struct token *token, const char *filename, const char **path)
+{
+	int flen = strlen(filename) + 1;
+
+	/* Absolute path? */
+	if (filename[0] == '/') {
+		if (try_include("", filename, flen, list, includepath))
+			return;
+		goto out;
+	}
+
+	/* Dir of input file is first dir to search for quoted includes */
+	set_stream_include_path(stream);
+
+	if (!path)
+		/* Do not search quote include if <> is in use */
+		path = local ? quote_includepath : angle_includepath;
+
+	/* Check the standard include paths.. */
+	if (do_include_path(path, list, token, filename, flen))
+		return;
+out:
+	error_die(token->pos, "unable to open '%s'", filename);
+}
+
+static int free_preprocessor_line(struct token *token)
+{
+	while (token_type(token) != TOKEN_EOF) {
+		struct token *free = token;
+		token = token->next;
+		__free_token(free);
+	};
+	return 1;
+}
+
+static int handle_include_path(struct stream *stream, struct token **list, struct token *token, const char **path)
+{
+	const char *filename;
+	struct token *next;
+	int expect;
+
+	next = token->next;
+	expect = '>';
+	if (!match_op(next, '<')) {
+		expand_list(&token->next);
+		expect = 0;
+		next = token;
+		if (match_op(token->next, '<')) {
+			next = token->next;
+			expect = '>';
+		}
+	}
+	token = next->next;
+	filename = token_name_sequence(token, expect, token);
+	do_include(!expect, stream, list, token, filename, path);
+	return 0;
+}
+
+static int handle_include(struct stream *stream, struct token **list, struct token *token)
+{
+	return handle_include_path(stream, list, token, NULL);
+}
+
+static int handle_include_next(struct stream *stream, struct token **list, struct token *token)
+{
+	return handle_include_path(stream, list, token, stream->next_path);
+}
+
+static int token_different(struct token *t1, struct token *t2)
+{
+	int different;
+
+	if (token_type(t1) != token_type(t2))
+		return 1;
+
+	switch (token_type(t1)) {
+	case TOKEN_IDENT:
+		different = t1->ident != t2->ident;
+		break;
+	case TOKEN_ARG_COUNT:
+	case TOKEN_UNTAINT:
+	case TOKEN_CONCAT:
+	case TOKEN_GNU_KLUDGE:
+		different = 0;
+		break;
+	case TOKEN_NUMBER:
+		different = strcmp(t1->number, t2->number);
+		break;
+	case TOKEN_SPECIAL:
+		different = t1->special != t2->special;
+		break;
+	case TOKEN_MACRO_ARGUMENT:
+	case TOKEN_QUOTED_ARGUMENT:
+	case TOKEN_STR_ARGUMENT:
+		different = t1->argnum != t2->argnum;
+		break;
+	case TOKEN_CHAR:
+	case TOKEN_WIDE_CHAR:
+		different = t1->character != t2->character;
+		break;
+	case TOKEN_STRING:
+	case TOKEN_WIDE_STRING: {
+		struct string *s1, *s2;
+
+		s1 = t1->string;
+		s2 = t2->string;
+		different = 1;
+		if (s1->length != s2->length)
+			break;
+		different = memcmp(s1->data, s2->data, s1->length);
+		break;
+	}
+	default:
+		different = 1;
+		break;
+	}
+	return different;
+}
+
+static int token_list_different(struct token *list1, struct token *list2)
+{
+	for (;;) {
+		if (list1 == list2)
+			return 0;
+		if (!list1 || !list2)
+			return 1;
+		if (token_different(list1, list2))
+			return 1;
+		list1 = list1->next;
+		list2 = list2->next;
+	}
+}
+
+static inline void set_arg_count(struct token *token)
+{
+	token_type(token) = TOKEN_ARG_COUNT;
+	token->count.normal = token->count.quoted =
+	token->count.str = token->count.vararg = 0;
+}
+
+static struct token *parse_arguments(struct token *list)
+{
+	struct token *arg = list->next, *next = list;
+	struct argcount *count = &list->count;
+
+	set_arg_count(list);
+
+	if (match_op(arg, ')')) {
+		next = arg->next;
+		list->next = &eof_token_entry;
+		return next;
+	}
+
+	while (token_type(arg) == TOKEN_IDENT) {
+		if (arg->ident == &__VA_ARGS___ident)
+			goto Eva_args;
+		if (!++count->normal)
+			goto Eargs;
+		next = arg->next;
+
+		if (match_op(next, ',')) {
+			set_arg_count(next);
+			arg = next->next;
+			continue;
+		}
+
+		if (match_op(next, ')')) {
+			set_arg_count(next);
+			next = next->next;
+			arg->next->next = &eof_token_entry;
+			return next;
+		}
+
+		/* normal cases are finished here */
+
+		if (match_op(next, SPECIAL_ELLIPSIS)) {
+			if (match_op(next->next, ')')) {
+				set_arg_count(next);
+				next->count.vararg = 1;
+				next = next->next;
+				arg->next->next = &eof_token_entry;
+				return next->next;
+			}
+
+			arg = next;
+			goto Enotclosed;
+		}
+
+		if (eof_token(next)) {
+			goto Enotclosed;
+		} else {
+			arg = next;
+			goto Ebadstuff;
+		}
+	}
+
+	if (match_op(arg, SPECIAL_ELLIPSIS)) {
+		next = arg->next;
+		token_type(arg) = TOKEN_IDENT;
+		arg->ident = &__VA_ARGS___ident;
+		if (!match_op(next, ')'))
+			goto Enotclosed;
+		if (!++count->normal)
+			goto Eargs;
+		set_arg_count(next);
+		next->count.vararg = 1;
+		next = next->next;
+		arg->next->next = &eof_token_entry;
+		return next;
+	}
+
+	if (eof_token(arg)) {
+		arg = next;
+		goto Enotclosed;
+	}
+	if (match_op(arg, ','))
+		goto Emissing;
+	else
+		goto Ebadstuff;
+
+
+Emissing:
+	sparse_error(arg->pos, "parameter name missing");
+	return NULL;
+Ebadstuff:
+	sparse_error(arg->pos, "\"%s\" may not appear in macro parameter list",
+		show_token(arg));
+	return NULL;
+Enotclosed:
+	sparse_error(arg->pos, "missing ')' in macro parameter list");
+	return NULL;
+Eva_args:
+	sparse_error(arg->pos, "__VA_ARGS__ can only appear in the expansion of a C99 variadic macro");
+	return NULL;
+Eargs:
+	sparse_error(arg->pos, "too many arguments in macro definition");
+	return NULL;
+}
+
+static int try_arg(struct token *token, enum token_type type, struct token *arglist)
+{
+	struct ident *ident = token->ident;
+	int nr;
+
+	if (!arglist || token_type(token) != TOKEN_IDENT)
+		return 0;
+
+	arglist = arglist->next;
+
+	for (nr = 0; !eof_token(arglist); nr++, arglist = arglist->next->next) {
+		if (arglist->ident == ident) {
+			struct argcount *count = &arglist->next->count;
+			int n;
+
+			token->argnum = nr;
+			token_type(token) = type;
+			switch (type) {
+			case TOKEN_MACRO_ARGUMENT:
+				n = ++count->normal;
+				break;
+			case TOKEN_QUOTED_ARGUMENT:
+				n = ++count->quoted;
+				break;
+			default:
+				n = ++count->str;
+			}
+			if (n)
+				return count->vararg ? 2 : 1;
+			token_type(token) = TOKEN_ERROR;
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static struct token *parse_expansion(struct token *expansion, struct token *arglist, struct ident *name)
+{
+	struct token *token = expansion;
+	struct token **p;
+	struct token *last = NULL;
+
+	if (match_op(token, SPECIAL_HASHHASH))
+		goto Econcat;
+
+	for (p = &expansion; !eof_token(token); p = &token->next, token = *p) {
+		if (match_op(token, '#')) {
+			if (arglist) {
+				struct token *next = token->next;
+				if (!try_arg(next, TOKEN_STR_ARGUMENT, arglist))
+					goto Equote;
+				next->pos.whitespace = token->pos.whitespace;
+				token = *p = next;
+			} else {
+				token->pos.noexpand = 1;
+			}
+		} else if (match_op(token, SPECIAL_HASHHASH)) {
+			struct token *next = token->next;
+			int arg = try_arg(next, TOKEN_QUOTED_ARGUMENT, arglist);
+			token_type(token) = TOKEN_CONCAT;
+			if (arg) {
+				token = next;
+				/* GNU kludge */
+				if (arg == 2 && last && match_op(last, ',')) {
+					token_type(last) = TOKEN_GNU_KLUDGE;
+					last->next = token;
+				}
+			} else if (match_op(next, SPECIAL_HASHHASH))
+				token = next;
+			else if (eof_token(next))
+				goto Econcat;
+		} else if (match_op(token->next, SPECIAL_HASHHASH)) {
+			try_arg(token, TOKEN_QUOTED_ARGUMENT, arglist);
+		} else {
+			try_arg(token, TOKEN_MACRO_ARGUMENT, arglist);
+		}
+		if (token_type(token) == TOKEN_ERROR)
+			goto Earg;
+		last = token;
+	}
+	token = alloc_token(&expansion->pos);
+	token_type(token) = TOKEN_UNTAINT;
+	token->ident = name;
+	token->next = *p;
+	*p = token;
+	return expansion;
+
+Equote:
+	sparse_error(token->pos, "'#' is not followed by a macro parameter");
+	return NULL;
+
+Econcat:
+	sparse_error(token->pos, "'##' cannot appear at the ends of macro expansion");
+	return NULL;
+Earg:
+	sparse_error(token->pos, "too many instances of argument in body");
+	return NULL;
+}
+
+static int do_handle_define(struct stream *stream, struct token **line, struct token *token, int attr)
+{
+	struct token *arglist, *expansion;
+	struct token *left = token->next;
+	struct symbol *sym;
+	struct ident *name;
+	int ret;
+
+	if (token_type(left) != TOKEN_IDENT) {
+		sparse_error(token->pos, "expected identifier to 'define'");
+		return 1;
+	}
+
+	name = left->ident;
+
+	arglist = NULL;
+	expansion = left->next;
+	if (!expansion->pos.whitespace) {
+		if (match_op(expansion, '(')) {
+			arglist = expansion;
+			expansion = parse_arguments(expansion);
+			if (!expansion)
+				return 1;
+		} else if (!eof_token(expansion)) {
+			warning(expansion->pos,
+				"no whitespace before object-like macro body");
+		}
+	}
+
+	expansion = parse_expansion(expansion, arglist, name);
+	if (!expansion)
+		return 1;
+
+	ret = 1;
+	sym = lookup_symbol(name, NS_MACRO | NS_UNDEF);
+	if (sym) {
+		int clean;
+
+		if (attr < sym->attr)
+			goto out;
+
+		clean = (attr == sym->attr && sym->namespace == NS_MACRO);
+
+		if (token_list_different(sym->expansion, expansion) ||
+		    token_list_different(sym->arglist, arglist)) {
+			ret = 0;
+			if ((clean && attr == SYM_ATTR_NORMAL)
+					|| sym->used_in == file_scope) {
+				warning(left->pos, "preprocessor token %.*s redefined",
+						name->len, name->name);
+				info(sym->pos, "this was the original definition");
+			}
+		} else if (clean)
+			goto out;
+	}
+
+	if (!sym || sym->scope != file_scope) {
+		sym = alloc_symbol(left->pos, SYM_NODE);
+		bind_symbol(sym, name, NS_MACRO);
+		ret = 0;
+	}
+
+	if (!ret) {
+		sym->expansion = expansion;
+		sym->arglist = arglist;
+		__free_token(token);	/* Free the "define" token, but not the rest of the line */
+	}
+
+	sym->namespace = NS_MACRO;
+	sym->used_in = NULL;
+	sym->attr = attr;
+out:
+	return ret;
+}
+
+static int handle_define(struct stream *stream, struct token **line, struct token *token)
+{
+	return do_handle_define(stream, line, token, SYM_ATTR_NORMAL);
+}
+
+static int handle_weak_define(struct stream *stream, struct token **line, struct token *token)
+{
+	return do_handle_define(stream, line, token, SYM_ATTR_WEAK);
+}
+
+static int handle_strong_define(struct stream *stream, struct token **line, struct token *token)
+{
+	return do_handle_define(stream, line, token, SYM_ATTR_STRONG);
+}
+
+static int do_handle_undef(struct stream *stream, struct token **line, struct token *token, int attr)
+{
+	struct token *left = token->next;
+	struct symbol *sym;
+
+	if (token_type(left) != TOKEN_IDENT) {
+		sparse_error(token->pos, "expected identifier to 'undef'");
+		return 1;
+	}
+
+	sym = lookup_symbol(left->ident, NS_MACRO | NS_UNDEF);
+	if (sym) {
+		if (attr < sym->attr)
+			return 1;
+		if (attr == sym->attr && sym->namespace == NS_UNDEF)
+			return 1;
+	} else if (attr <= SYM_ATTR_NORMAL)
+		return 1;
+
+	if (!sym || sym->scope != file_scope) {
+		sym = alloc_symbol(left->pos, SYM_NODE);
+		bind_symbol(sym, left->ident, NS_MACRO);
+	}
+
+	sym->namespace = NS_UNDEF;
+	sym->used_in = NULL;
+	sym->attr = attr;
+
+	return 1;
+}
+
+static int handle_undef(struct stream *stream, struct token **line, struct token *token)
+{
+	return do_handle_undef(stream, line, token, SYM_ATTR_NORMAL);
+}
+
+static int handle_strong_undef(struct stream *stream, struct token **line, struct token *token)
+{
+	return do_handle_undef(stream, line, token, SYM_ATTR_STRONG);
+}
+
+static int preprocessor_if(struct stream *stream, struct token *token, int true)
+{
+	token_type(token) = false_nesting ? TOKEN_SKIP_GROUPS : TOKEN_IF;
+	free_preprocessor_line(token->next);
+	token->next = stream->top_if;
+	stream->top_if = token;
+	if (false_nesting || true != 1)
+		false_nesting++;
+	return 0;
+}
+
+static int handle_ifdef(struct stream *stream, struct token **line, struct token *token)
+{
+	struct token *next = token->next;
+	int arg;
+	if (token_type(next) == TOKEN_IDENT) {
+		arg = token_defined(next);
+	} else {
+		dirty_stream(stream);
+		if (!false_nesting)
+			sparse_error(token->pos, "expected preprocessor identifier");
+		arg = -1;
+	}
+	return preprocessor_if(stream, token, arg);
+}
+
+static int handle_ifndef(struct stream *stream, struct token **line, struct token *token)
+{
+	struct token *next = token->next;
+	int arg;
+	if (token_type(next) == TOKEN_IDENT) {
+		if (!stream->dirty && !stream->ifndef) {
+			if (!stream->protect) {
+				stream->ifndef = token;
+				stream->protect = next->ident;
+			} else if (stream->protect == next->ident) {
+				stream->ifndef = token;
+				stream->dirty = 1;
+			}
+		}
+		arg = !token_defined(next);
+	} else {
+		dirty_stream(stream);
+		if (!false_nesting)
+			sparse_error(token->pos, "expected preprocessor identifier");
+		arg = -1;
+	}
+
+	return preprocessor_if(stream, token, arg);
+}
+
+/*
+ * Expression handling for #if and #elif; it differs from normal expansion
+ * due to special treatment of "defined".
+ */
+static int expression_value(struct token **where)
+{
+	struct expression *expr;
+	struct token *p;
+	struct token **list = where, **beginning = NULL;
+	long long value;
+	int state = 0;
+
+	while (!eof_token(p = scan_next(list))) {
+		switch (state) {
+		case 0:
+			if (token_type(p) != TOKEN_IDENT)
+				break;
+			if (p->ident == &defined_ident) {
+				state = 1;
+				beginning = list;
+				break;
+			}
+			if (!expand_one_symbol(list))
+				continue;
+			if (token_type(p) != TOKEN_IDENT)
+				break;
+			token_type(p) = TOKEN_ZERO_IDENT;
+			break;
+		case 1:
+			if (match_op(p, '(')) {
+				state = 2;
+			} else {
+				state = 0;
+				replace_with_defined(p);
+				*beginning = p;
+			}
+			break;
+		case 2:
+			if (token_type(p) == TOKEN_IDENT)
+				state = 3;
+			else
+				state = 0;
+			replace_with_defined(p);
+			*beginning = p;
+			break;
+		case 3:
+			state = 0;
+			if (!match_op(p, ')'))
+				sparse_error(p->pos, "missing ')' after \"defined\"");
+			*list = p->next;
+			continue;
+		}
+		list = &p->next;
+	}
+
+	p = constant_expression(*where, &expr);
+	if (!eof_token(p))
+		sparse_error(p->pos, "garbage at end: %s", show_token_sequence(p));
+	value = get_expression_value(expr);
+	return value != 0;
+}
+
+static int handle_if(struct stream *stream, struct token **line, struct token *token)
+{
+	int value = 0;
+	if (!false_nesting)
+		value = expression_value(&token->next);
+
+	dirty_stream(stream);
+	return preprocessor_if(stream, token, value);
+}
+
+static int handle_elif(struct stream * stream, struct token **line, struct token *token)
+{
+	struct token *top_if = stream->top_if;
+	end_group(stream);
+
+	if (!top_if) {
+		nesting_error(stream);
+		sparse_error(token->pos, "unmatched #elif within stream");
+		return 1;
+	}
+
+	if (token_type(top_if) == TOKEN_ELSE) {
+		nesting_error(stream);
+		sparse_error(token->pos, "#elif after #else");
+		if (!false_nesting)
+			false_nesting = 1;
+		return 1;
+	}
+
+	dirty_stream(stream);
+	if (token_type(top_if) != TOKEN_IF)
+		return 1;
+	if (false_nesting) {
+		false_nesting = 0;
+		if (!expression_value(&token->next))
+			false_nesting = 1;
+	} else {
+		false_nesting = 1;
+		token_type(top_if) = TOKEN_SKIP_GROUPS;
+	}
+	return 1;
+}
+
+static int handle_else(struct stream *stream, struct token **line, struct token *token)
+{
+	struct token *top_if = stream->top_if;
+	end_group(stream);
+
+	if (!top_if) {
+		nesting_error(stream);
+		sparse_error(token->pos, "unmatched #else within stream");
+		return 1;
+	}
+
+	if (token_type(top_if) == TOKEN_ELSE) {
+		nesting_error(stream);
+		sparse_error(token->pos, "#else after #else");
+	}
+	if (false_nesting) {
+		if (token_type(top_if) == TOKEN_IF)
+			false_nesting = 0;
+	} else {
+		false_nesting = 1;
+	}
+	token_type(top_if) = TOKEN_ELSE;
+	return 1;
+}
+
+static int handle_endif(struct stream *stream, struct token **line, struct token *token)
+{
+	struct token *top_if = stream->top_if;
+	end_group(stream);
+	if (!top_if) {
+		nesting_error(stream);
+		sparse_error(token->pos, "unmatched #endif in stream");
+		return 1;
+	}
+	if (false_nesting)
+		false_nesting--;
+	stream->top_if = top_if->next;
+	__free_token(top_if);
+	return 1;
+}
+
+static const char *show_token_sequence(struct token *token)
+{
+	static char buffer[1024];
+	char *ptr = buffer;
+	int whitespace = 0;
+
+	if (!token)
+		return "<none>";
+	while (!eof_token(token)) {
+		const char *val = show_token(token);
+		int len = strlen(val);
+
+		if (ptr + whitespace + len >= buffer + sizeof(buffer)) {
+			sparse_error(token->pos, "too long token expansion");
+			break;
+		}
+
+		if (whitespace)
+			*ptr++ = ' ';
+		memcpy(ptr, val, len);
+		ptr += len;
+		token = token->next;
+		whitespace = token->pos.whitespace;
+	}
+	*ptr = 0;
+	return buffer;
+}
+
+static int handle_warning(struct stream *stream, struct token **line, struct token *token)
+{
+	warning(token->pos, "%s", show_token_sequence(token->next));
+	return 1;
+}
+
+static int handle_error(struct stream *stream, struct token **line, struct token *token)
+{
+	sparse_error(token->pos, "%s", show_token_sequence(token->next));
+	return 1;
+}
+
+static int handle_nostdinc(struct stream *stream, struct token **line, struct token *token)
+{
+	/*
+	 * Do we have any non-system includes?
+	 * Clear them out if so..
+	 */
+	*sys_includepath = NULL;
+	return 1;
+}
+
+static inline void update_inc_ptrs(const char ***where)
+{
+
+	if (*where <= dirafter_includepath) {
+		dirafter_includepath++;
+		/* If this was the entry that we prepend, don't
+		 * rise the lower entries, even if they are at
+		 * the same level. */
+		if (where == &dirafter_includepath)
+			return;
+	}
+	if (*where <= sys_includepath) {
+		sys_includepath++;
+		if (where == &sys_includepath)
+			return;
+	}
+	if (*where <= isys_includepath) {
+		isys_includepath++;
+		if (where == &isys_includepath)
+			return;
+	}
+
+	/* angle_includepath is actually never updated, since we
+	 * don't suppport -iquote rught now. May change some day. */
+	if (*where <= angle_includepath) {
+		angle_includepath++;
+		if (where == &angle_includepath)
+			return;
+	}
+}
+
+/* Add a path before 'where' and update the pointers associated with the
+ * includepath array */
+static void add_path_entry(struct token *token, const char *path,
+	const char ***where)
+{
+	const char **dst;
+	const char *next;
+
+	/* Need one free entry.. */
+	if (includepath[INCLUDEPATHS-2])
+		error_die(token->pos, "too many include path entries");
+
+	/* check that this is not a duplicate */
+	dst = includepath;
+	while (*dst) {
+		if (strcmp(*dst, path) == 0)
+			return;
+		dst++;
+	}
+	next = path;
+	dst = *where;
+
+	update_inc_ptrs(where);
+
+	/*
+	 * Move them all up starting at dst,
+	 * insert the new entry..
+	 */
+	do {
+		const char *tmp = *dst;
+		*dst = next;
+		next = tmp;
+		dst++;
+	} while (next);
+}
+
+static int handle_add_include(struct stream *stream, struct token **line, struct token *token)
+{
+	for (;;) {
+		token = token->next;
+		if (eof_token(token))
+			return 1;
+		if (token_type(token) != TOKEN_STRING) {
+			warning(token->pos, "expected path string");
+			return 1;
+		}
+		add_path_entry(token, token->string->data, &isys_includepath);
+	}
+}
+
+static int handle_add_isystem(struct stream *stream, struct token **line, struct token *token)
+{
+	for (;;) {
+		token = token->next;
+		if (eof_token(token))
+			return 1;
+		if (token_type(token) != TOKEN_STRING) {
+			sparse_error(token->pos, "expected path string");
+			return 1;
+		}
+		add_path_entry(token, token->string->data, &sys_includepath);
+	}
+}
+
+static int handle_add_system(struct stream *stream, struct token **line, struct token *token)
+{
+	for (;;) {
+		token = token->next;
+		if (eof_token(token))
+			return 1;
+		if (token_type(token) != TOKEN_STRING) {
+			sparse_error(token->pos, "expected path string");
+			return 1;
+		}
+		add_path_entry(token, token->string->data, &dirafter_includepath);
+	}
+}
+
+/* Add to end on includepath list - no pointer updates */
+static void add_dirafter_entry(struct token *token, const char *path)
+{
+	const char **dst = includepath;
+
+	/* Need one free entry.. */
+	if (includepath[INCLUDEPATHS-2])
+		error_die(token->pos, "too many include path entries");
+
+	/* Add to the end */
+	while (*dst)
+		dst++;
+	*dst = path;
+	dst++;
+	*dst = NULL;
+}
+
+static int handle_add_dirafter(struct stream *stream, struct token **line, struct token *token)
+{
+	for (;;) {
+		token = token->next;
+		if (eof_token(token))
+			return 1;
+		if (token_type(token) != TOKEN_STRING) {
+			sparse_error(token->pos, "expected path string");
+			return 1;
+		}
+		add_dirafter_entry(token, token->string->data);
+	}
+}
+
+static int handle_split_include(struct stream *stream, struct token **line, struct token *token)
+{
+	/*
+	 * -I-
+	 *  From info gcc:
+	 *  Split the include path.  Any directories specified with `-I'
+	 *  options before `-I-' are searched only for headers requested with
+	 *  `#include "FILE"'; they are not searched for `#include <FILE>'.
+	 *  If additional directories are specified with `-I' options after
+	 *  the `-I-', those directories are searched for all `#include'
+	 *  directives.
+	 *  In addition, `-I-' inhibits the use of the directory of the current
+	 *  file directory as the first search directory for `#include "FILE"'.
+	 */
+	quote_includepath = includepath+1;
+	angle_includepath = sys_includepath;
+	return 1;
+}
+
+/*
+ * We replace "#pragma xxx" with "__pragma__" in the token
+ * stream. Just as an example.
+ *
+ * We'll just #define that away for now, but the theory here
+ * is that we can use this to insert arbitrary token sequences
+ * to turn the pragmas into internal front-end sequences for
+ * when we actually start caring about them.
+ *
+ * So eventually this will turn into some kind of extended
+ * __attribute__() like thing, except called __pragma__(xxx).
+ */
+static int handle_pragma(struct stream *stream, struct token **line, struct token *token)
+{
+	struct token *next = *line;
+
+	token->ident = &pragma_ident;
+	token->pos.newline = 1;
+	token->pos.whitespace = 1;
+	token->pos.pos = 1;
+	*line = token;
+	token->next = next;
+	return 0;
+}
+
+/*
+ * We ignore #line for now.
+ */
+static int handle_line(struct stream *stream, struct token **line, struct token *token)
+{
+	return 1;
+}
+
+static int handle_nondirective(struct stream *stream, struct token **line, struct token *token)
+{
+	sparse_error(token->pos, "unrecognized preprocessor line '%s'", show_token_sequence(token));
+	return 1;
+}
+
+
+static void init_preprocessor(void)
+{
+	int i;
+	int stream = init_stream("preprocessor", -1, includepath);
+	static struct {
+		const char *name;
+		int (*handler)(struct stream *, struct token **, struct token *);
+	} normal[] = {
+		{ "define",		handle_define },
+		{ "weak_define",	handle_weak_define },
+		{ "strong_define",	handle_strong_define },
+		{ "undef",		handle_undef },
+		{ "strong_undef",	handle_strong_undef },
+		{ "warning",		handle_warning },
+		{ "error",		handle_error },
+		{ "include",		handle_include },
+		{ "include_next",	handle_include_next },
+		{ "pragma",		handle_pragma },
+		{ "line",		handle_line },
+
+		// our internal preprocessor tokens
+		{ "nostdinc",	   handle_nostdinc },
+		{ "add_include",   handle_add_include },
+		{ "add_isystem",   handle_add_isystem },
+		{ "add_system",    handle_add_system },
+		{ "add_dirafter",  handle_add_dirafter },
+		{ "split_include", handle_split_include },
+	}, special[] = {
+		{ "ifdef",	handle_ifdef },
+		{ "ifndef",	handle_ifndef },
+		{ "else",	handle_else },
+		{ "endif",	handle_endif },
+		{ "if",		handle_if },
+		{ "elif",	handle_elif },
+	};
+
+	for (i = 0; i < ARRAY_SIZE(normal); i++) {
+		struct symbol *sym;
+		sym = create_symbol(stream, normal[i].name, SYM_PREPROCESSOR, NS_PREPROCESSOR);
+		sym->handler = normal[i].handler;
+		sym->normal = 1;
+	}
+	for (i = 0; i < ARRAY_SIZE(special); i++) {
+		struct symbol *sym;
+		sym = create_symbol(stream, special[i].name, SYM_PREPROCESSOR, NS_PREPROCESSOR);
+		sym->handler = special[i].handler;
+		sym->normal = 0;
+	}
+
+}
+
+static void handle_preprocessor_line(struct stream *stream, struct token **line, struct token *start)
+{
+	int (*handler)(struct stream *, struct token **, struct token *);
+	struct token *token = start->next;
+	int is_normal = 1;
+
+	if (eof_token(token))
+		return;
+
+	if (token_type(token) == TOKEN_IDENT) {
+		struct symbol *sym = lookup_symbol(token->ident, NS_PREPROCESSOR);
+		if (sym) {
+			handler = sym->handler;
+			is_normal = sym->normal;
+		} else {
+			handler = handle_nondirective;
+		}
+	} else if (token_type(token) == TOKEN_NUMBER) {
+		handler = handle_line;
+	} else {
+		handler = handle_nondirective;
+	}
+
+	if (is_normal) {
+		dirty_stream(stream);
+		if (false_nesting)
+			goto out;
+	}
+	if (!handler(stream, line, token))	/* all set */
+		return;
+
+out:
+	free_preprocessor_line(token);
+}
+
+static void preprocessor_line(struct stream *stream, struct token **line)
+{
+	struct token *start = *line, *next;
+	struct token **tp = &start->next;
+
+	for (;;) {
+		next = *tp;
+		if (next->pos.newline)
+			break;
+		tp = &next->next;
+	}
+	*line = next;
+	*tp = &eof_token_entry;
+	handle_preprocessor_line(stream, line, start);
+}
+
+static void do_preprocess(struct token **list)
+{
+	struct token *next;
+
+	while (!eof_token(next = scan_next(list))) {
+		struct stream *stream = input_streams + next->pos.stream;
+
+		if (next->pos.newline && match_op(next, '#')) {
+			if (!next->pos.noexpand) {
+				preprocessor_line(stream, list);
+				__free_token(next);	/* Free the '#' token */
+				continue;
+			}
+		}
+
+		switch (token_type(next)) {
+		case TOKEN_STREAMEND:
+			if (stream->top_if) {
+				nesting_error(stream);
+				sparse_error(stream->top_if->pos, "unterminated preprocessor conditional");
+				stream->top_if = NULL;
+				false_nesting = 0;
+			}
+			if (!stream->dirty)
+				stream->constant = CONSTANT_FILE_YES;
+			*list = next->next;
+			continue;
+		case TOKEN_STREAMBEGIN:
+			*list = next->next;
+			continue;
+
+		default:
+			dirty_stream(stream);
+			if (false_nesting) {
+				*list = next->next;
+				__free_token(next);
+				continue;
+			}
+
+			if (token_type(next) != TOKEN_IDENT ||
+			    expand_one_symbol(list))
+				list = &next->next;
+		}
+	}
+}
+
+struct token * preprocess(struct token *token)
+{
+	preprocessing = 1;
+	init_preprocessor();
+	do_preprocess(&token);
+
+	// Drop all expressions from preprocessing, they're not used any more.
+	// This is not true when we have multiple files, though ;/
+	// clear_expression_alloc();
+	preprocessing = 0;
+
+	return token;
+}
diff --git a/deps/sparse/ptrlist.c b/deps/sparse/ptrlist.c
new file mode 100644
index 0000000..2620412
--- /dev/null
+++ b/deps/sparse/ptrlist.c
@@ -0,0 +1,248 @@
+/*
+ * ptrlist.c
+ *
+ * Pointer list manipulation
+ *
+ * (C) Copyright Linus Torvalds 2003-2005
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "ptrlist.h"
+#include "allocate.h"
+#include "compat.h"
+
+__DECLARE_ALLOCATOR(struct ptr_list, ptrlist);
+__ALLOCATOR(struct ptr_list, "ptr list", ptrlist);
+
+int ptr_list_size(struct ptr_list *head)
+{
+	int nr = 0;
+
+	if (head) {
+		struct ptr_list *list = head;
+		do {
+			nr += list->nr;
+		} while ((list = list->next) != head);
+	}
+	return nr;
+}
+
+/*
+ * Linearize the entries of a list up to a total of 'max',
+ * and return the nr of entries linearized.
+ *
+ * The array to linearize into (second argument) should really
+ * be "void *x[]", but we want to let people fill in any kind
+ * of pointer array, so let's just call it "void *".
+ */
+int linearize_ptr_list(struct ptr_list *head, void **arr, int max)
+{
+	int nr = 0;
+	if (head && max > 0) {
+		struct ptr_list *list = head;
+
+		do {
+			int i = list->nr;
+			if (i > max) 
+				i = max;
+			memcpy(arr, list->list, i*sizeof(void *));
+			arr += i;
+			nr += i;
+			max -= i;
+			if (!max)
+				break;
+		} while ((list = list->next) != head);
+	}
+	return nr;
+}
+
+/*
+ * When we've walked the list and deleted entries,
+ * we may need to re-pack it so that we don't have
+ * any empty blocks left (empty blocks upset the
+ * walking code
+ */
+void pack_ptr_list(struct ptr_list **listp)
+{
+	struct ptr_list *head = *listp;
+
+	if (head) {
+		struct ptr_list *entry = head;
+		do {
+			struct ptr_list *next;
+restart:
+			next = entry->next;
+			if (!entry->nr) {
+				struct ptr_list *prev;
+				if (next == entry) {
+					__free_ptrlist(entry);
+					*listp = NULL;
+					return;
+				}
+				prev = entry->prev;
+				prev->next = next;
+				next->prev = prev;
+				__free_ptrlist(entry);
+				if (entry == head) {
+					*listp = next;
+					head = next;
+					entry = next;
+					goto restart;
+				}
+			}
+			entry = next;
+		} while (entry != head);
+	}
+}		
+
+void split_ptr_list_head(struct ptr_list *head)
+{
+	int old = head->nr, nr = old / 2;
+	struct ptr_list *newlist = __alloc_ptrlist(0);
+	struct ptr_list *next = head->next;
+
+	old -= nr;
+	head->nr = old;
+	newlist->next = next;
+	next->prev = newlist;
+	newlist->prev = head;
+	head->next = newlist;
+	newlist->nr = nr;
+	memcpy(newlist->list, head->list + old, nr * sizeof(void *));
+	memset(head->list + old, 0xf0, nr * sizeof(void *));
+}
+
+void **__add_ptr_list(struct ptr_list **listp, void *ptr, unsigned long tag)
+{
+	struct ptr_list *list = *listp;
+	struct ptr_list *last = NULL; /* gcc complains needlessly */
+	void **ret;
+	int nr;
+
+	/* The low two bits are reserved for tags */
+	assert((3 & (unsigned long)ptr) == 0);
+	assert((~3 & tag) == 0);
+	ptr = (void *)(tag | (unsigned long)ptr);
+
+	if (!list || (nr = (last = list->prev)->nr) >= LIST_NODE_NR) {
+		struct ptr_list *newlist = __alloc_ptrlist(0);
+		if (!list) {
+			newlist->next = newlist;
+			newlist->prev = newlist;
+			*listp = newlist;
+		} else {
+			newlist->prev = last;
+			newlist->next = list;
+			list->prev = newlist;
+			last->next = newlist;
+		}
+		last = newlist;
+		nr = 0;
+	}
+	ret = last->list + nr;
+	*ret = ptr;
+	nr++;
+	last->nr = nr;
+	return ret;
+}
+
+int delete_ptr_list_entry(struct ptr_list **list, void *entry, int count)
+{
+	void *ptr;
+
+	FOR_EACH_PTR(*list, ptr) {
+		if (ptr == entry) {
+			DELETE_CURRENT_PTR(ptr);
+			if (!--count)
+				goto out;
+		}
+	} END_FOR_EACH_PTR(ptr);
+	assert(count <= 0);
+out:
+	pack_ptr_list(list);
+	return count;
+}
+
+int replace_ptr_list_entry(struct ptr_list **list, void *old_ptr, void *new_ptr, int count)
+{
+	void *ptr;
+
+	FOR_EACH_PTR(*list, ptr) {
+		if (ptr==old_ptr) {
+			REPLACE_CURRENT_PTR(ptr, new_ptr);
+			if (!--count)
+				goto out;
+		}
+	}END_FOR_EACH_PTR(ptr);
+	assert(count <= 0);
+out:
+	return count;
+}
+
+/* This removes the last entry, but doesn't pack the ptr list */
+void * undo_ptr_list_last(struct ptr_list **head)
+{
+	struct ptr_list *last, *first = *head;
+
+	if (!first)
+		return NULL;
+	last = first;
+	do {
+		last = last->prev;
+		if (last->nr) {
+			void *ptr;
+			int nr = --last->nr;
+			ptr = last->list[nr];
+			last->list[nr] = (void *)0xf1f1f1f1;
+			return ptr;
+		}
+	} while (last != first);
+	return NULL;
+}
+
+void * delete_ptr_list_last(struct ptr_list **head)
+{
+	void *ptr = NULL;
+	struct ptr_list *last, *first = *head;
+
+	if (!first)
+		return NULL;
+	last = first->prev;
+	if (last->nr)
+		ptr = last->list[--last->nr];
+	if (last->nr <=0) {
+		first->prev = last->prev;
+		last->prev->next = first;
+		if (last == first)
+			*head = NULL;
+		__free_ptrlist(last);
+	}
+	return ptr;
+}
+
+void concat_ptr_list(struct ptr_list *a, struct ptr_list **b)
+{
+	void *entry;
+	FOR_EACH_PTR(a, entry) {
+		add_ptr_list(b, entry);
+	} END_FOR_EACH_PTR(entry);
+}
+
+void __free_ptr_list(struct ptr_list **listp)
+{
+	struct ptr_list *tmp, *list = *listp;
+
+	if (!list)
+		return;
+
+	list->prev->next = NULL;
+	while (list) {
+		tmp = list;
+		list = list->next;
+		__free_ptrlist(tmp);
+	}
+
+	*listp = NULL;
+}
diff --git a/deps/sparse/ptrlist.h b/deps/sparse/ptrlist.h
new file mode 100644
index 0000000..58d3bda
--- /dev/null
+++ b/deps/sparse/ptrlist.h
@@ -0,0 +1,282 @@
+#ifndef PTR_LIST_H
+#define PTR_LIST_H
+
+#include <stdlib.h>
+
+/*
+ * Generic pointer list manipulation code. 
+ *
+ * (C) Copyright Linus Torvalds 2003-2005
+ */
+
+/* Silly type-safety check ;) */
+#define DECLARE_PTR_LIST(listname,type)	struct listname { type *list[1]; }
+#define CHECK_TYPE(head,ptr)		(void)(&(ptr) == &(head)->list[0])
+#define TYPEOF(head)			__typeof__(&(head)->list[0])
+#define VRFY_PTR_LIST(head)		(void)(sizeof((head)->list[0]))
+
+/*
+ * The "unnecessary" statement expression is there to shut up a totally 
+ * bogus gcc warning about unused expressions, brought on by the fact
+ * that we cast the result to the proper type.
+ */
+#define MKTYPE(head,expr)		({ (TYPEOF(head))(expr); })
+
+#define LIST_NODE_NR (29)
+
+struct ptr_list {
+	int nr;
+	struct ptr_list *prev;
+	struct ptr_list *next;
+	void *list[LIST_NODE_NR];
+};
+
+#define ptr_list_empty(x) ((x) == NULL)
+
+void * undo_ptr_list_last(struct ptr_list **head);
+void * delete_ptr_list_last(struct ptr_list **head);
+int delete_ptr_list_entry(struct ptr_list **, void *, int);
+int replace_ptr_list_entry(struct ptr_list **, void *old, void *new, int);
+extern void sort_list(struct ptr_list **, int (*)(const void *, const void *));
+
+extern void **__add_ptr_list(struct ptr_list **, void *, unsigned long);
+extern void concat_ptr_list(struct ptr_list *a, struct ptr_list **b);
+extern void __free_ptr_list(struct ptr_list **);
+extern int ptr_list_size(struct ptr_list *);
+extern int linearize_ptr_list(struct ptr_list *, void **, int);
+
+/*
+ * Hey, who said that you can't do overloading in C?
+ *
+ * You just have to be creative, and use some gcc
+ * extensions..
+ */
+#define add_ptr_list_tag(list,entry,tag) \
+	MKTYPE(*(list), (CHECK_TYPE(*(list),(entry)),__add_ptr_list((struct ptr_list **)(list), (entry), (tag))))
+#define add_ptr_list_notag(list,entry)										\
+	MKTYPE(*(list), (CHECK_TYPE(*(list),(entry)),__add_ptr_list((struct ptr_list **)(list),			\
+								    (void *)((unsigned long)(entry) & ~3UL), 	\
+								    (unsigned long)(entry) & 3)))
+#define add_ptr_list(list,entry) \
+	add_ptr_list_tag(list,entry,0)
+#define free_ptr_list(list) \
+	do { VRFY_PTR_LIST(*(list)); __free_ptr_list((struct ptr_list **)(list)); } while (0)
+
+#define PTR_ENTRY_NOTAG(h,i)	((h)->list[i])
+#define PTR_ENTRY(h,i)	(void *)(~3UL & (unsigned long)PTR_ENTRY_NOTAG(h,i))
+
+static inline void *first_ptr_list(struct ptr_list *list)
+{
+	if (!list)
+		return NULL;
+	return PTR_ENTRY(list, 0);
+}
+
+static inline void *last_ptr_list(struct ptr_list *list)
+{
+
+	if (!list)
+		return NULL;
+	list = list->prev;
+	return PTR_ENTRY(list, list->nr-1);
+}
+
+#define DO_PREPARE(head, ptr, __head, __list, __nr, PTR_ENTRY)				\
+	do {										\
+		struct ptr_list *__head = (struct ptr_list *) (head);			\
+		struct ptr_list *__list = __head;					\
+		int __nr = 0;								\
+		CHECK_TYPE(head,ptr);							\
+		if (__head) ptr = PTR_ENTRY(__head, 0);					\
+		else ptr = NULL
+
+#define DO_NEXT(ptr, __head, __list, __nr, PTR_ENTRY)					\
+		if (ptr) {								\
+			if (++__nr < __list->nr) {					\
+				ptr = PTR_ENTRY(__list,__nr);				\
+			} else {							\
+				__list = __list->next;					\
+				ptr = NULL;						\
+				if (__list != __head) {					\
+					__nr = 0;					\
+					ptr = PTR_ENTRY(__list,0);			\
+				}							\
+			}								\
+		}
+
+#define DO_RESET(ptr, __head, __list, __nr, PTR_ENTRY)					\
+	do {										\
+		__nr = 0;								\
+		__list = __head;							\
+		if (__head) ptr = PTR_ENTRY(__head, 0);					\
+	} while (0)
+
+#define DO_FINISH(ptr, __head, __list, __nr)						\
+		(void)(__nr); /* Sanity-check nesting */				\
+	} while (0)
+
+#define PREPARE_PTR_LIST(head, ptr) \
+	DO_PREPARE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY)
+
+#define NEXT_PTR_LIST(ptr) \
+	DO_NEXT(ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY)
+
+#define RESET_PTR_LIST(ptr) \
+	DO_RESET(ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY)
+
+#define FINISH_PTR_LIST(ptr) \
+	DO_FINISH(ptr, __head##ptr, __list##ptr, __nr##ptr)
+
+#define DO_FOR_EACH(head, ptr, __head, __list, __nr, PTR_ENTRY) do {			\
+	struct ptr_list *__head = (struct ptr_list *) (head);				\
+	struct ptr_list *__list = __head;						\
+	CHECK_TYPE(head,ptr);								\
+	if (__head) {									\
+		do { int __nr;								\
+			for (__nr = 0; __nr < __list->nr; __nr++) {			\
+				do {							\
+					ptr = PTR_ENTRY(__list,__nr);			\
+					do {
+
+#define DO_END_FOR_EACH(ptr, __head, __list, __nr)					\
+					} while (0);					\
+				} while (0);						\
+			}								\
+		} while ((__list = __list->next) != __head);				\
+	}										\
+} while (0)
+
+#define DO_FOR_EACH_REVERSE(head, ptr, __head, __list, __nr, PTR_ENTRY) do {		\
+	struct ptr_list *__head = (struct ptr_list *) (head);				\
+	struct ptr_list *__list = __head;						\
+	CHECK_TYPE(head,ptr);								\
+	if (__head) {									\
+		do { int __nr;								\
+			__list = __list->prev;						\
+			__nr = __list->nr;						\
+			while (--__nr >= 0) {						\
+				do {							\
+					ptr = PTR_ENTRY(__list,__nr);			\
+					do {
+
+
+#define DO_END_FOR_EACH_REVERSE(ptr, __head, __list, __nr)				\
+					} while (0);					\
+				} while (0);						\
+			}								\
+		} while (__list != __head);						\
+	}										\
+} while (0)
+
+#define DO_REVERSE(ptr, __head, __list, __nr, new, __newhead,				\
+		   __newlist, __newnr, PTR_ENTRY) do { 					\
+	struct ptr_list *__newhead = __head;						\
+	struct ptr_list *__newlist = __list;						\
+	int __newnr = __nr;								\
+	new = ptr;									\
+	goto __inside##new;								\
+	if (1) {									\
+		do {									\
+			__newlist = __newlist->prev;					\
+			__newnr = __newlist->nr;					\
+	__inside##new:									\
+			while (--__newnr >= 0) {					\
+				do {							\
+					new = PTR_ENTRY(__newlist,__newnr);		\
+					do {
+
+#define RECURSE_PTR_REVERSE(ptr, new)							\
+	DO_REVERSE(ptr, __head##ptr, __list##ptr, __nr##ptr,				\
+		   new, __head##new, __list##new, __nr##new, PTR_ENTRY)
+
+#define DO_THIS_ADDRESS(ptr, __head, __list, __nr)					\
+	((__typeof__(&(ptr))) (__list->list + __nr))
+
+#define FOR_EACH_PTR(head, ptr) \
+	DO_FOR_EACH(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY)
+
+#define END_FOR_EACH_PTR(ptr) \
+	DO_END_FOR_EACH(ptr, __head##ptr, __list##ptr, __nr##ptr)
+
+#define FOR_EACH_PTR_NOTAG(head, ptr) \
+	DO_FOR_EACH(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_NOTAG)
+
+#define END_FOR_EACH_PTR_NOTAG(ptr) END_FOR_EACH_PTR(ptr)
+
+#define FOR_EACH_PTR_REVERSE(head, ptr) \
+	DO_FOR_EACH_REVERSE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY)
+
+#define END_FOR_EACH_PTR_REVERSE(ptr) \
+	DO_END_FOR_EACH_REVERSE(ptr, __head##ptr, __list##ptr, __nr##ptr)
+
+#define FOR_EACH_PTR_REVERSE_NOTAG(head, ptr) \
+	DO_FOR_EACH_REVERSE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_NOTAG)
+
+#define END_FOR_EACH_PTR_REVERSE_NOTAG(ptr) END_FOR_EACH_PTR_REVERSE(ptr)
+
+#define THIS_ADDRESS(ptr) \
+	DO_THIS_ADDRESS(ptr, __head##ptr, __list##ptr, __nr##ptr)
+
+extern void split_ptr_list_head(struct ptr_list *);
+
+#define DO_SPLIT(ptr, __head, __list, __nr) do {					\
+	split_ptr_list_head(__list);							\
+	if (__nr >= __list->nr) {							\
+		__nr -= __list->nr;							\
+		__list = __list->next;							\
+	};										\
+} while (0)
+
+#define DO_INSERT_CURRENT(new, ptr, __head, __list, __nr) do {				\
+	void **__this, **__last;							\
+	if (__list->nr == LIST_NODE_NR)							\
+		DO_SPLIT(ptr, __head, __list, __nr);					\
+	__this = __list->list + __nr;							\
+	__last = __list->list + __list->nr - 1;						\
+	while (__last >= __this) {							\
+		__last[1] = __last[0];							\
+		__last--;								\
+	}										\
+	*__this = (new);								\
+	__list->nr++;									\
+} while (0)
+
+#define INSERT_CURRENT(new, ptr) \
+	DO_INSERT_CURRENT(new, ptr, __head##ptr, __list##ptr, __nr##ptr)
+
+#define DO_DELETE_CURRENT(ptr, __head, __list, __nr) do {				\
+	void **__this = __list->list + __nr;						\
+	void **__last = __list->list + __list->nr - 1;					\
+	while (__this < __last) {							\
+		__this[0] = __this[1];							\
+		__this++;								\
+	}										\
+	*__this = (void *)0xf0f0f0f0;							\
+	__list->nr--; __nr--;								\
+} while (0)
+
+#define DELETE_CURRENT_PTR(ptr) \
+	DO_DELETE_CURRENT(ptr, __head##ptr, __list##ptr, __nr##ptr)
+
+#define REPLACE_CURRENT_PTR(ptr, new_ptr)						\
+	do { *THIS_ADDRESS(ptr) = (new_ptr); } while (0)
+
+extern void pack_ptr_list(struct ptr_list **);
+
+#define PACK_PTR_LIST(x) pack_ptr_list((struct ptr_list **)(x))
+
+static inline void update_tag(void *p, unsigned long tag)
+{
+	unsigned long *ptr = p;
+	*ptr = tag | (~3UL & *ptr);
+}
+
+static inline void *tag_ptr(void *ptr, unsigned long tag)
+{
+	return (void *)(tag | (unsigned long)ptr);
+}
+
+#define CURRENT_TAG(ptr) (3 & (unsigned long)*THIS_ADDRESS(ptr))
+#define TAG_CURRENT(ptr,val)	update_tag(THIS_ADDRESS(ptr),val)
+
+#endif /* PTR_LIST_H */
diff --git a/deps/sparse/scope.c b/deps/sparse/scope.c
new file mode 100644
index 0000000..27e38bc
--- /dev/null
+++ b/deps/sparse/scope.c
@@ -0,0 +1,118 @@
+/*
+ * Symbol scoping.
+ *
+ * This is pretty trivial.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "symbol.h"
+#include "scope.h"
+
+static struct scope builtin_scope = { .next = &builtin_scope };
+
+struct scope	*block_scope = &builtin_scope,		// regular automatic variables etc
+		*function_scope = &builtin_scope,	// labels, arguments etc
+		*file_scope = &builtin_scope,		// static
+		*global_scope = &builtin_scope;		// externally visible
+
+void bind_scope(struct symbol *sym, struct scope *scope)
+{
+	sym->scope = scope;
+	add_symbol(&scope->symbols, sym);
+}
+
+static void start_scope(struct scope **s)
+{
+	struct scope *scope = __alloc_scope(0);
+	memset(scope, 0, sizeof(*scope));
+	scope->next = *s;
+	*s = scope;
+}
+
+void start_file_scope(void)
+{
+	struct scope *scope = __alloc_scope(0);
+
+	memset(scope, 0, sizeof(*scope));
+	scope->next = &builtin_scope;
+	file_scope = scope;
+
+	/* top-level stuff defaults to file scope, "extern" etc will choose global scope */
+	function_scope = scope;
+	block_scope = scope;
+}
+
+void start_symbol_scope(void)
+{
+	start_scope(&block_scope);
+}
+
+void start_function_scope(void)
+{
+	start_scope(&function_scope);
+	start_scope(&block_scope);
+}
+
+static void remove_symbol_scope(struct symbol *sym)
+{
+	struct symbol **ptr = &sym->ident->symbols;
+
+	while (*ptr != sym)
+		ptr = &(*ptr)->next_id;
+	*ptr = sym->next_id;
+}
+
+static void end_scope(struct scope **s)
+{
+	struct scope *scope = *s;
+	struct symbol_list *symbols = scope->symbols;
+	struct symbol *sym;
+
+	*s = scope->next;
+	scope->symbols = NULL;
+	FOR_EACH_PTR(symbols, sym) {
+		remove_symbol_scope(sym);
+	} END_FOR_EACH_PTR(sym);
+}
+
+void end_file_scope(void)
+{
+	end_scope(&file_scope);
+}
+
+void new_file_scope(void)
+{
+	if (file_scope != &builtin_scope)
+		end_file_scope();
+	start_file_scope();
+}
+
+void end_symbol_scope(void)
+{
+	end_scope(&block_scope);
+}
+
+void end_function_scope(void)
+{
+	end_scope(&block_scope);
+	end_scope(&function_scope);
+}
+
+int is_outer_scope(struct scope *scope)
+{
+	if (scope == block_scope)
+		return 0;
+	if (scope == &builtin_scope && block_scope->next == &builtin_scope)
+		return 0;
+	return 1;
+}
+
diff --git a/deps/sparse/scope.h b/deps/sparse/scope.h
new file mode 100644
index 0000000..0fab286
--- /dev/null
+++ b/deps/sparse/scope.h
@@ -0,0 +1,44 @@
+#ifndef SCOPE_H
+#define SCOPE_H
+/*
+ * Symbol scoping is pretty simple.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+
+struct symbol;
+
+struct scope {
+	struct token *token;		/* Scope start information */
+	struct symbol_list *symbols;	/* List of symbols in this scope */
+	struct scope *next;
+};
+
+extern struct scope
+		*block_scope,
+		*function_scope,
+		*file_scope,
+		*global_scope;
+
+static inline int toplevel(struct scope *scope)
+{
+	return scope == file_scope || scope == global_scope;
+}
+
+extern void start_file_scope(void);
+extern void end_file_scope(void);
+extern void new_file_scope(void);
+
+extern void start_symbol_scope(void);
+extern void end_symbol_scope(void);
+
+extern void start_function_scope(void);
+extern void end_function_scope(void);
+
+extern void bind_scope(struct symbol *, struct scope *);
+
+extern int is_outer_scope(struct scope *);
+#endif
diff --git a/deps/sparse/show-parse.c b/deps/sparse/show-parse.c
new file mode 100644
index 0000000..1333e30
--- /dev/null
+++ b/deps/sparse/show-parse.c
@@ -0,0 +1,1158 @@
+/*
+ * sparse/show-parse.c
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ *
+ * Print out results of parsing for debugging and testing.
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "scope.h"
+#include "expression.h"
+#include "target.h"
+
+static int show_symbol_expr(struct symbol *sym);
+static int show_string_expr(struct expression *expr);
+
+static void do_debug_symbol(struct symbol *sym, int indent)
+{
+	static const char indent_string[] = "                                  ";
+	static const char *typestr[] = {
+		[SYM_UNINITIALIZED] = "none",
+		[SYM_PREPROCESSOR] = "cpp.",
+		[SYM_BASETYPE] = "base",
+		[SYM_NODE] = "node",
+		[SYM_PTR] = "ptr.",
+		[SYM_FN] = "fn..",
+		[SYM_ARRAY] = "arry",
+		[SYM_STRUCT] = "strt",
+		[SYM_UNION] = "unin",
+		[SYM_ENUM] = "enum",
+		[SYM_TYPEDEF] = "tdef",
+		[SYM_TYPEOF] = "tpof",
+		[SYM_MEMBER] = "memb",
+		[SYM_BITFIELD] = "bitf",
+		[SYM_LABEL] = "labl",
+		[SYM_RESTRICT] = "rstr",
+		[SYM_FOULED] = "foul",
+		[SYM_BAD] = "bad.",
+	};
+	struct context *context;
+	int i;
+
+	if (!sym)
+		return;
+	fprintf(stderr, "%.*s%s%3d:%lu %s %s (as: %d) %p (%s:%d:%d) %s\n",
+		indent, indent_string, typestr[sym->type],
+		sym->bit_size, sym->ctype.alignment,
+		modifier_string(sym->ctype.modifiers), show_ident(sym->ident), sym->ctype.as,
+		sym, stream_name(sym->pos.stream), sym->pos.line, sym->pos.pos,
+		builtin_typename(sym) ?: "");
+	i = 0;
+	FOR_EACH_PTR(sym->ctype.contexts, context) {
+		/* FIXME: should print context expression */
+		fprintf(stderr, "< context%d: in=%d, out=%d\n",
+			i, context->in, context->out);
+		fprintf(stderr, "  end context%d >\n", i);
+		i++;
+	} END_FOR_EACH_PTR(context);
+	if (sym->type == SYM_FN) {
+		struct symbol *arg;
+		i = 0;
+		FOR_EACH_PTR(sym->arguments, arg) {
+			fprintf(stderr, "< arg%d:\n", i);
+			do_debug_symbol(arg, 0);
+			fprintf(stderr, "  end arg%d >\n", i);
+			i++;
+		} END_FOR_EACH_PTR(arg);
+	}
+	do_debug_symbol(sym->ctype.base_type, indent+2);
+}
+
+void debug_symbol(struct symbol *sym)
+{
+	do_debug_symbol(sym, 0);
+}
+
+/*
+ * Symbol type printout. The type system is by far the most
+ * complicated part of C - everything else is trivial.
+ */
+const char *modifier_string(unsigned long mod)
+{
+	static char buffer[100];
+	int len = 0;
+	int i;
+	struct mod_name {
+		unsigned long mod;
+		const char *name;
+	} *m;
+
+	static struct mod_name mod_names[] = {
+		{MOD_AUTO,		"auto"},
+		{MOD_REGISTER,		"register"},
+		{MOD_STATIC,		"static"},
+		{MOD_EXTERN,		"extern"},
+		{MOD_CONST,		"const"},
+		{MOD_VOLATILE,		"volatile"},
+		{MOD_SIGNED,		"[signed]"},
+		{MOD_UNSIGNED,		"[unsigned]"},
+		{MOD_CHAR,		"[char]"},
+		{MOD_SHORT,		"[short]"},
+		{MOD_LONG,		"[long]"},
+		{MOD_LONGLONG,		"[long long]"},
+		{MOD_LONGLONGLONG,	"[long long long]"},
+		{MOD_TYPEDEF,		"[typedef]"},
+		{MOD_TLS,		"[tls]"},
+		{MOD_INLINE,		"inline"},
+		{MOD_ADDRESSABLE,	"[addressable]"},
+		{MOD_NOCAST,		"[nocast]"},
+		{MOD_NODEREF,		"[noderef]"},
+		{MOD_ACCESSED,		"[accessed]"},
+		{MOD_TOPLEVEL,		"[toplevel]"},
+		{MOD_ASSIGNED,		"[assigned]"},
+		{MOD_TYPE,		"[type]"},
+		{MOD_SAFE,		"[safe]"},
+		{MOD_USERTYPE,		"[usertype]"},
+		{MOD_NORETURN,		"[noreturn]"},
+		{MOD_EXPLICITLY_SIGNED,	"[explicitly-signed]"},
+		{MOD_BITWISE,		"[bitwise]"},
+		{MOD_PURE,		"[pure]"},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(mod_names); i++) {
+		m = mod_names + i;
+		if (mod & m->mod) {
+			char c;
+			const char *name = m->name;
+			while ((c = *name++) != '\0' && len + 2 < sizeof buffer)
+				buffer[len++] = c;
+			buffer[len++] = ' ';
+		}
+	}
+	buffer[len] = 0;
+	return buffer;
+}
+
+static void show_struct_member(struct symbol *sym)
+{
+	printf("\t%s:%d:%ld at offset %ld.%d", show_ident(sym->ident), sym->bit_size, sym->ctype.alignment, sym->offset, sym->bit_offset);
+	printf("\n");
+}
+
+void show_symbol_list(struct symbol_list *list, const char *sep)
+{
+	struct symbol *sym;
+	const char *prepend = "";
+
+	FOR_EACH_PTR(list, sym) {
+		puts(prepend);
+		prepend = ", ";
+		show_symbol(sym);
+	} END_FOR_EACH_PTR(sym);
+}
+
+struct type_name {
+	char *start;
+	char *end;
+};
+
+static void FORMAT_ATTR(2) prepend(struct type_name *name, const char *fmt, ...)
+{
+	static char buffer[512];
+	int n;
+
+	va_list args;
+	va_start(args, fmt);
+	n = vsprintf(buffer, fmt, args);
+	va_end(args);
+
+	name->start -= n;
+	memcpy(name->start, buffer, n);
+}
+
+static void FORMAT_ATTR(2) append(struct type_name *name, const char *fmt, ...)
+{
+	static char buffer[512];
+	int n;
+
+	va_list args;
+	va_start(args, fmt);
+	n = vsprintf(buffer, fmt, args);
+	va_end(args);
+
+	memcpy(name->end, buffer, n);
+	name->end += n;
+}
+
+static struct ctype_name {
+	struct symbol *sym;
+	const char *name;
+} typenames[] = {
+	{ & char_ctype,  "char" },
+	{ &schar_ctype,  "signed char" },
+	{ &uchar_ctype,  "unsigned char" },
+	{ & short_ctype, "short" },
+	{ &sshort_ctype, "signed short" },
+	{ &ushort_ctype, "unsigned short" },
+	{ & int_ctype,   "int" },
+	{ &sint_ctype,   "signed int" },
+	{ &uint_ctype,   "unsigned int" },
+	{ &slong_ctype,  "signed long" },
+	{ & long_ctype,  "long" },
+	{ &ulong_ctype,  "unsigned long" },
+	{ & llong_ctype, "long long" },
+	{ &sllong_ctype, "signed long long" },
+	{ &ullong_ctype, "unsigned long long" },
+	{ & lllong_ctype, "long long long" },
+	{ &slllong_ctype, "signed long long long" },
+	{ &ulllong_ctype, "unsigned long long long" },
+
+	{ &void_ctype,   "void" },
+	{ &bool_ctype,   "bool" },
+	{ &string_ctype, "string" },
+
+	{ &float_ctype,  "float" },
+	{ &double_ctype, "double" },
+	{ &ldouble_ctype,"long double" },
+	{ &incomplete_ctype, "incomplete type" },
+	{ &int_type, "abstract int" },
+	{ &fp_type, "abstract fp" },
+	{ &label_ctype, "label type" },
+	{ &bad_ctype, "bad type" },
+};
+
+const char *builtin_typename(struct symbol *sym)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(typenames); i++)
+		if (typenames[i].sym == sym)
+			return typenames[i].name;
+	return NULL;
+}
+
+const char *builtin_ctypename(struct ctype *ctype)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(typenames); i++)
+		if (&typenames[i].sym->ctype == ctype)
+			return typenames[i].name;
+	return NULL;
+}
+
+static void do_show_type(struct symbol *sym, struct type_name *name)
+{
+	const char *typename;
+	unsigned long mod = 0;
+	int as = 0;
+	int was_ptr = 0;
+	int restr = 0;
+	int fouled = 0;
+
+deeper:
+	if (!sym || (sym->type != SYM_NODE && sym->type != SYM_ARRAY &&
+		     sym->type != SYM_BITFIELD)) {
+		const char *s;
+		size_t len;
+
+		if (as)
+			prepend(name, "<asn:%d>", as);
+
+		s = modifier_string(mod);
+		len = strlen(s);
+		name->start -= len;    
+		memcpy(name->start, s, len);  
+		mod = 0;
+		as = 0;
+	}
+
+	if (!sym)
+		goto out;
+
+	if ((typename = builtin_typename(sym))) {
+		int len = strlen(typename);
+		if (name->start != name->end)
+			*--name->start = ' ';
+		name->start -= len;
+		memcpy(name->start, typename, len);
+		goto out;
+	}
+
+	/* Prepend */
+	switch (sym->type) {
+	case SYM_PTR:
+		prepend(name, "*");
+		mod = sym->ctype.modifiers;
+		as = sym->ctype.as;
+		was_ptr = 1;
+		break;
+
+	case SYM_FN:
+		if (was_ptr) {
+			prepend(name, "( ");
+			append(name, " )");
+			was_ptr = 0;
+		}
+		append(name, "( ... )");
+		break;
+
+	case SYM_STRUCT:
+		if (name->start != name->end)
+			*--name->start = ' ';
+		prepend(name, "struct %s", show_ident(sym->ident));
+		goto out;
+
+	case SYM_UNION:
+		if (name->start != name->end)
+			*--name->start = ' ';
+		prepend(name, "union %s", show_ident(sym->ident));
+		goto out;
+
+	case SYM_ENUM:
+		prepend(name, "enum %s ", show_ident(sym->ident));
+		break;
+
+	case SYM_NODE:
+		append(name, "%s", show_ident(sym->ident));
+		mod |= sym->ctype.modifiers;
+		as |= sym->ctype.as;
+		break;
+
+	case SYM_BITFIELD:
+		mod |= sym->ctype.modifiers;
+		as |= sym->ctype.as;
+		append(name, ":%d", sym->bit_size);
+		break;
+
+	case SYM_LABEL:
+		append(name, "label(%s:%p)", show_ident(sym->ident), sym);
+		return;
+
+	case SYM_ARRAY:
+		mod |= sym->ctype.modifiers;
+		as |= sym->ctype.as;
+		if (was_ptr) {
+			prepend(name, "( ");
+			append(name, " )");
+			was_ptr = 0;
+		}
+		append(name, "[%lld]", get_expression_value(sym->array_size));
+		break;
+
+	case SYM_RESTRICT:
+		if (!sym->ident) {
+			restr = 1;
+			break;
+		}
+		if (name->start != name->end)
+			*--name->start = ' ';
+		prepend(name, "restricted %s", show_ident(sym->ident));
+		goto out;
+
+	case SYM_FOULED:
+		fouled = 1;
+		break;
+
+	default:
+		if (name->start != name->end)
+			*--name->start = ' ';
+		prepend(name, "unknown type %d", sym->type);
+		goto out;
+	}
+
+	sym = sym->ctype.base_type;
+	goto deeper;
+
+out:
+	if (restr)
+		prepend(name, "restricted ");
+	if (fouled)
+		prepend(name, "fouled ");
+}
+
+void show_type(struct symbol *sym)
+{
+	char array[200];
+	struct type_name name;
+
+	name.start = name.end = array+100;
+	do_show_type(sym, &name);
+	*name.end = 0;
+	printf("%s", name.start);
+}
+
+const char *show_typename(struct symbol *sym)
+{
+	static char array[200];
+	struct type_name name;
+
+	name.start = name.end = array+100;
+	do_show_type(sym, &name);
+	*name.end = 0;
+	return name.start;
+}
+
+void show_symbol(struct symbol *sym)
+{
+	struct symbol *type;
+
+	if (!sym)
+		return;
+
+	if (sym->ctype.alignment)
+		printf(".align %ld\n", sym->ctype.alignment);
+
+	show_type(sym);
+	type = sym->ctype.base_type;
+	if (!type) {
+		printf("\n");
+		return;
+	}
+
+	/*
+	 * Show actual implementation information
+	 */
+	switch (type->type) {
+		struct symbol *member;
+
+	case SYM_STRUCT:
+	case SYM_UNION:
+		printf(" {\n");
+		FOR_EACH_PTR(type->symbol_list, member) {
+			show_struct_member(member);
+		} END_FOR_EACH_PTR(member);
+		printf("}\n");
+		break;
+
+	case SYM_FN: {
+		struct statement *stmt = type->stmt;
+		printf("\n");
+		if (stmt) {
+			int val;
+			val = show_statement(stmt);
+			if (val)
+				printf("\tmov.%d\t\tretval,%d\n", stmt->ret->bit_size, val);
+			printf("\tret\n");
+		}
+		break;
+	}
+
+	default:
+		printf("\n");
+		break;
+	}
+
+	if (sym->initializer) {
+		printf(" = \n");
+		show_expression(sym->initializer);
+	}
+}
+
+static int show_symbol_init(struct symbol *sym);
+
+static int new_pseudo(void)
+{
+	static int nr = 0;
+	return ++nr;
+}
+
+static int new_label(void)
+{
+	static int label = 0;
+	return ++label;
+}
+
+static void show_switch_statement(struct statement *stmt)
+{
+	int val = show_expression(stmt->switch_expression);
+	struct symbol *sym;
+	printf("\tswitch v%d\n", val);
+
+	/*
+	 * Debugging only: Check that the case list is correct
+	 * by printing it out.
+	 *
+	 * This is where a _real_ back-end would go through the
+	 * cases to decide whether to use a lookup table or a
+	 * series of comparisons etc
+	 */
+	printf("# case table:\n");
+	FOR_EACH_PTR(stmt->switch_case->symbol_list, sym) {
+		struct statement *case_stmt = sym->stmt;
+		struct expression *expr = case_stmt->case_expression;
+		struct expression *to = case_stmt->case_to;
+
+		if (!expr) {
+			printf("    default");
+		} else {
+			if (expr->type == EXPR_VALUE) {
+				printf("    case %lld", expr->value);
+				if (to) {
+					if (to->type == EXPR_VALUE) {
+						printf(" .. %lld", to->value);
+					} else {
+						printf(" .. what?");
+					}
+				}
+			} else
+				printf("    what?");
+		}
+		printf(": .L%p\n", sym->bb_target);
+	} END_FOR_EACH_PTR(sym);
+	printf("# end case table\n");
+
+	show_statement(stmt->switch_statement);
+
+	if (stmt->switch_break->used)
+		printf(".L%p:\n", stmt->switch_break->bb_target);
+}
+
+static void show_symbol_decl(struct symbol_list *syms)
+{
+	struct symbol *sym;
+	FOR_EACH_PTR(syms, sym) {
+		show_symbol_init(sym);
+	} END_FOR_EACH_PTR(sym);
+}
+
+static int show_return_stmt(struct statement *stmt);
+
+/*
+ * Print out a statement
+ */
+int show_statement(struct statement *stmt)
+{
+	if (!stmt)
+		return 0;
+	switch (stmt->type) {
+	case STMT_DECLARATION:
+		show_symbol_decl(stmt->declaration);
+		return 0;
+	case STMT_RETURN:
+		return show_return_stmt(stmt);
+	case STMT_COMPOUND: {
+		struct statement *s;
+		int last = 0;
+
+		if (stmt->inline_fn) {
+			show_statement(stmt->args);
+			printf("\tbegin_inline \t%s\n", show_ident(stmt->inline_fn->ident));
+		}
+		FOR_EACH_PTR(stmt->stmts, s) {
+			last = show_statement(s);
+		} END_FOR_EACH_PTR(s);
+		if (stmt->ret) {
+			int addr, bits;
+			printf(".L%p:\n", stmt->ret);
+			addr = show_symbol_expr(stmt->ret);
+			bits = stmt->ret->bit_size;
+			last = new_pseudo();
+			printf("\tld.%d\t\tv%d,[v%d]\n", bits, last, addr);
+		}
+		if (stmt->inline_fn)
+			printf("\tend_inlined\t%s\n", show_ident(stmt->inline_fn->ident));
+		return last;
+	}
+
+	case STMT_EXPRESSION:
+		return show_expression(stmt->expression);
+	case STMT_IF: {
+		int val, target;
+		struct expression *cond = stmt->if_conditional;
+
+/* This is only valid if nobody can jump into the "dead" statement */
+#if 0
+		if (cond->type == EXPR_VALUE) {
+			struct statement *s = stmt->if_true;
+			if (!cond->value)
+				s = stmt->if_false;
+			show_statement(s);
+			break;
+		}
+#endif
+		val = show_expression(cond);
+		target = new_label();
+		printf("\tje\t\tv%d,.L%d\n", val, target);
+		show_statement(stmt->if_true);
+		if (stmt->if_false) {
+			int last = new_label();
+			printf("\tjmp\t\t.L%d\n", last);
+			printf(".L%d:\n", target);
+			target = last;
+			show_statement(stmt->if_false);
+		}
+		printf(".L%d:\n", target);
+		break;
+	}
+	case STMT_SWITCH:
+		show_switch_statement(stmt);
+		break;
+
+	case STMT_CASE:
+		printf(".L%p:\n", stmt->case_label);
+		show_statement(stmt->case_statement);
+		break;
+
+	case STMT_ITERATOR: {
+		struct statement  *pre_statement = stmt->iterator_pre_statement;
+		struct expression *pre_condition = stmt->iterator_pre_condition;
+		struct statement  *statement = stmt->iterator_statement;
+		struct statement  *post_statement = stmt->iterator_post_statement;
+		struct expression *post_condition = stmt->iterator_post_condition;
+		int val, loop_top = 0, loop_bottom = 0;
+
+		show_symbol_decl(stmt->iterator_syms);
+		show_statement(pre_statement);
+		if (pre_condition) {
+			if (pre_condition->type == EXPR_VALUE) {
+				if (!pre_condition->value) {
+					loop_bottom = new_label();   
+					printf("\tjmp\t\t.L%d\n", loop_bottom);
+				}
+			} else {
+				loop_bottom = new_label();
+				val = show_expression(pre_condition);
+				printf("\tje\t\tv%d, .L%d\n", val, loop_bottom);
+			}
+		}
+		if (!post_condition || post_condition->type != EXPR_VALUE || post_condition->value) {
+			loop_top = new_label();
+			printf(".L%d:\n", loop_top);
+		}
+		show_statement(statement);
+		if (stmt->iterator_continue->used)
+			printf(".L%p:\n", stmt->iterator_continue);
+		show_statement(post_statement);
+		if (!post_condition) {
+			printf("\tjmp\t\t.L%d\n", loop_top);
+		} else if (post_condition->type == EXPR_VALUE) {
+			if (post_condition->value)
+				printf("\tjmp\t\t.L%d\n", loop_top);
+		} else {
+			val = show_expression(post_condition);
+			printf("\tjne\t\tv%d, .L%d\n", val, loop_top);
+		}
+		if (stmt->iterator_break->used)
+			printf(".L%p:\n", stmt->iterator_break);
+		if (loop_bottom)
+			printf(".L%d:\n", loop_bottom);
+		break;
+	}
+	case STMT_NONE:
+		break;
+	
+	case STMT_LABEL:
+		printf(".L%p:\n", stmt->label_identifier);
+		show_statement(stmt->label_statement);
+		break;
+
+	case STMT_GOTO:
+		if (stmt->goto_expression) {
+			int val = show_expression(stmt->goto_expression);
+			printf("\tgoto\t\t*v%d\n", val);
+		} else {
+			printf("\tgoto\t\t.L%p\n", stmt->goto_label->bb_target);
+		}
+		break;
+	case STMT_ASM:
+		printf("\tasm( .... )\n");
+		break;
+	case STMT_CONTEXT: {
+		int val = show_expression(stmt->expression);
+		printf("\tcontext( %d )\n", val);
+		break;
+	}
+	case STMT_RANGE: {
+		int val = show_expression(stmt->range_expression);
+		int low = show_expression(stmt->range_low);
+		int high = show_expression(stmt->range_high);
+		printf("\trange( %d %d-%d)\n", val, low, high); 
+		break;
+	}	
+	}
+	return 0;
+}
+
+static int show_call_expression(struct expression *expr)
+{
+	struct symbol *direct;
+	struct expression *arg, *fn;
+	int fncall, retval;
+	int framesize;
+
+	if (!expr->ctype) {
+		warning(expr->pos, "\tcall with no type!");
+		return 0;
+	}
+
+	framesize = 0;
+	FOR_EACH_PTR_REVERSE(expr->args, arg) {
+		int new = show_expression(arg);
+		int size = arg->ctype->bit_size;
+		printf("\tpush.%d\t\tv%d\n", size, new);
+		framesize += bits_to_bytes(size);
+	} END_FOR_EACH_PTR_REVERSE(arg);
+
+	fn = expr->fn;
+
+	/* Remove dereference, if any */
+	direct = NULL;
+	if (fn->type == EXPR_PREOP) {
+		if (fn->unop->type == EXPR_SYMBOL) {
+			struct symbol *sym = fn->unop->symbol;
+			if (sym->ctype.base_type->type == SYM_FN)
+				direct = sym;
+		}
+	}
+	if (direct) {
+		printf("\tcall\t\t%s\n", show_ident(direct->ident));
+	} else {
+		fncall = show_expression(fn);
+		printf("\tcall\t\t*v%d\n", fncall);
+	}
+	if (framesize)
+		printf("\tadd.%d\t\tvSP,vSP,$%d\n", bits_in_pointer, framesize);
+
+	retval = new_pseudo();
+	printf("\tmov.%d\t\tv%d,retval\n", expr->ctype->bit_size, retval);
+	return retval;
+}
+
+static int show_comma(struct expression *expr)
+{
+	show_expression(expr->left);
+	return show_expression(expr->right);
+}
+
+static int show_binop(struct expression *expr)
+{
+	int left = show_expression(expr->left);
+	int right = show_expression(expr->right);
+	int new = new_pseudo();
+	const char *opname;
+	static const char *name[] = {
+		['+'] = "add", ['-'] = "sub",
+		['*'] = "mul", ['/'] = "div",
+		['%'] = "mod", ['&'] = "and",
+		['|'] = "lor", ['^'] = "xor"
+	};
+	unsigned int op = expr->op;
+
+	opname = show_special(op);
+	if (op < ARRAY_SIZE(name))
+		opname = name[op];
+	printf("\t%s.%d\t\tv%d,v%d,v%d\n", opname,
+		expr->ctype->bit_size,
+		new, left, right);
+	return new;
+}
+
+static int show_slice(struct expression *expr)
+{
+	int target = show_expression(expr->base);
+	int new = new_pseudo();
+	printf("\tslice.%d\t\tv%d,v%d,%d\n", expr->r_nrbits, target, new, expr->r_bitpos);
+	return new;
+}
+
+static int show_regular_preop(struct expression *expr)
+{
+	int target = show_expression(expr->unop);
+	int new = new_pseudo();
+	static const char *name[] = {
+		['!'] = "nonzero", ['-'] = "neg",
+		['~'] = "not",
+	};
+	unsigned int op = expr->op;
+	const char *opname;
+
+	opname = show_special(op);
+	if (op < ARRAY_SIZE(name))
+		opname = name[op];
+	printf("\t%s.%d\t\tv%d,v%d\n", opname, expr->ctype->bit_size, new, target);
+	return new;
+}
+
+/*
+ * FIXME! Not all accesses are memory loads. We should
+ * check what kind of symbol is behind the dereference.
+ */
+static int show_address_gen(struct expression *expr)
+{
+	return show_expression(expr->unop);
+}
+
+static int show_load_gen(int bits, struct expression *expr, int addr)
+{
+	int new = new_pseudo();
+
+	printf("\tld.%d\t\tv%d,[v%d]\n", bits, new, addr);
+	return new;
+}
+
+static void show_store_gen(int bits, int value, struct expression *expr, int addr)
+{
+	/* FIXME!!! Bitfield store! */
+	printf("\tst.%d\t\tv%d,[v%d]\n", bits, value, addr);
+}
+
+static int show_assignment(struct expression *expr)
+{
+	struct expression *target = expr->left;
+	int val, addr, bits;
+
+	if (!expr->ctype)
+		return 0;
+
+	bits = expr->ctype->bit_size;
+	val = show_expression(expr->right);
+	addr = show_address_gen(target);
+	show_store_gen(bits, val, target, addr);
+	return val;
+}
+
+static int show_return_stmt(struct statement *stmt)
+{
+	struct expression *expr = stmt->ret_value;
+	struct symbol *target = stmt->ret_target;
+
+	if (expr && expr->ctype) {
+		int val = show_expression(expr);
+		int bits = expr->ctype->bit_size;
+		int addr = show_symbol_expr(target);
+		show_store_gen(bits, val, NULL, addr);
+	}
+	printf("\tret\t\t(%p)\n", target);
+	return 0;
+}
+
+static int show_initialization(struct symbol *sym, struct expression *expr)
+{
+	int val, addr, bits;
+
+	if (!expr->ctype)
+		return 0;
+
+	bits = expr->ctype->bit_size;
+	val = show_expression(expr);
+	addr = show_symbol_expr(sym);
+	// FIXME! The "target" expression is for bitfield store information.
+	// Leave it NULL, which works fine.
+	show_store_gen(bits, val, NULL, addr);
+	return 0;
+}
+
+static int show_access(struct expression *expr)
+{
+	int addr = show_address_gen(expr);
+	return show_load_gen(expr->ctype->bit_size, expr, addr);
+}
+
+static int show_inc_dec(struct expression *expr, int postop)
+{
+	int addr = show_address_gen(expr->unop);
+	int retval, new;
+	const char *opname = expr->op == SPECIAL_INCREMENT ? "add" : "sub";
+	int bits = expr->ctype->bit_size;
+
+	retval = show_load_gen(bits, expr->unop, addr);
+	new = retval;
+	if (postop)
+		new = new_pseudo();
+	printf("\t%s.%d\t\tv%d,v%d,$1\n", opname, bits, new, retval);
+	show_store_gen(bits, new, expr->unop, addr);
+	return retval;
+}	
+
+static int show_preop(struct expression *expr)
+{
+	/*
+	 * '*' is an lvalue access, and is fundamentally different
+	 * from an arithmetic operation. Maybe it should have an
+	 * expression type of its own..
+	 */
+	if (expr->op == '*')
+		return show_access(expr);
+	if (expr->op == SPECIAL_INCREMENT || expr->op == SPECIAL_DECREMENT)
+		return show_inc_dec(expr, 0);
+	return show_regular_preop(expr);
+}
+
+static int show_postop(struct expression *expr)
+{
+	return show_inc_dec(expr, 1);
+}	
+
+static int show_symbol_expr(struct symbol *sym)
+{
+	int new = new_pseudo();
+
+	if (sym->initializer && sym->initializer->type == EXPR_STRING)
+		return show_string_expr(sym->initializer);
+
+	if (sym->ctype.modifiers & (MOD_TOPLEVEL | MOD_EXTERN | MOD_STATIC)) {
+		printf("\tmovi.%d\t\tv%d,$%s\n", bits_in_pointer, new, show_ident(sym->ident));
+		return new;
+	}
+	if (sym->ctype.modifiers & MOD_ADDRESSABLE) {
+		printf("\taddi.%d\t\tv%d,vFP,$%lld\n", bits_in_pointer, new, sym->value);
+		return new;
+	}
+	printf("\taddi.%d\t\tv%d,vFP,$offsetof(%s:%p)\n", bits_in_pointer, new, show_ident(sym->ident), sym);
+	return new;
+}
+
+static int show_symbol_init(struct symbol *sym)
+{
+	struct expression *expr = sym->initializer;
+
+	if (expr) {
+		int val, addr, bits;
+
+		bits = expr->ctype->bit_size;
+		val = show_expression(expr);
+		addr = show_symbol_expr(sym);
+		show_store_gen(bits, val, NULL, addr);
+	}
+	return 0;
+}
+
+static int type_is_signed(struct symbol *sym)
+{
+	if (sym->type == SYM_NODE)
+		sym = sym->ctype.base_type;
+	if (sym->type == SYM_PTR)
+		return 0;
+	return !(sym->ctype.modifiers & MOD_UNSIGNED);
+}
+
+static int show_cast_expr(struct expression *expr)
+{
+	struct symbol *old_type, *new_type;
+	int op = show_expression(expr->cast_expression);
+	int oldbits, newbits;
+	int new, is_signed;
+
+	old_type = expr->cast_expression->ctype;
+	new_type = expr->cast_type;
+	
+	oldbits = old_type->bit_size;
+	newbits = new_type->bit_size;
+	if (oldbits >= newbits)
+		return op;
+	new = new_pseudo();
+	is_signed = type_is_signed(old_type);
+	if (is_signed) {
+		printf("\tsext%d.%d\tv%d,v%d\n", oldbits, newbits, new, op);
+	} else {
+		printf("\tandl.%d\t\tv%d,v%d,$%lu\n", newbits, new, op, (1UL << oldbits)-1);
+	}
+	return new;
+}
+
+static int show_value(struct expression *expr)
+{
+	int new = new_pseudo();
+	unsigned long long value = expr->value;
+
+	printf("\tmovi.%d\t\tv%d,$%llu\n", expr->ctype->bit_size, new, value);
+	return new;
+}
+
+static int show_fvalue(struct expression *expr)
+{
+	int new = new_pseudo();
+	long double value = expr->fvalue;
+
+	printf("\tmovf.%d\t\tv%d,$%Lf\n", expr->ctype->bit_size, new, value);
+	return new;
+}
+
+static int show_string_expr(struct expression *expr)
+{
+	int new = new_pseudo();
+
+	printf("\tmovi.%d\t\tv%d,&%s\n", bits_in_pointer, new, show_string(expr->string));
+	return new;
+}
+
+static int show_label_expr(struct expression *expr)
+{
+	int new = new_pseudo();
+	printf("\tmovi.%d\t\tv%d,.L%p\n",bits_in_pointer, new, expr->label_symbol);
+	return new;
+}
+
+static int show_conditional_expr(struct expression *expr)
+{
+	int cond = show_expression(expr->conditional);
+	int true = show_expression(expr->cond_true);
+	int false = show_expression(expr->cond_false);
+	int new = new_pseudo();
+
+	printf("[v%d]\tcmov.%d\t\tv%d,v%d,v%d\n", cond, expr->ctype->bit_size, new, true, false);
+	return new;
+}
+
+static int show_statement_expr(struct expression *expr)
+{
+	return show_statement(expr->statement);
+}
+
+static int show_position_expr(struct expression *expr, struct symbol *base)
+{
+	int new = show_expression(expr->init_expr);
+	struct symbol *ctype = expr->init_expr->ctype;
+	int bit_offset;
+
+	bit_offset = ctype ? ctype->bit_offset : -1;
+
+	printf("\tinsert v%d at [%d:%d] of %s\n", new,
+		expr->init_offset, bit_offset,
+		show_ident(base->ident));
+	return 0;
+}
+
+static int show_initializer_expr(struct expression *expr, struct symbol *ctype)
+{
+	struct expression *entry;
+
+	FOR_EACH_PTR(expr->expr_list, entry) {
+
+again:
+		// Nested initializers have their positions already
+		// recursively calculated - just output them too
+		if (entry->type == EXPR_INITIALIZER) {
+			show_initializer_expr(entry, ctype);
+			continue;
+		}
+
+		// Initializer indexes and identifiers should
+		// have been evaluated to EXPR_POS
+		if (entry->type == EXPR_IDENTIFIER) {
+			printf(" AT '%s':\n", show_ident(entry->expr_ident));
+			entry = entry->ident_expression;
+			goto again;
+		}
+			
+		if (entry->type == EXPR_INDEX) {
+			printf(" AT '%d..%d:\n", entry->idx_from, entry->idx_to);
+			entry = entry->idx_expression;
+			goto again;
+		}
+		if (entry->type == EXPR_POS) {
+			show_position_expr(entry, ctype);
+			continue;
+		}
+		show_initialization(ctype, entry);
+	} END_FOR_EACH_PTR(entry);
+	return 0;
+}
+
+int show_symbol_expr_init(struct symbol *sym)
+{
+	struct expression *expr = sym->initializer;
+
+	if (expr)
+		show_expression(expr);
+	return show_symbol_expr(sym);
+}
+
+/*
+ * Print out an expression. Return the pseudo that contains the
+ * variable.
+ */
+int show_expression(struct expression *expr)
+{
+	if (!expr)
+		return 0;
+
+	if (!expr->ctype) {
+		struct position *pos = &expr->pos;
+		printf("\tno type at %s:%d:%d\n",
+			stream_name(pos->stream),
+			pos->line, pos->pos);
+		return 0;
+	}
+		
+	switch (expr->type) {
+	case EXPR_CALL:
+		return show_call_expression(expr);
+		
+	case EXPR_ASSIGNMENT:
+		return show_assignment(expr);
+
+	case EXPR_COMMA:
+		return show_comma(expr);
+	case EXPR_BINOP:
+	case EXPR_COMPARE:
+	case EXPR_LOGICAL:
+		return show_binop(expr);
+	case EXPR_PREOP:
+		return show_preop(expr);
+	case EXPR_POSTOP:
+		return show_postop(expr);
+	case EXPR_SYMBOL:
+		return show_symbol_expr(expr->symbol);
+	case EXPR_DEREF:
+	case EXPR_SIZEOF:
+	case EXPR_PTRSIZEOF:
+	case EXPR_ALIGNOF:
+	case EXPR_OFFSETOF:
+		warning(expr->pos, "invalid expression after evaluation");
+		return 0;
+	case EXPR_CAST:
+	case EXPR_FORCE_CAST:
+	case EXPR_IMPLIED_CAST:
+		return show_cast_expr(expr);
+	case EXPR_VALUE:
+		return show_value(expr);
+	case EXPR_FVALUE:
+		return show_fvalue(expr);
+	case EXPR_STRING:
+		return show_string_expr(expr);
+	case EXPR_INITIALIZER:
+		return show_initializer_expr(expr, expr->ctype);
+	case EXPR_SELECT:
+	case EXPR_CONDITIONAL:
+		return show_conditional_expr(expr);
+	case EXPR_STATEMENT:
+		return show_statement_expr(expr);
+	case EXPR_LABEL:
+		return show_label_expr(expr);
+	case EXPR_SLICE:
+		return show_slice(expr);
+
+	// None of these should exist as direct expressions: they are only
+	// valid as sub-expressions of initializers.
+	case EXPR_POS:
+		warning(expr->pos, "unable to show plain initializer position expression");
+		return 0;
+	case EXPR_IDENTIFIER:
+		warning(expr->pos, "unable to show identifier expression");
+		return 0;
+	case EXPR_INDEX:
+		warning(expr->pos, "unable to show index expression");
+		return 0;
+	case EXPR_TYPE:
+		warning(expr->pos, "unable to show type expression");
+		return 0;
+	}
+	return 0;
+}
diff --git a/deps/sparse/simplify.c b/deps/sparse/simplify.c
new file mode 100644
index 0000000..8200584
--- /dev/null
+++ b/deps/sparse/simplify.c
@@ -0,0 +1,950 @@
+/*
+ * Simplify - do instruction simplification before CSE
+ *
+ * Copyright (C) 2004 Linus Torvalds
+ */
+
+#include <assert.h>
+
+#include "parse.h"
+#include "expression.h"
+#include "linearize.h"
+#include "flow.h"
+
+/* Find the trivial parent for a phi-source */
+static struct basic_block *phi_parent(struct basic_block *source, pseudo_t pseudo)
+{
+	/* Can't go upwards if the pseudo is defined in the bb it came from.. */
+	if (pseudo->type == PSEUDO_REG) {
+		struct instruction *def = pseudo->def;
+		if (def->bb == source)
+			return source;
+	}
+	if (bb_list_size(source->children) != 1 || bb_list_size(source->parents) != 1)
+		return source;
+	return first_basic_block(source->parents);
+}
+
+static void clear_phi(struct instruction *insn)
+{
+	pseudo_t phi;
+
+	insn->bb = NULL;
+	FOR_EACH_PTR(insn->phi_list, phi) {
+		*THIS_ADDRESS(phi) = VOID;
+	} END_FOR_EACH_PTR(phi);
+}
+
+static int if_convert_phi(struct instruction *insn)
+{
+	pseudo_t array[3];
+	struct basic_block *parents[3];
+	struct basic_block *bb, *bb1, *bb2, *source;
+	struct instruction *br;
+	pseudo_t p1, p2;
+
+	bb = insn->bb;
+	if (linearize_ptr_list((struct ptr_list *)insn->phi_list, (void **)array, 3) != 2)
+		return 0;
+	if (linearize_ptr_list((struct ptr_list *)bb->parents, (void **)parents, 3) != 2)
+		return 0;
+	p1 = array[0]->def->src1;
+	bb1 = array[0]->def->bb;
+	p2 = array[1]->def->src1;
+	bb2 = array[1]->def->bb;
+
+	/* Only try the simple "direct parents" case */
+	if ((bb1 != parents[0] || bb2 != parents[1]) &&
+	    (bb1 != parents[1] || bb2 != parents[0]))
+		return 0;
+
+	/*
+	 * See if we can find a common source for this..
+	 */
+	source = phi_parent(bb1, p1);
+	if (source != phi_parent(bb2, p2))
+		return 0;
+
+	/*
+	 * Cool. We now know that 'source' is the exclusive
+	 * parent of both phi-nodes, so the exit at the
+	 * end of it fully determines which one it is, and
+	 * we can turn it into a select.
+	 *
+	 * HOWEVER, right now we only handle regular
+	 * conditional branches. No multijumps or computed
+	 * stuff. Verify that here.
+	 */
+	br = last_instruction(source->insns);
+	if (!br || br->opcode != OP_BR)
+		return 0;
+
+	assert(br->cond);
+	assert(br->bb_false);
+
+	/*
+	 * We're in business. Match up true/false with p1/p2.
+	 */
+	if (br->bb_true == bb2 || br->bb_false == bb1) {
+		pseudo_t p = p1;
+		p1 = p2;
+		p2 = p;
+	}
+
+	/*
+	 * OK, we can now replace that last
+	 *
+	 *	br cond, a, b
+	 *
+	 * with the sequence
+	 *
+	 *	setcc cond
+	 *	select pseudo, p1, p2
+	 *	br cond, a, b
+	 *
+	 * and remove the phi-node. If it then
+	 * turns out that 'a' or 'b' is entirely
+	 * empty (common case), and now no longer
+	 * a phi-source, we'll be able to simplify
+	 * the conditional branch too.
+	 */
+	insert_select(source, br, insn, p1, p2);
+	clear_phi(insn);
+	return REPEAT_CSE;
+}
+
+static int clean_up_phi(struct instruction *insn)
+{
+	pseudo_t phi;
+	struct instruction *last;
+	int same;
+
+	last = NULL;
+	same = 1;
+	FOR_EACH_PTR(insn->phi_list, phi) {
+		struct instruction *def;
+		if (phi == VOID)
+			continue;
+		def = phi->def;
+		if (def->src1 == VOID || !def->bb)
+			continue;
+		if (last) {
+			if (last->src1 != def->src1)
+				same = 0;
+			continue;
+		}
+		last = def;
+	} END_FOR_EACH_PTR(phi);
+
+	if (same) {
+		pseudo_t pseudo = last ? last->src1 : VOID;
+		convert_instruction_target(insn, pseudo);
+		clear_phi(insn);
+		return REPEAT_CSE;
+	}
+
+	return if_convert_phi(insn);
+}
+
+static int delete_pseudo_user_list_entry(struct pseudo_user_list **list, pseudo_t *entry, int count)
+{
+	struct pseudo_user *pu;
+
+	FOR_EACH_PTR(*list, pu) {
+		if (pu->userp == entry) {
+			DELETE_CURRENT_PTR(pu);
+			if (!--count)
+				goto out;
+		}
+	} END_FOR_EACH_PTR(pu);
+	assert(count <= 0);
+out:
+	pack_ptr_list((struct ptr_list **)list);
+	return count;
+}
+
+static inline void remove_usage(pseudo_t p, pseudo_t *usep)
+{
+	if (has_use_list(p)) {
+		delete_pseudo_user_list_entry(&p->users, usep, 1);
+		if (!p->users)
+			kill_instruction(p->def);
+	}
+}
+
+void kill_use(pseudo_t *usep)
+{
+	if (usep) {
+		pseudo_t p = *usep;
+		*usep = VOID;
+		remove_usage(p, usep);
+	}
+}
+
+void kill_instruction(struct instruction *insn)
+{
+	if (!insn || !insn->bb)
+		return;
+
+	switch (insn->opcode) {
+	case OP_BINARY ... OP_BINCMP_END:
+		insn->bb = NULL;
+		kill_use(&insn->src1);
+		kill_use(&insn->src2);
+		repeat_phase |= REPEAT_CSE;
+		return;
+
+	case OP_NOT: case OP_NEG:
+		insn->bb = NULL;
+		kill_use(&insn->src1);
+		repeat_phase |= REPEAT_CSE;
+		return;
+
+	case OP_PHI:
+		insn->bb = NULL;
+		repeat_phase |= REPEAT_CSE;
+		return;
+
+	case OP_SYMADDR:
+		insn->bb = NULL;
+		repeat_phase |= REPEAT_CSE | REPEAT_SYMBOL_CLEANUP;
+		return;
+
+	case OP_RANGE:
+		insn->bb = NULL;
+		repeat_phase |= REPEAT_CSE;
+		kill_use(&insn->src1);
+		kill_use(&insn->src2);
+		kill_use(&insn->src3);
+		return;
+	case OP_BR:
+		insn->bb = NULL;
+		repeat_phase |= REPEAT_CSE;
+		if (insn->cond)
+			kill_use(&insn->cond);
+		return;
+	}
+}
+
+/*
+ * Kill trivially dead instructions
+ */
+static int dead_insn(struct instruction *insn, pseudo_t *src1, pseudo_t *src2, pseudo_t *src3)
+{
+	struct pseudo_user *pu;
+	FOR_EACH_PTR(insn->target->users, pu) {
+		if (*pu->userp != VOID)
+			return 0;
+	} END_FOR_EACH_PTR(pu);
+
+	insn->bb = NULL;
+	kill_use(src1);
+	kill_use(src2);
+	kill_use(src3);
+	return REPEAT_CSE;
+}
+
+static inline int constant(pseudo_t pseudo)
+{
+	return pseudo->type == PSEUDO_VAL;
+}
+
+static int replace_with_pseudo(struct instruction *insn, pseudo_t pseudo)
+{
+	convert_instruction_target(insn, pseudo);
+	insn->bb = NULL;
+	return REPEAT_CSE;
+}
+
+static unsigned int value_size(long long value)
+{
+	value >>= 8;
+	if (!value)
+		return 8;
+	value >>= 8;
+	if (!value)
+		return 16;
+	value >>= 16;
+	if (!value)
+		return 32;
+	return 64;
+}
+
+/*
+ * Try to determine the maximum size of bits in a pseudo.
+ *
+ * Right now this only follow casts and constant values, but we
+ * could look at things like logical 'and' instructions etc.
+ */
+static unsigned int operand_size(struct instruction *insn, pseudo_t pseudo)
+{
+	unsigned int size = insn->size;
+
+	if (pseudo->type == PSEUDO_REG) {
+		struct instruction *src = pseudo->def;
+		if (src && src->opcode == OP_CAST && src->orig_type) {
+			unsigned int orig_size = src->orig_type->bit_size;
+			if (orig_size < size)
+				size = orig_size;
+		}
+	}
+	if (pseudo->type == PSEUDO_VAL) {
+		unsigned int orig_size = value_size(pseudo->value);
+		if (orig_size < size)
+			size = orig_size;
+	}
+	return size;
+}
+
+static int simplify_asr(struct instruction *insn, pseudo_t pseudo, long long value)
+{
+	unsigned int size = operand_size(insn, pseudo);
+
+	if (value >= size) {
+		warning(insn->pos, "right shift by bigger than source value");
+		return replace_with_pseudo(insn, value_pseudo(0));
+	}
+	if (!value)
+		return replace_with_pseudo(insn, pseudo);
+	return 0;
+}
+
+static int simplify_constant_rightside(struct instruction *insn)
+{
+	long long value = insn->src2->value;
+
+	switch (insn->opcode) {
+	case OP_SUB:
+		if (value) {
+			insn->opcode = OP_ADD;
+			insn->src2 = value_pseudo(-value);
+			return REPEAT_CSE;
+		}
+	/* Fall through */
+	case OP_ADD:
+	case OP_OR: case OP_XOR:
+	case OP_OR_BOOL:
+	case OP_SHL:
+	case OP_LSR:
+		if (!value)
+			return replace_with_pseudo(insn, insn->src1);
+		return 0;
+	case OP_ASR:
+		return simplify_asr(insn, insn->src1, value);
+
+	case OP_MULU: case OP_MULS:
+	case OP_AND_BOOL:
+		if (value == 1)
+			return replace_with_pseudo(insn, insn->src1);
+	/* Fall through */
+	case OP_AND:
+		if (!value)
+			return replace_with_pseudo(insn, insn->src2);
+		return 0;
+	}
+	return 0;
+}
+
+static int simplify_constant_leftside(struct instruction *insn)
+{
+	long long value = insn->src1->value;
+
+	switch (insn->opcode) {
+	case OP_ADD: case OP_OR: case OP_XOR:
+		if (!value)
+			return replace_with_pseudo(insn, insn->src2);
+		return 0;
+
+	case OP_SHL:
+	case OP_LSR: case OP_ASR:
+	case OP_AND:
+	case OP_MULU: case OP_MULS:
+		if (!value)
+			return replace_with_pseudo(insn, insn->src1);
+		return 0;
+	}
+	return 0;
+}
+
+static int simplify_constant_binop(struct instruction *insn)
+{
+	/* FIXME! Verify signs and sizes!! */
+	long long left = insn->src1->value;
+	long long right = insn->src2->value;
+	unsigned long long ul, ur;
+	long long res, mask, bits;
+
+	mask = 1ULL << (insn->size-1);
+	bits = mask | (mask-1);
+
+	if (left & mask)
+		left |= ~bits;
+	if (right & mask)
+		right |= ~bits;
+	ul = left & bits;
+	ur = right & bits;
+
+	switch (insn->opcode) {
+	case OP_ADD:
+		res = left + right;
+		break;
+	case OP_SUB:
+		res = left - right;
+		break;
+	case OP_MULU:
+		res = ul * ur;
+		break;
+	case OP_MULS:
+		res = left * right;
+		break;
+	case OP_DIVU:
+		if (!ur)
+			return 0;
+		res = ul / ur;
+		break;
+	case OP_DIVS:
+		if (!right)
+			return 0;
+		res = left / right;
+		break;
+	case OP_MODU:
+		if (!ur)
+			return 0;
+		res = ul % ur;
+		break;
+	case OP_MODS:
+		if (!right)
+			return 0;
+		res = left % right;
+		break;
+	case OP_SHL:
+		res = left << right;
+		break;
+	case OP_LSR:
+		res = ul >> ur;
+		break;
+	case OP_ASR:
+		res = left >> right;
+		break;
+       /* Logical */
+	case OP_AND:
+		res = left & right;
+		break;
+	case OP_OR:
+		res = left | right;
+		break;
+	case OP_XOR:
+		res = left ^ right;
+		break;
+	case OP_AND_BOOL:
+		res = left && right;
+		break;
+	case OP_OR_BOOL:
+		res = left || right;
+		break;
+			       
+	/* Binary comparison */
+	case OP_SET_EQ:
+		res = left == right;
+		break;
+	case OP_SET_NE:
+		res = left != right;
+		break;
+	case OP_SET_LE:
+		res = left <= right;
+		break;
+	case OP_SET_GE:
+		res = left >= right;
+		break;
+	case OP_SET_LT:
+		res = left < right;
+		break;
+	case OP_SET_GT:
+		res = left > right;
+		break;
+	case OP_SET_B:
+		res = ul < ur;
+		break;
+	case OP_SET_A:
+		res = ul > ur;
+		break;
+	case OP_SET_BE:
+		res = ul <= ur;
+		break;
+	case OP_SET_AE:
+		res = ul >= ur;
+		break;
+	default:
+		return 0;
+	}
+	res &= bits;
+
+	replace_with_pseudo(insn, value_pseudo(res));
+	return REPEAT_CSE;
+}
+
+static int simplify_binop(struct instruction *insn)
+{
+	if (dead_insn(insn, &insn->src1, &insn->src2, NULL))
+		return REPEAT_CSE;
+	if (constant(insn->src1)) {
+		if (constant(insn->src2))
+			return simplify_constant_binop(insn);
+		return simplify_constant_leftside(insn);
+	}
+	if (constant(insn->src2))
+		return simplify_constant_rightside(insn);
+	return 0;
+}
+
+static void switch_pseudo(struct instruction *insn1, pseudo_t *pp1, struct instruction *insn2, pseudo_t *pp2)
+{
+	pseudo_t p1 = *pp1, p2 = *pp2;
+
+	use_pseudo(insn1, p2, pp1);
+	use_pseudo(insn2, p1, pp2);
+	remove_usage(p1, pp1);
+	remove_usage(p2, pp2);
+}
+
+static int canonical_order(pseudo_t p1, pseudo_t p2)
+{
+	/* symbol/constants on the right */
+	if (p1->type == PSEUDO_VAL)
+		return p2->type == PSEUDO_VAL;
+
+	if (p1->type == PSEUDO_SYM)
+		return p2->type == PSEUDO_SYM || p2->type == PSEUDO_VAL;
+
+	return 1;
+}
+
+static int simplify_commutative_binop(struct instruction *insn)
+{
+	if (!canonical_order(insn->src1, insn->src2)) {
+		switch_pseudo(insn, &insn->src1, insn, &insn->src2);
+		return REPEAT_CSE;
+	}
+	return 0;
+}
+
+static inline int simple_pseudo(pseudo_t pseudo)
+{
+	return pseudo->type == PSEUDO_VAL || pseudo->type == PSEUDO_SYM;
+}
+
+static int simplify_associative_binop(struct instruction *insn)
+{
+	struct instruction *def;
+	pseudo_t pseudo = insn->src1;
+
+	if (!simple_pseudo(insn->src2))
+		return 0;
+	if (pseudo->type != PSEUDO_REG)
+		return 0;
+	def = pseudo->def;
+	if (def == insn)
+		return 0;
+	if (def->opcode != insn->opcode)
+		return 0;
+	if (!simple_pseudo(def->src2))
+		return 0;
+	if (ptr_list_size((struct ptr_list *)def->target->users) != 1)
+		return 0;
+	switch_pseudo(def, &def->src1, insn, &insn->src2);
+	return REPEAT_CSE;
+}
+
+static int simplify_constant_unop(struct instruction *insn)
+{
+	long long val = insn->src1->value;
+	long long res, mask;
+
+	switch (insn->opcode) {
+	case OP_NOT:
+		res = ~val;
+		break;
+	case OP_NEG:
+		res = -val;
+		break;
+	default:
+		return 0;
+	}
+	mask = 1ULL << (insn->size-1);
+	res &= mask | (mask-1);
+	
+	replace_with_pseudo(insn, value_pseudo(res));
+	return REPEAT_CSE;
+}
+
+static int simplify_unop(struct instruction *insn)
+{
+	if (dead_insn(insn, &insn->src1, NULL, NULL))
+		return REPEAT_CSE;
+	if (constant(insn->src1))
+		return simplify_constant_unop(insn);
+	return 0;
+}
+
+static int simplify_one_memop(struct instruction *insn, pseudo_t orig)
+{
+	pseudo_t addr = insn->src;
+	pseudo_t new, off;
+
+	if (addr->type == PSEUDO_REG) {
+		struct instruction *def = addr->def;
+		if (def->opcode == OP_SYMADDR && def->src) {
+			kill_use(&insn->src);
+			use_pseudo(insn, def->src, &insn->src);
+			return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP;
+		}
+		if (def->opcode == OP_ADD) {
+			new = def->src1;
+			off = def->src2;
+			if (constant(off))
+				goto offset;
+			new = off;
+			off = def->src1;
+			if (constant(off))
+				goto offset;
+			return 0;
+		}
+	}
+	return 0;
+
+offset:
+	/* Invalid code */
+	if (new == orig) {
+		if (new == VOID)
+			return 0;
+		new = VOID;
+		warning(insn->pos, "crazy programmer");
+	}
+	insn->offset += off->value;
+	use_pseudo(insn, new, &insn->src);
+	remove_usage(addr, &insn->src);
+	return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP;
+}
+
+/*
+ * We walk the whole chain of adds/subs backwards. That's not
+ * only more efficient, but it allows us to find loops.
+ */
+static int simplify_memop(struct instruction *insn)
+{
+	int one, ret = 0;
+	pseudo_t orig = insn->src;
+
+	do {
+		one = simplify_one_memop(insn, orig);
+		ret |= one;
+	} while (one);
+	return ret;
+}
+
+static long long get_cast_value(long long val, int old_size, int new_size, int sign)
+{
+	long long mask;
+
+	if (sign && new_size > old_size) {
+		mask = 1 << (old_size-1);
+		if (val & mask)
+			val |= ~(mask | (mask-1));
+	}
+	mask = 1 << (new_size-1);
+	return val & (mask | (mask-1));
+}
+
+static int simplify_cast(struct instruction *insn)
+{
+	struct symbol *orig_type;
+	int orig_size, size;
+	pseudo_t src;
+
+	if (dead_insn(insn, &insn->src, NULL, NULL))
+		return REPEAT_CSE;
+
+	orig_type = insn->orig_type;
+	if (!orig_type)
+		return 0;
+	orig_size = orig_type->bit_size;
+	size = insn->size;
+	src = insn->src;
+
+	/* A cast of a constant? */
+	if (constant(src)) {
+		int sign = orig_type->ctype.modifiers & MOD_SIGNED;
+		long long val = get_cast_value(src->value, orig_size, size, sign);
+		src = value_pseudo(val);
+		goto simplify;
+	}
+
+	/* A cast of a "and" might be a no-op.. */
+	if (src->type == PSEUDO_REG) {
+		struct instruction *def = src->def;
+		if (def->opcode == OP_AND && def->size >= size) {
+			pseudo_t val = def->src2;
+			if (val->type == PSEUDO_VAL) {
+				unsigned long long value = val->value;
+				if (!(value >> (size-1)))
+					goto simplify;
+			}
+		}
+	}
+
+	if (size == orig_size) {
+		int op = (orig_type->ctype.modifiers & MOD_SIGNED) ? OP_SCAST : OP_CAST;
+		if (insn->opcode == op)
+			goto simplify;
+	}
+
+	return 0;
+
+simplify:
+	return replace_with_pseudo(insn, src);
+}
+
+static int simplify_select(struct instruction *insn)
+{
+	pseudo_t cond, src1, src2;
+
+	if (dead_insn(insn, &insn->src1, &insn->src2, &insn->src3))
+		return REPEAT_CSE;
+
+	cond = insn->src1;
+	src1 = insn->src2;
+	src2 = insn->src3;
+	if (constant(cond) || src1 == src2) {
+		pseudo_t *kill, take;
+		kill_use(&insn->src1);
+		take = cond->value ? src1 : src2;
+		kill = cond->value ? &insn->src3 : &insn->src2;
+		kill_use(kill);
+		replace_with_pseudo(insn, take);
+		return REPEAT_CSE;
+	}
+	if (constant(src1) && constant(src2)) {
+		long long val1 = src1->value;
+		long long val2 = src2->value;
+
+		/* The pair 0/1 is special - replace with SETNE/SETEQ */
+		if ((val1 | val2) == 1) {
+			int opcode = OP_SET_EQ;
+			if (val1) {
+				src1 = src2;
+				opcode = OP_SET_NE;
+			}
+			insn->opcode = opcode;
+			/* insn->src1 is already cond */
+			insn->src2 = src1; /* Zero */
+			return REPEAT_CSE;
+		}
+	}
+	return 0;
+}
+
+static int is_in_range(pseudo_t src, long long low, long long high)
+{
+	long long value;
+
+	switch (src->type) {
+	case PSEUDO_VAL:
+		value = src->value;
+		return value >= low && value <= high;
+	default:
+		return 0;
+	}
+}
+
+static int simplify_range(struct instruction *insn)
+{
+	pseudo_t src1, src2, src3;
+
+	src1 = insn->src1;
+	src2 = insn->src2;
+	src3 = insn->src3;
+	if (src2->type != PSEUDO_VAL || src3->type != PSEUDO_VAL)
+		return 0;
+	if (is_in_range(src1, src2->value, src3->value)) {
+		kill_instruction(insn);
+		return REPEAT_CSE;
+	}
+	return 0;
+}
+
+/*
+ * Simplify "set_ne/eq $0 + br"
+ */
+static int simplify_cond_branch(struct instruction *br, pseudo_t cond, struct instruction *def, pseudo_t *pp)
+{
+	use_pseudo(br, *pp, &br->cond);
+	remove_usage(cond, &br->cond);
+	if (def->opcode == OP_SET_EQ) {
+		struct basic_block *true = br->bb_true;
+		struct basic_block *false = br->bb_false;
+		br->bb_false = true;
+		br->bb_true = false;
+	}
+	return REPEAT_CSE;
+}
+
+static int simplify_branch(struct instruction *insn)
+{
+	pseudo_t cond = insn->cond;
+
+	if (!cond)
+		return 0;
+
+	/* Constant conditional */
+	if (constant(cond)) {
+		insert_branch(insn->bb, insn, cond->value ? insn->bb_true : insn->bb_false);
+		return REPEAT_CSE;
+	}
+
+	/* Same target? */
+	if (insn->bb_true == insn->bb_false) {
+		struct basic_block *bb = insn->bb;
+		struct basic_block *target = insn->bb_false;
+		remove_bb_from_list(&target->parents, bb, 1);
+		remove_bb_from_list(&bb->children, target, 1);
+		insn->bb_false = NULL;
+		kill_use(&insn->cond);
+		insn->cond = NULL;
+		return REPEAT_CSE;
+	}
+
+	/* Conditional on a SETNE $0 or SETEQ $0 */
+	if (cond->type == PSEUDO_REG) {
+		struct instruction *def = cond->def;
+
+		if (def->opcode == OP_SET_NE || def->opcode == OP_SET_EQ) {
+			if (constant(def->src1) && !def->src1->value)
+				return simplify_cond_branch(insn, cond, def, &def->src2);
+			if (constant(def->src2) && !def->src2->value)
+				return simplify_cond_branch(insn, cond, def, &def->src1);
+		}
+		if (def->opcode == OP_SEL) {
+			if (constant(def->src2) && constant(def->src3)) {
+				long long val1 = def->src2->value;
+				long long val2 = def->src3->value;
+				if (!val1 && !val2) {
+					insert_branch(insn->bb, insn, insn->bb_false);
+					return REPEAT_CSE;
+				}
+				if (val1 && val2) {
+					insert_branch(insn->bb, insn, insn->bb_true);
+					return REPEAT_CSE;
+				}
+				if (val2) {
+					struct basic_block *true = insn->bb_true;
+					struct basic_block *false = insn->bb_false;
+					insn->bb_false = true;
+					insn->bb_true = false;
+				}
+				use_pseudo(insn, def->src1, &insn->cond);
+				remove_usage(cond, &insn->cond);
+				return REPEAT_CSE;
+			}
+		}
+		if (def->opcode == OP_CAST || def->opcode == OP_SCAST) {
+			int orig_size = def->orig_type ? def->orig_type->bit_size : 0;
+			if (def->size > orig_size) {
+				use_pseudo(insn, def->src, &insn->cond);
+				remove_usage(cond, &insn->cond);
+				return REPEAT_CSE;
+			}
+		}
+	}
+	return 0;
+}
+
+static int simplify_switch(struct instruction *insn)
+{
+	pseudo_t cond = insn->cond;
+	long long val;
+	struct multijmp *jmp;
+
+	if (!constant(cond))
+		return 0;
+	val = insn->cond->value;
+
+	FOR_EACH_PTR(insn->multijmp_list, jmp) {
+		/* Default case */
+		if (jmp->begin > jmp->end)
+			goto found;
+		if (val >= jmp->begin && val <= jmp->end)
+			goto found;
+	} END_FOR_EACH_PTR(jmp);
+	warning(insn->pos, "Impossible case statement");
+	return 0;
+
+found:
+	insert_branch(insn->bb, insn, jmp->target);
+	return REPEAT_CSE;
+}
+
+int simplify_instruction(struct instruction *insn)
+{
+	if (!insn->bb)
+		return 0;
+	switch (insn->opcode) {
+	case OP_ADD: case OP_MULS:
+	case OP_AND: case OP_OR: case OP_XOR:
+	case OP_AND_BOOL: case OP_OR_BOOL:
+		if (simplify_binop(insn))
+			return REPEAT_CSE;
+		if (simplify_commutative_binop(insn))
+			return REPEAT_CSE;
+		return simplify_associative_binop(insn);
+
+	case OP_MULU:
+	case OP_SET_EQ: case OP_SET_NE:
+		if (simplify_binop(insn))
+			return REPEAT_CSE;
+		return simplify_commutative_binop(insn);
+
+	case OP_SUB:
+	case OP_DIVU: case OP_DIVS:
+	case OP_MODU: case OP_MODS:
+	case OP_SHL:
+	case OP_LSR: case OP_ASR:
+	case OP_SET_LE: case OP_SET_GE:
+	case OP_SET_LT: case OP_SET_GT:
+	case OP_SET_B:  case OP_SET_A:
+	case OP_SET_BE: case OP_SET_AE:
+		return simplify_binop(insn);
+
+	case OP_NOT: case OP_NEG:
+		return simplify_unop(insn);
+	case OP_LOAD: case OP_STORE:
+		return simplify_memop(insn);
+	case OP_SYMADDR:
+		if (dead_insn(insn, NULL, NULL, NULL))
+			return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP;
+		return replace_with_pseudo(insn, insn->symbol);
+	case OP_CAST:
+	case OP_SCAST:
+	case OP_FPCAST:
+	case OP_PTRCAST:
+		return simplify_cast(insn);
+	case OP_PHI:
+		if (dead_insn(insn, NULL, NULL, NULL)) {
+			clear_phi(insn);
+			return REPEAT_CSE;
+		}
+		return clean_up_phi(insn);
+	case OP_PHISOURCE:
+		if (dead_insn(insn, &insn->phi_src, NULL, NULL))
+			return REPEAT_CSE;
+		break;
+	case OP_SEL:
+		return simplify_select(insn);
+	case OP_BR:
+		return simplify_branch(insn);
+	case OP_SWITCH:
+		return simplify_switch(insn);
+	case OP_RANGE:
+		return simplify_range(insn);
+	}
+	return 0;
+}
diff --git a/deps/sparse/sort.c b/deps/sparse/sort.c
new file mode 100644
index 0000000..afd7184
--- /dev/null
+++ b/deps/sparse/sort.c
@@ -0,0 +1,290 @@
+/*
+ * sort_list: a stable sort for lists.
+ *
+ * Time complexity: O(n*log n)
+ *   [assuming limited zero-element fragments]
+ *
+ * Space complexity: O(1).
+ *
+ * Stable: yes.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib.h"
+#include "allocate.h"
+
+#undef PARANOIA
+#undef COVERAGE
+
+#ifdef PARANOIA
+#include <assert.h>
+#else
+#define assert(x)
+#endif
+
+#ifdef COVERAGE
+static unsigned char been_there[256];
+#define BEEN_THERE(_c)					\
+  do {							\
+	if (!been_there[_c]) {				\
+		been_there[_c] = 1;			\
+		printf ("Been there: %c\n", _c);	\
+	}						\
+  } while (0)
+#else
+#define BEEN_THERE(_c) do { } while (0)
+#endif
+
+// Sort one fragment.  LIST_NODE_NR (==29) is a bit too high for my
+// taste for something this simple.  But, hey, it's O(1).
+//
+// I would use libc qsort for this, but its comparison function
+// gets a pointer indirection extra.
+static void array_sort(void **ptr, int nr, int (*cmp)(const void *, const void *))
+{
+	int i;
+	for (i = 1; i < nr; i++) {
+		void *p = ptr[i];
+		if (cmp(ptr[i-1],p) > 0) {
+			int j = i;
+			do {
+				ptr[j] = ptr[j-1];
+				if (!--j)
+					break;
+			} while (cmp(ptr[j-1], p) > 0);
+			ptr[j] = p;
+		}
+	}
+}
+
+#ifdef PARANOIA
+static void verify_seq_sorted (struct ptr_list *l, int n,
+			       int (*cmp)(const void *, const void *))
+{
+	int i = 0;
+	const void *a;
+	struct ptr_list *head = l;
+
+	while (l->nr == 0) {
+		l = l->next;
+		if (--n == 0)
+			return;
+		assert (l != head);
+	}
+
+	a = l->list[0];
+	while (n > 0) {
+		const void *b;
+		if (++i >= l->nr) {
+			i = 0;
+			l = l->next;
+			n--;
+			assert (l != head || n == 0);
+			continue;
+		}
+		b = l->list[i];
+		assert (cmp (a, b) <= 0);
+		a = b;
+	}
+}
+#endif
+
+
+#define FLUSH_TO(b)						\
+  do {								\
+	int nr = (b)->nr;					\
+	assert (nbuf >= nr);					\
+	memcpy ((b)->list, buffer, nr * sizeof (void *));	\
+	nbuf -= nr;						\
+	memcpy (buffer, buffer + nr, nbuf * sizeof (void *));	\
+  } while (0)
+
+#define DUMP_TO(b)						\
+  do {								\
+        assert (nbuf <= (b)->nr);				\
+	memcpy ((b)->list, buffer, nbuf * sizeof (void *));	\
+  } while (0)
+
+
+// Merge two already-sorted sequences of blocks:
+//   (b1_1, ..., b1_n)  and  (b2_1, ..., b2_m)
+// Since we may be moving blocks around, we return the new head
+// of the merged list.
+static struct ptr_list *
+merge_block_seqs (struct ptr_list *b1, int n,
+		  struct ptr_list *b2, int m,
+		  int (*cmp)(const void *, const void *))
+{
+	int i1 = 0, i2 = 0;
+	const void *buffer[2 * LIST_NODE_NR];
+	int nbuf = 0;
+	struct ptr_list *newhead = b1;
+
+	// printf ("Merging %d blocks at %p with %d blocks at %p\n", n, b1, m, b2);
+
+	// Skip empty blocks in b2.
+	while (b2->nr == 0) {
+		BEEN_THERE('F');
+		b2 = b2->next;
+		if (--m == 0) {
+			BEEN_THERE('G');
+			return newhead;
+		}
+	}
+
+	// Do a quick skip in case entire blocks from b1 are
+	// already less than smallest element in b2.
+	while (b1->nr == 0 ||
+	       cmp (PTR_ENTRY(b1, b1->nr - 1), PTR_ENTRY(b2,0)) < 0) {
+		// printf ("Skipping whole block.\n");
+		BEEN_THERE('H');
+		b1 = b1->next;
+		if (--n == 0) {
+			BEEN_THERE('I');
+			return newhead;	
+		}
+	}
+
+	while (1) {
+		const void *d1 = PTR_ENTRY(b1,i1);
+		const void *d2 = PTR_ENTRY(b2,i2);
+
+		assert (i1 >= 0 && i1 < b1->nr);
+		assert (i2 >= 0 && i2 < b2->nr);
+		assert (b1 != b2);
+		assert (n > 0);
+		assert (m > 0);
+
+		if (cmp (d1, d2) <= 0) {
+			BEEN_THERE('J');
+			buffer[nbuf++] = d1;
+			// Element from b1 is smaller
+			if (++i1 >= b1->nr) {
+				BEEN_THERE('L');
+				FLUSH_TO(b1);
+				do {
+					b1 = b1->next;
+					if (--n == 0) {
+						BEEN_THERE('O');
+						while (b1 != b2) {
+							BEEN_THERE('P');
+							FLUSH_TO(b1);
+							b1 = b1->next;
+						}
+						assert (nbuf == i2);
+						DUMP_TO(b2);
+						return newhead;
+					}
+				} while (b1->nr == 0);
+				i1 = 0;
+			}
+		} else {
+			BEEN_THERE('K');
+			// Element from b2 is smaller
+			buffer[nbuf++] = d2;
+			if (++i2 >= b2->nr) {
+				struct ptr_list *l = b2;
+				BEEN_THERE('M');
+				// OK, we finished with b2.  Pull it out
+				// and plug it in before b1.
+
+				b2 = b2->next;
+				b2->prev = l->prev;
+				b2->prev->next = b2;
+				l->next = b1;
+				l->prev = b1->prev;
+				l->next->prev = l;
+				l->prev->next = l;
+
+				if (b1 == newhead) {
+					BEEN_THERE('N');
+					newhead = l;
+				}
+
+				FLUSH_TO(l);
+				b2 = b2->prev;
+				do {
+					b2 = b2->next;
+					if (--m == 0) {
+						BEEN_THERE('Q');
+						assert (nbuf == i1);
+						DUMP_TO(b1);
+						return newhead;
+					}
+				} while (b2->nr == 0);
+				i2 = 0;
+			}
+		}
+	}
+}
+
+
+void sort_list(struct ptr_list **plist, int (*cmp)(const void *, const void *))
+{
+	struct ptr_list *head = *plist, *list = head;
+	int blocks = 1;
+
+	if (!head)
+		return;
+
+	// Sort all the sub-lists
+	do {
+		array_sort(list->list, list->nr, cmp);
+#ifdef PARANOIA
+		verify_seq_sorted (list, 1, cmp);
+#endif
+		list = list->next;
+	} while (list != head);
+
+	// Merge the damn things together
+	while (1) {
+		struct ptr_list *block1 = head;
+
+		do {
+			struct ptr_list *block2 = block1;
+			struct ptr_list *next, *newhead;
+			int i;
+
+			for (i = 0; i < blocks; i++) {
+				block2 = block2->next;
+				if (block2 == head) {
+					if (block1 == head) {
+						BEEN_THERE('A');
+						*plist = head;
+						return;
+					}
+					BEEN_THERE('B');
+					goto next_pass;
+				}						
+			}
+
+			next = block2;
+			for (i = 0; i < blocks; ) {
+				next = next->next;
+				i++;
+				if (next == head) {
+					BEEN_THERE('C');
+					break;
+				}
+				BEEN_THERE('D');
+			}
+
+			newhead = merge_block_seqs (block1, blocks,
+						    block2, i,
+						    cmp);
+#ifdef PARANOIA
+			verify_seq_sorted (newhead, blocks + i, cmp);
+#endif
+			if (block1 == head) {
+				BEEN_THERE('E');
+				head = newhead;
+			}
+			block1 = next;
+		} while (block1 != head);
+	next_pass:
+		blocks <<= 1;
+	}
+}
diff --git a/deps/sparse/sparse.1 b/deps/sparse/sparse.1
new file mode 100644
index 0000000..bde6b6d
--- /dev/null
+++ b/deps/sparse/sparse.1
@@ -0,0 +1,317 @@
+.\" Sparse manpage by Josh Triplett
+.TH sparse "1"
+.
+.SH NAME
+sparse \- Semantic Parser for C
+.
+.SH SYNOPSIS
+.B sparse
+[\fIWARNING OPTIONS\fR]... \fIfile.c\fR
+.
+.SH DESCRIPTION
+Sparse parses C source and looks for errors, producing warnings on standard
+error.
+.P
+Sparse accepts options controlling the set of warnings to generate.  To turn
+on warnings Sparse does not issue by default, use the corresponding warning
+option \fB\-Wsomething\fR.  Sparse issues some warnings by default; to turn
+off those warnings, pass the negation of the associated warning option,
+\fB\-Wno\-something\fR.
+.
+.SH WARNING OPTIONS
+.TP
+.B \-Wsparse\-all
+Turn on all sparse warnings, except for those explicitly disabled via
+\fB\-Wno\-something\fR.
+.TP
+.B \-Waddress\-space
+Warn about code which mixes pointers to different address spaces.
+
+Sparse allows an extended attribute
+.BI __attribute__((address_space( num )))
+on pointers, which designates a pointer target in address space \fInum\fR (a
+constant integer).  With \fB\-Waddress\-space\fR, Sparse treats pointers with
+identical target types but different address spaces as distinct types.  To
+override this warning, such as for functions which convert pointers between
+address spaces, use a type that includes \fB__attribute__((force))\fR.
+
+Sparse issues these warnings by default.  To turn them off, use
+\fB\-Wno\-address\-space\fR.
+.
+.TP
+.B \-Wbitwise
+Warn about unsupported operations or type mismatches with restricted integer
+types.
+
+Sparse supports an extended attribute, \fB__attribute__((bitwise))\fR, which
+creates a new restricted integer type from a base integer type, distinct from
+the base integer type and from any other restricted integer type not declared
+in the same declaration or \fBtypedef\fR.  For example, this allows programs
+to create \fBtypedef\fRs for integer types with specific endianness.  With
+\fB-Wbitwise\fR, Sparse will warn on any use of a restricted type in
+arithmetic operations other than bitwise operations, and on any conversion of
+one restricted type into another, except via a cast that includes
+\fB__attribute__((force))\fR.
+
+Sparse does not issue these warnings by default.
+.
+.TP
+.B \-Wcast\-to\-as
+Warn about casts which add an address space to a pointer type.
+
+A cast that includes \fB__attribute__((force))\fR will suppress this warning.
+
+Sparse does not issue these warnings by default.
+.
+.TP
+.B \-Wcast\-truncate
+Warn about casts that truncate constant values.
+
+Sparse issues these warnings by default.  To turn them off, use
+\fB\-Wno\-cast\-truncate\fR.
+.
+.TP
+.B \-Wcontext
+Warn about potential errors in synchronization or other delimited contexts.
+
+Sparse supports several means of designating functions or statements that
+delimit contexts, such as synchronization.  Functions with the extended
+attribute
+.BI __attribute__((context( expression , in_context , out_context ))
+require the context \fIexpression\fR (for instance, a lock) to have the value
+\fIin_context\fR (a constant nonnegative integer) when called, and return with
+the value \fIout_context\fR (a constant nonnegative integer).  For APIs
+defined via macros, use the statement form
+.BI __context__( expression , in_value , out_value )
+in the body of the macro.
+
+With \fB-Wcontext\fR Sparse will warn when it sees a function change the
+context without indicating this with a \fBcontext\fR attribute, either by
+decreasing a context below zero (such as by releasing a lock without acquiring
+it), or returning with a changed context (such as by acquiring a lock without
+releasing it).  Sparse will also warn about blocks of code which may
+potentially execute with different contexts.
+
+Sparse issues these warnings by default.  To turn them off, use
+\fB\-Wno\-context\fR.
+.
+.TP
+.B \-Wdecl
+Warn about any non-\fBstatic\fR variable or function definition that has no
+previous declaration.
+
+Private symbols (functions and variables) internal to a given source file
+should use \fBstatic\fR, to allow additional compiler optimizations, allow
+detection of unused symbols, and prevent other code from relying on these
+internal symbols.  Public symbols used by other source files will need
+declarations visible to those other source files, such as in a header file.
+All declarations should fall into one of these two categories.  Thus, with
+\fB-Wdecl\fR, Sparse warns about any symbol definition with neither
+\fBstatic\fR nor a declaration.  To fix this warning, declare private symbols
+\fBstatic\fR, and ensure that the files defining public symbols have the
+symbol declarations available first (such as by including the appropriate
+header file).
+
+Sparse issues these warnings by default.  To turn them off, use
+\fB\-Wno\-decl\fR.
+.
+.TP
+.B \-Wdeclaration-after-statement
+Warn about declarations that are not at the start of a block.
+
+These declarations are permitted in C99 but not in C89.
+
+Sparse issues these warnings by default only when the C dialect is
+C89 (i.e. -ansi or -std=c89).  To turn them off, use
+\fB\-Wno\-declaration\-after\-statement\fR.
+.
+.TP
+.B \-Wdefault\-bitfield\-sign
+Warn about any bitfield with no explicit signedness.
+
+Bitfields have no standard-specified default signedness. (C99 6.7.2) A
+bitfield without an explicit \fBsigned\fR or \fBunsigned\fR creates a
+portability problem for software that relies on the available range of values.
+To fix this, specify the bitfield type as \fBsigned\fR or \fBunsigned\fR
+explicitly.
+
+Sparse does not issue these warnings by default.
+.
+.TP
+.B \-Wdesignated\-init
+Warn about positional initialization of structs marked as requiring designated
+initializers.
+
+Sparse allows an attribute
+.BI __attribute__((designated_init))
+which marks a struct as requiring designated initializers.  Sparse will warn
+about positional initialization of a struct variable or struct literal of a
+type that has this attribute.
+
+Requiring designated initializers for a particular struct type will insulate
+code using that struct type from changes to the layout of the type, avoiding
+the need to change initializers for that type unless they initialize a removed
+or incompatibly changed field.
+
+Common examples of this type of struct include collections of function pointers
+for the implementations of a class of related operations, for which the default
+NULL for an unmentioned field in a designated initializer will correctly
+indicate the absence of that operation.
+
+Sparse issues these warnings by default.  To turn them off, use
+\fB\-Wno\-designated\-init\fR.
+.
+.TP
+.B \-Wdo\-while
+Warn about do-while loops that do not delimit the loop body with braces.
+
+Sparse does not issue these warnings by default.
+.
+.TP
+.B \-Wenum\-mismatch
+Warn about the use of an expression of an incorrect \fBenum\fR type when
+initializing another \fBenum\fR type, assigning to another \fBenum\fR type, or
+passing an argument to a function which expects another \fBenum\fR type.
+
+Sparse issues these warnings by default.  To turn them off, use
+\fB\-Wno\-enum\-mismatch\fR.
+.
+.TP
+.B \-Wnon\-pointer\-null
+Warn about the use of 0 as a NULL pointer.
+
+0 has integer type.  NULL has pointer type.
+
+Sparse issues these warnings by default.  To turn them off, use
+\fB\-Wno\-non\-pointer\-null\fR.
+.
+.TP
+.B \-Wold\-initializer
+Warn about the use of the pre-C99 GCC syntax for designated initializers.
+
+C99 provides a standard syntax for designated fields in \fBstruct\fR or
+\fBunion\fR initializers:
+
+.nf
+struct structname var = { .field = value };
+.fi
+
+GCC also has an old, non-standard syntax for designated initializers which
+predates C99:
+
+.nf
+struct structname var = { field: value };
+.fi
+
+Sparse will warn about the use of GCC's non-standard syntax for designated
+initializers.  To fix this warning, convert designated initializers to use the
+standard C99 syntax.
+
+Sparse issues these warnings by default.  To turn them off, use
+\fB\-Wno\-old\-initializer\fR.
+.
+.TP
+.B \-Wone\-bit\-signed\-bitfield
+Warn about any one-bit \fBsigned\fR bitfields.
+
+A one-bit \fBsigned\fR bitfield can only have the values 0 and -1, or with
+some compilers only 0; this results in unexpected behavior for programs which
+expected the ability to store 0 and 1.
+
+Sparse issues these warnings by default.  To turn them off, use
+\fB\-Wno\-one\-bit\-signed\-bitfield\fR.
+.
+.TP
+.B \-Wparen\-string
+Warn about the use of a parenthesized string to initialize an array.
+
+Standard C syntax does not permit a parenthesized string as an array
+initializer.  GCC allows this syntax as an extension.  With
+\fB\-Wparen\-string\fR, Sparse will warn about this syntax.
+
+Sparse does not issue these warnings by default.
+.
+.TP
+.B \-Wptr\-subtraction\-blows
+Warn when subtracting two pointers to a type with a non-power-of-two size.
+
+Subtracting two pointers to a given type gives a difference in terms of the
+number of items of that type.  To generate this value, compilers will usually
+need to divide the difference by the size of the type, an potentially
+expensive operation for sizes other than powers of two.
+
+Code written using pointer subtraction can often use another approach instead,
+such as array indexing with an explicit array index variable, which may allow
+compilers to generate more efficient code.
+
+Sparse does not issue these warnings by default.
+.
+.TP
+.B \-Wreturn\-void
+Warn if a function with return type void returns a void expression.
+
+C99 permits this, and in some cases this allows for more generic code in
+macros that use typeof or take a type as a macro argument.  However, some
+programs consider this poor style, and those programs can use
+\fB\-Wreturn\-void\fR to get warnings about it.
+
+Sparse does not issue these warnings by default.
+.
+.TP
+.B \-Wshadow
+Warn when declaring a symbol which shadows a declaration with the same name in
+an outer scope.
+
+Such declarations can lead to error-prone code.
+
+Sparse does not issue these warnings by default.
+.
+.TP
+.B \-Wtransparent\-union
+Warn about any declaration using the GCC extension
+\fB__attribute__((transparent_union))\fR.
+
+Sparse issues these warnings by default.  To turn them off, use
+\fB\-Wno\-transparent\-union\fR.
+.
+.TP
+.B \-Wtypesign
+Warn when converting a pointer to an integer type into a pointer to an integer
+type with different signedness.
+
+Sparse does not issue these warnings by default.
+.
+.TP
+.B \-Wundef
+Warn about preprocessor conditionals that use the value of an undefined
+preprocessor symbol.
+
+Standard C (C99 6.10.1) permits using the value of an undefined preprocessor
+symbol in preprocessor conditionals, and specifies it has have a value of 0.
+However, this behavior can lead to subtle errors.
+
+Sparse does not issue these warnings by default.
+.
+.SH MISC OPTIONS
+.TP
+.B \-gcc-base-dir \fIdir\fR
+Look for compiler-provided system headers in \fIdir\fR/include/ and \fIdir\fR/include-fixed/.
+.
+.SH OTHER OPTIONS
+.TP
+.B \-ftabstop=WIDTH
+Set the distance between tab stops.  This helps sparse report correct
+column numbers in warnings or errors.  If the value is less than 1 or
+greater than 100, the option is ignored.  The default is 8.
+.
+.SH SEE ALSO
+.BR cgcc (1)
+.
+.SH HOMEPAGE
+http://www.kernel.org/pub/software/devel/sparse/
+.
+.SH MAILING LIST
+linux-sparse vger kernel org
+.
+.SH MAINTAINER
+Josh Triplett <josh kernel org>
diff --git a/deps/sparse/sparse.c b/deps/sparse/sparse.c
new file mode 100644
index 0000000..67b7d9e
--- /dev/null
+++ b/deps/sparse/sparse.c
@@ -0,0 +1,287 @@
+/*
+ * Example trivial client program that uses the sparse library
+ * to tokenize, preprocess and parse a C file, and prints out
+ * the results.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "expression.h"
+#include "linearize.h"
+
+static int context_increase(struct basic_block *bb, int entry)
+{
+	int sum = 0;
+	struct instruction *insn;
+
+	FOR_EACH_PTR(bb->insns, insn) {
+		int val;
+		if (insn->opcode != OP_CONTEXT)
+			continue;
+		val = insn->increment;
+		if (insn->check) {
+			int current = sum + entry;
+			if (!val) {
+				if (!current)
+					continue;
+			} else if (current >= val)
+				continue;
+			warning(insn->pos, "context check failure");
+			continue;
+		}
+		sum += val;
+	} END_FOR_EACH_PTR(insn);
+	return sum;
+}
+
+static int imbalance(struct entrypoint *ep, struct basic_block *bb, int entry, int exit, const char *why)
+{
+	if (Wcontext) {
+		struct symbol *sym = ep->name;
+		warning(bb->pos, "context imbalance in '%s' - %s", show_ident(sym->ident), why);
+	}
+	return -1;
+}
+
+static int check_bb_context(struct entrypoint *ep, struct basic_block *bb, int entry, int exit);
+
+static int check_children(struct entrypoint *ep, struct basic_block *bb, int entry, int exit)
+{
+	struct instruction *insn;
+	struct basic_block *child;
+
+	insn = last_instruction(bb->insns);
+	if (!insn)
+		return 0;
+	if (insn->opcode == OP_RET)
+		return entry != exit ? imbalance(ep, bb, entry, exit, "wrong count at exit") : 0;
+
+	FOR_EACH_PTR(bb->children, child) {
+		if (check_bb_context(ep, child, entry, exit))
+			return -1;
+	} END_FOR_EACH_PTR(child);
+	return 0;
+}
+
+static int check_bb_context(struct entrypoint *ep, struct basic_block *bb, int entry, int exit)
+{
+	if (!bb)
+		return 0;
+	if (bb->context == entry)
+		return 0;
+
+	/* Now that's not good.. */
+	if (bb->context >= 0)
+		return imbalance(ep, bb, entry, bb->context, "different lock contexts for basic block");
+
+	bb->context = entry;
+	entry += context_increase(bb, entry);
+	if (entry < 0)
+		return imbalance(ep, bb, entry, exit, "unexpected unlock");
+
+	return check_children(ep, bb, entry, exit);
+}
+
+static void check_cast_instruction(struct instruction *insn)
+{
+	struct symbol *orig_type = insn->orig_type;
+	if (orig_type) {
+		int old = orig_type->bit_size;
+		int new = insn->size;
+		int oldsigned = (orig_type->ctype.modifiers & MOD_SIGNED) != 0;
+		int newsigned = insn->opcode == OP_SCAST;
+
+		if (new > old) {
+			if (oldsigned == newsigned)
+				return;
+			if (newsigned)
+				return;
+			warning(insn->pos, "cast loses sign");
+			return;
+		}
+		if (new < old) {
+			warning(insn->pos, "cast drops bits");
+			return;
+		}
+		if (oldsigned == newsigned) {
+			warning(insn->pos, "cast wasn't removed");
+			return;
+		}
+		warning(insn->pos, "cast changes sign");
+	}
+}
+
+static void check_range_instruction(struct instruction *insn)
+{
+	warning(insn->pos, "value out of range");
+}
+
+static void check_byte_count(struct instruction *insn, pseudo_t count)
+{
+	if (!count)
+		return;
+	if (count->type == PSEUDO_VAL) {
+		long long val = count->value;
+		if (val <= 0 || val > 100000)
+			warning(insn->pos, "%s with byte count of %lld",
+				show_ident(insn->func->sym->ident), val);
+		return;
+	}
+	/* OK, we could try to do the range analysis here */
+}
+
+static pseudo_t argument(struct instruction *call, unsigned int argno)
+{
+	pseudo_t args[8];
+	struct ptr_list *arg_list = (struct ptr_list *) call->arguments;
+
+	argno--;
+	if (linearize_ptr_list(arg_list, (void *)args, 8) > argno)
+		return args[argno];
+	return NULL;
+}
+
+static void check_memset(struct instruction *insn)
+{
+	check_byte_count(insn, argument(insn, 3));
+}
+
+#define check_memcpy check_memset
+#define check_ctu check_memset
+#define check_cfu check_memset
+
+struct checkfn {
+	struct ident *id;
+	void (*check)(struct instruction *insn);
+};
+
+static void check_call_instruction(struct instruction *insn)
+{
+	pseudo_t fn = insn->func;
+	struct ident *ident;
+	static const struct checkfn check_fn[] = {
+		{ &memset_ident, check_memset },
+		{ &memcpy_ident, check_memcpy },
+		{ &copy_to_user_ident, check_ctu },
+		{ &copy_from_user_ident, check_cfu },
+	};
+	int i;
+
+	if (fn->type != PSEUDO_SYM)
+		return;
+	ident = fn->sym->ident;
+	if (!ident)
+		return;
+	for (i = 0; i < ARRAY_SIZE(check_fn); i++) {
+		if (check_fn[i].id != ident)
+			continue;
+		check_fn[i].check(insn);
+		break;
+	}
+}
+
+static void check_one_instruction(struct instruction *insn)
+{
+	switch (insn->opcode) {
+	case OP_CAST: case OP_SCAST:
+		if (verbose)
+			check_cast_instruction(insn);
+		break;
+	case OP_RANGE:
+		check_range_instruction(insn);
+		break;
+	case OP_CALL:
+		check_call_instruction(insn);
+		break;
+	default:
+		break;
+	}
+}
+
+static void check_bb_instructions(struct basic_block *bb)
+{
+	struct instruction *insn;
+	FOR_EACH_PTR(bb->insns, insn) {
+		if (!insn->bb)
+			continue;
+		check_one_instruction(insn);
+	} END_FOR_EACH_PTR(insn);
+}
+
+static void check_instructions(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+	FOR_EACH_PTR(ep->bbs, bb) {
+		check_bb_instructions(bb);
+	} END_FOR_EACH_PTR(bb);
+}
+
+static void check_context(struct entrypoint *ep)
+{
+	struct symbol *sym = ep->name;
+	struct context *context;
+	unsigned int in_context = 0, out_context = 0;
+
+	if (Wuninitialized && verbose && ep->entry->bb->needs) {
+		pseudo_t pseudo;
+		FOR_EACH_PTR(ep->entry->bb->needs, pseudo) {
+			if (pseudo->type != PSEUDO_ARG)
+				warning(sym->pos, "%s: possible uninitialized variable (%s)",
+					show_ident(sym->ident), show_pseudo(pseudo));
+		} END_FOR_EACH_PTR(pseudo);
+	}
+
+	check_instructions(ep);
+
+	FOR_EACH_PTR(sym->ctype.contexts, context) {
+		in_context += context->in;
+		out_context += context->out;
+	} END_FOR_EACH_PTR(context);
+	check_bb_context(ep, ep->entry->bb, in_context, out_context);
+}
+
+static void check_symbols(struct symbol_list *list)
+{
+	struct symbol *sym;
+
+	FOR_EACH_PTR(list, sym) {
+		struct entrypoint *ep;
+
+		expand_symbol(sym);
+		ep = linearize_symbol(sym);
+		if (ep) {
+			if (dbg_entry)
+				show_entry(ep);
+
+			check_context(ep);
+		}
+	} END_FOR_EACH_PTR(sym);
+}
+
+int main(int argc, char **argv)
+{
+	struct string_list *filelist = NULL;
+	char *file;
+
+	// Expand, linearize and show it.
+	check_symbols(sparse_initialize(argc, argv, &filelist));
+	FOR_EACH_PTR_NOTAG(filelist, file) {
+		check_symbols(sparse(file));
+	} END_FOR_EACH_PTR_NOTAG(file);
+	return 0;
+}
diff --git a/deps/sparse/sparse.pc.in b/deps/sparse/sparse.pc.in
new file mode 100644
index 0000000..f1281c9
--- /dev/null
+++ b/deps/sparse/sparse.pc.in
@@ -0,0 +1,9 @@
+prefix= prefix@
+libdir= libdir@
+includedir= includedir@
+
+Name: Sparse
+Description: Semantic parser for C
+Version: @version@
+Libs: -L${libdir} -lsparse
+Cflags: -I${includedir}
diff --git a/deps/sparse/storage.c b/deps/sparse/storage.c
new file mode 100644
index 0000000..acbc477
--- /dev/null
+++ b/deps/sparse/storage.c
@@ -0,0 +1,307 @@
+/*
+ * Storage - associate pseudos with "storage" that keeps them alive
+ * between basic blocks.  The aim is to be able to turn as much of
+ * the global storage allocation problem as possible into a local
+ * per-basic-block one.
+ *
+ * Copyright (C) 2004 Linus Torvalds
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "symbol.h"
+#include "expression.h"
+#include "linearize.h"
+#include "storage.h"
+
+ALLOCATOR(storage, "storages");
+ALLOCATOR(storage_hash, "storage hash");
+
+#define MAX_STORAGE_HASH 64
+static struct storage_hash_list *storage_hash_table[MAX_STORAGE_HASH];
+
+static inline unsigned int storage_hash(struct basic_block *bb, pseudo_t pseudo, enum inout_enum inout)
+{
+	unsigned hash = hashval(bb) + hashval(pseudo) + hashval(inout);
+	hash += hash / MAX_STORAGE_HASH;
+	return hash & (MAX_STORAGE_HASH-1);
+}
+
+static int hash_list_cmp(const void *_a, const void *_b)
+{
+	const struct storage_hash *a = _a;
+	const struct storage_hash *b = _b;
+	if (a->pseudo != b->pseudo)
+		return a->pseudo < b->pseudo ? -1 : 1;
+	return 0;
+}
+
+static void sort_hash_list(struct storage_hash_list **listp)
+{
+	sort_list((struct ptr_list **)listp, hash_list_cmp);
+}
+
+struct storage_hash_list *gather_storage(struct basic_block *bb, enum inout_enum inout)
+{
+	int i;
+	struct storage_hash *entry, *prev;
+	struct storage_hash_list *list = NULL;
+
+	for (i = 0; i < MAX_STORAGE_HASH; i++) {
+		struct storage_hash *hash;
+		FOR_EACH_PTR(storage_hash_table[i], hash) {
+			if (hash->bb == bb && hash->inout == inout)
+				add_ptr_list(&list, hash);
+		} END_FOR_EACH_PTR(hash);
+	}
+	sort_hash_list(&list);
+
+	prev = NULL;
+	FOR_EACH_PTR(list, entry) {
+		if (prev && entry->pseudo == prev->pseudo) {
+			assert(entry == prev);
+			DELETE_CURRENT_PTR(entry);
+		}
+		prev = entry;
+	} END_FOR_EACH_PTR(entry);
+	PACK_PTR_LIST(&list);
+	return list;
+}
+
+static void name_storage(void)
+{
+	int i;
+	int name = 0;
+
+	for (i = 0; i < MAX_STORAGE_HASH; i++) {
+		struct storage_hash *hash;
+		FOR_EACH_PTR(storage_hash_table[i], hash) {
+			struct storage *storage = hash->storage;
+			if (storage->name)
+				continue;
+			storage->name = ++name;
+		} END_FOR_EACH_PTR(hash);
+	}
+}
+
+struct storage *lookup_storage(struct basic_block *bb, pseudo_t pseudo, enum inout_enum inout)
+{
+	struct storage_hash_list *list = storage_hash_table[storage_hash(bb,pseudo,inout)];
+	struct storage_hash *hash;
+
+	FOR_EACH_PTR(list, hash) {
+		if (hash->bb == bb && hash->pseudo == pseudo && hash->inout == inout)
+			return hash->storage;
+	} END_FOR_EACH_PTR(hash);
+	return NULL;
+}
+
+void add_storage(struct storage *storage, struct basic_block *bb, pseudo_t pseudo, enum inout_enum inout)
+{
+	struct storage_hash_list **listp = storage_hash_table + storage_hash(bb,pseudo,inout);
+	struct storage_hash *hash = alloc_storage_hash(storage);
+
+	hash->bb = bb;
+	hash->pseudo = pseudo;
+	hash->inout = inout;
+
+	add_ptr_list(listp, hash);
+}
+
+
+static int storage_hash_cmp(const void *_a, const void *_b)
+{
+	const struct storage_hash *a = _a;
+	const struct storage_hash *b = _b;
+	struct storage *aa = a->storage;
+	struct storage *bb = b->storage;
+
+	if (a->bb != b->bb)
+		return a->bb < b->bb ? -1 : 1;
+	if (a->inout != b->inout)
+		return a->inout < b->inout ? -1 : 1;
+	if (aa->type != bb->type)
+		return aa->type < bb->type ? -1 : 1;
+	if (aa->regno != bb->regno)
+		return aa->regno < bb->regno ? -1 : 1;
+	return 0;
+}
+
+static void vrfy_storage(struct storage_hash_list **listp)
+{
+	struct storage_hash *entry, *last;
+
+	sort_list((struct ptr_list **)listp, storage_hash_cmp);
+	last = NULL;
+	FOR_EACH_PTR(*listp, entry) {
+		if (last) {
+			struct storage *a = last->storage;
+			struct storage *b = entry->storage;
+			if (a == b)
+				continue;
+			if (last->bb == entry->bb
+			    && last->inout == entry->inout
+			    && a->type != REG_UDEF
+			    && a->type == b->type
+			    && a->regno == b->regno) {
+				printf("\t BAD: same storage as %s in %p: %s (%s and %s)\n",
+					last->inout == STOR_IN ? "input" : "output",
+					last->bb,
+					show_storage(a),
+					show_pseudo(last->pseudo),
+					show_pseudo(entry->pseudo));
+			}
+		}
+		last = entry;
+	} END_FOR_EACH_PTR(entry);
+}
+
+void free_storage(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_STORAGE_HASH; i++) {
+		vrfy_storage(storage_hash_table + i);
+		free_ptr_list(storage_hash_table + i);
+	}
+}
+
+const char *show_storage(struct storage *s)
+{
+	static char buffer[1024];
+	if (!s)
+		return "none";
+	switch (s->type) {
+	case REG_REG:
+		sprintf(buffer, "reg%d (%d)", s->regno, s->name);
+		break;
+	case REG_STACK:
+		sprintf(buffer, "%d(SP) (%d)", s->offset, s->name);
+		break;
+	case REG_ARG:
+		sprintf(buffer, "ARG%d (%d)", s->regno, s->name);
+		break;
+	default:
+		sprintf(buffer, "%d:%d (%d)", s->type, s->regno, s->name);
+		break;
+	}
+	return buffer;
+}
+
+/*
+ * Combine two storage allocations into one.
+ *
+ * We just randomly pick one over the other, and replace
+ * the other uses.
+ */
+static struct storage * combine_storage(struct storage *src, struct storage *dst)
+{
+	struct storage **usep;
+
+	/* Remove uses of "src_storage", replace with "dst" */
+	FOR_EACH_PTR(src->users, usep) {
+		assert(*usep == src);
+		*usep = dst;
+		add_ptr_list(&dst->users, usep);
+	} END_FOR_EACH_PTR(usep);
+
+	/* Mark it unused */
+	src->type = REG_BAD;
+	src->users = NULL;
+	return dst;
+}
+
+static void set_up_bb_storage(struct basic_block *bb)
+{
+	struct basic_block *child;
+
+	FOR_EACH_PTR(bb->children, child) {
+		pseudo_t pseudo;
+		FOR_EACH_PTR(child->needs, pseudo) {
+			struct storage *child_in, *parent_out;
+
+			parent_out = lookup_storage(bb, pseudo, STOR_OUT);
+			child_in = lookup_storage(child, pseudo, STOR_IN);
+
+			if (parent_out) {
+				if (!child_in) {
+					add_storage(parent_out, child, pseudo, STOR_IN);
+					continue;
+				}
+				if (parent_out == child_in)
+					continue;
+				combine_storage(parent_out, child_in);
+				continue;
+			}
+			if (child_in) {
+				add_storage(child_in, bb, pseudo, STOR_OUT);
+				continue;
+			}
+			parent_out = alloc_storage();
+			add_storage(parent_out, bb, pseudo, STOR_OUT);
+			add_storage(parent_out, child, pseudo, STOR_IN);
+		} END_FOR_EACH_PTR(pseudo);
+	} END_FOR_EACH_PTR(child);
+}
+
+static void set_up_argument_storage(struct entrypoint *ep, struct basic_block *bb)
+{
+	pseudo_t arg;
+
+	FOR_EACH_PTR(bb->needs, arg) {
+		struct storage *storage = alloc_storage();
+
+		/* FIXME! Totally made-up argument passing conventions */
+		if (arg->type == PSEUDO_ARG) {
+			storage->type = REG_ARG;
+			storage->regno = arg->nr;
+		}
+		add_storage(storage, bb, arg, STOR_IN);
+	} END_FOR_EACH_PTR(arg);
+}
+
+/*
+ * One phi-source may feed multiple phi nodes. If so, combine
+ * the storage output for this bb into one entry to reduce
+ * storage pressure.
+ */
+static void combine_phi_storage(struct basic_block *bb)
+{
+	struct instruction *insn;
+	FOR_EACH_PTR(bb->insns, insn) {
+		struct instruction *phi;
+		struct storage *last;
+
+		if (!insn->bb || insn->opcode != OP_PHISOURCE)
+			continue;
+		last = NULL;
+		FOR_EACH_PTR(insn->phi_users, phi) {
+			struct storage *storage = lookup_storage(bb, phi->target, STOR_OUT);
+			if (!storage) {
+				DELETE_CURRENT_PTR(phi);
+				continue;
+			}
+			if (last && storage != last)
+				storage = combine_storage(storage, last);
+			last = storage;
+		} END_FOR_EACH_PTR(phi);
+		PACK_PTR_LIST(&insn->phi_users);
+	} END_FOR_EACH_PTR(insn);
+}
+
+void set_up_storage(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+
+	/* First set up storage for the incoming arguments */
+	set_up_argument_storage(ep, ep->entry->bb);
+
+	/* Then do a list of all the inter-bb storage */
+	FOR_EACH_PTR(ep->bbs, bb) {
+		set_up_bb_storage(bb);
+		combine_phi_storage(bb);
+	} END_FOR_EACH_PTR(bb);
+
+	name_storage();
+}
diff --git a/deps/sparse/storage.h b/deps/sparse/storage.h
new file mode 100644
index 0000000..e049e23
--- /dev/null
+++ b/deps/sparse/storage.h
@@ -0,0 +1,79 @@
+#ifndef STORAGE_H
+#define STORAGE_H
+
+#include "allocate.h"
+#include "lib.h"
+
+/*
+ * The "storage" that underlies an incoming/outgoing pseudo. It's
+ * basically the backing store for a pseudo, and may be a real hardware
+ * register, a stack slot or a static symbol. Or nothing at all,
+ * since some pseudos can just be recalculated on the fly.
+ */
+enum storage_type {
+	REG_UDEF,
+	REG_REG,
+	REG_STACK,
+	REG_FRAME,
+	REG_SYM,
+	REG_ARG,
+	REG_BAD,
+};
+
+enum inout_enum {
+	STOR_IN,
+	STOR_OUT
+};
+
+struct storage;
+DECLARE_PTR_LIST(storage_ptr_list, struct storage *);
+
+struct storage {
+	enum storage_type type;
+	int name;
+	struct storage_ptr_list *users;
+	union {
+		int regno;
+		int offset;
+		struct symbol *sym;
+	};
+};
+
+DECLARE_PTR_LIST(storage_list, struct storage);
+
+struct storage_hash {
+	struct basic_block *bb;
+	pseudo_t pseudo;
+	enum inout_enum inout;
+	struct storage *storage;
+	unsigned long flags;
+};
+
+DECLARE_PTR_LIST(storage_hash_list, struct storage_hash);
+
+extern struct storage_hash_list *gather_storage(struct basic_block *, enum inout_enum);
+extern void free_storage(void);
+extern const char *show_storage(struct storage *);
+extern void set_up_storage(struct entrypoint *);
+struct storage *lookup_storage(struct basic_block *, pseudo_t, enum inout_enum);
+void add_storage(struct storage *, struct basic_block *, pseudo_t, enum inout_enum);
+
+DECLARE_ALLOCATOR(storage);
+DECLARE_ALLOCATOR(storage_hash);
+
+static inline struct storage *alloc_storage(void)
+{
+	return __alloc_storage(0);
+}
+
+static inline struct storage_hash *alloc_storage_hash(struct storage *s)
+{
+	struct storage_hash *entry = __alloc_storage_hash(0);
+	struct storage **usep = &entry->storage;
+
+	*usep = s;
+	add_ptr_list(&s->users, usep);
+	return entry;
+}
+
+#endif /* STORAGE_H */
diff --git a/deps/sparse/symbol.c b/deps/sparse/symbol.c
new file mode 100644
index 0000000..86aef1c
--- /dev/null
+++ b/deps/sparse/symbol.c
@@ -0,0 +1,856 @@
+/*
+ * Symbol lookup and handling.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "scope.h"
+#include "expression.h"
+
+#include "target.h"
+
+/*
+ * Secondary symbol list for stuff that needs to be output because it
+ * was used. 
+ */
+struct symbol_list *translation_unit_used_list = NULL;
+
+/*
+ * If the symbol is an inline symbol, add it to the list of symbols to parse
+ */
+void access_symbol(struct symbol *sym)
+{
+	if (sym->ctype.modifiers & MOD_INLINE) {
+		if (!(sym->ctype.modifiers & MOD_ACCESSED)) {
+			add_symbol(&translation_unit_used_list, sym);
+			sym->ctype.modifiers |= MOD_ACCESSED;
+		}
+	}
+}
+
+struct symbol *lookup_symbol(struct ident *ident, enum namespace ns)
+{
+	struct symbol *sym;
+
+	for (sym = ident->symbols; sym; sym = sym->next_id) {
+		if (sym->namespace & ns) {
+			sym->used = 1;
+			return sym;
+		}
+	}
+	return NULL;
+}
+
+struct context *alloc_context(void)
+{
+	return __alloc_context(0);
+}
+
+struct symbol *alloc_symbol(struct position pos, int type)
+{
+	struct symbol *sym = __alloc_symbol(0);
+	sym->type = type;
+	sym->pos = pos;
+	sym->endpos.type = 0;
+	return sym;
+}
+
+struct struct_union_info {
+	unsigned long max_align;
+	unsigned long bit_size;
+	int align_size;
+};
+
+/*
+ * Unions are fairly easy to lay out ;)
+ */
+static void lay_out_union(struct symbol *sym, struct struct_union_info *info)
+{
+	examine_symbol_type(sym);
+
+	// Unnamed bitfields do not affect alignment.
+	if (sym->ident || !is_bitfield_type(sym)) {
+		if (sym->ctype.alignment > info->max_align)
+			info->max_align = sym->ctype.alignment;
+	}
+
+	if (sym->bit_size > info->bit_size)
+		info->bit_size = sym->bit_size;
+
+	sym->offset = 0;
+}
+
+static int bitfield_base_size(struct symbol *sym)
+{
+	if (sym->type == SYM_NODE)
+		sym = sym->ctype.base_type;
+	if (sym->type == SYM_BITFIELD)
+		sym = sym->ctype.base_type;
+	return sym->bit_size;
+}
+
+/*
+ * Structures are a bit more interesting to lay out
+ */
+static void lay_out_struct(struct symbol *sym, struct struct_union_info *info)
+{
+	unsigned long bit_size, align_bit_mask;
+	int base_size;
+
+	examine_symbol_type(sym);
+
+	// Unnamed bitfields do not affect alignment.
+	if (sym->ident || !is_bitfield_type(sym)) {
+		if (sym->ctype.alignment > info->max_align)
+			info->max_align = sym->ctype.alignment;
+	}
+
+	bit_size = info->bit_size;
+	base_size = sym->bit_size; 
+
+	/*
+	 * Unsized arrays cause us to not align the resulting
+	 * structure size
+	 */
+	if (base_size < 0) {
+		info->align_size = 0;
+		base_size = 0;
+	}
+
+	align_bit_mask = bytes_to_bits(sym->ctype.alignment) - 1;
+
+	/*
+	 * Bitfields have some very special rules..
+	 */
+	if (is_bitfield_type (sym)) {
+		unsigned long bit_offset = bit_size & align_bit_mask;
+		int room = bitfield_base_size(sym) - bit_offset;
+		// Zero-width fields just fill up the unit.
+		int width = base_size ? : (bit_offset ? room : 0);
+
+		if (width > room) {
+			bit_size = (bit_size + align_bit_mask) & ~align_bit_mask;
+			bit_offset = 0;
+		}
+		sym->offset = bits_to_bytes(bit_size - bit_offset);
+		sym->bit_offset = bit_offset;
+		sym->ctype.base_type->bit_offset = bit_offset;
+		info->bit_size = bit_size + width;
+		// warning (sym->pos, "bitfield: offset=%d:%d  size=:%d", sym->offset, sym->bit_offset, width);
+
+		return;
+	}
+
+	/*
+	 * Otherwise, just align it right and add it up..
+	 */
+	bit_size = (bit_size + align_bit_mask) & ~align_bit_mask;
+	sym->offset = bits_to_bytes(bit_size);
+
+	info->bit_size = bit_size + base_size;
+	// warning (sym->pos, "regular: offset=%d", sym->offset);
+}
+
+static struct symbol * examine_struct_union_type(struct symbol *sym, int advance)
+{
+	struct struct_union_info info = {
+		.max_align = 1,
+		.bit_size = 0,
+		.align_size = 1
+	};
+	unsigned long bit_size, bit_align;
+	void (*fn)(struct symbol *, struct struct_union_info *);
+	struct symbol *member;
+
+	fn = advance ? lay_out_struct : lay_out_union;
+	FOR_EACH_PTR(sym->symbol_list, member) {
+		fn(member, &info);
+	} END_FOR_EACH_PTR(member);
+
+	if (!sym->ctype.alignment)
+		sym->ctype.alignment = info.max_align;
+	bit_size = info.bit_size;
+	if (info.align_size) {
+		bit_align = bytes_to_bits(sym->ctype.alignment)-1;
+		bit_size = (bit_size + bit_align) & ~bit_align;
+	}
+	sym->bit_size = bit_size;
+	return sym;
+}
+
+static struct symbol *examine_base_type(struct symbol *sym)
+{
+	struct symbol *base_type;
+
+	/* Check the base type */
+	base_type = examine_symbol_type(sym->ctype.base_type);
+	if (!base_type || base_type->type == SYM_PTR)
+		return base_type;
+	sym->ctype.as |= base_type->ctype.as;
+	sym->ctype.modifiers |= base_type->ctype.modifiers & MOD_PTRINHERIT;
+	concat_ptr_list((struct ptr_list *)base_type->ctype.contexts,
+			(struct ptr_list **)&sym->ctype.contexts);
+	if (base_type->type == SYM_NODE) {
+		base_type = base_type->ctype.base_type;
+		sym->ctype.base_type = base_type;
+	}
+	return base_type;
+}
+
+static struct symbol * examine_array_type(struct symbol *sym)
+{
+	struct symbol *base_type = examine_base_type(sym);
+	unsigned long bit_size, alignment;
+
+	if (!base_type)
+		return sym;
+	bit_size = base_type->bit_size * get_expression_value(sym->array_size);
+	if (!sym->array_size || sym->array_size->type != EXPR_VALUE)
+		bit_size = -1;
+	alignment = base_type->ctype.alignment;
+	if (!sym->ctype.alignment)
+		sym->ctype.alignment = alignment;
+	sym->bit_size = bit_size;
+	return sym;
+}
+
+static struct symbol *examine_bitfield_type(struct symbol *sym)
+{
+	struct symbol *base_type = examine_base_type(sym);
+	unsigned long bit_size, alignment, modifiers;
+
+	if (!base_type)
+		return sym;
+	bit_size = base_type->bit_size;
+	if (sym->bit_size > bit_size)
+		warning(sym->pos, "impossible field-width, %d, for this type",  sym->bit_size);
+
+	alignment = base_type->ctype.alignment;
+	if (!sym->ctype.alignment)
+		sym->ctype.alignment = alignment;
+	modifiers = base_type->ctype.modifiers;
+
+	/* Bitfields are unsigned, unless the base type was explicitly signed */
+	if (!(modifiers & MOD_EXPLICITLY_SIGNED))
+		modifiers = (modifiers & ~MOD_SIGNED) | MOD_UNSIGNED;
+	sym->ctype.modifiers |= modifiers & MOD_SIGNEDNESS;
+	return sym;
+}
+
+/*
+ * "typeof" will have to merge the types together
+ */
+void merge_type(struct symbol *sym, struct symbol *base_type)
+{
+	sym->ctype.as |= base_type->ctype.as;
+	sym->ctype.modifiers |= (base_type->ctype.modifiers & ~MOD_STORAGE);
+	concat_ptr_list((struct ptr_list *)base_type->ctype.contexts,
+	                (struct ptr_list **)&sym->ctype.contexts);
+	sym->ctype.base_type = base_type->ctype.base_type;
+	if (sym->ctype.base_type->type == SYM_NODE)
+		merge_type(sym, sym->ctype.base_type);
+}
+
+static int count_array_initializer(struct symbol *t, struct expression *expr)
+{
+	int nr = 0;
+	int is_char = 0;
+
+	/*
+	 * Arrays of character types are special; they can be initialized by
+	 * string literal _or_ by string literal in braces.  The latter means
+	 * that with T x[] = {<string literal>} number of elements in x depends
+	 * on T - if it's a character type, we get the length of string literal
+	 * (including NUL), otherwise we have one element here.
+	 */
+	if (t->ctype.base_type == &int_type && t->ctype.modifiers & MOD_CHAR)
+		is_char = 1;
+
+	switch (expr->type) {
+	case EXPR_INITIALIZER: {
+		struct expression *entry;
+		int count = 0;
+		int str_len = 0;
+		FOR_EACH_PTR(expr->expr_list, entry) {
+			count++;
+			switch (entry->type) {
+			case EXPR_INDEX:
+				if (entry->idx_to >= nr)
+					nr = entry->idx_to+1;
+				break;
+			case EXPR_STRING:
+				if (is_char)
+					str_len = entry->string->length;
+			default:
+				nr++;
+			}
+		} END_FOR_EACH_PTR(entry);
+		if (count == 1 && str_len)
+			nr = str_len;
+		break;
+	}
+	case EXPR_STRING:
+		if (is_char)
+			nr = expr->string->length;
+	default:
+		break;
+	}
+	return nr;
+}
+
+static struct symbol * examine_node_type(struct symbol *sym)
+{
+	struct symbol *base_type = examine_base_type(sym);
+	int bit_size;
+	unsigned long alignment;
+
+	/* SYM_NODE - figure out what the type of the node was.. */
+	bit_size = 0;
+	alignment = 0;
+	if (!base_type)
+		return sym;
+
+	bit_size = base_type->bit_size;
+	alignment = base_type->ctype.alignment;
+
+	/* Pick up signedness information into the node */
+	sym->ctype.modifiers |= (MOD_SIGNEDNESS & base_type->ctype.modifiers);
+
+	if (!sym->ctype.alignment)
+		sym->ctype.alignment = alignment;
+
+	/* Unsized array? The size might come from the initializer.. */
+	if (bit_size < 0 && base_type->type == SYM_ARRAY && sym->initializer) {
+		struct symbol *node_type = base_type->ctype.base_type;
+		int count = count_array_initializer(node_type, sym->initializer);
+
+		if (node_type && node_type->bit_size >= 0)
+			bit_size = node_type->bit_size * count;
+	}
+	
+	sym->bit_size = bit_size;
+	return sym;
+}
+
+static struct symbol *examine_enum_type(struct symbol *sym)
+{
+	struct symbol *base_type = examine_base_type(sym);
+
+	sym->ctype.modifiers |= (base_type->ctype.modifiers & MOD_SIGNEDNESS);
+	sym->bit_size = bits_in_enum;
+	if (base_type->bit_size > sym->bit_size)
+		sym->bit_size = base_type->bit_size;
+	sym->ctype.alignment = enum_alignment;
+	if (base_type->ctype.alignment > sym->ctype.alignment)
+		sym->ctype.alignment = base_type->ctype.alignment;
+	return sym;
+}
+
+static struct symbol *examine_pointer_type(struct symbol *sym)
+{
+	/*
+	 * We need to set the pointer size first, and
+	 * examine the thing we point to only afterwards.
+	 * That's because this pointer type may end up
+	 * being needed for the base type size evaluation.
+	 */
+	if (!sym->bit_size)
+		sym->bit_size = bits_in_pointer;
+	if (!sym->ctype.alignment)
+		sym->ctype.alignment = pointer_alignment;
+	return sym;
+}
+
+/*
+ * Fill in type size and alignment information for
+ * regular SYM_TYPE things.
+ */
+struct symbol *examine_symbol_type(struct symbol * sym)
+{
+	if (!sym)
+		return sym;
+
+	/* Already done? */
+	if (sym->examined)
+		return sym;
+	sym->examined = 1;
+
+	switch (sym->type) {
+	case SYM_FN:
+	case SYM_NODE:
+		return examine_node_type(sym);
+	case SYM_ARRAY:
+		return examine_array_type(sym);
+	case SYM_STRUCT:
+		return examine_struct_union_type(sym, 1);
+	case SYM_UNION:
+		return examine_struct_union_type(sym, 0);
+	case SYM_PTR:
+		return examine_pointer_type(sym);
+	case SYM_ENUM:
+		return examine_enum_type(sym);
+	case SYM_BITFIELD:
+		return examine_bitfield_type(sym);
+	case SYM_BASETYPE:
+		/* Size and alignment had better already be set up */
+		return sym;
+	case SYM_TYPEOF: {
+		struct symbol *base = evaluate_expression(sym->initializer);
+		if (base) {
+			if (is_bitfield_type(base))
+				warning(base->pos, "typeof applied to bitfield type");
+			if (base->type == SYM_NODE)
+				base = base->ctype.base_type;
+			sym->type = SYM_NODE;
+			sym->ctype.modifiers = 0;
+			sym->ctype.base_type = base;
+			return examine_node_type(sym);
+		}
+		break;
+	}
+	case SYM_PREPROCESSOR:
+		sparse_error(sym->pos, "ctype on preprocessor command? (%s)", show_ident(sym->ident));
+		return NULL;
+	case SYM_UNINITIALIZED:
+		sparse_error(sym->pos, "ctype on uninitialized symbol %p", sym);
+		return NULL;
+	case SYM_RESTRICT:
+		examine_base_type(sym);
+		return sym;
+	case SYM_FOULED:
+		examine_base_type(sym);
+		return sym;
+	default:
+		sparse_error(sym->pos, "Examining unknown symbol type %d", sym->type);
+		break;
+	}
+	return sym;
+}
+
+const char* get_type_name(enum type type)
+{
+	const char *type_lookup[] = {
+	[SYM_UNINITIALIZED] = "uninitialized",
+	[SYM_PREPROCESSOR] = "preprocessor",
+	[SYM_BASETYPE] = "basetype",
+	[SYM_NODE] = "node",
+	[SYM_PTR] = "pointer",
+	[SYM_FN] = "function",
+	[SYM_ARRAY] = "array",
+	[SYM_STRUCT] = "struct",
+	[SYM_UNION] = "union",
+	[SYM_ENUM] = "enum",
+	[SYM_TYPEDEF] = "typedef",
+	[SYM_TYPEOF] = "typeof",
+	[SYM_MEMBER] = "member",
+	[SYM_BITFIELD] = "bitfield",
+	[SYM_LABEL] = "label",
+	[SYM_RESTRICT] = "restrict",
+	[SYM_FOULED] = "fouled",
+	[SYM_KEYWORD] = "keyword",
+	[SYM_BAD] = "bad"};
+
+	if (type <= SYM_BAD)
+		return type_lookup[type];
+	else
+		return NULL;
+}
+
+struct symbol *examine_pointer_target(struct symbol *sym)
+{
+	return examine_base_type(sym);
+}
+
+static struct symbol_list *restr, *fouled;
+
+void create_fouled(struct symbol *type)
+{
+	if (type->bit_size < bits_in_int) {
+		struct symbol *new = alloc_symbol(type->pos, type->type);
+		*new = *type;
+		new->bit_size = bits_in_int;
+		new->type = SYM_FOULED;
+		new->ctype.base_type = type;
+		add_symbol(&restr, type);
+		add_symbol(&fouled, new);
+	}
+}
+
+struct symbol *befoul(struct symbol *type)
+{
+	struct symbol *t1, *t2;
+	while (type->type == SYM_NODE)
+		type = type->ctype.base_type;
+	PREPARE_PTR_LIST(restr, t1);
+	PREPARE_PTR_LIST(fouled, t2);
+	for (;;) {
+		if (t1 == type)
+			return t2;
+		if (!t1)
+			break;
+		NEXT_PTR_LIST(t1);
+		NEXT_PTR_LIST(t2);
+	}
+	FINISH_PTR_LIST(t2);
+	FINISH_PTR_LIST(t1);
+	return NULL;
+}
+
+void check_declaration(struct symbol *sym)
+{
+	int warned = 0;
+	struct symbol *next = sym;
+
+	while ((next = next->next_id) != NULL) {
+		if (next->namespace != sym->namespace)
+			continue;
+		if (sym->scope == next->scope) {
+			sym->same_symbol = next;
+			return;
+		}
+		if (sym->ctype.modifiers & next->ctype.modifiers & MOD_EXTERN) {
+			if ((sym->ctype.modifiers ^ next->ctype.modifiers) & MOD_INLINE)
+				continue;
+			sym->same_symbol = next;
+			return;
+		}
+
+		if (!Wshadow || warned)
+			continue;
+		if (get_sym_type(next) == SYM_FN)
+			continue;
+		warned = 1;
+		warning(sym->pos, "symbol '%s' shadows an earlier one", show_ident(sym->ident));
+		info(next->pos, "originally declared here");
+	}
+}
+
+void bind_symbol(struct symbol *sym, struct ident *ident, enum namespace ns)
+{
+	struct scope *scope;
+	if (sym->bound) {
+		sparse_error(sym->pos, "internal error: symbol type already bound");
+		return;
+	}
+	if (ident->reserved && (ns & (NS_TYPEDEF | NS_STRUCT | NS_LABEL | NS_SYMBOL))) {
+		sparse_error(sym->pos, "Trying to use reserved word '%s' as identifier", show_ident(ident));
+		return;
+	}
+	sym->namespace = ns;
+	sym->next_id = ident->symbols;
+	ident->symbols = sym;
+	if (sym->ident && sym->ident != ident)
+		warning(sym->pos, "Symbol '%s' already bound", show_ident(sym->ident));
+	sym->ident = ident;
+	sym->bound = 1;
+
+	scope = block_scope;
+	if (ns == NS_SYMBOL && toplevel(scope)) {
+		unsigned mod = MOD_ADDRESSABLE | MOD_TOPLEVEL;
+
+		scope = global_scope;
+		if (sym->ctype.modifiers & MOD_STATIC ||
+		    is_extern_inline(sym)) {
+			scope = file_scope;
+			mod = MOD_TOPLEVEL;
+		}
+		sym->ctype.modifiers |= mod;
+	}
+	if (ns == NS_MACRO)
+		scope = file_scope;
+	if (ns == NS_LABEL)
+		scope = function_scope;
+	bind_scope(sym, scope);
+}
+
+struct symbol *create_symbol(int stream, const char *name, int type, int namespace)
+{
+	struct token *token = built_in_token(stream, name);
+	struct symbol *sym = alloc_symbol(token->pos, type);
+
+	bind_symbol(sym, token->ident, namespace);
+	return sym;
+}
+
+static int evaluate_to_integer(struct expression *expr)
+{
+	expr->ctype = &int_ctype;
+	return 1;
+}
+
+static int evaluate_expect(struct expression *expr)
+{
+	/* Should we evaluate it to return the type of the first argument? */
+	expr->ctype = &int_ctype;
+	return 1;
+}
+
+static int arguments_choose(struct expression *expr)
+{
+	struct expression_list *arglist = expr->args;
+	struct expression *arg;
+	int i = 0;
+
+	FOR_EACH_PTR (arglist, arg) {
+		if (!evaluate_expression(arg))
+			return 0;
+		i++;
+	} END_FOR_EACH_PTR(arg);
+	if (i < 3) {
+		sparse_error(expr->pos,
+			     "not enough arguments for __builtin_choose_expr");
+		return 0;
+	} if (i > 3) {
+		sparse_error(expr->pos,
+			     "too many arguments for __builtin_choose_expr");
+		return 0;
+	}
+	return 1;
+}
+
+static int evaluate_choose(struct expression *expr)
+{
+	struct expression_list *list = expr->args;
+	struct expression *arg, *args[3];
+	int n = 0;
+
+	/* there will be exactly 3; we'd already verified that */
+	FOR_EACH_PTR(list, arg) {
+		args[n++] = arg;
+	} END_FOR_EACH_PTR(arg);
+
+	*expr = get_expression_value(args[0]) ? *args[1] : *args[2];
+
+	return 1;
+}
+
+static int expand_expect(struct expression *expr, int cost)
+{
+	struct expression *arg = first_ptr_list((struct ptr_list *) expr->args);
+
+	if (arg)
+		*expr = *arg;
+	return 0;
+}
+
+/*
+ * __builtin_warning() has type "int" and always returns 1,
+ * so that you can use it in conditionals or whatever
+ */
+static int expand_warning(struct expression *expr, int cost)
+{
+	struct expression *arg;
+	struct expression_list *arglist = expr->args;
+
+	FOR_EACH_PTR (arglist, arg) {
+		/*
+		 * Constant strings get printed out as a warning. By the
+		 * time we get here, the EXPR_STRING has been fully 
+		 * evaluated, so by now it's an anonymous symbol with a
+		 * string initializer.
+		 *
+		 * Just for the heck of it, allow any constant string
+		 * symbol.
+		 */
+		if (arg->type == EXPR_SYMBOL) {
+			struct symbol *sym = arg->symbol;
+			if (sym->initializer && sym->initializer->type == EXPR_STRING) {
+				struct string *string = sym->initializer->string;
+				warning(expr->pos, "%*s", string->length-1, string->data);
+			}
+			continue;
+		}
+
+		/*
+		 * Any other argument is a conditional. If it's
+		 * non-constant, or it is false, we exit and do
+		 * not print any warning.
+		 */
+		if (arg->type != EXPR_VALUE)
+			goto out;
+		if (!arg->value)
+			goto out;
+	} END_FOR_EACH_PTR(arg);
+out:
+	expr->type = EXPR_VALUE;
+	expr->value = 1;
+	expr->taint = 0;
+	return 0;
+}
+
+static struct symbol_op constant_p_op = {
+	.evaluate = evaluate_to_integer,
+	.expand = expand_constant_p
+};
+
+static struct symbol_op safe_p_op = {
+	.evaluate = evaluate_to_integer,
+	.expand = expand_safe_p
+};
+
+static struct symbol_op warning_op = {
+	.evaluate = evaluate_to_integer,
+	.expand = expand_warning
+};
+
+static struct symbol_op expect_op = {
+	.evaluate = evaluate_expect,
+	.expand = expand_expect
+};
+
+static struct symbol_op choose_op = {
+	.evaluate = evaluate_choose,
+	.args = arguments_choose,
+};
+
+/*
+ * Builtin functions
+ */
+static struct symbol builtin_fn_type = { .type = SYM_FN /* , .variadic =1 */ };
+static struct sym_init {
+	const char *name;
+	struct symbol *base_type;
+	unsigned int modifiers;
+	struct symbol_op *op;
+} eval_init_table[] = {
+	{ "__builtin_constant_p", &builtin_fn_type, MOD_TOPLEVEL, &constant_p_op },
+	{ "__builtin_safe_p", &builtin_fn_type, MOD_TOPLEVEL, &safe_p_op },
+	{ "__builtin_warning", &builtin_fn_type, MOD_TOPLEVEL, &warning_op },
+	{ "__builtin_expect", &builtin_fn_type, MOD_TOPLEVEL, &expect_op },
+	{ "__builtin_choose_expr", &builtin_fn_type, MOD_TOPLEVEL, &choose_op },
+	{ NULL,		NULL,		0 }
+};
+
+
+/*
+ * Abstract types
+ */
+struct symbol	int_type,
+		fp_type;
+
+/*
+ * C types (i.e. actual instances that the abstract types
+ * can map onto)
+ */
+struct symbol	bool_ctype, void_ctype, type_ctype,
+		char_ctype, schar_ctype, uchar_ctype,
+		short_ctype, sshort_ctype, ushort_ctype,
+		int_ctype, sint_ctype, uint_ctype,
+		long_ctype, slong_ctype, ulong_ctype,
+		llong_ctype, sllong_ctype, ullong_ctype,
+		lllong_ctype, slllong_ctype, ulllong_ctype,
+		float_ctype, double_ctype, ldouble_ctype,
+		string_ctype, ptr_ctype, lazy_ptr_ctype,
+		incomplete_ctype, label_ctype, bad_ctype,
+		null_ctype;
+
+struct symbol	zero_int;
+
+#define __INIT_IDENT(str, res) { .len = sizeof(str)-1, .name = str, .reserved = res }
+#define __IDENT(n,str,res) \
+	struct ident n  = __INIT_IDENT(str,res)
+
+#include "ident-list.h"
+
+void init_symbols(void)
+{
+	int stream = init_stream("builtin", -1, includepath);
+	struct sym_init *ptr;
+
+#define __IDENT(n,str,res) \
+	hash_ident(&n)
+#include "ident-list.h"
+
+	init_parser(stream);
+
+	builtin_fn_type.variadic = 1;
+	for (ptr = eval_init_table; ptr->name; ptr++) {
+		struct symbol *sym;
+		sym = create_symbol(stream, ptr->name, SYM_NODE, NS_SYMBOL);
+		sym->ctype.base_type = ptr->base_type;
+		sym->ctype.modifiers = ptr->modifiers;
+		sym->op = ptr->op;
+	}
+}
+
+#define MOD_ESIGNED (MOD_SIGNED | MOD_EXPLICITLY_SIGNED)
+#define MOD_LL (MOD_LONG | MOD_LONGLONG)
+#define MOD_LLL MOD_LONGLONGLONG
+static const struct ctype_declare {
+	struct symbol *ptr;
+	enum type type;
+	unsigned long modifiers;
+	int *bit_size;
+	int *maxalign;
+	struct symbol *base_type;
+} ctype_declaration[] = {
+	{ &bool_ctype,	    SYM_BASETYPE, MOD_UNSIGNED,		    &bits_in_bool,	     &max_int_alignment, &int_type },
+	{ &void_ctype,	    SYM_BASETYPE, 0,			    NULL,	     NULL,		 NULL },
+	{ &type_ctype,	    SYM_BASETYPE, MOD_TYPE,		    NULL,		     NULL,		 NULL },
+	{ &incomplete_ctype,SYM_BASETYPE, 0,			    NULL,		     NULL,		 NULL },
+	{ &bad_ctype,	    SYM_BASETYPE, 0,			    NULL,		     NULL,		 NULL },
+
+	{ &char_ctype,	    SYM_BASETYPE, MOD_SIGNED | MOD_CHAR,    &bits_in_char,	     &max_int_alignment, &int_type },
+	{ &schar_ctype,	    SYM_BASETYPE, MOD_ESIGNED | MOD_CHAR,   &bits_in_char,	     &max_int_alignment, &int_type },
+	{ &uchar_ctype,	    SYM_BASETYPE, MOD_UNSIGNED | MOD_CHAR,  &bits_in_char,	     &max_int_alignment, &int_type },
+	{ &short_ctype,	    SYM_BASETYPE, MOD_SIGNED | MOD_SHORT,   &bits_in_short,	     &max_int_alignment, &int_type },
+	{ &sshort_ctype,    SYM_BASETYPE, MOD_ESIGNED | MOD_SHORT,  &bits_in_short,	     &max_int_alignment, &int_type },
+	{ &ushort_ctype,    SYM_BASETYPE, MOD_UNSIGNED | MOD_SHORT, &bits_in_short,	     &max_int_alignment, &int_type },
+	{ &int_ctype,	    SYM_BASETYPE, MOD_SIGNED,		    &bits_in_int,	     &max_int_alignment, &int_type },
+	{ &sint_ctype,	    SYM_BASETYPE, MOD_ESIGNED,		    &bits_in_int,	     &max_int_alignment, &int_type },
+	{ &uint_ctype,	    SYM_BASETYPE, MOD_UNSIGNED,		    &bits_in_int,	     &max_int_alignment, &int_type },
+	{ &long_ctype,	    SYM_BASETYPE, MOD_SIGNED | MOD_LONG,    &bits_in_long,	     &max_int_alignment, &int_type },
+	{ &slong_ctype,	    SYM_BASETYPE, MOD_ESIGNED | MOD_LONG,   &bits_in_long,	     &max_int_alignment, &int_type },
+	{ &ulong_ctype,	    SYM_BASETYPE, MOD_UNSIGNED | MOD_LONG,  &bits_in_long,	     &max_int_alignment, &int_type },
+	{ &llong_ctype,	    SYM_BASETYPE, MOD_SIGNED | MOD_LL,	    &bits_in_longlong,       &max_int_alignment, &int_type },
+	{ &sllong_ctype,    SYM_BASETYPE, MOD_ESIGNED | MOD_LL,	    &bits_in_longlong,       &max_int_alignment, &int_type },
+	{ &ullong_ctype,    SYM_BASETYPE, MOD_UNSIGNED | MOD_LL,    &bits_in_longlong,       &max_int_alignment, &int_type },
+	{ &lllong_ctype,    SYM_BASETYPE, MOD_SIGNED | MOD_LLL,	    &bits_in_longlonglong,   &max_int_alignment, &int_type },
+	{ &slllong_ctype,   SYM_BASETYPE, MOD_ESIGNED | MOD_LLL,    &bits_in_longlonglong,   &max_int_alignment, &int_type },
+	{ &ulllong_ctype,   SYM_BASETYPE, MOD_UNSIGNED | MOD_LLL,   &bits_in_longlonglong,   &max_int_alignment, &int_type },
+
+	{ &float_ctype,	    SYM_BASETYPE,  0,			    &bits_in_float,          &max_fp_alignment,  &fp_type },
+	{ &double_ctype,    SYM_BASETYPE, MOD_LONG,		    &bits_in_double,         &max_fp_alignment,  &fp_type },
+	{ &ldouble_ctype,   SYM_BASETYPE, MOD_LONG | MOD_LONGLONG,  &bits_in_longdouble,     &max_fp_alignment,  &fp_type },
+
+	{ &string_ctype,    SYM_PTR,	  0,			    &bits_in_pointer,        &pointer_alignment, &char_ctype },
+	{ &ptr_ctype,	    SYM_PTR,	  0,			    &bits_in_pointer,        &pointer_alignment, &void_ctype },
+	{ &null_ctype,	    SYM_PTR,	  0,			    &bits_in_pointer,        &pointer_alignment, &void_ctype },
+	{ &label_ctype,	    SYM_PTR,	  0,			    &bits_in_pointer,        &pointer_alignment, &void_ctype },
+	{ &lazy_ptr_ctype,  SYM_PTR,	  0,			    &bits_in_pointer,        &pointer_alignment, &void_ctype },
+	{ NULL, }
+};
+#undef MOD_LLL
+#undef MOD_LL
+#undef MOD_ESIGNED
+
+void init_ctype(void)
+{
+	const struct ctype_declare *ctype;
+
+	for (ctype = ctype_declaration ; ctype->ptr; ctype++) {
+		struct symbol *sym = ctype->ptr;
+		unsigned long bit_size = ctype->bit_size ? *ctype->bit_size : -1;
+		unsigned long maxalign = ctype->maxalign ? *ctype->maxalign : 0;
+		unsigned long alignment = bits_to_bytes(bit_size + bits_in_char - 1);
+
+		if (alignment > maxalign)
+			alignment = maxalign;
+		sym->type = ctype->type;
+		sym->bit_size = bit_size;
+		sym->ctype.alignment = alignment;
+		sym->ctype.base_type = ctype->base_type;
+		sym->ctype.modifiers = ctype->modifiers;
+	}
+}
diff --git a/deps/sparse/symbol.h b/deps/sparse/symbol.h
new file mode 100644
index 0000000..1e74579
--- /dev/null
+++ b/deps/sparse/symbol.h
@@ -0,0 +1,393 @@
+#ifndef SYMBOL_H
+#define SYMBOL_H
+/*
+ * Basic symbol and namespace definitions.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+
+#include "token.h"
+#include "target.h"
+
+/*
+ * An identifier with semantic meaning is a "symbol".
+ *
+ * There's a 1:n relationship: each symbol is always
+ * associated with one identifier, while each identifier
+ * can have one or more semantic meanings due to C scope
+ * rules.
+ *
+ * The progression is symbol -> token -> identifier. The
+ * token contains the information on where the symbol was
+ * declared.
+ */
+enum namespace {
+	NS_NONE = 0,
+	NS_MACRO = 1,
+	NS_TYPEDEF = 2,
+	NS_STRUCT = 4,  // Also used for unions and enums.
+	NS_LABEL = 8,
+	NS_SYMBOL = 16,
+	NS_ITERATOR = 32,
+	NS_PREPROCESSOR = 64,
+	NS_UNDEF = 128,
+	NS_KEYWORD = 256,
+};
+
+enum type {
+	SYM_UNINITIALIZED,
+	SYM_PREPROCESSOR,
+	SYM_BASETYPE,
+	SYM_NODE,
+	SYM_PTR,
+	SYM_FN,
+	SYM_ARRAY,
+	SYM_STRUCT,
+	SYM_UNION,
+	SYM_ENUM,
+	SYM_TYPEDEF,
+	SYM_TYPEOF,
+	SYM_MEMBER,
+	SYM_BITFIELD,
+	SYM_LABEL,
+	SYM_RESTRICT,
+	SYM_FOULED,
+	SYM_KEYWORD,
+	SYM_BAD,
+};
+
+enum keyword {
+	KW_SPECIFIER 	= 1 << 0,
+	KW_MODIFIER	= 1 << 1,
+	KW_QUALIFIER	= 1 << 2,
+	KW_ATTRIBUTE	= 1 << 3,
+	KW_STATEMENT	= 1 << 4,
+	KW_ASM		= 1 << 5,
+	KW_MODE		= 1 << 6,
+	KW_SHORT	= 1 << 7,
+	KW_LONG		= 1 << 8,
+	KW_EXACT	= 1 << 9,
+};
+
+struct context {
+	struct expression *context;
+	unsigned int in, out;
+};
+
+extern struct context *alloc_context(void);
+
+DECLARE_PTR_LIST(context_list, struct context);
+
+struct ctype {
+	unsigned long modifiers;
+	unsigned long alignment;
+	struct context_list *contexts;
+	unsigned int as;
+	struct symbol *base_type;
+};
+
+struct decl_state {
+	struct ctype ctype;
+	struct ident **ident;
+	struct symbol_op *mode;
+	unsigned char prefer_abstract, is_inline, storage_class, is_tls;
+};
+
+struct symbol_op {
+	enum keyword type;
+	int (*evaluate)(struct expression *);
+	int (*expand)(struct expression *, int);
+	int (*args)(struct expression *);
+
+	/* keywords */
+	struct token *(*declarator)(struct token *token, struct decl_state *ctx);
+	struct token *(*statement)(struct token *token, struct statement *stmt);
+	struct token *(*toplevel)(struct token *token, struct symbol_list **list);
+	struct token *(*attribute)(struct token *token, struct symbol *attr, struct decl_state *ctx);
+	struct symbol *(*to_mode)(struct symbol *);
+
+	int test, set, class;
+};
+
+extern int expand_safe_p(struct expression *expr, int cost);
+extern int expand_constant_p(struct expression *expr, int cost);
+
+#define SYM_ATTR_WEAK		0
+#define SYM_ATTR_NORMAL		1
+#define SYM_ATTR_STRONG		2
+
+struct symbol {
+	enum type type:8;
+	enum namespace namespace:9;
+	unsigned char used:1, attr:2, enum_member:1, bound:1;
+	struct position pos;		/* Where this symbol was declared */
+	struct position endpos;		/* Where this symbol ends*/
+	struct ident *ident;		/* What identifier this symbol is associated with */
+	struct symbol *next_id;		/* Next semantic symbol that shares this identifier */
+	struct symbol	*replace;	/* What is this symbol shadowed by in copy-expression */
+	struct scope	*scope;
+	union {
+		struct symbol	*same_symbol;
+		struct symbol	*next_subobject;
+	};
+
+	struct symbol_op *op;
+
+	union {
+		struct /* NS_MACRO */ {
+			struct token *expansion;
+			struct token *arglist;
+			struct scope *used_in;
+		};
+		struct /* NS_PREPROCESSOR */ {
+			int (*handler)(struct stream *, struct token **, struct token *);
+			int normal;
+		};
+		struct /* NS_SYMBOL */ {
+			unsigned long	offset;
+			int		bit_size;
+			unsigned int	bit_offset:8,
+					arg_count:10,
+					variadic:1,
+					initialized:1,
+					examined:1,
+					expanding:1,
+					evaluated:1,
+					string:1,
+					designated_init:1;
+			struct expression *array_size;
+			struct ctype ctype;
+			struct symbol_list *arguments;
+			struct statement *stmt;
+			struct symbol_list *symbol_list;
+			struct statement *inline_stmt;
+			struct symbol_list *inline_symbol_list;
+			struct expression *initializer;
+			struct entrypoint *ep;
+			long long value;		/* Initial value */
+			struct symbol *definition;
+		};
+	};
+	union /* backend */ {
+		struct basic_block *bb_target;	/* label */
+		void *aux;			/* Auxiliary info, e.g. backend information */
+		struct {			/* sparse ctags */
+			char kind;
+			unsigned char visited:1;
+		};
+	};
+	pseudo_t pseudo;
+};
+
+/* Modifiers */
+#define MOD_AUTO	0x0001
+#define MOD_REGISTER	0x0002
+#define MOD_STATIC	0x0004
+#define MOD_EXTERN	0x0008
+
+#define MOD_CONST	0x0010
+#define MOD_VOLATILE	0x0020
+#define MOD_SIGNED	0x0040
+#define MOD_UNSIGNED	0x0080
+
+#define MOD_CHAR	0x0100
+#define MOD_SHORT	0x0200
+#define MOD_LONG	0x0400
+#define MOD_LONGLONG	0x0800
+#define MOD_LONGLONGLONG	0x1000
+#define MOD_PURE	0x2000
+
+#define MOD_TYPEDEF	0x10000
+
+#define MOD_TLS		0x20000
+#define MOD_INLINE	0x40000
+#define MOD_ADDRESSABLE	0x80000
+
+#define MOD_NOCAST	0x100000
+#define MOD_NODEREF	0x200000
+#define MOD_ACCESSED	0x400000
+#define MOD_TOPLEVEL	0x800000	// scoping..
+
+#define MOD_ASSIGNED	0x2000000
+#define MOD_TYPE	0x4000000
+#define MOD_SAFE	0x8000000	// non-null/non-trapping pointer
+
+#define MOD_USERTYPE	0x10000000
+#define MOD_NORETURN	0x20000000
+#define MOD_EXPLICITLY_SIGNED	0x40000000
+#define MOD_BITWISE	0x80000000
+
+
+#define MOD_NONLOCAL	(MOD_EXTERN | MOD_TOPLEVEL)
+#define MOD_STORAGE	(MOD_AUTO | MOD_REGISTER | MOD_STATIC | MOD_EXTERN | MOD_INLINE | MOD_TOPLEVEL)
+#define MOD_SIGNEDNESS	(MOD_SIGNED | MOD_UNSIGNED | MOD_EXPLICITLY_SIGNED)
+#define MOD_LONG_ALL	(MOD_LONG | MOD_LONGLONG | MOD_LONGLONGLONG)
+#define MOD_SPECIFIER	(MOD_CHAR | MOD_SHORT | MOD_LONG_ALL | MOD_SIGNEDNESS)
+#define MOD_SIZE	(MOD_CHAR | MOD_SHORT | MOD_LONG_ALL)
+#define MOD_IGNORE (MOD_TOPLEVEL | MOD_STORAGE | MOD_ADDRESSABLE |	\
+	MOD_ASSIGNED | MOD_USERTYPE | MOD_ACCESSED | MOD_EXPLICITLY_SIGNED)
+#define MOD_PTRINHERIT (MOD_VOLATILE | MOD_CONST | MOD_NODEREF | MOD_STORAGE | MOD_NORETURN)
+
+
+/* Current parsing/evaluation function */
+extern struct symbol *current_fn;
+
+/* Abstract types */
+extern struct symbol	int_type,
+			fp_type;
+
+/* C types */
+extern struct symbol	bool_ctype, void_ctype, type_ctype,
+			char_ctype, schar_ctype, uchar_ctype,
+			short_ctype, sshort_ctype, ushort_ctype,
+			int_ctype, sint_ctype, uint_ctype,
+			long_ctype, slong_ctype, ulong_ctype,
+			llong_ctype, sllong_ctype, ullong_ctype,
+			lllong_ctype, slllong_ctype, ulllong_ctype,
+			float_ctype, double_ctype, ldouble_ctype,
+			string_ctype, ptr_ctype, lazy_ptr_ctype,
+			incomplete_ctype, label_ctype, bad_ctype,
+			null_ctype;
+
+/* Special internal symbols */
+extern struct symbol	zero_int;
+
+#define __IDENT(n,str,res) \
+	extern struct ident n
+#include "ident-list.h"
+
+#define symbol_is_typename(sym) ((sym)->type == SYM_TYPE)
+
+extern struct symbol_list *translation_unit_used_list;
+
+extern void access_symbol(struct symbol *);
+
+extern const char * type_difference(struct ctype *c1, struct ctype *c2,
+	unsigned long mod1, unsigned long mod2);
+
+extern struct symbol *lookup_symbol(struct ident *, enum namespace);
+extern struct symbol *create_symbol(int stream, const char *name, int type, int namespace);
+extern void init_symbols(void);
+extern void init_ctype(void);
+extern struct symbol *alloc_symbol(struct position, int type);
+extern void show_type(struct symbol *);
+extern const char *modifier_string(unsigned long mod);
+extern void show_symbol(struct symbol *);
+extern int show_symbol_expr_init(struct symbol *sym);
+extern void show_type_list(struct symbol *);
+extern void show_symbol_list(struct symbol_list *, const char *);
+extern void add_symbol(struct symbol_list **, struct symbol *);
+extern void bind_symbol(struct symbol *, struct ident *, enum namespace);
+
+extern struct symbol *examine_symbol_type(struct symbol *);
+extern struct symbol *examine_pointer_target(struct symbol *);
+extern void examine_simple_symbol_type(struct symbol *);
+extern const char *show_typename(struct symbol *sym);
+extern const char *builtin_typename(struct symbol *sym);
+extern const char *builtin_ctypename(struct ctype *ctype);
+extern const char* get_type_name(enum type type);
+
+extern void debug_symbol(struct symbol *);
+extern void merge_type(struct symbol *sym, struct symbol *base_type);
+extern void check_declaration(struct symbol *sym);
+
+static inline struct symbol *get_base_type(const struct symbol *sym)
+{
+	return examine_symbol_type(sym->ctype.base_type);
+}
+
+static inline int is_int_type(const struct symbol *type)
+{
+	if (type->type == SYM_NODE)
+		type = type->ctype.base_type;
+	if (type->type == SYM_ENUM)
+		type = type->ctype.base_type;
+	return type->type == SYM_BITFIELD ||
+	       type->ctype.base_type == &int_type;
+}
+
+static inline int is_enum_type(const struct symbol *type)
+{
+	if (type->type == SYM_NODE)
+		type = type->ctype.base_type;
+	return (type->type == SYM_ENUM);
+}
+
+static inline int is_type_type(struct symbol *type)
+{
+	return (type->ctype.modifiers & MOD_TYPE) != 0;
+}
+
+static inline int is_ptr_type(struct symbol *type)
+{
+	if (type->type == SYM_NODE)
+		type = type->ctype.base_type;
+	return type->type == SYM_PTR || type->type == SYM_ARRAY || type->type == SYM_FN;
+}
+
+static inline int is_float_type(struct symbol *type)
+{
+	if (type->type == SYM_NODE)
+		type = type->ctype.base_type;
+	return type->ctype.base_type == &fp_type;
+}
+
+static inline int is_byte_type(struct symbol *type)
+{
+	return type->bit_size == bits_in_char && type->type != SYM_BITFIELD;
+}
+
+static inline int is_void_type(struct symbol *type)
+{
+	if (type->type == SYM_NODE)
+		type = type->ctype.base_type;
+	return type == &void_ctype;
+}
+
+static inline int is_bool_type(struct symbol *type)
+{
+	if (type->type == SYM_NODE)
+		type = type->ctype.base_type;
+	return type == &bool_ctype;
+}
+
+static inline int is_function(struct symbol *type)
+{
+	return type && type->type == SYM_FN;
+}
+
+static inline int is_extern_inline(struct symbol *sym)
+{
+	return (sym->ctype.modifiers & MOD_EXTERN) &&
+		(sym->ctype.modifiers & MOD_INLINE) &&
+		is_function(sym->ctype.base_type);
+}
+
+static inline int get_sym_type(struct symbol *type)
+{
+	if (type->type == SYM_NODE)
+		type = type->ctype.base_type;
+	if (type->type == SYM_ENUM)
+		type = type->ctype.base_type;
+	return type->type;
+}
+
+static inline struct symbol *lookup_keyword(struct ident *ident, enum namespace ns)
+{
+	if (!ident->keyword)
+		return NULL;
+	return lookup_symbol(ident, ns);
+}
+
+#define is_restricted_type(type) (get_sym_type(type) == SYM_RESTRICT)
+#define is_fouled_type(type) (get_sym_type(type) == SYM_FOULED)
+#define is_bitfield_type(type)   (get_sym_type(type) == SYM_BITFIELD)
+extern int is_ptr_type(struct symbol *);
+
+void create_fouled(struct symbol *type);
+struct symbol *befoul(struct symbol *type);
+
+#endif /* SYMBOL_H */
diff --git a/deps/sparse/target.c b/deps/sparse/target.c
new file mode 100644
index 0000000..17b228a
--- /dev/null
+++ b/deps/sparse/target.c
@@ -0,0 +1,46 @@
+#include <stdio.h>
+
+#include "symbol.h"
+#include "target.h"
+
+struct symbol *size_t_ctype = &uint_ctype;
+struct symbol *ssize_t_ctype = &int_ctype;
+
+/*
+ * For "__attribute__((aligned))"
+ */
+int max_alignment = 16;
+
+/*
+ * Integer data types
+ */
+int bits_in_bool = 1;
+int bits_in_char = 8;
+int bits_in_short = 16;
+int bits_in_int = 32;
+int bits_in_long = 32;
+int bits_in_longlong = 64;
+int bits_in_longlonglong = 128;
+
+int max_int_alignment = 4;
+
+/*
+ * Floating point data types
+ */
+int bits_in_float = 32;
+int bits_in_double = 64;
+int bits_in_longdouble = 80;
+
+int max_fp_alignment = 8;
+
+/*
+ * Pointer data type
+ */
+int bits_in_pointer = 32;
+int pointer_alignment = 4;
+
+/*
+ * Enum data types
+ */
+int bits_in_enum = 32;
+int enum_alignment = 4;
diff --git a/deps/sparse/target.h b/deps/sparse/target.h
new file mode 100644
index 0000000..1030c7c
--- /dev/null
+++ b/deps/sparse/target.h
@@ -0,0 +1,60 @@
+#ifndef TARGET_H
+#define TARGET_H
+
+extern struct symbol *size_t_ctype;
+extern struct symbol *ssize_t_ctype;
+
+/*
+ * For "__attribute__((aligned))"
+ */
+extern int max_alignment;
+
+/*
+ * Integer data types
+ */
+extern int bits_in_bool;
+extern int bits_in_char;
+extern int bits_in_short;
+extern int bits_in_int;
+extern int bits_in_long;
+extern int bits_in_longlong;
+extern int bits_in_longlonglong;
+
+extern int max_int_alignment;
+
+/*
+ * Floating point data types
+ */
+extern int bits_in_float;
+extern int bits_in_double;
+extern int bits_in_longdouble;
+
+extern int max_fp_alignment;
+
+/*
+ * Pointer data type
+ */
+extern int bits_in_pointer;
+extern int pointer_alignment;
+
+/*
+ * Enum data types
+ */
+extern int bits_in_enum;
+extern int enum_alignment;
+
+/*
+ * Helper functions for converting bits to bytes and vice versa.
+ */
+
+static inline int bits_to_bytes(int bits)
+{
+	return bits >= 0 ? bits / bits_in_char : -1;
+}
+
+static inline int bytes_to_bits(int bytes)
+{
+	return bytes * bits_in_char;
+}
+
+#endif
diff --git a/deps/sparse/test-dissect.c b/deps/sparse/test-dissect.c
new file mode 100644
index 0000000..a2548b7
--- /dev/null
+++ b/deps/sparse/test-dissect.c
@@ -0,0 +1,97 @@
+#include "dissect.h"
+
+static unsigned dotc_stream;
+
+static inline char storage(struct symbol *sym)
+{
+	int t = sym->type;
+	unsigned m = sym->ctype.modifiers;
+
+	if (m & MOD_INLINE || t == SYM_STRUCT || t == SYM_UNION /*|| t == SYM_ENUM*/)
+		return sym->pos.stream == dotc_stream ? 's' : 'g';
+
+	return (m & MOD_STATIC) ? 's' : (m & MOD_NONLOCAL) ? 'g' : 'l';
+}
+
+static inline const char *show_mode(unsigned mode)
+{
+	static char str[3];
+
+	if (mode == -1)
+		return "def";
+
+#define	U(u_r)	"-rwm"[(mode / u_r) & 3]
+	str[0] = U(U_R_AOF);
+	str[1] = U(U_R_VAL);
+	str[2] = U(U_R_PTR);
+#undef	U
+
+	return str;
+}
+
+static void print_usage(struct position *pos, struct symbol *sym, unsigned mode)
+{
+	static unsigned curr_stream = -1;
+
+	if (curr_stream != pos->stream) {
+		curr_stream = pos->stream;
+		printf("\nFILE: %s\n\n", stream_name(curr_stream));
+	}
+
+	printf("%4d:%-3d %c %-5.3s",
+		pos->line, pos->pos, storage(sym), show_mode(mode));
+}
+
+static void r_symbol(unsigned mode, struct position *pos, struct symbol *sym)
+{
+	print_usage(pos, sym, mode);
+
+	if (!sym->ident)
+		sym->ident = MK_IDENT("__asm__");
+
+	printf("%-32.*s %s\n",
+		sym->ident->len, sym->ident->name,
+		show_typename(sym->ctype.base_type));
+}
+
+static void r_member(unsigned mode, struct position *pos, struct symbol *sym, struct symbol *mem)
+{
+	struct ident *ni, *si, *mi;
+
+	print_usage(pos, sym, mode);
+
+	ni = MK_IDENT("?");
+	si = sym->ident ?: ni;
+	/* mem == NULL means entire struct accessed */
+	mi = mem ? (mem->ident ?: ni) : MK_IDENT("*");
+
+	printf("%.*s.%-*.*s %s\n",
+		si->len, si->name,
+		32-1 - si->len, mi->len, mi->name,
+		show_typename(mem ? mem->ctype.base_type : sym));
+}
+
+static void r_symdef(struct symbol *sym)
+{
+	r_symbol(-1, &sym->pos, sym);
+}
+
+int main(int argc, char **argv)
+{
+	static struct reporter reporter = {
+		.r_symdef = r_symdef,
+		.r_symbol = r_symbol,
+		.r_member = r_member,
+	};
+	struct string_list *filelist = NULL;
+	char *file;
+
+	sparse_initialize(argc, argv, &filelist);
+
+	FOR_EACH_PTR_NOTAG(filelist, file) {
+		dotc_stream = input_stream_nr;
+		dissect(__sparse(file), &reporter);
+	} END_FOR_EACH_PTR_NOTAG(file);
+
+	return 0;
+}
diff --git a/deps/sparse/test-inspect.c b/deps/sparse/test-inspect.c
new file mode 100644
index 0000000..e437e11
--- /dev/null
+++ b/deps/sparse/test-inspect.c
@@ -0,0 +1,43 @@
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "expression.h"
+
+#include "ast-view.h"
+
+static void expand_symbols(struct symbol_list *list)
+{
+	struct symbol *sym;
+	FOR_EACH_PTR(list, sym) {
+		expand_symbol(sym);
+	} END_FOR_EACH_PTR(sym);
+}
+
+int main(int argc, char **argv)
+{
+	struct string_list *filelist = NULL;
+	char *file;
+	struct symbol_list *view_syms = NULL;
+
+	gtk_init(&argc,&argv);
+	expand_symbols(sparse_initialize(argc, argv, &filelist));
+	FOR_EACH_PTR_NOTAG(filelist, file) {
+		struct symbol_list *syms = sparse(file);
+		expand_symbols(syms);
+		concat_symbol_list(syms, &view_syms);
+	} END_FOR_EACH_PTR_NOTAG(file);
+	treeview_main(view_syms);
+	return 0;
+}
+ 
diff --git a/deps/sparse/test-lexing.c b/deps/sparse/test-lexing.c
new file mode 100644
index 0000000..f4639ec
--- /dev/null
+++ b/deps/sparse/test-lexing.c
@@ -0,0 +1,33 @@
+/*
+ * Example test program that just uses the tokenization and
+ * preprocessing phases, and prints out the results.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "token.h"
+#include "symbol.h"
+
+int main(int argc, char **argv)
+{
+	struct string_list *filelist = NULL;
+	char *file;
+
+	preprocess_only = 1;
+	sparse_initialize(argc, argv, &filelist);
+	FOR_EACH_PTR_NOTAG(filelist, file) {
+		sparse(file);
+	} END_FOR_EACH_PTR_NOTAG(file);
+	show_identifier_stats();
+	return 0;
+}
diff --git a/deps/sparse/test-linearize.c b/deps/sparse/test-linearize.c
new file mode 100644
index 0000000..5cc54cd
--- /dev/null
+++ b/deps/sparse/test-linearize.c
@@ -0,0 +1,49 @@
+/*
+ * Parse and linearize the tree for testing.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003-2004 Linus Torvalds
+ *
+ * Licensed under the Open Software License version 1.1
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "expression.h"
+#include "linearize.h"
+
+static void clean_up_symbols(struct symbol_list *list)
+{
+	struct symbol *sym;
+
+	FOR_EACH_PTR(list, sym) {
+		struct entrypoint *ep;
+
+		expand_symbol(sym);
+		ep = linearize_symbol(sym);
+		if (ep)
+			show_entry(ep);
+	} END_FOR_EACH_PTR(sym);
+}
+
+int main(int argc, char **argv)
+{
+	struct string_list *filelist = NULL;
+	char *file;
+
+	clean_up_symbols(sparse_initialize(argc, argv, &filelist));
+	FOR_EACH_PTR_NOTAG(filelist, file) {
+		clean_up_symbols(sparse(file));
+	} END_FOR_EACH_PTR_NOTAG(file);
+	return 0;
+}
diff --git a/deps/sparse/test-parsing.c b/deps/sparse/test-parsing.c
new file mode 100644
index 0000000..0a0b1d4
--- /dev/null
+++ b/deps/sparse/test-parsing.c
@@ -0,0 +1,75 @@
+/*
+ * Example trivial client program that uses the sparse library
+ * to tokenize, preprocess and parse a C file, and prints out
+ * the results.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "expression.h"
+
+static void clean_up_symbols(struct symbol_list *list)
+{
+	struct symbol *sym;
+
+	FOR_EACH_PTR(list, sym) {
+		expand_symbol(sym);
+	} END_FOR_EACH_PTR(sym);
+}
+
+int main(int argc, char **argv)
+{
+	struct symbol_list * list;
+	struct string_list * filelist = NULL;
+	char *file;
+
+	list = sparse_initialize(argc, argv, &filelist);
+
+	// Simplification
+	clean_up_symbols(list);
+
+#if 1
+	show_symbol_list(list, "\n\n");
+	printf("\n\n");
+#endif
+
+	FOR_EACH_PTR_NOTAG(filelist, file) {
+		list = sparse(file);
+
+		// Simplification
+		clean_up_symbols(list);
+
+#if 1
+		// Show the end result.
+		show_symbol_list(list, "\n\n");
+		printf("\n\n");
+#endif
+	} END_FOR_EACH_PTR_NOTAG(file);
+
+#if 0
+	// And show the allocation statistics
+	show_ident_alloc();
+	show_token_alloc();
+	show_symbol_alloc();
+	show_expression_alloc();
+	show_statement_alloc();
+	show_string_alloc();
+	show_bytes_alloc();
+#endif
+	return 0;
+}
diff --git a/deps/sparse/test-sort.c b/deps/sparse/test-sort.c
new file mode 100644
index 0000000..5f17676
--- /dev/null
+++ b/deps/sparse/test-sort.c
@@ -0,0 +1,46 @@
+#include "lib.h"
+#include "allocate.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+static int
+int_cmp (const void *_a, const void *_b)
+{
+  const int *a = _a;
+  const int *b = _b;
+  return *a - *b;
+}
+
+#define MIN(_x,_y) ((_x) < (_y) ? (_x) : (_y))
+
+int
+main (int argc, char **argv)
+{
+  struct ptr_list *l = NULL, *l2;
+  int i, *e;
+  const int N = argv[1] ? atoi (argv[1]) : 10000;
+
+  srand (N);
+  for (i = 0; i < 1000; i++)
+    (void)rand ();
+
+  for (i = 0; i < N; i++) {
+    e = (int *)malloc (sizeof (int));
+    *e = rand ();
+    add_ptr_list (&l, e);
+  }
+  sort_list (&l, int_cmp);
+  // Sort already sorted stuff.
+  sort_list (&l, int_cmp);
+
+  l2 = l;
+  do {
+    l2->nr = MIN (l2->nr, rand () % 3);
+    for (i = 0; i < l2->nr; i++)
+      *((int *)(l2->list[i])) = rand();
+    l2 = l2->next;
+  } while (l2 != l);
+  sort_list (&l, int_cmp);
+
+  return 0;
+}
diff --git a/deps/sparse/test-unssa.c b/deps/sparse/test-unssa.c
new file mode 100644
index 0000000..88ce025
--- /dev/null
+++ b/deps/sparse/test-unssa.c
@@ -0,0 +1,86 @@
+#include <stdio.h>
+#include <assert.h>
+
+#include "symbol.h"
+#include "expression.h"
+#include "linearize.h"
+#include "flow.h"
+
+
+static void output_bb(struct basic_block *bb, unsigned long generation)
+{
+	struct instruction *insn;
+
+	bb->generation = generation;
+	printf(".L%p\n", bb);
+
+	FOR_EACH_PTR(bb->insns, insn) {
+		if (!insn->bb)
+			continue;
+		printf("\t%s\n", show_instruction(insn));
+	}
+	END_FOR_EACH_PTR(insn);
+
+	printf("\n");
+}
+
+static void output_fn(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+	unsigned long generation = ++bb_generation;
+	struct symbol *sym = ep->name;
+	const char *name = show_ident(sym->ident);
+
+	if (sym->ctype.modifiers & MOD_STATIC)
+		printf("\n\n%s:\n", name);
+	else
+		printf("\n\n.globl %s\n%s:\n", name, name);
+
+	unssa(ep);
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		if (bb->generation == generation)
+			continue;
+		output_bb(bb, generation);
+	}
+	END_FOR_EACH_PTR(bb);
+}
+
+static int output_data(struct symbol *sym)
+{
+	printf("symbol %s:\n", show_ident(sym->ident));
+	printf("\ttype = %d\n", sym->ctype.base_type->type);
+	printf("\tmodif= %lx\n", sym->ctype.modifiers);
+
+	return 0;
+}
+
+static int compile(struct symbol_list *list)
+{
+	struct symbol *sym;
+	FOR_EACH_PTR(list, sym) {
+		struct entrypoint *ep;
+		expand_symbol(sym);
+		ep = linearize_symbol(sym);
+		if (ep)
+			output_fn(ep);
+		else
+			output_data(sym);
+	}
+	END_FOR_EACH_PTR(sym);
+
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	struct string_list * filelist = NULL;
+	char *file;
+
+	compile(sparse_initialize(argc, argv, &filelist));
+	FOR_EACH_PTR_NOTAG(filelist, file) {
+		compile(sparse(file));
+	} END_FOR_EACH_PTR_NOTAG(file);
+
+	return 0;
+}
diff --git a/deps/sparse/token.h b/deps/sparse/token.h
new file mode 100644
index 0000000..cd29233
--- /dev/null
+++ b/deps/sparse/token.h
@@ -0,0 +1,217 @@
+#ifndef TOKEN_H
+#define TOKEN_H
+/*
+ * Basic tokenization structures. NOTE! Those tokens had better
+ * be pretty small, since we're going to keep them all in memory
+ * indefinitely.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+
+#include <sys/types.h>
+#include "lib.h"
+
+/*
+ * This describes the pure lexical elements (tokens), with
+ * no semantic meaning. In other words, an identifier doesn't
+ * have a type or meaning, it is only a specific string in
+ * the input stream.
+ *
+ * Semantic meaning is handled elsewhere.
+ */
+
+enum constantfile {
+  CONSTANT_FILE_MAYBE,    // To be determined, not inside any #ifs in this file
+  CONSTANT_FILE_IFNDEF,   // To be determined, currently inside #ifndef
+  CONSTANT_FILE_NOPE,     // No
+  CONSTANT_FILE_YES       // Yes
+};
+
+extern const char *includepath[];
+
+struct stream {
+	int fd;
+	const char *name;
+	const char *path;    // input-file path - see set_stream_include_path()
+	const char **next_path;
+
+	/* Use these to check for "already parsed" */
+	enum constantfile constant;
+	int dirty, next_stream;
+	struct ident *protect;
+	struct token *ifndef;
+	struct token *top_if;
+};
+
+extern int input_stream_nr;
+extern struct stream *input_streams;
+extern unsigned int tabstop;
+extern int *hash_stream(const char *name);
+
+struct ident {
+	struct ident *next;	/* Hash chain of identifiers */
+	struct symbol *symbols;	/* Pointer to semantic meaning list */
+	unsigned char len;	/* Length of identifier name */
+	unsigned char tainted:1,
+	              reserved:1,
+		      keyword:1;
+	char name[];		/* Actual identifier */
+};
+
+enum token_type {
+	TOKEN_EOF,
+	TOKEN_ERROR,
+	TOKEN_IDENT,
+	TOKEN_ZERO_IDENT,
+	TOKEN_NUMBER,
+	TOKEN_CHAR,
+	TOKEN_WIDE_CHAR,
+	TOKEN_STRING,
+	TOKEN_WIDE_STRING,
+	TOKEN_SPECIAL,
+	TOKEN_STREAMBEGIN,
+	TOKEN_STREAMEND,
+	TOKEN_MACRO_ARGUMENT,
+	TOKEN_STR_ARGUMENT,
+	TOKEN_QUOTED_ARGUMENT,
+	TOKEN_CONCAT,
+	TOKEN_GNU_KLUDGE,
+	TOKEN_UNTAINT,
+	TOKEN_ARG_COUNT,
+	TOKEN_IF,
+	TOKEN_SKIP_GROUPS,
+	TOKEN_ELSE,
+};
+
+/* Combination tokens */
+#define COMBINATION_STRINGS {	\
+	"+=", "++",		\
+	"-=", "--", "->",	\
+	"*=",			\
+	"/=",			\
+	"%=",			\
+	"<=", ">=",		\
+	"==", "!=",		\
+	"&&", "&=",		\
+	"||", "|=",		\
+	"^=", "##",		\
+	"<<", ">>", "..",	\
+	"<<=", ">>=", "...",	\
+	"",			\
+	"<", ">", "<=", ">="	\
+}
+
+extern unsigned char combinations[][4];
+
+enum special_token {
+	SPECIAL_BASE = 256,
+	SPECIAL_ADD_ASSIGN = SPECIAL_BASE,
+	SPECIAL_INCREMENT,
+	SPECIAL_SUB_ASSIGN,
+	SPECIAL_DECREMENT,
+	SPECIAL_DEREFERENCE,
+	SPECIAL_MUL_ASSIGN,
+	SPECIAL_DIV_ASSIGN,
+	SPECIAL_MOD_ASSIGN,
+	SPECIAL_LTE,
+	SPECIAL_GTE,
+	SPECIAL_EQUAL,
+	SPECIAL_NOTEQUAL,
+	SPECIAL_LOGICAL_AND,
+	SPECIAL_AND_ASSIGN,
+	SPECIAL_LOGICAL_OR,
+	SPECIAL_OR_ASSIGN,
+	SPECIAL_XOR_ASSIGN,
+	SPECIAL_HASHHASH,
+	SPECIAL_LEFTSHIFT,
+	SPECIAL_RIGHTSHIFT,
+	SPECIAL_DOTDOT,
+	SPECIAL_SHL_ASSIGN,
+	SPECIAL_SHR_ASSIGN,
+	SPECIAL_ELLIPSIS,
+	SPECIAL_ARG_SEPARATOR,
+	SPECIAL_UNSIGNED_LT,
+	SPECIAL_UNSIGNED_GT,
+	SPECIAL_UNSIGNED_LTE,
+	SPECIAL_UNSIGNED_GTE,
+};
+
+struct string {
+	unsigned int length;
+	char data[];
+};
+
+/* will fit into 32 bits */
+struct argcount {
+	unsigned normal:10;
+	unsigned quoted:10;
+	unsigned str:10;
+	unsigned vararg:1;
+};
+
+/*
+ * This is a very common data structure, it should be kept
+ * as small as humanly possible. Big (rare) types go as
+ * pointers.
+ */
+struct token {
+	struct position pos;
+	struct token *next;
+	union {
+		const char *number;
+		struct ident *ident;
+		unsigned int special;
+		struct string *string;
+		int character;
+		int argnum;
+		struct argcount count;
+	};
+};
+
+#define MAX_STRING 4095
+
+static inline struct token *containing_token(struct token **p)
+{
+	void *addr = (char *)p - ((char *)&((struct token *)0)->next - (char *)0);
+	return addr;
+}
+
+#define token_type(x) ((x)->pos.type)
+
+/*
+ * Last token in the stream - points to itself.
+ * This allows us to not test for NULL pointers
+ * when following the token->next chain..
+ */
+extern struct token eof_token_entry;
+#define eof_token(x) ((x) == &eof_token_entry)
+
+extern int init_stream(const char *, int fd, const char **next_path);
+extern const char *stream_name(int stream);
+extern struct ident *hash_ident(struct ident *);
+extern struct ident *built_in_ident(const char *);
+extern struct token *built_in_token(int, const char *);
+extern const char *show_special(int);
+extern const char *show_ident(const struct ident *);
+extern const char *show_string(const struct string *string);
+extern const char *show_token(const struct token *);
+extern struct token * tokenize(const char *, int, struct token *, const char **next_path);
+extern struct token * tokenize_buffer(void *, unsigned long, struct token **);
+
+extern void show_identifier_stats(void);
+extern struct token *preprocess(struct token *);
+
+static inline int match_op(struct token *token, int op)
+{
+	return token->pos.type == TOKEN_SPECIAL && token->special == op;
+}
+
+static inline int match_ident(struct token *token, struct ident *id)
+{
+	return token->pos.type == TOKEN_IDENT && token->ident == id;
+}
+
+#endif
diff --git a/deps/sparse/tokenize.c b/deps/sparse/tokenize.c
new file mode 100644
index 0000000..d4f05e5
--- /dev/null
+++ b/deps/sparse/tokenize.c
@@ -0,0 +1,1002 @@
+/*
+ * This is a really stupid C tokenizer. It doesn't do any include
+ * files or anything complex at all. That's the preprocessor.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ *               2003 Linus Torvalds
+ *
+ *  Licensed under the Open Software License version 1.1
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "symbol.h"
+
+#define EOF (-1)
+
+int input_stream_nr = 0;
+struct stream *input_streams;
+static int input_streams_allocated;
+unsigned int tabstop = 8;
+
+#define BUFSIZE (8192)
+
+typedef struct {
+	int fd, offset, size;
+	int pos, line, nr;
+	int newline, whitespace;
+	struct token **tokenlist;
+	struct token *token;
+	unsigned char *buffer;
+} stream_t;
+
+const char *stream_name(int stream)
+{
+	if (stream < 0 || stream > input_stream_nr)
+		return "<bad stream>";
+	return input_streams[stream].name;
+}
+
+static struct position stream_pos(stream_t *stream)
+{
+	struct position pos;
+	pos.type = 0;
+	pos.stream = stream->nr;
+	pos.newline = stream->newline;
+	pos.whitespace = stream->whitespace;
+	pos.pos = stream->pos;
+	pos.line = stream->line;
+	pos.noexpand = 0;
+	return pos;
+}
+
+const char *show_special(int val)
+{
+	static char buffer[4];
+
+	buffer[0] = val;
+	buffer[1] = 0;
+	if (val >= SPECIAL_BASE)
+		strcpy(buffer, (char *) combinations[val - SPECIAL_BASE]);
+	return buffer;
+}
+
+const char *show_ident(const struct ident *ident)
+{
+	static char buffer[256];
+	if (!ident)
+		return "<noident>";
+	sprintf(buffer, "%.*s", ident->len, ident->name);
+	return buffer;
+}
+
+static char *charstr(char *ptr, unsigned char c, unsigned char escape, unsigned char next)
+{
+	if (isprint(c)) {
+		if (c == escape || c == '\\')
+			*ptr++ = '\\';
+		*ptr++ = c;
+		return ptr;
+	}
+	*ptr++ = '\\';
+	switch (c) {
+	case '\n':
+		*ptr++ = 'n';
+		return ptr;
+	case '\t':
+		*ptr++ = 't';
+		return ptr;
+	}
+	if (!isdigit(next))
+		return ptr + sprintf(ptr, "%o", c);
+		
+	return ptr + sprintf(ptr, "%03o", c);
+}
+
+const char *show_string(const struct string *string)
+{
+	static char buffer[4 * MAX_STRING + 3];
+	char *ptr;
+	int i;
+
+	if (!string->length)
+		return "<bad_string>";
+	ptr = buffer;
+	*ptr++ = '"';
+	for (i = 0; i < string->length-1; i++) {
+		const char *p = string->data + i;
+		ptr = charstr(ptr, p[0], '"', p[1]);
+	}
+	*ptr++ = '"';
+	*ptr = '\0';
+	return buffer;
+}
+
+const char *show_token(const struct token *token)
+{
+	static char buffer[256];
+
+	if (!token)
+		return "<no token>";
+	switch (token_type(token)) {
+	case TOKEN_ERROR:
+		return "syntax error";
+
+	case TOKEN_EOF:
+		return "end-of-input";
+
+	case TOKEN_IDENT:
+		return show_ident(token->ident);
+
+	case TOKEN_STRING:
+	case TOKEN_WIDE_STRING:
+		return show_string(token->string);
+
+	case TOKEN_NUMBER:
+		return token->number;
+
+	case TOKEN_SPECIAL:
+		return show_special(token->special);
+
+	case TOKEN_CHAR: 
+	case TOKEN_WIDE_CHAR: {
+		char *ptr = buffer;
+		int c = token->character;
+		*ptr++ = '\'';
+		ptr = charstr(ptr, c, '\'', 0);
+		*ptr++ = '\'';
+		*ptr++ = '\0';
+		return buffer;
+	}
+
+	case TOKEN_STREAMBEGIN:
+		sprintf(buffer, "<beginning of '%s'>", stream_name(token->pos.stream));
+		return buffer;
+
+	case TOKEN_STREAMEND:
+		sprintf(buffer, "<end of '%s'>", stream_name(token->pos.stream));
+		return buffer;
+
+	case TOKEN_UNTAINT:
+		sprintf(buffer, "<untaint>");
+		return buffer;
+
+	case TOKEN_ARG_COUNT:
+		sprintf(buffer, "<argcnt>");
+		return buffer;
+
+	default:
+		sprintf(buffer, "unhandled token type '%d' ", token_type(token));
+		return buffer;
+	}
+}
+
+#define HASHED_INPUT_BITS (6)
+#define HASHED_INPUT (1 << HASHED_INPUT_BITS)
+#define HASH_PRIME 0x9e370001UL
+
+static int input_stream_hashes[HASHED_INPUT] = { [0 ... HASHED_INPUT-1] = -1 };
+
+int *hash_stream(const char *name)
+{
+	uint32_t hash = 0;
+	unsigned char c;
+
+	while ((c = *name++) != 0)
+		hash = (hash + (c << 4) + (c >> 4)) * 11;
+
+	hash *= HASH_PRIME;
+	hash >>= 32 - HASHED_INPUT_BITS;
+	return input_stream_hashes + hash;
+}
+
+int init_stream(const char *name, int fd, const char **next_path)
+{
+	int stream = input_stream_nr, *hash;
+	struct stream *current;
+
+	if (stream >= input_streams_allocated) {
+		int newalloc = stream * 4 / 3 + 10;
+		input_streams = realloc(input_streams, newalloc * sizeof(struct stream));
+		if (!input_streams)
+			die("Unable to allocate more streams space");
+		input_streams_allocated = newalloc;
+	}
+	current = input_streams + stream;
+	memset(current, 0, sizeof(*current));
+	current->name = name;
+	current->fd = fd;
+	current->next_path = next_path;
+	current->path = NULL;
+	current->constant = CONSTANT_FILE_MAYBE;
+	input_stream_nr = stream+1;
+	hash = hash_stream(name);
+	current->next_stream = *hash;
+	*hash = stream;
+	return stream;
+}
+
+static struct token * alloc_token(stream_t *stream)
+{
+	struct token *token = __alloc_token(0);
+	token->pos = stream_pos(stream);
+	return token;
+}
+
+/*
+ *  Argh...  That was surprisingly messy - handling '\r' complicates the
+ *  things a _lot_.
+ */
+static int nextchar_slow(stream_t *stream)
+{
+	int offset = stream->offset;
+	int size = stream->size;
+	int c;
+	int spliced = 0, had_cr, had_backslash, complain;
+
+restart:
+	had_cr = had_backslash = complain = 0;
+
+repeat:
+	if (offset >= size) {
+		if (stream->fd < 0)
+			goto got_eof;
+		size = read(stream->fd, stream->buffer, BUFSIZE);
+		if (size <= 0)
+			goto got_eof;
+		stream->size = size;
+		stream->offset = offset = 0;
+	}
+
+	c = stream->buffer[offset++];
+
+	if (had_cr && c != '\n')
+		complain = 1;
+
+	if (c == '\r') {
+		had_cr = 1;
+		goto repeat;
+	}
+
+	stream->pos += (c == '\t') ? (tabstop - stream->pos % tabstop) : 1;
+
+	if (c == '\n') {
+		stream->line++;
+		stream->pos = 0;
+	}
+
+	if (!had_backslash) {
+		if (c == '\\') {
+			had_backslash = 1;
+			goto repeat;
+		}
+		if (c == '\n')
+			stream->newline = 1;
+	} else {
+		if (c == '\n') {
+			if (complain)
+				warning(stream_pos(stream), "non-ASCII data stream");
+			spliced = 1;
+			goto restart;
+		}
+		stream->pos--;
+		offset--;
+		c = '\\';
+	}
+
+out:
+	stream->offset = offset;
+	if (complain)
+		warning(stream_pos(stream), "non-ASCII data stream");
+
+	return c;
+
+got_eof:
+	if (had_backslash) {
+		c = '\\';
+		goto out;
+	}
+	if (stream->pos)
+		warning(stream_pos(stream), "no newline at end of file");
+	else if (had_cr)
+		warning(stream_pos(stream), "non-ASCII data stream");
+	else if (spliced)
+		warning(stream_pos(stream), "backslash-newline at end of file");
+	return EOF;
+}
+
+/*
+ *  We want that as light as possible while covering all normal cases.
+ *  Slow path (including the logics with line-splicing and EOF sanity
+ *  checks) is in nextchar_slow().
+ */
+static inline int nextchar(stream_t *stream)
+{
+	int offset = stream->offset;
+
+	if (offset < stream->size) {
+		int c = stream->buffer[offset++];
+		static const char special[256] = {
+			['\t'] = 1, ['\r'] = 1, ['\n'] = 1, ['\\'] = 1
+		};
+		if (!special[c]) {
+			stream->offset = offset;
+			stream->pos++;
+			return c;
+		}
+	}
+	return nextchar_slow(stream);
+}
+
+struct token eof_token_entry;
+
+static struct token *mark_eof(stream_t *stream)
+{
+	struct token *end;
+
+	end = alloc_token(stream);
+	token_type(end) = TOKEN_STREAMEND;
+	end->pos.newline = 1;
+
+	eof_token_entry.next = &eof_token_entry;
+	eof_token_entry.pos.newline = 1;
+
+	end->next =  &eof_token_entry;
+	*stream->tokenlist = end;
+	stream->tokenlist = NULL;
+	return end;
+}
+
+static void add_token(stream_t *stream)
+{
+	struct token *token = stream->token;
+
+	stream->token = NULL;
+	token->next = NULL;
+	*stream->tokenlist = token;
+	stream->tokenlist = &token->next;
+}
+
+static void drop_token(stream_t *stream)
+{
+	stream->newline |= stream->token->pos.newline;
+	stream->whitespace |= stream->token->pos.whitespace;
+	stream->token = NULL;
+}
+
+enum {
+	Letter = 1,
+	Digit = 2,
+	Hex = 4,
+	Exp = 8,
+	Dot = 16,
+	ValidSecond = 32,
+};
+
+static const long cclass[257] = {
+	['0' + 1 ... '9' + 1] = Digit | Hex,
+	['A' + 1 ... 'D' + 1] = Letter | Hex,
+	['E' + 1] = Letter | Hex | Exp,
+	['F' + 1] = Letter | Hex,
+	['G' + 1 ... 'O' + 1] = Letter,
+	['P' + 1] = Letter | Exp,
+	['Q' + 1 ... 'Z' + 1] = Letter,
+	['a' + 1 ... 'd' + 1] = Letter | Hex,
+	['e' + 1] = Letter | Hex | Exp,
+	['f' + 1] = Letter | Hex,
+	['g' + 1 ... 'o' + 1] = Letter,
+	['p' + 1] = Letter | Exp,
+	['q' + 1 ... 'z' + 1] = Letter,
+	['_' + 1] = Letter,
+	['.' + 1] = Dot | ValidSecond,
+	['=' + 1] = ValidSecond,
+	['+' + 1] = ValidSecond,
+	['-' + 1] = ValidSecond,
+	['>' + 1] = ValidSecond,
+	['<' + 1] = ValidSecond,
+	['&' + 1] = ValidSecond,
+	['|' + 1] = ValidSecond,
+	['#' + 1] = ValidSecond,
+};
+
+/*
+ * pp-number:
+ *	digit
+ *	. digit
+ *	pp-number digit
+ *	pp-number identifier-nodigit
+ *	pp-number e sign
+ *	pp-number E sign
+ *	pp-number p sign
+ *	pp-number P sign
+ *	pp-number .
+ */
+static int get_one_number(int c, int next, stream_t *stream)
+{
+	struct token *token;
+	static char buffer[4095];
+	char *p = buffer, *buf, *buffer_end = buffer + sizeof (buffer);
+	int len;
+
+	*p++ = c;
+	for (;;) {
+		long class =  cclass[next + 1];
+		if (!(class & (Dot | Digit | Letter)))
+			break;
+		if (p != buffer_end)
+			*p++ = next;
+		next = nextchar(stream);
+		if (class & Exp) {
+			if (next == '-' || next == '+') {
+				if (p != buffer_end)
+					*p++ = next;
+				next = nextchar(stream);
+			}
+		}
+	}
+
+	if (p == buffer_end) {
+		sparse_error(stream_pos(stream), "number token exceeds %td characters",
+		      buffer_end - buffer);
+		// Pretend we saw just "1".
+		buffer[0] = '1';
+		p = buffer + 1;
+	}
+
+	*p++ = 0;
+	len = p - buffer;
+	buf = __alloc_bytes(len);
+	memcpy(buf, buffer, len);
+
+	token = stream->token;
+	token_type(token) = TOKEN_NUMBER;
+	token->number = buf;
+	add_token(stream);
+
+	return next;
+}
+
+static int escapechar(int first, int type, stream_t *stream, int *valp)
+{
+	int next, value;
+
+	next = nextchar(stream);
+	value = first;
+
+	if (first == '\n')
+		warning(stream_pos(stream), "Newline in string or character constant");
+
+	if (first == '\\' && next != EOF) {
+		value = next;
+		next = nextchar(stream);
+		if (value != type) {
+			switch (value) {
+			case 'a':
+				value = '\a';
+				break;
+			case 'b':
+				value = '\b';
+				break;
+			case 't':
+				value = '\t';
+				break;
+			case 'n':
+				value = '\n';
+				break;
+			case 'v':
+				value = '\v';
+				break;
+			case 'f':
+				value = '\f';
+				break;
+			case 'r':
+				value = '\r';
+				break;
+			case 'e':
+				value = '\e';
+				break;
+			case '\\':
+				break;
+			case '?':
+				break;
+			case '\'':
+				break;
+			case '"':
+				break;
+			case '\n':
+				warning(stream_pos(stream), "Newline in string or character constant");
+				break;
+			case '0'...'7': {
+				int nr = 2;
+				value -= '0';
+				while (next >= '0' && next <= '7') {
+					value = (value << 3) + (next-'0');
+					next = nextchar(stream);
+					if (!--nr)
+						break;
+				}
+				value &= 0xff;
+				break;
+			}
+			case 'x': {
+				int hex = hexval(next);
+				if (hex < 16) {
+					value = hex;
+					next = nextchar(stream);
+					while ((hex = hexval(next)) < 16) {
+						value = (value << 4) + hex;
+						next = nextchar(stream);
+					}
+					value &= 0xff;
+					break;
+				}
+			}
+			/* Fall through */
+			default:
+				warning(stream_pos(stream), "Unknown escape '%c'", value);
+			}
+		}
+		/* Mark it as escaped */
+		value |= 0x100;
+	}
+	*valp = value;
+	return next;
+}
+
+static int get_char_token(int next, stream_t *stream, enum token_type type)
+{
+	int value;
+	struct token *token;
+
+	next = escapechar(next, '\'', stream, &value);
+	if (value == '\'' || next != '\'') {
+		sparse_error(stream_pos(stream), "Bad character constant");
+		drop_token(stream);
+		return next;
+	}
+
+	token = stream->token;
+	token_type(token) = type;
+	token->character = value & 0xff;
+
+	add_token(stream);
+	return nextchar(stream);
+}
+
+static int get_string_token(int next, stream_t *stream, enum token_type type)
+{
+	static char buffer[MAX_STRING];
+	struct string *string;
+	struct token *token;
+	int len = 0;
+
+	for (;;) {
+		int val;
+		next = escapechar(next, '"', stream, &val);
+		if (val == '"')
+			break;
+		if (next == EOF) {
+			warning(stream_pos(stream), "End of file in middle of string");
+			return next;
+		}
+		if (len < MAX_STRING)
+			buffer[len] = val;
+		len++;
+	}
+
+	if (len > MAX_STRING) {
+		warning(stream_pos(stream), "string too long (%d bytes, %d bytes max)", len, MAX_STRING);
+		len = MAX_STRING;
+	}
+
+	string = __alloc_string(len+1);
+	memcpy(string->data, buffer, len);
+	string->data[len] = '\0';
+	string->length = len+1;
+
+	/* Pass it on.. */
+	token = stream->token;
+	token_type(token) = type;
+	token->string = string;
+	add_token(stream);
+	
+	return next;
+}
+
+static int drop_stream_eoln(stream_t *stream)
+{
+	drop_token(stream);
+	for (;;) {
+		switch (nextchar(stream)) {
+		case EOF:
+			return EOF;
+		case '\n':
+			return nextchar(stream);
+		}
+	}
+}
+
+static int drop_stream_comment(stream_t *stream)
+{
+	int newline;
+	int next;
+	drop_token(stream);
+	newline = stream->newline;
+
+	next = nextchar(stream);
+	for (;;) {
+		int curr = next;
+		if (curr == EOF) {
+			warning(stream_pos(stream), "End of file in the middle of a comment");
+			return curr;
+		}
+		next = nextchar(stream);
+		if (curr == '*' && next == '/')
+			break;
+	}
+	stream->newline = newline;
+	return nextchar(stream);
+}
+
+unsigned char combinations[][4] = COMBINATION_STRINGS;
+
+#define NR_COMBINATIONS (SPECIAL_ARG_SEPARATOR - SPECIAL_BASE)
+
+/* hash function for two-character punctuators - all give unique values */
+#define special_hash(c0, c1) (((c0*8+c1*2)+((c0*8+c1*2)>>5))&31)
+
+/*
+ * note that we won't get false positives - special_hash(0,0) is 0 and
+ * entry 0 is filled (by +=), so all the missing ones are OK.
+ */
+static unsigned char hash_results[32][2] = {
+#define RES(c0, c1) [special_hash(c0, c1)] = {c0, c1}
+	RES('+', '='), /* 00 */
+	RES('/', '='), /* 01 */
+	RES('^', '='), /* 05 */
+	RES('&', '&'), /* 07 */
+	RES('#', '#'), /* 08 */
+	RES('<', '<'), /* 0a */
+	RES('<', '='), /* 0c */
+	RES('!', '='), /* 0e */
+	RES('%', '='), /* 0f */
+	RES('-', '-'), /* 10 */
+	RES('-', '='), /* 11 */
+	RES('-', '>'), /* 13 */
+	RES('=', '='), /* 15 */
+	RES('&', '='), /* 17 */
+	RES('*', '='), /* 18 */
+	RES('.', '.'), /* 1a */
+	RES('+', '+'), /* 1b */
+	RES('|', '='), /* 1c */
+	RES('>', '='), /* 1d */
+	RES('|', '|'), /* 1e */
+	RES('>', '>')  /* 1f */
+#undef RES
+};
+static int code[32] = {
+#define CODE(c0, c1, value) [special_hash(c0, c1)] = value
+	CODE('+', '=', SPECIAL_ADD_ASSIGN), /* 00 */
+	CODE('/', '=', SPECIAL_DIV_ASSIGN), /* 01 */
+	CODE('^', '=', SPECIAL_XOR_ASSIGN), /* 05 */
+	CODE('&', '&', SPECIAL_LOGICAL_AND), /* 07 */
+	CODE('#', '#', SPECIAL_HASHHASH), /* 08 */
+	CODE('<', '<', SPECIAL_LEFTSHIFT), /* 0a */
+	CODE('<', '=', SPECIAL_LTE), /* 0c */
+	CODE('!', '=', SPECIAL_NOTEQUAL), /* 0e */
+	CODE('%', '=', SPECIAL_MOD_ASSIGN), /* 0f */
+	CODE('-', '-', SPECIAL_DECREMENT), /* 10 */
+	CODE('-', '=', SPECIAL_SUB_ASSIGN), /* 11 */
+	CODE('-', '>', SPECIAL_DEREFERENCE), /* 13 */
+	CODE('=', '=', SPECIAL_EQUAL), /* 15 */
+	CODE('&', '=', SPECIAL_AND_ASSIGN), /* 17 */
+	CODE('*', '=', SPECIAL_MUL_ASSIGN), /* 18 */
+	CODE('.', '.', SPECIAL_DOTDOT), /* 1a */
+	CODE('+', '+', SPECIAL_INCREMENT), /* 1b */
+	CODE('|', '=', SPECIAL_OR_ASSIGN), /* 1c */
+	CODE('>', '=', SPECIAL_GTE), /* 1d */
+	CODE('|', '|', SPECIAL_LOGICAL_OR), /* 1e */
+	CODE('>', '>', SPECIAL_RIGHTSHIFT)  /* 1f */
+#undef CODE
+};
+
+static int get_one_special(int c, stream_t *stream)
+{
+	struct token *token;
+	int next, value, i;
+
+	next = nextchar(stream);
+
+	/*
+	 * Check for numbers, strings, character constants, and comments
+	 */
+	switch (c) {
+	case '.':
+		if (next >= '0' && next <= '9')
+			return get_one_number(c, next, stream);
+		break;
+	case '"':
+		return get_string_token(next, stream, TOKEN_STRING);
+	case '\'':
+		return get_char_token(next, stream, TOKEN_CHAR);
+	case '/':
+		if (next == '/')
+			return drop_stream_eoln(stream);
+		if (next == '*')
+			return drop_stream_comment(stream);
+	}
+
+	/*
+	 * Check for combinations
+	 */
+	value = c;
+	if (cclass[next + 1] & ValidSecond) {
+		i = special_hash(c, next);
+		if (hash_results[i][0] == c && hash_results[i][1] == next) {
+			value = code[i];
+			next = nextchar(stream);
+			if (value >= SPECIAL_LEFTSHIFT &&
+			    next == "==."[value - SPECIAL_LEFTSHIFT]) {
+				value += 3;
+				next = nextchar(stream);
+			}
+		}
+	}
+
+	/* Pass it on.. */
+	token = stream->token;
+	token_type(token) = TOKEN_SPECIAL;
+	token->special = value;
+	add_token(stream);
+	return next;
+}
+
+#define IDENT_HASH_BITS (13)
+#define IDENT_HASH_SIZE (1<<IDENT_HASH_BITS)
+#define IDENT_HASH_MASK (IDENT_HASH_SIZE-1)
+
+#define ident_hash_init(c)		(c)
+#define ident_hash_add(oldhash,c)	((oldhash)*11 + (c))
+#define ident_hash_end(hash)		((((hash) >> IDENT_HASH_BITS) + (hash)) & IDENT_HASH_MASK)
+
+static struct ident *hash_table[IDENT_HASH_SIZE];
+static int ident_hit, ident_miss, idents;
+
+void show_identifier_stats(void)
+{
+	int i;
+	int distribution[100];
+
+	fprintf(stderr, "identifiers: %d hits, %d misses\n",
+		ident_hit, ident_miss);
+
+	for (i = 0; i < 100; i++)
+		distribution[i] = 0;
+
+	for (i = 0; i < IDENT_HASH_SIZE; i++) {
+		struct ident * ident = hash_table[i];
+		int count = 0;
+
+		while (ident) {
+			count++;
+			ident = ident->next;
+		}
+		if (count > 99)
+			count = 99;
+		distribution[count]++;
+	}
+
+	for (i = 0; i < 100; i++) {
+		if (distribution[i])
+			fprintf(stderr, "%2d: %d buckets\n", i, distribution[i]);
+	}
+}
+
+static struct ident *alloc_ident(const char *name, int len)
+{
+	struct ident *ident = __alloc_ident(len);
+	ident->symbols = NULL;
+	ident->len = len;
+	ident->tainted = 0;
+	memcpy(ident->name, name, len);
+	return ident;
+}
+
+static struct ident * insert_hash(struct ident *ident, unsigned long hash)
+{
+	ident->next = hash_table[hash];
+	hash_table[hash] = ident;
+	ident_miss++;
+	return ident;
+}
+
+static struct ident *create_hashed_ident(const char *name, int len, unsigned long hash)
+{
+	struct ident *ident;
+	struct ident **p;
+
+	p = &hash_table[hash];
+	while ((ident = *p) != NULL) {
+		if (ident->len == (unsigned char) len) {
+			if (strncmp(name, ident->name, len) != 0)
+				goto next;
+
+			ident_hit++;
+			return ident;
+		}
+next:
+		//misses++;
+		p = &ident->next;
+	}
+	ident = alloc_ident(name, len);
+	*p = ident;
+	ident->next = NULL;
+	ident_miss++;
+	idents++;
+	return ident;
+}
+
+static unsigned long hash_name(const char *name, int len)
+{
+	unsigned long hash;
+	const unsigned char *p = (const unsigned char *)name;
+
+	hash = ident_hash_init(*p++);
+	while (--len) {
+		unsigned int i = *p++;
+		hash = ident_hash_add(hash, i);
+	}
+	return ident_hash_end(hash);
+}
+
+struct ident *hash_ident(struct ident *ident)
+{
+	return insert_hash(ident, hash_name(ident->name, ident->len));
+}
+
+struct ident *built_in_ident(const char *name)
+{
+	int len = strlen(name);
+	return create_hashed_ident(name, len, hash_name(name, len));
+}
+
+struct token *built_in_token(int stream, const char *name)
+{
+	struct token *token;
+
+	token = __alloc_token(0);
+	token->pos.stream = stream;
+	token_type(token) = TOKEN_IDENT;
+	token->ident = built_in_ident(name);
+	return token;
+}
+
+static int get_one_identifier(int c, stream_t *stream)
+{
+	struct token *token;
+	struct ident *ident;
+	unsigned long hash;
+	char buf[256];
+	int len = 1;
+	int next;
+
+	hash = ident_hash_init(c);
+	buf[0] = c;
+	for (;;) {
+		next = nextchar(stream);
+		if (!(cclass[next + 1] & (Letter | Digit)))
+			break;
+		if (len >= sizeof(buf))
+			break;
+		hash = ident_hash_add(hash, next);
+		buf[len] = next;
+		len++;
+	};
+	hash = ident_hash_end(hash);
+
+	ident = create_hashed_ident(buf, len, hash);
+
+	if (ident == &L_ident) {
+		if (next == '\'')
+			return get_char_token(nextchar(stream), stream, TOKEN_WIDE_CHAR);
+		if (next == '\"')
+			return get_string_token(nextchar(stream), stream, TOKEN_WIDE_STRING);
+	}
+
+	/* Pass it on.. */
+	token = stream->token;
+	token_type(token) = TOKEN_IDENT;
+	token->ident = ident;
+	add_token(stream);
+	return next;
+}		
+
+static int get_one_token(int c, stream_t *stream)
+{
+	long class = cclass[c + 1];
+	if (class & Digit)
+		return get_one_number(c, nextchar(stream), stream);
+	if (class & Letter)
+		return get_one_identifier(c, stream);
+	return get_one_special(c, stream);
+}
+
+static struct token *setup_stream(stream_t *stream, int idx, int fd,
+	unsigned char *buf, unsigned int buf_size)
+{
+	struct token *begin;
+
+	stream->nr = idx;
+	stream->line = 1;
+	stream->newline = 1;
+	stream->whitespace = 0;
+	stream->pos = 0;
+
+	stream->token = NULL;
+	stream->fd = fd;
+	stream->offset = 0;
+	stream->size = buf_size;
+	stream->buffer = buf;
+
+	begin = alloc_token(stream);
+	token_type(begin) = TOKEN_STREAMBEGIN;
+	stream->tokenlist = &begin->next;
+	return begin;
+}
+
+static struct token *tokenize_stream(stream_t *stream)
+{
+	int c = nextchar(stream);
+	while (c != EOF) {
+		if (!isspace(c)) {
+			struct token *token = alloc_token(stream);
+			stream->token = token;
+			stream->newline = 0;
+			stream->whitespace = 0;
+			c = get_one_token(c, stream);
+			continue;
+		}
+		stream->whitespace = 1;
+		c = nextchar(stream);
+	}
+	return mark_eof(stream);
+}
+
+struct token * tokenize_buffer(void *buffer, unsigned long size, struct token **endtoken)
+{
+	stream_t stream;
+	struct token *begin;
+
+	begin = setup_stream(&stream, 0, -1, buffer, size);
+	*endtoken = tokenize_stream(&stream);
+	return begin;
+}
+
+struct token * tokenize(const char *name, int fd, struct token *endtoken, const char **next_path)
+{
+	struct token *begin, *end;
+	stream_t stream;
+	unsigned char buffer[BUFSIZE];
+	int idx;
+
+	idx = init_stream(name, fd, next_path);
+	if (idx < 0) {
+		// info(endtoken->pos, "File %s is const", name);
+		return endtoken;
+	}
+
+	begin = setup_stream(&stream, idx, fd, buffer, 0);
+	end = tokenize_stream(&stream);
+	if (endtoken)
+		end->next = endtoken;
+	return begin;
+}
diff --git a/deps/sparse/unssa.c b/deps/sparse/unssa.c
new file mode 100644
index 0000000..3eea9b2
--- /dev/null
+++ b/deps/sparse/unssa.c
@@ -0,0 +1,139 @@
+/*
+ * UnSSA - translate the SSA back to normal form.
+ *
+ * For now it's done by replacing to set of copies:
+ * 1) For each phi-node, replace all their phisrc by copies to a common
+ *    temporary.
+ * 2) Replace all the phi-nodes by copies of the temporaries to the phi-node target.
+ *    This is node to preserve the semantic of the phi-node (they should all "execute"
+ *    simultaneously on entry in the basic block in which they belong).
+ *
+ * This is similar to the "Sreedhar method I" except that the copies to the
+ * temporaries are not placed at the end of the predecessor basic blocks, but
+ * at the place where the phi-node operands are defined (the same place where the
+ * phisrc were present).
+ * Is this a problem? 
+ *
+ * While very simple this method create a lot more copies that really necessary.
+ * Ideally, "Sreedhar method III" should be used:
+ * "Translating Out of Static Single Assignment Form", V. C. Sreedhar, R. D.-C. Ju,
+ * D. M. Gillies and V. Santhanam.  SAS'99, Vol. 1694 of Lecture Notes in Computer
+ * Science, Springer-Verlag, pp. 194-210, 1999.
+ *
+ * Copyright (C) 2005 Luc Van Oostenryck
+ */
+
+#include "lib.h"
+#include "linearize.h"
+#include "allocate.h"
+#include "flow.h"
+#include <assert.h>
+
+
+static void remove_phisrc_defines(struct instruction *phisrc)
+{
+	struct instruction *phi;
+	struct basic_block *bb = phisrc->bb;
+
+	FOR_EACH_PTR(phisrc->phi_users, phi) {
+		remove_pseudo(&bb->defines, phi->target);
+	} END_FOR_EACH_PTR(phi);
+}
+
+static void replace_phi_node(struct instruction *phi)
+{
+	pseudo_t tmp;
+
+	tmp = alloc_pseudo(NULL);
+	tmp->type = phi->target->type;
+	tmp->ident = phi->target->ident;
+	tmp->def = NULL;		// defined by all the phisrc
+	
+	// update the current liveness
+	remove_pseudo(&phi->bb->needs, phi->target);
+	add_pseudo(&phi->bb->needs, tmp);
+	track_phi_uses(phi);
+
+	phi->opcode = OP_COPY;
+	phi->src = tmp;
+
+	// FIXME: free phi->phi_list;
+}
+
+static void rewrite_phi_bb(struct basic_block *bb)
+{
+	struct instruction *insn;
+
+	// Replace all the phi-nodes by copies of a temporary
+	// (which represent the set of all the %phi that feed them).
+	// The target pseudo doesn't change.
+	FOR_EACH_PTR(bb->insns, insn) {
+		if (!insn->bb)
+			continue;
+		if (insn->opcode != OP_PHI)
+			continue;
+		replace_phi_node(insn);
+	} END_FOR_EACH_PTR(insn);
+}
+
+static void rewrite_phisrc_bb(struct basic_block *bb)
+{
+	struct instruction *insn;
+
+	// Replace all the phisrc by one or several copies to
+	// the temporaries associated to each phi-node it defines.
+	FOR_EACH_PTR_REVERSE(bb->insns, insn) {
+		struct instruction *phi;
+		int i;
+
+		if (!insn->bb)
+			continue;
+		if (insn->opcode != OP_PHISOURCE)
+			continue;
+
+		i = 0;
+		FOR_EACH_PTR(insn->phi_users, phi) {
+			pseudo_t tmp = phi->src;
+			pseudo_t src = insn->phi_src;
+
+			if (i == 0) {	// first phi: we overwrite the phisrc
+				insn->opcode = OP_COPY;
+				insn->target = tmp;
+				insn->src = src;
+			} else {
+				struct instruction *copy = __alloc_instruction(0);
+
+				copy->bb = bb;
+				copy->opcode = OP_COPY;
+				copy->size = insn->size;
+				copy->pos = insn->pos;
+				copy->target = tmp;
+				copy->src = src;
+
+				INSERT_CURRENT(copy, insn);
+			}
+			// update the liveness info
+			remove_phisrc_defines(insn);
+			// FIXME: should really something like add_pseudo_exclusive()
+			add_pseudo(&bb->defines, tmp);
+
+			i++;
+		} END_FOR_EACH_PTR(phi);
+
+	} END_FOR_EACH_PTR_REVERSE(insn);
+}
+
+int unssa(struct entrypoint *ep)
+{
+	struct basic_block *bb;
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		rewrite_phi_bb(bb);
+	} END_FOR_EACH_PTR(bb);
+
+	FOR_EACH_PTR(ep->bbs, bb) {
+		rewrite_phisrc_bb(bb);
+	} END_FOR_EACH_PTR(bb);
+
+	return 0;
+}
diff --git a/deps/sparse/validation/.gitignore b/deps/sparse/validation/.gitignore
new file mode 100644
index 0000000..77276ba
--- /dev/null
+++ b/deps/sparse/validation/.gitignore
@@ -0,0 +1,4 @@
+# test-suite
+*.diff
+*.got
+*.expected
diff --git a/deps/sparse/validation/address_space.c b/deps/sparse/validation/address_space.c
new file mode 100644
index 0000000..c55b78d
--- /dev/null
+++ b/deps/sparse/validation/address_space.c
@@ -0,0 +1,17 @@
+#define __user __attribute__((address_space(1)))
+
+extern int poke_memory(void *addr);
+
+static int sys_do_stuff(void __user *user_addr)
+{
+	return poke_memory(user_addr);
+}
+/*
+ * check-name: address_space attribute
+ *
+ * check-error-start
+address_space.c:7:28: warning: incorrect type in argument 1 (different address spaces)
+address_space.c:7:28:    expected void *addr
+address_space.c:7:28:    got void <asn:1>*user_addr
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/asm-empty-clobber.c b/deps/sparse/validation/asm-empty-clobber.c
new file mode 100644
index 0000000..eb1e105
--- /dev/null
+++ b/deps/sparse/validation/asm-empty-clobber.c
@@ -0,0 +1,28 @@
+
+# define __ASM_FORM(x)  " " #x " "
+# define JUMP_LABEL_INITIAL_NOP ".byte 0xe9 \n\t .long 0\n\t"
+# define __ASM_SEL(a,b) __ASM_FORM(b)
+#define _ASM_PTR        __ASM_SEL(.long, .quad)
+
+# define JUMP_LABEL(key, label)                                 \
+       do {                                                    \
+               asm goto("1:"                                   \
+                       JUMP_LABEL_INITIAL_NOP                  \
+                       ".pushsection __jump_table,  \"a\" \n\t"\
+                       _ASM_PTR "1b, %l[" #label "], %c0 \n\t" \
+                       ".popsection \n\t"                      \
+                       : :  "i" (key) :  : label);             \
+       } while (0)
+
+int main(int argc, char *argv[])
+{
+       JUMP_LABEL("1", do_trace );
+       return 1;
+do_trace:
+       return 0;
+}
+
+/*
+ *  check-name: Asm with goto labels.
+ */
+
diff --git a/deps/sparse/validation/asm-goto-lables.c b/deps/sparse/validation/asm-goto-lables.c
new file mode 100644
index 0000000..ac2bf2a
--- /dev/null
+++ b/deps/sparse/validation/asm-goto-lables.c
@@ -0,0 +1,22 @@
+static inline int __static_cpu_has(unsigned char bit)
+{
+       asm goto("1: jmp %l[t_no]\n"
+                "2:\n"
+                ".section .altinstructions,\"a\"\n"
+                "\n"
+                "1b\n"
+                "0\n"         /* no replacement */
+                " .byte %P0\n"         /* feature bit */
+                " .byte 2b - 1b\n"     /* source len */
+                " .byte 0\n"           /* replacement len */
+                " .byte 0xff + 0 - (2b-1b)\n"  /* padding */
+                ".previous\n"
+                : : "i" (bit) : : t_no, ble);
+       return 1;
+t_no:
+       return 0;
+}
+/*
+ *  check-name: Asm with goto labels.
+ */
+
diff --git a/deps/sparse/validation/attr-warning.c b/deps/sparse/validation/attr-warning.c
new file mode 100644
index 0000000..1c0976f
--- /dev/null
+++ b/deps/sparse/validation/attr-warning.c
@@ -0,0 +1,8 @@
+# define __warndecl(name, msg) \
+  extern void name (void) __attribute__((__warning__ (msg)))
+
+__warndecl (__warn_func, "warn message");
+
+/*
+ * check-name: attribute warning
+ */
diff --git a/deps/sparse/validation/attr_in_parameter.c b/deps/sparse/validation/attr_in_parameter.c
new file mode 100644
index 0000000..1b104ea
--- /dev/null
+++ b/deps/sparse/validation/attr_in_parameter.c
@@ -0,0 +1,12 @@
+#define A __attribute__((address_space(1)))
+static int (A *p);
+static int A *q;
+static void (*f)(A int *x, A int *y) = (void *)0;
+static void g(int A *x)
+{
+	f(x, x);
+	p = q;
+}
+/*
+ * check-name: attribute after ( in direct-declarator
+ */
diff --git a/deps/sparse/validation/attr_vector_size.c b/deps/sparse/validation/attr_vector_size.c
new file mode 100644
index 0000000..6982922
--- /dev/null
+++ b/deps/sparse/validation/attr_vector_size.c
@@ -0,0 +1,7 @@
+typedef unsigned int u32;
+typedef u32 __attribute__((vector_size(16))) sse128_t;
+
+/*
+ * check-name: attribute vector_size
+ */
+
diff --git a/deps/sparse/validation/bad-array-designated-initializer.c b/deps/sparse/validation/bad-array-designated-initializer.c
new file mode 100644
index 0000000..fb7d91f
--- /dev/null
+++ b/deps/sparse/validation/bad-array-designated-initializer.c
@@ -0,0 +1,13 @@
+static int a[] = {
+	[0] = 0,		// OK
+	[\0] = 1,		// KO
+};
+/*
+ * check-name: Bad array designated initializer
+ *
+ * check-error-start
+bad-array-designated-initializer.c:3:10: error: Expected constant expression
+bad-array-designated-initializer.c:3:10: error: Expected } at end of initializer
+bad-array-designated-initializer.c:3:10: error: got \
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/bad-assignment.c b/deps/sparse/validation/bad-assignment.c
new file mode 100644
index 0000000..71938db
--- /dev/null
+++ b/deps/sparse/validation/bad-assignment.c
@@ -0,0 +1,14 @@
+static int foo(int a)
+{
+	a |=\1;
+
+	return a;
+}
+/*
+ * check-name: bad assignment
+ *
+ * check-error-start
+bad-assignment.c:3:13: error: Expected ; at end of statement
+bad-assignment.c:3:13: error: got \
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/bad-cast.c b/deps/sparse/validation/bad-cast.c
new file mode 100644
index 0000000..bf577e0
--- /dev/null
+++ b/deps/sparse/validation/bad-cast.c
@@ -0,0 +1,15 @@
+struct st;
+
+static int foo(int a)
+{
+	return (struct/st *) a;
+}
+/*
+ * check-name: Bad cast syntax
+ *
+ * check-error-start
+bad-cast.c:5:23: error: expected declaration
+bad-cast.c:5:23: error: Expected ) at end of cast operator
+bad-cast.c:5:23: error: got /
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/bad-ternary-cond.c b/deps/sparse/validation/bad-ternary-cond.c
new file mode 100644
index 0000000..e3d07b5
--- /dev/null
+++ b/deps/sparse/validation/bad-ternary-cond.c
@@ -0,0 +1,12 @@
+static int foo(int a)
+{
+	return a ?? 1 : 0;
+}
+/*
+ * check-name: Bad ternary syntax
+ * check-description: Once caused Sparse to segfault
+ * check-error-start
+bad-ternary-cond.c:3:19: error: Expected : in conditional expression
+bad-ternary-cond.c:3:19: error: got ?
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/bad-typeof.c b/deps/sparse/validation/bad-typeof.c
new file mode 100644
index 0000000..90c3e42
--- /dev/null
+++ b/deps/sparse/validation/bad-typeof.c
@@ -0,0 +1,14 @@
+static int fun(void)
+{
+	typeof() a;
+	int b;
+
+	a = b;
+}
+/*
+ * check-name: Bad typeof syntax segfault
+ *
+ * check-error-start
+bad-typeof.c:3:16: error: expected expression after the '(' token
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/badtype1.c b/deps/sparse/validation/badtype1.c
new file mode 100644
index 0000000..ced7f9f
--- /dev/null
+++ b/deps/sparse/validation/badtype1.c
@@ -0,0 +1,6 @@
+static void foo(enum bar baz);
+
+/*
+ * check-name: enum not in scope
+ * check-known-to-fail
+ */
diff --git a/deps/sparse/validation/badtype2.c b/deps/sparse/validation/badtype2.c
new file mode 100644
index 0000000..90a5fa1
--- /dev/null
+++ b/deps/sparse/validation/badtype2.c
@@ -0,0 +1,24 @@
+//typedef int undef;
+extern undef bar(void);
+static undef foo(char *c)
+{
+  char p = *c;
+  switch (p) {
+  default:
+    return bar();
+  }
+}
+
+/*
+ * check-name: missing type
+ * check-error-start
+badtype2.c:2:14: error: Expected ; at end of declaration
+badtype2.c:2:14: error: got bar
+badtype2.c:3:14: error: Expected ; at end of declaration
+badtype2.c:3:14: error: got foo
+badtype2.c:6:3: error: Trying to use reserved word 'switch' as identifier
+badtype2.c:7:3: error: not in switch scope
+badtype2.c:10:1: error: Expected ; at the end of type declaration
+badtype2.c:10:1: error: got }
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/badtype3.c b/deps/sparse/validation/badtype3.c
new file mode 100644
index 0000000..20f346c
--- /dev/null
+++ b/deps/sparse/validation/badtype3.c
@@ -0,0 +1,27 @@
+int
+foo (int (*func) (undef, void *), void *data)
+{
+  int err = 0;
+  while (cur) {
+    if ((*func) (cur, data))
+      break;
+  }
+  return err;
+}
+
+/*
+ * check-name: missing type in argument list
+ * check-error-start
+badtype3.c:2:18: warning: identifier list not in definition
+badtype3.c:2:24: error: Expected ) in function declarator
+badtype3.c:2:24: error: got ,
+badtype3.c:5:3: error: Trying to use reserved word 'while' as identifier
+badtype3.c:7:7: error: break/continue not in iterator scope
+badtype3.c:9:3: error: Trying to use reserved word 'return' as identifier
+badtype3.c:9:10: error: Expected ; at end of declaration
+badtype3.c:9:10: error: got err
+badtype3.c:10:1: error: Expected ; at the end of type declaration
+badtype3.c:10:1: error: got }
+badtype3.c:6:11: error: undefined identifier 'func'
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/badtype4.c b/deps/sparse/validation/badtype4.c
new file mode 100644
index 0000000..7421ba4
--- /dev/null
+++ b/deps/sparse/validation/badtype4.c
@@ -0,0 +1,15 @@
+void a(void)
+{
+	switch(x) {
+	case 1:
+		break;
+	}
+}
+/*
+ * check-name: switch(bad_type) {...} segfault
+ *
+ * check-error-start
+badtype4.c:3:16: error: undefined identifier 'x'
+badtype4.c:4:14: error: incompatible types for 'case' statement
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/binary-constant.c b/deps/sparse/validation/binary-constant.c
new file mode 100644
index 0000000..a589403
--- /dev/null
+++ b/deps/sparse/validation/binary-constant.c
@@ -0,0 +1,7 @@
+extern int x;
+
+int x = 0b11;
+
+/*
+ * check-name: inline compound literals
+ */
diff --git a/deps/sparse/validation/bitfields.c b/deps/sparse/validation/bitfields.c
new file mode 100644
index 0000000..ea24841
--- /dev/null
+++ b/deps/sparse/validation/bitfields.c
@@ -0,0 +1,21 @@
+/*
+ * Al Viro points out that we don't
+ * do bitfield -> integer promotions
+ * for array dereferences
+ *
+ * "warning: a.c:16:10: incompatible types for operation"
+ */
+static struct {
+	int x:4;
+} y;
+
+extern int a[];
+
+static int b(void)
+{
+	return a[y.x];
+}
+
+/*
+ * check-name: bitfield to integer promotion
+ */
diff --git a/deps/sparse/validation/bug_inline_switch.c b/deps/sparse/validation/bug_inline_switch.c
new file mode 100644
index 0000000..9578824
--- /dev/null
+++ b/deps/sparse/validation/bug_inline_switch.c
@@ -0,0 +1,25 @@
+
+#define __u16 unsigned short
+int foo(__u16 n);
+static inline __u16 f(__u16 val)
+{
+       return val;
+}
+
+static inline unsigned int bar(__u16 n)
+{
+      switch (n) {
+      case (1 ? 1 : f(1)):
+              return 4;
+      }
+}
+
+int foo(__u16 n)
+{
+       bar(n);
+       bar(n);
+       return 0;
+}
+/*
+ * check-name: inlining switch statement
+ */
diff --git a/deps/sparse/validation/builtin_safe1.c b/deps/sparse/validation/builtin_safe1.c
new file mode 100644
index 0000000..eeddcc8
--- /dev/null
+++ b/deps/sparse/validation/builtin_safe1.c
@@ -0,0 +1,38 @@
+#define MY_MACRO(a) do { \
+  __builtin_warning(!__builtin_safe_p(a), "Macro argument with side effects: " #a); \
+    a;	\
+  } while (0)
+
+int g(int);
+int h(int) __attribute__((pure));
+int i(int) __attribute__((const));
+
+static int foo(int x, int y)
+{
+  /* unsafe: */
+  MY_MACRO(x++);
+  MY_MACRO(x+=1);
+  MY_MACRO(x=x+1);
+  MY_MACRO(x%=y);
+  MY_MACRO(x=y);
+  MY_MACRO(g(x));
+  MY_MACRO((y,g(x)));
+  /* safe: */
+  MY_MACRO(x+1);
+  MY_MACRO(h(x));
+  MY_MACRO(i(x));
+  return x;
+}
+
+/*
+ * check-name: __builtin_safe
+ * check-error-start
+builtin_safe1.c:13:3: warning: Macro argument with side effects: x++
+builtin_safe1.c:14:3: warning: Macro argument with side effects: x+=1
+builtin_safe1.c:15:3: warning: Macro argument with side effects: x=x+1
+builtin_safe1.c:16:3: warning: Macro argument with side effects: x%=y
+builtin_safe1.c:17:3: warning: Macro argument with side effects: x=y
+builtin_safe1.c:18:3: warning: Macro argument with side effects: g(x)
+builtin_safe1.c:19:3: warning: Macro argument with side effects: (y,g(x))
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/builtin_unreachable.c b/deps/sparse/validation/builtin_unreachable.c
new file mode 100644
index 0000000..29799b5
--- /dev/null
+++ b/deps/sparse/validation/builtin_unreachable.c
@@ -0,0 +1,15 @@
+/* example from gcc documents */
+
+void function_that_never_returns (void);
+
+static int g (int c)
+{
+	if (c)
+		return 1;
+	function_that_never_returns ();
+	__builtin_unreachable ();
+}
+     
+/*
+ * check-name: __builtin_unreachable()
+ */
diff --git a/deps/sparse/validation/calling-convention-attributes.c b/deps/sparse/validation/calling-convention-attributes.c
new file mode 100644
index 0000000..1c1c876
--- /dev/null
+++ b/deps/sparse/validation/calling-convention-attributes.c
@@ -0,0 +1,26 @@
+extern void __attribute__((cdecl)) c1(void);
+typedef void (__attribute__((cdecl)) *c2)(void);
+typedef c2 c2ptr;
+
+extern void __attribute__((__cdecl__)) c_1(void);
+typedef void (__attribute__((__cdecl__)) *c_2)(void);
+typedef c_2 c_2ptr;
+
+extern void __attribute__((stdcall)) s1(void);
+typedef void (__attribute__((stdcall)) *s2)(void);
+typedef s2 s2ptr;
+
+extern void __attribute__((__stdcall__)) s_1(void);
+typedef void (__attribute__((__stdcall__)) *s_2)(void);
+typedef s_2 s_2ptr;
+
+extern void __attribute__((fastcall)) f1(void);
+typedef void (__attribute__((fastcall)) *f2)(void);
+typedef f2 f2ptr;
+
+extern void __attribute__((__fastcall__)) f_1(void);
+typedef void (__attribute__((__fastcall__)) *f_2)(void);
+typedef f_2 f_2ptr;
+/*
+ * check-name: Calling convention attributes
+ */
diff --git a/deps/sparse/validation/check_byte_count-ice.c b/deps/sparse/validation/check_byte_count-ice.c
new file mode 100644
index 0000000..58b98ce
--- /dev/null
+++ b/deps/sparse/validation/check_byte_count-ice.c
@@ -0,0 +1,14 @@
+extern void *memset (void *s, int c, int n);
+
+static void foo(void *a)
+{
+	memset(foo, + ', 20);
+}
+/*
+ * check-name: Segfault in check_byte_count after syntax error
+ *
+ * check-error-start
+check_byte_count-ice.c:5:25: error: Bad character constant
+check_byte_count-ice.c:5:15: error: not enough arguments for function memset
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/choose_expr.c b/deps/sparse/validation/choose_expr.c
new file mode 100644
index 0000000..f6fd84c
--- /dev/null
+++ b/deps/sparse/validation/choose_expr.c
@@ -0,0 +1,17 @@
+static int x = __builtin_choose_expr(0,(char *)0,(void)0);
+static int y = __builtin_choose_expr(1,(char *)0,(void)0);
+static char s[42];
+static int z = 1/(sizeof(__builtin_choose_expr(1,s,0)) - 42);
+
+/*
+ * check-name: choose expr builtin
+ * check-error-start
+choose_expr.c:1:51: warning: incorrect type in initializer (different base types)
+choose_expr.c:1:51:    expected int static [signed] [toplevel] x
+choose_expr.c:1:51:    got void <noident>
+choose_expr.c:2:41: warning: incorrect type in initializer (different base types)
+choose_expr.c:2:41:    expected int static [signed] [toplevel] y
+choose_expr.c:2:41:    got char *<noident>
+choose_expr.c:4:17: warning: division by zero
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/comma.c b/deps/sparse/validation/comma.c
new file mode 100644
index 0000000..374948e
--- /dev/null
+++ b/deps/sparse/validation/comma.c
@@ -0,0 +1,12 @@
+static char a[sizeof(char *) + 1];
+static char b[1/(sizeof(a) - sizeof(0,a))];
+static void f(void)
+{
+        int c[42];
+        typeof((void)0,c) d;
+        d = c;
+}
+/*
+ * check-name: Comma and array decay
+ * check-description: arguments of comma should degenerate
+ */
diff --git a/deps/sparse/validation/compare-null-to-int.c b/deps/sparse/validation/compare-null-to-int.c
new file mode 100644
index 0000000..08e556b
--- /dev/null
+++ b/deps/sparse/validation/compare-null-to-int.c
@@ -0,0 +1,11 @@
+static unsigned int comparison = (void *)0 == 1;
+/*
+ * check-name: Compare null pointer constant to int
+ * check-description: Sparse used to allow this.
+ *
+ * check-error-start
+compare-null-to-int.c:1:44: error: incompatible types for operation (==)
+compare-null-to-int.c:1:44:    left side has type void *
+compare-null-to-int.c:1:44:    right side has type int
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/cond_expr.c b/deps/sparse/validation/cond_expr.c
new file mode 100644
index 0000000..e55711c
--- /dev/null
+++ b/deps/sparse/validation/cond_expr.c
@@ -0,0 +1,19 @@
+/*
+ *  Bug in original tree: (real_v ? : x) had been treated as equivalent of
+ *  (real_v == 0 ? real_v == 0 : x), which gives the wrong type (and no
+ *  warning from the testcase below).
+ */
+static int x;
+static double y;
+int a(void)
+{
+	return ~(y ? : x);	/* should warn */
+}
+/*
+ * check-name: Two-argument conditional expression types
+ *
+ * check-error-start
+cond_expr.c:10:16: error: incompatible types for operation (~)
+cond_expr.c:10:16:    argument has type double
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/cond_expr2.c b/deps/sparse/validation/cond_expr2.c
new file mode 100644
index 0000000..5e974cf
--- /dev/null
+++ b/deps/sparse/validation/cond_expr2.c
@@ -0,0 +1,22 @@
+extern const int *p;
+extern volatile void *q;
+extern volatile int *r;
+static void f(void)
+{
+	q = 1 ? p : q;	// warn: const volatile void * -> const int *
+	r = 1 ? r : q;	// OK: volatile void * -> volatile int *
+	r = 1 ? r : p;	// warn: const volatile int * -> volatile int *
+}
+/*
+ * check-name: type of conditional expression
+ * check-description: Used to miss qualifier mixing and mishandle void *
+ *
+ * check-error-start
+cond_expr2.c:6:11: warning: incorrect type in assignment (different modifiers)
+cond_expr2.c:6:11:    expected void volatile *extern [addressable] [toplevel] q
+cond_expr2.c:6:11:    got void const volatile *
+cond_expr2.c:8:11: warning: incorrect type in assignment (different modifiers)
+cond_expr2.c:8:11:    expected int volatile *extern [addressable] [toplevel] [assigned] r
+cond_expr2.c:8:11:    got int const volatile *
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/context.c b/deps/sparse/validation/context.c
new file mode 100644
index 0000000..33b70b8
--- /dev/null
+++ b/deps/sparse/validation/context.c
@@ -0,0 +1,336 @@
+#define __cond_lock(c) ((c) ? ({ __context__(1); 1; }) : 0)
+
+static void a(void) __attribute__((context(0,1)))
+{
+	__context__(1);
+}
+
+static void r(void) __attribute__((context(1,0)))
+{
+	__context__(-1);
+}
+
+extern void _ca(int fail);
+#define ca(fail) __cond_lock(_ca(fail))
+
+static void good_paired1(void)
+{
+	a();
+	r();
+}
+
+static void good_paired2(void)
+{
+	a();
+	r();
+	a();
+	r();
+}
+
+static void good_paired3(void)
+{
+	a();
+	a();
+	r();
+	r();
+}
+
+static void good_lock1(void) __attribute__((context(0,1)))
+{
+	a();
+}
+
+static void good_lock2(void) __attribute__((context(0,1)))
+{
+	a();
+	r();
+	a();
+}
+
+static void good_lock3(void) __attribute__((context(0,1)))
+{
+	a();
+	a();
+	r();
+}
+
+static void good_unlock1(void) __attribute__((context(1,0)))
+{
+	r();
+}
+
+static void good_unlock2(void) __attribute__((context(1,0)))
+{
+	a();
+	r();
+	r();
+}
+
+static void warn_lock1(void)
+{
+	a();
+}
+
+static void warn_lock2(void)
+{
+	a();
+	r();
+	a();
+}
+
+static void warn_lock3(void)
+{
+	a();
+	a();
+	r();
+}
+
+static void warn_unlock1(void)
+{
+	r();
+}
+
+static void warn_unlock2(void)
+{
+	a();
+	r();
+	r();
+}
+
+extern int condition, condition2;
+
+static int good_if1(void)
+{
+	a();
+	if(condition) {
+		r();
+		return -1;
+	}
+	r();
+	return 0;
+}
+
+static void good_if2(void)
+{
+	if(condition) {
+		a();
+		r();
+	}
+}
+
+static void good_if3(void)
+{
+	a();
+	if(condition) {
+		a();
+		r();
+	}
+	r();
+}
+
+static int warn_if1(void)
+{
+	a();
+	if(condition)
+		return -1;
+	r();
+	return 0;
+}
+
+static int warn_if2(void)
+{
+	a();
+	if(condition) {
+		r();
+		return -1;
+	}
+	return 0;
+}
+
+static void good_while1(void)
+{
+	a();
+	while(condition)
+		;
+	r();
+}
+
+static void good_while2(void)
+{
+	while(condition) {
+		a();
+		r();
+	}
+}
+
+static void good_while3(void)
+{
+	while(condition) {
+		a();
+		r();
+		if(condition2)
+			break;
+		a();
+		r();
+	}
+}
+
+static void good_while4(void)
+{
+	a();
+	while(1) {
+		if(condition2) {
+			r();
+			break;
+		}
+	}
+}
+
+static void good_while5(void)
+{
+	a();
+	while(1) {
+		r();
+		if(condition2)
+			break;
+		a();
+	}
+}
+
+static void warn_while1(void)
+{
+	while(condition) {
+		a();
+	}
+}
+
+static void warn_while2(void)
+{
+	while(condition) {
+		r();
+	}
+}
+
+static void warn_while3(void)
+{
+	while(condition) {
+		a();
+		if(condition2)
+			break;
+		r();
+	}
+}
+
+static void good_goto1(void)
+{
+    a();
+    goto label;
+label:
+    r();
+}
+
+static void good_goto2(void)
+{
+    a();
+    goto label;
+    a();
+    r();
+label:
+    r();
+}
+
+static void good_goto3(void)
+{
+    a();
+    if(condition)
+        goto label;
+    a();
+    r();
+label:
+    r();
+}
+
+static void good_goto4(void)
+{
+    if(condition)
+        goto label;
+    a();
+    r();
+label:
+    ;
+}
+
+static void good_goto5(void)
+{
+    a();
+    if(condition)
+        goto label;
+    r();
+    return;
+label:
+    r();
+}
+
+static void warn_goto1(void)
+{
+    a();
+    goto label;
+    r();
+label:
+    ;
+}
+
+static void warn_goto2(void)
+{
+    a();
+    goto label;
+    r();
+label:
+    a();
+    r();
+}
+
+static void warn_goto3(void)
+{
+    a();
+    if(condition)
+        goto label;
+    r();
+label:
+    r();
+}
+
+static void good_cond_lock1(void)
+{
+    if(ca(condition)) {
+        condition2 = 1; /* do stuff */
+        r();
+    }
+}
+
+static void warn_cond_lock1(void)
+{
+    if(ca(condition))
+        condition2 = 1; /* do stuff */
+    r();
+}
+/*
+ * check-name: Check -Wcontext
+ *
+ * check-error-start
+context.c:69:13: warning: context imbalance in 'warn_lock1' - wrong count at exit
+context.c:74:13: warning: context imbalance in 'warn_lock2' - wrong count at exit
+context.c:81:13: warning: context imbalance in 'warn_lock3' - wrong count at exit
+context.c:88:13: warning: context imbalance in 'warn_unlock1' - unexpected unlock
+context.c:93:13: warning: context imbalance in 'warn_unlock2' - unexpected unlock
+context.c:131:12: warning: context imbalance in 'warn_if1' - wrong count at exit
+context.c:140:12: warning: context imbalance in 'warn_if2' - different lock contexts for basic block
+context.c:202:9: warning: context imbalance in 'warn_while1' - different lock contexts for basic block
+context.c:210:17: warning: context imbalance in 'warn_while2' - unexpected unlock
+context.c:216:9: warning: context imbalance in 'warn_while3' - wrong count at exit
+context.c:274:13: warning: context imbalance in 'warn_goto1' - wrong count at exit
+context.c:283:13: warning: context imbalance in 'warn_goto2' - wrong count at exit
+context.c:300:5: warning: context imbalance in 'warn_goto3' - different lock contexts for basic block
+context.c:315:5: warning: context imbalance in 'warn_cond_lock1' - different lock contexts for basic block
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/declaration-after-statement-ansi.c b/deps/sparse/validation/declaration-after-statement-ansi.c
new file mode 100644
index 0000000..22635cf
--- /dev/null
+++ b/deps/sparse/validation/declaration-after-statement-ansi.c
@@ -0,0 +1,12 @@
+static void func (int i)
+{
+	i;
+	int j = i;
+}
+/*
+ * check-name: declaration after statement (ANSI)
+ * check-command: sparse -ansi $file
+ * check-error-start
+declaration-after-statement-ansi.c:4:9: warning: mixing declarations and code
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/declaration-after-statement-c89.c b/deps/sparse/validation/declaration-after-statement-c89.c
new file mode 100644
index 0000000..886f971
--- /dev/null
+++ b/deps/sparse/validation/declaration-after-statement-c89.c
@@ -0,0 +1,12 @@
+static void func (int i)
+{
+	i;
+	int j = i;
+}
+/*
+ * check-name: declaration after statement (C89)
+ * check-command: sparse -std=c89 $file
+ * check-error-start
+declaration-after-statement-c89.c:4:9: warning: mixing declarations and code
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/declaration-after-statement-c99.c b/deps/sparse/validation/declaration-after-statement-c99.c
new file mode 100644
index 0000000..dd36e6e
--- /dev/null
+++ b/deps/sparse/validation/declaration-after-statement-c99.c
@@ -0,0 +1,9 @@
+static void func (int i)
+{
+	i;
+	int j = i;
+}
+/*
+ * check-name: declaration after statement (C99)
+ * check-command: sparse -std=c99 $file
+ */
diff --git a/deps/sparse/validation/declaration-after-statement-default.c b/deps/sparse/validation/declaration-after-statement-default.c
new file mode 100644
index 0000000..c3fe2cd
--- /dev/null
+++ b/deps/sparse/validation/declaration-after-statement-default.c
@@ -0,0 +1,9 @@
+static void func (int i)
+{
+	i;
+	int j = i;
+}
+/*
+ * check-name: declaration after statement (default)
+ * check-command: sparse $file
+ */
diff --git a/deps/sparse/validation/definitions.c b/deps/sparse/validation/definitions.c
new file mode 100644
index 0000000..fce7393
--- /dev/null
+++ b/deps/sparse/validation/definitions.c
@@ -0,0 +1,12 @@
+static inline int f(void);
+static int g(void)
+{
+        return f();
+}
+static inline int f(void)
+{
+	return 0;
+}
+/*
+ * check-name: finding definitions
+ */
diff --git a/deps/sparse/validation/designated-init.c b/deps/sparse/validation/designated-init.c
new file mode 100644
index 0000000..23423e9
--- /dev/null
+++ b/deps/sparse/validation/designated-init.c
@@ -0,0 +1,195 @@
+struct s1 {
+	int x;
+	int y;
+};
+
+struct s2 {
+	int x;
+	int y;
+} __attribute__((designated_init));
+
+struct nest1 {
+	struct s1 s1;
+	struct s2 s2;
+};
+
+struct nest2 {
+	struct s1 s1;
+	struct s2 s2;
+} __attribute__((designated_init));
+
+static struct s1 s1_positional = { 5, 10 };
+static struct s1 s1_designated = { .x = 5, .y = 10 };
+static struct s2 s2_positional = { 5, 10 };
+static struct s2 s2_designated = { .x = 5, .y = 10 };
+static struct nest1 nest1_positional = {
+	{ 5, 10 },
+	{ 5, 10 },
+};
+static struct nest1 nest1_designated_outer = {
+	.s1 = { 5, 10 },
+	.s2 = { 5, 10 },
+};
+static struct nest1 nest1_designated_inner = {
+	{ .x = 5, .y = 10 },
+	{ .x = 5, .y = 10 },
+};
+static struct nest1 nest1_designated_both = {
+	.s1 = { .x = 5, .y = 10 },
+	.s2 = { .x = 5, .y = 10 },
+};
+static struct nest2 nest2_positional = {
+	{ 5, 10 },
+	{ 5, 10 },
+};
+static struct nest2 nest2_designated_outer = {
+	.s1 = { 5, 10 },
+	.s2 = { 5, 10 },
+};
+static struct nest2 nest2_designated_inner = {
+	{ .x = 5, .y = 10 },
+	{ .x = 5, .y = 10 },
+};
+static struct nest2 nest2_designated_both = {
+	.s1 = { .x = 5, .y = 10 },
+	.s2 = { .x = 5, .y = 10 },
+};
+
+static struct {
+	int x;
+	int y;
+} __attribute__((designated_init))
+	anon_positional = { 5, 10 },
+	anon_designated = { .x = 5, .y = 10};
+
+static struct s1 s1_array[] = {
+	{ 5, 10 },
+	{ .x = 5, .y = 10 },
+};
+
+static struct s2 s2_array[] = {
+	{ 5, 10 },
+	{ .x = 5, .y = 10 },
+};
+
+static struct s1 ret_s1_positional(void)
+{
+	return ((struct s1){ 5, 10 });
+}
+
+static struct s1 ret_s1_designated(void)
+{
+	return ((struct s1){ .x = 5, .y = 10 });
+}
+
+static struct s2 ret_s2_positional(void)
+{
+	return ((struct s2){ 5, 10 });
+}
+
+static struct s2 ret_s2_designated(void)
+{
+	return ((struct s2){ .x = 5, .y = 10 });
+}
+
+static struct nest1 ret_nest1_positional(void)
+{
+	return ((struct nest1){
+			{ 5, 10 },
+			{ 5, 10 },
+		});
+}
+
+static struct nest1 ret_nest1_designated_outer(void)
+{
+	return ((struct nest1){
+			.s1 = { 5, 10 },
+			.s2 = { 5, 10 },
+		});
+}
+
+static struct nest1 ret_nest1_designated_inner(void)
+{
+	return ((struct nest1){
+			{ .x = 5, .y = 10 },
+			{ .x = 5, .y = 10 },
+		});
+}
+
+static struct nest1 ret_nest1_designated_both(void)
+{
+	return ((struct nest1){
+			.s1 = { .x = 5, .y = 10 },
+			.s2 = { .x = 5, .y = 10 },
+		});
+}
+
+static struct nest2 ret_nest2_positional(void)
+{
+	return ((struct nest2){
+			{ 5, 10 },
+			{ 5, 10 },
+		});
+}
+
+static struct nest2 ret_nest2_designated_outer(void)
+{
+	return ((struct nest2){
+			.s1 = { 5, 10 },
+			.s2 = { 5, 10 },
+		});
+}
+
+static struct nest2 ret_nest2_designated_inner(void)
+{
+	return ((struct nest2){
+			{ .x = 5, .y = 10 },
+			{ .x = 5, .y = 10 },
+		});
+}
+
+static struct nest2 ret_nest2_designated_both(void)
+{
+	return ((struct nest2){
+			.s1 = { .x = 5, .y = 10 },
+			.s2 = { .x = 5, .y = 10 },
+		});
+}
+/*
+ * check-name: designated_init attribute
+ *
+ * check-error-start
+designated-init.c:23:36: warning: in initializer for s2_positional: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:23:39: warning: in initializer for s2_positional: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:27:11: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:27:14: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:31:17: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:31:20: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:42:9: warning: in initializer for nest2_positional: positional init of field in struct nest2, declared with attribute designated_init
+designated-init.c:43:9: warning: in initializer for nest2_positional: positional init of field in struct nest2, declared with attribute designated_init
+designated-init.c:43:11: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:43:14: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:47:17: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:47:20: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:50:9: warning: in initializer for nest2_designated_inner: positional init of field in struct nest2, declared with attribute designated_init
+designated-init.c:51:9: warning: in initializer for nest2_designated_inner: positional init of field in struct nest2, declared with attribute designated_init
+designated-init.c:62:29: warning: in initializer for anon_positional: positional init of field in struct <noident>, declared with attribute designated_init
+designated-init.c:62:32: warning: in initializer for anon_positional: positional init of field in struct <noident>, declared with attribute designated_init
+designated-init.c:71:11: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:71:14: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:87:30: warning: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:87:33: warning: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:99:27: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:99:30: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:107:33: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:107:36: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:130:25: warning: positional init of field in struct nest2, declared with attribute designated_init
+designated-init.c:131:25: warning: positional init of field in struct nest2, declared with attribute designated_init
+designated-init.c:131:27: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:131:30: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:139:33: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:139:36: warning: in initializer for s2: positional init of field in struct s2, declared with attribute designated_init
+designated-init.c:146:25: warning: positional init of field in struct nest2, declared with attribute designated_init
+designated-init.c:147:25: warning: positional init of field in struct nest2, declared with attribute designated_init
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/double-semicolon.c b/deps/sparse/validation/double-semicolon.c
new file mode 100644
index 0000000..a1b8e63
--- /dev/null
+++ b/deps/sparse/validation/double-semicolon.c
@@ -0,0 +1,9 @@
+extern void *memset (void *s, int c, int n);
+static void test(void)
+{
+	struct { int foo;; } val;
+	memset(&val, 0, sizeof(val));
+}
+/*
+ * check-name: Double semicolon in struct
+ */
diff --git a/deps/sparse/validation/dubious-bitwise-with-not.c b/deps/sparse/validation/dubious-bitwise-with-not.c
new file mode 100644
index 0000000..c48bcae
--- /dev/null
+++ b/deps/sparse/validation/dubious-bitwise-with-not.c
@@ -0,0 +1,24 @@
+static unsigned int ok1  = !1 &&  2;
+static unsigned int bad1 = !1 &   2;
+static unsigned int ok2  = !1 ||  2;
+static unsigned int bad2 = !1 |   2;
+static unsigned int ok3  =  1 && !2;
+static unsigned int bad3 =  1 &  !2;
+static unsigned int ok4  =  1 || !2;
+static unsigned int bad4 =  1 |  !2;
+static unsigned int ok5  = !1 && !2;
+static unsigned int bad5 = !1 &  !2;
+static unsigned int ok6  = !1 || !2;
+static unsigned int bad6 = !1 |  !2;
+/*
+ * check-name: Dubious bitwise operation on !x
+ *
+ * check-error-start
+dubious-bitwise-with-not.c:2:31: warning: dubious: !x & y
+dubious-bitwise-with-not.c:4:31: warning: dubious: !x | y
+dubious-bitwise-with-not.c:6:31: warning: dubious: x & !y
+dubious-bitwise-with-not.c:8:31: warning: dubious: x | !y
+dubious-bitwise-with-not.c:10:31: warning: dubious: !x & !y
+dubious-bitwise-with-not.c:12:31: warning: dubious: !x | !y
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/enum_scope.c b/deps/sparse/validation/enum_scope.c
new file mode 100644
index 0000000..92ffc8e
--- /dev/null
+++ b/deps/sparse/validation/enum_scope.c
@@ -0,0 +1,11 @@
+enum {A = 12};
+
+static void f(void)
+{
+	enum {A = A + 1, B};
+	char s[1 - 2 * (B != 14)];
+}
+
+/*
+ * check-name: enumeration constants' scope [6.2.1p7]
+ */
diff --git a/deps/sparse/validation/escapes.c b/deps/sparse/validation/escapes.c
new file mode 100644
index 0000000..13f8f9c
--- /dev/null
+++ b/deps/sparse/validation/escapes.c
@@ -0,0 +1,21 @@
+static int e[] = { '\'', '\"', '\?', '\\',
+                   '\a', '\b', '\f', '\n', '\r', '\t', '\v',
+		   '\0', '\012', '\x7890', '\xabcd' };
+static char *s = "\'\"\?\\ \a\b\f\n\r\t\v \377\xcafe";
+
+static int bad_e[] = { '\c', '\0123', '\789', '\xdefg' };
+/*
+ * check-name: Character escape sequences
+ *
+ * check-error-start
+escapes.c:6:27: warning: Unknown escape 'c'
+escapes.c:6:35: error: Bad character constant
+escapes.c:6:38: error: Bad character constant
+escapes.c:6:42: error: Bad character constant
+escapes.c:6:46: error: Bad character constant
+escapes.c:6:53: error: Bad character constant
+escapes.c:6:56: error: Bad character constant
+escapes.c:6:42: error: Expected } at end of initializer
+escapes.c:6:42: error: got 89
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/extern-inline.c b/deps/sparse/validation/extern-inline.c
new file mode 100644
index 0000000..4f12ac0
--- /dev/null
+++ b/deps/sparse/validation/extern-inline.c
@@ -0,0 +1,23 @@
+extern __inline__ int f(int);
+
+extern __inline__ int
+f(int x)
+{
+        return x;
+}
+
+extern int g(int);
+
+extern __inline__ int
+g(int x)
+{
+        return x;
+}
+
+
+/*
+ * check-name: extern inline function
+ * check-command: sparse $file $file
+ * check-description: Extern inline function never emits stand alone copy
+ * of the function. It allows multiple such definitions in different file.
+ */
diff --git a/deps/sparse/validation/field-overlap.c b/deps/sparse/validation/field-overlap.c
new file mode 100644
index 0000000..a6abab2
--- /dev/null
+++ b/deps/sparse/validation/field-overlap.c
@@ -0,0 +1,16 @@
+static struct {
+	int x;
+	struct {
+		int z;
+		int w;
+	} y;
+} a = { .y.z = 1, .y.w = 2, };
+
+static struct {int x, y, z;} w[2] = {
+	{.x = 1, .y = 2, .z = 3},
+	{.x = 1, .y = 2, .z = 3}
+};
+
+/*
+ * check-name: field overlap
+ */
diff --git a/deps/sparse/validation/foul-bitwise.c b/deps/sparse/validation/foul-bitwise.c
new file mode 100644
index 0000000..9e21eab
--- /dev/null
+++ b/deps/sparse/validation/foul-bitwise.c
@@ -0,0 +1,30 @@
+typedef unsigned short __attribute__((bitwise))__le16;
+static __le16 foo(__le16 a)
+{
+	return a |= ~a;
+}
+
+static int baz(__le16 a)
+{
+	return ~a == ~a;
+}
+
+static int barf(__le16 a)
+{
+	return a == (a & ~a);
+}
+
+static __le16 bar(__le16 a)
+{
+	return -a;
+}
+
+/*
+ * check-name: foul bitwise
+ * check-error-start
+foul-bitwise.c:9:16: warning: restricted __le16 degrades to integer
+foul-bitwise.c:9:22: warning: restricted __le16 degrades to integer
+foul-bitwise.c:19:16: error: incompatible types for operation (-)
+foul-bitwise.c:19:16:    argument has type restricted __le16 [usertype] a
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/function-pointer-modifier-inheritance.c b/deps/sparse/validation/function-pointer-modifier-inheritance.c
new file mode 100644
index 0000000..3428715
--- /dev/null
+++ b/deps/sparse/validation/function-pointer-modifier-inheritance.c
@@ -0,0 +1,18 @@
+struct sk_buff;
+struct sock;
+
+extern int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb,
+                    int getfrag(void *from, char *to, int offset,
+                    int len,int odd, struct sk_buff *skb),
+                    void *from, int length);
+
+int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb,
+                    int (*getfrag)(void *from, char *to, int offset,
+                    int len,int odd, struct sk_buff *skb),
+                    void *from, int length)
+{
+    return 0;
+}
+/*
+ * check-name: Function pointer modifier inheritance
+ */
diff --git a/deps/sparse/validation/identifier_list.c b/deps/sparse/validation/identifier_list.c
new file mode 100644
index 0000000..4691989
--- /dev/null
+++ b/deps/sparse/validation/identifier_list.c
@@ -0,0 +1,18 @@
+typedef int T;
+void f(...);
+void g(*);
+void h(x,int);
+void i_OK(T);
+void j(x,T);
+/*
+ * check-name: identifier-list parsing
+ * check-error-start
+identifier_list.c:2:8: warning: variadic functions must have one named argument
+identifier_list.c:3:8: error: Expected ) in function declarator
+identifier_list.c:3:8: error: got *
+identifier_list.c:4:9: error: Expected ) in function declarator
+identifier_list.c:4:9: error: got ,
+identifier_list.c:6:9: error: Expected ) in function declarator
+identifier_list.c:6:9: error: got ,
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/init-char-array.c b/deps/sparse/validation/init-char-array.c
new file mode 100644
index 0000000..5ede9bd
--- /dev/null
+++ b/deps/sparse/validation/init-char-array.c
@@ -0,0 +1,18 @@
+/*
+ * for array of char {<string>} gets special treatment in initializer.
+ */
+static char *s[] = {"aaaaaaaaa"};
+static char t[][10] = {"aaaaaaaaa"};
+static char u[] = {"aaaaaaaaa"};
+static char v[] = "aaaaaaaaa";
+static void f(void)
+{
+	char x[1/(sizeof(s) == sizeof(char *))];
+	char y[1/(sizeof(u) == 10)];
+	char z[1/(sizeof(v) == 10)];
+	char w[1/(sizeof(t) == 10)];
+}
+
+/*
+ * check-name: char array initializers
+ */
diff --git a/deps/sparse/validation/initializer-entry-defined-twice.c b/deps/sparse/validation/initializer-entry-defined-twice.c
new file mode 100644
index 0000000..968e3dd
--- /dev/null
+++ b/deps/sparse/validation/initializer-entry-defined-twice.c
@@ -0,0 +1,53 @@
+/* Tests for the "Initializer entry defined twice" warning. */
+
+/* Initializing a struct field twice should trigger the warning. */
+struct normal {
+	int field1;
+	int field2;
+};
+
+static struct normal struct_error = {
+	.field1 = 0,
+	.field1 = 0
+};
+
+/* Initializing two different fields of a union should trigger the warning. */
+struct has_union {
+	int x;
+	union {
+		int a;
+		int b;
+	} y;
+	int z;
+};
+
+static struct has_union union_error = {
+	.y = {
+		.a = 0,
+		.b = 0
+	}
+};
+
+/* Empty structures can make two fields have the same offset in a struct.
+ * Initializing both should not trigger the warning. */
+struct empty { };
+
+struct same_offset {
+	struct empty field1;
+	int field2;
+};
+
+static struct same_offset not_an_error = {
+	.field1 = { },
+	.field2 = 0
+};
+/*
+ * check-name: Initializer entry defined twice
+ *
+ * check-error-start
+initializer-entry-defined-twice.c:10:10: warning: Initializer entry defined twice
+initializer-entry-defined-twice.c:11:10:   also defined here
+initializer-entry-defined-twice.c:26:18: warning: Initializer entry defined twice
+initializer-entry-defined-twice.c:27:18:   also defined here
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/inline_compound_literals.c b/deps/sparse/validation/inline_compound_literals.c
new file mode 100644
index 0000000..fc223ff
--- /dev/null
+++ b/deps/sparse/validation/inline_compound_literals.c
@@ -0,0 +1,22 @@
+struct foo {
+	int x;
+};
+
+static inline void baz(void)
+{
+	(struct foo) { .x = 0 };
+}
+
+static void barf(void)
+{
+	baz();
+}
+
+static void foo(void)
+{
+	baz();
+}
+
+/*
+ * check-name: inline compound literals
+ */
diff --git a/deps/sparse/validation/integer-promotions.c b/deps/sparse/validation/integer-promotions.c
new file mode 100644
index 0000000..4245fe2
--- /dev/null
+++ b/deps/sparse/validation/integer-promotions.c
@@ -0,0 +1,7 @@
+static int add_char(void)
+{
+	return (char) 127 + (char) 127 + (char) 2;
+}
+/*
+ * check-name: Integer promotions
+ */
diff --git a/deps/sparse/validation/label-asm.c b/deps/sparse/validation/label-asm.c
new file mode 100644
index 0000000..411020a
--- /dev/null
+++ b/deps/sparse/validation/label-asm.c
@@ -0,0 +1,12 @@
+#define barrier() __asm__ __volatile__("": : :"memory")
+
+static void f(void)
+{
+	barrier();
+l:
+	barrier();
+}
+/*
+ * check-name: Label followed by __asm__
+ * check-description: Sparse used to parse the __asm__ as modifying the label.
+ */
diff --git a/deps/sparse/validation/label-attr.c b/deps/sparse/validation/label-attr.c
new file mode 100644
index 0000000..a82d7bc
--- /dev/null
+++ b/deps/sparse/validation/label-attr.c
@@ -0,0 +1,9 @@
+static int foo(void)
+{
+       return 0;
+rtattr_failure: __attribute__ ((unused))
+       return -1;
+}
+/*
+ * check-name: Label attribute
+ */
diff --git a/deps/sparse/validation/label-scope.c b/deps/sparse/validation/label-scope.c
new file mode 100644
index 0000000..7af3d91
--- /dev/null
+++ b/deps/sparse/validation/label-scope.c
@@ -0,0 +1,12 @@
+static int f(int n)
+{
+	__label__ n;
+n:	return n;
+}
+static int g(int n)
+{
+n:	return n;
+}
+/*
+ * check-name: __label__ scope
+ */
diff --git a/deps/sparse/validation/local-label.c b/deps/sparse/validation/local-label.c
new file mode 100644
index 0000000..951b085
--- /dev/null
+++ b/deps/sparse/validation/local-label.c
@@ -0,0 +1,11 @@
+void f(unsigned long ip);
+static void g(void)
+{
+       if (1) {
+	     f(({ __label__ x; x: (unsigned long)&&x; }));
+       }
+       f(({ __label__ x; x: (unsigned long)&&x; }));
+}
+/*
+ * check-name: Local label
+ */
diff --git a/deps/sparse/validation/logical.c b/deps/sparse/validation/logical.c
new file mode 100644
index 0000000..3f97522
--- /dev/null
+++ b/deps/sparse/validation/logical.c
@@ -0,0 +1,17 @@
+extern int a(void);
+extern int b(void);
+extern int c(void);
+
+static int or(void)
+{
+	return a() || b() || c();
+}
+
+static int and(void)
+{
+	return a() && b() && c();
+}
+/*
+ * check-name: Logical and/or
+ */
+
diff --git a/deps/sparse/validation/member_of_typeof.c b/deps/sparse/validation/member_of_typeof.c
new file mode 100644
index 0000000..db863b0
--- /dev/null
+++ b/deps/sparse/validation/member_of_typeof.c
@@ -0,0 +1,10 @@
+static struct foo {int x;} v;
+static typeof(v) *p;
+static void bar(void)
+{
+	p->x = 0;
+}
+/*
+ * check-name: Expansion of typeof when dealing with member of struct
+ * check-description: Used to expand SYM_TYPEOF too late
+ */
diff --git a/deps/sparse/validation/missing-ident.c b/deps/sparse/validation/missing-ident.c
new file mode 100644
index 0000000..ce73983
--- /dev/null
+++ b/deps/sparse/validation/missing-ident.c
@@ -0,0 +1,18 @@
+int [2];
+int *;
+int (*);
+int ();
+int;
+struct foo;
+union bar {int x; int y;};
+struct baz {int x, :3, y:2;};
+/*
+ * check-name: handling of identifier-less declarations
+ *
+ * check-error-start
+missing-ident.c:1:8: warning: missing identifier in declaration
+missing-ident.c:2:6: warning: missing identifier in declaration
+missing-ident.c:3:8: warning: missing identifier in declaration
+missing-ident.c:4:7: warning: missing identifier in declaration
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/multi_typedef.c b/deps/sparse/validation/multi_typedef.c
new file mode 100644
index 0000000..d9ffd0f
--- /dev/null
+++ b/deps/sparse/validation/multi_typedef.c
@@ -0,0 +1,15 @@
+typedef int T, *P;
+static void f(void)
+{
+	unsigned P = 0;
+	unsigned x = P;
+}
+static void g(void)
+{
+	int P = 0;
+	int x = P;
+}
+/*
+ * check-name: typedefs with many declarators
+ * check-description: we didn't recognize P above as a typedef
+ */
diff --git a/deps/sparse/validation/nested-declarator.c b/deps/sparse/validation/nested-declarator.c
new file mode 100644
index 0000000..1efe20c
--- /dev/null
+++ b/deps/sparse/validation/nested-declarator.c
@@ -0,0 +1,29 @@
+typedef int T;
+extern void f(int);
+static void g(int x)
+{
+	int (T);
+	T = x;
+	f(T);
+}
+static void h(void)
+{
+	static int [2](T)[3];
+}
+static int [2](*p)[3];
+int i(void (void)(*f));
+int j(int [2](*));
+/*
+ * check-name: nested declarator vs. parameters
+ * check-error-start:
+nested-declarator.c:11:23: warning: missing identifier in declaration
+nested-declarator.c:11:23: error: Expected ; at the end of type declaration
+nested-declarator.c:11:23: error: got (
+nested-declarator.c:13:15: error: Expected ; at the end of type declaration
+nested-declarator.c:13:15: error: got (
+nested-declarator.c:14:18: error: Expected ) in function declarator
+nested-declarator.c:14:18: error: got (
+nested-declarator.c:15:14: error: Expected ) in function declarator
+nested-declarator.c:15:14: error: got (
+ * check-error-end:
+ */
diff --git a/deps/sparse/validation/nested-declarator2.c b/deps/sparse/validation/nested-declarator2.c
new file mode 100644
index 0000000..345a04b
--- /dev/null
+++ b/deps/sparse/validation/nested-declarator2.c
@@ -0,0 +1,41 @@
+typedef int T;
+extern void f1(int);
+extern void f2(T);
+static void (*f3)(int) = f2;
+static void (*f4)(T) = f1;
+extern void f5(void (int));
+extern void f6(void (T));
+static void z(int x)
+{
+	int (T) = x;
+	f5(f2);
+	f6(f3);
+}
+static void f8();
+static int (x) = 1;
+static void w1(y)
+int y;
+{
+	x = y;
+}
+static void w2(int ());
+static void w3(...);
+static void f9(__attribute__((mode(DI))) T);
+static void w4(int f(x,y));
+static void bad1(__attribute__((mode(DI))) x);
+static int (-bad2);
+static void [2](*bad3);
+/*
+ * check-name: more on handling of ( in direct-declarator
+ * check-error-start:
+nested-declarator2.c:17:1: warning: non-ANSI definition of function 'w1'
+nested-declarator2.c:21:21: warning: non-ANSI function declaration of function '<noident>'
+nested-declarator2.c:22:16: warning: variadic functions must have one named argument
+nested-declarator2.c:24:21: warning: identifier list not in definition
+nested-declarator2.c:25:45: error: don't know how to apply mode to incomplete type
+nested-declarator2.c:26:13: error: Expected ) in nested declarator
+nested-declarator2.c:26:13: error: got -
+nested-declarator2.c:27:16: error: Expected ; at the end of type declaration
+nested-declarator2.c:27:16: error: got (
+ * check-error-end:
+ */
diff --git a/deps/sparse/validation/noderef.c b/deps/sparse/validation/noderef.c
new file mode 100644
index 0000000..8c89f60
--- /dev/null
+++ b/deps/sparse/validation/noderef.c
@@ -0,0 +1,51 @@
+# define __A	__attribute__((noderef))
+
+struct x {
+	int a;
+	int b;
+};
+
+struct y {
+	int a[2];
+};
+
+static void h(void)
+{
+	char __A *p;
+	char __A * * q1;
+	char * __A * q2;
+	struct x __A *xp;
+	struct x __A x;
+	int __A *q;
+	int __A *r;
+	struct y __A *py;
+	
+	q1 = &p;
+	q2 = &p;	/* This should complain */
+
+	r = &*q;
+	r = q;
+	r = &*(q+1);	/* This should NOT complain */
+	r = q+1;
+
+	r = &xp->a;	/* This should NOT complain */
+	r = &xp->b;
+	r = &(*xp).a;
+	r = &(*xp).b;
+
+	r = &x.a;
+	r = &x.b;
+
+	r = py->a;
+	r = py->a+1;
+	r = &py->a[0];
+}
+/*
+ * check-name: noderef attribute
+ *
+ * check-error-start
+noderef.c:24:12: warning: incorrect type in assignment (different modifiers)
+noderef.c:24:12:    expected char *[noderef] *q2
+noderef.c:24:12:    got char [noderef] **<noident>
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/non-pointer-null.c b/deps/sparse/validation/non-pointer-null.c
new file mode 100644
index 0000000..10b8b47
--- /dev/null
+++ b/deps/sparse/validation/non-pointer-null.c
@@ -0,0 +1,8 @@
+static void *p = 0;
+/*
+ * check-name: Using plain integer as NULL pointer
+ *
+ * check-error-start
+non-pointer-null.c:1:18: warning: Using plain integer as NULL pointer
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/old-initializer-nowarn.c b/deps/sparse/validation/old-initializer-nowarn.c
new file mode 100644
index 0000000..4efe00d
--- /dev/null
+++ b/deps/sparse/validation/old-initializer-nowarn.c
@@ -0,0 +1,9 @@
+struct s {
+	int i;
+};
+
+static struct s the_s = { i: 1 };
+/*
+ * check-name: Old initializer with -Wno-old-initializer
+ * check-command: sparse -Wno-old-initializer
+ */
diff --git a/deps/sparse/validation/old-initializer.c b/deps/sparse/validation/old-initializer.c
new file mode 100644
index 0000000..48aeeaa
--- /dev/null
+++ b/deps/sparse/validation/old-initializer.c
@@ -0,0 +1,12 @@
+struct s {
+	int i;
+};
+
+static struct s the_s = { i: 1 };
+/*
+ * check-name: Old initializer
+ *
+ * check-error-start
+old-initializer.c:5:27: warning: obsolete struct initializer, use C99 syntax
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/outer-scope.c b/deps/sparse/validation/outer-scope.c
new file mode 100644
index 0000000..f86ffc7
--- /dev/null
+++ b/deps/sparse/validation/outer-scope.c
@@ -0,0 +1,16 @@
+#ifndef FOO
+struct st { int len; };
+#define FOO
+#else
+struct st;
+static int test(struct st *s);
+static int test(struct st *s)
+{
+	return s->len;
+}
+#endif
+/*
+ * check-name: There is no scope boundary between global and file scope
+ * check-description: Used to mess scopes with -include
+ * check-command: sparse -include $file $file
+ */
diff --git a/deps/sparse/validation/phase2/backslash b/deps/sparse/validation/phase2/backslash
new file mode 100644
index 0000000..29c85b4
--- /dev/null
+++ b/deps/sparse/validation/phase2/backslash
@@ -0,0 +1,62 @@
+/*
+ *	'\\' has a special meaning on phase 2 if and only if it is immediately
+ * followed by '\n'.  In any other position it's left alone as any other
+ * character.
+ *
+ * [5.1.1.2(1.2)]:
+ *   Each instance of a backslash character (\) immediately followed by
+ *   a new-line character is deleted, splicing physical source lines to
+ *   form logical source lines.  Only the last backslash on any physical
+ *   source line shall be eligible for being part of such a splice.
+ *   A source file that is not empty shall end in a new-line character,
+ *   which shall not be immediately preceded by a backslash character
+ *   before any such splicing takes place.
+ *
+ * Note that this happens on the phase 2, before we even think of any
+ * tokens.  In other words, splicing is ignorant of and transparent for
+ * the rest of tokenizer.
+ */
+
+#define A(x) #x
+#define B(x) A(x)
+/* This should result in "\a" */
+/* XXX: currently sparse produces "a" */
+/* Partially fixed: now it gives "\\a", which is a separate problem */
+B(\a)
+
+#define C\
+ 1
+/* This should give 1 */
+C
+
+#define D\
+1
+/* And this should give D, since '\n' is removed and we get no whitespace */
+/* XXX: currently sparse produces 1 */
+/* Fixed */
+D
+
+#define E '\\
+a'
+/* This should give '\a' - with no warnings issued */
+/* XXX: currently sparse complains a lot and ends up producing a */
+/* Fixed */
+E
+
+/* This should give nothing */
+/* XXX: currently sparse produces more junk */
+/* Fixed */
+// junk \
+more junk
+
+/* This should also give nothing */
+/* XXX: currently sparse produces / * comment * / */
+/* Fixed */
+/\
+* comment *\
+/
+
+/* And this should complain since final newline should not be eaten by '\\' */
+/* XXX: currently sparse does not notice */
+/* Fixed */
+\
diff --git a/deps/sparse/validation/phase3/comments b/deps/sparse/validation/phase3/comments
new file mode 100644
index 0000000..8f51a30
--- /dev/null
+++ b/deps/sparse/validation/phase3/comments
@@ -0,0 +1,9 @@
+/*
+ *  Each comment should be treated as if it had been a single space.
+ */
+
+/* This should give nothing */
+/* XXX: currently sparse produces Y */
+/* Fixed */
+#define X /*
+ */ Y
diff --git a/deps/sparse/validation/preprocessor/preprocessor1.c b/deps/sparse/validation/preprocessor/preprocessor1.c
new file mode 100644
index 0000000..7d81474
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor1.c
@@ -0,0 +1,14 @@
+#define func(x) x
+#define bar func(
+#define foo bar foo
+foo )
+/*
+ * check-name: Preprocessor #1
+ * check-description: Used to cause infinite recursion.
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+foo
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor10.c b/deps/sparse/validation/preprocessor/preprocessor10.c
new file mode 100644
index 0000000..02b56df
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor10.c
@@ -0,0 +1,19 @@
+/* concatenation of 'defi' and 'ned' should result in the same token
+ * we would get if we had 'defined' in the input stream.
+ */
+#define A
+#define B defi ## ned
+#if B(A)
+defined
+#else
+undefined
+#endif
+/*
+ * check-name: Preprocessor #10
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+defined
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor11.c b/deps/sparse/validation/preprocessor/preprocessor11.c
new file mode 100644
index 0000000..4b37664
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor11.c
@@ -0,0 +1,31 @@
+#define A(1) x
+#define B(x
+#define C(x,
+#define D(,)
+#define E(__VA_ARGS__)
+#define F(x+
+#define G(x...,
+#define H(x...,y)
+#define I(...+
+#define J(x,y)
+/*
+ * check-name: Preprocessor #11
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+
+ * check-output-end
+ *
+ * check-error-start
+preprocessor/preprocessor11.c:1:11: error: "1" may not appear in macro parameter list
+preprocessor/preprocessor11.c:2:11: error: missing ')' in macro parameter list
+preprocessor/preprocessor11.c:3:12: error: missing ')' in macro parameter list
+preprocessor/preprocessor11.c:4:11: error: parameter name missing
+preprocessor/preprocessor11.c:5:11: error: __VA_ARGS__ can only appear in the expansion of a C99 variadic macro
+preprocessor/preprocessor11.c:6:12: error: "+" may not appear in macro parameter list
+preprocessor/preprocessor11.c:7:12: error: missing ')' in macro parameter list
+preprocessor/preprocessor11.c:8:12: error: missing ')' in macro parameter list
+preprocessor/preprocessor11.c:9:11: error: missing ')' in macro parameter list
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor12.c b/deps/sparse/validation/preprocessor/preprocessor12.c
new file mode 100644
index 0000000..e23e53b
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor12.c
@@ -0,0 +1,18 @@
+/*
+ * GNU kludge
+ */
+#define A(x,...) x,##__VA_ARGS__
+A(1)
+A(1,2)
+A(1,2,3)
+/*
+ * check-name: Preprocessor #12
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+1
+1,2
+1,2,3
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor13.c b/deps/sparse/validation/preprocessor/preprocessor13.c
new file mode 100644
index 0000000..b1af855
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor13.c
@@ -0,0 +1,23 @@
+/*
+ * GNU kludge, corner case
+ */
+#define A(x,...) x##,##__VA_ARGS__
+A(1)
+A(1,2)
+A(1,2,3)
+/*
+ * check-name: Preprocessor #13
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+1
+1,2
+1,2,3
+ * check-output-end
+ *
+ * check-error-start
+preprocessor/preprocessor13.c:6:1: error: '##' failed: concatenation is not a valid token
+preprocessor/preprocessor13.c:7:1: error: '##' failed: concatenation is not a valid token
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor14.c b/deps/sparse/validation/preprocessor/preprocessor14.c
new file mode 100644
index 0000000..05fc248
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor14.c
@@ -0,0 +1,17 @@
+/*
+ * GNU kludge, another corner case
+ */
+#define A(x,y,...) ,##x##__VA_ARGS__
+A(,1)
+#define B(x,y,...) x##,##__VA_ARGS__
+B(,1)
+/*
+ * check-name: Preprocessor #14
+ * check-known-to-fail
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor15.c b/deps/sparse/validation/preprocessor/preprocessor15.c
new file mode 100644
index 0000000..df87751
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor15.c
@@ -0,0 +1,16 @@
+#define A defi
+#define B ned
+#define C(x,y) x##y
+#define D(x,y) C(x,y)
+#if D(A,B) B
+D(1,2)
+#endif
+/*
+ * check-name: Preprocessor #15
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+12
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor16.c b/deps/sparse/validation/preprocessor/preprocessor16.c
new file mode 100644
index 0000000..75a4a0b
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor16.c
@@ -0,0 +1,30 @@
+#if 0
+/*
+From 6.10.1(5):
+	Each directive's condition is checked in order.  If it evaluates
+	to false (zero), the group it controls is skipped: directives are
+	processed only through the name that determines the directive in
+	order to keep track of the level of nested conditionals; the rest
+	of the directives' preprocessing tokens are ignores, >>as are the
+	other preprocessing tokens in the group<<.
+
+In other words, bogus arguments of directives are silently ignored and
+so are text lines and non-directives (# <something unknown>).  We *do*
+complain about the things like double #else or #elif after #else, since
+they hit before we get to the level of groups.
+*/
+
+#define 1
+#undef 1
+#bullshit
+
+#endif
+/*
+ * check-name: Preprocessor #16
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor17.c b/deps/sparse/validation/preprocessor/preprocessor17.c
new file mode 100644
index 0000000..bd54fa6
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor17.c
@@ -0,0 +1,15 @@
+#if 0
+/* these should not warn */
+#ifdef (
+#endif
+#ifndef (
+#endif
+#endif
+/*
+ * check-name: Preprocessor #17
+ * check-command: sparse -E $file
+ * check-output-start
+
+
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor18.c b/deps/sparse/validation/preprocessor/preprocessor18.c
new file mode 100644
index 0000000..20169e8
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor18.c
@@ -0,0 +1,17 @@
+/* one warning for each, please... */
+#define 1
+#undef 1
+/*
+ * check-name: Preprocessor #18
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+
+ * check-output-end
+ *
+ * check-error-start
+preprocessor/preprocessor18.c:2:2: error: expected identifier to 'define'
+preprocessor/preprocessor18.c:3:2: error: expected identifier to 'undef'
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor19.c b/deps/sparse/validation/preprocessor/preprocessor19.c
new file mode 100644
index 0000000..e70dad1
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor19.c
@@ -0,0 +1,18 @@
+/* got burned by that - freed the new definition in the case when we had
+   warned and replaced the old one */
+#define A x
+#define A y
+A
+/*
+ * check-name: Preprocessor #19
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+y
+ * check-output-end
+ * check-error-start
+preprocessor/preprocessor19.c:4:9: warning: preprocessor token A redefined
+preprocessor/preprocessor19.c:3:9: this was the original definition
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor2.c b/deps/sparse/validation/preprocessor/preprocessor2.c
new file mode 100644
index 0000000..56abb53
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor2.c
@@ -0,0 +1,15 @@
+#define TWO a, b
+
+#define UNARY(x) BINARY(x)
+#define BINARY(x, y) x + y
+
+UNARY(TWO)
+/*
+ * check-name: Preprocessor #2
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+a + b
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor20.c b/deps/sparse/validation/preprocessor/preprocessor20.c
new file mode 100644
index 0000000..90e93f3
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor20.c
@@ -0,0 +1,14 @@
+#include "preprocessor20.h"
+#define X
+#define Y
+#include "preprocessor20.h"
+/*
+ * check-name: Preprocessor #20
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+A
+B
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor20.h b/deps/sparse/validation/preprocessor/preprocessor20.h
new file mode 100644
index 0000000..322c543
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor20.h
@@ -0,0 +1,6 @@
+#ifdef X
+B
+#endif
+#ifndef Y
+A
+#endif
diff --git a/deps/sparse/validation/preprocessor/preprocessor21.c b/deps/sparse/validation/preprocessor/preprocessor21.c
new file mode 100644
index 0000000..4b55a6b
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor21.c
@@ -0,0 +1,16 @@
+#if 1
+#if
+/*
+ * check-name: Preprocessor #21
+ * check-description: This used to hang Sparse.
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+
+ * check-output-end
+ *
+ * check-error-start
+preprocessor/preprocessor21.c:2:2: error: unterminated preprocessor conditional
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor22.c b/deps/sparse/validation/preprocessor/preprocessor22.c
new file mode 100644
index 0000000..af5bcb3
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor22.c
@@ -0,0 +1,35 @@
+#define CONFIG_FOO 1
+
+#define define_struct(name, fields...) struct fields name;
+
+define_struct(a, {
+#ifdef CONFIG_FOO
+  int b;
+#elif defined(CONFIG_BAR)
+  int c;
+#else
+  int d;
+#endif
+});
+/*
+ * check-name: Preprocessor #22
+ *
+ * check-description: Directives are not allowed within a macro argument list,
+ * although cpp deals with it to treat macro more like C functions.
+ *
+ * check-command: sparse -E $file
+ *
+ * check-error-start
+preprocessor/preprocessor22.c:6:1: error: directive in argument list
+preprocessor/preprocessor22.c:8:1: error: directive in argument list
+preprocessor/preprocessor22.c:10:1: error: directive in argument list
+preprocessor/preprocessor22.c:12:1: error: directive in argument list
+ * check-error-end
+ *
+ * check-output-start
+
+struct {
+int b;
+} a;;
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor3.c b/deps/sparse/validation/preprocessor/preprocessor3.c
new file mode 100644
index 0000000..e9f6ae7
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor3.c
@@ -0,0 +1,32 @@
+/*
+ * Each iteration of the scanning of "SCAN()" re-evaluates the recursive
+ * B->A->B expansion.
+ *
+ * Did I already mention that the C preprocessor language
+ * is a perverse thing?
+ */
+
+#define LP (
+
+#define A() B LP )
+#define B() A LP )
+
+#define SCAN(x) x
+
+A()                     // B ( )
+SCAN( A() )             // A ( )
+SCAN(SCAN( A() ))       // B ( )
+SCAN(SCAN(SCAN( A() ))) // A ( )
+/*
+ * check-name: Preprocessor #3
+ * check-description: Sparse used to get this wrong, outputting A third, not B.
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+B ( )
+A ( )
+B ( )
+A ( )
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor4.c b/deps/sparse/validation/preprocessor/preprocessor4.c
new file mode 100644
index 0000000..710c494
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor4.c
@@ -0,0 +1,15 @@
+#define foo bar
+#define mac(x) x(foo)
+
+mac(foo)
+
+/*
+ * check-name: Preprocessor #4
+ * check-description: More examples from the comp.std.c discussion.
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+bar(bar)
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor5.c b/deps/sparse/validation/preprocessor/preprocessor5.c
new file mode 100644
index 0000000..b431627
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor5.c
@@ -0,0 +1,14 @@
+#define a a|
+#define b(x) x
+
+b(a)
+/*
+ * check-name: Preprocessor #5
+ * check-description: Yet more examples from comp.std.c.
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+a|
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor6.c b/deps/sparse/validation/preprocessor/preprocessor6.c
new file mode 100644
index 0000000..41da267
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor6.c
@@ -0,0 +1,29 @@
+/* We used to get '##' wrong for the kernel.
+ *
+ * It could possibly be argued that the kernel usage is undefined (since the
+ * different sides of the '##' are not proper tokens), but we try to do it
+ * right anyway.
+ *
+ * We used to break up the "003d" into two tokens ('003' and 'd') and then put
+ * the 'o' marker to mark the token 003 as an octal number, resulting in:
+ *
+ *	static char __vendorstr_o03 d [ ] __devinitdata = "Lockheed Martin-Marietta Corp";
+ *
+ * which didn't work, of course.
+ */
+
+#define __devinitdata __attribute__((section(".devinit")))
+
+#define VENDOR( vendor, name ) \
+	static char __vendorstr_##vendor[] __devinitdata = name;
+VENDOR(003d,"Lockheed Martin-Marietta Corp")
+
+/*
+ * check-name: Preprocessor #6
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+static char __vendorstr_003d[] __attribute__((section(".devinit"))) = "Lockheed Martin-Marietta Corp";
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor7.c b/deps/sparse/validation/preprocessor/preprocessor7.c
new file mode 100644
index 0000000..07fce8c
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor7.c
@@ -0,0 +1,14 @@
+#define A(x) C(B, D
+#define D A(1))
+#define C(x,y) E(y)
+#define E(y) #y
+A(2))
+/*
+ * check-name: Preprocessor #7
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+"\"D\""
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor8.c b/deps/sparse/validation/preprocessor/preprocessor8.c
new file mode 100644
index 0000000..524825c
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor8.c
@@ -0,0 +1,38 @@
+#define A(x) ## x
+#define B(x) x ##
+#define C(x) x ## ## ##
+#define D(x) x#y
+#define E x#y
+#define F(x,y) x x##y #x y
+#define G a##b
+#define H 1##2
+#define I(x,y,z) x y z
+"A(x)"			: A(x)
+"B(x)"			: B(x)
+"C(x)"			: C(x)
+"D(x)"			: D(x)
+"x#y"			: E
+"ab GH \"G\" 12"	: F(G,H)
+"a ## b"		: I(a,##,b)
+/*
+ * check-name: Preprocessor #8
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+"A(x)" : A(x)
+"B(x)" : B(x)
+"C(x)" : C(x)
+"D(x)" : D(x)
+"x#y" : x#y
+"ab GH \"G\" 12" : ab GH "G" 12
+"a ## b" : a ## b
+ * check-output-end
+ *
+ * check-error-start
+preprocessor/preprocessor8.c:1:14: error: '##' cannot appear at the ends of macro expansion
+preprocessor/preprocessor8.c:2:16: error: '##' cannot appear at the ends of macro expansion
+preprocessor/preprocessor8.c:3:22: error: '##' cannot appear at the ends of macro expansion
+preprocessor/preprocessor8.c:4:15: error: '#' is not followed by a macro parameter
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/preprocessor/preprocessor9.c b/deps/sparse/validation/preprocessor/preprocessor9.c
new file mode 100644
index 0000000..20f9c8f
--- /dev/null
+++ b/deps/sparse/validation/preprocessor/preprocessor9.c
@@ -0,0 +1,16 @@
+/* Only # in the input stream marks the beginning of preprocessor command,
+ * and here we get it from macro expansion.
+ */
+#define A # define X 1
+A
+X
+/*
+ * check-name: Preprocessor #9
+ * check-command: sparse -E $file
+ *
+ * check-output-start
+
+# define X 1
+X
+ * check-output-end
+ */
diff --git a/deps/sparse/validation/reserved.c b/deps/sparse/validation/reserved.c
new file mode 100644
index 0000000..caacd21
--- /dev/null
+++ b/deps/sparse/validation/reserved.c
@@ -0,0 +1,40 @@
+static int (struct);
+static int (union);
+static int (enum);
+static int (volatile);
+static int (__volatile);
+static int (__volatile__);
+static int (const);
+static int (__const);
+static int (__const__);
+static int (restrict);
+static int (__restrict);
+static int (__restrict__);
+static int (typedef);
+static int (__typeof);
+static int (__typeof__);
+static int (inline);
+static int (__inline);
+static int (__inline__);
+/*
+ * check-name: const et.al. are reserved identifiers
+ * check-error-start:
+reserved.c:1:12: error: Trying to use reserved word 'struct' as identifier
+reserved.c:2:12: error: Trying to use reserved word 'union' as identifier
+reserved.c:3:12: error: Trying to use reserved word 'enum' as identifier
+reserved.c:4:12: error: Trying to use reserved word 'volatile' as identifier
+reserved.c:5:12: error: Trying to use reserved word '__volatile' as identifier
+reserved.c:6:12: error: Trying to use reserved word '__volatile__' as identifier
+reserved.c:7:12: error: Trying to use reserved word 'const' as identifier
+reserved.c:8:12: error: Trying to use reserved word '__const' as identifier
+reserved.c:9:12: error: Trying to use reserved word '__const__' as identifier
+reserved.c:10:12: error: Trying to use reserved word 'restrict' as identifier
+reserved.c:11:12: error: Trying to use reserved word '__restrict' as identifier
+reserved.c:13:12: error: Trying to use reserved word 'typedef' as identifier
+reserved.c:14:12: error: Trying to use reserved word '__typeof' as identifier
+reserved.c:15:12: error: Trying to use reserved word '__typeof__' as identifier
+reserved.c:16:12: error: Trying to use reserved word 'inline' as identifier
+reserved.c:17:12: error: Trying to use reserved word '__inline' as identifier
+reserved.c:18:12: error: Trying to use reserved word '__inline__' as identifier
+ * check-error-end:
+ */
diff --git a/deps/sparse/validation/restrict-array.c b/deps/sparse/validation/restrict-array.c
new file mode 100644
index 0000000..3facebf
--- /dev/null
+++ b/deps/sparse/validation/restrict-array.c
@@ -0,0 +1,12 @@
+#define __restrict_arr __restrict
+
+struct aiocb64;
+struct sigevent;
+
+extern int lio_listio64 (int __mode,
+			 struct aiocb64 *__const __list[__restrict_arr],
+			 int __nent, struct sigevent *__restrict __sig);
+
+/*
+ * check-name: restrict array attribute
+ */
diff --git a/deps/sparse/validation/restricted-typeof.c b/deps/sparse/validation/restricted-typeof.c
new file mode 100644
index 0000000..1592664
--- /dev/null
+++ b/deps/sparse/validation/restricted-typeof.c
@@ -0,0 +1,8 @@
+typedef unsigned __attribute__((bitwise)) A;
+static A x;
+static __typeof__(x) y;
+static A *p = &y;
+/*
+ * check-name: typeof with bitwise types
+ * check-command: sparse -Wbitwise $file
+ */
diff --git a/deps/sparse/validation/sizeof-bool.c b/deps/sparse/validation/sizeof-bool.c
new file mode 100644
index 0000000..6c68748
--- /dev/null
+++ b/deps/sparse/validation/sizeof-bool.c
@@ -0,0 +1,12 @@
+static int a(void)
+{
+	return sizeof(_Bool);
+}
+/*
+ * check-name: sizeof(_Bool) is valid
+ * check-description: sizeof(_Bool) was rejected because _Bool is not an even
+ * number of bytes
+ * check-error-start
+sizeof-bool.c:3:16: warning: expression using sizeof bool
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/sizeof-compound-postfix.c b/deps/sparse/validation/sizeof-compound-postfix.c
new file mode 100644
index 0000000..3b716fe
--- /dev/null
+++ b/deps/sparse/validation/sizeof-compound-postfix.c
@@ -0,0 +1,8 @@
+struct foo {int x, y;};
+static int a(void)
+{
+	return sizeof (struct foo){0,1}.y;
+}
+/*
+ * check-name: Handling of sizeof compound-literal . member
+ */
diff --git a/deps/sparse/validation/specifiers1.c b/deps/sparse/validation/specifiers1.c
new file mode 100644
index 0000000..1a4e1d5
--- /dev/null
+++ b/deps/sparse/validation/specifiers1.c
@@ -0,0 +1,101 @@
+static void OK(void)
+{
+#define TEST(x) { T a; x *b = &a; }
+#define TEST2(x, y) TEST(x y) TEST(y x)
+#define TEST3(x, y, z) TEST(x y z) TEST(x z y) TEST(y x z) \
+		       TEST(y z x) TEST(z x y) TEST(z y x)
+#define TEST4(x, y, z, w) TEST2(x y, z w) TEST2(x y, w z) \
+			  TEST2(y x, z w) TEST2(y x, w z) \
+			  TEST2(x z, y w) TEST2(x z, w y) \
+			  TEST2(z x, y w) TEST2(z x, w y) \
+			  TEST2(x w, y z) TEST2(x w, z y) \
+			  TEST2(w x, y z) TEST2(w x, z y)
+
+
+#define T char
+TEST(char)
+#undef T
+
+#define T signed char
+TEST2(char, signed)
+#undef T
+
+#define T unsigned char
+TEST2(char, unsigned)
+#undef T
+
+#define T short
+TEST(short)
+TEST2(int, short)
+#undef T
+
+#define T int
+TEST(int)
+#undef T
+
+#define T long
+TEST(long)
+TEST2(int, long)
+#undef T
+
+#define T long long
+TEST2(long, long)
+TEST3(int, long, long)
+#undef T
+
+#define T signed short
+TEST2(short, signed)
+TEST3(int, short, signed)
+#undef T
+
+#define T signed
+TEST(signed)
+TEST2(int, signed)
+#undef T
+
+#define T signed long
+TEST2(long, signed)
+TEST3(int, long, signed)
+#undef T
+
+#define T signed long long
+TEST3(long, long, signed)
+TEST4(int, long, long, signed)
+#undef T
+
+#define T unsigned short
+TEST2(short, unsigned)
+TEST3(int, short, unsigned)
+#undef T
+
+#define T unsigned
+TEST(unsigned)
+TEST2(int, unsigned)
+#undef T
+
+#define T unsigned long
+TEST2(long, unsigned)
+TEST3(int, long, unsigned)
+#undef T
+
+#define T unsigned long long
+TEST3(long, long, unsigned)
+TEST4(int, long, long, unsigned)
+#undef T
+
+#define T float
+TEST(float)
+#undef T
+
+#define T double
+TEST(double)
+#undef T
+
+#define T long double
+TEST2(double, long)
+#undef T
+}
+/*
+ * check-name: valid specifier combinations
+ * check-command: sparse $file
+ */
diff --git a/deps/sparse/validation/specifiers2.c b/deps/sparse/validation/specifiers2.c
new file mode 100644
index 0000000..d5be118
--- /dev/null
+++ b/deps/sparse/validation/specifiers2.c
@@ -0,0 +1,152 @@
+typedef int T;
+void BAD(
+char char,
+char int,
+char double,
+char float,
+char long,
+char short,
+int char,
+int int,
+int double,
+int float,
+double char,
+double int,
+double double,
+double float,
+double short,
+double signed,
+double unsigned,
+float char,
+float int,
+float double,
+float float,
+float short,
+float long,
+float signed,
+float unsigned,
+short char,
+short double,
+short float,
+short short,
+short long,
+long char,
+long float,
+long short,
+signed double,
+signed float,
+signed signed,
+signed unsigned,
+unsigned double,
+unsigned float,
+unsigned signed,
+unsigned unsigned,
+unsigned signed,
+long long long,
+long double long,
+long long double,
+double long long,
+T char,
+T int,
+T double,
+T float,
+T short,
+T long,
+T signed,
+T unsigned,
+T void,
+void char,
+void int,
+void double,
+void float,
+void short,
+void long,
+void signed,
+void unsigned,
+char void,
+int void,
+double void,
+float void,
+short void,
+long void,
+signed void,
+unsigned void,
+void void
+);
+/*
+ * check-name: invalid specifier combinations
+ * check-error-start
+specifiers2.c:3:6: error: two or more data types in declaration specifiers
+specifiers2.c:4:6: error: two or more data types in declaration specifiers
+specifiers2.c:5:6: error: two or more data types in declaration specifiers
+specifiers2.c:6:6: error: two or more data types in declaration specifiers
+specifiers2.c:7:6: error: impossible combination of type specifiers: char long
+specifiers2.c:8:6: error: impossible combination of type specifiers: char short
+specifiers2.c:9:5: error: two or more data types in declaration specifiers
+specifiers2.c:10:5: error: two or more data types in declaration specifiers
+specifiers2.c:11:5: error: two or more data types in declaration specifiers
+specifiers2.c:12:5: error: two or more data types in declaration specifiers
+specifiers2.c:13:8: error: two or more data types in declaration specifiers
+specifiers2.c:14:8: error: two or more data types in declaration specifiers
+specifiers2.c:15:8: error: two or more data types in declaration specifiers
+specifiers2.c:16:8: error: two or more data types in declaration specifiers
+specifiers2.c:17:8: error: impossible combination of type specifiers: double short
+specifiers2.c:18:8: error: impossible combination of type specifiers: double signed
+specifiers2.c:19:8: error: impossible combination of type specifiers: double unsigned
+specifiers2.c:20:7: error: two or more data types in declaration specifiers
+specifiers2.c:21:7: error: two or more data types in declaration specifiers
+specifiers2.c:22:7: error: two or more data types in declaration specifiers
+specifiers2.c:23:7: error: two or more data types in declaration specifiers
+specifiers2.c:24:7: error: impossible combination of type specifiers: float short
+specifiers2.c:25:7: error: impossible combination of type specifiers: float long
+specifiers2.c:26:7: error: impossible combination of type specifiers: float signed
+specifiers2.c:27:7: error: impossible combination of type specifiers: float unsigned
+specifiers2.c:28:7: error: impossible combination of type specifiers: short char
+specifiers2.c:29:7: error: impossible combination of type specifiers: short double
+specifiers2.c:30:7: error: impossible combination of type specifiers: short float
+specifiers2.c:31:7: error: impossible combination of type specifiers: short short
+specifiers2.c:32:7: error: impossible combination of type specifiers: short long
+specifiers2.c:33:6: error: impossible combination of type specifiers: long char
+specifiers2.c:34:6: error: impossible combination of type specifiers: long float
+specifiers2.c:35:6: error: impossible combination of type specifiers: long short
+specifiers2.c:36:8: error: impossible combination of type specifiers: signed double
+specifiers2.c:37:8: error: impossible combination of type specifiers: signed float
+specifiers2.c:38:8: error: impossible combination of type specifiers: signed signed
+specifiers2.c:39:8: error: impossible combination of type specifiers: signed unsigned
+specifiers2.c:40:10: error: impossible combination of type specifiers: unsigned double
+specifiers2.c:41:10: error: impossible combination of type specifiers: unsigned float
+specifiers2.c:42:10: error: impossible combination of type specifiers: unsigned signed
+specifiers2.c:43:10: error: impossible combination of type specifiers: unsigned unsigned
+specifiers2.c:44:10: error: impossible combination of type specifiers: unsigned signed
+specifiers2.c:45:11: error: impossible combination of type specifiers: long long long
+specifiers2.c:46:13: error: impossible combination of type specifiers: long long double
+specifiers2.c:47:11: error: impossible combination of type specifiers: long long double
+specifiers2.c:48:13: error: impossible combination of type specifiers: long long double
+specifiers2.c:49:3: error: two or more data types in declaration specifiers
+specifiers2.c:50:3: error: two or more data types in declaration specifiers
+specifiers2.c:51:3: error: two or more data types in declaration specifiers
+specifiers2.c:52:3: error: two or more data types in declaration specifiers
+specifiers2.c:53:3: error: two or more data types in declaration specifiers
+specifiers2.c:54:3: error: two or more data types in declaration specifiers
+specifiers2.c:55:3: error: two or more data types in declaration specifiers
+specifiers2.c:56:3: error: two or more data types in declaration specifiers
+specifiers2.c:57:3: error: two or more data types in declaration specifiers
+specifiers2.c:58:6: error: two or more data types in declaration specifiers
+specifiers2.c:59:6: error: two or more data types in declaration specifiers
+specifiers2.c:60:6: error: two or more data types in declaration specifiers
+specifiers2.c:61:6: error: two or more data types in declaration specifiers
+specifiers2.c:62:6: error: two or more data types in declaration specifiers
+specifiers2.c:63:6: error: two or more data types in declaration specifiers
+specifiers2.c:64:6: error: two or more data types in declaration specifiers
+specifiers2.c:65:6: error: two or more data types in declaration specifiers
+specifiers2.c:66:6: error: two or more data types in declaration specifiers
+specifiers2.c:67:5: error: two or more data types in declaration specifiers
+specifiers2.c:68:8: error: two or more data types in declaration specifiers
+specifiers2.c:69:7: error: two or more data types in declaration specifiers
+specifiers2.c:70:7: error: impossible combination of type specifiers: short void
+specifiers2.c:71:6: error: impossible combination of type specifiers: long void
+specifiers2.c:72:8: error: impossible combination of type specifiers: signed void
+specifiers2.c:73:10: error: impossible combination of type specifiers: unsigned void
+specifiers2.c:74:6: error: two or more data types in declaration specifiers
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/static-forward-decl.c b/deps/sparse/validation/static-forward-decl.c
new file mode 100644
index 0000000..47e46dc
--- /dev/null
+++ b/deps/sparse/validation/static-forward-decl.c
@@ -0,0 +1,10 @@
+static int f(void);
+
+int f(void)
+{
+	return 0;
+}
+/*
+ * check-name: static forward declaration
+ * check-known-to-fail
+ */
diff --git a/deps/sparse/validation/struct-as.c b/deps/sparse/validation/struct-as.c
new file mode 100644
index 0000000..f31f7c9
--- /dev/null
+++ b/deps/sparse/validation/struct-as.c
@@ -0,0 +1,19 @@
+/*
+ * Structure members should get the address
+ * space of their pointer.
+ */
+#define __user __attribute__((address_space(1)))
+
+struct hello {
+	int a;
+};
+
+extern int test(int __user *ip);
+
+static int broken(struct hello __user *sp)
+{
+	test(&sp->a);
+}
+/*
+ * check-name: Address space of a struct member
+ */
diff --git a/deps/sparse/validation/struct-attribute-placement.c b/deps/sparse/validation/struct-attribute-placement.c
new file mode 100644
index 0000000..53c1214
--- /dev/null
+++ b/deps/sparse/validation/struct-attribute-placement.c
@@ -0,0 +1,6 @@
+struct __attribute__((__aligned__(16))) foo {
+    int a;
+};
+/*
+ * check-name: struct attribute placement
+ */
diff --git a/deps/sparse/validation/struct-ns1.c b/deps/sparse/validation/struct-ns1.c
new file mode 100644
index 0000000..096bb5d
--- /dev/null
+++ b/deps/sparse/validation/struct-ns1.c
@@ -0,0 +1,20 @@
+// This actually isn't allowed in C99, but sparse and gcc will take it:
+enum Foo;
+
+static void
+f (void)
+{
+  enum Foo *pefoo;         // Pointer to incomplete type
+  struct Foo;              // Forward declaration
+  struct Foo *psfoo;       // Pointer to incomplete type
+  {
+    struct Foo { int foo; }; // Local definition.
+    struct Foo foo;          // variable declaration.
+    foo.foo = 1;
+  }
+}
+
+enum Foo { FOO };
+/*
+ * check-name: struct namespaces #1
+ */
diff --git a/deps/sparse/validation/struct-ns2.c b/deps/sparse/validation/struct-ns2.c
new file mode 100644
index 0000000..4dd2c3b
--- /dev/null
+++ b/deps/sparse/validation/struct-ns2.c
@@ -0,0 +1,19 @@
+static void
+g (struct Bar { int i; } *x)
+{
+  struct Bar y;
+  y.i = 1;
+}
+
+static void
+h (void)
+{
+  // This is not in scope and should barf loudly.
+  struct Bar y;
+  y.i = 1;
+}
+
+/*
+ * check-name: struct not in scope
+ * check-known-to-fail
+ */
diff --git a/deps/sparse/validation/struct-size1.c b/deps/sparse/validation/struct-size1.c
new file mode 100644
index 0000000..cf956a4
--- /dev/null
+++ b/deps/sparse/validation/struct-size1.c
@@ -0,0 +1,21 @@
+struct A;
+struct B {
+  struct A *pA;
+};
+struct C;
+struct E {
+  struct A **pA;
+  struct C *pC;
+};
+static void f(struct E *pE, struct B *pB)
+{
+  pB->pA = pE->pA[0];
+}
+static const struct { int x; } foo[] = {{ 1 }};
+struct C {
+  int bar[(sizeof foo/sizeof foo[0])];
+};
+
+/*
+ * check-name: struct size
+ */
diff --git a/deps/sparse/validation/test-be.c b/deps/sparse/validation/test-be.c
new file mode 100644
index 0000000..deda3cc
--- /dev/null
+++ b/deps/sparse/validation/test-be.c
@@ -0,0 +1,46 @@
+int printf(char *c, ...);
+void exit(int c);
+
+#undef PRINT_OUTPUTS
+
+static void test_func_args(int x, int y)
+{
+	if (x == y)
+		exit(1);
+}
+
+static int binop_s32(int x, int y)
+{
+	int a;
+
+	a = a + x;
+	a = a / y;
+	a = a * x;
+	a = a - y;
+
+	return a;
+}
+
+static void test_binops(void)
+{
+	int tmp_s32 = binop_s32(987123, 234);
+
+#ifdef PRINT_OUTPUTS
+	printf("binop_s32(987123, 234) == %d\n", tmp_s32);
+#else
+	if (tmp_s32 != -1470599007)
+		exit(2);
+#endif
+}
+
+int main (int argc, char *argv[])
+{
+	test_func_args(1, 2);
+	test_binops();
+
+	return 0;
+}
+
+/*
+ * check-name: binary operations
+ */
diff --git a/deps/sparse/validation/test-suite b/deps/sparse/validation/test-suite
new file mode 100755
index 0000000..3c011c6
--- /dev/null
+++ b/deps/sparse/validation/test-suite
@@ -0,0 +1,252 @@
+#!/bin/sh
+
+#set -x
+
+default_path=".."
+default_cmd="sparse \$file"
+tests_list=`find . -name '*.c' | sed -e 's#^\./\(.*\)#\1#' | sort`
+prog_name=`basename $0`
+
+# counts:
+#	- tests that have not been converted to test-suite format
+#	- tests that passed
+#	- tests that failed
+#	- tests that failed but are known to fail
+unhandled_tests=0
+ok_tests=0
+ko_tests=0
+known_ko_tests=0
+
+# defaults to not verbose
+[ -z "$V" ] && V=0
+
+##
+# get_value(key, file) - gets the value of a (key, value) pair in file.
+#
+# returns 0 on success, 1 if the file does not have the key
+get_value()
+{
+	last_result=`grep $1: $2 | sed -e "s/^.*$1:\(.*\)$/\1/"`
+	[ -z "$last_result" ] && return 1
+	return 0
+}
+
+##
+# get_tag(key, file) - does file has the tag key in it ?
+#
+# returns 0 if present, 1 otherwise
+get_tag()
+{
+	last_result=`grep $1 $2`
+	return $?
+}
+
+##
+# verbose(string) - prints string if we are in verbose mode
+verbose()
+{
+	[ "$V" -eq "1" ] && echo "        $1"
+	return 0
+}
+
+##
+# error(string[, die]) - prints an error and exits with value die if given
+error()
+{
+	echo "error: $1"
+	[ -n "$2" ] && exit $2
+	return 0
+}
+
+do_usage()
+{
+echo "$prog_name - a tiny automatic testing script"
+echo "Usage: $prog_name [command] [command arguments]"
+echo
+echo "commands:"
+echo "    none                       runs the whole test suite"
+echo "    single file                runs the test in 'file'"
+echo "    format file [name [cmd]]   helps writing a new test case using cmd"
+echo
+echo "    help                       prints usage"
+}
+
+##
+# do_test(file) - tries to validate a test case
+#
+# it "parses" file, looking for check-* tags and tries to validate
+# the test against an expected result
+# returns:
+#	- 0 if the test passed,
+#	- 1 if it failed,
+#	- 2 if it is not a "test-suite" test.
+do_test()
+{
+	test_failed=0
+	file="$1"
+
+	# can this test be handled by test-suite ?
+	# (it has to have a check-name key in it)
+	get_value "check-name" $file
+	if [ "$?" -eq 1 ]; then
+		echo "warning: test '$file' unhandled"
+		unhandled_tests=`expr $unhandled_tests + 1`
+		return 2
+	fi
+	test_name=$last_result
+
+	echo "     TEST    $test_name ($file)"
+
+	# does the test provide a specific command ?
+	cmd=`eval echo $default_path/$default_cmd`
+	get_value "check-command" $file
+	if [ "$?" -eq "0" ]; then
+		last_result=`echo $last_result | sed -e 's/^ *//'`
+		cmd=`eval echo $default_path/$last_result`
+	fi
+	verbose "Using command       : $cmd"
+
+	# grab the expected exit value
+	get_value "check-exit-value" $file
+	if [ "$?" -eq "0" ]; then
+		expected_exit_value=`echo $last_result | tr -d ' '`
+	else
+		expected_exit_value=0
+	fi
+	verbose "Expecting exit value: $expected_exit_value"
+
+	# grab the expected output
+	sed -n '/check-output-start/,/check-output-end/p' $file \
+		| grep -v check-output > "$file".output.expected
+	sed -n '/check-error-start/,/check-error-end/p' $file \
+		| grep -v check-error > "$file".error.expected
+
+	# grab the actual output & exit value
+	$cmd 1> $file.output.got 2> $file.error.got
+	actual_exit_value=$?
+
+	for stream in output error; do
+		diff -u "$file".$stream.expected "$file".$stream.got > "$file".$stream.diff
+		if [ "$?" -ne "0" ]; then
+			error "actual $stream text does not match expected $stream text."
+			error  "see $file.$stream.* for further investigation."
+			cat "$file".$stream.diff
+			test_failed=1
+		fi
+	done
+
+	if [ "$actual_exit_value" -ne "$expected_exit_value" ]; then
+		error "Actual exit value does not match the expected one."
+		error "expected $expected_exit_value, got $actual_exit_value."
+		test_failed=1
+	fi
+
+	if [ "$test_failed" -eq "1" ]; then
+		ko_tests=`expr $ko_tests + 1`
+		get_tag "check-known-to-fail" $file
+		if [ "$?" -eq "0" ]; then
+			echo "info: test '$file' is known to fail"
+			known_ko_tests=`expr $known_ko_tests + 1`
+		fi
+		return 1
+	else
+		ok_tests=`expr $ok_tests + 1`
+		return 0
+	fi
+}
+
+do_test_suite()
+{
+	for i in $tests_list; do
+		do_test "$i"
+	done
+
+	# prints some numbers
+	tests_nr=`expr $ok_tests + $ko_tests`
+	echo -n "Out of $tests_nr tests, $ok_tests passed, $ko_tests failed"
+	echo " ($known_ko_tests of them are known to fail)"
+	if [ "$unhandled_tests" -ne "0" ]; then
+		echo "$unhandled_tests tests could not be handled by $prog_name"
+	fi
+}
+
+##
+# do_format(file[, name[, cmd]]) - helps a test writer to format test-suite tags
+do_format()
+{
+	if [ -z "$2" ]; then
+		fname="$1"
+		fcmd=$default_cmd
+	elif [ -z "$3" ]; then
+		fname="$2"
+		fcmd=$default_cmd
+	else
+		fname="$2"
+		fcmd="$3"
+	fi
+	file="$1"
+	cmd=`eval echo $default_path/$fcmd`
+	$cmd 1> $file.output.got 2> $file.error.got
+	fexit_value=$?
+	cat <<_EOF
+/*
+ * check-name: $fname
+_EOF
+	if [ "$fcmd" != "$default_cmd" ]; then
+		echo " * check-command: $fcmd"
+	fi
+	if [ "$fexit_value" -ne "0" ]; then
+		echo " * check-exit-value: $fexit_value"
+	fi
+	for stream in output error; do
+		if [ -s "$file.$stream.got" ]; then
+			echo " *"
+			echo " * check-$stream-start"
+			cat "$file.$stream.got"
+			echo " * check-$stream-end"
+		fi
+	done
+	echo " */"
+	return 0
+}
+
+##
+# arg_file(filename) - checks if filename exists
+arg_file()
+{
+	[ -z "$1" ] && {
+		do_usage
+		exit 1
+	}
+	[ -e "$1" ] || {
+		error "Can't open file $1"
+		exit 1
+	}
+	return 0
+}
+
+case "$1" in
+	'')
+		do_test_suite
+		;;
+	single)
+		arg_file "$2"
+		do_test "$2"
+		case "$?" in
+			0) echo "$2 passed !";;
+			1) echo "$2 failed !";;
+			2) echo "$2 can't be handled by $prog_name";;
+		esac
+		;;
+	format)
+		arg_file "$2"
+		do_format "$2" "$3" "$4"
+		;;
+	help | *)
+		do_usage
+		exit 1
+		;;
+esac
+
+exit 0
+
diff --git a/deps/sparse/validation/type1.c b/deps/sparse/validation/type1.c
new file mode 100644
index 0000000..2a55f2a
--- /dev/null
+++ b/deps/sparse/validation/type1.c
@@ -0,0 +1,27 @@
+/*
+ * Sparse used to get this wrong.
+ *
+ * When evaluating the argument to the inline function for the array, Sparse
+ * didn't properly demote the "char []" to a "char *", but instead it would
+ * follow the dereference and get a "struct hello".
+ *
+ * Which made no sense at all.
+ */
+
+static inline int deref(const char *s)
+{
+	return *s;
+}
+
+struct hello {
+	char array[10];
+};
+
+static int test(struct hello *arg)
+{
+	return deref(arg->array);
+}
+
+/*
+ * check-name: "char []" to "char *" demotion
+ */
diff --git a/deps/sparse/validation/typedef_shadow.c b/deps/sparse/validation/typedef_shadow.c
new file mode 100644
index 0000000..c72cec7
--- /dev/null
+++ b/deps/sparse/validation/typedef_shadow.c
@@ -0,0 +1,12 @@
+typedef int T;
+static void f(int T)
+{
+	static T a;
+}
+/*
+ * check-name: typedef shadowing
+ * check-error-start:
+typedef_shadow.c:4:18: error: Expected ; at end of declaration
+typedef_shadow.c:4:18: error: got a
+ * check-error-end:
+ */
diff --git a/deps/sparse/validation/typeof-attribute.c b/deps/sparse/validation/typeof-attribute.c
new file mode 100644
index 0000000..f79a61c
--- /dev/null
+++ b/deps/sparse/validation/typeof-attribute.c
@@ -0,0 +1,16 @@
+#define __percpu __attribute__((noderef, address_space(3)))
+
+/* Turn v back into a normal var. */
+#define convert(v) \
+       (*(typeof(v) __attribute__((address_space(0), force)) *)(&v))
+
+int main(int argc, char *argv)
+{
+       unsigned int __percpu x;
+
+       convert(x) = 0;
+       return 0;
+}
+/*
+ * check-name: Rusty Russell's typeof attribute casting.
+ */
diff --git a/deps/sparse/validation/typesign.c b/deps/sparse/validation/typesign.c
new file mode 100644
index 0000000..e5dc525
--- /dev/null
+++ b/deps/sparse/validation/typesign.c
@@ -0,0 +1,61 @@
+static unsigned int * s_to_u_return(signed int *sp)
+{
+	return sp;
+}
+
+static signed int * u_to_s_return(unsigned int *up)
+{
+	return up;
+}
+
+static unsigned int * s_to_u_init(signed int *sp)
+{
+	unsigned int *up = sp;
+	return up;
+}
+
+static signed int * u_to_s_init(unsigned int *up)
+{
+	signed int *sp = up;
+	return sp;
+}
+
+static unsigned int * s_to_u_assign(signed int *sp)
+{
+	unsigned int *up;
+	up = sp;
+	return up;
+}
+
+static signed int * u_to_s_assign(unsigned int *up)
+{
+	signed int *sp;
+	sp = up;
+	return sp;
+}
+
+/*
+ * check-name: -Wtypesign
+ * check-command: sparse -Wtypesign $file
+ *
+ * check-error-start
+typesign.c:3:16: warning: incorrect type in return expression (different signedness)
+typesign.c:3:16:    expected unsigned int *
+typesign.c:3:16:    got signed int *sp
+typesign.c:8:16: warning: incorrect type in return expression (different signedness)
+typesign.c:8:16:    expected signed int *
+typesign.c:8:16:    got unsigned int *up
+typesign.c:13:28: warning: incorrect type in initializer (different signedness)
+typesign.c:13:28:    expected unsigned int *up
+typesign.c:13:28:    got signed int *sp
+typesign.c:19:26: warning: incorrect type in initializer (different signedness)
+typesign.c:19:26:    expected signed int *sp
+typesign.c:19:26:    got unsigned int *up
+typesign.c:26:12: warning: incorrect type in assignment (different signedness)
+typesign.c:26:12:    expected unsigned int *up
+typesign.c:26:12:    got signed int *sp
+typesign.c:33:12: warning: incorrect type in assignment (different signedness)
+typesign.c:33:12:    expected signed int *sp
+typesign.c:33:12:    got unsigned int *up
+ * check-error-end
+ */
diff --git a/deps/sparse/validation/varargs1.c b/deps/sparse/validation/varargs1.c
new file mode 100644
index 0000000..2e3b429
--- /dev/null
+++ b/deps/sparse/validation/varargs1.c
@@ -0,0 +1,8 @@
+extern int foo (const char *, ...);
+static void sparse_error(const char err[])
+{
+  foo("%s\n",err);
+}
+/*
+ * check-name: Varargs bogus warning regression test #1
+ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]