[cogl/wip/sparse: 2/5] import sparse 0.4.4 snapshot
- From: Robert Bragg <rbragg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [cogl/wip/sparse: 2/5] import sparse 0.4.4 snapshot
- Date: Tue, 10 Apr 2012 17:45:32 +0000 (UTC)
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 = ' ' . "e_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__ ' . "e_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 ®class_8;
+ case 16: return ®class_16;
+ case 64: return ®class_64;
+ default: return ®class_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(®class_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, ®class_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, ®class_32);
+ src = get_reg_value(left, ®class_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, ®class_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, <ype);
+ 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), <ype);
+ classify_type(degenerate(r), &rtype);
+
+ lbase = examine_pointer_target(ltype);
+ examine_pointer_target(rtype);
+ typediff = type_difference(<ype->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, <ype);
+ 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, <ype);
+ 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, <ype);
+ 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), <ype);
+ 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(<ype->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, <ype);
+ 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(<ype->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(®->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(®->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(®->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(®->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(®->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 = ®ister_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 },
+ { ©_to_user_ident, check_ctu },
+ { ©_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]