diff options
author | Robert Bragg <robert@linux.intel.com> | 2012-04-09 19:55:21 +0100 |
---|---|---|
committer | Robert Bragg <robert@linux.intel.com> | 2012-04-10 18:45:04 +0100 |
commit | 8f15279ffd4340b94510ed1c3ffd4e1790283a7c (patch) | |
tree | 4c402f34162c38f88cf8206c377c06c4005b3e40 | |
parent | d6a44e5941abae8964d452c034e96b11e6a1bf35 (diff) | |
download | cogl-8f15279ffd4340b94510ed1c3ffd4e1790283a7c.tar.gz |
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.
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 00000000..3745b54c --- /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 00000000..6c4f24f6 --- /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 00000000..4cad826a --- /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 00000000..be0fd01d --- /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 00000000..5cc52a98 --- /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 00000000..9f1dc8cc --- /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 00000000..24d4a4a6 --- /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 00000000..6e15c91b --- /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 00000000..704c4878 --- /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 00000000..650053d2 --- /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 00000000..c2b39630 --- /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 00000000..da8f5f50 --- /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 00000000..4d81ffc0 --- /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 00000000..af995b1e --- /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 00000000..6636cc65 --- /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 00000000..227c02ba --- /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 00000000..4f3c8c0b --- /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 00000000..e65b538a --- /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 00000000..a7a61406 --- /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 00000000..c978e04e --- /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 00000000..7253a892 --- /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 00000000..9814ae3e --- /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 00000000..1cab4dea --- /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 00000000..1b6ad7d5 --- /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 00000000..da3ee497 --- /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 00000000..d405b22d --- /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 00000000..177363a7 --- /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 00000000..e8fbe344 --- /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 00000000..7e129a6d --- /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 00000000..61240d7a --- /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 00000000..3b72b898 --- /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 00000000..bebe9687 --- /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 00000000..24444c6c --- /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 00000000..63a90752 --- /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 00000000..0ae3a60c --- /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 00000000..9778de88 --- /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 00000000..7db9548f --- /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 00000000..370aaddf --- /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 00000000..86347863 --- /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 00000000..3633783c --- /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 00000000..5815fc70 --- /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 00000000..c4072130 --- /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 00000000..cd9bdddd --- /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 00000000..b12d1720 --- /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 00000000..9ed45709 --- /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 00000000..396e9f1c --- /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 00000000..2cea2520 --- /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 00000000..32097274 --- /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 00000000..50b36018 --- /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 00000000..eeff0f79 --- /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 00000000..45bd3401 --- /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 00000000..1015510b --- /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 00000000..bd42180d --- /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 00000000..cbf95ec1 --- /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 00000000..b26bd03f --- /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 00000000..8a16f8b3 --- /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 00000000..2620412f --- /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 00000000..58d3bdaa --- /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 00000000..27e38bc9 --- /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 00000000..0fab2864 --- /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 00000000..1333e301 --- /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 00000000..82005849 --- /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 00000000..afd7184d --- /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 00000000..bde6b6d6 --- /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 00000000..67b7d9eb --- /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 00000000..f1281c97 --- /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 00000000..acbc477d --- /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 00000000..e049e235 --- /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 00000000..86aef1c3 --- /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 00000000..1e745799 --- /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 00000000..17b228ae --- /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 00000000..1030c7c3 --- /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 00000000..a2548b7f --- /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 00000000..e437e112 --- /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 00000000..f4639ecf --- /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 00000000..5cc54cd9 --- /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 00000000..0a0b1d44 --- /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 00000000..5f176769 --- /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 00000000..88ce025f --- /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 00000000..cd292331 --- /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 00000000..d4f05e56 --- /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 00000000..3eea9b2b --- /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 00000000..77276ba4 --- /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 00000000..c55b78df --- /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 00000000..eb1e1058 --- /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 00000000..ac2bf2ad --- /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 00000000..1c0976f6 --- /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 00000000..1b104ea4 --- /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 00000000..69829228 --- /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 00000000..fb7d91f8 --- /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 00000000..71938db7 --- /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 00000000..bf577e00 --- /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 00000000..e3d07b51 --- /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 00000000..90c3e424 --- /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 00000000..ced7f9f1 --- /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 00000000..90a5fa1e --- /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 00000000..20f346c5 --- /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 00000000..7421ba48 --- /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 00000000..a589403a --- /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 00000000..ea24841f --- /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 00000000..95788240 --- /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 00000000..eeddcc82 --- /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 00000000..29799b50 --- /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 00000000..1c1c8766 --- /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 00000000..58b98ce5 --- /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 00000000..f6fd84cf --- /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 00000000..374948ef --- /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 00000000..08e556b3 --- /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 00000000..e55711cc --- /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 00000000..5e974cfa --- /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 00000000..33b70b84 --- /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 00000000..22635cfa --- /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 00000000..886f9713 --- /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 00000000..dd36e6ef --- /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 00000000..c3fe2cd2 --- /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 00000000..fce7393e --- /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 00000000..23423e96 --- /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 00000000..a1b8e636 --- /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 00000000..c48bcaee --- /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 00000000..92ffc8ef --- /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 00000000..13f8f9c8 --- /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 00000000..4f12ac06 --- /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 00000000..a6abab25 --- /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 00000000..9e21eab7 --- /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 00000000..3428715a --- /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 00000000..4691989c --- /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 00000000..5ede9bdc --- /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 00000000..968e3dd1 --- /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 00000000..fc223ff5 --- /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 00000000..4245fe23 --- /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 00000000..411020ac --- /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 00000000..a82d7bc9 --- /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 00000000..7af3d916 --- /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 00000000..951b0856 --- /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 00000000..3f975227 --- /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 00000000..db863b08 --- /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 00000000..ce73983d --- /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 00000000..d9ffd0f7 --- /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 00000000..1efe20ce --- /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 00000000..345a04b0 --- /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 00000000..8c89f609 --- /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 00000000..10b8b474 --- /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 00000000..4efe00d7 --- /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 00000000..48aeeaab --- /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 00000000..f86ffc74 --- /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 00000000..29c85b4d --- /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 00000000..8f51a307 --- /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 00000000..7d814743 --- /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 00000000..02b56df4 --- /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 00000000..4b376648 --- /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 00000000..e23e53b2 --- /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 00000000..b1af8555 --- /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 00000000..05fc248b --- /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 00000000..df877519 --- /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 00000000..75a4a0be --- /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 00000000..bd54fa6e --- /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 00000000..20169e8b --- /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 00000000..e70dad1e --- /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 00000000..56abb53c --- /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 00000000..90e93f37 --- /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 00000000..322c543a --- /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 00000000..4b55a6b8 --- /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 00000000..af5bcb37 --- /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 00000000..e9f6ae72 --- /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 00000000..710c494f --- /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 00000000..b4316275 --- /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 00000000..41da2674 --- /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 00000000..07fce8c3 --- /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 00000000..524825c1 --- /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 00000000..20f9c8fc --- /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 00000000..caacd21f --- /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 00000000..3facebf4 --- /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 00000000..15926645 --- /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 00000000..6c68748a --- /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 00000000..3b716fed --- /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 00000000..1a4e1d57 --- /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 00000000..d5be118b --- /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 00000000..47e46dcd --- /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 00000000..f31f7c96 --- /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 00000000..53c12143 --- /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 00000000..096bb5d9 --- /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 00000000..4dd2c3bf --- /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 00000000..cf956a41 --- /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 00000000..deda3cc1 --- /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 00000000..3c011c6a --- /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 00000000..2a55f2a9 --- /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 00000000..c72cec72 --- /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 00000000..f79a61c4 --- /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 00000000..e5dc525a --- /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 00000000..2e3b4295 --- /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 + */ |