pax_global_header00006660000000000000000000000064131454335760014525gustar00rootroot0000000000000052 comment=f28217d3ad1bf9e7099c86412d1e4c09c28addf3 sparse-0.5.1/000077500000000000000000000000001314543357600130255ustar00rootroot00000000000000sparse-0.5.1/.gitignore000066400000000000000000000006031314543357600150140ustar00rootroot00000000000000# generic *.o *.o.d *.a *.so .*.swp # generated pre-process.h sparse.pc version.h # programs test-inspect test-lexing test-parsing obfuscate sparse compile graph test-dissect test-linearize example test-unssa ctags c2xml sparse-llvm # tags tags TAGS # stgit generated dirs patches-* # quilt's files patches series # local makefile local.mk # cscope and Qt files cscope.out *.pro* sparse-0.5.1/Documentation/000077500000000000000000000000001314543357600156365ustar00rootroot00000000000000sparse-0.5.1/Documentation/data-structures.txt000066400000000000000000000116101314543357600215300ustar00rootroot00000000000000 As far as the parsing structures go... The C parser exists in two main files: parse.c, which parses statements, and expression.c, which parses expressions. parse.h contains the definition of struct statement, which represents a C statement. 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. 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. A series of statements forms a compound statement (STMT_COMPOUND). That appears as another struct statement which has a statement_list member. A function body consists of a compound statement. 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. Also note that all loops get turned into a single "iterator" statement. for, while, and do-while. A symbol, then, represents a name in a C file. A symbol might represent a variable, a function, a label, or various other things. See symbol.h. "struct symbol" represents one symbol. 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. Most of the interesting bits come in the NS_SYMBOL case. 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. Together, struct symbol, struct statement, and struct expression represent most of the abstract syntax tree for C. So, that represents most of the "front-end" of Sparse: parsing C and generating that abstract syntax tree. That much occurs in pretty much any program using the Sparse frontend. The backend varies among programs. For instance, the c2xml backend goes that far, then outputs XML. The sparse static analysis backend has a few steps: it generates linearized bytecode, does some evaluation on that, and outputs some warnings. Several other backends run that linearized bytecode stage. The linearized bytecode itself has a set of nested structures. linearize.h defines all of them. At the top level, it has struct entrypoint. That represents an entrypoint to the code, which would normally mean a function. An entrypoint has a list of basic blocks. struct basic_block. A basic block represents a series of instructions with no branches. Straight-line code. A branch only occurs at the end of a basic block, and branches can only target the beginning of a basic block. 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. Either the true or the false case may not exist. 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. So basic blocks represent a node in the control flow graph. The edges in that graph lead from one basic block to a basic block which can follow it in the execution of the program. Each basic block has a series of instructions, "struct instruction". "enum opcode" lists all the instructions. Fairly high-level instruction set, corresponding directly to bits of C. So you have an entrypoint, which has a graph of basic blocks, each of which has a list of instructions. An entrypoint also has a pointer to the first instruction. One last bit of trickiness: struct pseudo. Have you ever heard of "static single assignment" or SSA form? struct pseudo represents one of those single-assignment variables. Each one has a pointer to the symbol it represents (which may have many pseudos referencing it). Each one also has a pointer to the instruction that defines it. That covers most of the major data structures in Sparse. Now, given all that, some of the top-level stuff in sparse.c may make more sense. For instance, the context checking works in terms of basic blocks. Hopefully some of that helped you understand Sparse better. sparse-0.5.1/Documentation/project-ideas.md000066400000000000000000000040051314543357600207100ustar00rootroot00000000000000Why hacking on sparse ===================== 1. sparse is small. The full project compiles in less than 10 seconds on old and not performing laptop. 2. sparse is fast. Typically, sparse can check a C file 1/10 of time it takes for gcc to generate object files. 3. sparse can digest the full kernel source files. With sparse-llvm, sparse uses llvm as back end to emit real machine code. New developer hacking on sparse ============================== * All sparse warning messages should include the option how to disable it. e.g. "pre-process.c:20*:28: warning: Variable length array is used." should be something like "pre-process.c:20*:28: warning: Variable length array is used. (-Wno-vla)" * extend test-inspect to inspect more AST fields. * extend test-inspect to inspect instructions. * adding architecture handling in sparse similar to cgcc * parallel processing of test-suite * Howto: fix the kernel rcu related checker warnings * option to disable AST level inline. * debug: debug version of sparse do all the verification double check * test suite: verify and compare IR (suggested by Dibyendu Majumdar) * checker error output database For experienced developers ========================== * merge C type on incremental declare of C type and function prototype. * move attribute out of ctype to allow easier to add new attribute. * serialize, general object walking driven by data structures. * serialize, write sparse byte code into file * serialize, load sparse byte code from file. * symbol index/linker, know which symbol in which byte code file. * inline function in instruction level * cross function checking * debug: optimization step by step log * debug: fancy animation of CFG * phi node location (Luc has patch) * revisit crazy programmer warning, invalid SSA form. * ptrlist, looping while modify inside the loop. * dead code elimination using ssa * constant propagation using ssa. * x86/arm back end instruction set define * register allocation. * emit x86/arm machine level code sparse-0.5.1/Documentation/sparse.txt000066400000000000000000000034431314543357600177000ustar00rootroot00000000000000Sparse ~~~~~~ __nocast vs __bitwise: __nocast warns about explicit or implicit casting to different types. HOWEVER, it doesn't consider two 32-bit integers to be different types, so a __nocast 'int' type may be returned as a regular 'int' type and then the __nocast is lost. So "__nocast" on integer types is usually not that powerful. It just gets lost too easily. It's more useful for things like pointers. It also doesn't warn about the mixing: you can add integers to __nocast integer types, and it's not really considered anything wrong. __bitwise ends up being a "stronger integer separation". That one doesn't allow you to mix with non-bitwise integers, so now it's much harder to lose the type by mistake. So the basic rule is: - "__nocast" on its own tends to be more useful for *big* integers that still need to act like integers, but you want to make it much less likely that they get truncated by mistake. So a 64-bit integer that you don't want to mistakenly/silently be returned as "int", for example. But they mix well with random integer types, so you can add to them etc without using anything special. However, that mixing also means that the __nocast really gets lost fairly easily. - "__bitwise" is for *unique types* that cannot be mixed with other types, and that you'd never want to just use as a random integer (the integer 0 is special, though, and gets silently accepted iirc - it's kind of like "NULL" for pointers). So "gfp_t" or the "safe endianness" types would be __bitwise: you can only operate on them by doing specific operations that know about *that* particular type. Generally, you want __bitwise if you are looking for type safety. "__nocast" really is pretty weak. Reference: * Linus' e-mail about __nocast vs __bitwise: http://marc.info/?l=linux-mm&m=133245421127324&w=2 sparse-0.5.1/Documentation/submitting-patches.md000066400000000000000000000014771314543357600220030ustar00rootroot00000000000000Submitting patches: the sparse version ====================================== Sparse uses a patch submit process similar to the Linux Kernel [Submitting Patches](https://www.kernel.org/doc/html/v4.12/process/submitting-patches.html) This document mostly focuses on the parts that might be different from the Linux Kernel submitting process. 1. Git clone a sparse repository: git clone git://git.kernel.org/pub/scm/devel/sparse/sparse.git 2. [Coding Style](https://www.kernel.org/doc/html/v4.12/process/coding-style.html) remains the same. 3. Sign off the patch. The usage of the Signed-off-by tag is the same as [Linux Kernel Sign your work](https://www.kernel.org/doc/html/v4.12/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin). Notice that sparse uses the MIT License. sparse-0.5.1/Documentation/test-suite000066400000000000000000000074231314543357600176750ustar00rootroot00000000000000 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-timeout: (optional) The maximum expected duration of check-command, in seconds. It defaults to 1. 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-output-ignore / check-error-ignore (optional) Don't check the expected output (stdout or stderr) of check-command (usefull when this output is not comparable or if you're only interested in the exit value). By default this check is done. check-known-to-fail (optional) Mark the test as being known to fail. check-output-contains: (optional) Check that the output (stdout) contains the given pattern. Several such tags can be given, in which case the output must contains all the patterns. check-output-excludes: (optional) Similar than the above one, but with opposite logic. Check that the output (stdout) doesn't contain the given pattern. Several such tags can be given, in which case the output must contains none of the patterns. check-output-pattern--times: (optional) Similar than the contains/excludes her above, but with full control of the number of times the pattern should occurs in the output. 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 */ sparse-0.5.1/FAQ000066400000000000000000000064771314543357600133750ustar00rootroot00000000000000 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. 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. Wiki: http://sparse.wiki.kernel.org/index.php/Main_Page 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 sparse-0.5.1/LICENSE000066400000000000000000000027261314543357600140410ustar00rootroot00000000000000The 'sparse' C parser front-end library is copyrighted by Transmeta Corp and other authors and licensed under the "MIT License" as obtained from www.opensource.org (and included here-in for easy reference). [ This copy of the license is the flat-text version of original, available in its full glory at http://opensource.org/licenses/MIT please refer to there for the authoritative and slightly more pretty-printed version ] ------ The MIT License (MIT) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. sparse-0.5.1/Makefile000066400000000000000000000165711314543357600144770ustar00rootroot00000000000000VERSION=0.5.1 # Generating file version.h if current version has changed SPARSE_VERSION:=$(shell git describe 2>/dev/null || echo '$(VERSION)') VERSION_H := $(shell cat version.h 2>/dev/null) ifneq ($(lastword $(VERSION_H)),"$(SPARSE_VERSION)") $(info $(shell echo ' GEN 'version.h)) $(shell echo '#define SPARSE_VERSION "$(SPARSE_VERSION)"' > version.h) endif OS = linux CC = gcc CFLAGS = -O2 -finline-functions -fno-strict-aliasing -g CFLAGS += -Wall -Wwrite-strings LDFLAGS += -g LD = gcc AR = ar PKG_CONFIG = pkg-config CHECKER = ./cgcc -no-compile CHECKER_FLAGS = ALL_CFLAGS = $(CFLAGS) $(BASIC_CFLAGS) # # For debugging, put this in local.mk: # # CFLAGS += -O0 -DDEBUG -g3 -gdwarf-2 # HAVE_LIBXML:=$(shell $(PKG_CONFIG) --exists libxml-2.0 2>/dev/null && echo 'yes') HAVE_GCC_DEP:=$(shell touch .gcc-test.c && \ $(CC) -c -Wp,-MD,.gcc-test.d .gcc-test.c 2>/dev/null && \ echo 'yes'; rm -f .gcc-test.d .gcc-test.o .gcc-test.c) GTK_VERSION:=3.0 HAVE_GTK:=$(shell $(PKG_CONFIG) --exists gtk+-$(GTK_VERSION) 2>/dev/null && echo 'yes') ifneq ($(HAVE_GTK),yes) GTK_VERSION:=2.0 HAVE_GTK:=$(shell $(PKG_CONFIG) --exists gtk+-$(GTK_VERSION) 2>/dev/null && echo 'yes') endif LLVM_CONFIG:=llvm-config HAVE_LLVM:=$(shell $(LLVM_CONFIG) --version >/dev/null 2>&1 && echo 'yes') GCC_BASE := $(shell $(CC) --print-file-name=) BASIC_CFLAGS = -DGCC_BASE=\"$(GCC_BASE)\" MULTIARCH_TRIPLET := $(shell $(CC) -print-multiarch 2>/dev/null) BASIC_CFLAGS += -DMULTIARCH_TRIPLET=\"$(MULTIARCH_TRIPLET)\" ifeq ($(HAVE_GCC_DEP),yes) BASIC_CFLAGS += -Wp,-MD,$(@D)/.$(@F).d endif DESTDIR= PREFIX=$(HOME) BINDIR=$(PREFIX)/bin LIBDIR=$(PREFIX)/lib MANDIR=$(PREFIX)/share/man MAN1DIR=$(MANDIR)/man1 INCLUDEDIR=$(PREFIX)/include PKGCONFIGDIR=$(LIBDIR)/pkgconfig PROGRAMS=test-lexing test-parsing obfuscate compile graph sparse \ test-linearize example test-unssa test-dissect ctags INST_PROGRAMS=sparse cgcc INST_MAN1=sparse.1 cgcc.1 ifeq ($(HAVE_LIBXML),yes) PROGRAMS+=c2xml INST_PROGRAMS+=c2xml c2xml_EXTRA_OBJS = `$(PKG_CONFIG) --libs libxml-2.0` LIBXML_CFLAGS := $(shell $(PKG_CONFIG) --cflags libxml-2.0) else $(warning Your system does not have libxml, disabling c2xml) endif ifeq ($(HAVE_GTK),yes) GTK_CFLAGS := $(shell $(PKG_CONFIG) --cflags gtk+-$(GTK_VERSION)) GTK_LIBS := $(shell $(PKG_CONFIG) --libs gtk+-$(GTK_VERSION)) PROGRAMS += test-inspect INST_PROGRAMS += test-inspect test-inspect_EXTRA_DEPS := ast-model.o ast-view.o ast-inspect.o test-inspect_OBJS := test-inspect.o $(test-inspect_EXTRA_DEPS) $(test-inspect_OBJS) $(test-inspect_OBJS:.o=.sc): CFLAGS += $(GTK_CFLAGS) test-inspect_EXTRA_OBJS := $(GTK_LIBS) else $(warning Your system does not have gtk3/gtk2, disabling test-inspect) endif ifeq ($(HAVE_LLVM),yes) LLVM_VERSION:=$(shell $(LLVM_CONFIG) --version) ifeq ($(shell expr "$(LLVM_VERSION)" : '[3-9]\.'),2) LLVM_PROGS := sparse-llvm $(LLVM_PROGS): LD := g++ LLVM_LDFLAGS := $(shell $(LLVM_CONFIG) --ldflags) LLVM_CFLAGS := $(shell $(LLVM_CONFIG) --cflags | sed -e "s/-DNDEBUG//g" | sed -e "s/-pedantic//g") LLVM_LIBS := $(shell $(LLVM_CONFIG) --libs) LLVM_LIBS += $(shell $(LLVM_CONFIG) --system-libs 2>/dev/null) PROGRAMS += $(LLVM_PROGS) INST_PROGRAMS += sparse-llvm sparsec sparse-llvm.o: BASIC_CFLAGS += $(LLVM_CFLAGS) sparse-llvm_EXTRA_OBJS := $(LLVM_LIBS) $(LLVM_LDFLAGS) else $(warning LLVM 3.0 or later required. Your system has version $(LLVM_VERSION) installed.) endif else $(warning Your system does not have llvm, disabling sparse-llvm) endif LIB_H= token.h parse.h lib.h symbol.h scope.h expression.h target.h \ linearize.h bitmap.h ident-list.h compat.h flow.h allocate.h \ storage.h ptrlist.h dissect.h LIB_OBJS= target.o parse.o tokenize.o pre-process.o symbol.o lib.o scope.o \ expression.o show-parse.o evaluate.o expand.o inline.o linearize.o \ char.o sort.o allocate.o compat-$(OS).o ptrlist.o \ builtin.o \ stats.o \ flow.o cse.o simplify.o memops.o liveness.o storage.o unssa.o dissect.o LIB_FILE= libsparse.a SLIB_FILE= libsparse.so # If you add $(SLIB_FILE) to this, you also need to add -fpic to BASIC_CFLAGS above. # Doing so incurs a noticeable performance hit, and Sparse does not have a # stable shared library interface, so this does not occur by default. If you # really want a shared library, you may want to build Sparse twice: once # without -fpic to get all the Sparse tools, and again with -fpic to get the # shared library. LIBS=$(LIB_FILE) # # Pretty print # V = @ Q = $(V:1=) QUIET_CC = $(Q:@=@echo ' CC '$@;) QUIET_CHECK = $(Q:@=@echo ' CHECK '$<;) QUIET_AR = $(Q:@=@echo ' AR '$@;) QUIET_GEN = $(Q:@=@echo ' GEN '$@;) QUIET_LINK = $(Q:@=@echo ' LINK '$@;) # We rely on the -v switch of install to print 'file -> $install_dir/file' QUIET_INST_SH = $(Q:@=echo -n ' INSTALL ';) QUIET_INST = $(Q:@=@echo -n ' INSTALL ';) define INSTALL_EXEC $(QUIET_INST)install -v $1 $(DESTDIR)$2/$1 || exit 1; endef define INSTALL_FILE $(QUIET_INST)install -v -m 644 $1 $(DESTDIR)$2/$1 || exit 1; endef SED_PC_CMD = 's|@version@|$(VERSION)|g; \ s|@prefix@|$(PREFIX)|g; \ s|@libdir@|$(LIBDIR)|g; \ s|@includedir@|$(INCLUDEDIR)|g' # Allow users to override build settings without dirtying their trees -include local.mk all: $(PROGRAMS) sparse.pc all-installable: $(INST_PROGRAMS) $(LIBS) $(LIB_H) sparse.pc install: all-installable $(Q)install -d $(DESTDIR)$(BINDIR) $(Q)install -d $(DESTDIR)$(LIBDIR) $(Q)install -d $(DESTDIR)$(MAN1DIR) $(Q)install -d $(DESTDIR)$(INCLUDEDIR)/sparse $(Q)install -d $(DESTDIR)$(PKGCONFIGDIR) $(foreach f,$(INST_PROGRAMS),$(call INSTALL_EXEC,$f,$(BINDIR))) $(foreach f,$(INST_MAN1),$(call INSTALL_FILE,$f,$(MAN1DIR))) $(foreach f,$(LIBS),$(call INSTALL_FILE,$f,$(LIBDIR))) $(foreach f,$(LIB_H),$(call INSTALL_FILE,$f,$(INCLUDEDIR)/sparse)) $(call INSTALL_FILE,sparse.pc,$(PKGCONFIGDIR)) sparse.pc: sparse.pc.in $(QUIET_GEN)sed $(SED_PC_CMD) sparse.pc.in > sparse.pc compile_EXTRA_DEPS = compile-i386.o $(foreach p,$(PROGRAMS),$(eval $(p): $($(p)_EXTRA_DEPS) $(LIBS))) $(PROGRAMS): % : %.o $(QUIET_LINK)$(LD) $(LDFLAGS) -o $@ $^ $($@_EXTRA_OBJS) $(LIB_FILE): $(LIB_OBJS) $(QUIET_AR)$(AR) rcs $@ $(LIB_OBJS) $(SLIB_FILE): $(LIB_OBJS) $(QUIET_LINK)$(CC) $(LDFLAGS) -Wl,-soname,$@ -shared -o $@ $(LIB_OBJS) DEP_FILES := $(wildcard .*.o.d) ifneq ($(DEP_FILES),) include $(DEP_FILES) endif c2xml.o c2xml.sc: CFLAGS += $(LIBXML_CFLAGS) pre-process.sc: CHECKER_FLAGS += -Wno-vla %.o: %.c $(LIB_H) $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< %.sc: %.c sparse $(QUIET_CHECK) $(CHECKER) $(CHECKER_FLAGS) -c $(ALL_CFLAGS) $< ALL_OBJS := $(LIB_OBJS) $(foreach p,$(PROGRAMS),$(p).o $($(p)_EXTRA_DEPS)) selfcheck: $(ALL_OBJS:.o=.sc) clean: clean-check rm -f *.[oa] .*.d *.so $(PROGRAMS) $(SLIB_FILE) pre-process.h sparse.pc dist: @if test "$(SPARSE_VERSION)" != "v$(VERSION)" ; then \ echo 'Update VERSION in the Makefile before running "make dist".' ; \ exit 1 ; \ fi git archive --format=tar --prefix=sparse-$(VERSION)/ HEAD^{tree} | gzip -9 > sparse-$(VERSION).tar.gz check: all $(Q)cd validation && ./test-suite clean-check: find validation/ \( -name "*.c.output.expected" \ -o -name "*.c.output.got" \ -o -name "*.c.output.diff" \ -o -name "*.c.error.expected" \ -o -name "*.c.error.got" \ -o -name "*.c.error.diff" \ \) -exec rm {} \; sparse-0.5.1/README000066400000000000000000000057131314543357600137130ustar00rootroot00000000000000 sparse (spärs), adj,., spars-er, spars-est. 1. thinly scattered or distributed; "a sparse population" 2. thin; not thick or dense: "sparse hair" 3. scanty; meager. 4. semantic parse [ from Latin: spars(us) scattered, past participle of spargere 'to sparge' ] Antonym: abundant Sparse is a semantic parser of source files: it's neither a compiler (although it could be used as a front-end for one) nor is it a preprocessor (although it contains as a part of it a preprocessing phase). It is meant to be a small - and simple - library. Scanty and meager, and partly because of that easy to use. It has one mission in life: create a semantic parse tree for some arbitrary user for further analysis. It's not a tokenizer, nor is it some generic context-free parser. In fact, context (semantics) is what it's all about - figuring out not just what the grouping of tokens are, but what the _types_ are that the grouping implies. And no, it doesn't use lex and yacc (or flex and bison). In my personal opinion, the result of using lex/yacc tends to end up just having to fight the assumptions the tools make. The parsing is done in five phases: - full-file tokenization - pre-processing (which can cause another tokenization phase of another file) - semantic parsing. - lazy type evaluation - inline function expansion and tree simplification Note the "full file" part. Partly for efficiency, but mostly for ease of use, there are no "partial results". The library completely parses one whole source file, and builds up the _complete_ parse tree in memory. Also note the "lazy" in the type evaluation. The semantic parsing itself will know which symbols are typedefines (required for parsing C correctly), but it will not have calculated what the details of the different types are. That will be done only on demand, as the back-end requires the information. This means that a user of the library will literally just need to do struct string_list *filelist = NULL; char *file; action(sparse_initialize(argc, argv, filelist)); FOR_EACH_PTR_NOTAG(filelist, file) { action(sparse(file)); } END_FOR_EACH_PTR_NOTAG(file); and he is now done - having a full C parse of the file he opened. The library doesn't need any more setup, and once done does not impose any more requirements. The user is free to do whatever he wants with the parse tree that got built up, and needs not worry about the library ever again. There is no extra state, there are no parser callbacks, there is only the parse tree that is described by the header files. The action funtion takes a pointer to a symbol_list and does whatever it likes with it. The library also contains (as an example user) a few clients that do the preprocessing, parsing and type evaluation and just print out the results. These clients were done to verify and debug the library, and also as trivial examples of what you can do with the parse tree once it is formed, so that users can see how the tree is organized. sparse-0.5.1/allocate.c000066400000000000000000000111241314543357600147540ustar00rootroot00000000000000/* * allocate.c - simple space-efficient blob allocator. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * 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 #include #include #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); } void get_allocator_stats(struct allocator_struct *x, struct allocator_stats *s) { s->name = x->name; s->allocations = x->allocations; s->useful_bytes = x->useful_bytes; s->total_bytes = x->total_bytes; } 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"); sparse-0.5.1/allocate.h000066400000000000000000000051531314543357600147660ustar00rootroot00000000000000#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; }; struct allocator_stats { const char *name; unsigned int allocations; unsigned long 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 *); extern void get_allocator_stats(struct allocator_struct *, struct allocator_stats *); extern void show_allocation_stats(void); #define __DECLARE_ALLOCATOR(type, x) \ extern type *__alloc_##x(int); \ extern void __free_##x(type *); \ extern void show_##x##_alloc(void); \ extern void get_##x##_stats(struct allocator_stats *); \ 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 get_##x##_stats(struct allocator_stats *s) \ { \ get_allocator_stats(&x##_allocator, s); \ } \ 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(pseudo); #endif sparse-0.5.1/ast-inspect.c000066400000000000000000000154141314543357600154300ustar00rootroot00000000000000 #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; } } sparse-0.5.1/ast-inspect.h000066400000000000000000000005121314543357600154260ustar00rootroot00000000000000 #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 sparse-0.5.1/ast-model.c000066400000000000000000000346021314543357600150630ustar00rootroot00000000000000/* * 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 NULL; 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 ? iter->user_data : AST_NODE(tree_model); 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; } sparse-0.5.1/ast-model.h000066400000000000000000000043061314543357600150660ustar00rootroot00000000000000 /* * ast-model.h * * Copyright (C) 2010 Christopher Li. * */ #ifndef _ast_model_h_ #define _ast_model_h_ #include #include #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_*/ sparse-0.5.1/ast-view.c000066400000000000000000000022151314543357600147300ustar00rootroot00000000000000 #include #include "ast-model.h" #include "ast-inspect.h" #include "ast-view.h" static 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(); } sparse-0.5.1/ast-view.h000066400000000000000000000001371314543357600147360ustar00rootroot00000000000000 #include #include "lib.h" extern void treeview_main(struct symbol_list *syms); sparse-0.5.1/bitmap.h000066400000000000000000000026341314543357600144570ustar00rootroot00000000000000#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 */ sparse-0.5.1/builtin.c000066400000000000000000000145671314543357600146540ustar00rootroot00000000000000/* * builtin evaluation & expansion. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "expression.h" #include "expand.h" #include "symbol.h" #include "compat/bswap.h" 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; } /* The arguments are constant if the cost of all of them is zero */ static 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 */ static int expand_safe_p(struct expression *expr, int cost) { expr->type = EXPR_VALUE; expr->value = (cost < SIDE_EFFECTS); 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, }; /* The argument is constant and valid if the cost is zero */ static int expand_bswap(struct expression *expr, int cost) { struct expression *arg; long long val; if (cost) return cost; /* the arguments number & type have already been checked */ arg = first_expression(expr->args); val = get_expression_value_silent(arg); switch (expr->ctype->bit_size) { case 16: expr->value = bswap16(val); break; case 32: expr->value = bswap32(val); break; case 64: expr->value = bswap64(val); break; default: /* impossible error */ return SIDE_EFFECTS; } expr->type = EXPR_VALUE; expr->taint = 0; return 0; } static struct symbol_op bswap_op = { .expand = expand_bswap, }; /* * 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; } builtins_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 }, { "__builtin_bswap16", NULL, MOD_TOPLEVEL, &bswap_op }, { "__builtin_bswap32", NULL, MOD_TOPLEVEL, &bswap_op }, { "__builtin_bswap64", NULL, MOD_TOPLEVEL, &bswap_op }, { NULL, NULL, 0 } }; void init_builtins(int stream) { struct sym_init *ptr; builtin_fn_type.variadic = 1; for (ptr = builtins_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; } } sparse-0.5.1/c2xml.c000066400000000000000000000172561314543357600142310ustar00rootroot00000000000000/* * Sparse c2xml * * Dumps the parse tree as an xml document * * Copyright (C) 2007 Rob Taylor * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include #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<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; default: 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; ipos.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; } sparse-0.5.1/cgcc000077500000000000000000000255251314543357600136630ustar00rootroot00000000000000#!/usr/bin/perl -w # ----------------------------------------------------------------------------- my $cc = $ENV{'REAL_CC'} || 'cc'; my $check = $ENV{'CHECK'} || 'sparse'; my $ccom = $cc; 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 $multiarch_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 (/^-multiarch-dir$/) { $multiarch_dir = shift @ARGV; die ("$0: missing argument for -multiarch-dir option") if !$multiarch_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($ccom -print-file-name=) if !$gcc_base_dir; chomp($gcc_base_dir); # possibly remove '\n' from compiler $check .= " -gcc-base-dir " . $gcc_base_dir if $gcc_base_dir; $multiarch_dir = qx($ccom -print-multiarch) if ! defined $multiarch_dir; chomp($multiarch_dir); # possibly remove '\n' from compiler $check .= " -multiarch-dir " . $multiarch_dir if $multiarch_dir; print "$check\n" if $verbose; if ($do_compile) { system ($check) == 0 or exit 1; } 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-?)?(address-space|bitwise|cast-to-as|cast-truncate|context|decl|default-bitfield-sign|designated-init|do-while|enum-mismatch|init-cstring|memcpy-max-count|non-pointer-null|old-initializer|one-bit-signed-bitfield|override-init-all|paren-string|ptr-subtraction-blows|return-void|sizeof-bool|sparse-all|sparse-error|transparent-union|typesign|undef|unknown-attribute)$/; return 1 if $arg =~ /^-v(no-?)?(entry|dead)$/; return 1 if $arg =~ /^-f(dump-linearize|memcpy-max-count)(=\S*)?$/; 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 'darwin') { return ' -D__APPLE__=1 -D__MACH__=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 (' -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") . ' -D__SIZEOF_POINTER__=' . ($m64 ? '8' : '4')); } elsif ($spec eq 'sparc') { return (' -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") . ' -D__SIZEOF_POINTER__=' . ($m64 ? '8' : '4')); } elsif ($spec eq 'sparc64') { return (' -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") . ' -D__SIZEOF_POINTER__=8'); } elsif ($spec eq 'x86_64') { return (' -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") . ' -D__SIZEOF_POINTER__=' . ($m32 ? '4' : '8')); } 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") . ' -D__SIZEOF_POINTER__=' . ($m64 ? '8' : '4')); } elsif ($spec eq 'ppc64') { return (' -D__powerpc__=1 -D__PPC__=1 -D_STRING_ARCH_unaligned=1' . ' -D__powerpc64__=1 -D__PPC64__=1' . ' -D_CALL_ELF=2' . ' -m64' . &float_types (1, 1, 21, [24,8], [53,11], [113,15])); } elsif ($spec eq 's390x') { return (' -D__s390x__ -D__s390__ -D_BIG_ENDIAN' . &integer_types (8, 16, 32, $m64 ? 64 : 32, 64) . &float_types (1, 1, 36, [24,8], [53,11], [113,15]) . &define_size_t ("long unsigned int") . ' -D__SIZEOF_POINTER__=' . ($m64 ? '8' : '4')); } elsif ($spec eq 'arm') { return (' -D__arm__=1 -m32' . &float_types (1, 1, 36, [24,8], [53,11], [53, 11])); } elsif ($spec eq 'aarch64') { return (' -D__aarch64__=1 -m64' . &float_types (1, 1, 36, [24,8], [53,11], [113,15])); } 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 =~ /^(ppc64)$/i) { return &add_specs ('ppc64') . ' -mbig-endian'; } elsif ($arch =~ /^(ppc64le)$/i) { return &add_specs ('ppc64') . ' -mlittle-endian'; } elsif ($arch =~ /^(s390x)$/i) { return &add_specs ('s390x'); } elsif ($arch =~ /^(sparc64)$/i) { return &add_specs ('sparc64'); } elsif ($arch =~ /^(arm)$/i) { return &add_specs ('arm'); } elsif ($arch =~ /^(aarch64)$/i) { return &add_specs ('aarch64'); } } else { die "$0: invalid specs: $spec\n"; } } # ----------------------------------------------------------------------------- sparse-0.5.1/cgcc.1000066400000000000000000000020461314543357600140100ustar00rootroot00000000000000.\" 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) sparse-0.5.1/char.c000066400000000000000000000063561314543357600141200ustar00rootroot00000000000000#include #include "target.h" #include "lib.h" #include "allocate.h" #include "token.h" #include "expression.h" #include "char.h" static const char *parse_escape(const char *p, unsigned *val, const char *end, int bits, struct position pos) { unsigned c = *p++; unsigned d; if (c != '\\') { *val = c; return p; } c = *p++; switch (c) { case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 't': c = '\t'; break; case 'n': c = '\n'; break; case 'v': c = '\v'; break; case 'f': c = '\f'; break; case 'r': c = '\r'; break; case 'e': c = '\e'; break; case 'x': { unsigned mask = -(1U << (bits - 4)); for (c = 0; p < end; c = (c << 4) + d) { d = hexval(*p); if (d > 16) break; p++; if (c & mask) { warning(pos, "hex escape sequence out of range"); mask = 0; } } break; } case '0'...'7': { if (p + 2 < end) end = p + 2; c -= '0'; while (p < end && (d = *p - '0') < 8) { c = (c << 3) + d; p++; } if ((c & 0400) && bits < 9) warning(pos, "octal escape sequence out of range"); break; } default: /* everything else is left as is */ warning(pos, "unknown escape sequence: '\\%c'", c); break; case '\\': case '\'': case '"': case '?': break; /* those are legal, so no warnings */ } *val = c & ~((~0U << (bits - 1)) << 1); return p; } void get_char_constant(struct token *token, unsigned long long *val) { const char *p = token->embedded, *end; unsigned v; int type = token_type(token); switch (type) { case TOKEN_CHAR: case TOKEN_WIDE_CHAR: p = token->string->data; end = p + token->string->length - 1; break; case TOKEN_CHAR_EMBEDDED_0 ... TOKEN_CHAR_EMBEDDED_3: end = p + type - TOKEN_CHAR; break; default: end = p + type - TOKEN_WIDE_CHAR; } p = parse_escape(p, &v, end, type < TOKEN_WIDE_CHAR ? bits_in_char : bits_in_wchar, token->pos); if (p != end) warning(token->pos, "multi-character character constant"); *val = v; } struct token *get_string_constant(struct token *token, struct expression *expr) { struct string *string = token->string; struct token *next = token->next, *done = NULL; int stringtype = token_type(token); int is_wide = stringtype == TOKEN_WIDE_STRING; static char buffer[MAX_STRING]; int len = 0; int bits; int esc_count = 0; while (!done) { switch (token_type(next)) { case TOKEN_WIDE_STRING: is_wide = 1; case TOKEN_STRING: next = next->next; break; default: done = next; } } bits = is_wide ? bits_in_wchar : bits_in_char; while (token != done) { unsigned v; const char *p = token->string->data; const char *end = p + token->string->length - 1; while (p < end) { if (*p == '\\') esc_count++; p = parse_escape(p, &v, end, bits, token->pos); if (len < MAX_STRING) buffer[len] = v; len++; } token = token->next; } if (len > MAX_STRING) { warning(token->pos, "trying to concatenate %d-character string (%d bytes max)", len, MAX_STRING); len = MAX_STRING; } if (esc_count || len >= string->length) { if (string->immutable || len >= string->length) /* can't cannibalize */ string = __alloc_string(len+1); string->length = len+1; memcpy(string->data, buffer, len); string->data[len] = '\0'; } expr->string = string; expr->wide = is_wide; return token; } sparse-0.5.1/char.h000066400000000000000000000002241314543357600141110ustar00rootroot00000000000000extern void get_char_constant(struct token *, unsigned long long *); extern struct token *get_string_constant(struct token *, struct expression *); sparse-0.5.1/compat-bsd.c000066400000000000000000000024771314543357600152340ustar00rootroot00000000000000/* * BSD Compatibility functions * * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #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); } sparse-0.5.1/compat-cygwin.c000066400000000000000000000033231314543357600157530ustar00rootroot00000000000000/* * Cygwin Compatibility functions * * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #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); } sparse-0.5.1/compat-linux.c000066400000000000000000000001671314543357600156150ustar00rootroot00000000000000#define _GNU_SOURCE #include "lib.h" #include "allocate.h" #include "compat/mmap-blob.c" #include "compat/strtold.c" sparse-0.5.1/compat-mingw.c000066400000000000000000000031021314543357600155670ustar00rootroot00000000000000/* * MinGW Compatibility functions * * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #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); } sparse-0.5.1/compat-solaris.c000066400000000000000000000012551314543357600161310ustar00rootroot00000000000000#include "lib.h" #include "allocate.h" #include "compat/mmap-blob.c" #include #include #include 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; } sparse-0.5.1/compat.h000066400000000000000000000013111314543357600144550ustar00rootroot00000000000000#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 sparse-0.5.1/compat/000077500000000000000000000000001314543357600143105ustar00rootroot00000000000000sparse-0.5.1/compat/bswap.h000066400000000000000000000023021314543357600155720ustar00rootroot00000000000000#ifndef _COMPAT_BSWAP_H_ #define _COMPAT_BSWAP_H_ #if defined(__GNUC__) #if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) #define __HAS_BUILTIN_BSWAP16 #endif #if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) #define __HAS_BUILTIN_BSWAP32 #define __HAS_BUILTIN_BSWAP64 #endif #endif #if defined(__clang__) #if (__clang_major__ > 3) || ((__clang_major__ == 3) && (__clang_minor__ >= 2)) #define __HAS_BUILTIN_BSWAP16 #endif #if (__clang_major__ > 3) || ((__clang_major__ == 3) && (__clang_minor__ >= 0)) #define __HAS_BUILTIN_BSWAP32 #define __HAS_BUILTIN_BSWAP64 #endif #endif #ifdef __HAS_BUILTIN_BSWAP16 #define bswap16(x) __builtin_bswap16(x) #else #include static inline uint16_t bswap16(uint16_t x) { return x << 8 | x >> 8; } #endif #ifdef __HAS_BUILTIN_BSWAP32 #define bswap32(x) __builtin_bswap32(x) #else #include static inline uint32_t bswap32(uint32_t x) { return x >> 24 | (x >> 8 & 0xff00) | (x << 8 & 0xff0000) | x << 24; } #endif #ifdef __HAS_BUILTIN_BSWAP64 #define bswap64(x) __builtin_bswap64(x) #else #include static inline uint64_t bswap64(uint64_t x) { return ((uint64_t)bswap32(x)) << 32 | bswap32(x >> 32); } #endif #endif sparse-0.5.1/compat/mmap-blob.c000066400000000000000000000015251314543357600163250ustar00rootroot00000000000000#include #include /* * 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 } sparse-0.5.1/compat/strtold.c000066400000000000000000000001621314543357600161460ustar00rootroot00000000000000#include long double string_to_ld(const char *nptr, char **endptr) { return strtold(nptr, endptr); } sparse-0.5.1/compile-i386.c000066400000000000000000001560431314543357600153210ustar00rootroot00000000000000/* * sparse/compile-i386.c * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * Copyright 2003 Jeff Garzik * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * 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 #include #include #include #include #include #include #include #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); if (write(STDOUT_FILENO, s, strlen(s)) < 0) die("can't write to stdout"); } static void emit_atom_list(struct function *f) { struct atom *atom; FOR_EACH_PTR(f->atom_list, atom) { switch (atom->type) { case ATOM_TEXT: { if (write(STDOUT_FILENO, atom->text, atom->text_len) < 0) die("can't write to stdout"); 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->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); FOR_EACH_PTR(f->pseudo_list, stor) { free(stor); } END_FOR_EACH_PTR(stor); 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; } sparse-0.5.1/compile.c000066400000000000000000000046331314543357600146270ustar00rootroot00000000000000/* * Example trivial client program that uses the sparse library * and x86 backend. * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * Copyright 2003 Jeff Garzik * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include #include #include #include #include #include #include #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; bits_in_bool = 8; 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; } sparse-0.5.1/compile.h000066400000000000000000000003071314543357600146260ustar00rootroot00000000000000#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 */ sparse-0.5.1/cse.c000066400000000000000000000215551314543357600137530ustar00rootroot00000000000000/* * CSE - walk the linearized instruction flow, and * see if we can simplify it and apply CSE on it. * * Copyright (C) 2004 Linus Torvalds */ #include #include #include #include #include #include #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); if (!insn->bb) return; 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) { /* commutative binop */ case OP_ADD: case OP_MULU: case OP_MULS: case OP_AND_BOOL: case OP_OR_BOOL: case OP_AND: case OP_OR: case OP_XOR: case OP_SET_EQ: case OP_SET_NE: if (i1->src1 == i2->src2 && i1->src2 == i2->src1) return 0; goto case_binops; case OP_SEL: if (i1->src3 != i2->src3) return i1->src3 < i2->src3 ? -1 : 1; /* Fall-through to binops */ /* Binary arithmetic */ case OP_SUB: case OP_DIVU: case OP_DIVS: case OP_MODU: case OP_MODS: case OP_SHL: case OP_LSR: case OP_ASR: /* Binary comparison */ 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: case_binops: 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); kill_instruction(insn); 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); if (repeat_phase & REPEAT_CFG_CLEANUP) kill_unreachable_bbs(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; } sparse-0.5.1/ctags.c000066400000000000000000000131451314543357600142760ustar00rootroot00000000000000/* * Sparse Ctags * * Ctags generates tags from preprocessing results. * * Copyright (C) 2006 Christopher Li * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #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; } sparse-0.5.1/dissect.c000066400000000000000000000343071314543357600146360ustar00rootroot00000000000000/* * sparse/dissect.c * * Started by Oleg Nesterov * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #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(usage_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_OFFSETOF: { struct symbol *in = base_type(expr->in); do { if (expr->op == '.') { in = report_member(U_VOID, &expr->pos, in, lookup_member(in, expr->ident, NULL)); } else { do_expression(U_R_VAL, expr->index); in = in->ctype.base_type; } } while ((expr = expr->down)); } 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 { int *m_atop = &m_addr; m_type = type; while (m_expr->type == EXPR_IDENTIFIER) { m_type = report_member(U_W_VAL, &m_expr->pos, m_type, lookup_member(m_type, m_expr->expr_ident, m_atop)); m_expr = m_expr->ident_expression; m_atop = NULL; } if (m_atop) { m_type = report_member(U_W_VAL, &m_expr->pos, m_type, lookup_member(m_type, NULL, m_atop)); } if (m_expr->type != EXPR_INITIALIZER) report_implicit(U_W_VAL, &m_expr->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); } sparse-0.5.1/dissect.h000066400000000000000000000010631314543357600146340ustar00rootroot00000000000000#ifndef DISSECT_H #define DISSECT_H #include #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 *); #endif sparse-0.5.1/evaluate.c000066400000000000000000002563601314543357600150130ustar00rootroot00000000000000/* * sparse/evaluate.c * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * Evaluate constant expressions. */ #include #include #include #include #include #include #include #include #include #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) { 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; } 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 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)); } } static struct symbol *cast_to_bool(struct expression *expr); /* * 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; if (is_bool_type(type)) cast_to_bool(expr); 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 | MOD_PURE) 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 (t1->variadic != t2->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'"); if (is_func_type(ctype)) { if (Waddress) warning(expr->pos, "the address of %s will always evaluate as true", "a function"); } else if (is_array_type(ctype)) { if (Waddress) warning(expr->pos, "the address of %s will always evaluate as true", "an array"); } else if (!is_scalar_type(ctype)) { sparse_error(expr->pos, "incorrect type in conditional"); info(expr->pos, " got %s", show_typename(ctype)); ctype = NULL; } } ctype = degenerate(expr); 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; /* the result is int [6.5.13(3), 6.5.14(3)] */ expr->ctype = &int_ctype; if (expr->flags) { if (!(expr->left->flags & expr->right->flags & Int_const_expr)) expr->flags = 0; } return &int_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: /* the result is int [6.5.8(6), 6.5.9(3)]*/ expr->ctype = &int_ctype; return &int_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); /* * if the condition is constant, the type is in fact known * so use it, as gcc & clang do. */ switch (expr_truth_value(expr->conditional)) { case 1: expr->ctype = ltype; break; case 0: expr->ctype = rtype; break; default: break; } 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 usual; /* 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; usual: target = usual_conversions(op, expr->left, expr->right, tclass, sclass, target, source); 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 check_assignment_types(struct symbol *target, struct expression **rp, 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; if (t == &bool_ctype) { if (is_fouled_type(s)) warning((*rp)->pos, "%s degrades to integer", show_typename(s->ctype.base_type)); goto Cast; } *typediff = "different base types"; return 0; } 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"; return 0; } 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"; return 0; } /* * If this is a function pointer assignment, it is * actually fine to assign a pointer to const data to * it, as a function pointer points to const data * implicitly, i.e., dereferencing it does not produce * an lvalue. */ if (b1->type == SYM_FN) mod1 |= MOD_CONST; if (mod2 & ~mod1) { *typediff = "different modifiers"; return 0; } 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) return 0; 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"; return 0; } *typediff = "invalid types"; return 0; Cast: *rp = cast_to(*rp, target); return 1; } 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); if (!check_assignment_types(target, rp, &typediff)) { 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; } return 1; } static int compatible_transparent_union(struct symbol *target, struct expression **rp) { struct symbol *t, *member; classify_type(target, &t); if (t->type != SYM_UNION || !t->transparent_union) return 0; FOR_EACH_PTR(t->symbol_list, member) { const char *typediff; if (check_assignment_types(member, rp, &typediff)) return 1; } END_FOR_EACH_PTR(member); return 0; } static int compatible_argument_type(struct expression *expr, struct symbol *target, struct expression **rp, const char *where) { if (compatible_transparent_union(target, rp)) return 1; return compatible_assignment_types(expr, target, rp, where); } 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; } examine_symbol_type(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(ctype, &ctype); int multiply = 0; if (!class || class & TYPE_COMPOUND) { expression_error(expr, "need scalar for ++/--"); return NULL; } if (!lvalue_expression(expr->unop)) { expression_error(expr, "need lvalue expression for ++/--"); return NULL; } if ((class & TYPE_RESTRICT) && restricted_unop(expr->op, &ctype)) unrestrict(expr, class, &ctype); 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_RESTRICT) goto Restr; Normal: if (!(class & TYPE_FLOAT)) { ctype = integer_promotion(ctype); expr->unop = cast_to(expr->unop, ctype); } else if (expr->op != '~') { /* no conversions needed */ } else { return bad_expr_type(expr); } if (expr->op == '+') *expr = *expr->unop; expr->ctype = ctype; return ctype; Restr: if (restricted_unop(expr->op, &ctype)) unrestrict(expr, class, &ctype); goto Normal; } 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_COMPARE; 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)); } /* the result is int [6.5.3.3(5)]*/ ctype = &int_ctype; break; default: break; } expr->ctype = ctype; return 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 = ""; 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)) { if (Wsizeof_bool) 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 *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 if (!target->forced_arg){ static char where[30]; examine_symbol_type(target); sprintf(where, "argument %d", i); compatible_argument_type(expr, target, p, where); } i++; NEXT_PTR_LIST(argtype); } END_FOR_EACH_PTR(expr); FINISH_PTR_LIST(argtype); return 1; } 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; int offset = e->offset; e->type = EXPR_POS; e->init_offset = 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; new->offset = field->offset; } *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 = array_element_offset(type->bit_size, e->idx_to); 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) { int offset = 0; if (ctype->type != SYM_STRUCT && ctype->type != SYM_UNION) { err = "field name not in struct or union"; break; } ctype = find_identifier(e->expr_ident, ctype->symbol_list, &offset); if (!ctype) { err = "unknown field name in"; break; } e->offset = offset; 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 (array_element_offset(old->ctype->bit_size, n) == 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; int offset = 0; 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); /* * We can't necessarily trust "field->offset", * because the field might be in an anonymous * union, and the field offset is then the offset * within that union. * * The "old->offset - old->field->offset" * would be the offset of such an anonymous * union. */ offset = old->offset - old->field->offset; } 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; new->offset = field->offset + offset; 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) { if (ctype->bit_size + bits_in_char < type->bit_size) warning(e->pos, "too long initializer-string for array of char"); else if (Winit_cstring && ctype->bit_size + bits_in_char == type->bit_size) { warning(e->pos, "too long initializer-string for array of char(no space for nul 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 *cast_to_bool(struct expression *expr) { struct expression *old = expr->cast_expression; struct expression *zero; struct symbol *otype; int oclass = classify_type(degenerate(old), &otype); struct symbol *ctype; if (oclass & TYPE_COMPOUND) return NULL; zero = alloc_const_expression(expr->pos, 0); expr->op = SPECIAL_NOTEQUAL; ctype = usual_conversions(expr->op, old, zero, oclass, TYPE_NUM, otype, zero->ctype); expr->type = EXPR_COMPARE; expr->left = cast_to(old, ctype); expr->right = cast_to(zero, ctype); return expr->ctype; } 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) && restricted_value(target, t1)) warning(expr->pos, "cast to %s", show_typename(t1)); if (class2 & TYPE_RESTRICT) { if (t1 == &bool_ctype) { if (class2 & TYPE_FOULED) warning(expr->pos, "%s degrades to integer", show_typename(t2)); } else { 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 (->)", as2, as1); if (as1 > 0 && !as2 && !is_null_pointer_constant(target) && Wcast_to_as) warning(expr->pos, "cast adds address space to expression ()", 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 expr->ctype; } } } if (t1 == &bool_ctype) cast_to_bool(expr); 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(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; int initialized = sym->initializer != NULL; while ((next = next->same_symbol) != NULL) { const char *typediff; evaluate_symbol(next); if (initialized && next->initializer) { sparse_error(sym->pos, "symbol '%s' has multiple initializers (originally initialized at %s:%d)", show_ident(sym->ident), stream_name(next->pos.stream), next->pos.line); /* Only warn once */ initialized = 0; } 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) { has_error &= ~ERROR_CURR_PHASE; 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); } static void evaluate_goto_statement(struct statement *stmt) { struct symbol *label = stmt->goto_label; if (label && !label->stmt && !lookup_keyword(label->ident, NS_KEYWORD)) sparse_error(stmt->pos, "label '%s' was not declared", show_ident(label->ident)); evaluate_expression(stmt->goto_expression); } 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_goto_statement(stmt); 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; } sparse-0.5.1/example.c000066400000000000000000001301561314543357600146320ustar00rootroot00000000000000/* * Example of how to write a compiler with sparse */ #include #include #include #include #include #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] = "", /* Terminator */ [OP_RET] = "ret", [OP_BR] = "br", [OP_CBR] = "cbr", [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: case OP_CBR: 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; } sparse-0.5.1/expand.c000066400000000000000000000733521314543357600144620ustar00rootroot00000000000000/* * sparse/expand.c * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * expand constant expressions. */ #include #include #include #include #include #include #include #include #include #include "lib.h" #include "allocate.h" #include "parse.h" #include "token.h" #include "symbol.h" #include "target.h" #include "expression.h" #include "expand.h" 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 (is_float_type(newtype) || is_float_type(oldtype)) 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: // _Bool requires a zero test rather than truncation. if (is_bool_type(newtype)) { expr->value = !!value; if (!conservative && value != 0 && value != 1) warning(old->pos, "odd constant _Bool cast (%llx becomes 1)", value); return; } // 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; if (l == mask && sr == -1) goto Overflow; 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; } /* * 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 cost + 1; 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 unsigned long bit_range(const struct expression *expr) { unsigned long range = 0; unsigned long size = 0; while (expr->type == EXPR_POS) { unsigned long nr = expr->init_nr; size = expr->ctype->bit_size; range += (nr - 1) * size; expr = expr->init_expr; } range += size; return range; } 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 *expr) { struct expression *a = NULL; unsigned long max = 0; unsigned long whole = expr->ctype->bit_size; struct expression *b; if (!Woverride_init) return; FOR_EACH_PTR(*list, b) { unsigned long off, end; if (!b->ctype || !b->ctype->bit_size) continue; off = bit_offset(b); if (a && off < max) { warning(a->pos, "Initializer entry defined twice"); info(b->pos, " also defined here"); if (!Woverride_init_all) return; } end = off + bit_range(b); if (!a && !Woverride_init_whole_range) { // If first entry is the whole range, do not let // any warning about it (this allow to initialize // an array with some default value and then override // some specific entries). if (off == 0 && end == whole) continue; } if (end > max) { max = end; 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, expr); 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) { if (strict != 2) expression_error(expr, "bad constant expression"); return 0; } if ((strict == 1) && 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); } long long get_expression_value_silent(struct expression *expr) { return __get_expression_value(expr, 2); } int expr_truth_value(struct expression *expr) { const int saved = conservative; struct symbol *ctype; if (!expr) return 0; ctype = evaluate_expression(expr); if (!ctype) return -1; conservative = 1; expand_expression(expr); conservative = saved; redo: switch (expr->type) { case EXPR_COMMA: expr = expr->right; goto redo; case EXPR_VALUE: return expr->value != 0; case EXPR_FVALUE: return expr->fvalue != 0; default: return -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; } sparse-0.5.1/expand.h000066400000000000000000000030001314543357600144460ustar00rootroot00000000000000#ifndef EXPAND_H #define EXPAND_H /* * sparse/expand.h * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /* 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 */ #endif sparse-0.5.1/expression.c000066400000000000000000000610741314543357600154000ustar00rootroot00000000000000/* * sparse/expression.c * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * This is the expression parsing part of parsing C. */ #include #include #include #include #include #include #include #include #include #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 "char.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 struct symbol *handle_func(struct token *token) { struct ident *ident = token->ident; struct symbol *decl, *array; struct string *string; int len; if (ident != &__func___ident && ident != &__FUNCTION___ident && ident != &__PRETTY_FUNCTION___ident) return NULL; if (!current_fn || !current_fn->ident) return NULL; /* OK, it's one of ours */ array = alloc_symbol(token->pos, SYM_ARRAY); array->ctype.base_type = &char_ctype; array->ctype.alignment = 1; array->endpos = token->pos; decl = alloc_symbol(token->pos, SYM_NODE); decl->ctype.base_type = array; decl->ctype.alignment = 1; decl->ctype.modifiers = MOD_STATIC; decl->endpos = token->pos; /* function-scope, but in NS_SYMBOL */ bind_symbol(decl, ident, NS_LABEL); decl->namespace = NS_SYMBOL; len = current_fn->ident->len; string = __alloc_string(len + 1); memcpy(string->data, current_fn->ident->name, len); string->data[len] = 0; string->length = len + 1; decl->initializer = alloc_expression(token->pos, EXPR_STRING); decl->initializer->string = string; decl->initializer->ctype = decl; decl->array_size = alloc_const_expression(token->pos, len + 1); array->array_size = decl->array_size; decl->bit_size = array->bit_size = bytes_to_bits(len + 1); return decl; } 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; } } #ifndef ULLONG_MAX #define ULLONG_MAX (~0ULL) #endif static unsigned long long parse_num(const char *nptr, char **end) { if (nptr[0] == '0' && tolower((unsigned char)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 ... TOKEN_WIDE_CHAR_EMBEDDED_3: expr = alloc_expression(token->pos, EXPR_VALUE); expr->flags = Int_const_expr; expr->ctype = token_type(token) < TOKEN_WIDE_CHAR ? &int_ctype : &long_ctype; get_char_constant(token, &expr->value); 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) { sym = handle_func(token); 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: expr = alloc_expression(token->pos, EXPR_STRING); token = get_string_constant(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 }, { &_Alignof_ident, EXPR_ALIGNOF }, { &__sizeof_ptr___ident, EXPR_PTRSIZEOF }, }; int i; for (i = 0; i < ARRAY_SIZE(type_information); 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); } sparse-0.5.1/expression.h000066400000000000000000000144541314543357600154050ustar00rootroot00000000000000#ifndef EXPRESSION_H #define EXPRESSION_H /* * sparse/expression.h * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * 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 */ { int offset; 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 *); int expr_truth_value(struct expression *expr); long long get_expression_value(struct expression *); long long const_expression_value(struct expression *); long long get_expression_value_silent(struct expression *expr); /* 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 sparse-0.5.1/flow.c000066400000000000000000000566131314543357600141530ustar00rootroot00000000000000/* * Flow - walk the linearized flowgraph, simplifying it as we * go along. * * Copyright (C) 2004 Linus Torvalds */ #include #include #include #include #include #include #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 || !bb->ep) return 0; /* We might find new if-conversions or non-dominating CSEs */ /* we may also create new dead cycles */ repeat_phase |= REPEAT_CSE | REPEAT_CFG_CLEANUP; *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; } /* * This really should be handled by bb_depends_on() * which efficiently check the dependence using the * defines - needs liveness info. Problem is that * there is no liveness done on OP_PHI & OP_PHISRC. * * This function add the missing dependency checks. */ static int bb_depends_on_phi(struct basic_block *target, struct basic_block *src) { struct instruction *insn; FOR_EACH_PTR(src->insns, insn) { if (!insn->bb) continue; if (insn->opcode != OP_PHI) continue; if (pseudo_in_list(target->needs, insn->target)) return 1; } END_FOR_EACH_PTR(insn); 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; int bogus; /* * This a due to improper dominance tracking during * simplify_symbol_usage()/conversion to SSA form. * No sane simplification can be done when we have this. */ bogus = bb_list_size(bb->parents) != pseudo_list_size(first->phi_list); 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_CBR && 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; if (bb_depends_on_phi(target, bb)) continue; changed |= rewrite_branch(source, &br->bb_true, bb, target); changed |= rewrite_branch(source, &br->bb_false, bb, target); if (changed && !bogus) kill_use(THIS_ADDRESS(phi)); } 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_CBR || 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; if (bb_depends_on_phi(final, target)) return 0; 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); 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_CBR) 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); if (has_use_list(src)) 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; } static inline int distinct_symbols(pseudo_t a, pseudo_t b) { if (a->type != PSEUDO_SYM) return 0; if (b->type != PSEUDO_SYM) return 0; return a->sym != b->sym; } /* * 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 (distinct_symbols(insn->src, dom->src)) 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 phisrc_in_bb(struct pseudo_list *list, struct basic_block *bb) { pseudo_t p; FOR_EACH_PTR(list, p) { if (p->def->bb == bb) return 1; } END_FOR_EACH_PTR(p); return 0; } static int find_dominating_parents(pseudo_t pseudo, struct instruction *insn, struct basic_block *bb, unsigned long generation, struct pseudo_list **dominators, int local) { struct basic_block *parent; if (!bb->parents) return !!local; 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; 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)) return 0; continue; found_dominator: if (dominators && phisrc_in_bb(*dominators, parent)) continue; 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) { kill_instruction(phi->def); } 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)) 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; struct pseudo_user *pu; unsigned long mod; int all; /* 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; 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: 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)); } } END_FOR_EACH_PTR(pu); 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_force(insn); kill_defs(insn); /* * We kill unreachable instructions even if they * otherwise aren't "killable" (e.g. volatile loads) */ } 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_CBR: changed |= rewrite_branch(bb, &insn->bb_false, old, new); /* fall through */ case OP_BR: changed |= rewrite_branch(bb, &insn->bb_true, 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 (br->opcode == OP_CBR) { 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_CBR: vrfy_bb_in_list(br->bb_false, bb->children); /* fall through */ case OP_BR: vrfy_bb_in_list(br->bb_true, 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_CBR: 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); } sparse-0.5.1/flow.h000066400000000000000000000031061314543357600141450ustar00rootroot00000000000000#ifndef FLOW_H #define FLOW_H #include "lib.h" extern unsigned long bb_generation; #define REPEAT_CSE 1 #define REPEAT_SYMBOL_CLEANUP 2 #define REPEAT_CFG_CLEANUP 3 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_unreachable_bbs(struct entrypoint *ep); extern void kill_insn(struct instruction *, int force); static inline void kill_instruction(struct instruction *insn) { kill_insn(insn, 0); } static inline void kill_instruction_force(struct instruction *insn) { kill_insn(insn, 1); } 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 sparse-0.5.1/gcc-attr-list.h000066400000000000000000000104111314543357600156500ustar00rootroot00000000000000GCC_ATTR(BELOW100) GCC_ATTR(OS_Task) GCC_ATTR(OS_main) GCC_ATTR(OS_task) GCC_ATTR(abi_tag) GCC_ATTR(absdata) GCC_ATTR(address) GCC_ATTR(alias) GCC_ATTR(aligned) GCC_ATTR(alloc_align) GCC_ATTR(alloc_size) GCC_ATTR(altivec) GCC_ATTR(always_inline) GCC_ATTR(artificial) GCC_ATTR(assume_aligned) GCC_ATTR(bank_switch) GCC_ATTR(based) GCC_ATTR(below100) GCC_ATTR(bnd_instrument) GCC_ATTR(bnd_legacy) GCC_ATTR(bnd_variable_size) GCC_ATTR(break_handler) GCC_ATTR(brk_interrupt) GCC_ATTR(callee_pop_aggregate_return) GCC_ATTR(cb) GCC_ATTR(cdecl) GCC_ATTR(cleanup) GCC_ATTR(cmse_nonsecure_call) GCC_ATTR(cmse_nonsecure_entry) GCC_ATTR(cold) GCC_ATTR(common) GCC_ATTR(common_object) GCC_ATTR(const) GCC_ATTR(constructor) GCC_ATTR(critical) GCC_ATTR(default) GCC_ATTR(deprecated) GCC_ATTR(designated_init) GCC_ATTR(destructor) GCC_ATTR(disinterrupt) GCC_ATTR(dllexport) GCC_ATTR(dllimport) GCC_ATTR(eightbit_data) GCC_ATTR(either) GCC_ATTR(error) GCC_ATTR(exception) GCC_ATTR(exception_handler) GCC_ATTR(externally_visible) GCC_ATTR(fallthrough) GCC_ATTR(far) GCC_ATTR(fast_interrupt) GCC_ATTR(fastcall) GCC_ATTR(flatten) GCC_ATTR(force_align_arg_pointer) GCC_ATTR(format) GCC_ATTR(format_arg) GCC_ATTR(forwarder_section) GCC_ATTR(function_vector) GCC_ATTR(gcc_struct) GCC_ATTR(gnu_inline) GCC_ATTR(hidden) GCC_ATTR(hot) GCC_ATTR(hotpatch) GCC_ATTR(ifunc) GCC_ATTR(init_priority) GCC_ATTR(interfacearm) GCC_ATTR(internal) GCC_ATTR(interrupt) GCC_ATTR(interrupt_handler) GCC_ATTR(interrupt_thread) GCC_ATTR(io) GCC_ATTR(io_low) GCC_ATTR(isr) GCC_ATTR(keep_interrupts_masked) GCC_ATTR(kernel) GCC_ATTR(kspisusp) GCC_ATTR(l1_data) GCC_ATTR(l1_data_A) GCC_ATTR(l1_data_B) GCC_ATTR(l1_text) GCC_ATTR(l2) GCC_ATTR(leaf) GCC_ATTR(long_call) GCC_ATTR(longcall) GCC_ATTR(lower) GCC_ATTR(malloc) GCC_ATTR(may_alias) GCC_ATTR(maybe_unused) GCC_ATTR(medium_call) GCC_ATTR(micromips) GCC_ATTR(mips16) GCC_ATTR(mode) GCC_ATTR(model) GCC_ATTR(monitor) GCC_ATTR(ms_abi) GCC_ATTR(ms_hook_prologue) GCC_ATTR(ms_struct) GCC_ATTR(naked) GCC_ATTR(near) GCC_ATTR(nested) GCC_ATTR(nested_ready) GCC_ATTR(nesting) GCC_ATTR(nmi) GCC_ATTR(nmi_handler) GCC_ATTR(no_address_safety_analysis) GCC_ATTR(no_caller_saved_registers) GCC_ATTR(no_gccisr) GCC_ATTR(no_icf) GCC_ATTR(no_instrument_function) GCC_ATTR(no_profile_instrument_function) GCC_ATTR(no_reorder) GCC_ATTR(no_sanitize) GCC_ATTR(no_sanitize_address) GCC_ATTR(no_sanitize_thread) GCC_ATTR(no_sanitize_undefined) GCC_ATTR(no_split_stack) GCC_ATTR(no_stack_limit) GCC_ATTR(noclone) GCC_ATTR(nocommon) GCC_ATTR(nocompression) GCC_ATTR(nodiscard) GCC_ATTR(noinit) GCC_ATTR(noinline) GCC_ATTR(noipa) GCC_ATTR(nomicromips) GCC_ATTR(nomips16) GCC_ATTR(nonnull) GCC_ATTR(noplt) GCC_ATTR(noreturn) GCC_ATTR(nosave_low_regs) GCC_ATTR(not_nested) GCC_ATTR(nothrow) GCC_ATTR(notshared) GCC_ATTR(optimize) GCC_ATTR(packed) GCC_ATTR(partial_save) GCC_ATTR(patchable_function_entry) GCC_ATTR(pcs) GCC_ATTR(persistent) GCC_ATTR(progmem) GCC_ATTR(protected) GCC_ATTR(pure) GCC_ATTR(reentrant) GCC_ATTR(regparm) GCC_ATTR(renesas) GCC_ATTR(resbank) GCC_ATTR(reset) GCC_ATTR(returns_nonnull) GCC_ATTR(returns_twice) GCC_ATTR(s390_vector_bool) GCC_ATTR(saddr) GCC_ATTR(save_all) GCC_ATTR(save_volatiles) GCC_ATTR(saveall) GCC_ATTR(scalar_storage_order) GCC_ATTR(sda) GCC_ATTR(section) GCC_ATTR(selectany) GCC_ATTR(sentinel) GCC_ATTR(shared) GCC_ATTR(short_call) GCC_ATTR(shortcall) GCC_ATTR(signal) GCC_ATTR(simd) GCC_ATTR(sp_switch) GCC_ATTR(spu_vector) GCC_ATTR(sseregparm) GCC_ATTR(stack_protect) GCC_ATTR(stdcall) GCC_ATTR(syscall_linkage) GCC_ATTR(sysv_abi) GCC_ATTR(target) GCC_ATTR(target_clones) GCC_ATTR(tda) GCC_ATTR(thiscall) GCC_ATTR(tiny) GCC_ATTR(tiny_data) GCC_ATTR(tls_model) GCC_ATTR(transaction_callable) GCC_ATTR(transaction_may_cancel_outer) GCC_ATTR(transaction_pure) GCC_ATTR(transaction_safe) GCC_ATTR(transaction_safe_dynamic) GCC_ATTR(transaction_unsafe) GCC_ATTR(transaction_wrap) GCC_ATTR(transparent_union) GCC_ATTR(trap_exit) GCC_ATTR(trapa_handler) GCC_ATTR(unused) GCC_ATTR(upper) GCC_ATTR(use_debug_exception_return) GCC_ATTR(use_shadow_register_set) GCC_ATTR(used) GCC_ATTR(vector) GCC_ATTR(vector_size) GCC_ATTR(version_id) GCC_ATTR(visibility) GCC_ATTR(vliw) GCC_ATTR(volatile) GCC_ATTR(wakeup) GCC_ATTR(warm) GCC_ATTR(warn_unused) GCC_ATTR(warn_unused_result) GCC_ATTR(warning) GCC_ATTR(weak) GCC_ATTR(weakref) GCC_ATTR(zda) sparse-0.5.1/gdbhelpers000066400000000000000000000127121314543357600150720ustar00rootroot00000000000000 # 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 sparse-0.5.1/graph.c000066400000000000000000000132061314543357600142740ustar00rootroot00000000000000/* Copyright © International Business Machines Corp., 2006 * Adelard LLP, 2007 * * Author: Josh Triplett * Dan Sheridan * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #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=<\n" " \n" " \n" "
%s
%s()
>;\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; } sparse-0.5.1/gvpr/000077500000000000000000000000001314543357600140035ustar00rootroot00000000000000sparse-0.5.1/gvpr/return-paths000077500000000000000000000041371314543357600163720ustar00rootroot00000000000000#!/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($); } sparse-0.5.1/gvpr/subg-fwd000077500000000000000000000031471314543357600154540ustar00rootroot00000000000000#!/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); } sparse-0.5.1/gvpr/subg-rev000077500000000000000000000036271314543357600154730ustar00rootroot00000000000000#!/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); } sparse-0.5.1/ident-list.h000066400000000000000000000044001314543357600152500ustar00rootroot00000000000000 #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); /* C storage classes. They get marked as reserved when initialized */ IDENT(static); /* C99 keywords */ IDENT(restrict); IDENT(__restrict); IDENT(__restrict__); IDENT(_Bool); IDENT_RESERVED(_Complex); IDENT_RESERVED(_Imaginary); /* C11 keywords */ IDENT(_Alignas); IDENT_RESERVED(_Alignof); IDENT_RESERVED(_Atomic); IDENT_RESERVED(_Generic); IDENT(_Noreturn); IDENT_RESERVED(_Static_assert); IDENT(_Thread_local); /* 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__); /* Preprocessor idents. Direct use of __IDENT avoids mentioning the keyword * itself by name, preventing these tokens from expanding when compiling * sparse. */ IDENT(defined); IDENT(once); __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); __IDENT(__COUNTER___ident, "__COUNTER__", 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 sparse-0.5.1/inline.c000066400000000000000000000365171314543357600144630ustar00rootroot00000000000000/* * Sparse - a semantic source parser. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #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); } sparse-0.5.1/lib.c000066400000000000000000001150371314543357600137460ustar00rootroot00000000000000/* * 'sparse' library helper routines. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #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" #include "version.h" int verbose, optimize, optimize_size, preprocessing; int die_if_error = 0; int has_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; static const char *multiarch_dir = MULTIARCH_TRIPLET; 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); } 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 */ has_error |= ERROR_CURR_PHASE; 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 warning(struct position pos, const char * fmt, ...) { va_list args; if (Wsparse_error) { va_start(args, fmt); do_error(pos, fmt, args); va_end(args); return; } if (!max_warnings || has_error) { 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); } 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; } NORETURN_ATTR 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); } NORETURN_ATTR 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 = 0; int Waddress_space = 1; int Wbitwise = 1; int Wcast_to_as = 0; int Wcast_truncate = 1; int Wcontext = 1; int Wdecl = 1; int Wdeclarationafterstatement = -1; int Wdefault_bitfield_sign = 0; int Wdesignated_init = 1; int Wdo_while = 0; int Winit_cstring = 0; int Wenum_mismatch = 1; int Wsparse_error = 0; int Wmemcpy_max_count = 1; int Wnon_pointer_null = 1; int Wold_initializer = 1; int Wone_bit_signed_bitfield = 1; int Woverride_init = 1; int Woverride_init_all = 0; int Woverride_init_whole_range = 0; int Wparen_string = 0; int Wptr_subtraction_blows = 0; int Wreturn_void = 0; int Wshadow = 0; int Wsizeof_bool = 0; int Wtautological_compare = 0; int Wtransparent_union = 0; int Wtypesign = 0; int Wundef = 0; int Wuninitialized = 1; int Wunknown_attribute = 1; int Wvla = 1; int dump_macro_defs = 0; int dbg_entry = 0; int dbg_dead = 0; int fmem_report = 0; int fdump_linearize; unsigned long long fmemcpy_max_count = 100000; int preprocess_only; static enum { STANDARD_C89, STANDARD_C94, STANDARD_C99, STANDARD_C11, STANDARD_GNU11, STANDARD_GNU89, STANDARD_GNU99, } standard = STANDARD_GNU89; #define ARCH_LP32 0 #define ARCH_LP64 1 #define ARCH_LLP64 2 #ifdef __x86_64__ #define ARCH_M64_DEFAULT ARCH_LP64 #else #define ARCH_M64_DEFAULT ARCH_LP32 #endif int arch_m64 = ARCH_M64_DEFAULT; int arch_msize_long = 0; #ifdef __BIG_ENDIAN__ #define ARCH_BIG_ENDIAN 1 #else #define ARCH_BIG_ENDIAN 0 #endif int arch_big_endian = ARCH_BIG_ENDIAN; #define CMDLINE_INCLUDE 20 static int cmdline_include_nr = 0; static char *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((unsigned char)*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) { if (cmdline_include_nr >= CMDLINE_INCLUDE) die("too many include files for %s\n", filename); cmdline_include[cmdline_include_nr++] = filename; } 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_multiarch_dir(char *arg, char **next) { multiarch_dir = *++next; if (!multiarch_dir) die("missing argument for -multiarch-dir option"); return next; } static char **handle_switch_m(char *arg, char **next) { if (!strcmp(arg, "m64")) { arch_m64 = ARCH_LP64; } else if (!strcmp(arg, "m32")) { arch_m64 = ARCH_LP32; } else if (!strcmp(arg, "msize-llp64")) { arch_m64 = ARCH_LLP64; } else if (!strcmp(arg, "msize-long")) { arch_msize_long = 1; } else if (!strcmp(arg, "multiarch-dir")) { return handle_multiarch_dir(arg, next); } else if (!strcmp(arg, "mbig-endian")) { arch_big_endian = 1; } else if (!strcmp(arg, "mlittle-endian")) { arch_big_endian = 0; } return next; } static void handle_arch_m64_finalize(void) { switch (arch_m64) { case ARCH_LP32: /* default values */ return; case ARCH_LP64: bits_in_long = 64; max_int_alignment = 8; size_t_ctype = &ulong_ctype; ssize_t_ctype = &long_ctype; add_pre_buffer("#weak_define __LP64__ 1\n"); add_pre_buffer("#weak_define _LP64 1\n"); goto case_64bit_common; case ARCH_LLP64: bits_in_long = 32; max_int_alignment = 4; size_t_ctype = &ullong_ctype; ssize_t_ctype = &llong_ctype; add_pre_buffer("#weak_define __LLP64__ 1\n"); goto case_64bit_common; case_64bit_common: bits_in_pointer = 64; pointer_alignment = 8; #ifdef __x86_64__ add_pre_buffer("#weak_define __x86_64__ 1\n"); #endif break; } } static void handle_arch_msize_long_finalize(void) { if (arch_msize_long) { size_t_ctype = &ulong_ctype; ssize_t_ctype = &long_ctype; } } static void handle_arch_finalize(void) { handle_arch_m64_finalize(); handle_arch_msize_long_finalize(); } static int handle_simple_switch(const char *arg, const char *name, int *flag) { int val = 1; // Prefixe "no-" mean to turn flag off. if (strncmp(arg, "no-", 3) == 0) { arg += 3; val = 0; } if (strcmp(arg, name) == 0) { *flag = val; return 1; } // not handled return 0; } 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", &Waddress }, { "address-space", &Waddress_space }, { "bitwise", &Wbitwise }, { "cast-to-as", &Wcast_to_as }, { "cast-truncate", &Wcast_truncate }, { "context", &Wcontext }, { "decl", &Wdecl }, { "declaration-after-statement", &Wdeclarationafterstatement }, { "default-bitfield-sign", &Wdefault_bitfield_sign }, { "designated-init", &Wdesignated_init }, { "do-while", &Wdo_while }, { "enum-mismatch", &Wenum_mismatch }, { "init-cstring", &Winit_cstring }, { "memcpy-max-count", &Wmemcpy_max_count }, { "non-pointer-null", &Wnon_pointer_null }, { "old-initializer", &Wold_initializer }, { "one-bit-signed-bitfield", &Wone_bit_signed_bitfield }, { "override-init", &Woverride_init }, { "override-init-all", &Woverride_init_all }, { "paren-string", &Wparen_string }, { "ptr-subtraction-blows", &Wptr_subtraction_blows }, { "return-void", &Wreturn_void }, { "shadow", &Wshadow }, { "sizeof-bool", &Wsizeof_bool }, { "sparse-error", &Wsparse_error }, { "tautological-compare", &Wtautological_compare }, { "transparent-union", &Wtransparent_union }, { "typesign", &Wtypesign }, { "undef", &Wundef }, { "uninitialized", &Wuninitialized }, { "unknown-attribute", &Wunknown_attribute }, { "vla", &Wvla }, }; 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 != &Wsparse_error) *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 struct warning dumps[] = { { "D", &dump_macro_defs}, }; static char **handle_switch_d(char *arg, char **next) { char ** ret = handle_onoff_switch(arg, next, dumps, ARRAY_SIZE(dumps)); if (ret) return ret; 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: case STANDARD_C11: case STANDARD_GNU11: 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_fmemcpy_max_count(char *arg, char **next) { unsigned long long val; char *end; val = strtoull(arg, &end, 0); if (*end != '\0' || end == arg) die("error: missing argument to \"-fmemcpy-max-count=\""); if (val == 0) val = ~0ULL; fmemcpy_max_count = val; 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_fdump(char *arg, char **next) { if (!strncmp(arg, "linearize", 9)) { arg += 9; if (*arg == '\0') fdump_linearize = 1; else if (!strcmp(arg, "=only")) fdump_linearize = 2; else goto err; } /* ignore others flags */ return next; err: die("error: unknown flag \"-fdump-%s\"", arg); } static char **handle_switch_f(char *arg, char **next) { arg++; if (!strncmp(arg, "tabstop=", 8)) return handle_switch_ftabstop(arg+8, next); if (!strncmp(arg, "dump-", 5)) return handle_switch_fdump(arg+5, next); if (!strncmp(arg, "memcpy-max-count=", 17)) return handle_switch_fmemcpy_max_count(arg+17, next); /* handle switches w/ arguments above, boolean and only boolean below */ if (handle_simple_switch(arg, "mem-report", &fmem_report)) return next; 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 if (!strcmp(arg, "c11") || !strcmp(arg, "c1x") || !strcmp(arg, "iso9899:2011")) standard = STANDARD_C11; else if (!strcmp(arg, "gnu11")) standard = STANDARD_GNU11; 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_switch_n(char *arg, char **next) { if (!strcmp (arg, "nostdinc")) return handle_nostdinc(arg, next); 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; } static char **handle_switch_g(char *arg, char **next) { if (!strcmp (arg, "gcc-base-dir")) return handle_base_dir(arg, next); return next; } static char **handle_version(char *arg, char **next) { printf("%s\n", SPARSE_VERSION); exit(0); } static char **handle_param(char *arg, char **next) { char *value = NULL; /* For now just skip any '--param=*' or '--param *' */ if (*arg == '\0') { value = *++next; } else if (isspace((unsigned char)*arg) || *arg == '=') { value = ++arg; } if (!value) die("missing argument for --param option"); return next; } struct switches { const char *name; char **(*fn)(char *, char **); unsigned int prefix:1; }; static char **handle_long_options(char *arg, char **next) { static struct switches cmd[] = { { "param", handle_param, 1 }, { "version", handle_version }, { NULL, NULL } }; struct switches *s = cmd; while (s->name) { int optlen = strlen(s->name); if (!strncmp(s->name, arg, optlen + !s->prefix)) return s->fn(arg + optlen, next); s++; } return next; } static char **handle_switch(char *arg, char **next) { switch (*arg) { case 'a': return handle_switch_a(arg, next); case 'D': return handle_switch_D(arg, next); case 'd': return handle_switch_d(arg, next); case 'E': return handle_switch_E(arg, next); case 'f': return handle_switch_f(arg, next); case 'g': return handle_switch_g(arg, next); case 'G': return handle_switch_G(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 'n': return handle_switch_n(arg, next); case 'o': return handle_switch_o(arg, next); case 'O': return handle_switch_O(arg, next); case 's': return handle_switch_s(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 '-': return handle_long_options(arg + 1, next); default: break; } /* * Ignore unknown command line options: * they're probably gcc switches */ return next; } static void predefined_sizeof(const char *name, unsigned bits) { add_pre_buffer("#weak_define __SIZEOF_%s__ %d\n", name, bits/8); } static void predefined_max(const char *name, const char *suffix, unsigned bits) { unsigned long long max = (1ULL << (bits - 1 )) - 1; add_pre_buffer("#weak_define __%s_MAX__ %#llx%s\n", name, max, suffix); } static void predefined_type_size(const char *name, const char *suffix, unsigned bits) { predefined_max(name, suffix, bits); predefined_sizeof(name, bits); } static void predefined_macros(void) { add_pre_buffer("#define __CHECKER__ 1\n"); predefined_sizeof("SHORT", bits_in_short); predefined_max("SHRT", "", bits_in_short); predefined_max("SCHAR", "", bits_in_char); predefined_max("WCHAR", "", bits_in_wchar); add_pre_buffer("#weak_define __CHAR_BIT__ %d\n", bits_in_char); predefined_type_size("INT", "", bits_in_int); predefined_type_size("LONG", "L", bits_in_long); predefined_type_size("LONG_LONG", "LL", bits_in_longlong); predefined_sizeof("INT128", 128); predefined_sizeof("SIZE_T", bits_in_pointer); predefined_sizeof("PTRDIFF_T", bits_in_pointer); predefined_sizeof("POINTER", bits_in_pointer); predefined_sizeof("FLOAT", bits_in_float); predefined_sizeof("DOUBLE", bits_in_double); predefined_sizeof("LONG_DOUBLE", bits_in_longdouble); add_pre_buffer("#weak_define __%s_ENDIAN__ 1\n", arch_big_endian ? "BIG" : "LITTLE"); add_pre_buffer("#weak_define __ORDER_LITTLE_ENDIAN__ 1234\n"); add_pre_buffer("#weak_define __ORDER_BIG_ENDIAN__ 4321\n"); add_pre_buffer("#weak_define __ORDER_PDP_ENDIAN__ 3412\n"); add_pre_buffer("#weak_define __BYTE_ORDER__ __ORDER_%s_ENDIAN__\n", arch_big_endian ? "BIG" : "LITTLE"); } void declare_builtin_functions(void) { /* Gaah. gcc knows tons of builtin functions */ add_pre_buffer("extern void *__builtin_memchr(const void *, int, __SIZE_TYPE__);\n"); 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_memmove(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 int __builtin_strncmp(const char *, const char *, __SIZE_TYPE__);\n"); add_pre_buffer("extern int __builtin_strcasecmp(const char *, const char *);\n"); add_pre_buffer("extern int __builtin_strncasecmp(const char *, const char *, __SIZE_TYPE__);\n"); add_pre_buffer("extern char *__builtin_strchr(const char *, int);\n"); add_pre_buffer("extern char *__builtin_strrchr(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 char *__builtin_strdup(const char *);\n"); add_pre_buffer("extern char *__builtin_strndup(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 char* __builtin_stpcpy(const char *, const char*);\n"); add_pre_buffer("extern char* __builtin_stpncpy(const char *, const char*, __SIZE_TYPE__);\n"); add_pre_buffer("extern __SIZE_TYPE__ __builtin_strlen(const char *);\n"); add_pre_buffer("extern char *__builtin_strstr(const char *, const char *);\n"); add_pre_buffer("extern char *__builtin_strcasestr(const char *, const char *);\n"); add_pre_buffer("extern char *__builtin_strnstr(const char *, const char *, __SIZE_TYPE__);\n"); /* And even some from */ add_pre_buffer("extern int __builtin_bcmp(const void *, const void *, __SIZE_TYPE__);\n"); add_pre_buffer("extern void __builtin_bcopy(const void *, void *, __SIZE_TYPE__);\n"); add_pre_buffer("extern void __builtin_bzero(void *, __SIZE_TYPE__);\n"); add_pre_buffer("extern char*__builtin_index(const char *, int);\n"); add_pre_buffer("extern char*__builtin_rindex(const char *, int);\n"); /* And bitwise operations.. */ add_pre_buffer("extern int __builtin_clrsb(int);\n"); add_pre_buffer("extern int __builtin_clrsbl(long);\n"); add_pre_buffer("extern int __builtin_clrsbll(long long);\n"); 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_parity(unsigned int);\n"); add_pre_buffer("extern int __builtin_parityl(unsigned long);\n"); add_pre_buffer("extern int __builtin_parityll(unsigned 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 byte swaps.. */ add_pre_buffer("extern unsigned short __builtin_bswap16(unsigned short);\n"); add_pre_buffer("extern unsigned int __builtin_bswap32(unsigned int);\n"); add_pre_buffer("extern unsigned long long __builtin_bswap64(unsigned long long);\n"); /* And atomic memory access functions.. */ add_pre_buffer("extern int __sync_fetch_and_add(void *, ...);\n"); add_pre_buffer("extern int __sync_fetch_and_sub(void *, ...);\n"); add_pre_buffer("extern int __sync_fetch_and_or(void *, ...);\n"); add_pre_buffer("extern int __sync_fetch_and_and(void *, ...);\n"); add_pre_buffer("extern int __sync_fetch_and_xor(void *, ...);\n"); add_pre_buffer("extern int __sync_fetch_and_nand(void *, ...);\n"); add_pre_buffer("extern int __sync_add_and_fetch(void *, ...);\n"); add_pre_buffer("extern int __sync_sub_and_fetch(void *, ...);\n"); add_pre_buffer("extern int __sync_or_and_fetch(void *, ...);\n"); add_pre_buffer("extern int __sync_and_and_fetch(void *, ...);\n"); add_pre_buffer("extern int __sync_xor_and_fetch(void *, ...);\n"); add_pre_buffer("extern int __sync_nand_and_fetch(void *, ...);\n"); add_pre_buffer("extern int __sync_bool_compare_and_swap(void *, ...);\n"); add_pre_buffer("extern int __sync_val_compare_and_swap(void *, ...);\n"); add_pre_buffer("extern void __sync_synchronize();\n"); add_pre_buffer("extern int __sync_lock_test_and_set(void *, ...);\n"); add_pre_buffer("extern void __sync_lock_release(void *, ...);\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 int __builtin_abs(int);\n"); add_pre_buffer("extern long __builtin_labs(long);\n"); add_pre_buffer("extern long long __builtin_llabs(long long);\n"); add_pre_buffer("extern double __builtin_fabs(double);\n"); add_pre_buffer("extern __SIZE_TYPE__ __builtin_va_arg_pack_len(void);\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 INFINITY / NAN stuff.. */ add_pre_buffer("extern double __builtin_huge_val(void);\n"); add_pre_buffer("extern float __builtin_huge_valf(void);\n"); add_pre_buffer("extern long double __builtin_huge_vall(void);\n"); add_pre_buffer("extern double __builtin_inf(void);\n"); add_pre_buffer("extern float __builtin_inff(void);\n"); add_pre_buffer("extern long double __builtin_infl(void);\n"); add_pre_buffer("extern double __builtin_nan(const char *);\n"); add_pre_buffer("extern float __builtin_nanf(const char *);\n"); add_pre_buffer("extern long double __builtin_nanl(const char *);\n"); /* And some __FORTIFY_SOURCE ones.. */ add_pre_buffer ("extern __SIZE_TYPE__ __builtin_object_size(const 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"); /* And some from */ add_pre_buffer("extern void __builtin_abort(void);\n"); add_pre_buffer("extern void *__builtin_calloc(__SIZE_TYPE__, __SIZE_TYPE__);\n"); add_pre_buffer("extern void __builtin_exit(int);\n"); add_pre_buffer("extern void *__builtin_malloc(__SIZE_TYPE__);\n"); add_pre_buffer("extern void *__builtin_realloc(void *, __SIZE_TYPE__);\n"); add_pre_buffer("extern void __builtin_free(void *);\n"); /* And some from */ add_pre_buffer("extern int __builtin_printf(const char *, ...);\n"); add_pre_buffer("extern int __builtin_sprintf(char *, const char *, ...);\n"); add_pre_buffer("extern int __builtin_snprintf(char *, __SIZE_TYPE__, const char *, ...);\n"); add_pre_buffer("extern int __builtin_puts(const char *);\n"); add_pre_buffer("extern int __builtin_vprintf(const char *, __builtin_va_list);\n"); add_pre_buffer("extern int __builtin_vsprintf(char *, const char *, __builtin_va_list);\n"); add_pre_buffer("extern int __builtin_vsnprintf(char *, __SIZE_TYPE__, const char *, __builtin_va_list ap);\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); /* add the multiarch include directories, if any */ if (multiarch_dir && *multiarch_dir) { add_pre_buffer("#add_system \"/usr/include/%s\"\n", multiarch_dir); add_pre_buffer("#add_system \"/usr/local/include/%s\"\n", multiarch_dir); } /* 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"); add_pre_buffer("#define _Pragma(x)\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; case STANDARD_C11: add_pre_buffer("#weak_define __STRICT_ANSI__ 1\n"); case STANDARD_GNU11: add_pre_buffer("#weak_define __STDC_NO_ATOMICS__ 1\n"); add_pre_buffer("#weak_define __STDC_NO_COMPLEX__ 1\n"); add_pre_buffer("#weak_define __STDC_NO_THREADS__ 1\n"); add_pre_buffer("#weak_define __STDC_VERSION__ 201112L\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_ms_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"); add_pre_buffer("#define __builtin_va_arg_pack()\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"); } static struct symbol_list *sparse_tokenstream(struct token *token) { int builtin = token && !token->pos.stream; // Preprocess the stream token = preprocess(token); if (dump_macro_defs && !builtin) dump_macro_definitions(); 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, NULL); 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) { int i; // Prepend any "include" file to the stream. // We're in global scope, it will affect all files! for (i = 0; i < cmdline_include_nr; i++) add_pre_buffer("#argv_include \"%s\"\n", cmdline_include[i]); 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(); handle_arch_finalize(); list = NULL; if (!ptr_list_empty(filelist)) { // Initialize type system init_ctype(); create_builtin_stream(); predefined_macros(); 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); if (has_error & ERROR_CURR_PHASE) has_error = ERROR_PREV_PHASE; /* Evaluate the complete symbol list */ evaluate_symbol_list(res); return res; } sparse-0.5.1/lib.h000066400000000000000000000174231314543357600137530ustar00rootroot00000000000000#ifndef LIB_H #define LIB_H #include #include /* * Basic helper routine descriptions for 'sparse'. * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * 2004 Christopher Li * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #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 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(ident_list, struct ident); 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 FORMAT_ATTR(1) NORETURN_ATTR extern void die(const char *, ...); FORMAT_ATTR(2) NORETURN_ATTR extern void error_die(struct position, const char *, ...); 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 expression_error(struct expression *, const char *, ...) FORMAT_ATTR(2); #define ERROR_CURR_PHASE (1 << 0) #define ERROR_PREV_PHASE (1 << 1) extern int has_error; extern void add_pre_buffer(const char *fmt, ...) FORMAT_ATTR(1); extern int preprocess_only; extern int Waddress; extern int Waddress_space; extern int Wbitwise; extern int Wcast_to_as; extern int Wcast_truncate; extern int Wcontext; extern int Wdecl; extern int Wdeclarationafterstatement; extern int Wdefault_bitfield_sign; extern int Wdesignated_init; extern int Wdo_while; extern int Wenum_mismatch; extern int Wsparse_error; extern int Winit_cstring; extern int Wmemcpy_max_count; extern int Wnon_pointer_null; extern int Wold_initializer; extern int Wone_bit_signed_bitfield; extern int Woverride_init; extern int Woverride_init_all; extern int Woverride_init_whole_range; extern int Wparen_string; extern int Wptr_subtraction_blows; extern int Wreturn_void; extern int Wshadow; extern int Wsizeof_bool; extern int Wtautological_compare; extern int Wtransparent_union; extern int Wtypesign; extern int Wundef; extern int Wuninitialized; extern int Wunknown_attribute; extern int Wvla; extern int dump_macro_defs; extern int dbg_entry; extern int dbg_dead; extern int fmem_report; extern int fdump_linearize; extern unsigned long long fmemcpy_max_count; extern int arch_m64; extern int arch_msize_long; extern int arch_big_endian; extern void declare_builtin_functions(void); extern void create_builtin_stream(void); extern void dump_macro_definitions(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); extern void report_stats(void); 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 struct expression *first_expression(struct expression_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); } static inline void add_ident(struct ident_list **list, struct ident *ident) { add_ptr_list(list, ident); } #define hashval(x) ((unsigned long)(x)) #endif sparse-0.5.1/linearize.c000066400000000000000000001627411314543357600151660ustar00rootroot00000000000000/* * 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 #include #include #include #include #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 *); static pseudo_t cast_pseudo(struct entrypoint *ep, pseudo_t src, struct symbol *from, struct symbol *to); 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) { static int nr; struct basic_block *bb = __alloc_basic_block(0); bb->context = -1; bb->pos = pos; bb->ep = ep; bb->nr = nr++; 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%u", sym->bb_target->nr); break; } if (sym->ident) { snprintf(buf, 64, "%s", show_ident(sym->ident)); break; } expr = sym->initializer; snprintf(buf, 64, "", sym); if (expr) { switch (expr->type) { case EXPR_VALUE: snprintf(buf, 64, "", 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, "", pseudo->type); } return buf; } static const char *opcodes[] = { [OP_BADOP] = "bad_op", /* Fn entrypoint */ [OP_ENTRY] = "", /* Terminator */ [OP_RET] = "ret", [OP_BR] = "br", [OP_CBR] = "cbr", [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_CBR: buf += sprintf(buf, "%s, .L%u, .L%u", show_pseudo(insn->cond), insn->bb_true->nr, insn->bb_false->nr); break; case OP_BR: buf += sprintf(buf, ".L%u", insn->bb_true->nr); break; case OP_SYMADDR: { struct symbol *sym = insn->symbol->sym; buf += sprintf(buf, "%s <- ", show_pseudo(insn->target)); if (!insn->bb && !sym) break; if (sym->bb_target) { buf += sprintf(buf, ".L%u", sym->bb_target->nr); break; } if (sym->ident) { buf += sprintf(buf, "%s", show_ident(sym->ident)); break; } buf += sprintf(buf, "", sym); break; } case OP_SETVAL: { struct expression *expr = insn->val; struct symbol *sym; buf += sprintf(buf, "%s <- ", show_pseudo(insn->target)); if (!expr) { buf += sprintf(buf, "%s", ""); 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: sym = expr->symbol; if (sym->bb_target) buf += sprintf(buf, ".L%u", sym->bb_target->nr); 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->cond)); FOR_EACH_PTR(insn->multijmp_list, jmp) { if (jmp->begin == jmp->end) buf += sprintf(buf, ", %d -> .L%u", jmp->begin, jmp->target->nr); else if (jmp->begin < jmp->end) buf += sprintf(buf, ", %d ... %d -> .L%u", jmp->begin, jmp->end, jmp->target->nr); else buf += sprintf(buf, ", default -> .L%u", jmp->target->nr); } 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%u", jmp->target->nr); } 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%u:\n", bb->nr); 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%u)**\n", show_pseudo(needs), def->bb->nr); } 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%u)", sep, show_pseudo(phi), phi->def->bb->nr); 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 .L%u (%s:%d:%d)**\n", from->nr, 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 .L%u (%s:%d:%d)**\n", to->nr, 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) repeat_phase |= REPEAT_CFG_CLEANUP; } /* Change a "switch" or a conditional branch 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); kill_instruction(old); 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_CBR, 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; pseudo_t phi; static int nr = 0; if (!source) return VOID; insn = alloc_instruction(OP_PHISOURCE, size); phi = __alloc_pseudo(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 .. unsigned int offset; // byte offset 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); 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; insn = alloc_typed_instruction(OP_LOAD, ad->source_type); new = alloc_pseudo(insn); 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)) { struct symbol *ctype = ad->result_type; unsigned int shift = ctype->bit_offset; unsigned int size = ctype->bit_size; pseudo_t orig = add_load(ep, ad); unsigned long long mask = (1ULL << 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) { struct symbol *ctype = ad->result_type; pseudo_t new = add_load(ep, ad); if (ctype->bit_offset) { pseudo_t shift = value_pseudo(ctype->bit_offset); pseudo_t newval = add_binary_op(ep, ad->source_type, OP_LSR, new, shift); new = newval; } if (ctype->bit_size != type_size(ad->source_type)) new = cast_pseudo(ep, new, ad->source_type, ad->result_type); 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->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 = ctype; if (src->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; } else 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 inline pseudo_t add_convert_to_bool(struct entrypoint *ep, pseudo_t src, struct symbol *type) { pseudo_t zero; int op; if (is_bool_type(type)) return src; zero = value_pseudo(0); op = OP_SET_NE; return add_binary_op(ep, &bool_ctype, op, src, zero); } static pseudo_t linearize_expression_to_bool(struct entrypoint *ep, struct expression *expr) { pseudo_t dst; dst = linearize_expression(ep, expr); dst = add_convert_to_bool(ep, dst, expr->ctype); return dst; } 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; struct symbol *ctype; 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; ctype = src->ctype; oldvalue = cast_pseudo(ep, oldvalue, target->ctype, ctype); opcode = opcode_sign(op_trans[expr->op - SPECIAL_BASE], ctype); dst = add_binary_op(ep, ctype, opcode, oldvalue, value); value = cast_pseudo(ep, dst, ctype, expr->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 symbol *fntype; 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; fntype = fn->ctype; if (fntype) { if (fntype->type == SYM_NODE) fntype = fntype->ctype.base_type; } insn->fntype = fntype; 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_bool(struct entrypoint *ep, struct expression *expr) { pseudo_t src1, src2, dst; int op = (expr->op == SPECIAL_LOGICAL_OR) ? OP_OR_BOOL : OP_AND_BOOL; src1 = linearize_expression_to_bool(ep, expr->left); src2 = linearize_expression_to_bool(ep, expr->right); dst = add_binary_op(ep, &bool_ctype, op, src1, src2); if (expr->ctype != &bool_ctype) dst = cast_pseudo(ep, dst, &bool_ctype, expr->ctype); return dst; } 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, }; 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->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: if (expr->op == SPECIAL_LOGICAL_AND || expr->op == SPECIAL_LOGICAL_OR) return linearize_binop_bool(ep, expr); 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); if (sym->initializer && !is_scalar_type(sym)) { // default zero initialization [6.7.9.21] // FIXME: this init the whole aggregate while // only the existing fields need to be initialized. // FIXME: this init the whole aggregate even if // all fields arelater explicitely initialized. struct expression *expr = sym->initializer; ad.pos = expr->pos; ad.result_type = sym; ad.source_type = base_type(sym); ad.address = symbol_pseudo(ep, sym); linearize_store_gen(ep, value_pseudo(0), &ad); } 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) { 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; struct symbol *sym; FOR_EACH_PTR(stmt->iterator_syms, sym) { linearize_one_symbol(ep, sym); } END_FOR_EACH_PTR(sym); 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); } return linearize_statement(ep, stmt->label_statement); } 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); } if (fdump_linearize) { if (fdump_linearize == 2) return ep; show_entry(ep); } /* * 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; } sparse-0.5.1/linearize.h000066400000000000000000000155451314543357600151720ustar00rootroot00000000000000#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; }; void *priv; }; 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 /* setval */ { pseudo_t symbol; /* Subtle: same offset as "src" !! */ struct expression *val; }; struct /* call */ { pseudo_t func; struct pseudo_list *arguments; struct symbol *fntype; }; 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_CBR, 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; union { unsigned int nr; /* unique id for label's names */ void *priv; }; }; 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 */ sparse-0.5.1/liveness.c000066400000000000000000000170631314543357600150300ustar00rootroot00000000000000/* * Register - track pseudo usage, maybe eventually try to do register * allocation. * * Copyright (C) 2004 Linus Torvalds */ #include #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 *, 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; defines(def->bb, target); } END_FOR_EACH_PTR(phi); } static void asm_liveness(struct basic_block *bb, struct instruction *insn, void (*def)(struct basic_block *, pseudo_t), void (*use)(struct basic_block *, pseudo_t)) { struct asm_constraint *entry; FOR_EACH_PTR(insn->asm_rules->inputs, entry) { use(bb, entry->pseudo); } END_FOR_EACH_PTR(entry); FOR_EACH_PTR(insn->asm_rules->outputs, entry) { def(bb, entry->pseudo); } END_FOR_EACH_PTR(entry); } static void track_instruction_usage(struct basic_block *bb, struct instruction *insn, void (*def)(struct basic_block *, pseudo_t), void (*use)(struct basic_block *, pseudo_t)) { pseudo_t pseudo; #define USES(x) use(bb, insn->x) #define DEFINES(x) def(bb, insn->x) switch (insn->opcode) { case OP_RET: USES(src); break; case OP_CBR: 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, 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, 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, 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, pseudo_t pseudo) { } static void death_use(struct basic_block *bb, 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); } sparse-0.5.1/memops.c000066400000000000000000000110671314543357600144760ustar00rootroot00000000000000/* * memops - try to combine memory ops. * * Copyright (C) 2004 Linus Torvalds */ #include #include #include #include #include #include #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) { struct basic_block *parent; 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->bb) continue; 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; 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)) 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); if (insn->type->ctype.modifiers & MOD_VOLATILE) continue; 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)) { /* 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); } sparse-0.5.1/obfuscate.c000066400000000000000000000042571314543357600151540ustar00rootroot00000000000000/* * 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 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #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; } sparse-0.5.1/parse.c000066400000000000000000002370141314543357600143120ustar00rootroot00000000000000/* * 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 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include #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); static struct token *parse_static_assert(struct token *token, struct symbol_list **unused); typedef struct token *attr_t(struct token *, struct symbol *, struct decl_state *); static attr_t attribute_packed, attribute_aligned, attribute_modifier, attribute_bitwise, 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_Int128 = 2048, 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, SMax, }; 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 declarator_t noreturn_specifier; static struct symbol_op noreturn_op = { .type = KW_MODIFIER, .declarator = noreturn_specifier, }; static declarator_t alignas_specifier; static struct symbol_op alignas_op = { .type = KW_MODIFIER, .declarator = alignas_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 int128_op = { .type = KW_SPECIFIER | KW_LONG, .test = Set_S|Set_T|Set_Char|Set_Short|Set_Int|Set_Float|Set_Double|Set_Long|Set_Vlong|Set_Int128, .set = Set_T|Set_Int128, }; 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 static_assert_op = { .toplevel = parse_static_assert, }; 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_bitwise_op = { .attribute = attribute_bitwise, }; 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 }; /* Using NS_TYPEDEF will also make the keyword a reserved one */ 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 }, { "__int128", NS_TYPEDEF, .op = &int128_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 }, { "_Noreturn", NS_TYPEDEF, .op = &noreturn_op }, { "_Alignas", NS_TYPEDEF, .op = &alignas_op }, /* Ignored for now.. */ { "restrict", NS_TYPEDEF, .op = &restrict_op}, { "__restrict", NS_TYPEDEF, .op = &restrict_op}, { "__restrict__", NS_TYPEDEF, .op = &restrict_op}, /* Static assertion */ { "_Static_assert", NS_KEYWORD, .op = &static_assert_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 }, { "_Thread_local", 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_bitwise_op }, { "__bitwise__",NS_KEYWORD, MOD_BITWISE, .op = &attr_bitwise_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 }, }; static const char *ignored_attributes[] = { #define GCC_ATTR(x) \ STRINGIFY(x), \ STRINGIFY(__##x##__), #include "gcc-attr-list.h" #undef GCC_ATTR "bounded", "__bounded__", "__noclone", "__nonnull", "__nothrow", }; 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); if (!sym->op) { 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_bitwise(struct token *token, struct symbol *attr, struct decl_state *ctx) { if (Wbitwise) attribute_modifier(token, attr, ctx); 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, "attribute __transparent_union__"); if (ctx->ctype.base_type && ctx->ctype.base_type->type == SYM_UNION) ctx->ctype.base_type->transparent_union = 1; else warning(token->pos, "attribute __transparent_union__ applied to non-union type"); return token; } static struct token *recover_unknown_attribute(struct token *token) { struct expression *expr = NULL; if (Wunknown_attribute) warning(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[SMax] = { [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 *noreturn_specifier(struct token *next, struct decl_state *ctx) { apply_qualifier(&next->pos, &ctx->ctype, MOD_NORETURN); return next; } static struct token *alignas_specifier(struct token *token, struct decl_state *ctx) { int alignment = 0; if (!match_op(token, '(')) { sparse_error(token->pos, "expected '(' after _Alignas"); return token; } if (lookup_type(token->next)) { struct symbol *sym = NULL; token = typename(token->next, &sym, NULL); sym = examine_symbol_type(sym); alignment = sym->ctype.alignment; token = expect(token, ')', "after _Alignas(..."); } else { struct expression *expr = NULL; token = parens_expression(token, &expr, "after _Alignas"); if (!expr) return token; alignment = const_expression_value(expr); } if (alignment < 0) { warning(token->pos, "non-positive alignment"); return token; } if (alignment & (alignment-1)) { warning(token->pos, "non-power-of-2 alignment"); return token; } if (alignment > ctx->ctype.alignment) ctx->ctype.alignment = alignment; return token; } 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, &lllong_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->set & Set_Int128) size = 2; 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_static_declarator(struct token *token, int *has_static) { while (token->ident == &static_ident) { if (*has_static) sparse_error(token->pos, "duplicate array static declarator"); *has_static = 1; token = token->next; } return token; } static struct token *abstract_array_declarator(struct token *token, struct symbol *sym) { struct expression *expr = NULL; int has_static = 0; token = abstract_array_static_declarator(token, &has_static); if (match_idents(token, &restrict_ident, &__restrict_ident, &__restrict___ident, NULL)) token = abstract_array_static_declarator(token->next, &has_static); 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_ident(token, &_Static_assert_ident)) { token = parse_static_assert(token, NULL); continue; } 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; sym->forced_arg = ctx.storage_class == SForced; 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; } static struct token *parse_static_assert(struct token *token, struct symbol_list **unused) { struct expression *cond = NULL, *message = NULL; token = expect(token->next, '(', "after _Static_assert"); token = constant_expression(token, &cond); if (!cond) sparse_error(token->pos, "Expected constant expression"); token = expect(token, ',', "after conditional expression in _Static_assert"); token = parse_expression(token, &message); if (!message || message->type != EXPR_STRING) { struct position pos; pos = message ? message->pos : token->pos; sparse_error(pos, "bad or missing string literal"); cond = NULL; } token = expect(token, ')', "after diagnostic message in _Static_assert"); token = expect(token, ';', "after _Static_assert()"); if (cond && !const_expression_value(cond) && cond->type == EXPR_VALUE) sparse_error(cond->pos, "static assertion failed: %s", show_string(message->string)); 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 void validate_for_loop_decl(struct symbol *sym) { unsigned long storage = sym->ctype.modifiers & MOD_STORAGE; if (storage & ~(MOD_AUTO | MOD_REGISTER)) { const char *name = show_ident(sym->ident); sparse_error(sym->pos, "non-local var '%s' in for-loop initializer", name); sym->ctype.modifiers &= ~MOD_STORAGE; } } 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, validate_for_loop_decl); } 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, ':')) { struct symbol *s = label_symbol(token); stmt->type = STMT_LABEL; stmt->label_identifier = s; if (s->stmt) sparse_error(stmt->pos, "label '%s' redefined", show_ident(token->ident)); s->stmt = stmt; 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 (match_ident(token, &_Static_assert_ident)) { token = parse_static_assert(token, NULL); continue; } 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, NULL); } 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) { rebind_scope(prev, decl->scope); 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, validate_decl_t validate_decl) { 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 or static assertion? */ 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; break; default: break; } } } else if (base_type && base_type->type == SYM_FN) { if (base_type->ctype.base_type == &incomplete_ctype) { warning(decl->pos, "'%s()' has implicit return type", show_ident(decl->ident)); base_type->ctype.base_type = &int_ctype; } /* 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"); } if (base_type == &incomplete_ctype) { warning(decl->pos, "'%s' has implicit type", show_ident(decl->ident)); decl->ctype.base_type = &int_ctype;; } for (;;) { if (!is_typedef && match_op(token, '=')) { token = initializer(&decl->initializer, token->next); } if (!is_typedef) { if (validate_decl) validate_decl(decl); if (decl->initializer && decl->ctype.modifiers & MOD_EXTERN) { warning(decl->pos, "symbol with external linkage has initializer"); decl->ctype.modifiers &= ~MOD_EXTERN; } 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; decl->op = decl->same_symbol->op; } 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"); } sparse-0.5.1/parse.dtd000066400000000000000000000031471314543357600146410ustar00rootroot00000000000000 sparse-0.5.1/parse.h000066400000000000000000000104101314543357600143040ustar00rootroot00000000000000#ifndef PARSE_H #define PARSE_H /* * Basic parsing data structures. Statements and symbols. * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #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 /* context */ { 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 /* 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 *); typedef void (*validate_decl_t)(struct symbol *decl); extern struct token *external_declaration(struct token *, struct symbol_list **, validate_decl_t); 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 */ sparse-0.5.1/pre-process.c000066400000000000000000001416751314543357600154510ustar00rootroot00000000000000/* * 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 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include "lib.h" #include "allocate.h" #include "parse.h" #include "token.h" #include "symbol.h" #include "expression.h" #include "scope.h" static struct ident_list *macros; // only needed for -dD static int false_nesting = 0; static int counter_macro = 0; // __COUNTER__ expansion #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; } /* 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); } else if (token->ident == &__COUNTER___ident) { replace_with_integer(token, counter_macro++); } 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, int count) { 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; case TOKEN_STRING: case TOKEN_WIDE_STRING: if (count > 1) next->string->immutable = 1; break; } 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 [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, 0); 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, p->normal); 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, 0); 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 const char *show_token_sequence(struct token *token, int quote) { static char buffer[MAX_STRING]; char *ptr = buffer; int whitespace = 0; if (!token && !quote) return ""; while (!eof_token(token)) { const char *val = quote ? quote_token(token) : 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 struct token *stringify(struct token *arg) { const char *s = show_token_sequence(arg, 1); 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 '-'. * - 'L' + char constant -> wide char constant * - 'L' + string literal -> wide string literal * - 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 (t1 == TOKEN_IDENT && left->ident == &L_ident) { if (t2 >= TOKEN_CHAR && t2 < TOKEN_WIDE_CHAR) return t2 + TOKEN_WIDE_CHAR - TOKEN_CHAR; if (t2 == TOKEN_STRING) return TOKEN_WIDE_STRING; } 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]; enum token_type res = combine(left, right, buffer); int n; switch (res) { 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; } } break; case TOKEN_WIDE_CHAR: case TOKEN_WIDE_STRING: token_type(left) = res; left->pos.noexpand = 0; left->string = right->string; return 1; case TOKEN_WIDE_CHAR_EMBEDDED_0 ... TOKEN_WIDE_CHAR_EMBEDDED_3: token_type(left) = res; left->pos.noexpand = 0; memcpy(left->embedded, right->embedded, 4); 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 token *alloc = alloc_token(streampos); token_type(alloc) = token_type(token); alloc->pos.newline = token->pos.newline; alloc->pos.whitespace = token->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); 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 int handle_kludge(struct token **p, struct arg *args) { struct token *t = (*p)->next->next; while (1) { struct arg *v = &args[t->argnum]; if (token_type(t->next) != TOKEN_CONCAT) { if (v->arg) { /* ignore the first ## */ *p = (*p)->next; return 0; } /* skip the entire thing */ *p = t; return 1; } if (v->arg && !eof_token(v->arg)) return 0; /* no magic */ t = t->next->next; } } static struct token **substitute(struct token **list, struct token *body, struct arg *args) { struct position *base_pos = &(*list)->pos; int *count; enum {Normal, Placeholder, Concat} state = Normal; for (; !eof_token(body); body = body->next) { struct token *added, *arg; struct token **tail; struct token *t; switch (token_type(body)) { case TOKEN_GNU_KLUDGE: /* * GNU kludge: if we had ##, behaviour * depends on whether we had enough arguments to have * a vararg. If we did, ## is just ignored. Otherwise * both , and ## are ignored. Worse, there can be * an arbitrary number of ## in between; if all of * those are empty, we act as if they hadn't been there, * otherwise we act as if the kludge didn't exist. */ t = body; if (handle_kludge(&body, args)) { if (state == Concat) state = Normal; else state = Placeholder; continue; } added = dup_token(t, base_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 = body->pos.newline; added->pos.whitespace = body->pos.whitespace; break; case TOKEN_CONCAT: if (state == Placeholder) state = Normal; else state = Concat; continue; case TOKEN_IDENT: added = dup_token(body, base_pos); if (added->ident->tainted) added->pos.noexpand = 1; tail = &added->next; break; default: added = dup_token(body, base_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); /* * Note that it won't be eof - at least TOKEN_UNTAINT will be there. * We still can lose the newline flag if the sucker expands to nothing, * but the price of dealing with that is probably too high (we'd need * to collect the flags during scan_next()) */ (*list)->pos.newline = token->pos.newline; (*list)->pos.whitespace = token->pos.whitespace; *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->once) { if (strcmp(path, s->name)) continue; return 1; } 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 * 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 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, int how) { const char *filename; struct token *next; const char **path; int expect; int flen; 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); flen = strlen(filename) + 1; /* Absolute path? */ if (filename[0] == '/') { if (try_include("", filename, flen, list, includepath)) return 0; goto out; } switch (how) { case 1: path = stream->next_path; break; case 2: includepath[0] = ""; path = includepath; break; default: /* Dir of input file is first dir to search for quoted includes */ set_stream_include_path(stream); path = expect ? angle_includepath : quote_includepath; break; } /* Check the standard include paths.. */ if (do_include_path(path, list, token, filename, flen)) return 0; out: error_die(token->pos, "unable to open '%s'", filename); } static int handle_include(struct stream *stream, struct token **list, struct token *token) { return handle_include_path(stream, list, token, 0); } static int handle_include_next(struct stream *stream, struct token **list, struct token *token) { return handle_include_path(stream, list, token, 1); } static int handle_argv_include(struct stream *stream, struct token **list, struct token *token) { return handle_include_path(stream, list, token, 2); } 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_EMBEDDED_0 ... TOKEN_CHAR_EMBEDDED_3: case TOKEN_WIDE_CHAR_EMBEDDED_0 ... TOKEN_WIDE_CHAR_EMBEDDED_3: different = memcmp(t1->embedded, t2->embedded, 4); break; case TOKEN_CHAR: case TOKEN_WIDE_CHAR: 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; /* * XXX - need saner handling of that * (>= 1024 instances of argument) */ token_type(token) = TOKEN_ERROR; return -1; } } return 0; } static struct token *handle_hash(struct token **p, struct token *arglist) { struct token *token = *p; if (arglist) { struct token *next = token->next; if (!try_arg(next, TOKEN_STR_ARGUMENT, arglist)) goto Equote; next->pos.whitespace = token->pos.whitespace; __free_token(token); token = *p = next; } else { token->pos.noexpand = 1; } return token; Equote: sparse_error(token->pos, "'#' is not followed by a macro parameter"); return NULL; } /* token->next is ## */ static struct token *handle_hashhash(struct token *token, struct token *arglist) { struct token *last = token; struct token *concat; int state = match_op(token, ','); try_arg(token, TOKEN_QUOTED_ARGUMENT, arglist); while (1) { struct token *t; int is_arg; /* eat duplicate ## */ concat = token->next; while (match_op(t = concat->next, SPECIAL_HASHHASH)) { token->next = t; __free_token(concat); concat = t; } token_type(concat) = TOKEN_CONCAT; if (eof_token(t)) goto Econcat; if (match_op(t, '#')) { t = handle_hash(&concat->next, arglist); if (!t) return NULL; } is_arg = try_arg(t, TOKEN_QUOTED_ARGUMENT, arglist); if (state == 1 && is_arg) { state = is_arg; } else { last = t; state = match_op(t, ','); } token = t; if (!match_op(token->next, SPECIAL_HASHHASH)) break; } /* handle GNU ,##__VA_ARGS__ kludge, in all its weirdness */ if (state == 2) token_type(last) = TOKEN_GNU_KLUDGE; return token; Econcat: sparse_error(concat->pos, "'##' cannot appear at the ends of macro expansion"); return NULL; } static struct token *parse_expansion(struct token *expansion, struct token *arglist, struct ident *name) { struct token *token = expansion; struct token **p; if (match_op(token, SPECIAL_HASHHASH)) goto Econcat; for (p = &expansion; !eof_token(token); p = &token->next, token = *p) { if (match_op(token, '#')) { token = handle_hash(p, arglist); if (!token) return NULL; } if (match_op(token->next, SPECIAL_HASHHASH)) { token = handle_hashhash(token, arglist); if (!token) return NULL; } else { try_arg(token, TOKEN_MACRO_ARGUMENT, arglist); } switch (token_type(token)) { case TOKEN_ERROR: goto Earg; case TOKEN_STRING: case TOKEN_WIDE_STRING: token->string->immutable = 1; break; } } token = alloc_token(&expansion->pos); token_type(token) = TOKEN_UNTAINT; token->ident = name; token->next = *p; *p = token; return expansion; 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); add_ident(¯os, name); 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); } static const char *show_token_sequence(struct token *token, int quote); /* * 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, 0)); 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 int handle_warning(struct stream *stream, struct token **line, struct token *token) { warning(token->pos, "%s", show_token_sequence(token->next, 0)); 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, 0)); 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 '. * 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; if (match_ident(token->next, &once_ident) && eof_token(token->next->next)) { stream->once = 1; return 1; } 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, 0)); 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 }, { "argv_include", handle_argv_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; } counter_macro = 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; } static void dump_macro(struct symbol *sym) { int nargs = sym->arglist ? sym->arglist->count.normal : 0; struct token *args[nargs]; struct token *token; printf("#define %s", show_ident(sym->ident)); token = sym->arglist; if (token) { const char *sep = ""; int narg = 0; putchar('('); for (; !eof_token(token); token = token->next) { if (token_type(token) == TOKEN_ARG_COUNT) continue; printf("%s%s", sep, show_token(token)); args[narg++] = token; sep = ", "; } putchar(')'); } putchar(' '); token = sym->expansion; while (!eof_token(token)) { struct token *next = token->next; switch (token_type(token)) { case TOKEN_UNTAINT: break; case TOKEN_MACRO_ARGUMENT: token = args[token->argnum]; /* fall-through */ default: printf("%s", show_token(token)); if (next->pos.whitespace) putchar(' '); } token = next; } putchar('\n'); } void dump_macro_definitions(void) { struct ident *name; FOR_EACH_PTR(macros, name) { struct symbol *sym = lookup_macro(name); if (sym) dump_macro(sym); } END_FOR_EACH_PTR(name); } sparse-0.5.1/ptrlist.c000066400000000000000000000114661314543357600147020ustar00rootroot00000000000000/* * ptrlist.c * * Pointer list manipulation * * (C) Copyright Linus Torvalds 2003-2005 */ #include #include #include #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 - list->rm; } 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; } sparse-0.5.1/ptrlist.h000066400000000000000000000226131314543357600147030ustar00rootroot00000000000000#ifndef PTR_LIST_H #define PTR_LIST_H #include /* * 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:8; int rm:8; 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) { struct ptr_list *head = list; if (!list) return NULL; while (list->nr == 0) { list = list->next; if (list == head) return NULL; } return PTR_ENTRY(list, 0); } static inline void *last_ptr_list(struct ptr_list *list) { struct ptr_list *head = list; if (!list) return NULL; list = list->prev; while (list->nr == 0) { if (list == head) return NULL; list = list->prev; } return PTR_ENTRY(list, list->nr-1); } #define PTR_DEREF(__head, idx, PTR_ENTRY) ({ \ struct ptr_list *__list = __head; \ while (__list && __list->nr == 0) { \ __list = __list->next; \ if (__list == __head) \ __list = NULL; \ } \ __list ? PTR_ENTRY(__list, idx) : NULL; \ }) #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); \ ptr = PTR_DEREF(__head, 0, PTR_ENTRY); \ #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; \ while (__list->nr == 0 && __list != __head) \ __list = __list->next; \ 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_DEREF(__head, 0, PTR_ENTRY); \ } 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); \ if (__list->rm && !ptr) \ continue; \ 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); \ if (__list->rm && !ptr) \ continue; \ 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) #define DO_MARK_CURRENT_DELETED(ptr, __list) do { \ REPLACE_CURRENT_PTR(ptr, NULL); \ __list->rm++; \ } while (0) #define MARK_CURRENT_DELETED(ptr) \ DO_MARK_CURRENT_DELETED(ptr, __list##ptr) 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 */ sparse-0.5.1/scope.c000066400000000000000000000067261314543357600143150ustar00rootroot00000000000000/* * Symbol scoping. * * This is pretty trivial. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #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); } void rebind_scope(struct symbol *sym, struct scope *new) { struct scope *old = sym->scope; if (old == new) return; if (old) delete_ptr_list_entry((struct ptr_list**) &old->symbols, sym, 1); bind_scope(sym, new); } 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; } sparse-0.5.1/scope.h000066400000000000000000000037511314543357600143150ustar00rootroot00000000000000#ifndef SCOPE_H #define SCOPE_H /* * Symbol scoping is pretty simple. * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ 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 void rebind_scope(struct symbol *, struct scope *); extern int is_outer_scope(struct scope *); #endif sparse-0.5.1/show-parse.c000066400000000000000000000666001314543357600152710ustar00rootroot00000000000000/* * sparse/show-parse.c * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * Print out results of parsing for debugging and testing. */ #include #include #include #include #include #include #include #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, "", 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); } 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); } 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); } 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; } sparse-0.5.1/simplify.c000066400000000000000000000636601314543357600150400ustar00rootroot00000000000000/* * Simplify - do instruction simplification before CSE * * Copyright (C) 2004 Linus Torvalds */ #include #include "parse.h" #include "expression.h" #include "linearize.h" #include "flow.h" #include "symbol.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); } /* * Copy the phi-node's phisrcs into to given array. * Returns 0 if the the list contained the expected * number of element, a positive number if there was * more than expected and a negative one if less. * * Note: we can't reuse a function like linearize_ptr_list() * because any VOIDs in the phi-list must be ignored here * as in this context they mean 'entry has been removed'. */ static int get_phisources(struct instruction *sources[], int nbr, struct instruction *insn) { pseudo_t phi; int i = 0; assert(insn->opcode == OP_PHI); FOR_EACH_PTR(insn->phi_list, phi) { struct instruction *def; if (phi == VOID) continue; if (i >= nbr) return 1; def = phi->def; assert(def->opcode == OP_PHISOURCE); sources[i++] = def; } END_FOR_EACH_PTR(phi); return i - nbr; } static int if_convert_phi(struct instruction *insn) { struct instruction *array[2]; struct basic_block *parents[3]; struct basic_block *bb, *bb1, *bb2, *source; struct instruction *br; pseudo_t p1, p2; bb = insn->bb; if (get_phisources(array, 2, insn)) return 0; if (linearize_ptr_list((struct ptr_list *)bb->parents, (void **)parents, 3) != 2) return 0; p1 = array[0]->src1; bb1 = array[0]->bb; p2 = array[1]->src1; bb2 = array[1]->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_CBR) 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); kill_instruction(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); kill_instruction(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) { MARK_CURRENT_DELETED(pu); if (!--count) goto out; } } END_FOR_EACH_PTR(pu); assert(count <= 0); out: if (ptr_list_size((struct ptr_list *) *list) == 0) *list = NULL; 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); } } static void kill_use_list(struct pseudo_list *list) { pseudo_t p; FOR_EACH_PTR(list, p) { if (p == VOID) continue; kill_use(THIS_ADDRESS(p)); } END_FOR_EACH_PTR(p); } /* * kill an instruction: * - remove it from its bb * - remove the usage of all its operands * If forse is zero, the normal case, the function only for * instructions free of (possible) side-effects. Otherwise * the function does that unconditionally (must only be used * for unreachable instructions. */ void kill_insn(struct instruction *insn, int force) { if (!insn || !insn->bb) return; switch (insn->opcode) { case OP_SEL: case OP_RANGE: kill_use(&insn->src3); /* fall through */ case OP_BINARY ... OP_BINCMP_END: kill_use(&insn->src2); /* fall through */ case OP_CAST: case OP_SCAST: case OP_FPCAST: case OP_PTRCAST: case OP_SETVAL: case OP_NOT: case OP_NEG: case OP_SLICE: kill_use(&insn->src1); break; case OP_PHI: kill_use_list(insn->phi_list); break; case OP_PHISOURCE: kill_use(&insn->phi_src); break; case OP_SYMADDR: repeat_phase |= REPEAT_SYMBOL_CLEANUP; break; case OP_CBR: case OP_COMPUTEDGOTO: kill_use(&insn->cond); break; case OP_CALL: if (!force) { /* a "pure" function can be killed too */ if (!(insn->func->type == PSEUDO_SYM)) return; if (!(insn->func->sym->ctype.modifiers & MOD_PURE)) return; } kill_use_list(insn->arguments); if (insn->func->type == PSEUDO_REG) kill_use(&insn->func); break; case OP_LOAD: if (!force && insn->type->ctype.modifiers & MOD_VOLATILE) return; kill_use(&insn->src); break; case OP_STORE: if (!force) return; kill_use(&insn->src); kill_use(&insn->target); break; case OP_ENTRY: /* ignore */ return; case OP_BR: default: break; } insn->bb = NULL; repeat_phase |= REPEAT_CSE; 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); switch (insn->opcode) { case OP_SEL: case OP_RANGE: kill_use(&insn->src3); case OP_BINARY ... OP_BINCMP_END: kill_use(&insn->src2); case OP_NOT: case OP_NEG: case OP_SYMADDR: case OP_CAST: case OP_SCAST: case OP_FPCAST: case OP_PTRCAST: kill_use(&insn->src1); break; default: assert(0); } 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_mul_div(struct instruction *insn, long long value) { unsigned long long sbit = 1ULL << (insn->size - 1); unsigned long long bits = sbit | (sbit - 1); if (value == 1) return replace_with_pseudo(insn, insn->src1); switch (insn->opcode) { case OP_MULS: case OP_MULU: if (value == 0) return replace_with_pseudo(insn, insn->src2); /* Fall through */ case OP_DIVS: if (!(value & sbit)) // positive break; value |= ~bits; if (value == -1) { insn->opcode = OP_NEG; return REPEAT_CSE; } } return 0; } static int compare_opcode(int opcode, int inverse) { if (!inverse) return opcode; switch (opcode) { case OP_SET_EQ: return OP_SET_NE; case OP_SET_NE: return OP_SET_EQ; case OP_SET_LT: return OP_SET_GE; case OP_SET_LE: return OP_SET_GT; case OP_SET_GT: return OP_SET_LE; case OP_SET_GE: return OP_SET_LT; case OP_SET_A: return OP_SET_BE; case OP_SET_AE: return OP_SET_B; case OP_SET_B: return OP_SET_AE; case OP_SET_BE: return OP_SET_A; default: return opcode; } } static int simplify_seteq_setne(struct instruction *insn, long long value) { pseudo_t old = insn->src1; struct instruction *def = old->def; pseudo_t src1, src2; int inverse; int opcode; if (value != 0 && value != 1) return 0; if (!def) return 0; inverse = (insn->opcode == OP_SET_NE) == value; opcode = def->opcode; switch (opcode) { case OP_BINCMP ... OP_BINCMP_END: // Convert: // setcc.n %t <- %a, %b // setne.m %r <- %t, $0 // into: // setcc.n %t <- %a, %b // setcc.m %r <- %a, $b // and similar for setne/eq ... 0/1 src1 = def->src1; src2 = def->src2; insn->opcode = compare_opcode(opcode, inverse); use_pseudo(insn, src1, &insn->src1); use_pseudo(insn, src2, &insn->src2); remove_usage(old, &insn->src1); return REPEAT_CSE; default: return 0; } } static int simplify_constant_rightside(struct instruction *insn) { long long value = insn->src2->value; switch (insn->opcode) { case OP_OR_BOOL: if (value == 1) return replace_with_pseudo(insn, insn->src2); goto case_neutral_zero; 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_SHL: case OP_LSR: case_neutral_zero: if (!value) return replace_with_pseudo(insn, insn->src1); return 0; case OP_ASR: return simplify_asr(insn, insn->src1, value); case OP_MODU: case OP_MODS: if (value == 1) return replace_with_pseudo(insn, value_pseudo(0)); return 0; case OP_DIVU: case OP_DIVS: case OP_MULU: case OP_MULS: return simplify_mul_div(insn, value); 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; case OP_SET_NE: case OP_SET_EQ: return simplify_seteq_setne(insn, value); } 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; if (left == mask && right == -1) return 0; res = left / right; break; case OP_MODU: if (!ur) return 0; res = ul % ur; break; case OP_MODS: if (!right) return 0; if (left == mask && right == -1) 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_same_args(struct instruction *insn, pseudo_t arg) { switch (insn->opcode) { case OP_SET_NE: case OP_SET_LT: case OP_SET_GT: case OP_SET_B: case OP_SET_A: if (Wtautological_compare) warning(insn->pos, "self-comparison always evaluates to false"); case OP_SUB: case OP_XOR: return replace_with_pseudo(insn, value_pseudo(0)); case OP_SET_EQ: case OP_SET_LE: case OP_SET_GE: case OP_SET_BE: case OP_SET_AE: if (Wtautological_compare) warning(insn->pos, "self-comparison always evaluates to true"); return replace_with_pseudo(insn, value_pseudo(1)); case OP_AND: case OP_OR: return replace_with_pseudo(insn, arg); case OP_AND_BOOL: case OP_OR_BOOL: remove_usage(arg, &insn->src2); insn->src2 = value_pseudo(0); insn->opcode = OP_SET_NE; return REPEAT_CSE; default: break; } return 0; } 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); if (insn->src1 == insn->src2) return simplify_binop_same_args(insn, insn->src1); 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); switch (insn->opcode) { struct instruction *def; case OP_NOT: def = insn->src->def; if (def && def->opcode == OP_NOT) return replace_with_pseudo(insn, def->src); break; case OP_NEG: def = insn->src->def; if (def && def->opcode == OP_NEG) return replace_with_pseudo(insn, def->src); break; default: return 0; } 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; /* * If some BB have been removed it is possible that this * memop is in fact part of a dead BB. In this case * we must not warn since nothing is wrong. * If not part of a dead BB this will be redone after * the BBs have been cleaned up. */ if (repeat_phase & REPEAT_CFG_CLEANUP) 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; /* Keep casts with pointer on either side (not only case of OP_PTRCAST) */ if (is_ptr_type(orig_type) || is_ptr_type(insn->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; if (insn->opcode == OP_FPCAST && is_float_type(orig_type)) 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; /* 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; insn->opcode = OP_BR; 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)) { kill_use_list(insn->phi_list); 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_CBR: return simplify_branch(insn); case OP_SWITCH: return simplify_switch(insn); case OP_RANGE: return simplify_range(insn); } return 0; } sparse-0.5.1/sort.c000066400000000000000000000131031314543357600141560ustar00rootroot00000000000000/* * 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 #include #include #include "lib.h" #include "allocate.h" #undef PARANOIA #undef COVERAGE #ifdef PARANOIA #include #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; \ memmove (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; } } sparse-0.5.1/sparse-llvm.c000066400000000000000000000655621314543357600154540ustar00rootroot00000000000000/* * Example usage: * ./sparse-llvm hello.c | llc | as -o hello.o */ #include #include #include #include #include #include #include #include #include #include "symbol.h" #include "expression.h" #include "linearize.h" #include "flow.h" struct function { LLVMBuilderRef builder; LLVMTypeRef type; LLVMValueRef fn; LLVMModuleRef module; }; static inline bool symbol_is_fp_type(struct symbol *sym) { if (!sym) return false; return sym->ctype.base_type == &fp_type; } static LLVMTypeRef symbol_type(LLVMModuleRef module, struct symbol *sym); static LLVMTypeRef func_return_type(LLVMModuleRef module, struct symbol *sym) { return symbol_type(module, sym->ctype.base_type); } static LLVMTypeRef sym_func_type(LLVMModuleRef module, struct symbol *sym) { LLVMTypeRef *arg_type; LLVMTypeRef func_type; LLVMTypeRef ret_type; struct symbol *arg; int n_arg = 0; /* to avoid strangeness with varargs [for now], we build * the function and type anew, for each call. This * is probably wrong. We should look up the * symbol declaration info. */ ret_type = func_return_type(module, sym); /* count args, build argument type information */ FOR_EACH_PTR(sym->arguments, arg) { n_arg++; } END_FOR_EACH_PTR(arg); arg_type = calloc(n_arg, sizeof(LLVMTypeRef)); int idx = 0; FOR_EACH_PTR(sym->arguments, arg) { struct symbol *arg_sym = arg->ctype.base_type; arg_type[idx++] = symbol_type(module, arg_sym); } END_FOR_EACH_PTR(arg); func_type = LLVMFunctionType(ret_type, arg_type, n_arg, sym->variadic); return func_type; } static LLVMTypeRef sym_array_type(LLVMModuleRef module, struct symbol *sym) { LLVMTypeRef elem_type; struct symbol *base_type; base_type = sym->ctype.base_type; /* empty struct is undefined [6.7.2.1(8)] */ assert(base_type->bit_size > 0); elem_type = symbol_type(module, base_type); if (!elem_type) return NULL; return LLVMArrayType(elem_type, sym->bit_size / base_type->bit_size); } #define MAX_STRUCT_MEMBERS 64 static LLVMTypeRef sym_struct_type(LLVMModuleRef module, struct symbol *sym) { LLVMTypeRef elem_types[MAX_STRUCT_MEMBERS]; struct symbol *member; char buffer[256]; LLVMTypeRef ret; unsigned nr = 0; snprintf(buffer, sizeof(buffer), "struct.%s", sym->ident ? sym->ident->name : "anno"); ret = LLVMStructCreateNamed(LLVMGetGlobalContext(), buffer); /* set ->aux to avoid recursion */ sym->aux = ret; FOR_EACH_PTR(sym->symbol_list, member) { LLVMTypeRef member_type; assert(nr < MAX_STRUCT_MEMBERS); member_type = symbol_type(module, member); elem_types[nr++] = member_type; } END_FOR_EACH_PTR(member); LLVMStructSetBody(ret, elem_types, nr, 0 /* packed? */); return ret; } static LLVMTypeRef sym_union_type(LLVMModuleRef module, struct symbol *sym) { LLVMTypeRef elements; unsigned union_size; /* * There's no union support in the LLVM API so we treat unions as * opaque structs. The downside is that we lose type information on the * members but as LLVM doesn't care, neither do we. */ union_size = sym->bit_size / 8; elements = LLVMArrayType(LLVMInt8Type(), union_size); return LLVMStructType(&elements, 1, 0 /* packed? */); } static LLVMTypeRef sym_ptr_type(LLVMModuleRef module, struct symbol *sym) { LLVMTypeRef type; /* 'void *' is treated like 'char *' */ if (is_void_type(sym->ctype.base_type)) type = LLVMInt8Type(); else type = symbol_type(module, sym->ctype.base_type); return LLVMPointerType(type, 0); } static LLVMTypeRef sym_basetype_type(struct symbol *sym) { LLVMTypeRef ret = NULL; if (symbol_is_fp_type(sym)) { switch (sym->bit_size) { case 32: ret = LLVMFloatType(); break; case 64: ret = LLVMDoubleType(); break; case 80: ret = LLVMX86FP80Type(); break; default: die("invalid bit size %d for type %d", sym->bit_size, sym->type); break; } } else { switch (sym->bit_size) { case -1: ret = LLVMVoidType(); break; case 1: ret = LLVMInt1Type(); break; case 8: ret = LLVMInt8Type(); break; case 16: ret = LLVMInt16Type(); break; case 32: ret = LLVMInt32Type(); break; case 64: ret = LLVMInt64Type(); break; default: die("invalid bit size %d for type %d", sym->bit_size, sym->type); break; } } return ret; } static LLVMTypeRef symbol_type(LLVMModuleRef module, struct symbol *sym) { LLVMTypeRef ret = NULL; /* don't cache the result for SYM_NODE */ if (sym->type == SYM_NODE) return symbol_type(module, sym->ctype.base_type); if (sym->aux) return sym->aux; switch (sym->type) { case SYM_BITFIELD: case SYM_ENUM: ret = symbol_type(module, sym->ctype.base_type); break; case SYM_BASETYPE: ret = sym_basetype_type(sym); break; case SYM_PTR: ret = sym_ptr_type(module, sym); break; case SYM_UNION: ret = sym_union_type(module, sym); break; case SYM_STRUCT: ret = sym_struct_type(module, sym); break; case SYM_ARRAY: ret = sym_array_type(module, sym); break; case SYM_FN: ret = sym_func_type(module, sym); break; default: assert(0); } /* cache the result */ sym->aux = ret; return ret; } static LLVMTypeRef insn_symbol_type(LLVMModuleRef module, struct instruction *insn) { if (insn->type) return symbol_type(module, insn->type); switch (insn->size) { case 8: return LLVMInt8Type(); case 16: return LLVMInt16Type(); case 32: return LLVMInt32Type(); case 64: return LLVMInt64Type(); default: die("invalid bit size %d", insn->size); break; } return NULL; /* not reached */ } static LLVMLinkage data_linkage(struct symbol *sym) { if (sym->ctype.modifiers & MOD_STATIC) return LLVMPrivateLinkage; return LLVMExternalLinkage; } static LLVMLinkage function_linkage(struct symbol *sym) { if (sym->ctype.modifiers & MOD_STATIC) return LLVMInternalLinkage; return LLVMExternalLinkage; } #define MAX_PSEUDO_NAME 64 static void pseudo_name(pseudo_t pseudo, char *buf) { switch (pseudo->type) { case PSEUDO_REG: snprintf(buf, MAX_PSEUDO_NAME, "R%d", pseudo->nr); break; case PSEUDO_SYM: assert(0); break; case PSEUDO_VAL: assert(0); break; case PSEUDO_ARG: { assert(0); break; } case PSEUDO_PHI: snprintf(buf, MAX_PSEUDO_NAME, "PHI%d", pseudo->nr); break; default: assert(0); } } static LLVMValueRef pseudo_to_value(struct function *fn, struct instruction *insn, pseudo_t pseudo) { LLVMValueRef result = NULL; switch (pseudo->type) { case PSEUDO_REG: result = pseudo->priv; break; case PSEUDO_SYM: { struct symbol *sym = pseudo->sym; struct expression *expr; assert(sym->bb_target == NULL); expr = sym->initializer; if (expr) { switch (expr->type) { case EXPR_STRING: { const char *s = expr->string->data; LLVMValueRef indices[] = { LLVMConstInt(LLVMInt64Type(), 0, 0), LLVMConstInt(LLVMInt64Type(), 0, 0) }; LLVMValueRef data; data = LLVMAddGlobal(fn->module, LLVMArrayType(LLVMInt8Type(), strlen(s) + 1), ".str"); LLVMSetLinkage(data, LLVMPrivateLinkage); LLVMSetGlobalConstant(data, 1); LLVMSetInitializer(data, LLVMConstString(strdup(s), strlen(s) + 1, true)); result = LLVMConstGEP(data, indices, ARRAY_SIZE(indices)); break; } case EXPR_SYMBOL: { struct symbol *sym = expr->symbol; result = LLVMGetNamedGlobal(fn->module, show_ident(sym->ident)); assert(result != NULL); break; } default: assert(0); } } else { const char *name = show_ident(sym->ident); LLVMTypeRef type = symbol_type(fn->module, sym); if (LLVMGetTypeKind(type) == LLVMFunctionTypeKind) { result = LLVMGetNamedFunction(fn->module, name); if (!result) result = LLVMAddFunction(fn->module, name, type); } else { result = LLVMGetNamedGlobal(fn->module, name); if (!result) result = LLVMAddGlobal(fn->module, type, name); } } break; } case PSEUDO_VAL: result = LLVMConstInt(insn_symbol_type(fn->module, insn), pseudo->value, 1); break; case PSEUDO_ARG: { result = LLVMGetParam(fn->fn, pseudo->nr - 1); break; } case PSEUDO_PHI: result = pseudo->priv; break; case PSEUDO_VOID: result = NULL; break; default: assert(0); } return result; } static LLVMValueRef calc_gep(LLVMBuilderRef builder, LLVMValueRef base, LLVMValueRef off) { LLVMTypeRef type = LLVMTypeOf(base); unsigned int as = LLVMGetPointerAddressSpace(type); LLVMTypeRef bytep = LLVMPointerType(LLVMInt8Type(), as); LLVMValueRef addr; /* convert base to char* type */ base = LLVMBuildPointerCast(builder, base, bytep, ""); /* addr = base + off */ addr = LLVMBuildInBoundsGEP(builder, base, &off, 1, ""); /* convert back to the actual pointer type */ addr = LLVMBuildPointerCast(builder, addr, type, ""); return addr; } static LLVMRealPredicate translate_fop(int opcode) { static const LLVMRealPredicate trans_tbl[] = { [OP_SET_EQ] = LLVMRealOEQ, [OP_SET_NE] = LLVMRealUNE, [OP_SET_LE] = LLVMRealOLE, [OP_SET_GE] = LLVMRealOGE, [OP_SET_LT] = LLVMRealOLT, [OP_SET_GT] = LLVMRealOGT, /* Are these used with FP? */ [OP_SET_B] = LLVMRealOLT, [OP_SET_A] = LLVMRealOGT, [OP_SET_BE] = LLVMRealOLE, [OP_SET_AE] = LLVMRealOGE, }; return trans_tbl[opcode]; } static LLVMIntPredicate translate_op(int opcode) { static const LLVMIntPredicate trans_tbl[] = { [OP_SET_EQ] = LLVMIntEQ, [OP_SET_NE] = LLVMIntNE, [OP_SET_LE] = LLVMIntSLE, [OP_SET_GE] = LLVMIntSGE, [OP_SET_LT] = LLVMIntSLT, [OP_SET_GT] = LLVMIntSGT, [OP_SET_B] = LLVMIntULT, [OP_SET_A] = LLVMIntUGT, [OP_SET_BE] = LLVMIntULE, [OP_SET_AE] = LLVMIntUGE, }; return trans_tbl[opcode]; } static void output_op_binary(struct function *fn, struct instruction *insn) { LLVMValueRef lhs, rhs, target; char target_name[64]; lhs = pseudo_to_value(fn, insn, insn->src1); rhs = pseudo_to_value(fn, insn, insn->src2); pseudo_name(insn->target, target_name); switch (insn->opcode) { /* Binary */ case OP_ADD: if (symbol_is_fp_type(insn->type)) target = LLVMBuildFAdd(fn->builder, lhs, rhs, target_name); else target = LLVMBuildAdd(fn->builder, lhs, rhs, target_name); break; case OP_SUB: if (symbol_is_fp_type(insn->type)) target = LLVMBuildFSub(fn->builder, lhs, rhs, target_name); else target = LLVMBuildSub(fn->builder, lhs, rhs, target_name); break; case OP_MULU: if (symbol_is_fp_type(insn->type)) target = LLVMBuildFMul(fn->builder, lhs, rhs, target_name); else target = LLVMBuildMul(fn->builder, lhs, rhs, target_name); break; case OP_MULS: assert(!symbol_is_fp_type(insn->type)); target = LLVMBuildMul(fn->builder, lhs, rhs, target_name); break; case OP_DIVU: if (symbol_is_fp_type(insn->type)) target = LLVMBuildFDiv(fn->builder, lhs, rhs, target_name); else target = LLVMBuildUDiv(fn->builder, lhs, rhs, target_name); break; case OP_DIVS: assert(!symbol_is_fp_type(insn->type)); target = LLVMBuildSDiv(fn->builder, lhs, rhs, target_name); break; case OP_MODU: assert(!symbol_is_fp_type(insn->type)); target = LLVMBuildURem(fn->builder, lhs, rhs, target_name); break; case OP_MODS: assert(!symbol_is_fp_type(insn->type)); target = LLVMBuildSRem(fn->builder, lhs, rhs, target_name); break; case OP_SHL: assert(!symbol_is_fp_type(insn->type)); target = LLVMBuildShl(fn->builder, lhs, rhs, target_name); break; case OP_LSR: assert(!symbol_is_fp_type(insn->type)); target = LLVMBuildLShr(fn->builder, lhs, rhs, target_name); break; case OP_ASR: assert(!symbol_is_fp_type(insn->type)); target = LLVMBuildAShr(fn->builder, lhs, rhs, target_name); break; /* Logical */ case OP_AND: assert(!symbol_is_fp_type(insn->type)); target = LLVMBuildAnd(fn->builder, lhs, rhs, target_name); break; case OP_OR: assert(!symbol_is_fp_type(insn->type)); target = LLVMBuildOr(fn->builder, lhs, rhs, target_name); break; case OP_XOR: assert(!symbol_is_fp_type(insn->type)); target = LLVMBuildXor(fn->builder, lhs, rhs, target_name); break; case OP_AND_BOOL: { LLVMValueRef lhs_nz, rhs_nz; LLVMTypeRef dst_type; lhs_nz = LLVMBuildIsNotNull(fn->builder, lhs, ""); rhs_nz = LLVMBuildIsNotNull(fn->builder, rhs, ""); target = LLVMBuildAnd(fn->builder, lhs_nz, rhs_nz, target_name); dst_type = insn_symbol_type(fn->module, insn); target = LLVMBuildZExt(fn->builder, target, dst_type, target_name); break; } case OP_OR_BOOL: { LLVMValueRef lhs_nz, rhs_nz; LLVMTypeRef dst_type; lhs_nz = LLVMBuildIsNotNull(fn->builder, lhs, ""); rhs_nz = LLVMBuildIsNotNull(fn->builder, rhs, ""); target = LLVMBuildOr(fn->builder, lhs_nz, rhs_nz, target_name); dst_type = insn_symbol_type(fn->module, insn); target = LLVMBuildZExt(fn->builder, target, dst_type, target_name); break; } default: assert(0); break; } insn->target->priv = target; } static void output_op_compare(struct function *fn, struct instruction *insn) { LLVMValueRef lhs, rhs, target; char target_name[64]; lhs = pseudo_to_value(fn, insn, insn->src1); if (insn->src2->type == PSEUDO_VAL) rhs = LLVMConstInt(LLVMTypeOf(lhs), insn->src2->value, 1); else rhs = pseudo_to_value(fn, insn, insn->src2); pseudo_name(insn->target, target_name); LLVMTypeRef dst_type = insn_symbol_type(fn->module, insn); if (LLVMGetTypeKind(LLVMTypeOf(lhs)) == LLVMIntegerTypeKind) { LLVMIntPredicate op = translate_op(insn->opcode); target = LLVMBuildICmp(fn->builder, op, lhs, rhs, target_name); } else { LLVMRealPredicate op = translate_fop(insn->opcode); target = LLVMBuildFCmp(fn->builder, op, lhs, rhs, target_name); } target = LLVMBuildZExt(fn->builder, target, dst_type, target_name); insn->target->priv = target; } static void output_op_ret(struct function *fn, struct instruction *insn) { pseudo_t pseudo = insn->src; if (pseudo && pseudo != VOID) { LLVMValueRef result = pseudo_to_value(fn, insn, pseudo); LLVMBuildRet(fn->builder, result); } else LLVMBuildRetVoid(fn->builder); } static LLVMValueRef calc_memop_addr(struct function *fn, struct instruction *insn) { LLVMTypeRef int_type, addr_type; LLVMValueRef src, off, addr; unsigned int as; /* int type large enough to hold a pointer */ int_type = LLVMIntType(bits_in_pointer); off = LLVMConstInt(int_type, insn->offset, 0); /* convert src to the effective pointer type */ src = pseudo_to_value(fn, insn, insn->src); as = LLVMGetPointerAddressSpace(LLVMTypeOf(src)); addr_type = LLVMPointerType(insn_symbol_type(fn->module, insn), as); src = LLVMBuildPointerCast(fn->builder, src, addr_type, ""); /* addr = src + off */ addr = calc_gep(fn->builder, src, off); return addr; } static void output_op_load(struct function *fn, struct instruction *insn) { LLVMValueRef addr, target; addr = calc_memop_addr(fn, insn); /* perform load */ target = LLVMBuildLoad(fn->builder, addr, "load_target"); insn->target->priv = target; } static void output_op_store(struct function *fn, struct instruction *insn) { LLVMValueRef addr, target, target_in; addr = calc_memop_addr(fn, insn); target_in = pseudo_to_value(fn, insn, insn->target); /* perform store */ target = LLVMBuildStore(fn->builder, target_in, addr); insn->target->priv = target; } static LLVMValueRef bool_value(struct function *fn, LLVMValueRef value) { if (LLVMTypeOf(value) != LLVMInt1Type()) value = LLVMBuildIsNotNull(fn->builder, value, "cond"); return value; } static void output_op_cbr(struct function *fn, struct instruction *br) { LLVMValueRef cond = bool_value(fn, pseudo_to_value(fn, br, br->cond)); LLVMBuildCondBr(fn->builder, cond, br->bb_true->priv, br->bb_false->priv); } static void output_op_br(struct function *fn, struct instruction *br) { LLVMBuildBr(fn->builder, br->bb_true->priv); } static void output_op_sel(struct function *fn, struct instruction *insn) { LLVMValueRef target, src1, src2, src3; src1 = bool_value(fn, pseudo_to_value(fn, insn, insn->src1)); src2 = pseudo_to_value(fn, insn, insn->src2); src3 = pseudo_to_value(fn, insn, insn->src3); target = LLVMBuildSelect(fn->builder, src1, src2, src3, "select"); insn->target->priv = target; } static void output_op_switch(struct function *fn, struct instruction *insn) { LLVMValueRef sw_val, target; struct basic_block *def = NULL; struct multijmp *jmp; int n_jmp = 0; FOR_EACH_PTR(insn->multijmp_list, jmp) { if (jmp->begin == jmp->end) { /* case N */ n_jmp++; } else if (jmp->begin < jmp->end) { /* case M..N */ assert(0); } else /* default case */ def = jmp->target; } END_FOR_EACH_PTR(jmp); sw_val = pseudo_to_value(fn, insn, insn->target); target = LLVMBuildSwitch(fn->builder, sw_val, def ? def->priv : NULL, n_jmp); FOR_EACH_PTR(insn->multijmp_list, jmp) { if (jmp->begin == jmp->end) { /* case N */ LLVMAddCase(target, LLVMConstInt(LLVMInt32Type(), jmp->begin, 0), jmp->target->priv); } else if (jmp->begin < jmp->end) { /* case M..N */ assert(0); } } END_FOR_EACH_PTR(jmp); insn->target->priv = target; } static void output_op_call(struct function *fn, struct instruction *insn) { LLVMValueRef target, func; int n_arg = 0, i; struct pseudo *arg; LLVMValueRef *args; FOR_EACH_PTR(insn->arguments, arg) { n_arg++; } END_FOR_EACH_PTR(arg); args = calloc(n_arg, sizeof(LLVMValueRef)); i = 0; FOR_EACH_PTR(insn->arguments, arg) { args[i++] = pseudo_to_value(fn, insn, arg); } END_FOR_EACH_PTR(arg); func = pseudo_to_value(fn, insn, insn->func); target = LLVMBuildCall(fn->builder, func, args, n_arg, ""); insn->target->priv = target; } static void output_op_phisrc(struct function *fn, struct instruction *insn) { LLVMValueRef v; struct instruction *phi; assert(insn->target->priv == NULL); /* target = src */ v = pseudo_to_value(fn, insn, insn->phi_src); FOR_EACH_PTR(insn->phi_users, phi) { LLVMValueRef load, ptr; assert(phi->opcode == OP_PHI); /* phi must be load from alloca */ load = phi->target->priv; assert(LLVMGetInstructionOpcode(load) == LLVMLoad); ptr = LLVMGetOperand(load, 0); /* store v to alloca */ LLVMBuildStore(fn->builder, v, ptr); } END_FOR_EACH_PTR(phi); } static void output_op_phi(struct function *fn, struct instruction *insn) { LLVMValueRef load = insn->target->priv; /* forward load */ assert(LLVMGetInstructionOpcode(load) == LLVMLoad); /* forward load has no parent block */ assert(!LLVMGetInstructionParent(load)); /* finalize load in current block */ LLVMInsertIntoBuilder(fn->builder, load); } static void output_op_ptrcast(struct function *fn, struct instruction *insn) { LLVMValueRef src, target; char target_name[64]; src = insn->src->priv; if (!src) src = pseudo_to_value(fn, insn, insn->src); pseudo_name(insn->target, target_name); assert(!symbol_is_fp_type(insn->type)); target = LLVMBuildBitCast(fn->builder, src, insn_symbol_type(fn->module, insn), target_name); insn->target->priv = target; } static void output_op_cast(struct function *fn, struct instruction *insn, LLVMOpcode op) { LLVMValueRef src, target; char target_name[64]; src = insn->src->priv; if (!src) src = pseudo_to_value(fn, insn, insn->src); pseudo_name(insn->target, target_name); assert(!symbol_is_fp_type(insn->type)); if (insn->size < LLVMGetIntTypeWidth(LLVMTypeOf(src))) target = LLVMBuildTrunc(fn->builder, src, insn_symbol_type(fn->module, insn), target_name); else target = LLVMBuildCast(fn->builder, op, src, insn_symbol_type(fn->module, insn), target_name); insn->target->priv = target; } static void output_insn(struct function *fn, struct instruction *insn) { switch (insn->opcode) { case OP_RET: output_op_ret(fn, insn); break; case OP_BR: output_op_br(fn, insn); break; case OP_CBR: output_op_cbr(fn, insn); break; case OP_SYMADDR: assert(0); break; case OP_SETVAL: assert(0); break; case OP_SWITCH: output_op_switch(fn, insn); break; case OP_COMPUTEDGOTO: assert(0); break; case OP_PHISOURCE: output_op_phisrc(fn, insn); break; case OP_PHI: output_op_phi(fn, insn); break; case OP_LOAD: output_op_load(fn, insn); break; case OP_LNOP: assert(0); break; case OP_STORE: output_op_store(fn, insn); break; case OP_SNOP: assert(0); break; case OP_INLINED_CALL: assert(0); break; case OP_CALL: output_op_call(fn, insn); break; case OP_CAST: output_op_cast(fn, insn, LLVMZExt); break; case OP_SCAST: output_op_cast(fn, insn, LLVMSExt); break; case OP_FPCAST: assert(0); break; case OP_PTRCAST: output_op_ptrcast(fn, insn); break; case OP_BINARY ... OP_BINARY_END: output_op_binary(fn, insn); break; case OP_BINCMP ... OP_BINCMP_END: output_op_compare(fn, insn); break; case OP_SEL: output_op_sel(fn, insn); break; case OP_SLICE: assert(0); break; case OP_NOT: { LLVMValueRef src, target; char target_name[64]; src = pseudo_to_value(fn, insn, insn->src); pseudo_name(insn->target, target_name); target = LLVMBuildNot(fn->builder, src, target_name); insn->target->priv = target; break; } case OP_NEG: assert(0); break; case OP_CONTEXT: assert(0); break; case OP_RANGE: assert(0); break; case OP_NOP: assert(0); break; case OP_DEATHNOTE: break; case OP_ASM: assert(0); break; case OP_COPY: assert(0); break; default: break; } } static void output_bb(struct function *fn, struct basic_block *bb, unsigned long generation) { struct instruction *insn; bb->generation = generation; FOR_EACH_PTR(bb->insns, insn) { if (!insn->bb) continue; output_insn(fn, insn); } END_FOR_EACH_PTR(insn); } #define MAX_ARGS 64 static void output_fn(LLVMModuleRef module, struct entrypoint *ep) { unsigned long generation = ++bb_generation; struct symbol *sym = ep->name; struct symbol *base_type = sym->ctype.base_type; struct symbol *ret_type = sym->ctype.base_type->ctype.base_type; LLVMTypeRef arg_types[MAX_ARGS]; LLVMTypeRef return_type; struct function function = { .module = module }; struct basic_block *bb; struct symbol *arg; const char *name; int nr_args = 0; FOR_EACH_PTR(base_type->arguments, arg) { struct symbol *arg_base_type = arg->ctype.base_type; arg_types[nr_args++] = symbol_type(module, arg_base_type); } END_FOR_EACH_PTR(arg); name = show_ident(sym->ident); return_type = symbol_type(module, ret_type); function.type = LLVMFunctionType(return_type, arg_types, nr_args, 0); function.fn = LLVMAddFunction(module, name, function.type); LLVMSetFunctionCallConv(function.fn, LLVMCCallConv); LLVMSetLinkage(function.fn, function_linkage(sym)); function.builder = LLVMCreateBuilder(); static int nr_bb; FOR_EACH_PTR(ep->bbs, bb) { if (bb->generation == generation) continue; LLVMBasicBlockRef bbr; char bbname[32]; struct instruction *insn; sprintf(bbname, "L%d", nr_bb++); bbr = LLVMAppendBasicBlock(function.fn, bbname); bb->priv = bbr; /* allocate alloca for each phi */ FOR_EACH_PTR(bb->insns, insn) { LLVMBasicBlockRef entrybbr; LLVMTypeRef phi_type; LLVMValueRef ptr; if (!insn->bb || insn->opcode != OP_PHI) continue; /* insert alloca into entry block */ entrybbr = LLVMGetEntryBasicBlock(function.fn); LLVMPositionBuilderAtEnd(function.builder, entrybbr); phi_type = insn_symbol_type(module, insn); ptr = LLVMBuildAlloca(function.builder, phi_type, ""); /* emit forward load for phi */ LLVMClearInsertionPosition(function.builder); insn->target->priv = LLVMBuildLoad(function.builder, ptr, "phi"); } END_FOR_EACH_PTR(insn); } END_FOR_EACH_PTR(bb); FOR_EACH_PTR(ep->bbs, bb) { if (bb->generation == generation) continue; LLVMPositionBuilderAtEnd(function.builder, bb->priv); output_bb(&function, bb, generation); } END_FOR_EACH_PTR(bb); } static LLVMValueRef output_data(LLVMModuleRef module, struct symbol *sym) { struct expression *initializer = sym->initializer; LLVMValueRef initial_value; LLVMValueRef data; const char *name; if (initializer) { switch (initializer->type) { case EXPR_VALUE: initial_value = LLVMConstInt(symbol_type(module, sym), initializer->value, 1); break; case EXPR_SYMBOL: { struct symbol *sym = initializer->symbol; initial_value = LLVMGetNamedGlobal(module, show_ident(sym->ident)); if (!initial_value) initial_value = output_data(module, sym); break; } case EXPR_STRING: { const char *s = initializer->string->data; initial_value = LLVMConstString(strdup(s), strlen(s) + 1, true); break; } default: assert(0); } } else { LLVMTypeRef type = symbol_type(module, sym); initial_value = LLVMConstNull(type); } name = show_ident(sym->ident); data = LLVMAddGlobal(module, LLVMTypeOf(initial_value), name); LLVMSetLinkage(data, data_linkage(sym)); if (sym->ctype.modifiers & MOD_CONST) LLVMSetGlobalConstant(data, 1); if (sym->ctype.modifiers & MOD_TLS) LLVMSetThreadLocal(data, 1); if (sym->ctype.alignment) LLVMSetAlignment(data, sym->ctype.alignment); if (!(sym->ctype.modifiers & MOD_EXTERN)) LLVMSetInitializer(data, initial_value); return data; } static int is_prototype(struct symbol *sym) { if (sym->type == SYM_NODE) sym = sym->ctype.base_type; return sym && sym->type == SYM_FN && !sym->stmt; } static int compile(LLVMModuleRef module, struct symbol_list *list) { struct symbol *sym; FOR_EACH_PTR(list, sym) { struct entrypoint *ep; expand_symbol(sym); if (is_prototype(sym)) continue; ep = linearize_symbol(sym); if (ep) output_fn(module, ep); else output_data(module, sym); } END_FOR_EACH_PTR(sym); return 0; } #ifndef LLVM_DEFAULT_TARGET_TRIPLE #define LLVM_DEFAULT_TARGET_TRIPLE LLVM_HOSTTRIPLE #endif #define X86_LINUX_LAYOUT \ "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-" \ "i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-" \ "a0:0:64-f80:32:32-n8:16:32-S128" #define X86_64_LINUX_LAYOUT \ "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-" \ "i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-" \ "a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" static void set_target(LLVMModuleRef module) { char target[] = LLVM_DEFAULT_TARGET_TRIPLE; const char *arch, *vendor, *os, *env, *layout = NULL; char triple[256]; arch = strtok(target, "-"); vendor = strtok(NULL, "-"); os = strtok(NULL, "-"); env = strtok(NULL, "-"); if (!os) return; if (!env) env = "unknown"; if (!strcmp(arch, "x86_64") && !strcmp(os, "linux")) { if (arch_m64) { layout = X86_64_LINUX_LAYOUT; } else { arch = "i386"; layout = X86_LINUX_LAYOUT; } } /* unsupported target */ if (!layout) return; snprintf(triple, sizeof(triple), "%s-%s-%s-%s", arch, vendor, os, env); LLVMSetTarget(module, triple); LLVMSetDataLayout(module, layout); } int main(int argc, char **argv) { struct string_list *filelist = NULL; struct symbol_list *symlist; LLVMModuleRef module; char *file; symlist = sparse_initialize(argc, argv, &filelist); module = LLVMModuleCreateWithName("sparse"); set_target(module); compile(module, symlist); /* need ->phi_users */ dbg_dead = 1; FOR_EACH_PTR_NOTAG(filelist, file) { symlist = sparse(file); if (die_if_error) return 1; compile(module, symlist); } END_FOR_EACH_PTR_NOTAG(file); LLVMVerifyModule(module, LLVMPrintMessageAction, NULL); LLVMWriteBitcodeToFD(module, STDOUT_FILENO, 0, 0); LLVMDisposeModule(module); report_stats(); return 0; } sparse-0.5.1/sparse.1000066400000000000000000000325051314543357600144110ustar00rootroot00000000000000.\" 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 \-Wsparse\-error Turn all sparse warnings into errors. .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. __bitwise ends up being a "stronger integer separation". That one doesn't allow you to mix with non-bitwise integers, so now it's much harder to lose the type by mistake. __bitwise is for *unique types* that cannot be mixed with other types, and that you'd never want to just use as a random integer (the integer 0 is special, though, and gets silently accepted iirc - it's kind of like "NULL" for pointers). So "gfp_t" or the "safe endianness" types would be __bitwise: you can only operate on them by doing specific operations that know about *that* particular type. Generally, you want bitwise if you are looking for type safety. 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 \-Winit\-cstring Warn about initialization of a char array with a too long constant C string. If the size of the char array and the length of the string is the same, there is no space for the last nul char of the string in the array: .nf char s[3] = "abc"; .fi If the array is used as a byte array, not as C string, this warning is just noise. However, if the array is passed to functions dealing with C string like printf(%s) and strcmp, it may cause a trouble. Sparse does not issue these warnings by default. . .TP .B \-Wmemcpy\-max\-count Warn about call of \fBmemcpy()\fR, \fBmemset()\fR, \fBcopy_from_user()\fR, or \fBcopy_to_user()\fR with a large compile-time byte count. Sparse issues these warnings by default. To turn them off, use \fB\-Wno\-memcpy\-max\-count\fR. The limit can be changed with \fB\-fmemcpy\-max\-count=COUNT\fR, the default being \fB100000\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 \-Wsizeof-bool Warn when checking the sizeof a _Bool. C99 does not specify the sizeof a _Bool. gcc uses 1. 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/. . .TP .B \-multiarch-dir \fIdir\fR Look for system headers in the multiarch subdirectory \fIdir\fR. The \fIdir\fR name would normally take the form of the target's normalized GNU triplet. (e.g. i386-linux-gnu). . .SH DEBUG OPTIONS .TP .B \-fdump-linearize[=only] Dump the IR code of a function directly after its linearization, before any simplifications is made. If the argument \fB=only\fR is also given no further processing is done on the function. . .B \-fmem-report Report some statistics about memory allocation used by the tool. . .SH OTHER OPTIONS .TP .B \-fmemcpy-max-count=COUNT Set the limit for the warnings given by \fB-Wmemcpy-max-count\fR. A COUNT of 0, useless in itself, will effectively disable the warning. The default limit is 100000. . .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 Christopher Li sparse-0.5.1/sparse.c000066400000000000000000000167701314543357600145010ustar00rootroot00000000000000/* * 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 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #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) { unsigned long long val = count->value; if (Wmemcpy_max_count && val > fmemcpy_max_count) warning(insn->pos, "%s with byte count of %llu", 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); if (Wsparse_error && die_if_error) exit(1); } 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); report_stats(); return 0; } sparse-0.5.1/sparse.pc.in000066400000000000000000000002571314543357600152570ustar00rootroot00000000000000prefix=@prefix@ libdir=@libdir@ includedir=@includedir@ Name: Sparse Description: Semantic parser for C Version: @version@ Libs: -L${libdir} -lsparse Cflags: -I${includedir} sparse-0.5.1/sparsec000077500000000000000000000014101314543357600144070ustar00rootroot00000000000000#!/bin/sh # # GCC compatible C compiler based on Sparse LLVM set +e SPARSEOPTS="" DIRNAME=`dirname $0` NEED_LINK=1 if [ $# -eq 0 ]; then echo "`basename $0`: no input files" exit 1 fi while [ $# -gt 0 ]; do case $1 in '-o') OUTFILE=$2 shift ;; '-c') NEED_LINK=0 ;; *) SPARSEOPTS="$SPARSEOPTS $1 " ;; esac shift done TMPLLVM=`mktemp -t tmp.XXXXXX`".llvm" TMPFILE=`mktemp -t tmp.XXXXXX`".o" $DIRNAME/sparse-llvm $SPARSEOPTS > $TMPLLVM LLC=`"${LLVM_CONFIG:-llvm-config}" --bindir`/llc $LLC -o - $TMPLLVM | as -o $TMPFILE if [ $NEED_LINK -eq 1 ]; then if [ -z $OUTFILE ]; then OUTFILE=a.out fi gcc $TMPFILE -o $OUTFILE else if [ -z $OUTFILE ]; then echo "`basename $0`: no output file" exit 1 fi mv $TMPFILE $OUTFILE fi rm -f $TMPLLVM sparse-0.5.1/sparsei000077500000000000000000000003031314543357600144150ustar00rootroot00000000000000#!/bin/sh set +e DIRNAME=`dirname $0` LLI=`"${LLVM_CONFIG:-llvm-config}" --bindir`/lli if [ $# -eq 0 ]; then echo "`basename $0`: no input files" exit 1 fi $DIRNAME/sparse-llvm $@ | $LLI sparse-0.5.1/stats.c000066400000000000000000000032621314543357600143320ustar00rootroot00000000000000#include #include "allocate.h" #include "linearize.h" #include "storage.h" __DECLARE_ALLOCATOR(struct ptr_list, ptrlist); typedef void (*get_t)(struct allocator_stats*); static void show_stats(get_t get, struct allocator_stats * tot) { struct allocator_stats x; if (get) get(&x); else x = *tot; fprintf(stderr, "%16s: %8d, %10ld, %10ld, %6.2f%%, %8.2f\n", x.name, x.allocations, x.useful_bytes, x.total_bytes, 100 * (double) x.useful_bytes / (x.total_bytes ? : 1), (double) x.useful_bytes / (x.allocations ? : 1)); tot->allocations += x.allocations; tot->useful_bytes += x.useful_bytes; tot->total_bytes += x.total_bytes; } void show_allocation_stats(void) { struct allocator_stats tot = { .name = "total", }; fprintf(stderr, "%16s: %8s, %10s, %10s, %7s, %8s\n", "allocator", "allocs", "bytes", "total", "%usage", "average"); show_stats(get_token_stats, &tot); show_stats(get_ident_stats, &tot); show_stats(get_symbol_stats, &tot); show_stats(get_expression_stats, &tot); show_stats(get_statement_stats, &tot); show_stats(get_scope_stats, &tot); show_stats(get_basic_block_stats, &tot); show_stats(get_instruction_stats, &tot); show_stats(get_pseudo_stats, &tot); show_stats(get_pseudo_user_stats, &tot); show_stats(get_ptrlist_stats, &tot); show_stats(get_multijmp_stats, &tot); show_stats(get_asm_rules_stats, &tot); show_stats(get_asm_constraint_stats, &tot); show_stats(get_context_stats, &tot); show_stats(get_string_stats, &tot); show_stats(get_bytes_stats, &tot); //show_stats(get_storage_stats, &tot); //show_stats(get_storage_hash_stats, &tot); show_stats(NULL, &tot); } void report_stats(void) { if (fmem_report) show_allocation_stats(); } sparse-0.5.1/storage.c000066400000000000000000000166501314543357600146450ustar00rootroot00000000000000/* * 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 #include #include #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(); } sparse-0.5.1/storage.h000066400000000000000000000033251314543357600146450ustar00rootroot00000000000000#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 */ sparse-0.5.1/symbol.c000066400000000000000000000533131314543357600145030ustar00rootroot00000000000000/* * Symbol lookup and handling. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #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 = -1, alignment; struct expression *array_size = sym->array_size; if (!base_type) return sym; if (array_size) { bit_size = array_element_offset(base_type->bit_size, get_expression_value_silent(array_size)); if (array_size->type != EXPR_VALUE) { if (Wvla) warning(array_size->pos, "Variable length array is used."); 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[] = {} 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_PREOP: { struct expression *e = entry; if (is_char) { while (e && e->type == EXPR_PREOP && e->op == '(') e = e->unop; if (e && e->type == EXPR_STRING) { entry = e; 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_PREOP: if (is_char) { struct expression *e = expr; while (e && e->type == EXPR_PREOP && e->op == '(') e = e->unop; if (e && e->type == EXPR_STRING) { expr = e; case EXPR_STRING: if (is_char) nr = expr->string->length; } } break; default: break; } return nr; } static struct expression *get_symbol_initializer(struct symbol *sym) { do { if (sym->initializer) return sym->initializer; } while ((sym = sym->same_symbol) != NULL); return NULL; } 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) { struct expression *initializer = get_symbol_initializer(sym); if (initializer) { struct symbol *node_type = base_type->ctype.base_type; int count = count_array_initializer(node_type, initializer); if (node_type && node_type->bit_size >= 0) bit_size = array_element_offset(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) { unsigned long mod = 0; if (is_bitfield_type(base)) warning(base->pos, "typeof applied to bitfield type"); if (base->type == SYM_NODE) { mod |= base->ctype.modifiers & MOD_TYPEOF; base = base->ctype.base_type; } sym->type = SYM_NODE; sym->ctype.modifiers = mod; 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; } /* Extern in block level matches a TOPLEVEL non-static symbol */ if (sym->ctype.modifiers & MOD_EXTERN) { if ((next->ctype.modifiers & (MOD_TOPLEVEL|MOD_STATIC)) == MOD_TOPLEVEL) { 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 ident *ident = built_in_ident(name); struct symbol *sym = lookup_symbol(ident, namespace); if (sym && sym->type != type) die("symbol %s created with different types: %d old %d", name, type, sym->type); if (!sym) { struct token *token = built_in_token(stream, ident); sym = alloc_symbol(token->pos, type); bind_symbol(sym, token->ident, namespace); } return sym; } /* * 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); #define __IDENT(n,str,res) \ hash_ident(&n) #include "ident-list.h" init_parser(stream); init_builtins(stream); } #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); 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; } } sparse-0.5.1/symbol.h000066400000000000000000000301501314543357600145020ustar00rootroot00000000000000#ifndef SYMBOL_H #define SYMBOL_H /* * Basic symbol and namespace definitions. * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #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; }; #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, forced_arg:1, transparent_union: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_NORETURN | MOD_NOCAST) /* modifiers preserved by typeof() operator */ #define MOD_TYPEOF (MOD_VOLATILE | MOD_CONST | MOD_NOCAST | MOD_SPECIFIER) /* 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_builtins(int stream); 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_func_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; return type->type == SYM_FN; } static inline int is_array_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; return type->type == SYM_ARRAY; } 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_scalar_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; switch (type->type) { case SYM_ENUM: case SYM_BITFIELD: case SYM_PTR: case SYM_RESTRICT: // OK, always integer types return 1; default: break; } if (type->ctype.base_type == &int_type) return 1; if (type->ctype.base_type == &fp_type) return 1; return 0; } 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 */ sparse-0.5.1/target.c000066400000000000000000000013761314543357600144660ustar00rootroot00000000000000#include #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 bits_in_wchar = 32; 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; sparse-0.5.1/target.h000066400000000000000000000023431314543357600144660ustar00rootroot00000000000000#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 bits_in_wchar; 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) / bits_in_char : -1; } static inline int bytes_to_bits(int bytes) { return bytes * bits_in_char; } static inline unsigned long array_element_offset(unsigned long base_bits, int idx) { int fragment = base_bits % bits_in_char; if (fragment) base_bits += bits_in_char - fragment; return base_bits * idx; } #endif sparse-0.5.1/test-dissect.c000066400000000000000000000041311314543357600156030ustar00rootroot00000000000000#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 = built_in_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 = built_in_ident("?"); si = sym->ident ?: ni; /* mem == NULL means entire struct accessed */ mi = mem ? (mem->ident ?: ni) : built_in_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; } sparse-0.5.1/test-inspect.c000066400000000000000000000015421314543357600156150ustar00rootroot00000000000000 #include #include #include #include #include #include #include #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; } sparse-0.5.1/test-lexing.c000066400000000000000000000033001314543357600154300ustar00rootroot00000000000000/* * Example test program that just uses the tokenization and * preprocessing phases, and prints out the results. * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #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; } sparse-0.5.1/test-linearize.c000066400000000000000000000037421314543357600161360ustar00rootroot00000000000000/* * Parse and linearize the tree for testing. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #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); report_stats(); return 0; } sparse-0.5.1/test-parsing.c000066400000000000000000000046221314543357600156150ustar00rootroot00000000000000/* * 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 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #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; } sparse-0.5.1/test-sort.c000066400000000000000000000015241314543357600151370ustar00rootroot00000000000000#include "lib.h" #include "allocate.h" #include #include 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; } sparse-0.5.1/test-unssa.c000066400000000000000000000031661314543357600153050ustar00rootroot00000000000000#include #include #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%u\n", bb->nr); 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); report_stats(); return 0; } sparse-0.5.1/token.h000066400000000000000000000143271314543357600143250ustar00rootroot00000000000000#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 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #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, once; 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_CHAR_EMBEDDED_0, TOKEN_CHAR_EMBEDDED_1, TOKEN_CHAR_EMBEDDED_2, TOKEN_CHAR_EMBEDDED_3, TOKEN_WIDE_CHAR, TOKEN_WIDE_CHAR_EMBEDDED_0, TOKEN_WIDE_CHAR_EMBEDDED_1, TOKEN_WIDE_CHAR_EMBEDDED_2, TOKEN_WIDE_CHAR_EMBEDDED_3, 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:31; unsigned int immutable:1; 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 argnum; struct argcount count; char embedded[4]; }; }; #define MAX_STRING 8191 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, struct ident *); 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 const char *quote_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, unsigned 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 sparse-0.5.1/tokenize.c000066400000000000000000000565021314543357600150310ustar00rootroot00000000000000/* * 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 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include #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 ""; 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 ""; 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 ""; 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; } static const char *show_char(const char *s, size_t len, char prefix, char delim) { static char buffer[MAX_STRING + 4]; char *p = buffer; if (prefix) *p++ = prefix; *p++ = delim; memcpy(p, s, len); p += len; *p++ = delim; *p++ = '\0'; return buffer; } static const char *quote_char(const char *s, size_t len, char prefix, char delim) { static char buffer[2*MAX_STRING + 6]; size_t i; char *p = buffer; if (prefix) *p++ = prefix; if (delim == '"') *p++ = '\\'; *p++ = delim; for (i = 0; i < len; i++) { if (s[i] == '"' || s[i] == '\\') *p++ = '\\'; *p++ = s[i]; } if (delim == '"') *p++ = '\\'; *p++ = delim; *p++ = '\0'; return buffer; } const char *show_token(const struct token *token) { static char buffer[256]; if (!token) return ""; 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_NUMBER: return token->number; case TOKEN_SPECIAL: return show_special(token->special); case TOKEN_CHAR: return show_char(token->string->data, token->string->length - 1, 0, '\''); case TOKEN_CHAR_EMBEDDED_0 ... TOKEN_CHAR_EMBEDDED_3: return show_char(token->embedded, token_type(token) - TOKEN_CHAR, 0, '\''); case TOKEN_WIDE_CHAR: return show_char(token->string->data, token->string->length - 1, 'L', '\''); case TOKEN_WIDE_CHAR_EMBEDDED_0 ... TOKEN_WIDE_CHAR_EMBEDDED_3: return show_char(token->embedded, token_type(token) - TOKEN_WIDE_CHAR, 'L', '\''); case TOKEN_STRING: return show_char(token->string->data, token->string->length - 1, 0, '"'); case TOKEN_WIDE_STRING: return show_char(token->string->data, token->string->length - 1, 'L', '"'); case TOKEN_STREAMBEGIN: sprintf(buffer, "", stream_name(token->pos.stream)); return buffer; case TOKEN_STREAMEND: sprintf(buffer, "", stream_name(token->pos.stream)); return buffer; case TOKEN_UNTAINT: sprintf(buffer, ""); return buffer; case TOKEN_ARG_COUNT: sprintf(buffer, ""); return buffer; default: sprintf(buffer, "unhandled token type '%d' ", token_type(token)); return buffer; } } const char *quote_token(const struct token *token) { static char buffer[256]; switch (token_type(token)) { case TOKEN_ERROR: return "syntax error"; case TOKEN_IDENT: return show_ident(token->ident); case TOKEN_NUMBER: return token->number; case TOKEN_SPECIAL: return show_special(token->special); case TOKEN_CHAR: return quote_char(token->string->data, token->string->length - 1, 0, '\''); case TOKEN_CHAR_EMBEDDED_0 ... TOKEN_CHAR_EMBEDDED_3: return quote_char(token->embedded, token_type(token) - TOKEN_CHAR, 0, '\''); case TOKEN_WIDE_CHAR: return quote_char(token->string->data, token->string->length - 1, 'L', '\''); case TOKEN_WIDE_CHAR_EMBEDDED_0 ... TOKEN_WIDE_CHAR_EMBEDDED_3: return quote_char(token->embedded, token_type(token) - TOKEN_WIDE_CHAR, 'L', '\''); case TOKEN_STRING: return quote_char(token->string->data, token->string->length - 1, 0, '"'); case TOKEN_WIDE_STRING: return quote_char(token->string->data, token->string->length - 1, 'L', '"'); 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; restart: had_cr = had_backslash = 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) goto check_lf; if (c == '\r') { had_cr = 1; goto repeat; } norm: if (!had_backslash) { switch (c) { case '\t': stream->pos += tabstop - stream->pos % tabstop; break; case '\n': stream->line++; stream->pos = 0; stream->newline = 1; break; case '\\': had_backslash = 1; stream->pos++; goto repeat; default: stream->pos++; } } else { if (c == '\n') { stream->line++; stream->pos = 0; spliced = 1; goto restart; } offset--; c = '\\'; } out: stream->offset = offset; return c; check_lf: if (c != '\n') offset--; c = '\n'; goto norm; got_eof: if (had_backslash) { c = '\\'; goto out; } if (stream->pos) warning(stream_pos(stream), "no newline at end of file"); 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, Quote = 64, }; static const long cclass[257] = { ['0' + 1 ... '7' + 1] = Digit | Hex, /* \ */ ['8' + 1 ... '9' + 1] = Digit | Hex, ['A' + 1 ... 'D' + 1] = Letter | Hex, ['E' + 1] = Letter | Hex | Exp, /* E */ ['F' + 1] = Letter | Hex, ['G' + 1 ... 'O' + 1] = Letter, ['P' + 1] = Letter | Exp, /* P */ ['Q' + 1 ... 'Z' + 1] = Letter, ['a' + 1 ... 'b' + 1] = Letter | Hex, /* \a, \b */ ['c' + 1 ... 'd' + 1] = Letter | Hex, ['e' + 1] = Letter | Hex | Exp,/* \e, e */ ['f' + 1] = Letter | Hex, /* \f */ ['g' + 1 ... 'm' + 1] = Letter, ['n' + 1] = Letter, /* \n */ ['o' + 1] = Letter, ['p' + 1] = Letter | Exp, /* p */ ['q' + 1] = Letter, ['r' + 1] = Letter, /* \r */ ['s' + 1] = Letter, ['t' + 1] = Letter, /* \t */ ['u' + 1] = Letter, ['v' + 1] = Letter, /* \v */ ['w' + 1] = Letter, ['x' + 1] = Letter, /* \x */ ['y' + 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, ['\'' + 1] = Quote, ['"' + 1] = Quote, }; /* * 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 eat_string(int next, stream_t *stream, enum token_type type) { static char buffer[MAX_STRING]; struct string *string; struct token *token = stream->token; int len = 0; int escape; int want_hex = 0; char delim = type < TOKEN_STRING ? '\'' : '"'; for (escape = 0; escape || next != delim; next = nextchar(stream)) { if (len < MAX_STRING) buffer[len] = next; len++; if (next == '\n') { warning(stream_pos(stream), "Newline in string or character constant"); if (delim == '\'') /* assume it's lost ' */ break; } if (next == EOF) { warning(stream_pos(stream), "End of file in middle of string"); return next; } if (!escape) { if (want_hex && !(cclass[next + 1] & Hex)) warning(stream_pos(stream), "\\x used with no following hex digits"); want_hex = 0; escape = next == '\\'; } else { escape = 0; want_hex = next == 'x'; } } if (want_hex) warning(stream_pos(stream), "\\x used with no following hex digits"); if (len > MAX_STRING) { warning(stream_pos(stream), "string too long (%d bytes, %d bytes max)", len, MAX_STRING); len = MAX_STRING; } if (delim == '\'' && len <= 4) { if (len == 0) { sparse_error(stream_pos(stream), "empty character constant"); return nextchar(stream); } token_type(token) = type + len; memset(buffer + len, '\0', 4 - len); memcpy(token->embedded, buffer, 4); } else { token_type(token) = type; string = __alloc_string(len+1); memcpy(string->data, buffer, len); string->data[len] = '\0'; string->length = len+1; token->string = string; } /* Pass it on.. */ token = stream->token; add_token(stream); return nextchar(stream); } 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 eat_string(next, stream, TOKEN_STRING); case '\'': return eat_string(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) + (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, struct ident *ident) { struct token *token; token = __alloc_token(0); token->pos.stream = stream; token_type(token) = TOKEN_IDENT; token->ident = ident; 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++; }; if (cclass[next + 1] & Quote) { if (len == 1 && buf[0] == 'L') { if (next == '\'') return eat_string(nextchar(stream), stream, TOKEN_WIDE_CHAR); else return eat_string(nextchar(stream), stream, TOKEN_WIDE_STRING); } } hash = ident_hash_end(hash); ident = create_hashed_ident(buf, len, hash); /* 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; } sparse-0.5.1/unssa.c000066400000000000000000000071721314543357600143310ustar00rootroot00000000000000/* * 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. * This is particulary easy since these copies are essentialy already present * as the corresponding OP_PHISOURCE. * * While very simple this method create a lot more copies that really necessary. * We eliminate some of these copies but most probably most of them are still * useless. * 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. * But for this we need precise liveness, on each %phi and not only on OP_PHI's * target pseudos. * * Copyright (C) 2005 Luc Van Oostenryck */ #include "lib.h" #include "linearize.h" #include "allocate.h" #include "flow.h" #include static inline int nbr_pseudo_users(pseudo_t p) { return ptr_list_size((struct ptr_list *)p->users); } static int simplify_phi_node(struct instruction *phi, pseudo_t tmp) { pseudo_t target = phi->target; struct pseudo_user *pu; pseudo_t src; // verify if this phi can be simplified FOR_EACH_PTR(phi->phi_list, src) { struct instruction *def = src->def; if (!def) continue; if (def->bb == phi->bb) return 0; } END_FOR_EACH_PTR(src); // no need to make a copy of this one // -> replace the target pseudo by the tmp FOR_EACH_PTR(target->users, pu) { use_pseudo(pu->insn, tmp, pu->userp); } END_FOR_EACH_PTR(pu); phi->bb = NULL; return 1; } static void replace_phi_node(struct instruction *phi) { pseudo_t tmp; pseudo_t p; tmp = alloc_pseudo(NULL); tmp->type = phi->target->type; tmp->ident = phi->target->ident; tmp->def = NULL; // defined by all the phisrc // can we avoid to make of copy? simplify_phi_node(phi, tmp); // rewrite all it's phi_src to copy to a new tmp FOR_EACH_PTR(phi->phi_list, p) { struct instruction *def = p->def; pseudo_t src; if (p == VOID) continue; assert(def->opcode == OP_PHISOURCE); def->opcode = OP_COPY; def->target = tmp; // can we eliminate the copy? src = def->phi_src; if (src->type != PSEUDO_REG) continue; switch (nbr_pseudo_users(src)) { struct instruction *insn; case 1: insn = src->def; if (!insn) break; insn->target = tmp; case 0: kill_instruction(def); def->bb = NULL; } } END_FOR_EACH_PTR(p); if (!phi->bb) return; // rewrite the phi node: // phi %rt, ... // to: // copy %rt, %tmp phi->opcode = OP_COPY; use_pseudo(phi, tmp, &phi->src); } 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); } int unssa(struct entrypoint *ep) { struct basic_block *bb; FOR_EACH_PTR(ep->bbs, bb) { rewrite_phi_bb(bb); } END_FOR_EACH_PTR(bb); return 0; } sparse-0.5.1/validation/000077500000000000000000000000001314543357600151575ustar00rootroot00000000000000sparse-0.5.1/validation/.gitignore000066400000000000000000000000451314543357600171460ustar00rootroot00000000000000# test-suite *.diff *.got *.expected sparse-0.5.1/validation/Woverride-init-def.c000066400000000000000000000004561314543357600207730ustar00rootroot00000000000000static int array[] = { [1] = 3, [1] = 1, /* check-should-warn */ }; /* * check-name: Woverride-init-def * check-command: sparse $file * * check-error-start Woverride-init-def.c:2:10: warning: Initializer entry defined twice Woverride-init-def.c:3:10: also defined here * check-error-end */ sparse-0.5.1/validation/Woverride-init-no.c000066400000000000000000000003151314543357600206430ustar00rootroot00000000000000static int array[] = { [1] = 3, [1] = 1, /* check-should-warn */ }; /* * check-name: Woverride-init-no * check-command: sparse -Wno-override-init $file * * check-error-start * check-error-end */ sparse-0.5.1/validation/Woverride-init-yes.c000066400000000000000000000004761314543357600210370ustar00rootroot00000000000000static int array[] = { [1] = 3, [1] = 1, /* check-should-warn */ }; /* * check-name: Woverride-init-yes * check-command: sparse -Woverride-init $file * * check-error-start Woverride-init-yes.c:2:10: warning: Initializer entry defined twice Woverride-init-yes.c:3:10: also defined here * check-error-end */ sparse-0.5.1/validation/Wunknown-attribute-def.c000066400000000000000000000003531314543357600217070ustar00rootroot00000000000000static int foo(void) __attribute__((unknown_attribute)); /* * check-name: warn-unknown-attribute * * check-error-start Wunknown-attribute-def.c:1:37: warning: attribute 'unknown_attribute': unknown attribute * check-error-end */ sparse-0.5.1/validation/Wunknown-attribute-no.c000066400000000000000000000003131314543357600215610ustar00rootroot00000000000000static int foo(void) __attribute__((unknown_attribute)); /* * check-name: warn-unknown-attribute-no * check-command: sparse -Wno-unknown-attribute $file * * check-error-start * check-error-end */ sparse-0.5.1/validation/Wunknown-attribute-yes.c000066400000000000000000000004421314543357600217500ustar00rootroot00000000000000static int foo(void) __attribute__((unknown_attribute)); /* * check-name: warn-unknown-attribute-yes * check-command: sparse -Wunknown-attribute $file * * check-error-start Wunknown-attribute-yes.c:1:37: warning: attribute 'unknown_attribute': unknown attribute * check-error-end */ sparse-0.5.1/validation/__func__.c000066400000000000000000000005051314543357600170520ustar00rootroot00000000000000static void f(void) { char *s1 = __func__; char arr[2 * (sizeof __func__ == 2) - 1]; char *s2 = __func__ __func__; } /* * check-name: __func__ * check-command: sparse -Wall $file * * check-error-start __func__.c:5:29: error: Expected ; at end of declaration __func__.c:5:29: error: got __func__ * check-error-end */ sparse-0.5.1/validation/abstract-array-declarator-static.c000066400000000000000000000010261314543357600236440ustar00rootroot00000000000000 extern void f1(int g[static 1]); extern void f2(int g[static restrict 1]); extern void f3(int g[restrict static 1]); extern void f4(int g[static restrict static 1]); /* duplicate static error */ extern void f5(int g[restrict static static 1]); /* duplicate static error */ /* * check-name: abstract array declarator static * check-error-start abstract-array-declarator-static.c:5:38: error: duplicate array static declarator abstract-array-declarator-static.c:6:38: error: duplicate array static declarator * check-error-end */ sparse-0.5.1/validation/address_space.c000066400000000000000000000006741314543357600201320ustar00rootroot00000000000000#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 *user_addr * check-error-end */ sparse-0.5.1/validation/alias-distinct.c000066400000000000000000000003561314543357600202370ustar00rootroot00000000000000extern int g; extern int h; static int foo(void) { g = 1; h = 2; return g == 1; } /* * check-name: alias distinct symbols * check-command: test-linearize $file * check-output-ignore * * check-output-contains: ret\\..* *\\$1 */ sparse-0.5.1/validation/alias-mixed.c000066400000000000000000000005201314543357600175150ustar00rootroot00000000000000extern int g; static int foo(int *p) { *p = 1; g = 2; return *p == 1; } static int bar(int *p) { g = 1; *p = 2; return g == 1; } static void test(void) { foo(&g); bar(&g); } /* * check-name: alias symbol/pointer * check-command: test-linearize $file * check-output-ignore * * check-output-excludes: ret\\..* *\\$1 */ sparse-0.5.1/validation/alias-same.c000066400000000000000000000003351314543357600173400ustar00rootroot00000000000000extern int g; static int foo(void) { g = 1; g = 2; return g != 1; } /* * check-name: alias same symbols * check-command: test-linearize $file * check-output-ignore * * check-output-contains: ret\\..* *\\$1 */ sparse-0.5.1/validation/alloc-align.c000066400000000000000000000024301314543357600175040ustar00rootroot00000000000000typedef unsigned long int size_t; /* * The alloc_align attribute is used to tell the compiler that the return * value points to memory, where the returned pointer minimum alignment is given * by one of the functions parameters. GCC uses this information to improve * pointer alignment analysis. * * The function parameter denoting the allocated alignment is specified by one * integer argument, whose number is the argument of the attribute. Argument * numbering starts at one. * * For instance, * * void* my_memalign(size_t, size_t) __attribute__((alloc_align(1))) * * declares that my_memalign returns memory with minimum alignment given by * parameter 1. */ #define __alloc_align(x) __attribute__((__alloc_align__(x))) /* * The aligned_alloc function allocates space for an object whose alignment is * specified by alignment, whose size is specified by size, and whose value is * indeterminate. The value of alignment shall be a valid alignment supported * by the implementation and the value of size shall be an integral multiple * of alignment. * * The aligned_alloc function returns either a null pointer or a pointer to the * allocated space. */ void *aligned_alloc(size_t alignment, size_t size) __alloc_align(1); /* * check-name: attribute __alloc_align__ */ sparse-0.5.1/validation/alternate-keywords.c000066400000000000000000000032171314543357600211520ustar00rootroot00000000000000 extern float strtof(const char *__restrict__ ptr, char **__restrict__ endptr); extern double strtod(const char *__restrict ptr, char **__restrict endptr); /* restrict: -std=c99 or -std=gnu99 or -std=c11 */ extern long double strtold(const char *restrict ptr, char **restrict endptr); extern int (*funcs[])(void); /* typeof: no -std or -std=gnu90 or -std=gnu99 or -std=gnu11 */ extern typeof (funcs[0]) f0; extern __typeof (funcs[1]) f1; extern __typeof__(funcs[2]) f2; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; static __inline__ uint16_t swap16(uint16_t val) { return ((((uint16_t)(val) & (uint16_t)0x00ffU) << 8) | (((uint16_t)(val) & (uint16_t)0xff00U) >> 8)); } static __inline uint32_t swap32(uint32_t val) { return ((((uint32_t)(val) & (uint32_t)0x000000ffUL) << 24) | (((uint32_t)(val) & (uint32_t)0x0000ff00UL) << 8) | (((uint32_t)(val) & (uint32_t)0x00ff0000UL) >> 8) | (((uint32_t)(val) & (uint32_t)0xff000000UL) >> 24)); } /* inline: no -std or -std=gnu90 or -std=c99 or -std=c11 */ static inline uint64_t swap64(uint64_t val) { return ((((uint64_t)(val) & (uint64_t)0x00000000000000ffULL) << 56) | (((uint64_t)(val) & (uint64_t)0x000000000000ff00ULL) << 40) | (((uint64_t)(val) & (uint64_t)0x0000000000ff0000ULL) << 24) | (((uint64_t)(val) & (uint64_t)0x00000000ff000000ULL) << 8) | (((uint64_t)(val) & (uint64_t)0x000000ff00000000ULL) >> 8) | (((uint64_t)(val) & (uint64_t)0x0000ff0000000000ULL) >> 24) | (((uint64_t)(val) & (uint64_t)0x00ff000000000000ULL) >> 40) | (((uint64_t)(val) & (uint64_t)0xff00000000000000ULL) >> 56)); } /* * check-name: alternate keywords */ sparse-0.5.1/validation/anon-union.c000066400000000000000000000002401314543357600174000ustar00rootroot00000000000000struct s { union { int val; }; }; static struct s foo = { .val = 5, }; /* * check-name: test anonymous union initializer */ sparse-0.5.1/validation/asm-empty-clobber.c000066400000000000000000000015711314543357600206510ustar00rootroot00000000000000 # 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. */ sparse-0.5.1/validation/asm-goto-lables.c000066400000000000000000000012251314543357600203110ustar00rootroot00000000000000static 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. */ sparse-0.5.1/validation/asm-toplevel.c000066400000000000000000000002541314543357600177340ustar00rootroot00000000000000__asm__("/* nothing */"); /* * check-name: asm-toplevel.c * check-command: test-linearize $file * check-output-ignore * check-output-contains: asm *".. nothing .." */ sparse-0.5.1/validation/attr-inline.c000066400000000000000000000005741314543357600175570ustar00rootroot00000000000000 static inline __attribute__((__always_inline__)) int gt(int lhs, int rhs) { return lhs > rhs; } extern inline __attribute__((__gnu_inline__)) int ge(int lhs, int rhs) { return lhs >= rhs; } static __attribute__((__warning__("That's junk!"))) __attribute__((__unused__)) __attribute__((__noinline__)) void junk(void) { __asm__(""); } /* * check-name: inline attributes */ sparse-0.5.1/validation/attr-no_sanitize_address.c000066400000000000000000000002511314543357600223200ustar00rootroot00000000000000#define __no_sanitize_address __attribute__((no_sanitize_address)) static void __no_sanitize_address bar(void) { } /* * check-name: attribute no_sanitize_address */ sparse-0.5.1/validation/attr-noclone.c000066400000000000000000000001721314543357600177300ustar00rootroot00000000000000#define noclone __attribute__((__noclone__)) static void noclone bar(void) { } /* * check-name: attribute noclone */ sparse-0.5.1/validation/attr-optimize.c000066400000000000000000000004021314543357600201270ustar00rootroot00000000000000 #define __noclone __attribute__((__noclone__, __optimize__("no-tracer"))) struct kvm_vcpu; static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu) { __asm__(""); } extern void *run; void *run = vmx_vcpu_run; /* * check-name: optimize attributes */ sparse-0.5.1/validation/attr-warning.c000066400000000000000000000002621314543357600177400ustar00rootroot00000000000000# define __warndecl(name, msg) \ extern void name (void) __attribute__((__warning__ (msg))) __warndecl (__warn_func, "warn message"); /* * check-name: attribute warning */ sparse-0.5.1/validation/attr_aligned.c000066400000000000000000000002421314543357600177560ustar00rootroot00000000000000void *foo(void) __attribute__((__assume_aligned__(4096))); void *foo(void) __attribute__((assume_aligned(4096))); /* * check-name: attribute assume_aligned */ sparse-0.5.1/validation/attr_in_parameter.c000066400000000000000000000003551314543357600210260ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/attr_vector_size.c000066400000000000000000000001771314543357600207160ustar00rootroot00000000000000typedef unsigned int u32; typedef u32 __attribute__((vector_size(16))) sse128_t; /* * check-name: attribute vector_size */ sparse-0.5.1/validation/backend/000077500000000000000000000000001314543357600165465ustar00rootroot00000000000000sparse-0.5.1/validation/backend/arithmetic-ops.c000066400000000000000000000022501314543357600216410ustar00rootroot00000000000000static int add(int x, int y) { return x + y; } static unsigned int uadd(unsigned int x, unsigned int y) { return x + y; } static float fadd(float x, float y) { return x + y; } static double dadd(double x, double y) { return x + y; } static int sub(int x, int y) { return x - y; } static unsigned int usub(unsigned int x, unsigned int y) { return x - y; } static float fsub(float x, float y) { return x - y; } static double dsub(double x, double y) { return x - y; } static int mul(int x, int y) { return x * y; } static unsigned int umul(unsigned int x, unsigned int y) { return x * y; } static float fmul(float x, float y) { return x * y; } static double dmul(double x, double y) { return x * y; } static int div(int x, int y) { return x / y; } static unsigned int udiv(unsigned int x, unsigned int y) { return x / y; } static float fdiv(float x, float y) { return x / y; } static double ddiv(double x, double y) { return x / y; } static int mod(int x, int y) { return x % y; } static unsigned int umod(unsigned int x, unsigned int y) { return x % y; } /* * check-name: Arithmetic operator code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/array.c000066400000000000000000000001611314543357600200260ustar00rootroot00000000000000static char array[128]; /* * check-name: Array code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/bitwise-ops.c000066400000000000000000000014741314543357600211650ustar00rootroot00000000000000static int shl(int x, int y) { return x << y; } static unsigned int ushl(unsigned int x, unsigned int y) { return x << y; } static int shr(int x, int y) { return x >> y; } static unsigned int ushr(unsigned int x, unsigned int y) { return x >> y; } static int and(int x, int y) { return x & y; } static unsigned int uand(unsigned int x, unsigned int y) { return x & y; } static int or(int x, int y) { return x | y; } static unsigned int uor(unsigned int x, unsigned int y) { return x | y; } static int xor(int x, int y) { return x ^ y; } static unsigned int uxor(unsigned int x, unsigned int y) { return x ^ y; } static int not(int x) { return ~x; } static unsigned int unot(unsigned int x) { return ~x; } /* * check-name: Bitwise operator code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/bool-test.c000066400000000000000000000002171314543357600206220ustar00rootroot00000000000000static _Bool return_false(void) { return 0; } /* * check-name: Boolean type code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/cast.c000066400000000000000000000020561314543357600176470ustar00rootroot00000000000000typedef _Bool bool; typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; typedef long long longlong; typedef unsigned long long ulonglong; #define DEFINE_CAST(from, to) \ static to from##2##to(from x) { \ return x; \ } #define DEFINE_CASTS(from) \ DEFINE_CAST(from, bool) \ DEFINE_CAST(from, char) \ DEFINE_CAST(from, uchar) \ DEFINE_CAST(from, short) \ DEFINE_CAST(from, ushort) \ DEFINE_CAST(from, int) \ DEFINE_CAST(from, uint) \ DEFINE_CAST(from, long) \ DEFINE_CAST(from, ulong) \ DEFINE_CAST(from, longlong) \ DEFINE_CAST(from, ulonglong) \ /* DEFINE_CAST(from, float) \ DEFINE_CAST(from, double) */ DEFINE_CASTS(bool) DEFINE_CASTS(char) DEFINE_CASTS(uchar) DEFINE_CASTS(short) DEFINE_CASTS(ushort) DEFINE_CASTS(int) DEFINE_CASTS(uint) DEFINE_CASTS(long) DEFINE_CASTS(ulong) DEFINE_CASTS(longlong) DEFINE_CASTS(ulonglong) /* DEFINE_CASTS(float) DEFINE_CASTS(double) */ /* * check-name: Cast code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/cmp-ops.c000066400000000000000000000017761314543357600203030ustar00rootroot00000000000000static int sete(int x, int y) { return x == y; } static int setne(int x, int y) { return x != y; } static int setl(int x, int y) { return x < y; } static int setg(int x, int y) { return x > y; } static int setle(int x, int y) { return x <= y; } static int setge(int x, int y) { return x >= y; } static int setb(unsigned int x, unsigned int y) { return x < y; } static int seta(unsigned int x, unsigned int y) { return x > y; } static int setbe(unsigned int x, unsigned int y) { return x <= y; } static int setae(unsigned int x, unsigned int y) { return x >= y; } static int setfe(float x, float y) { return x == y; } static int setfne(float x, float y) { return x != y; } static int setfl(float x, float y) { return x < y; } static int setfg(float x, float y) { return x > y; } static int setfle(float x, float y) { return x <= y; } static int setfge(float x, float y) { return x >= y; } /* * check-name: Comparison operator code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/extern.c000066400000000000000000000002541314543357600202200ustar00rootroot00000000000000extern unsigned long foo; static unsigned long bar(void) { return foo; } /* * check-name: Extern symbol code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/function-ptr.c000066400000000000000000000003041314543357600213370ustar00rootroot00000000000000typedef int (*fn_t)(int x, int y); static int run(fn_t fn, int x, int y) { return fn(x, y); } /* * check-name: Function pointer code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/hello.c000066400000000000000000000002761314543357600200220ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { puts("hello, world"); return 0; } /* * check-name: 'hello, world' code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/int-cond.c000066400000000000000000000006411314543357600204260ustar00rootroot00000000000000static long foo(long a, long b, long c) { return a? b:c; } static long foo_bool(_Bool a, long b, long c) { return a? b:c; } static long bar(long a, long b, long c) { if (a) return b; else return b + c; } static long bar_bool(_Bool a, long b, long c) { if (a) return b; else return b + c; } /* * check-name: Non-bool condition values in branch/select * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/load-type.c000066400000000000000000000003221314543357600206050ustar00rootroot00000000000000extern struct _IO_FILE *stdin; static void sub(struct _IO_FILE *in) {} static void test(void) { sub(stdin); } /* * check-name: Type of loaded objects * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/logical-ops.c000066400000000000000000000005651314543357600211310ustar00rootroot00000000000000static int and_bool(int x, int y) { return x && y; } static unsigned int uand_bool(unsigned int x, unsigned int y) { return x && y; } static int or_bool(int x, int y) { return x || y; } static unsigned int uor_bool(unsigned int x, unsigned int y) { return x || y; } /* * check-name: Logical operator code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/loop.c000066400000000000000000000003321314543357600176610ustar00rootroot00000000000000 extern int bar (int); extern int foo (int); int foo (int x) { int y = 0; while (y < 1000) { y += bar(x); } return y; } /* * check-name: Loops * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/loop2.c000066400000000000000000000002671314543357600177520ustar00rootroot00000000000000extern int op(void); static void test(void) { int i; for (i = 0; ; i++) { op(); } } /* * check-name: Loops with unused counter * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/ptrcast.c000066400000000000000000000002501314543357600203670ustar00rootroot00000000000000static char *ptrcast(unsigned long *x) { return (unsigned char *) x; } /* * check-name: Pointer cast code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/store-type.c000066400000000000000000000002621314543357600210250ustar00rootroot00000000000000struct foo; static struct foo *var; static void set(struct foo *f) { var = f; } /* * check-name: Type of stored objects * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/struct-access.c000066400000000000000000000005361314543357600215010ustar00rootroot00000000000000struct st { int i, *d; }; static int load_i(struct st *st) { return st->i; } static void store_i(struct st *st, int i) { st->i = i; } static int *load_d(struct st *st) { return st->d; } static void store_d(struct st *st, int *d) { st->d = d; } /* * check-name: struct access code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/struct.c000066400000000000000000000006001314543357600202320ustar00rootroot00000000000000struct ctype { int type; }; struct symbol { void *p; const char *name; struct ctype ctype; struct symbol *next_id; }; struct unnamed { struct { int x, y; }; }; static struct symbol sym; static struct symbol *sym_p; static struct symbol *sym_q = &sym; static struct unnamed un; /* * check-name: Struct code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/sum.c000066400000000000000000000005601314543357600175170ustar00rootroot00000000000000#include #include static int sum(int n) { int i, result = 0; for (i = 1; i <= n; ++i) result += i; return result; } int main(int argc, char **argv) { printf("%d\n", sum(5)); printf("%d\n", sum(100)); return 0; } /* * check-name: sum from 1 to n * check-command: sparsei $file * * check-output-start 15 5050 * check-output-end */ sparse-0.5.1/validation/backend/union.c000066400000000000000000000002671314543357600200470ustar00rootroot00000000000000union foo { unsigned long x; unsigned char y; char buf[128]; }; static union foo foo; /* * check-name: Union code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/backend/void-return-type.c000066400000000000000000000002501314543357600221440ustar00rootroot00000000000000static void foo(void) { } static void *bar(void *p) { return p; } /* * check-name: void return type code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/bad-array-designated-initializer.c000066400000000000000000000005571314543357600236220ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/bad-assignment.c000066400000000000000000000003401314543357600202140ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/bad-cast.c000066400000000000000000000004261314543357600170030ustar00rootroot00000000000000struct 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 */ sparse-0.5.1/validation/bad-ternary-cond.c000066400000000000000000000004371314543357600204600ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/bad-typeof.c000066400000000000000000000003251314543357600173550ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/badtype1.c000066400000000000000000000002751314543357600170400ustar00rootroot00000000000000static void foo(enum bar baz); /* * check-name: enum not in scope * check-known-to-fail * * check-error-start badtype1.c:1:22: warning: bad scope for 'enum bar' * check-error-end */ sparse-0.5.1/validation/badtype2.c000066400000000000000000000012201314543357600170300ustar00rootroot00000000000000//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:8: warning: 'undef' has implicit type 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 */ sparse-0.5.1/validation/badtype3.c000066400000000000000000000014761314543357600170460ustar00rootroot00000000000000int 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 */ sparse-0.5.1/validation/badtype4.c000066400000000000000000000004051314543357600170360ustar00rootroot00000000000000void 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 */ sparse-0.5.1/validation/badtype5.c000066400000000000000000000007371314543357600170470ustar00rootroot00000000000000#define __force __attribute__((force)) int foo(int *addr); int foo(int *addr) { return *(*((typeof(addr) __force *) addr)); } /* * check-name: badtype5.c * check-description: * evaluate_dereference() used to miss a call to * examine_symbol_type(). This, in the present, left * a SYM_TYPEOF as type for the last dereferencing * which produced "error: cannot dereference this type". * The presence of the __force and the typeof is needed * to create the situation. */ sparse-0.5.1/validation/binary-constant.c000066400000000000000000000001041314543357600204310ustar00rootroot00000000000000extern int x; int x = 0b11; /* * check-name: binary constant */ sparse-0.5.1/validation/bitfield-size.c000066400000000000000000000021251314543357600200550ustar00rootroot00000000000000struct bfu { unsigned int a:4; unsigned int :2; unsigned int b:4; }; unsigned int get__bfu_a(struct bfu bf) { return bf.a; } unsigned int get__bfu_b(struct bfu bf) { return bf.b; } unsigned int get_pbfu_a(struct bfu *bf) { return bf->a; } unsigned int get_pbfu_b(struct bfu *bf) { return bf->b; } struct bfs { signed int a:4; signed int :2; signed int b:4; }; signed int get__bfs_a(struct bfs bf) { return bf.a; } signed int get__bfs_b(struct bfs bf) { return bf.b; } signed int get_pbfs_a(struct bfs *bf) { return bf->a; } signed int get_pbfs_b(struct bfs *bf) { return bf->b; } struct bfi { int a:4; int :2; int b:4; }; unsigned int get__bfi_a(struct bfi bf) { return bf.a; } unsigned int get__bfi_b(struct bfi bf) { return bf.b; } unsigned int get_pbfi_a(struct bfi *bf) { return bf->a; } unsigned int get_pbfi_b(struct bfi *bf) { return bf->b; } /* * check-name: bitfield size * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-pattern-24-times: cast\\. * check-output-pattern-12-times: cast\\.4 * check-output-pattern-6-times: lsr\\..*\\$6 */ sparse-0.5.1/validation/bitfields.c000066400000000000000000000004641314543357600172740ustar00rootroot00000000000000/* * 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 */ sparse-0.5.1/validation/bitwise-cast.c000066400000000000000000000017141314543357600177240ustar00rootroot00000000000000typedef unsigned int u32; typedef u32 __attribute__((bitwise)) __be32; /* Implicit casts of 0, legal */ static __be32 foo(void) { __be32 x = 0; return 0; } /* Explicit cast of 0, legal */ static __be32 bar(void) { return (__be32)0; } /* Implicit casts of nonzero, bad */ static __be32 baz(void) { __be32 x = 0x2a; return 99; } /* Explicit cast of nonzero, bad */ static __be32 quux(void) { return (__be32)1729; } /* * check-name: conversions to bitwise types * check-command: sparse -Wbitwise $file * check-error-start bitwise-cast.c:21:20: warning: incorrect type in initializer (different base types) bitwise-cast.c:21:20: expected restricted __be32 [usertype] x bitwise-cast.c:21:20: got int bitwise-cast.c:23:16: warning: incorrect type in return expression (different base types) bitwise-cast.c:23:16: expected restricted __be32 bitwise-cast.c:23:16: got int bitwise-cast.c:29:17: warning: cast to restricted __be32 * check-error-end */ sparse-0.5.1/validation/bool-array.c000066400000000000000000000014771314543357600174030ustar00rootroot00000000000000static _Bool boolarray_d1[1]; static _Bool boolarray_d8[8]; static _Bool boolarray_i2[2] = { 0, 1, }; static int nd1 = sizeof(boolarray_d1); static int nd8 = sizeof(boolarray_d8); static int ni2 = sizeof(boolarray_i2); static long longarray_u2[] = { 0, 1, }; static int nl2 = sizeof(longarray_u2); /* * Used to get "warning: excessive elements in array initializer" * for all elements but the first one. * Note: only occurs if nbr of elements is a multiple of 8 * (if not, theer was another problem) */ static _Bool boolarray_u8[] = { 0, 1, 0, 1, 0, 1, 0, 1, }; /* * Used to get "error: cannot size expression" for the sizeof. */ static _Bool boolarray_u2[] = { 0, 1, }; static int nu2 = sizeof(boolarray_u2); /* * check-name: sizeof(bool array) * check-command: sparse -Wno-sizeof-bool $file */ sparse-0.5.1/validation/bool-cast-bad.c000066400000000000000000000011671314543357600177370ustar00rootroot00000000000000typedef unsigned short __attribute__((bitwise)) le16; struct s { int a:2; int b:2; int c:2; }; static _Bool fresi(le16 a) { return a; } static _Bool frese(le16 a) { return (_Bool)a; } static _Bool fstsi(struct s a) { return a; } static _Bool fstse(struct s a) { return (_Bool)a; } /* * check-name: bool-cast-bad.c * check-command: sparse $file * * check-error-start bool-cast-bad.c:10:41: warning: incorrect type in return expression (different base types) bool-cast-bad.c:10:41: expected bool bool-cast-bad.c:10:41: got struct s a bool-cast-bad.c:11:42: warning: cast from non-scalar * check-error-end */ sparse-0.5.1/validation/bool-cast-explicit.c000066400000000000000000000011731314543357600210270ustar00rootroot00000000000000typedef unsigned int u32; typedef int s32; typedef void *vdp; typedef int *sip; typedef double dbl; typedef unsigned short __attribute__((bitwise)) le16; static _Bool fs32(s32 a) { return (_Bool)a; } static _Bool fu32(u32 a) { return (_Bool)a; } static _Bool fvdp(vdp a) { return (_Bool)a; } static _Bool fsip(sip a) { return (_Bool)a; } static _Bool fdbl(dbl a) { return (_Bool)a; } static _Bool ffun(void) { return (_Bool)ffun; } static _Bool fres(le16 a) { return (_Bool)a; } /* * check-name: bool-cast-explicit * check-command: test-linearize -m64 $file * check-output-ignore * check-output-excludes: cast\\. */ sparse-0.5.1/validation/bool-cast-implicit.c000066400000000000000000000011651314543357600210210ustar00rootroot00000000000000typedef unsigned int u32; typedef int s32; typedef void *vdp; typedef int *sip; typedef double dbl; typedef unsigned short __attribute__((bitwise)) le16; static _Bool fs32(s32 a) { return a; } static _Bool fu32(u32 a) { return a; } static _Bool fvdp(vdp a) { return a; } static _Bool fsip(sip a) { return a; } static _Bool fdbl(dbl a) { return a; } static _Bool ffun(void) { return ffun; } static _Bool fres(le16 a) { return a; } /* * check-name: bool-cast-implicit * check-command: test-linearize -m64 $file * check-output-ignore * check-output-excludes: cast\\. * * check-error-start * check-error-end */ sparse-0.5.1/validation/bool-cast-restricted.c000066400000000000000000000032001314543357600213470ustar00rootroot00000000000000typedef unsigned int __attribute__((bitwise)) large_t; #define LBIT ((__attribute__((force)) large_t) 1) _Bool lfoo(large_t x) { return x; } _Bool qfoo(large_t x) { _Bool r = x; return r; } _Bool xfoo(large_t x) { return (_Bool)x; } _Bool lbar(large_t x) { return ~x; } _Bool qbar(large_t x) { _Bool r = ~x; return r; } _Bool xbar(large_t x) { return (_Bool)~x; } _Bool lbaz(large_t x) { return !x; } _Bool qbaz(large_t x) { _Bool r = !x; return r; } _Bool xbaz(large_t x) { return (_Bool)!x; } _Bool lqux(large_t x) { return x & LBIT; } _Bool qqux(large_t x) { _Bool r = x & LBIT; return r; } _Bool xqux(large_t x) { return (_Bool)(x & LBIT); } typedef unsigned short __attribute__((bitwise)) small_t; #define SBIT ((__attribute__((force)) small_t) 1) _Bool sfoo(small_t x) { return x; } _Bool tfoo(small_t x) { _Bool r = x; return r; } _Bool zfoo(small_t x) { return (_Bool)x; } _Bool sbar(small_t x) { return ~x; } _Bool tbar(small_t x) { _Bool r = ~x; return r; } _Bool zbar(small_t x) { return (_Bool)~x; } _Bool sbaz(small_t x) { return !x; } _Bool tbaz(small_t x) { _Bool r = !x; return r; } _Bool zbaz(small_t x) { return (_Bool)!x; } _Bool squx(small_t x) { return x & SBIT; } _Bool tqux(small_t x) { _Bool r = x & SBIT; return r; } _Bool zqux(small_t x) { return (_Bool)(x & SBIT); } /* * check-name: bool-cast-restricted.c * check-command: sparse -Wno-decl $file * * check-error-start bool-cast-restricted.c:24:32: warning: restricted small_t degrades to integer bool-cast-restricted.c:25:35: warning: restricted small_t degrades to integer bool-cast-restricted.c:26:33: warning: restricted small_t degrades to integer * check-error-end */ sparse-0.5.1/validation/bswap-constant-folding.c000066400000000000000000000011171314543357600217060ustar00rootroot00000000000000typedef unsigned short __be16; typedef unsigned short __u16; typedef unsigned short u16; #define __force #define __swab16(x) (__u16)__builtin_bswap16((__u16)(x)) /* the test behaves as though it's always on a little-endian machine */ #define __cpu_to_be16(x) ((__force __be16)__swab16((x))) #define ___htons(x) __cpu_to_be16(x) #define htons(x) ___htons(x) #define ETH_P_IPV6 0x86DD static u16 protocol; static void test(void) { switch (protocol) { case htons(ETH_P_IPV6): break; } } /* * check-name: constant folding in bswap builtins * check-error-start * check-error-end */ sparse-0.5.1/validation/bug_inline_switch.c000066400000000000000000000005301314543357600210150ustar00rootroot00000000000000 #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 */ sparse-0.5.1/validation/builtin-args-checking.c000066400000000000000000000034151314543357600214770ustar00rootroot00000000000000static unsigned int bad_nbr_args_cte(int a) { int r = 0; r |= __builtin_bswap16(); r |= __builtin_bswap16(1, 2); r |= __builtin_bswap32(); r |= __builtin_bswap32(1, 2); r |= __builtin_bswap64(); r |= __builtin_bswap64(1, 2); return r; } static unsigned int bad_nbr_args_var(int a, int b) { int r = 0; r |= __builtin_bswap16(); r |= __builtin_bswap16(a, b); r |= __builtin_bswap32(); r |= __builtin_bswap32(a, b); r |= __builtin_bswap64(); r |= __builtin_bswap64(a, b); return r; } /* * check-name: builtin-args-checking * check-command: sparse $file * check-description: Check that the arguments checking is done * for expanded builtins with a prototype. * * check-error-start builtin-args-checking.c:4:31: error: not enough arguments for function __builtin_bswap16 builtin-args-checking.c:5:31: error: too many arguments for function __builtin_bswap16 builtin-args-checking.c:6:31: error: not enough arguments for function __builtin_bswap32 builtin-args-checking.c:7:31: error: too many arguments for function __builtin_bswap32 builtin-args-checking.c:8:31: error: not enough arguments for function __builtin_bswap64 builtin-args-checking.c:9:31: error: too many arguments for function __builtin_bswap64 builtin-args-checking.c:16:31: error: not enough arguments for function __builtin_bswap16 builtin-args-checking.c:17:31: error: too many arguments for function __builtin_bswap16 builtin-args-checking.c:18:31: error: not enough arguments for function __builtin_bswap32 builtin-args-checking.c:19:31: error: too many arguments for function __builtin_bswap32 builtin-args-checking.c:20:31: error: not enough arguments for function __builtin_bswap64 builtin-args-checking.c:21:31: error: too many arguments for function __builtin_bswap64 * check-error-end */ sparse-0.5.1/validation/builtin-bswap-constant.c000066400000000000000000000013351314543357600217340ustar00rootroot00000000000000unsigned short bswap16(void); unsigned short bswap16(void) { return __builtin_bswap16(0x1234); } unsigned int bswap32(void); unsigned int bswap32(void) { return __builtin_bswap32(0x12345678); } unsigned long long bswap64(void); unsigned long long bswap64(void) { return __builtin_bswap64(0x123456789abcdef0ULL); } unsigned int half_constant(void); unsigned int half_constant(void) { int v = 0x12345678; return __builtin_bswap32(v); } /* * check-name: builtin-bswap-constant * check-command: test-linearize $file * * check-output-ignore * check-output-excludes: __builtin_bswap * check-output-contains:ret.16 *.0x3412 * check-output-contains:ret.32 *.0x78563412 * check-output-contains:ret.64 *.0xf0debc9a78563412 */ sparse-0.5.1/validation/builtin-bswap-variable.c000066400000000000000000000014151314543357600216670ustar00rootroot00000000000000typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; static u16 swap16v(u16 a) { return __builtin_bswap16(a); } static u32 swap32v(u64 a) { return __builtin_bswap32(a); } static u64 swap64v(u32 a) { return __builtin_bswap64(a); } /* * check-name: builtin-bswap * check-command: test-linearize $file * check-description: Check that the right builtin function is called, and * that the args are correctly promoted or truncated. * * check-output-ignore * check-output-contains:call.16 .* __builtin_bswap16 * check-output-contains:cast.32 .* (64) %arg1 * check-output-contains:call.32 .* __builtin_bswap32 * check-output-contains:cast.64 .* (32) %arg1 * check-output-contains:call.64 .* __builtin_bswap64 */ sparse-0.5.1/validation/builtin_atomic.c000066400000000000000000000013431314543357600203260ustar00rootroot00000000000000static void fn(void) { static int i, *ptr = (void *)0; i = __sync_fetch_and_add(ptr, 0); i = __sync_fetch_and_sub(ptr, 0); i = __sync_fetch_and_or(ptr, 0); i = __sync_fetch_and_and(ptr, 0); i = __sync_fetch_and_xor(ptr, 0); i = __sync_fetch_and_nand(ptr, 0); i = __sync_add_and_fetch(ptr, 0); i = __sync_sub_and_fetch(ptr, 0); i = __sync_or_and_fetch(ptr, 0); i = __sync_and_and_fetch(ptr, 0); i = __sync_xor_and_fetch(ptr, 0); i = __sync_nand_and_fetch(ptr, 0); i = __sync_bool_compare_and_swap(ptr, 0, 1); i = __sync_val_compare_and_swap(ptr, 0, 1); __sync_synchronize(); i = __sync_lock_test_and_set(ptr, 0); __sync_lock_release(ptr); } /* * check-name: __builtin_atomic * check-error-start * check-error-end */ sparse-0.5.1/validation/builtin_bswap.c000066400000000000000000000003411314543357600201630ustar00rootroot00000000000000static unsigned short x = __builtin_bswap16(0); static unsigned int y = __builtin_bswap32(0); static unsigned long long z = __builtin_bswap64(0); /* * check-name: __builtin_bswap * check-error-start * check-error-end */ sparse-0.5.1/validation/builtin_inf.c000066400000000000000000000006321314543357600176260ustar00rootroot00000000000000static double d = __builtin_huge_val(); static float f = __builtin_huge_valf(); static long double l = __builtin_huge_vall(); static double di = __builtin_inf(); static float fi = __builtin_inff(); static long double li = __builtin_infl(); static double dn = __builtin_nan(""); static float fn = __builtin_nanf(""); static long double ln = __builtin_nanl(""); /* * check-name: __builtin INFINITY / nan() */ sparse-0.5.1/validation/builtin_safe1.c000066400000000000000000000020271314543357600200510ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/builtin_unreachable.c000066400000000000000000000003511314543357600213210ustar00rootroot00000000000000/* 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() */ sparse-0.5.1/validation/builtin_va_arg_pack.c000066400000000000000000000005301314543357600213040ustar00rootroot00000000000000extern void v(int a, ...); extern inline __attribute__((__always_inline__)) void f(int a, ...) { __SIZE_TYPE__ b = __builtin_va_arg_pack_len(); } extern inline __attribute__((__always_inline__)) void g(int a, ...) { v(a, __builtin_va_arg_pack()); } static void h(void) { f(0, 0); g(0, 0); } /* * check-name: __builtin_va_arg_pack() */ sparse-0.5.1/validation/c11-alignas.c000066400000000000000000000017251314543357600173300ustar00rootroot00000000000000static _Alignas(8) int v; static _Alignas(long) int t; static _Alignas(void *) int p; static _Alignas(int[4]) int a; static _Alignas(0) int z; static _Alignas(3) int bnpow2; static _Alignas(-1) int bneg; static _Alignas(-2) int bnegpow2; static _Alignas(v) int bnc; static _Alignas(+) int bsyn; static int check(void) { if (_Alignof(v) != 8) return -1; if (_Alignof(t) != _Alignof(long)) return -1; if (_Alignof(p) != _Alignof(void *)) return -1; if (_Alignof(a) != _Alignof(int)) return -1; return 0; } /* * check-name: c11-alignas * check-command: test-linearize -std=c11 $file * * check-error-start c11-alignas.c:6:25: warning: non-power-of-2 alignment c11-alignas.c:7:25: warning: non-positive alignment c11-alignas.c:8:25: warning: non-positive alignment c11-alignas.c:9:17: error: bad constant expression c11-alignas.c:10:17: error: Syntax error in unary expression * check-error-end * * check-output-ignore * check-output-contains: ret\\.32 *\$0 */ sparse-0.5.1/validation/c11-alignof.c000066400000000000000000000003071314543357600173240ustar00rootroot00000000000000static int foo(void) { return _Alignof(short); } /* * check-name: c11-alignof * check-command: test-linearize -std=c11 $file * * check-output-ignore * check-output-contains: ret\\.32 *\$2 */ sparse-0.5.1/validation/c11-noreturn.c000066400000000000000000000003031314543357600175550ustar00rootroot00000000000000static _Noreturn void foo(void) { while (1) ; } /* * check-name: c11-noreturn * check-command: test-parsing -std=c11 $file * * check-output-ignore * check-output-contains: \[noreturn\] */ sparse-0.5.1/validation/c11-stdc-version.c000066400000000000000000000002321314543357600203220ustar00rootroot00000000000000__STDC_VERSION__ /* * check-name: c11-stdc-version * check-command: sparse -E -std=c11 $file * * check-output-start 201112L * check-output-end */ sparse-0.5.1/validation/c11-thread-local.c000066400000000000000000000002601314543357600202420ustar00rootroot00000000000000static _Thread_local int foo; /* * check-name: c11-thread-local * check-command: test-parsing -std=c11 $file * * check-output-ignore * check-output-contains: \[tls\] */ sparse-0.5.1/validation/c99-for-loop-decl.c000066400000000000000000000017321314543357600203720ustar00rootroot00000000000000static int bad_scope(void) { int r = 0; for (int i = 0; i < 10; i++) { r = i; } return i; /* check-should-fail */ } static int c99(void) { int r = 0; for ( int i = 0; i < 10; i++) /* check-should-pass */ r = i; for ( auto int j = 0; j < 10; j++) /* check-should-pass */ r = j; for (register int k = 0; k < 10; k++) /* check-should-pass */ r = k; for ( extern int l = 0; l < 10; l++) /* check-should-fail */ r = l; for ( extern int m; m < 10; m++) /* check-should-fail */ r = m; for ( static int n = 0; n < 10; n++) /* check-should-fail */ r = n; return r; } /* * check-name: C99 for-loop declarations * * check-error-start c99-for-loop-decl.c:22:27: error: non-local var 'l' in for-loop initializer c99-for-loop-decl.c:24:27: error: non-local var 'm' in for-loop initializer c99-for-loop-decl.c:26:27: error: non-local var 'n' in for-loop initializer c99-for-loop-decl.c:9:16: error: undefined identifier 'i' * check-error-end */ sparse-0.5.1/validation/c99-for-loop.c000066400000000000000000000005071314543357600174640ustar00rootroot00000000000000int c99(void); int c99(void) { int r = -1; for (int i = 0; i < 10; i++) { r = i; } return r; } /* * check-name: C99 for loop variable declaration * check-command: test-linearize $file * * check-output-ignore * check-output-contains: phisrc\\. * check-output-contains: phi\\. * check-output-contains: add\\. */ sparse-0.5.1/validation/calling-convention-attributes.c000066400000000000000000000014211314543357600232760ustar00rootroot00000000000000extern 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 */ sparse-0.5.1/validation/cast-constant-to-float.c000066400000000000000000000010071314543357600216250ustar00rootroot00000000000000typedef unsigned int uint; typedef unsigned long ulong; double f1(void) { return -1; } double f2(void) { return (double)-1; } double f3(void) { return -1.0; } /* * check-name: cast-constant-to-float * check-command: test-linearize -Wno-decl $file * * check-output-start f1: .L0: set.64 %r1 <- -1.000000 ret.64 %r1 f2: .L2: set.64 %r3 <- -1.000000 ret.64 %r3 f3: .L4: set.64 %r5 <- -1.000000 ret.64 %r5 * check-output-end */ sparse-0.5.1/validation/cast-constants.c000066400000000000000000000133101314543357600202650ustar00rootroot00000000000000typedef unsigned int uint; typedef unsigned long ulong; static int uint_2_int(void) { return (int)123U; } static int long_2_int(void) { return (int)123L; } static int ulong_2_int(void) { return (int)123UL; } static int vptr_2_int(void) { return (int)((void*)123); } static int iptr_2_int(void) { return (int)((int*)128); } static int float_2_int(void) { return (int)1.123F; } static int double_2_int(void) { return (int)1.123L; } static uint int_2_uint(void) { return (uint)123; } static uint long_2_uint(void) { return (uint)123L; } static uint ulong_2_uint(void) { return (uint)123UL; } static uint vptr_2_uint(void) { return (uint)((void*)123); } static uint iptr_2_uint(void) { return (uint)((int*)128); } static uint float_2_uint(void) { return (uint)1.123F; } static uint double_2_uint(void) { return (uint)1.123L; } static long int_2_long(void) { return (long)123; } static long uint_2_long(void) { return (long)123U; } static long ulong_2_long(void) { return (long)123UL; } static long vptr_2_long(void) { return (long)((void*)123); } static long iptr_2_long(void) { return (long)((int*)128); } static long float_2_long(void) { return (long)1.123F; } static long double_2_long(void) { return (long)1.123L; } static ulong int_2_ulong(void) { return (ulong)123; } static ulong uint_2_ulong(void) { return (ulong)123U; } static ulong long_2_ulong(void) { return (ulong)123L; } static ulong vptr_2_ulong(void) { return (ulong)((void*)123); } static ulong iptr_2_ulong(void) { return (ulong)((int*)128); } static ulong float_2_ulong(void) { return (ulong)1.123F; } static ulong double_2_ulong(void) { return (ulong)1.123L; } static void * int_2_vptr(void) { return (void *)123; } static void * uint_2_vptr(void) { return (void *)123U; } static void * long_2_vptr(void) { return (void *)123L; } static void * ulong_2_vptr(void) { return (void *)123UL; } static void * iptr_2_vptr(void) { return (void *)((int*)128); } static int * int_2_iptr(void) { return (int *)123; } static int * uint_2_iptr(void) { return (int *)123U; } static int * long_2_iptr(void) { return (int *)123L; } static int * ulong_2_iptr(void) { return (int *)123UL; } static int * vptr_2_iptr(void) { return (int *)((void*)123); } static float int_2_float(void) { return (float)123; } static float uint_2_float(void) { return (float)123U; } static float long_2_float(void) { return (float)123L; } static float ulong_2_float(void) { return (float)123UL; } static float double_2_float(void) { return (float)1.123L; } static double int_2_double(void) { return (double)123; } static double uint_2_double(void) { return (double)123U; } static double long_2_double(void) { return (double)123L; } static double ulong_2_double(void) { return (double)123UL; } static double float_2_double(void) { return (double)1.123F; } /* * check-name: cast-constants.c * check-command: test-linearize -m64 $file * * check-output-start uint_2_int: .L0: ret.32 $123 long_2_int: .L2: ret.32 $123 ulong_2_int: .L4: ret.32 $123 vptr_2_int: .L6: ret.32 $123 iptr_2_int: .L8: ret.32 $128 float_2_int: .L10: ret.32 $1 double_2_int: .L12: ret.32 $1 int_2_uint: .L14: ret.32 $123 long_2_uint: .L16: ret.32 $123 ulong_2_uint: .L18: ret.32 $123 vptr_2_uint: .L20: ret.32 $123 iptr_2_uint: .L22: ret.32 $128 float_2_uint: .L24: ret.32 $1 double_2_uint: .L26: ret.32 $1 int_2_long: .L28: ret.64 $123 uint_2_long: .L30: ret.64 $123 ulong_2_long: .L32: ret.64 $123 vptr_2_long: .L34: ret.64 $123 iptr_2_long: .L36: ret.64 $128 float_2_long: .L38: ret.64 $1 double_2_long: .L40: ret.64 $1 int_2_ulong: .L42: ret.64 $123 uint_2_ulong: .L44: ret.64 $123 long_2_ulong: .L46: ret.64 $123 vptr_2_ulong: .L48: ret.64 $123 iptr_2_ulong: .L50: ret.64 $128 float_2_ulong: .L52: ret.64 $1 double_2_ulong: .L54: ret.64 $1 int_2_vptr: .L56: ret.64 $123 uint_2_vptr: .L58: ret.64 $123 long_2_vptr: .L60: ret.64 $123 ulong_2_vptr: .L62: ret.64 $123 iptr_2_vptr: .L64: ret.64 $128 int_2_iptr: .L66: ret.64 $123 uint_2_iptr: .L68: ret.64 $123 long_2_iptr: .L70: ret.64 $123 ulong_2_iptr: .L72: ret.64 $123 vptr_2_iptr: .L74: ret.64 $123 int_2_float: .L76: set.32 %r39 <- 123.000000 ret.32 %r39 uint_2_float: .L78: set.32 %r41 <- 123.000000 ret.32 %r41 long_2_float: .L80: set.32 %r43 <- 123.000000 ret.32 %r43 ulong_2_float: .L82: set.32 %r45 <- 123.000000 ret.32 %r45 double_2_float: .L84: set.32 %r47 <- 1.123000 ret.32 %r47 int_2_double: .L86: set.64 %r49 <- 123.000000 ret.64 %r49 uint_2_double: .L88: set.64 %r51 <- 123.000000 ret.64 %r51 long_2_double: .L90: set.64 %r53 <- 123.000000 ret.64 %r53 ulong_2_double: .L92: set.64 %r55 <- 123.000000 ret.64 %r55 float_2_double: .L94: set.64 %r57 <- 1.123000 ret.64 %r57 * check-output-end */ sparse-0.5.1/validation/cast-kinds.c000066400000000000000000000150751314543357600173730ustar00rootroot00000000000000typedef unsigned int uint; typedef unsigned long ulong; static int uint_2_int(uint a) { return (int)a; } static int long_2_int(long a) { return (int)a; } static int ulong_2_int(ulong a) { return (int)a; } static int vptr_2_int(void *a) { return (int)a; } static int iptr_2_int(int *a) { return (int)a; } static int float_2_int(float a) { return (int)a; } static int double_2_int(double a) { return (int)a; } static uint int_2_uint(int a) { return (uint)a; } static uint long_2_uint(long a) { return (uint)a; } static uint ulong_2_uint(ulong a) { return (uint)a; } static uint vptr_2_uint(void *a) { return (uint)a; } static uint iptr_2_uint(int *a) { return (uint)a; } static uint float_2_uint(float a) { return (uint)a; } static uint double_2_uint(double a) { return (uint)a; } static long int_2_long(int a) { return (long)a; } static long uint_2_long(uint a) { return (long)a; } static long ulong_2_long(ulong a) { return (long)a; } static long vptr_2_long(void *a) { return (long)a; } static long iptr_2_long(int *a) { return (long)a; } static long float_2_long(float a) { return (long)a; } static long double_2_long(double a) { return (long)a; } static ulong int_2_ulong(int a) { return (ulong)a; } static ulong uint_2_ulong(uint a) { return (ulong)a; } static ulong long_2_ulong(long a) { return (ulong)a; } static ulong vptr_2_ulong(void *a) { return (ulong)a; } static ulong iptr_2_ulong(int *a) { return (ulong)a; } static ulong float_2_ulong(float a) { return (ulong)a; } static ulong double_2_ulong(double a) { return (ulong)a; } static void * int_2_vptr(int a) { return (void *)a; } static void * uint_2_vptr(uint a) { return (void *)a; } static void * long_2_vptr(long a) { return (void *)a; } static void * ulong_2_vptr(ulong a) { return (void *)a; } static void * iptr_2_vptr(int *a) { return (void *)a; } static int * int_2_iptr(int a) { return (int *)a; } static int * uint_2_iptr(uint a) { return (int *)a; } static int * long_2_iptr(long a) { return (int *)a; } static int * ulong_2_iptr(ulong a) { return (int *)a; } static int * vptr_2_iptr(void *a) { return (int *)a; } static float int_2_float(int a) { return (float)a; } static float uint_2_float(uint a) { return (float)a; } static float long_2_float(long a) { return (float)a; } static float ulong_2_float(ulong a) { return (float)a; } static float double_2_float(double a) { return (float)a; } static double int_2_double(int a) { return (double)a; } static double uint_2_double(uint a) { return (double)a; } static double long_2_double(long a) { return (double)a; } static double ulong_2_double(ulong a) { return (double)a; } static double float_2_double(float a) { return (double)a; } /* * check-name: cast-kinds * check-command: test-linearize -m64 $file * * check-output-start uint_2_int: .L0: ret.32 %arg1 long_2_int: .L2: scast.32 %r5 <- (64) %arg1 ret.32 %r5 ulong_2_int: .L4: cast.32 %r8 <- (64) %arg1 ret.32 %r8 vptr_2_int: .L6: cast.32 %r11 <- (64) %arg1 ret.32 %r11 iptr_2_int: .L8: cast.32 %r14 <- (64) %arg1 ret.32 %r14 float_2_int: .L10: ret.32 %arg1 double_2_int: .L12: cast.32 %r20 <- (64) %arg1 ret.32 %r20 int_2_uint: .L14: ret.32 %arg1 long_2_uint: .L16: scast.32 %r26 <- (64) %arg1 ret.32 %r26 ulong_2_uint: .L18: cast.32 %r29 <- (64) %arg1 ret.32 %r29 vptr_2_uint: .L20: cast.32 %r32 <- (64) %arg1 ret.32 %r32 iptr_2_uint: .L22: cast.32 %r35 <- (64) %arg1 ret.32 %r35 float_2_uint: .L24: ret.32 %arg1 double_2_uint: .L26: cast.32 %r41 <- (64) %arg1 ret.32 %r41 int_2_long: .L28: scast.64 %r44 <- (32) %arg1 ret.64 %r44 uint_2_long: .L30: cast.64 %r47 <- (32) %arg1 ret.64 %r47 ulong_2_long: .L32: ret.64 %arg1 vptr_2_long: .L34: cast.64 %r53 <- (64) %arg1 ret.64 %r53 iptr_2_long: .L36: cast.64 %r56 <- (64) %arg1 ret.64 %r56 float_2_long: .L38: cast.64 %r59 <- (32) %arg1 ret.64 %r59 double_2_long: .L40: ret.64 %arg1 int_2_ulong: .L42: scast.64 %r65 <- (32) %arg1 ret.64 %r65 uint_2_ulong: .L44: cast.64 %r68 <- (32) %arg1 ret.64 %r68 long_2_ulong: .L46: ret.64 %arg1 vptr_2_ulong: .L48: cast.64 %r74 <- (64) %arg1 ret.64 %r74 iptr_2_ulong: .L50: cast.64 %r77 <- (64) %arg1 ret.64 %r77 float_2_ulong: .L52: cast.64 %r80 <- (32) %arg1 ret.64 %r80 double_2_ulong: .L54: ret.64 %arg1 int_2_vptr: .L56: scast.64 %r86 <- (32) %arg1 ret.64 %r86 uint_2_vptr: .L58: cast.64 %r89 <- (32) %arg1 ret.64 %r89 long_2_vptr: .L60: scast.64 %r92 <- (64) %arg1 ret.64 %r92 ulong_2_vptr: .L62: cast.64 %r95 <- (64) %arg1 ret.64 %r95 iptr_2_vptr: .L64: cast.64 %r98 <- (64) %arg1 ret.64 %r98 int_2_iptr: .L66: ptrcast.64 %r101 <- (32) %arg1 ret.64 %r101 uint_2_iptr: .L68: ptrcast.64 %r104 <- (32) %arg1 ret.64 %r104 long_2_iptr: .L70: ptrcast.64 %r107 <- (64) %arg1 ret.64 %r107 ulong_2_iptr: .L72: ptrcast.64 %r110 <- (64) %arg1 ret.64 %r110 vptr_2_iptr: .L74: ptrcast.64 %r113 <- (64) %arg1 ret.64 %r113 int_2_float: .L76: fpcast.32 %r116 <- (32) %arg1 ret.32 %r116 uint_2_float: .L78: fpcast.32 %r119 <- (32) %arg1 ret.32 %r119 long_2_float: .L80: fpcast.32 %r122 <- (64) %arg1 ret.32 %r122 ulong_2_float: .L82: fpcast.32 %r125 <- (64) %arg1 ret.32 %r125 double_2_float: .L84: fpcast.32 %r128 <- (64) %arg1 ret.32 %r128 int_2_double: .L86: fpcast.64 %r131 <- (32) %arg1 ret.64 %r131 uint_2_double: .L88: fpcast.64 %r134 <- (32) %arg1 ret.64 %r134 long_2_double: .L90: fpcast.64 %r137 <- (64) %arg1 ret.64 %r137 ulong_2_double: .L92: fpcast.64 %r140 <- (64) %arg1 ret.64 %r140 float_2_double: .L94: fpcast.64 %r143 <- (32) %arg1 ret.64 %r143 * check-output-end */ sparse-0.5.1/validation/check_byte_count-ice.c000066400000000000000000000011651314543357600213740ustar00rootroot00000000000000extern 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:6:0: warning: Newline in string or character constant check_byte_count-ice.c:5:23: warning: multi-character character constant check_byte_count-ice.c:6:1: error: Expected ) in function call check_byte_count-ice.c:6:1: error: got } builtin:0:0: error: Expected } at end of function builtin:0:0: error: got end-of-input check_byte_count-ice.c:5:15: error: not enough arguments for function memset * check-error-end */ sparse-0.5.1/validation/choose_expr.c000066400000000000000000000013031314543357600176360ustar00rootroot00000000000000static 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 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 * choose_expr.c:4:17: warning: division by zero * check-error-end */ sparse-0.5.1/validation/comma.c000066400000000000000000000004151314543357600164170ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/compare-null-to-int.c000066400000000000000000000006011314543357600211260ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/compound-assign-type.c000066400000000000000000000004471314543357600214150ustar00rootroot00000000000000static unsigned int foo(unsigned int x, long a) { x /= a; return x; } /* * check-name: compound-assign-type * check-command: test-linearize -m64 $file * check-output-ignore * * check-output-excludes: divu\\.32 * check-output-contains: divs\\.64 * check-output-contains: scast\\.32 */ sparse-0.5.1/validation/cond-address-array.c000066400000000000000000000007351314543357600210120ustar00rootroot00000000000000int foo(void) { extern int a[]; if (a) return 1; return 0; } int bar(void) { int a[2]; if (a) return 1; return 0; } /* * check-name: cond-address-array.c * check-command: test-linearize -Wno-decl -Waddress $file * check-output-ignore * * check-error-start cond-address-array.c:4:13: warning: the address of an array will always evaluate as true cond-address-array.c:12:13: warning: the address of an array will always evaluate as true * check-error-end */ sparse-0.5.1/validation/cond-address-function.c000066400000000000000000000005411314543357600215140ustar00rootroot00000000000000extern void func(void); int global_function(void) { if (func) return 1; return 0; } /* * check-name: cond-address-function * check-command: test-linearize -Wno-decl -Waddress $file * check-output-ignore * * check-error-start cond-address-function.c:5:13: warning: the address of a function will always evaluate as true * check-error-end */ sparse-0.5.1/validation/cond-address.c000066400000000000000000000004731314543357600176750ustar00rootroot00000000000000extern void f(void); extern int a[]; int foo(void) { if (f) return 1; return 0; } int bar(void) { if (a) return 1; return 0; } int qux(void) { if (f && a) return 1; return 0; } /* * check-name: cond-address.c * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-excludes: VOID */ sparse-0.5.1/validation/cond-err-expand.c000066400000000000000000000010221314543357600203040ustar00rootroot00000000000000static inline void f(void) { __builtin_constant_p(0); } void foo(void) { 0 ? 0 : f(); } void bar(void) { 1 ? f() : 0; } /* * check-name: cond-err-expand.c * check-command: test-linearize -Wno-decl $file * * check-error-start cond-err-expand.c:8:11: error: incompatible types in conditional expression (different base types) cond-err-expand.c:13:11: error: incompatible types in conditional expression (different base types) * check-error-end * * check-output-ignore * check-excludes: call.* __builtin_constant_p */ sparse-0.5.1/validation/cond_expr.c000066400000000000000000000007561314543357600173140ustar00rootroot00000000000000/* * 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 */ sparse-0.5.1/validation/cond_expr2.c000066400000000000000000000015451314543357600173730ustar00rootroot00000000000000extern 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 */ sparse-0.5.1/validation/cond_expr3.c000066400000000000000000000011561314543357600173720ustar00rootroot00000000000000static int icmp = 1 / (sizeof(int) - sizeof(1 > 0)); static int fcmp = 1 / (sizeof(int) - sizeof(1.0 == 2.0 - 1.0)); static int lnot = 1 / (sizeof(int) - sizeof(!!1.0)); static int land = 1 / (sizeof(int) - sizeof(2 && 3)); static int lor = 1 / (sizeof(int) - sizeof('c' || 1.0f)); /* * check-name: result type of relational and logical operators * * check-error-start cond_expr3.c:1:21: warning: division by zero cond_expr3.c:2:21: warning: division by zero cond_expr3.c:3:21: warning: division by zero cond_expr3.c:4:21: warning: division by zero cond_expr3.c:5:21: warning: division by zero * check-error-end */ sparse-0.5.1/validation/conditional-type.c000066400000000000000000000036501314543357600206110ustar00rootroot00000000000000extern void afun(void); extern void vcond(void); static int array[3]; struct state { int nr:2; }; enum number { zero, one, two, many, }; static int bad_if(struct state s) { if (vcond()) return 1; if (s) return 1; return 0; } static void bad_if2(int *a, int *b) { if (vcond()) *a = 1; *b = 0; } static int bad_sel(struct state s) { return vcond() ? 1 : 0; return s ? 1 : 0; } static int bad_loop_void(void) { while (vcond()) ; for (;vcond();) ; do ; while (vcond()); return 0; } static int good_if_int(int a, _Bool b, long c, unsigned char d) { if (a) return 1; if (b) return 1; if (c) return 1; if (d) return 1; return 0; } static int good_if_float(float a, double b) { if (a) return 1; if (b) return 1; return 0; } static int good_if_enum(void) { if (many) return 1; return 0; } static int good_if_bitfield(struct state s, struct state *p) { if (s.nr) return 1; if (p->nr) return 1; return 0; } static int good_if_ptr(void *ptr) { if (ptr) return 1; if (array) return 1; if (afun) return 1; return 0; } /* * check-name: conditional-type * * check-error-start conditional-type.c:18:18: error: incorrect type in conditional conditional-type.c:18:18: got void conditional-type.c:19:13: error: incorrect type in conditional conditional-type.c:19:13: got struct state s conditional-type.c:24:18: error: incorrect type in conditional conditional-type.c:24:18: got void conditional-type.c:29:21: error: incorrect type in conditional conditional-type.c:29:21: got void conditional-type.c:30:16: error: incorrect type in conditional conditional-type.c:30:16: got struct state s conditional-type.c:34:21: error: incorrect type in conditional conditional-type.c:34:21: got void conditional-type.c:36:20: error: incorrect type in conditional conditional-type.c:36:20: got void conditional-type.c:40:21: error: incorrect type in conditional conditional-type.c:40:21: got void * check-error-end */ sparse-0.5.1/validation/context.c000066400000000000000000000106501314543357600170110ustar00rootroot00000000000000#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 int _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 */ sparse-0.5.1/validation/crash-add-doms.c000066400000000000000000000003671314543357600201170ustar00rootroot00000000000000char a; int b; void c(void) { if (0) { char *d; for (;;) for (;;) e: *d *= (a && 0) ^ b && *d; } goto e; } /* * check-name: crash add-doms * check-command: test-linearize $file * * check-error-ignore * check-output-ignore */ sparse-0.5.1/validation/crash-bb_target.c000066400000000000000000000002121314543357600203450ustar00rootroot00000000000000a() { &&b /* * check-name: crash bb_target * check-command: test-linearize $file * * check-error-ignore * check-output-ignore */ sparse-0.5.1/validation/crash-ep-active.c000066400000000000000000000002501314543357600202730ustar00rootroot00000000000000int a(int b) { return 0( && b; } /* * check-name: crash ep->active * check-command: test-linearize $file * * check-error-ignore * check-output-ignore */ sparse-0.5.1/validation/crash-ptrlist.c000066400000000000000000000006411314543357600201230ustar00rootroot00000000000000a; char b; c() { while () { int *d; while () { char *e = &b; if (a ?: (*d = f || *0) || g) { if ; } else { int h = 0; if (j) { char **i = &e; if (0 ? 0 : 0 ?: (**i = 1 || 0 && 0)) h ?: (*e = i && &h /* * check-name: crash ptrlist * check-command: test-linearize $file * * check-error-ignore * check-output-ignore */ sparse-0.5.1/validation/crash-rewrite-branch.c000066400000000000000000000004421314543357600213350ustar00rootroot00000000000000void a(int c, int e) { for(; b; c ; if (()) { unsigned short d = e; if (()) while () ; &d; } if (()) { int f = &f; } } /* * check-name: crash rewrite_branch * check-command: test-linearize $file * * check-error-ignore * check-output-ignore */ sparse-0.5.1/validation/crazy02-not-so.c000066400000000000000000000011261314543357600200320ustar00rootroot00000000000000int foo(int *ptr, int i) { int *p; switch (i - i) { // will be optimized to 0 case 0: return 0; case 1: // will be optimized away p = ptr; do { // will be an unreachable loop *p++ = 123; } while (--i); break; } return 1; } int bar(int *ptr, int i) { int *p; switch (i - i) { // will be optimized to 0 case 0: return 0; case 1: // will be optimized away // p is uninitialized do { // will be an unreachable loop *p++ = 123; } while (--i); break; } return 1; } /* * check-name: crazy02-not-so.c * check-command: sparse -Wno-decl $file */ sparse-0.5.1/validation/crazy03.c000066400000000000000000000005451314543357600166220ustar00rootroot00000000000000extern char a; extern int b; extern char *c, *d; extern void e(void); extern void f(char *); int g(int h); int g(int h) { if (h > 1) e(); if (h > 1) return 0; for (;;) { if (a) { while (c) ; b = 0; } else { c = (void*)0; b = 1; } if (b) { f(c); continue; } d = c; while (*c++) ; } } /* * check-name: crazy03.c */ sparse-0.5.1/validation/declaration-after-statement-ansi.c000066400000000000000000000004011314543357600236340ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/declaration-after-statement-c89.c000066400000000000000000000004021314543357600233060ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/declaration-after-statement-c99.c000066400000000000000000000002151314543357600233110ustar00rootroot00000000000000static void func (int i) { i; int j = i; } /* * check-name: declaration after statement (C99) * check-command: sparse -std=c99 $file */ sparse-0.5.1/validation/declaration-after-statement-default.c000066400000000000000000000002101314543357600243240ustar00rootroot00000000000000static void func (int i) { i; int j = i; } /* * check-name: declaration after statement (default) * check-command: sparse $file */ sparse-0.5.1/validation/definitions.c000066400000000000000000000002311314543357600176320ustar00rootroot00000000000000static inline int f(void); static int g(void) { return f(); } static inline int f(void) { return 0; } /* * check-name: finding definitions */ sparse-0.5.1/validation/designated-init.c000066400000000000000000000161261314543357600204010ustar00rootroot00000000000000struct 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 , declared with attribute designated_init designated-init.c:62:32: warning: in initializer for anon_positional: positional init of field in struct , 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 */ sparse-0.5.1/validation/discarded-label-statement.c000066400000000000000000000005461314543357600223310ustar00rootroot00000000000000/* * Verify that the statement following an unused label * is not discarded with the label. */ static int bad(int a, int b) { int r = 0; start: r += a; r += b; return r; } /* * check-name: discarded-label-statement * check-command: test-linearize $file * * check-output-ignore * check-output-contains: add * check-output-contains: %arg1 */ sparse-0.5.1/validation/div.c000066400000000000000000000016541314543357600161130ustar00rootroot00000000000000#define INT_MIN (-__INT_MAX__ - 1) #define LONG_MIN (-__LONG_MAX__ - 1) #define LLONG_MIN (-__LONG_LONG_MAX__ - 1) static int xd = 1 / 0; static int xl = 1L / 0; static int xll = 1LL / 0; static int yd = INT_MIN / -1; static long yl = LONG_MIN / -1; static long long yll = LLONG_MIN / -1; static int zd = INT_MIN % -1; static long zl = LONG_MIN % -1; static long long zll = LLONG_MIN % -1; /* * check-name: division constants * * check-error-start div.c:5:19: warning: division by zero div.c:6:20: warning: division by zero div.c:7:22: warning: division by zero div.c:9:25: warning: constant integer operation overflow div.c:10:27: warning: constant integer operation overflow div.c:11:34: warning: constant integer operation overflow div.c:13:25: warning: constant integer operation overflow div.c:14:27: warning: constant integer operation overflow div.c:15:34: warning: constant integer operation overflow * check-error-end */ sparse-0.5.1/validation/double-semicolon.c000066400000000000000000000002631314543357600205640ustar00rootroot00000000000000extern 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 */ sparse-0.5.1/validation/dubious-bitwise-with-not.c000066400000000000000000000015751314543357600222200ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/empty-file000066400000000000000000000000001314543357600171430ustar00rootroot00000000000000sparse-0.5.1/validation/endian-big.c000066400000000000000000000005151314543357600173210ustar00rootroot00000000000000#if defined(__LITTLE_ENDIAN__) #error "__LITTLE_ENDIAN__ defined!" #endif #if (__BIG_ENDIAN__ != 1) #error "__BIG_ENDIAN__ not correctly defined!" #endif #if (__BYTE_ORDER__ != __ORDER_BIG_ENDIAN__) #error "__BYTE_ORDER__ not correctly defined!" #endif /* * check-name: endian-big.c * check-command: sparse -mbig-endian $file */ sparse-0.5.1/validation/endian-little.c000066400000000000000000000005261314543357600200570ustar00rootroot00000000000000#if defined(__BIG_ENDIAN__) #error "__BIG_ENDIAN__ defined!" #endif #if (__LITTLE_ENDIAN__ != 1) #error "__LITTLE_ENDIAN__ not correctly defined!" #endif #if (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__) #error "__BYTE_ORDER__ not correctly defined!" #endif /* * check-name: endian-little.c * check-command: sparse -mlittle-endian $file */ sparse-0.5.1/validation/enum-mismatch.c000066400000000000000000000005461314543357600200770ustar00rootroot00000000000000enum ea { A = 0, }; enum eb { B = 1, }; static enum eb foo(enum ea a) { return a; } /* * check-name: enum-mismatch * check-command: sparse -Wenum-mismatch $file * * check-error-start enum-mismatch.c:7:16: warning: mixing different enum types enum-mismatch.c:7:16: int enum ea versus enum-mismatch.c:7:16: int enum eb * check-error-end */ sparse-0.5.1/validation/enum_scope.c000066400000000000000000000002301314543357600174530ustar00rootroot00000000000000enum {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] */ sparse-0.5.1/validation/escapes.c000066400000000000000000000023531314543357600167510ustar00rootroot00000000000000static 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' }; static char a_hex[3] = "\x61\x62\x63"; static char b_hex[3] = "\x61\x62\x63\x64"; static char c_hex[3] = "\x61\x62"; static char d_hex[3] = "\x61"; static char a_oct[3] = "\141\142\143"; static char b_oct[3] = "\141\142\143\144"; static char c_oct[3] = "\141\142"; static char d_oct[3] = "\141"; /* * check-name: Character escape sequences * * check-error-start escapes.c:3:34: warning: hex escape sequence out of range escapes.c:3:44: warning: hex escape sequence out of range escapes.c:4:18: warning: hex escape sequence out of range escapes.c:6:24: warning: unknown escape sequence: '\c' escapes.c:6:30: warning: multi-character character constant escapes.c:6:39: warning: multi-character character constant escapes.c:6:47: warning: hex escape sequence out of range escapes.c:6:47: warning: multi-character character constant escapes.c:9:24: warning: too long initializer-string for array of char escapes.c:14:24: warning: too long initializer-string for array of char * check-error-end */ sparse-0.5.1/validation/extern-array.c000066400000000000000000000003711314543357600177450ustar00rootroot00000000000000extern const char *v4l2_type_names[]; const char *v4l2_type_names[] = { "test" }; extern const char *v4l2_type_names[]; static void test(void) { unsigned sz = sizeof(v4l2_type_names); } /* * check-name: duplicate extern array */ sparse-0.5.1/validation/extern-inline.c000066400000000000000000000006071314543357600201070ustar00rootroot00000000000000extern __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. */ sparse-0.5.1/validation/field-overlap.c000066400000000000000000000003351314543357600200550ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/field-override.c000066400000000000000000000052131314543357600202240ustar00rootroot00000000000000static int ref[] = { [1] = 3, [2] = 3, [3] = 3, [2] = 2, /* check-should-warn */ [1] = 1, /* check-should-warn */ }; static int foo[] = { [1 ... 3] = 3, }; static int foz[4] = { [0 ... 3] = 3, [0] = 0, [1] = 0, [2 ... 3] = 1, [2] = 3, /* check-should-warn */ [3] = 3, /* check-should-warn */ }; static int bar[] = { [1 ... 3] = 3, [1] = 1, /* check-should-warn */ [2] = 2, /* check-should-warn */ [2 ... 4] = 2, /* check-should-warn */ [2 ... 3] = 2, /* check-should-warn */ [4] = 4, /* check-should-warn */ [0] = 0, [5] = 5, }; static int baz[3][3] = { [0 ... 2][0 ... 2] = 0, [0] = { 0, 0, 0, }, /* check-should-warn */ [0][0] = 1, /* check-should-warn */ [1] = { 0, 0, 0, }, /* check-should-warn */ [1][0] = 1, /* check-should-warn */ [1][1] = 1, /* check-should-warn */ [1 ... 2][1 ... 2] = 2, }; struct s { int i; int a[2]; }; static struct s s = { .a[0] = 0, .a[1] = 1, }; static struct s a[2] = { [0].i = 0, [1].i = 1, [0].a[0] = 2, [0].a[1] = 3, }; static struct s b[2] = { [0 ... 1] = { 0, { 1, 2 }, }, [0].i = 0, [1].i = 1, [0].a[0] = 2, [0].a[1] = 3, }; /* * check-name: field-override * check-command: sparse -Woverride-init -Woverride-init-all $file * * check-error-start field-override.c:2:10: warning: Initializer entry defined twice field-override.c:6:10: also defined here field-override.c:3:10: warning: Initializer entry defined twice field-override.c:5:10: also defined here field-override.c:17:10: warning: Initializer entry defined twice field-override.c:18:10: also defined here field-override.c:17:10: warning: Initializer entry defined twice field-override.c:19:10: also defined here field-override.c:23:10: warning: Initializer entry defined twice field-override.c:24:10: also defined here field-override.c:23:10: warning: Initializer entry defined twice field-override.c:25:10: also defined here field-override.c:23:10: warning: Initializer entry defined twice field-override.c:26:10: also defined here field-override.c:26:10: warning: Initializer entry defined twice field-override.c:27:10: also defined here field-override.c:26:10: warning: Initializer entry defined twice field-override.c:28:10: also defined here field-override.c:35:10: warning: Initializer entry defined twice field-override.c:36:10: also defined here field-override.c:37:10: warning: Initializer entry defined twice field-override.c:38:10: also defined here field-override.c:37:10: warning: Initializer entry defined twice field-override.c:39:10: also defined here field-override.c:37:10: warning: Initializer entry defined twice field-override.c:40:10: also defined here * check-error-end */ sparse-0.5.1/validation/fored_arg.c000066400000000000000000000003731314543357600172560ustar00rootroot00000000000000/* * check-name: Forced function argument type. */ #define __iomem __attribute__((noderef, address_space(2))) #define __force __attribute__((force)) static void foo(__force void * addr) { } static void bar(void) { void __iomem *a; foo(a); } sparse-0.5.1/validation/foul-bitwise.c000066400000000000000000000013031314543357600177310ustar00rootroot00000000000000typedef 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: warning: restricted __le16 degrades to integer foul-bitwise.c:19:16: warning: incorrect type in return expression (different base types) foul-bitwise.c:19:16: expected restricted __le16 foul-bitwise.c:19:16: got int * check-error-end */ sparse-0.5.1/validation/fp-vs-ptrcast.c000066400000000000000000000003261314543357600200350ustar00rootroot00000000000000float *f01(void* p) { return p; } /* * check-name: fp-vs-ptrcast * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: fpcast * check-output-contains: ptrcast */ sparse-0.5.1/validation/function-pointer-inheritance.c000066400000000000000000000002111314543357600231070ustar00rootroot00000000000000extern int foo(int f(int, void *)); int foo(int (*f)(int, void *)) { return 0; } /* * check-name: Function pointer inheritance */ sparse-0.5.1/validation/function-redecl.c000066400000000000000000000051501314543357600204050ustar00rootroot00000000000000#define __user __attribute__((address_space(1))) #define NULL ((void*)0) int ret_type(void); void ret_type(void) { } /* check-should-fail */ int ret_const(void); int const ret_const(void) { return 0; } /* check-should-fail */ void *ret_as(void); void __user *ret_as(void) { return NULL; } /* check-should-fail */ void *ret_mod(void); void const *ret_mod(void) { return NULL; } /* check-should-fail */ void arg_type(int a); void arg_type(void *a) { } /* check-should-fail */ void arg_const(int a); void arg_const(const int a) { } /* OK */ void arg_as(void *a); void arg_as(void __user *a) { } /* check-should-fail */ void arg_mod(void *); void arg_mod(void const *a) { } /* check-should-fail */ void arg_more_arg(int a); void arg_more_arg(int a, int b) { } /* check-should-fail */ void arg_less_arg(int a, int b); void arg_less_arg(int a) { } /* check-should-fail */ void arg_vararg(int a); void arg_vararg(int a, ...) { } /* check-should-fail */ /* * check-name: function-redecl * * check-error-start function-redecl.c:5:6: error: symbol 'ret_type' redeclared with different type (originally declared at function-redecl.c:4) - different base types function-redecl.c:9:11: error: symbol 'ret_const' redeclared with different type (originally declared at function-redecl.c:8) - different modifiers function-redecl.c:13:13: error: symbol 'ret_as' redeclared with different type (originally declared at function-redecl.c:12) - different address spaces function-redecl.c:17:12: error: symbol 'ret_mod' redeclared with different type (originally declared at function-redecl.c:16) - different modifiers function-redecl.c:21:6: error: symbol 'arg_type' redeclared with different type (originally declared at function-redecl.c:20) - incompatible argument 1 (different base types) function-redecl.c:29:6: error: symbol 'arg_as' redeclared with different type (originally declared at function-redecl.c:28) - incompatible argument 1 (different address spaces) function-redecl.c:33:6: error: symbol 'arg_mod' redeclared with different type (originally declared at function-redecl.c:32) - incompatible argument 1 (different modifiers) function-redecl.c:37:6: error: symbol 'arg_more_arg' redeclared with different type (originally declared at function-redecl.c:36) - different argument counts function-redecl.c:41:6: error: symbol 'arg_less_arg' redeclared with different type (originally declared at function-redecl.c:40) - different argument counts function-redecl.c:45:6: error: symbol 'arg_vararg' redeclared with different type (originally declared at function-redecl.c:44) - incompatible variadic arguments * check-error-end */ sparse-0.5.1/validation/goto-label.c000066400000000000000000000004601314543357600173500ustar00rootroot00000000000000void foo(void) { goto a; a: a: return; } void g(void) { goto a; a: return; } void bar(void) { goto neverland; } /* * check-name: goto labels * * check-error-start goto-label.c:5:1: error: label 'a' redefined goto-label.c:18:9: error: label 'neverland' was not declared * check-error-end */ sparse-0.5.1/validation/identifier_list.c000066400000000000000000000010431314543357600204760ustar00rootroot00000000000000typedef 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 */ sparse-0.5.1/validation/implicit-ret-type.c000066400000000000000000000006131314543357600207040ustar00rootroot00000000000000fun(void); foo(void) { return 1; } static bar(void) { return 1; } /* * check-name: implicit-ret-type.c * check-command: sparse -Wno-decl $file * * check-error-start implicit-ret-type.c:1:1: warning: 'fun()' has implicit return type implicit-ret-type.c:3:1: warning: 'foo()' has implicit return type implicit-ret-type.c:4:8: warning: 'bar()' has implicit return type * check-error-end */ sparse-0.5.1/validation/implicit-type.c000066400000000000000000000004561314543357600201210ustar00rootroot00000000000000extern a; static b; c; /* * check-name: implicit-type.c * check-command: sparse -Wno-decl $file * * check-error-start implicit-type.c:1:8: warning: 'a' has implicit type implicit-type.c:2:8: warning: 'b' has implicit type implicit-type.c:3:1: warning: 'c' has implicit type * check-error-end */ sparse-0.5.1/validation/infinite-loop0.c000066400000000000000000000002511314543357600201550ustar00rootroot00000000000000void foo(void) { int a = a || 0; if (a) ; } /* * check-name: internal infinite loop (0) * check-command: sparse -Wno-decl $file * check-timeout: */ sparse-0.5.1/validation/infinite-loop02.c000066400000000000000000000002061314543357600202370ustar00rootroot00000000000000void foo(void) { int a = 1; while ((a = !a)) ; } /* * check-name: infinite loop 02 * check-command: sparse -Wno-decl $file */ sparse-0.5.1/validation/infinite-loop03.c000066400000000000000000000003131314543357600202370ustar00rootroot00000000000000static void foo(int *buf) { int a = 1; int *b; do { if (a) b = buf; if (a) *buf = 0; } while (!(a = !a)); } /* * check-name: infinite loop 03 * check-command: sparse -Wno-decl $file */ sparse-0.5.1/validation/init-char-array.c000066400000000000000000000006401314543357600203150ustar00rootroot00000000000000/* * for array of char {} 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 */ sparse-0.5.1/validation/init-char-array1.c000066400000000000000000000015071314543357600204010ustar00rootroot00000000000000/* * for array of char, ("...") as the initializer is an gcc language * extension. check that a parenthesized string initializer is handled * correctly and that -Wparen-string warns about it's use. */ static const char u[] = ("hello"); static const char v[] = {"hello"}; static const char v1[] = {("hello")}; static const char w[] = "hello"; static const char x[5] = "hello"; static void f(void) { char a[1/(sizeof(u) == 6)]; char b[1/(sizeof(v) == 6)]; char c[1/(sizeof(w) == 6)]; char d[1/(sizeof(x) == 5)]; } /* * check-name: parenthesized string initializer * check-command: sparse -Wparen-string $file * * check-error-start init-char-array1.c:6:26: warning: array initialized from parenthesized string constant init-char-array1.c:8:28: warning: array initialized from parenthesized string constant * check-error-end */ sparse-0.5.1/validation/init_cstring.c000066400000000000000000000004401314543357600200150ustar00rootroot00000000000000static struct alpha { char a[2]; } x = { .a = "ab" }; /* * check-name: -Winit-cstring option * * check-command: sparse -Winit-cstring $file * check-error-start init_cstring.c:3:14: warning: too long initializer-string for array of char(no space for nul char) * check-error-end */ sparse-0.5.1/validation/initializer-entry-defined-twice.c000066400000000000000000000024341314543357600235150ustar00rootroot00000000000000/* 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 }; /* * _Bools generally take a whole byte, so ensure that we can initialize * them without spewing a warning. */ static _Bool boolarray[3] = { [0] = 1, [1] = 1, }; /* * 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 */ sparse-0.5.1/validation/inline_compound_literals.c000066400000000000000000000003131314543357600224010ustar00rootroot00000000000000struct 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 */ sparse-0.5.1/validation/int128.c000066400000000000000000000036401314543357600163530ustar00rootroot00000000000000typedef __int128 int128_t; typedef signed __int128 sint128_t; typedef unsigned __int128 uint128_t; typedef __int128 int badxi; typedef int __int128 badix; typedef unsigned unsigned __int128 baduu; typedef double __int128 baddx; typedef __int128 double badxd; int sizeof_int128(void) { return sizeof(__int128); } typedef unsigned long long u64; typedef unsigned long u32; u64 foo(u64 a, u64 b, u64 c, u32 s) { unsigned __int128 tmp; tmp = (((uint128_t)a) * b) + c; return (u64) (tmp >> s); } /* * check-name: int128 * check-command: test-linearize $file * check-output-ignore * * check-output-contains: ret\\..*\\$16 * check-output-contains: mulu\\.128 * check-output-contains: add\\.128 * * check-error-start int128.c:5:18: error: two or more data types in declaration specifiers int128.c:5:18: error: Trying to use reserved word 'int' as identifier int128.c:5:25: error: Expected ; at end of declaration int128.c:5:25: error: got badxi int128.c:6:13: error: two or more data types in declaration specifiers int128.c:6:13: error: Trying to use reserved word '__int128' as identifier int128.c:6:25: error: Expected ; at end of declaration int128.c:6:25: error: got badix int128.c:7:18: error: impossible combination of type specifiers: unsigned unsigned int128.c:7:18: error: Trying to use reserved word 'unsigned' as identifier int128.c:7:27: error: Expected ; at end of declaration int128.c:7:27: error: got __int128 int128.c:8:16: error: two or more data types in declaration specifiers int128.c:8:16: error: Trying to use reserved word '__int128' as identifier int128.c:8:25: error: Expected ; at end of declaration int128.c:8:25: error: got baddx int128.c:9:18: error: two or more data types in declaration specifiers int128.c:9:18: error: Trying to use reserved word 'double' as identifier int128.c:9:25: error: Expected ; at end of declaration int128.c:9:25: error: got badxd * check-error-end */ sparse-0.5.1/validation/integer-promotions.c000066400000000000000000000001631314543357600211670ustar00rootroot00000000000000static int add_char(void) { return (char) 127 + (char) 127 + (char) 2; } /* * check-name: Integer promotions */ sparse-0.5.1/validation/ioc-typecheck.c000066400000000000000000000004121314543357600200470ustar00rootroot00000000000000extern unsigned int __invalid_size_argument_for_IOC; static unsigned iocnrs[] = { [ 1 ? 0 : __invalid_size_argument_for_IOC ] = 1, }; /* * check-name: integer constant & conditional expression * check-known-to-fail * * check-error-start * check-error-end */ sparse-0.5.1/validation/kill-casts.c000066400000000000000000000004701314543357600173720ustar00rootroot00000000000000extern void __abort(void); struct s { int elem:3; }; void foo(struct s *x); void foo(struct s *x) { if (x->elem == 0) { if (x->elem != 0 && x->elem != 1) __abort(); } } /* * check-name: kill-casts * check-command: test-linearize $file * * check-output-ignore * check-output-excludes: cast\\. */ sparse-0.5.1/validation/kill-computedgoto.c000066400000000000000000000003431314543357600207650ustar00rootroot00000000000000void foo(int a); void foo(int a) { void *l = &&end + 3; end: if (a * 0) goto *l; } /* * check-name: kill-computedgoto * check-command: test-linearize $file * * check-output-ignore * check-output-excludes: add\\. */ sparse-0.5.1/validation/kill-cse.c000066400000000000000000000005331314543357600170270ustar00rootroot00000000000000int foo(int a) { return ((a == 0) + 1) != ((a == 0) + 1); } /* * check-name: kill-cse * check-description: * Verify that instructions removed at CSE are * properly adjust the usage of their operands. * check-command: test-linearize -Wno-decl $file * * check-output-start foo: .L0: ret.32 $0 * check-output-end */ sparse-0.5.1/validation/kill-insert-branch.c000066400000000000000000000003751314543357600210200ustar00rootroot00000000000000void foo(int a) { int b = 1; if (a) b++; if (b) ; } void bar(int a) { if (a ? 1 : 2) ; } /* * check-name: kill insert-branch * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: select\\. */ sparse-0.5.1/validation/kill-load.c000066400000000000000000000006611314543357600171760ustar00rootroot00000000000000int keep(volatile int *p) { return *p && 0; } int kill(int *p, int i) { return *p && 0; } void ind(volatile int *p,int i) { int v = i++; if (i && 0) p[v]; } /* * check-name: kill-load * check-command: test-linearize -Wno-decl $file * check-description: * Check that loads are optimized away but only * when needed: * - non-volatile * - bb unreachable. * * check-output-ignore * check-output-pattern-1-times: load\\. */ sparse-0.5.1/validation/kill-phi-node.c000066400000000000000000000005511314543357600177600ustar00rootroot00000000000000void foo(int a, int *b, unsigned int g); void foo(int a, int *b, unsigned int g) { int d = 0; if ((!a || *b) && g) d = 16; else d = 8; } int bar(void); int bar(void) { int i; for (i = 0; i; i--) ; return 0; } /* * check-name: kill-phi-node * check-command: test-linearize $file * * check-output-ignore * check-output-excludes: phisrc\\. */ sparse-0.5.1/validation/kill-phi-ttsbb.c000066400000000000000000000006361314543357600201550ustar00rootroot00000000000000int def(void); void use(int); static int foo(int a, int b) { int c; if (a) c = 1; else c = def(); if (c) use(1); else use(0); } /* * check-name: kill-phi-ttsbb * check-description: * Verify if OP_PHI usage is adjusted after successful try_to_simplify_bb() * check-command: test-linearize $file * check-output-ignore * * check-output-excludes: phi\\. * check-output-excludes: phisrc\\. */ sparse-0.5.1/validation/kill-phi-ttsbb2.c000066400000000000000000000012621314543357600202330ustar00rootroot00000000000000extern int error(int); int foo(int perr); int foo(int perr) { int err = 0; int rc = 0; int j = 0; int i = 1; i && j++; i-- && j; i && j--; if (j != 1) { err = 1; if (perr) error(1); } if (err != 0) rc = 1; return rc; } /* * check-name: kill-phi-ttsbb2 * check-description: * Verify if OP_PHI usage is adjusted after successful try_to_simplify_bb() * check-warning: this test is sensitive to details of code generation * with proper bb packing (taking care of phi-nodes) it * will be optimized away and test nothing. You have been warned. * check-command: test-linearize $file * check-output-ignore * * check-output-excludes: VOID */ sparse-0.5.1/validation/kill-phisrc.c000066400000000000000000000004041314543357600175420ustar00rootroot00000000000000int foo(int a, int b) { int r = a + b; if (a && 0) { int s = r; if (b) s = 0; (void) s; } return 0; } /* * check-name: kill-phisrc * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: add\\. */ sparse-0.5.1/validation/kill-pure-call.c000066400000000000000000000006641314543357600201460ustar00rootroot00000000000000int side(int a); int pure(int a) __attribute__((pure)); int keep(int a) { return side(a) && 0; } int kill(int a) { return pure(a) && 0; } /* * check-name: kill-pure-call * check-command: test-linearize -Wno-decl $file * check-description: * See that the call is optimized away but only * when the function is "pure". * * check-output-ignore * check-output-contains: call\\..* side * check-output-excludes: call\\..* pure */ sparse-0.5.1/validation/kill-replaced-insn.c000066400000000000000000000016661314543357600210110ustar00rootroot00000000000000// See if the replaced operation is effectively killed or not static int kill_add(int a, int b) { return (a + b) && 0; } static int kill_scast(short a) { return ((int) a) && 0; } static int kill_ucast(unsigned char a) { return ((int) a) && 0; } static int kill_pcast(int *a) { return ((void*) a) && 0; } static int kill_fcast(double a) { return ((int) a) && 0; } static int kill_select(int a) { return (a ? 1 : 0) && 0; } static int kill_setval(int a) { l: return &&l && 0; } static int kill_load(int *a) { return *a && 0; } static int kill_store(int *a) { return (*a = 1) && 0; } /* * check-name: kill-replaced-insn * check-command: test-linearize $file * * check-output-ignore * check-output-excludes: add\\. * check-output-excludes: scast\\. * check-output-excludes: \\ cast.9 %r2 <- (32) %arg1 shl.32 %r4 <- %r2, $11 ret.32 %r4 bfus_init: .L2: scast.9 %r10 <- (32) %arg1 shl.32 %r12 <- %r10, $11 ret.32 %r12 bfu_get0: .L4: ret.32 $0 bfsu_init: .L6: cast.9 %r23 <- (32) %arg1 shl.32 %r25 <- %r23, $11 ret.32 %r25 bfss_init: .L8: scast.9 %r31 <- (32) %arg1 shl.32 %r33 <- %r31, $11 ret.32 %r33 bfs_get0: .L10: ret.32 $0 * check-output-end */ sparse-0.5.1/validation/linear/missing-insn-size.c000066400000000000000000000006611314543357600221660ustar00rootroot00000000000000int foo(int **a); int foo(int **a) { return **a; } /* * check-name: missing instruction's size * check-description: * sparse used to have a problem with *all* * double dereferencing due to missing a * call to examine_symbol_type(). The symptom * here is that the inner deref had no type. * check-command: test-linearize $file * check-output-ignore * * check-output-excludes: load\\s * check-output-contains: load\\. */ sparse-0.5.1/validation/linear/struct-init-full.c000066400000000000000000000007001314543357600220170ustar00rootroot00000000000000struct s { int a, b, c; }; struct s s_init_all(int a) { struct s s = { .a = a, .b = 42, .c = 123, }; return s; } /* * check-name: struct implicit init zero not needed * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-start s_init_all: .L4: store.32 %arg1 -> 0[s] store.32 $42 -> 4[s] store.32 $123 -> 8[s] load.96 %r8 <- 0[s] ret.96 %r8 * check-output-end */ sparse-0.5.1/validation/linear/struct-init-partial.c000066400000000000000000000011041314543357600225100ustar00rootroot00000000000000struct s { int a, b, c; }; struct s s_init_first(int a) { struct s s = { .a = a, }; return s; } struct s s_init_third(int a) { struct s s = { .c = a, }; return s; } /* * check-name: struct implicit init zero needed * check-command: test-linearize -Wno-decl $file * * check-output-start s_init_first: .L0: store.96 $0 -> 0[s] store.32 %arg1 -> 0[s] load.96 %r2 <- 0[s] ret.96 %r2 s_init_third: .L2: store.96 $0 -> 0[s] store.32 %arg1 -> 8[s] load.96 %r5 <- 0[s] ret.96 %r5 * check-output-end */ sparse-0.5.1/validation/local-label.c000066400000000000000000000003261314543357600174730ustar00rootroot00000000000000void 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 */ sparse-0.5.1/validation/logical.c000066400000000000000000000003131314543357600167320ustar00rootroot00000000000000extern 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 */ sparse-0.5.1/validation/loop-linearization.c000066400000000000000000000037471314543357600211550ustar00rootroot00000000000000extern int p(int); static int ffor(void) { int i; for (int i = 0; i < 10; i++) { if (!p(i)) return 0; } return 1; } static int fwhile(void) { int i = 0; while (i < 10) { if (!p(i)) return 0; i++; } return 1; } static int fdo(void) { int i = 0; do { if (!p(i)) return 0; } while (i++ < 10); return 1; } /* * check-name: loop-linearization * check-command: test-linearize $file * * check-output-start ffor: .L0: phisrc.32 %phi5(i) <- $0 br .L4 .L4: phi.32 %r1(i) <- %phi5(i), %phi6(i) setlt.32 %r2 <- %r1(i), $10 cbr %r2, .L1, .L3 .L1: call.32 %r4 <- p, %r1(i) cbr %r4, .L2, .L5 .L5: phisrc.32 %phi1(return) <- $0 br .L7 .L2: add.32 %r7 <- %r1(i), $1 phisrc.32 %phi6(i) <- %r7 br .L4 .L3: phisrc.32 %phi2(return) <- $1 br .L7 .L7: phi.32 %r5 <- %phi1(return), %phi2(return) ret.32 %r5 fwhile: .L8: phisrc.32 %phi11(i) <- $0 br .L12 .L12: phi.32 %r8(i) <- %phi11(i), %phi12(i) setlt.32 %r9 <- %r8(i), $10 cbr %r9, .L9, .L11 .L9: call.32 %r11 <- p, %r8(i) cbr %r11, .L14, .L13 .L13: phisrc.32 %phi7(return) <- $0 br .L15 .L14: add.32 %r14 <- %r8(i), $1 phisrc.32 %phi12(i) <- %r14 br .L12 .L11: phisrc.32 %phi8(return) <- $1 br .L15 .L15: phi.32 %r12 <- %phi7(return), %phi8(return) ret.32 %r12 fdo: .L16: phisrc.32 %phi16(i) <- $0 br .L17 .L17: phi.32 %r15(i) <- %phi16(i), %phi17(i) call.32 %r16 <- p, %r15(i) cbr %r16, .L18, .L20 .L20: phisrc.32 %phi13(return) <- $0 br .L22 .L18: add.32 %r19 <- %r15(i), $1 setlt.32 %r20 <- %r15(i), $10 phisrc.32 %phi17(i) <- %r19 cbr %r20, .L17, .L19 .L19: phisrc.32 %phi14(return) <- $1 br .L22 .L22: phi.32 %r17 <- %phi13(return), %phi14(return) ret.32 %r17 * check-output-end */ sparse-0.5.1/validation/member_of_typeof.c000066400000000000000000000003361314543357600206460ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/memops-volatile.c000066400000000000000000000004431314543357600204410ustar00rootroot00000000000000static int foo(volatile int *a, int v) { *a = v; return *a; } /* * check-name: memops-volatile * check-command: test-linearize $file * * check-output-start foo: .L0: store.32 %arg2 -> 0[%arg1] load.32 %r5 <- 0[%arg1] ret.32 %r5 * check-output-end */ sparse-0.5.1/validation/missing-ident.c000066400000000000000000000007251314543357600201010ustar00rootroot00000000000000int [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 */ sparse-0.5.1/validation/multi_typedef.c000066400000000000000000000003611314543357600201750ustar00rootroot00000000000000typedef 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 */ sparse-0.5.1/validation/nested-declarator.c000066400000000000000000000014551314543357600207300ustar00rootroot00000000000000typedef 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: */ sparse-0.5.1/validation/nested-declarator2.c000066400000000000000000000023321314543357600210050ustar00rootroot00000000000000typedef 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 '' 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: */ sparse-0.5.1/validation/nocast.c000066400000000000000000000120161314543357600166120ustar00rootroot00000000000000#define __nocast __attribute__((nocast)) typedef unsigned long __nocast ulong_nc_t; extern void use_val(ulong_nc_t); extern void use_ptr(ulong_nc_t *); /* use address */ static void good_use_address(void) { ulong_nc_t t; use_ptr(&t); } static ulong_nc_t *good_ret_address(void) { static ulong_nc_t t; return &t; } static ulong_nc_t good_deref(ulong_nc_t *t) { return *t; } /* assign value */ static ulong_nc_t t; static ulong_nc_t good_assign_self = t; static unsigned long good_assign_sametype = t; /* assign pointer */ static ulong_nc_t *good_ptr = &t; static ulong_nc_t *bad_ptr_to = 1UL; static unsigned long *bad_ptr_from = &t; /* arithmetic operation */ static ulong_nc_t good_arith(ulong_nc_t t, unsigned int n) { return t + n; } /* implicit cast to other types */ static unsigned long good_ret_samecast(ulong_nc_t t) { return t; } static unsigned long long bad_ret_biggercast(ulong_nc_t t) { return t; } static long bad_ret_signcast(ulong_nc_t t) { return t; } static short bad_ret_smallercast(ulong_nc_t t) { return t; } static void assign_val(ulong_nc_t t) { ulong_nc_t good_c = t; unsigned long good_ul = t; unsigned long long bad_ull = t; long bad_l = t; short bad_i = t; } static void assign_via_ptr(ulong_nc_t *t) { ulong_nc_t good_c = *t; unsigned long good_ul = *t; unsigned long long bad_ull = *t; long bad_l = *t; short bad_i = *t; } static void assign_ptr(ulong_nc_t *t) { ulong_nc_t *good_same_type = t; unsigned long *bad_mod = t; unsigned long long __nocast *bad_size = t; short __nocast *bad_i = t; long __nocast *bad_l = t; } /* implicit cast to nocast */ static void implicit_assign_to(void) { ulong_nc_t t; unsigned long ul = 1; unsigned short us = 1; unsigned long long ull = 1; long l = 1; t = ul; /* implicit to nocast from same type: OK? */ t = us; t = ull; t = l; } static void bad_implicit_arg_to(void) { unsigned long ul = 1; unsigned short us = 1; unsigned long long ull = 1; long l = 1; use_val(ul); /* implicit to nocast from same type: OK? */ use_val(us); use_val(ull); use_val(l); } /* implicit cast from nocast */ static unsigned long good_implicit_ret_ul(ulong_nc_t t) { return t; /* implicit to nocast from same type: OK? */ } static unsigned short bad_implicit_ret_us(ulong_nc_t t) { return t; } static unsigned long long bad_implicit_ret_ull(ulong_nc_t t) { return t; } static long bad_implicit_ret_l(ulong_nc_t t) { return t; } /* FIXME: explicit cast: should we complain? */ static ulong_nc_t good_samecast(ulong_nc_t v) { return (ulong_nc_t) v; } static ulong_nc_t bad_tocast(unsigned long v) { return (ulong_nc_t) v; } static unsigned long bad_fromcast(ulong_nc_t v) { return (unsigned long) v; } /* * check-name: nocast.c * * check-error-start nocast.c:34:33: warning: incorrect type in initializer (different base types) nocast.c:34:33: expected unsigned long [nocast] [usertype] *static [toplevel] bad_ptr_to nocast.c:34:33: got unsigned long nocast.c:34:33: warning: implicit cast to nocast type nocast.c:35:39: warning: incorrect type in initializer (different modifiers) nocast.c:35:39: expected unsigned long *static [toplevel] bad_ptr_from nocast.c:35:39: got unsigned long [nocast] * nocast.c:35:39: warning: implicit cast from nocast type nocast.c:50:16: warning: implicit cast from nocast type nocast.c:54:16: warning: implicit cast from nocast type nocast.c:58:16: warning: implicit cast from nocast type nocast.c:65:38: warning: implicit cast from nocast type nocast.c:66:22: warning: implicit cast from nocast type nocast.c:67:23: warning: implicit cast from nocast type nocast.c:74:38: warning: implicit cast from nocast type nocast.c:75:22: warning: implicit cast from nocast type nocast.c:76:23: warning: implicit cast from nocast type nocast.c:82:34: warning: incorrect type in initializer (different modifiers) nocast.c:82:34: expected unsigned long *bad_mod nocast.c:82:34: got unsigned long [nocast] [usertype] *t nocast.c:82:34: warning: implicit cast from nocast type nocast.c:83:49: warning: incorrect type in initializer (different type sizes) nocast.c:83:49: expected unsigned long long [nocast] *bad_size nocast.c:83:49: got unsigned long [nocast] [usertype] *t nocast.c:83:49: warning: implicit cast to/from nocast type nocast.c:84:33: warning: incorrect type in initializer (different type sizes) nocast.c:84:33: expected short [nocast] *bad_i nocast.c:84:33: got unsigned long [nocast] [usertype] *t nocast.c:84:33: warning: implicit cast to/from nocast type nocast.c:85:32: warning: implicit cast to/from nocast type nocast.c:98:13: warning: implicit cast to nocast type nocast.c:99:13: warning: implicit cast to nocast type nocast.c:100:13: warning: implicit cast to nocast type nocast.c:111:17: warning: implicit cast to nocast type nocast.c:112:17: warning: implicit cast to nocast type nocast.c:113:17: warning: implicit cast to nocast type nocast.c:124:16: warning: implicit cast from nocast type nocast.c:129:16: warning: implicit cast from nocast type nocast.c:134:16: warning: implicit cast from nocast type * check-error-end */ sparse-0.5.1/validation/noderef.c000066400000000000000000000014241314543357600167460ustar00rootroot00000000000000# 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] ** * check-error-end */ sparse-0.5.1/validation/non-pointer-null.c000066400000000000000000000002771314543357600205510ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/old-initializer-nowarn.c000066400000000000000000000002471314543357600217270ustar00rootroot00000000000000struct s { int i; }; static struct s the_s = { i: 1 }; /* * check-name: Old initializer with -Wno-old-initializer * check-command: sparse -Wno-old-initializer */ sparse-0.5.1/validation/old-initializer.c000066400000000000000000000003271314543357600204240ustar00rootroot00000000000000struct 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 */ sparse-0.5.1/validation/optim/000077500000000000000000000000001314543357600163075ustar00rootroot00000000000000sparse-0.5.1/validation/optim/binops-same-args.c000066400000000000000000000024711314543357600216260ustar00rootroot00000000000000typedef unsigned int u32; int ssub(int a) { return a - a; } u32 usub(u32 a) { return a - a; } int sdiv(int a) { return a / a; } u32 udiv(u32 a) { return a / a; } int smod(int a) { return a % a; } u32 umod(u32 a) { return a % a; } int seq(int a) { return a == a; } int sne(int a) { return a != a; } int slt(int a) { return a < a; } int sgt(int a) { return a > a; } int sle(int a) { return a <= a; } int sge(int a) { return a >= a; } u32 ueq(u32 a) { return a == a; } u32 une(u32 a) { return a != a; } u32 ult(u32 a) { return a < a; } u32 ugt(u32 a) { return a > a; } u32 ule(u32 a) { return a <= a; } u32 uge(u32 a) { return a >= a; } u32 xor(u32 a) { return a ^ a; } u32 ior(u32 a) { return a | a; } u32 and(u32 a) { return a & a; } /* * check-name: double-unop * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: sub\\. * check-output-contains: divs\\. * check-output-contains: divu\\. * check-output-contains: mods\\. * check-output-contains: modu\\. * check-output-excludes: seteq\\. * check-output-excludes: setne\\. * check-output-excludes: set[gl]t\\. * check-output-excludes: set[gl]e\\. * check-output-excludes: set[ab]\\. * check-output-excludes: set[ab]e\\. * check-output-excludes: xor\\. * check-output-excludes: or\\. * check-output-excludes: and\\. */ sparse-0.5.1/validation/optim/bool-context.c000066400000000000000000000004261314543357600210720ustar00rootroot00000000000000#define bool _Bool bool bool_ior(int a, int b) { return a || b; } bool bool_and(int a, int b) { return a && b; } /* * check-name: bool-context * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-pattern-4-times: setne\\..* %arg[12] */ sparse-0.5.1/validation/optim/bool-same-args.c000066400000000000000000000004471314543357600212700ustar00rootroot00000000000000static int ior(int a) { return a || a; } static int and(int a) { return a && a; } /* * check-name: bool-same-args * check-command: test-linearize $file * check-output-ignore * * check-output-excludes: or-bool\\. * check-output-excludes: and-bool\\. * check-output-contains: setne\\. */ sparse-0.5.1/validation/optim/bool-simplify.c000066400000000000000000000011061314543357600212360ustar00rootroot00000000000000int and_0(int a) { return a && 0; } int and_1(int a) { return a && 1; } int or_0(int a) { return a || 0; } int or_1(int a) { return a || 1; } /* * check-name: bool-simplify * check-command: test-linearize -Wno-decl $file * * check-output-start and_0: .L0: ret.32 $0 and_1: .L2: setne.1 %r8 <- %arg1, $0 cast.32 %r11 <- (1) %r8 ret.32 %r11 or_0: .L4: setne.1 %r14 <- %arg1, $0 cast.32 %r17 <- (1) %r14 ret.32 %r17 or_1: .L6: ret.32 $1 * check-output-end */ sparse-0.5.1/validation/optim/cse-commutativity.c000066400000000000000000000013761314543357600221500ustar00rootroot00000000000000static int add(int a, int b) { return (a + b) == (b + a); } static int mul(int a, int b) { return (a * b) == (b * a); } static int and(int a, int b) { return (a & b) == (b & a); } static int ior(int a, int b) { return (a | b) == (b | a); } static int xor(int a, int b) { return (a ^ b) == (b ^ a); } static int eq(int a, int b) { return (a == b) == (b == a); } static int ne(int a, int b) { return (a != b) == (b != a); } /* * check-name: cse-commutativity * check-command: test-linearize $file * check-output-ignore * * check-output-excludes: add\\. * check-output-excludes: muls\\. * check-output-excludes: and\\. * check-output-excludes: or\\. * check-output-excludes: xor\\. * check-output-excludes: seteq\\. * check-output-excludes: setne\\. */ sparse-0.5.1/validation/optim/cse-dual-compare.c000066400000000000000000000026001314543357600215720ustar00rootroot00000000000000static int eqeq(int a, int b) { return (a == b) == (b == a); } static int nene(int a, int b) { return (a != b) == (b != a); } static int ltgt(int a, int b) { return (a < b) == (b > a); } static int lege(int a, int b) { return (a <= b) == (b >= a); } static int gele(int a, int b) { return (a >= b) == (b <= a); } static int gtlt(int a, int b) { return (a > b) == (b < a); } static int eneqne(int a, int b) { return (a == b) == !(b != a); } static int enneeq(int a, int b) { return (a != b) == !(b == a); } static int enltle(int a, int b) { return (a < b) == !(b <= a); } static int enlelt(int a, int b) { return (a <= b) == !(b < a); } static int engegt(int a, int b) { return (a >= b) == !(b > a); } static int engtge(int a, int b) { return (a > b) == !(b >= a); } static int neeqne(int a, int b) { return (a == b) != (b != a); } static int neneeq(int a, int b) { return (a != b) != (b == a); } static int neltle(int a, int b) { return (a < b) != (b <= a); } static int nelelt(int a, int b) { return (a <= b) != (b < a); } static int negegt(int a, int b) { return (a >= b) != (b > a); } static int negtge(int a, int b) { return (a > b) != (b >= a); } /* * check-name: cse-dual-compare * check-command: test-linearize $file * check-output-ignore * check-known-to-fail * * check-output-excludes: set[gl][et]\\. * check-output-excludes: seteq\\. * check-output-excludes: setne\\. */ sparse-0.5.1/validation/optim/double-unop.c000066400000000000000000000005371314543357600207110ustar00rootroot00000000000000typedef unsigned int u32; u32 unotnot(u32 a) { return ~(~a); } int snotnot(int a) { return ~(~a); } u32 unegneg(int a) { return -(-a); } int snegneg(int a) { return -(-a); } /* * check-name: double-unop * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: not\\. * check-output-excludes: neg\\. */ sparse-0.5.1/validation/optim/fpcast-nop.c000066400000000000000000000006361314543357600205320ustar00rootroot00000000000000static float foof( float a) { return ( float) a; } static double food(double a) { return (double) a; } static long double fool(long double a) { return (long double) a; } /* * check-name: fpcast-nop * check-description: * Verify that unneeded casts between same-type * floats are also optimized away. * * check-command: test-linearize $file * check-output-ignore * * check-output-excludes: fpcast\\. */ sparse-0.5.1/validation/optim/muldiv-by-one.c000066400000000000000000000007361314543357600211500ustar00rootroot00000000000000typedef unsigned int ui; typedef int si; si smul1(si a) { return a * 1; } ui umul1(ui a) { return a * 1; } si sdiv1(si a) { return a / 1; } ui udiv1(ui a) { return a / 1; } si smod1(si a) { return a % 1; } ui umod1(ui a) { return a % 1; } /* * check-name: muldiv-by-one * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: mul[us]\\. * check-output-excludes: div[us]\\. * check-output-excludes: mod[us]\\. */ sparse-0.5.1/validation/optim/muldiv-by-zero.c000066400000000000000000000004151314543357600213400ustar00rootroot00000000000000typedef unsigned int ui; typedef int si; si smul0(si a) { return a * 0; } ui umul0(ui a) { return a * 0; } /* * check-name: muldiv-by-zero * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: mul[us]\\. */ sparse-0.5.1/validation/optim/muldiv-minus-one.c000066400000000000000000000007401314543357600216640ustar00rootroot00000000000000typedef unsigned int u32; int smulm1(int a) { return a * -1; } u32 umulm1(u32 a) { return a * (u32) -1; } int sdivm1(int a) { return a / -1; } u32 udivm1(u32 a) { return a / (u32) -1; } /* * check-name: muldiv-minus-one * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: mul[us]\\. * check-output-excludes: divs\\. * check-output-contains: neg\\. * check-output-contains: divu\\. * check-output-pattern-3-times: neg\\. */ sparse-0.5.1/validation/optim/setcc-setcc.c000066400000000000000000000012551314543357600206560ustar00rootroot00000000000000static _Bool blt(int a, int b) { return (a < b); } static _Bool bnge(int a, int b) { return !(a >= b); } static _Bool bgt(int a, int b) { return (a > b); } static _Bool bnle(int a, int b) { return !(a <= b); } static _Bool ble(int a, int b) { return (a <= b); } static _Bool bngt(int a, int b) { return !(a > b); } static _Bool bge(int a, int b) { return (a >= b); } static _Bool bnlt(int a, int b) { return !(a < b); } /* * check-name: optim/setcc-setcc * check-command: test-linearize $file * check-output-ignore * * check-output-excludes: set..\\.32 * check-output-excludes: setne\\.1 * check-output-excludes: seteq\\.1 * check-output-contains: set[gt][te]\\.1 */ sparse-0.5.1/validation/optim/setcc-seteq.c000066400000000000000000000005411314543357600206730ustar00rootroot00000000000000static _Bool beq0(int a) { return (a == 0); } static _Bool bnotneq0(int a) { return !(a != 0); } static _Bool bnot(int a) { return !a; } /* * check-name: optim/setcc-seteq * check-command: test-linearize $file * check-output-ignore * * check-output-excludes: set..\\.32 * check-output-excludes: setne\\.1 * check-output-contains: seteq\\.1 */ sparse-0.5.1/validation/optim/setcc-setne.c000066400000000000000000000005441314543357600206730ustar00rootroot00000000000000static _Bool bnoteq0(int a) { return !(a == 0); } static _Bool bne0(int a) { return (a != 0); } static _Bool bnotnot(int a) { return !!a; } /* * check-name: optim/setcc-setne * check-command: test-linearize $file * check-output-ignore * * check-output-excludes: set..\\.32 * check-output-excludes: seteq\\.1 * check-output-contains: setne\\.1 */ sparse-0.5.1/validation/optim/void-if-convert.c000066400000000000000000000005071314543357600214700ustar00rootroot00000000000000int foo(int a) { if (a) return 0; else return 1; return 2; } /* * check-name: Ignore VOID in if-convert * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: phisrc\\. * check-output-excludes: phi\\. * check-output-excludes: VOID * check-output-contains: seteq\\. */ sparse-0.5.1/validation/outer-scope.c000066400000000000000000000005161314543357600175720ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/phase2/000077500000000000000000000000001314543357600163415ustar00rootroot00000000000000sparse-0.5.1/validation/phase2/backslash000066400000000000000000000032761314543357600202270ustar00rootroot00000000000000/* * '\\' 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 */ \ sparse-0.5.1/validation/phase3/000077500000000000000000000000001314543357600163425ustar00rootroot00000000000000sparse-0.5.1/validation/phase3/comments000066400000000000000000000002621314543357600201120ustar00rootroot00000000000000/* * 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 sparse-0.5.1/validation/pragma-once.c000066400000000000000000000001111314543357600175050ustar00rootroot00000000000000#pragma once #include "pragma-once.c" /* * check-name: #pragma once */ sparse-0.5.1/validation/preprocessor/000077500000000000000000000000001314543357600177055ustar00rootroot00000000000000sparse-0.5.1/validation/preprocessor/counter1.c000066400000000000000000000002211314543357600216040ustar00rootroot00000000000000__COUNTER__ __COUNTER__ /* * check-name: __COUNTER__ #1 * check-command: sparse -E $file * * check-output-start 0 1 * check-output-end */ sparse-0.5.1/validation/preprocessor/counter2.c000066400000000000000000000004301314543357600216070ustar00rootroot00000000000000__FILE__ __COUNTER__ #include __FILE__ __COUNTER__ /* * check-name: __COUNTER__ #2 * check-command: sparse -Ipreprocessor -E $file * * check-output-start "preprocessor/counter2.c" 0 "preprocessor/counter2.h" 1 "preprocessor/counter2.c" 2 * check-output-end */ sparse-0.5.1/validation/preprocessor/counter2.h000066400000000000000000000000251314543357600216140ustar00rootroot00000000000000__FILE__ __COUNTER__ sparse-0.5.1/validation/preprocessor/counter3.c000066400000000000000000000004121314543357600216100ustar00rootroot00000000000000/* * check-name: __COUNTER__ #3 * check-command: sparse -Ipreprocessor -E preprocessor/counter1.c $file * * check-output-start 0 1 "preprocessor/counter2.c" 0 "preprocessor/counter2.h" 1 "preprocessor/counter2.c" 2 * check-output-end */ #include "counter2.c" sparse-0.5.1/validation/preprocessor/dump-macros-empty.c000066400000000000000000000002531314543357600234340ustar00rootroot00000000000000/* * check-name: dump-macros with empty file * check-command: sparse -E -dD empty-file * * check-output-ignore check-output-pattern-1-times: #define __CHECKER__ 1 */ sparse-0.5.1/validation/preprocessor/dump-macros-multi.c000066400000000000000000000002651314543357600234330ustar00rootroot00000000000000/* * check-name: dump-macros with multiple files * check-command: sparse -E -dD empty-file $file * * check-output-ignore check-output-pattern-2-times: #define __CHECKER__ 1 */ sparse-0.5.1/validation/preprocessor/dump-macros.c000066400000000000000000000006021314543357600222760ustar00rootroot00000000000000#define ABC abc #undef ABC #define DEF def #undef DEF #define DEF xyz #define NYDEF ydef /* * check-name: dump-macros * check-command: sparse -E -dD -DIJK=ijk -UNDEF -UNYDEF $file * * check-output-ignore check-output-pattern-1-times: #define __CHECKER__ 1 check-output-contains: #define IJK ijk check-output-contains: #define DEF xyz check-output-contains: #define NYDEF ydef */ sparse-0.5.1/validation/preprocessor/early-escape.c000066400000000000000000000007271314543357600224310ustar00rootroot00000000000000#if 0 "\l" #endif /* * check-description: * Following the C standard, escape conversion must be * done in phase 5, just after preprocessing and just * before string concatenation. So we're not supposed * to receive a diagnostic for an unknown escape char * for a token which is excluded by the preprocessor. * check-name: early-escape * check-command: sparse -E $file * * check-output-start * check-output-end * * check-error-start * check-error-end */ sparse-0.5.1/validation/preprocessor/predef-char-bit.c000066400000000000000000000004431314543357600230060ustar00rootroot00000000000000#define TEST_BIT(X, T) if (__ ## X ## _BIT__ != 8 * sizeof(T)) return 1 int test(void) { TEST_BIT(CHAR, char); return 0; } /* * check-name: predefined ___BIT__ * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-contains: ret\\..*\\$0 */ sparse-0.5.1/validation/preprocessor/predef-max.c000066400000000000000000000005341314543357600221030ustar00rootroot00000000000000#define TEST_MAX(X, Z) if (X != ((~ Z) >> 1)) return 1 int test_max(void) { TEST_MAX(__INT_MAX__, 0U); TEST_MAX(__LONG_MAX__, 0UL); TEST_MAX(__LONG_LONG_MAX__, 0ULL); return 0; } /* * check-name: predefined ___MAX__ * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-contains: ret\\..*\\$0 */ sparse-0.5.1/validation/preprocessor/predef-sizeof.c000066400000000000000000000010011314543357600226030ustar00rootroot00000000000000#define TEST(X, T) if (__SIZEOF_ ## X ## __ != sizeof(T)) return 1 int test_sizeof(void) { TEST(SHORT, short); TEST(INT, int); TEST(LONG, long); TEST(LONG_LONG, long long); TEST(INT128, __int128); TEST(SIZE_T, __SIZE_TYPE__); TEST(POINTER, void*); TEST(FLOAT, float); TEST(DOUBLE, double); TEST(LONG_DOUBLE, long double); return 0; } /* * check-name: predefined __SIZEOF___ * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-contains: ret\\..*\\$0 */ sparse-0.5.1/validation/preprocessor/preprocessor1.c000066400000000000000000000003601314543357600226570ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/preprocessor/preprocessor10.c000066400000000000000000000005111314543357600227350ustar00rootroot00000000000000/* 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 */ sparse-0.5.1/validation/preprocessor/preprocessor11.c000066400000000000000000000020451314543357600227420ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/preprocessor/preprocessor12.c000066400000000000000000000003161314543357600227420ustar00rootroot00000000000000/* * 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 */ sparse-0.5.1/validation/preprocessor/preprocessor13.c000066400000000000000000000006741314543357600227520ustar00rootroot00000000000000/* * 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 */ sparse-0.5.1/validation/preprocessor/preprocessor14.c000066400000000000000000000003701314543357600227440ustar00rootroot00000000000000/* * 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-command: sparse -E $file * * check-output-start * check-output-end */ sparse-0.5.1/validation/preprocessor/preprocessor15.c000066400000000000000000000003341314543357600227450ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/preprocessor/preprocessor16.c000066400000000000000000000014711314543357600227510ustar00rootroot00000000000000#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 (# ). 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 */ sparse-0.5.1/validation/preprocessor/preprocessor17.c000066400000000000000000000002771314543357600227550ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/preprocessor/preprocessor18.c000066400000000000000000000005571314543357600227570ustar00rootroot00000000000000/* 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 */ sparse-0.5.1/validation/preprocessor/preprocessor19.c000066400000000000000000000006711314543357600227550ustar00rootroot00000000000000/* 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 */ sparse-0.5.1/validation/preprocessor/preprocessor2.c000066400000000000000000000003201314543357600226540ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/preprocessor/preprocessor20.c000066400000000000000000000003071314543357600227410ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/preprocessor/preprocessor20.h000066400000000000000000000000451314543357600227450ustar00rootroot00000000000000#ifdef X B #endif #ifndef Y A #endif sparse-0.5.1/validation/preprocessor/preprocessor21.c000066400000000000000000000004551314543357600227460ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/preprocessor/preprocessor22.c000066400000000000000000000014441314543357600227460ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/preprocessor/preprocessor23.c000066400000000000000000000025011314543357600227420ustar00rootroot00000000000000#define H(x,...) ,##x##__VA_ARGS__##,##__VA_ARGS__ H() H(x) H(,) H(x,) H(,x) H(x,x) #define I(x,...) ,##x##__VA_ARGS__ I() I(x) I(,) I(x,) I(,x) I(x,x) /* * check-name: Preprocessor #23 * check-command: sparse -E $file * * check-output-start , ,x ,, ,x, ,x,x ,xx,x ,x , ,x ,x ,xx * check-output-end * * check-error-start preprocessor/preprocessor23.c:3:1: error: '##' failed: concatenation is not a valid token preprocessor/preprocessor23.c:4:1: error: '##' failed: concatenation is not a valid token preprocessor/preprocessor23.c:5:1: error: '##' failed: concatenation is not a valid token preprocessor/preprocessor23.c:5:1: error: '##' failed: concatenation is not a valid token preprocessor/preprocessor23.c:6:1: error: '##' failed: concatenation is not a valid token preprocessor/preprocessor23.c:6:1: error: '##' failed: concatenation is not a valid token preprocessor/preprocessor23.c:7:1: error: '##' failed: concatenation is not a valid token preprocessor/preprocessor23.c:7:1: error: '##' failed: concatenation is not a valid token preprocessor/preprocessor23.c:10:1: error: '##' failed: concatenation is not a valid token preprocessor/preprocessor23.c:12:1: error: '##' failed: concatenation is not a valid token preprocessor/preprocessor23.c:14:1: error: '##' failed: concatenation is not a valid token * check-error-end */ sparse-0.5.1/validation/preprocessor/preprocessor3.c000066400000000000000000000011461314543357600226640ustar00rootroot00000000000000/* * 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 */ sparse-0.5.1/validation/preprocessor/preprocessor4.c000066400000000000000000000003641314543357600226660ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/preprocessor/preprocessor5.c000066400000000000000000000003241314543357600226630ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/preprocessor/preprocessor6.c000066400000000000000000000016311314543357600226660ustar00rootroot00000000000000/* 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 */ sparse-0.5.1/validation/preprocessor/preprocessor7.c000066400000000000000000000003141314543357600226640ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/preprocessor/preprocessor8.c000066400000000000000000000016451314543357600226750ustar00rootroot00000000000000#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 */ sparse-0.5.1/validation/preprocessor/preprocessor9.c000066400000000000000000000004331314543357600226700ustar00rootroot00000000000000/* 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 */ sparse-0.5.1/validation/preprocessor/stringify.c000066400000000000000000000004471314543357600220740ustar00rootroot00000000000000#define A(x) #x A('a') A("a") A(a) A(\n) A('\n') A("\n") A('"') A("a\nb") A(L"a\nb") A('\12') /* * check-name: Preprocessor #14 * check-command: sparse -E $file * * check-output-start "'a'" "\"a\"" "a" "\n" "'\\n'" "\"\\n\"" "'\"'" "\"a\\nb\"" "L\"a\\nb\"" "'\\12'" * check-output-end */ sparse-0.5.1/validation/preprocessor/wide.c000066400000000000000000000003431314543357600210010ustar00rootroot00000000000000#define A(x) L##x A('a') A("bc") /* * check-name: wide char token-pasting * check-description: Used to cause infinite recursion. * check-command: sparse -E $file * * check-output-start L'a' L"bc" * check-output-end */ sparse-0.5.1/validation/prototype.c000066400000000000000000000001771314543357600173750ustar00rootroot00000000000000static int prototype(void); /* * check-name: Compile skip function prototype * check-command: sparsec -c $file -o tmp.o */ sparse-0.5.1/validation/ptr-inherit.c000066400000000000000000000037561314543357600176030ustar00rootroot00000000000000#define __user __attribute__((address_space(1))) #define __noderef __attribute__((noderef)) #define __bitwise __attribute__((bitwise)) #define __nocast __attribute__((nocast)) #define __safe __attribute__((safe)) /* Should be inherited? */ static void test_const(void) { const int o; int *p = &o; /* check-should-fail */ } static void test_volatile(void) { volatile int o; int *p = &o; /* check-should-fail */ } static void test_noderef(void) { int __noderef o; int *p = &o; /* check-should-fail */ } static void test_bitwise(void) { int __bitwise o; int *p = &o; /* check-should-fail */ } static void test_user(void) { int __user o; int *p = &o; /* check-should-fail */ } static void test_nocast(void) { int __nocast o; int __nocast *p = &o; /* check-should-pass */ } /* Should be ignored? */ static void test_static(void) { /* storage is not inherited */ static int o; int *p = &o; /* check-should-pass */ } static void test_tls(void) { /* storage is not inherited */ static __thread int o; int *p = &o; /* check-should-pass */ } /* * check-name: ptr-inherit.c * * check-error-start ptr-inherit.c:12:19: warning: incorrect type in initializer (different modifiers) ptr-inherit.c:12:19: expected int *p ptr-inherit.c:12:19: got int const * ptr-inherit.c:18:19: warning: incorrect type in initializer (different modifiers) ptr-inherit.c:18:19: expected int *p ptr-inherit.c:18:19: got int volatile * ptr-inherit.c:24:19: warning: incorrect type in initializer (different modifiers) ptr-inherit.c:24:19: expected int *p ptr-inherit.c:24:19: got int [noderef] * ptr-inherit.c:30:19: warning: incorrect type in initializer (different base types) ptr-inherit.c:30:19: expected int *p ptr-inherit.c:30:19: got restricted int * ptr-inherit.c:36:19: warning: incorrect type in initializer (different address spaces) ptr-inherit.c:36:19: expected int *p ptr-inherit.c:36:19: got int * * check-error-end */ sparse-0.5.1/validation/pure-function.c000066400000000000000000000003331314543357600201200ustar00rootroot00000000000000 static __attribute__((__pure__)) int pure1(void) { int i = 0; return i; } static __attribute__((__pure__)) void *pure2(void) { void *i = (void *)0; return i; } /* * check-name: Pure function attribute */ sparse-0.5.1/validation/reserved.c000066400000000000000000000164251314543357600171520ustar00rootroot00000000000000static int (auto); static int (break); static int (case); static int (char); static int (const); static int (__const); static int (__const__); static int (continue); static int (default); static int (do); static int (double); static int (else); static int (enum); static int (extern); static int (float); static int (for); static int (goto); static int (if); static int (inline); static int (__inline); static int (__inline__); static int (int); static int (long); static int (register); static int (restrict); static int (__restrict); static int (__restrict__); static int (return); static int (short); static int (signed); static int (sizeof); static int (static); static int (struct); static int (switch); static int (typedef); static int (union); static int (unsigned); static int (void); static int (volatile); static int (volatile); static int (__volatile); static int (__volatile__); static int (while); static int (_Alignas); static int (_Alignof); static int (_Atomic); static int (_Bool); static int (_Complex); static int (_Generic); static int (_Imaginary); static int (_Noreturn); static int (_Static_assert); static int (_Thread_local); // Sparse extensions static int (__context__); static int (__range__); static int (__sizeof_ptr__); // GCC extensions static int (__alignof); static int (__alignof__); static int (asm); // not reserved! static int (__asm); static int (__asm__); static int (__label__); static int (__thread); static int (typeof); static int (__typeof); static int (__typeof__); static int (__int128); static int (__int128_t); static int (__uint128_t); static int (__builtin_ms_va_list); static int (__builtin_offsetof); static int (__builtin_types_compatible_p); static int (__builtin_va_list); /* * check-name: const et.al. are reserved identifiers * check-error-start: reserved.c:1:12: error: Trying to use reserved word 'auto' as identifier reserved.c:2:12: error: Trying to use reserved word 'break' as identifier reserved.c:3:12: error: Trying to use reserved word 'case' as identifier reserved.c:4:12: error: Trying to use reserved word 'char' as identifier reserved.c:5:12: error: Trying to use reserved word 'const' as identifier reserved.c:6:12: error: Trying to use reserved word '__const' 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 'continue' as identifier reserved.c:9:12: error: Trying to use reserved word 'default' as identifier reserved.c:10:12: error: Trying to use reserved word 'do' as identifier reserved.c:11:12: error: Trying to use reserved word 'double' as identifier reserved.c:12:12: error: Trying to use reserved word 'else' as identifier reserved.c:13:12: error: Trying to use reserved word 'enum' as identifier reserved.c:14:12: error: Trying to use reserved word 'extern' as identifier reserved.c:15:12: error: Trying to use reserved word 'float' as identifier reserved.c:16:12: error: Trying to use reserved word 'for' as identifier reserved.c:17:12: error: Trying to use reserved word 'goto' as identifier reserved.c:18:12: error: Trying to use reserved word 'if' as identifier reserved.c:19:12: error: Trying to use reserved word 'inline' as identifier reserved.c:20:12: error: Trying to use reserved word '__inline' as identifier reserved.c:21:12: error: Trying to use reserved word '__inline__' as identifier reserved.c:22:12: error: Trying to use reserved word 'int' as identifier reserved.c:23:12: error: Trying to use reserved word 'long' as identifier reserved.c:24:12: error: Trying to use reserved word 'register' as identifier reserved.c:25:12: error: Trying to use reserved word 'restrict' as identifier reserved.c:26:12: error: Trying to use reserved word '__restrict' as identifier reserved.c:27:12: error: Trying to use reserved word '__restrict__' as identifier reserved.c:28:12: error: Trying to use reserved word 'return' as identifier reserved.c:29:12: error: Trying to use reserved word 'short' as identifier reserved.c:30:12: error: Trying to use reserved word 'signed' as identifier reserved.c:31:12: error: Trying to use reserved word 'sizeof' as identifier reserved.c:32:12: error: Trying to use reserved word 'static' as identifier reserved.c:33:12: error: Trying to use reserved word 'struct' as identifier reserved.c:34:12: error: Trying to use reserved word 'switch' as identifier reserved.c:35:12: error: Trying to use reserved word 'typedef' as identifier reserved.c:36:12: error: Trying to use reserved word 'union' as identifier reserved.c:37:12: error: Trying to use reserved word 'unsigned' as identifier reserved.c:38:12: error: Trying to use reserved word 'void' as identifier reserved.c:39:12: error: Trying to use reserved word 'volatile' as identifier reserved.c:40:12: error: Trying to use reserved word 'volatile' as identifier reserved.c:41:12: error: Trying to use reserved word '__volatile' as identifier reserved.c:42:12: error: Trying to use reserved word '__volatile__' as identifier reserved.c:43:12: error: Trying to use reserved word 'while' as identifier reserved.c:45:12: error: Trying to use reserved word '_Alignas' as identifier reserved.c:46:12: error: Trying to use reserved word '_Alignof' as identifier reserved.c:47:12: error: Trying to use reserved word '_Atomic' as identifier reserved.c:48:12: error: Trying to use reserved word '_Bool' as identifier reserved.c:49:12: error: Trying to use reserved word '_Complex' as identifier reserved.c:50:12: error: Trying to use reserved word '_Generic' as identifier reserved.c:51:12: error: Trying to use reserved word '_Imaginary' as identifier reserved.c:52:12: error: Trying to use reserved word '_Noreturn' as identifier reserved.c:53:12: error: Trying to use reserved word '_Static_assert' as identifier reserved.c:54:12: error: Trying to use reserved word '_Thread_local' as identifier reserved.c:57:12: error: Trying to use reserved word '__context__' as identifier reserved.c:58:12: error: Trying to use reserved word '__range__' as identifier reserved.c:59:12: error: Trying to use reserved word '__sizeof_ptr__' as identifier reserved.c:62:12: error: Trying to use reserved word '__alignof' as identifier reserved.c:63:12: error: Trying to use reserved word '__alignof__' as identifier reserved.c:65:12: error: Trying to use reserved word '__asm' as identifier reserved.c:66:12: error: Trying to use reserved word '__asm__' as identifier reserved.c:67:12: error: Trying to use reserved word '__label__' as identifier reserved.c:68:12: error: Trying to use reserved word '__thread' as identifier reserved.c:69:12: error: Trying to use reserved word 'typeof' as identifier reserved.c:70:12: error: Trying to use reserved word '__typeof' as identifier reserved.c:71:12: error: Trying to use reserved word '__typeof__' as identifier reserved.c:73:12: error: Trying to use reserved word '__int128' as identifier reserved.c:74:12: error: Trying to use reserved word '__int128_t' as identifier reserved.c:75:12: error: Trying to use reserved word '__uint128_t' as identifier reserved.c:77:12: error: Trying to use reserved word '__builtin_ms_va_list' as identifier reserved.c:78:12: error: Trying to use reserved word '__builtin_offsetof' as identifier reserved.c:79:12: error: Trying to use reserved word '__builtin_types_compatible_p' as identifier reserved.c:80:12: error: Trying to use reserved word '__builtin_va_list' as identifier * check-error-end: */ sparse-0.5.1/validation/restrict-array.c000066400000000000000000000017231314543357600203010ustar00rootroot00000000000000#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); #undef __restrict_arr #define __restrict_arr __restrict__ struct gaicb; extern int getaddrinfo_a (int __mode, struct gaicb *__list[__restrict_arr], int __ent, struct sigevent *__restrict __sig); #undef __restrict_arr #define __restrict_arr restrict typedef struct re_pattern_buffer regex_t; typedef int regoff_t; typedef struct { regoff_t rm_so; /* Byte offset from string's start to substring's start. */ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ } regmatch_t; typedef unsigned long int size_t; extern int regexec (const regex_t *__restrict __preg, const char *__restrict __string, size_t __nmatch, regmatch_t __pmatch[__restrict_arr], int __eflags); /* * check-name: restrict array attribute */ sparse-0.5.1/validation/restricted-typeof.c000066400000000000000000000002741314543357600210020ustar00rootroot00000000000000typedef 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 */ sparse-0.5.1/validation/sizeof-bool.c000066400000000000000000000005131314543357600175520ustar00rootroot00000000000000static 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-command: sparse -Wsizeof-bool $file * check-error-start sizeof-bool.c:3:16: warning: expression using sizeof bool * check-error-end */ sparse-0.5.1/validation/sizeof-compound-postfix.c000066400000000000000000000002261314543357600221360ustar00rootroot00000000000000struct foo {int x, y;}; static int a(void) { return sizeof (struct foo){0,1}.y; } /* * check-name: Handling of sizeof compound-literal . member */ sparse-0.5.1/validation/specifiers1.c000066400000000000000000000032131314543357600175370ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/specifiers2.c000066400000000000000000000151561314543357600175510ustar00rootroot00000000000000typedef 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 */ sparse-0.5.1/validation/static-forward-decl.c000066400000000000000000000003421314543357600211600ustar00rootroot00000000000000static int f(void); int f(void) { return 0; } /* * check-name: static forward declaration * * check-error-start static-forward-decl.c:3:5: warning: symbol 'f' was not declared. Should it be static? * check-error-end */ sparse-0.5.1/validation/static_assert.c000066400000000000000000000032301314543357600201710ustar00rootroot00000000000000_Static_assert(1, "global ok"); struct foo { _Static_assert(1, "struct ok"); }; void bar(void) { _Static_assert(1, " func1 ok"); int i; i = 0; _Static_assert(1, " func2 ok"); if (1) { _Static_assert(1, " func3 ok"); } } _Static_assert(0, "expected assertion failure"); static int f; _Static_assert(f, "non-constant expression"); static int *p; _Static_assert(p, "non-integer expression"); _Static_assert(0.1, "float expression"); _Static_assert(!0 == 1, "non-trivial expression"); static char array[4]; _Static_assert(sizeof(array) == 4, "sizeof expression"); static const char non_literal_string[] = "non literal string"; _Static_assert(0, non_literal_string); _Static_assert(1 / 0, "invalid expression: should not show up?"); struct s { char arr[16]; _Static_assert(1, "inside struct"); }; union u { char c; int i; _Static_assert(1, "inside union"); }; _Static_assert(sizeof(struct s) == 16, "sizeof assertion"); _Static_assert(1, ); _Static_assert(, ""); _Static_assert(,); /* * check-name: static assertion * * check-error-start static_assert.c:19:16: error: static assertion failed: "expected assertion failure" static_assert.c:22:16: error: bad constant expression static_assert.c:25:16: error: bad constant expression static_assert.c:27:16: error: bad constant expression static_assert.c:35:19: error: bad or missing string literal static_assert.c:37:18: error: bad constant expression static_assert.c:52:19: error: bad or missing string literal static_assert.c:53:16: error: Expected constant expression static_assert.c:54:16: error: Expected constant expression static_assert.c:54:17: error: bad or missing string literal * check-error-end */ sparse-0.5.1/validation/struct-as.c000066400000000000000000000004621314543357600172520ustar00rootroot00000000000000/* * 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 */ sparse-0.5.1/validation/struct-attribute-placement.c000066400000000000000000000001551314543357600226170ustar00rootroot00000000000000struct __attribute__((__aligned__(16))) foo { int a; }; /* * check-name: struct attribute placement */ sparse-0.5.1/validation/struct-ns1.c000066400000000000000000000007171314543357600173530ustar00rootroot00000000000000// 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 */ sparse-0.5.1/validation/struct-ns2.c000066400000000000000000000007311314543357600173500ustar00rootroot00000000000000static 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 * * check-error-start struct-ns2.c:2:11: warning: bad scope for 'struct Bar' struct-ns2.c:12:14: error: incomplete type/unknown size for 'y' struct-ns2.c:13:5: error: using member 'i' in incomplete 'struct Bar' * check-error-end */ sparse-0.5.1/validation/struct-size1.c000066400000000000000000000004551314543357600177040ustar00rootroot00000000000000struct 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 */ sparse-0.5.1/validation/tautological-compare.c000066400000000000000000000030071314543357600214360ustar00rootroot00000000000000typedef unsigned int u32; int seq(int a) { return a == a; } int sne(int a) { return a != a; } int slt(int a) { return a < a; } int sgt(int a) { return a > a; } int sle(int a) { return a <= a; } int sge(int a) { return a >= a; } u32 ueq(u32 a) { return a == a; } u32 une(u32 a) { return a != a; } u32 ult(u32 a) { return a < a; } u32 ugt(u32 a) { return a > a; } u32 ule(u32 a) { return a <= a; } u32 uge(u32 a) { return a >= a; } /* * check-name: tautological-compare * check-command: sparse -Wno-decl -Wtautological-compare $file * * check-error-start tautological-compare.c:3:30: warning: self-comparison always evaluates to true tautological-compare.c:4:30: warning: self-comparison always evaluates to false tautological-compare.c:5:29: warning: self-comparison always evaluates to false tautological-compare.c:6:29: warning: self-comparison always evaluates to false tautological-compare.c:7:30: warning: self-comparison always evaluates to true tautological-compare.c:8:30: warning: self-comparison always evaluates to true tautological-compare.c:10:30: warning: self-comparison always evaluates to true tautological-compare.c:11:30: warning: self-comparison always evaluates to false tautological-compare.c:12:29: warning: self-comparison always evaluates to false tautological-compare.c:13:29: warning: self-comparison always evaluates to false tautological-compare.c:14:30: warning: self-comparison always evaluates to true tautological-compare.c:15:30: warning: self-comparison always evaluates to true * check-error-end */ sparse-0.5.1/validation/test-be.c000066400000000000000000000011011314543357600166570ustar00rootroot00000000000000int 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 */ sparse-0.5.1/validation/test-suite000077500000000000000000000212541314543357600172170ustar00rootroot00000000000000#!/bin/sh #set -x cd $(dirname "$0") default_path=".." default_cmd="sparse \$file" tests_list=`find . -name '*.c' | sed -e 's#^\./\(.*\)#\1#' | sort` prog_name=`basename $0` if [ ! -x "$default_path/sparse-llvm" ]; then disabled_cmds="sparsec sparsei sparse-llvm" fi # flags: # - some tests gave an unexpected result failed=0 # counts: # - tests that have not been converted to test-suite format # - tests that are disabled # - tests that passed # - tests that failed # - tests that failed but are known to fail unhandled_tests=0 disabled_tests=0 ok_tests=0 ko_tests=0 known_ko_tests=0 # defaults to not verbose [ -z "$V" ] && V=0 ## # get_tag_value(file) - get the 'check-<...>' tags & values get_tag_value() { check_name="" check_command="$default_cmd" check_exit_value=0 check_timeout=0 check_known_to_fail=0 check_error_ignore=0 check_output_ignore=0 check_output_contains=0 check_output_excludes=0 check_output_pattern=0 lines=$(grep 'check-[a-z-]*' $1 | \ sed -e 's/^.*\(check-[a-z-]*:*\) *\(.*\)$/\1 \2/') while read tag val; do #echo "-> tag: '$tag'" #echo "-> val: '$val'" case $tag in check-name:) check_name="$val" ;; check-command:) check_command="$val" ;; check-exit-value:) check_exit_value="$val" ;; check-timeout:) [ -z "$val" ] && val=1 check_timeout="$val" ;; check-known-to-fail) check_known_to_fail=1 ;; check-error-ignore) check_error_ignore=1 ;; check-output-ignore) check_output_ignore=1 ;; check-output-contains:) check_output_contains=1 ;; check-output-excludes:) check_output_excludes=1 ;; check-output-pattern-) check_output_pattern=1 ;; esac done << EOT $lines EOT } ## # helper for has_(each|none)_patterns() has_patterns() { ifile="$1" patt="$2" ofile="$3" cmp="$4" grep "$patt:" "$ifile" | \ sed -e "s/^.*$patt: *\(.*\)$/\1/" | \ while read val; do grep -s -q "$val" "$ofile" if [ "$?" $cmp 0 ]; then return 1 fi done return $? } ## # has_each_patterns(ifile tag ofile) - does ofile contains some # of the patterns given by ifile's tags? # # returns 0 if all present, 1 otherwise has_each_patterns() { has_patterns "$1" "$2" "$3" -ne } ## # has_none_patterns(ifile tag ofile) - does ofile contains some # of the patterns given by ifile's tags? # # returns 1 if any present, 0 otherwise has_none_patterns() { has_patterns "$1" "$2" "$3" -eq } ## # nbr_patterns(ifile tag ofile) - does ofile contains the # the patterns given by ifile's tags # the right number of time? nbr_patterns() { ifile="$1" patt="$2" ofile="$3" grep "$patt-[0-9][0-9]*-times:" "$ifile" | \ sed -e "s/^.*$patt-\([0-9][0-9]*\)-times: *\(.*\)/\1 \2/" | \ while read nbr pat; do n=$(grep -s "$pat" "$ofile" | wc -l) if [ "$n" -ne "$nbr" ]; then return 1 fi done 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() { [ "$quiet" -ne 1 ] && 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. # - 3 if the test is disabled. do_test() { test_failed=0 file="$1" get_tag_value $file # can this test be handled by test-suite ? # (it has to have a check-name key in it) if [ "$check_name" = "" ]; then echo "warning: test '$file' unhandled" unhandled_tests=$(($unhandled_tests + 1)) return 2 fi test_name="$check_name" # does the test provide a specific command ? if [ "$check_command" = "" ]; then check_command="$defaut_command" fi # check for disabled commands set -- $check_command base_cmd=$1 for i in $disabled_cmds; do if [ "$i" = "$base_cmd" ] ; then disabled_tests=$(($disabled_tests + 1)) echo " DISABLE $test_name ($file)" return 3 fi done cmd=`eval echo $default_path/$check_command` echo " TEST $test_name ($file)" verbose "Using command : $cmd" # grab the expected exit value expected_exit_value=$check_exit_value verbose "Expecting exit value: $expected_exit_value" # do we want a timeout? if [ $check_timeout -ne 0 ]; then cmd="timeout -k 1s $check_timeout $cmd" fi # grab the actual output & exit value $cmd 1> $file.output.got 2> $file.error.got actual_exit_value=$? must_fail=$check_known_to_fail quiet=0 [ $must_fail -eq 1 ] && [ $V -eq 0 ] && quiet=1 known_ko_tests=$(($known_ko_tests + $must_fail)) for stream in output error; do eval ignore=\$check_${stream}_ignore [ $ignore -eq 1 ] && continue # grab the expected output sed -n "/check-$stream-start/,/check-$stream-end/p" $file \ | grep -v check-$stream > "$file".$stream.expected 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." [ $quiet -ne 1 ] && 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 # verify the 'check-output-contains/excludes' tags if [ $check_output_contains -eq 1 ]; then has_each_patterns "$file" 'check-output-contains' $file.output.got if [ "$?" -ne "0" ]; then error "Actual output doesn't contain some of the expected patterns." test_failed=1 fi fi if [ $check_output_excludes -eq 1 ]; then has_none_patterns "$file" 'check-output-excludes' $file.output.got if [ "$?" -ne "0" ]; then error "Actual output contains some patterns which are not expected." test_failed=1 fi fi if [ $check_output_pattern -eq 1 ]; then # verify the 'check-output-pattern-X-times' tags nbr_patterns "$file" 'check-output-pattern' $file.output.got if [ "$?" -ne "0" ]; then error "Actual output doesn't contain the pattern the expected number." test_failed=1 fi fi [ "$test_failed" -eq "$must_fail" ] || failed=1 if [ "$must_fail" -eq "1" ]; then if [ "$test_failed" -eq "1" ]; then echo "info: test '$file' is known to fail" else echo "error: test '$file' is known to fail but succeed!" test_failed=1 fi fi if [ "$test_failed" -eq "1" ]; then ko_tests=$(($ko_tests + 1)) else ok_tests=$(($ok_tests + 1)) rm -f $file.{error,output}.{expected,got,diff} fi return $test_failed } do_test_suite() { for i in $tests_list; do do_test "$i" done # prints some numbers tests_nr=$(($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 if [ "$disabled_tests" -ne "0" ]; then echo "$disabled_tests tests were disabled" 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 $failed sparse-0.5.1/validation/testsuite-selfcheck1.c000066400000000000000000000002411314543357600213570ustar00rootroot00000000000000good /* * check-name: selfcheck1 * check-command: sparse -E $file * check-output-ignore * * check-output-contains: good * check-output-excludes: evil */ sparse-0.5.1/validation/testsuite-selfcheck2.c000066400000000000000000000002311314543357600213570ustar00rootroot00000000000000evil /* * check-name: selfcheck2 * check-command: sparse -E $file * check-output-ignore * check-known-to-fail * * check-output-contains: good */ sparse-0.5.1/validation/testsuite-selfcheck3.c000066400000000000000000000002311314543357600213600ustar00rootroot00000000000000evil /* * check-name: selfcheck3 * check-command: sparse -E $file * check-output-ignore * check-known-to-fail * * check-output-excludes: evil */ sparse-0.5.1/validation/transparent-union.c000066400000000000000000000005041314543357600210110ustar00rootroot00000000000000struct a { int field; }; struct b { int field; }; typedef union { struct a *a; struct b *b; } transparent_arg __attribute__((__transparent_union__)); static void foo(transparent_arg arg) { } static void bar(void) { struct b arg = { 0 }; foo((struct a *) &arg); } /* * check-name: Transparent union attribute. */ sparse-0.5.1/validation/type1.c000066400000000000000000000007601314543357600163700ustar00rootroot00000000000000/* * 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 */ sparse-0.5.1/validation/typedef_shadow.c000066400000000000000000000004401314543357600203260ustar00rootroot00000000000000typedef int T; static void f(int T) { static T a; } /* * check-name: typedef shadowing * check-error-start: typedef_shadow.c:4:16: warning: 'T' has implicit type typedef_shadow.c:4:18: error: Expected ; at end of declaration typedef_shadow.c:4:18: error: got a * check-error-end: */ sparse-0.5.1/validation/typeof-addresspace.c000066400000000000000000000006111314543357600211030ustar00rootroot00000000000000#define __as __attribute__((address_space(1))) static void test_as(void) { int __as obj, *ptr; typeof(obj) var = obj; typeof(ptr) ptr2 = ptr; typeof(*ptr) var2 = obj; typeof(*ptr) *ptr3 = ptr; /* check-should-pass */ typeof(obj) *ptr4 = ptr; /* check-should-pass */ obj = obj; ptr = ptr; ptr = &obj; obj = *ptr; } /* * check-name: typeof-addresspace.c * check-known-to-fail */ sparse-0.5.1/validation/typeof-attribute.c000066400000000000000000000005511314543357600206330ustar00rootroot00000000000000#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. */ sparse-0.5.1/validation/typeof-mods.c000066400000000000000000000035551314543357600176010ustar00rootroot00000000000000#define __noderef __attribute__((noderef)) #define __bitwise __attribute__((bitwise)) #define __nocast __attribute__((nocast)) #define __safe __attribute__((safe)) static void test_spec(void) { unsigned int obj, *ptr; typeof(obj) var = obj; typeof(ptr) ptr2 = ptr; typeof(*ptr) var2 = obj; typeof(*ptr) *ptr3 = ptr; typeof(obj) *ptr4 = ptr; obj = obj; ptr = ptr; ptr = &obj; obj = *ptr; } static void test_const(void) { const int obj, *ptr; typeof(obj) var = obj; typeof(ptr) ptr2 = ptr; typeof(*ptr) var2 = obj; typeof(*ptr) *ptr3 = ptr; typeof(obj) *ptr4 = ptr; ptr = ptr; ptr = &obj; } static void test_volatile(void) { volatile int obj, *ptr; typeof(obj) var = obj; typeof(ptr) ptr2 = ptr; typeof(*ptr) var2 = obj; typeof(*ptr) *ptr3 = ptr; typeof(obj) *ptr4 = ptr; obj = obj; ptr = ptr; ptr = &obj; obj = *ptr; } static void test_bitwise(void) { typedef int __bitwise type_t; type_t obj, *ptr; typeof(obj) var = obj; typeof(ptr) ptr2 = ptr; typeof(*ptr) var2 = obj; typeof(*ptr) *ptr3 = ptr; typeof(obj) *ptr4 = ptr; obj = obj; ptr = ptr; ptr = &obj; obj = *ptr; } static void test_static(void) { static int obj, *ptr; typeof(obj) var = obj; typeof(ptr) ptr2 = ptr; typeof(*ptr) var2 = obj; typeof(*ptr) *ptr3 = ptr; typeof(obj) *ptr4 = ptr; obj = obj; ptr = ptr; ptr = &obj; obj = *ptr; } static void test_tls(void) { __thread int obj, *ptr; typeof(obj) var = obj; typeof(ptr) ptr2 = ptr; typeof(*ptr) var2 = obj; typeof(*ptr) *ptr3 = ptr; typeof(obj) *ptr4 = ptr; obj = obj; ptr = ptr; ptr = &obj; obj = *ptr; } static void test_nocast(void) { int __nocast obj, *ptr; typeof(obj) var = obj; typeof(ptr) ptr2 = ptr; typeof(*ptr) var2 = obj; typeof(*ptr) *ptr3 = ptr; typeof(obj) *ptr4 = ptr; obj = obj; ptr = ptr; ptr = &obj; obj = *ptr; } /* * check-name: typeof-mods * * check-error-start * check-error-end */ sparse-0.5.1/validation/typeof-noderef.c000066400000000000000000000004701314543357600202520ustar00rootroot00000000000000#define __noderef __attribute__((noderef)) static void test_noderef(void) { int __noderef obj, *ptr; typeof(ptr) ptr2 = ptr; typeof(*ptr) *ptr3 = ptr; typeof(obj) *ptr4 = ptr; ptr = ptr; ptr = &obj; } /* * check-name: typeof-noderef * check-known-to-fail * * check-error-start * check-error-end */ sparse-0.5.1/validation/typeof-safe.c000066400000000000000000000005651314543357600175530ustar00rootroot00000000000000#define __safe __attribute__((safe)) static void test_safe(void) { int __safe obj, *ptr; typeof(obj) var = obj; typeof(ptr) ptr2 = ptr; typeof(*ptr) var2 = obj; typeof(*ptr) *ptr3 = ptr; typeof(obj) *ptr4 = ptr; obj = obj; ptr = ptr; ptr = &obj; obj = *ptr; } /* * check-name: typeof-safe * check-known-to-fail * * check-error-start * check-error-end */ sparse-0.5.1/validation/typesign.c000066400000000000000000000031401314543357600171630ustar00rootroot00000000000000static 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 */ sparse-0.5.1/validation/varargs1.c000066400000000000000000000002451314543357600170520ustar00rootroot00000000000000extern int foo (const char *, ...); static void sparse_error(const char err[]) { foo("%s\n",err); } /* * check-name: Varargs bogus warning regression test #1 */ sparse-0.5.1/validation/wide.c000066400000000000000000000002751314543357600162570ustar00rootroot00000000000000static char c = L'\x41'; static int n = 1/(0x41 - L'\x41'); /* * check-name: wide character constants * * check-error-start wide.c:2:17: warning: division by zero * check-error-end */